/* ============================================================================
   pwa/motion/motion.css — the shared MOTION LANGUAGE (single source of truth)
   ----------------------------------------------------------------------------
   One home for motion tokens, keyframes, utility classes, and the ONE
   reduced-motion block. Surfaces (cinema card, Today grid card, hero buttons,
   session/haul) wire to these via window.iatroMotion (motion.js) so a primitive
   works universally regardless of the card/hero context it lands on.

   Step 1 (this commit) is ADDITIVE: everything here is namespaced .mo-* / --mo-*
   so it cannot collide with the existing scattered animation rules. Step 2
   migrates the card/cinema/hero/session keyframes INTO this file and deletes the
   originals, repointing references.

   Constraints honoured (from the consolidation audit):
   - Height morph ONLY on the .mo-reveal-body child wrapper — never the outer
     .fc-card--v2 (its inline max-height is rewritten every frame by
     _syncFcStageAvail; a card-level height transition would fight it).
   - :active press feedback is PRM-exempt (instant direct manipulation).
   ========================================================================== */

:root {
  /* durations */
  --mo-dur-fast: 120ms;
  --mo-dur-base: 240ms;
  --mo-dur-slow: 380ms;
  /* easings — alias the existing design tokens so there is one vocabulary */
  --mo-ease-out:    var(--ease-out,    cubic-bezier(.4,.05,.2,1));
  --mo-ease-spring: var(--ease-spring, cubic-bezier(.34,1.56,.64,1));
  --mo-ease-in:     cubic-bezier(.4,0,1,1);
  /* shared knobs */
  --mo-reveal-ceiling: 600px;   /* generous cap; the inner scroller clips real content */
  --mo-press-depth: 3px;
}

/* ---- keyframes (the shared vocabulary) ---------------------------------- */
@keyframes mo-reveal-in   { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:none; } }
@keyframes mo-pulse-ring  { 0% { box-shadow:0 0 0 0 var(--mo-pulse-color, rgba(255,255,255,.45)); }
                            55% { box-shadow:0 0 0 9px rgba(255,255,255,0); }
                            100% { box-shadow:0 0 0 0 rgba(255,255,255,0); } }
@keyframes mo-stagger-in  { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:none; } }
@keyframes mo-scale-in    { from { opacity:0; transform:scale(.82); } to { opacity:1; transform:scale(1); } }
@keyframes mo-spin        { to { transform:rotate(360deg); } }
@keyframes mo-progress-pulse { 0% { filter:brightness(1); } 40% { filter:brightness(1.6); } 100% { filter:brightness(1); } }

/* ---- press / touch feedback (universal, PRM-exempt) --------------------- */
/* base: any tappable element — subtle scale + brightness */
.mo-press { transition: transform var(--mo-dur-fast) var(--mo-ease-out), filter var(--mo-dur-fast) var(--mo-ease-out); }
.mo-press:active { transform: scale(.97); filter: brightness(.96); }

/* hero: scale-spring for the per-tab primary CTA */
.mo-press-hero { transition: transform var(--mo-dur-base) var(--mo-ease-spring), filter var(--mo-dur-fast); }
.mo-press-hero:active { transform: scale(.96); }

/* 3d: Duolingo-style depress for grade buttons — resting shadow crushes on press.
   The element sets --mo-press-shadow (a darker shade of its own colour). */
.mo-press-3d { box-shadow: 0 var(--mo-press-depth) 0 var(--mo-press-shadow, rgba(0,0,0,.4));
  transition: transform var(--mo-dur-fast) var(--mo-ease-out), box-shadow var(--mo-dur-fast) var(--mo-ease-out); }
.mo-press-3d:active { transform: translateY(var(--mo-press-depth)); box-shadow: 0 0 0 var(--mo-press-shadow, rgba(0,0,0,.4)); }

/* ---- reveal morph (child-wrapper height; never the card) --------------- */
.mo-reveal-body { max-height: 0; overflow: hidden; }
.mo-reveal-body.mo-revealed { max-height: var(--mo-reveal-ceiling); }
.mo-reveal-body.mo-morphing { transition: max-height var(--mo-dur-base) var(--mo-ease-out); }

/* ---- reveal content stagger (the micro-pause beat) --------------------- */
.mo-reveal-anim { animation: mo-reveal-in var(--mo-dur-base) var(--mo-ease-spring) both;
  animation-delay: var(--mo-reveal-delay, 0ms); }

/* ---- attention pulse (box-shadow ripple, no transform) ----------------- */
.mo-pulse { animation: mo-pulse-ring 700ms var(--mo-ease-spring) forwards; }

/* ---- entrance stagger (list rows; JS sets animation-delay per row) ------ */
.mo-stagger-row { animation: mo-stagger-in var(--mo-dur-slow) var(--mo-ease-spring) backwards; }

/* ---- entrance stagger, fade+scale variant (badge grids / heatmap tiles;
   JS sets animation-delay per item via the same stagger() helper, opts.cls) */
.mo-scale-stagger { animation: mo-scale-in var(--mo-dur-slow) var(--mo-ease-spring) backwards; }

/* ---- bar-fill reveal (progress/coverage bars; JS toggles .mo-bar-in once
   the bar's section scrolls into view — width itself is set inline by the
   caller as usual, this only reveals via a GPU-only transform so the fill
   never triggers a layout-thrashing width transition). ---------------------- */
.mo-bar-fill { transform-origin: left center; transform: scaleX(0); }
.mo-bar-fill.mo-bar-in { transform: scaleX(1); transition: transform var(--duration-xl, 380ms) var(--ease-out-quart, cubic-bezier(.165,.84,.44,1)); }

/* ---- loading morph (label keeps; spinner appended) --------------------- */
.mo-loading { pointer-events: none; opacity: .72; position: relative; }
.mo-loading::after { content:''; display:inline-block; width:14px; height:14px; margin-left:8px;
  vertical-align:-2px; border:2px solid rgba(255,255,255,.32); border-top-color:#fff; border-radius:50%;
  animation: mo-spin .7s linear infinite; }

/* ---- progress pulse (brightness flash on the fill, never width) -------- */
.mo-progress-pulse { animation: mo-progress-pulse 320ms var(--mo-ease-out) both; }

/* ---- particle (celebrate(); JS drives the WAAPI flight) ---------------- */
.mo-particle { will-change: transform, opacity; }

/* ---- app component bindings -------------------------------------------
   Bind the per-tab HERO buttons to the hero press-spring in ONE place (#8),
   rather than scattering a class across ~10 render templates that rebuild
   often. motion.css loads last, so this wins over the old per-element :active
   rules (which are removed). Transition keeps filter so hover effects survive. */
.hero-act, .sh-act-card, .ppr-action, .start-action-btn, .pharma-session-launch {
  transition: transform var(--mo-dur-base) var(--mo-ease-spring), filter var(--mo-dur-fast) var(--mo-ease-out);
}
.hero-act:active, .sh-act-card:active, .ppr-action:active,
.start-action-btn:active, .pharma-session-launch:active {
  transform: scale(.96);
}

/* ============================================================================
   THE ONE reduced-motion block (replaces the per-file duplicates in Step 2)
   :active press states are intentionally NOT suppressed — instant direct
   manipulation is PRM-exempt (WCAG 2.1).
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .mo-reveal-body.mo-morphing { transition: none !important; }
  .mo-reveal-anim,
  .mo-pulse,
  .mo-stagger-row,
  .mo-progress-pulse { animation: none !important; }
  .mo-scale-stagger { animation: none !important; opacity: 1 !important; transform: none !important; }
  .mo-press-hero,
  .hero-act, .sh-act-card, .ppr-action, .start-action-btn, .pharma-session-launch { transition: none !important; }
  .mo-loading::after { animation: none !important; }
  .mo-bar-fill { transform: none !important; }
  .mo-bar-fill.mo-bar-in { transition: none !important; }
}
