/* ═══════════════════════════════════════════════════════════════════════════
   ORIX Motion Language — M1 Toast Arrival + M1a Tone + M2 Surface Lifecycle
   + M3 Menu/Popover Motion + M4 Dashboard Content Reveal + M6 Line-Chart
   Interaction + M7 Notification Bell Arrival
   ─────────────────────────────────────────────────────────────────────────
   Motion decorates an already-correct final state. It never owns visibility,
   content, ARIA state or business state. M2 adds a restrained modal surface
   enter/exit and the missing drawer exit while preserving the drawer's
   existing entry animation from components.css. M3 adds the same restrained
   enter/exit to the header account dropdown and the mobile bottom-nav "More"
   menu — both toggle the existing .hidden utility, so entry replays on its
   own whenever .hidden is removed (a display:none→block transition always
   restarts a CSS animation); .is-leaving only defers re-adding .hidden long
   enough for the exit animation to actually play. M4 adds a one-shot reveal
   for a Dashboard card slot the instant ui.revealContent() (ui.js) installs
   its real content over a ui.skeleton() placeholder — .ds-content-in is only
   ever applied by that helper, never automatically, so it never runs on a
   card render that had no skeleton to begin with. M6 adds no entry motion at
   all (the interactive line chart mounts fully drawn) — only the plain
   `transition:` rules for its own is-active hover/focus/tap state, owned
   here while the resting/active values themselves stay in components.css.
   ═══════════════════════════════════════════════════════════════════════ */

.ds-toast{
  animation:ds-toast-enter var(--duration-base) var(--ease-out);
}

.ds-toast-success{
  background:color-mix(in srgb,var(--color-success) 75%,var(--color-heading));
}

@keyframes ds-toast-enter{
  from{opacity:0;transform:translateY(var(--space-2));}
  to{opacity:1;transform:translateY(0);}
}

@media(prefers-reduced-motion:reduce){
  .ds-toast{animation:none;}
}

/* ─── M2: modal + shared surface lifecycle ──────────────────────────────── */
.modal-overlay > .modal,
.modal-overlay > .search-palette{
  animation:ds-surface-in var(--duration-slow) var(--ease-out);
}
.modal-overlay.is-leaving,
.ds-drawer-overlay.is-leaving{pointer-events:none;}
.modal-overlay.is-leaving > .modal,
.modal-overlay.is-leaving > .search-palette{
  animation:ds-surface-out var(--duration-base) var(--ease-in) forwards;
}
.ds-drawer-overlay.is-leaving .ds-drawer{
  animation:ds-drawer-out var(--duration-drawer) var(--ease-in) forwards;
}

@keyframes ds-surface-in{
  from{opacity:0;transform:translateY(var(--space-2));}
  to{opacity:1;transform:translateY(0);}
}
@keyframes ds-surface-out{
  from{opacity:1;transform:translateY(0);}
  to{opacity:0;transform:translateY(var(--space-1));}
}
@keyframes ds-drawer-out{
  from{opacity:1;transform:translateX(0);}
  to{opacity:0;transform:translateX(var(--space-4));}
}
@keyframes ds-sheet-out{
  from{opacity:1;transform:translateY(0);}
  to{opacity:0;transform:translateY(var(--space-5));}
}

@media(max-width:767px){
  .ds-drawer-overlay.is-leaving .ds-drawer{animation-name:ds-sheet-out;}
}

@media(prefers-reduced-motion:reduce){
  .modal-overlay > .modal,
  .modal-overlay > .search-palette,
  .modal-overlay.is-leaving > .modal,
  .modal-overlay.is-leaving > .search-palette,
  .ds-drawer-overlay.is-leaving .ds-drawer{animation:none;}
}

/* ─── M3: menu/popover motion (account dropdown + bottom-nav More) ──────── */
.account-dropdown{
  animation:ds-menu-in-below var(--duration-base) var(--ease-out);
}
.bn-more-menu{
  animation:ds-menu-in-above var(--duration-base) var(--ease-out);
}
/* N1-C: the notifications preview panel is the SAME kind of anchored
   menu/popover surface as the account dropdown right beside it — reusing
   M3's existing open/close motion, not new motion. This is surface
   open/close, never the identity-based "a new notification just arrived"
   arrival motion, which is explicitly N1-E's job and does not exist yet. */
.notif-panel{
  animation:ds-menu-in-below var(--duration-base) var(--ease-out);
}
/* U1.2C: the per-row overflow menu is the same anchored menu/popover
   surface again — reusing M3's motion once more, direction chosen
   dynamically by ui.js's positionFloatingMenu() (.is-open-up) rather than
   fixed per-surface like the two above. */
.row-menu{
  animation:ds-menu-in-below var(--duration-base) var(--ease-out);
}
.row-menu.is-open-up{
  animation:ds-menu-in-above var(--duration-base) var(--ease-out);
}
/* Same contract as M2's .modal-overlay.is-leaving/.ds-drawer-overlay.is-leaving
   just above: once a surface has logically closed (aria-expanded is already
   false), it must not still be clickable while its exit motion finishes. */
.account-dropdown.is-leaving,
.bn-more-menu.is-leaving{pointer-events:none;}
.account-dropdown.is-leaving{
  animation:ds-menu-out-below var(--duration-fast) var(--ease-in) forwards;
}
.bn-more-menu.is-leaving{
  animation:ds-menu-out-above var(--duration-fast) var(--ease-in) forwards;
}
.notif-panel.is-leaving{pointer-events:none;
  animation:ds-menu-out-below var(--duration-fast) var(--ease-in) forwards;
}
.row-menu.is-leaving{pointer-events:none;
  animation:ds-menu-out-below var(--duration-fast) var(--ease-in) forwards;
}
.row-menu.is-leaving.is-open-up{
  animation:ds-menu-out-above var(--duration-fast) var(--ease-in) forwards;
}

@keyframes ds-menu-in-below{
  from{opacity:0;transform:translateY(calc(-1 * var(--space-1)));}
  to{opacity:1;transform:translateY(0);}
}
@keyframes ds-menu-out-below{
  from{opacity:1;transform:translateY(0);}
  to{opacity:0;transform:translateY(calc(-1 * var(--space-1)));}
}
@keyframes ds-menu-in-above{
  from{opacity:0;transform:translateY(var(--space-1));}
  to{opacity:1;transform:translateY(0);}
}
@keyframes ds-menu-out-above{
  from{opacity:1;transform:translateY(0);}
  to{opacity:0;transform:translateY(var(--space-1));}
}

@media(prefers-reduced-motion:reduce){
  .account-dropdown,
  .account-dropdown.is-leaving,
  .bn-more-menu,
  .bn-more-menu.is-leaving,
  .notif-panel,
  .notif-panel.is-leaving,
  .row-menu,
  .row-menu.is-leaving{animation:none;}
}

/* ─── M4: Dashboard skeleton → content reveal ───────────────────────────── */
/* Applied only by ui.revealContent() (ui.js), and only when the container
   actually held a ui.skeleton() placeholder a moment before — never a
   global/implicit rule, so an ordinary card render with no skeleton, or any
   other .ds-dash-card update, is completely unaffected. */
.ds-content-in{
  animation:ds-content-reveal var(--duration-base) var(--ease-out);
}

@keyframes ds-content-reveal{
  from{opacity:0;transform:translateY(var(--space-1));}
  to{opacity:1;transform:translateY(0);}
}

@media(prefers-reduced-motion:reduce){
  .ds-content-in{animation:none;}
}

/* ─── M6: interactive line-chart activation (hover/focus/tap) ────────────── */
/* The line itself has no entry motion — components.css mounts it complete.
   This is the only motion M6 has: the transition between a point's resting
   and .is-active state (charts.wireLine()'s setActive()), never a mount/
   unmount animation, so it stays plain `transition:`, not `@keyframes`. */
.ds-line-guide{
  transition:opacity var(--duration-fast) var(--ease-out);
}
.ds-line-pt{
  transition:r var(--duration-fast) var(--ease-out);
}
.ds-line-pt-btn{
  transition:color var(--duration-fast) var(--ease-out),
             background-color var(--duration-fast) var(--ease-out);
}
.ds-line-value{
  transition:opacity var(--duration-fast) var(--ease-out),
             transform var(--duration-fast) var(--ease-out);
}

@media(prefers-reduced-motion:reduce){
  .ds-line-guide,
  .ds-line-pt,
  .ds-line-pt-btn,
  .ds-line-value{transition:none;}
}

/* ─── M7: notification bell arrival (identity-triggered, N1-E) ───────────── */
/* Applied by notifications-ui.js's triggerBellArrival() exactly once per
   genuinely new notification ID batch discovered after the page's initial
   fetch baseline (notifications-state.js's arrivalVersion) — never on the
   initial load itself, an ordinary state update, mark-read/mark-all-read, a
   route change, or a header re-render replaying an already-consumed
   arrival. A single restrained scale pop, not a repeating/urgent effect —
   "something new arrived", not an alarm. */
.notif-bell--arrived{
  animation:ds-bell-arrival var(--duration-slow) var(--ease-out);
}

@keyframes ds-bell-arrival{
  0%{transform:scale(1);}
  40%{transform:scale(1.14);}
  100%{transform:scale(1);}
}

@media(prefers-reduced-motion:reduce){
  /* Reduced motion gets the final (unchanged) bell state immediately — the
     unread badge/count, updated separately by updateBadge(), is completely
     unaffected by this rule either way. */
  .notif-bell--arrived{animation:none;}
}
