/* ============================================================
   motion.css — the app's choreography layer. One idea, applied everywhere:
   the interface powers on like an instrument. Pages stage their content in
   (eyebrow → headline → body → controls), headlines rise word by word,
   numbers count up, the rings sweep to their reading, and a live call makes
   the whole shell breathe. Compositor-only (opacity / transform / clip),
   every sequence guarded by prefers-reduced-motion. Loaded last.
   ============================================================ */

:root {
  --stage-step: 60ms;       /* delay between staged siblings */
  --stage-dur: 720ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
}

/* ---- page entrance: a calmer, slightly larger rise than before ---- */
@keyframes app-page-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.app-page:not([hidden]) { animation: app-page-in 480ms var(--ease-out) both; }

/* ---- staged reveal: every direct child of [data-stage] rises in sequence.
   display:none → visible restarts the animation, so each route show replays it.
   motion.js stamps --i on the children (and keeps it stamped on dynamic lists). ---- */
@keyframes stage-in { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
.app-page:not([hidden]) [data-stage] > * {
  animation: stage-in var(--stage-dur) var(--ease-out) backwards;
  animation-delay: calc(var(--stage-base, 0ms) + var(--i, 0) * var(--stage-step));
}
.app-hero[data-stage] { --stage-base: 40ms; --stage-step: 90ms; }
.home-stats[data-stage] { --stage-base: 320ms; --stage-step: 70ms; }
.bento[data-stage] { --stage-base: 420ms; --stage-step: 65ms; }
.page-head[data-stage] { --stage-base: 40ms; --stage-step: 90ms; }
.prospects[data-stage], .lb[data-stage], .ai-feed[data-stage] { --stage-base: 280ms; --stage-step: 55ms; }
.admin[data-stage], .brand-page[data-stage], .affiliates[data-stage] { --stage-base: 200ms; --stage-step: 90ms; }
.ref-grid[data-stage] { --stage-base: 260ms; --stage-step: 80ms; }
/* referral rows land async — they cascade in as they arrive */
.app-page:not([hidden]) .ref-list > .ref-row { animation: stage-in 520ms var(--ease-out) backwards; animation-delay: calc(120ms + var(--i, 0) * 45ms); }
/* the pulse ticker fades in last, on its own */
@keyframes fade-up-soft { from { opacity: 0; } to { opacity: 1; } }
.app-page:not([hidden]) .pulse { animation: fade-up-soft 900ms var(--ease-out) 820ms backwards; }

/* ---- word-stagger headline: each word rises out of a clipped line ---- */
[data-words] .w { display: inline-block; overflow: hidden; vertical-align: bottom; padding: 0 0.06em 0.14em 0; margin: 0 -0.06em -0.14em 0; }
[data-words] .w__in { display: inline-block; animation: word-up 900ms var(--ease-out) backwards; animation-delay: calc(140ms + var(--wi, 0) * 75ms); will-change: transform; }
@keyframes word-up { from { transform: translateY(112%); } to { transform: none; } }
/* the h1 itself should not double-animate with the stage rule */
[data-stage] > [data-words] { animation: none !important; opacity: 1; }

/* ---- count-up numbers: a settle pop when the tween lands ---- */
[data-count].landed { animation: num-land 420ms var(--ease-spring); }
@keyframes num-land { 0% { transform: scale(1); } 40% { transform: scale(1.06); } 100% { transform: scale(1); } }
.hstat__v, .lb-score, .pros__score > span, .adm-stat__v, .inst__num b { display: inline-block; transform-origin: left bottom; }
.lb-score, .pros__score > span { transform-origin: right bottom; }

/* ---- magnetic CTAs (pointer: fine only — motion.js writes --mx/--my in px) ---- */
@media (hover: hover) and (pointer: fine) {
  .btn[data-magnet] { transform: translate3d(var(--mx, 0px), var(--my, 0px), 0); transition: transform 320ms var(--ease-out), background var(--dur-1), box-shadow var(--dur-1), color var(--dur-1); }
  .btn[data-magnet]:hover { transform: translate3d(var(--mx, 0px), var(--my, 0px), 0); }
  .btn[data-magnet]:active { transform: translate3d(var(--mx, 0px), var(--my, 0px), 0) scale(0.96); }
  .btn[data-magnet] .btn-dot { transition: transform 320ms var(--ease-out); transform: translate3d(calc(var(--mx, 0px) * 0.35), calc(var(--my, 0px) * 0.35), 0); }
}

/* ============================================================
   LEFT RAIL — a single indicator that slides between routes, plus the
   signal that a call is live.
   ============================================================ */
.app-rail__nav { position: relative; }
.rail-ind {
  position: absolute; left: 0; top: 0; width: 100%; height: 38px; border-radius: var(--r-md);
  background: rgba(var(--ink), 0.06); pointer-events: none; opacity: 0;
  transform: translate3d(0, var(--ry, 0px), 0); transition: transform 460ms var(--ease-spring), opacity 200ms var(--ease-out), width 300ms var(--ease-out);
}
.rail-ind::before { content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 3px; height: 18px; border-radius: 99px; background: var(--accent); }
.app-rail__nav.has-ind .rail-ind { opacity: 1; }
.app-rail__nav.has-ind .rail-item.is-active { background: none; }
.app-rail__nav.has-ind .rail-item.is-active::before { display: none; }
.rail-item { z-index: 1; transition: color .18s var(--ease), background .18s var(--ease), transform .18s var(--ease); }
.rail-item:hover { transform: translateX(2px); }
.rail-item:active { transform: translateX(2px) scale(0.98); }
/* mobile: the rail is a row — the indicator slides horizontally */
@media (max-width: 820px) {
  .rail-ind { width: var(--rw, 0px); height: 100%; transform: translate3d(var(--rx, 0px), 0, 0); }
  .rail-ind::before { left: 50%; top: auto; bottom: -1px; transform: translateX(-50%); width: 16px; height: 2px; }
  .rail-item:hover { transform: none; }
}

/* live signal in the rail: the Standby item carries a pinging dot while a call runs */
.rail-item[data-route="standby"] { position: relative; }
.rail-item[data-route="standby"]::after { content: ""; position: absolute; right: 12px; top: 50%; width: 7px; height: 7px; border-radius: 50%; background: var(--danger); transform: translateY(-50%) scale(0); transition: transform 300ms var(--ease-spring); }
body[data-live="1"] .rail-item[data-route="standby"]::after { transform: translateY(-50%) scale(1); animation: live-ping 1.4s ease-out infinite; }
@media (max-width: 820px) { body[data-live="1"] .rail-item[data-route="standby"] { padding-right: 26px; } }

/* the whole shell breathes while live: a warm signal layer fades over the aurora
   (opacity only — never re-blur the aurora), the wordmark pulses softly */
.ambient { display: block; position: fixed; inset: 0; z-index: -1; pointer-events: none; opacity: 0; transition: opacity 1.4s var(--ease-out);
  background: radial-gradient(60% 50% at 50% 100%, color-mix(in oklab, var(--danger) 16%, transparent), transparent 70%), radial-gradient(50% 40% at 10% 0%, color-mix(in oklab, var(--accent) 18%, transparent), transparent 70%); }
.ambient::before, .ambient::after { display: none; }
body[data-live="1"] .ambient { opacity: 1; }
body[data-live="1"] .app-wordmark { animation: mark-breathe 2.4s ease-in-out infinite; }
@keyframes mark-breathe { 50% { opacity: 0.72; } }

/* ============================================================
   STANDBY COCKPIT — powers on: toolbar drops, panels rise, the dial's
   ticks rotate into place, the rings sweep to their reading, and the
   momentum trace draws itself.
   ============================================================ */
#page-standby:not([hidden]) .toolbar { animation: fade-down 560ms var(--ease-out) 60ms backwards; }
#page-standby:not([hidden]) .widget-bar > * { animation: stage-in 600ms var(--ease-out) backwards; animation-delay: calc(160ms + var(--i, 0) * 45ms); }
#page-standby:not([hidden]) .card { animation: card-rise 760ms var(--ease-out) backwards; animation-delay: calc(180ms + var(--rise-delay, 0s)); }

.ring-ticks { transform-origin: 50% 50%; transform-box: fill-box; }
#page-standby:not([hidden]) .ring-ticks-major { animation: ticks-in 1400ms var(--ease-out) 300ms backwards; }
#page-standby:not([hidden]) .ring-ticks-minor { animation: ticks-in-r 1400ms var(--ease-out) 300ms backwards; }
@keyframes ticks-in { from { transform: rotate(-42deg); opacity: 0; } to { transform: none; opacity: 0.55; } }
@keyframes ticks-in-r { from { transform: rotate(38deg); opacity: 0; } to { transform: none; opacity: 0.55; } }

/* motion.js flips .sweep on for one frame-set so the ring transition lengthens for the sweep */
.ring.sweep { transition: stroke-dashoffset 1300ms var(--ease-out) !important; }
.ring-momentum.sweep { transition-delay: 320ms; }
.ring-combo.sweep { transition-delay: 420ms; }
.ring-xp.sweep { transition-delay: 520ms; }

/* rings-center settles in after the sweep starts (as a block — its number/grade keep their own bump animations) */
#page-standby:not([hidden]) .rings-center { animation: stage-in 700ms var(--ease-out) 520ms backwards; }
#page-standby:not([hidden]) .ring-legend .lg { animation: stage-in 600ms var(--ease-out) backwards; animation-delay: calc(760ms + var(--i, 0) * 70ms); }
#page-standby:not([hidden]) .close-meter, #page-standby:not([hidden]) .spark, #page-standby:not([hidden]) .meters-row, #page-standby:not([hidden]) .dim-bars {
  animation: stage-in 700ms var(--ease-out) backwards;
}
#page-standby:not([hidden]) .close-meter { animation-delay: 700ms; }
#page-standby:not([hidden]) .spark { animation-delay: 780ms; }
#page-standby:not([hidden]) .meters-row { animation-delay: 860ms; }
#page-standby:not([hidden]) .dim-bars { animation-delay: 940ms; }
#page-standby:not([hidden]) .dim { animation: stage-in 560ms var(--ease-out) backwards; animation-delay: calc(980ms + var(--i, 0) * 40ms); }

/* the momentum trace draws left → right when the panel shows (polyline has pathLength=1) */
.spark-line.draw { stroke-dasharray: 1; stroke-dashoffset: 1; animation: draw-line 1400ms var(--ease-out) 900ms forwards; }
@keyframes draw-line { to { stroke-dashoffset: 0; } }

/* dimension bars fill from the left with a light sweep as values change */
.dim-fill { position: relative; overflow: hidden; }
.dim-fill::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.35), transparent); transform: translateX(-100%); }
.dim-fill.tick::after { animation: bar-sheen 700ms var(--ease-out); }
@keyframes bar-sheen { to { transform: translateX(100%); } }

/* live transcript: the interim line breathes; new bubbles spring in a touch more */
.tx-text.interim { animation: interim-breathe 1.4s ease-in-out infinite; }
@keyframes interim-breathe { 50% { opacity: 0.4; } }
@keyframes bubble-in { from { opacity: 0; transform: translateY(10px) scale(0.96); } }

/* next-best-move: the label ticks when the suggestion changes */
.nb-text.swap { animation: nb-swap 380ms var(--ease-out); }
@keyframes nb-swap { from { opacity: 0; transform: translateY(5px); } }

/* ============================================================
   SUB-PAGES — Prospects · AI Suggestions · Leaderboard · Admin
   ============================================================ */
/* prospects: a score line at the foot of each row fills to the best score */
.pros { position: relative; overflow: hidden; transition: border-color .2s var(--ease), transform .3s var(--ease-out); }
.pros:hover { transform: translateY(-2px); }
.pros::after { content: ""; position: absolute; left: 0; bottom: 0; height: 2px; width: 100%; background: var(--accent); transform-origin: left; transform: scaleX(var(--score, 0)); transition: transform 1.2s var(--ease-out); opacity: 0.7; }
.pros[data-band="low"]::after { background: var(--band-low); }
.pros[data-band="mid"]::after { background: var(--band-mid); }
.pros__temp[data-t="hot"] .dot { animation: hot-pulse 1.6s ease-in-out infinite; }
@keyframes hot-pulse { 50% { box-shadow: 0 0 0 4px color-mix(in oklab, var(--danger) 16%, transparent), 0 0 8px var(--danger); } }

/* AI feed: the accent bar draws down, the card lifts on hover */
.ai-card { transition: border-color .2s var(--ease), transform .3s var(--ease-out); }
.ai-card:hover { border-color: var(--hairline-strong); transform: translateY(-2px); }
.ai-card::before { transform-origin: top; animation: bar-draw 900ms var(--ease-out) backwards; animation-delay: calc(360ms + var(--i, 0) * 55ms); }
@keyframes bar-draw { from { transform: scaleY(0); } to { transform: none; } }

/* leaderboard: a podium — #1 reads larger; your row gets a one-time light sweep */
.lb-row { position: relative; overflow: hidden; transition: border-color .2s var(--ease), transform .3s var(--ease-out); }
.lb-row:hover { border-color: var(--hairline-strong); transform: translateX(3px); }
.lb-row:nth-child(1) { padding-block: 20px; background: radial-gradient(120% 120% at 0% 0%, color-mix(in oklab, var(--accent) 12%, transparent), var(--card-2) 62%); border-color: color-mix(in oklab, var(--accent) 28%, transparent); }
.lb-row:nth-child(1) .lb-rank { font-size: 2.2rem; }
.lb-row:nth-child(1) .lb-score { font-size: 2.1rem; }
.lb-row:nth-child(1) .lb-name { font-size: 1.05rem; }
.lb-row.me::after { content: ""; position: absolute; inset: 0; background: linear-gradient(100deg, transparent 20%, color-mix(in oklab, var(--accent) 18%, transparent) 50%, transparent 80%); transform: translateX(-120%); animation: row-sweep 1.6s var(--ease-out) 900ms both; pointer-events: none; }
@keyframes row-sweep { to { transform: translateX(120%); } }
.lb-rank { transition: color .2s; }

/* admin: stat cards + table rows stage in; bars grow */
.adm-stat { transition: border-color .2s var(--ease), transform .3s var(--ease-out); }
.adm-stat:hover { border-color: var(--hairline-strong); transform: translateY(-2px); }
.adm-table .adm-row { animation: stage-in 520ms var(--ease-out) backwards; animation-delay: calc(120ms + var(--i, 0) * 40ms); }
.adm-bar__fill { animation: grow-x 1.1s var(--ease-out) backwards; animation-delay: calc(200ms + var(--i, 0) * 60ms); }

/* ---- level pill: the XP ring sweeps on load ---- */
.lr-fill { transition: stroke-dashoffset 1.2s var(--ease-out) 400ms; }

/* ============================================================
   REDUCED MOTION — everything above collapses to its final state.
   (base.css also zeroes durations globally; this makes the intent explicit.)
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .app-page:not([hidden]) [data-stage] > *, [data-words] .w__in, .app-page:not([hidden]) .pulse,
  #page-standby:not([hidden]) .toolbar, #page-standby:not([hidden]) .card, #page-standby:not([hidden]) .widget-bar > *,
  #page-standby:not([hidden]) .ring-ticks-major, #page-standby:not([hidden]) .ring-ticks-minor,
  #page-standby:not([hidden]) .rings-center, #page-standby:not([hidden]) .ring-legend .lg,
  #page-standby:not([hidden]) .close-meter, #page-standby:not([hidden]) .spark, #page-standby:not([hidden]) .meters-row,
  #page-standby:not([hidden]) .dim-bars, #page-standby:not([hidden]) .dim, .spark-line.draw, .ai-card::before,
  .lb-row.me::after, .adm-table .adm-row, .adm-bar__fill, [data-count].landed { animation: none !important; opacity: 1; transform: none; }
  .spark-line.draw { stroke-dasharray: none; stroke-dashoffset: 0; }
  .ring.sweep { transition: none !important; }
  .pulse__run, .wave i, .persona-chip, .sig-dot::after, .tile__live, .rail-item[data-route="standby"]::after, .app-wordmark, .pros__temp .dot { animation: none !important; }
  .tile { transform: none !important; }
  .rail-ind { transition: none; }
}

/* admin sparklines draw in (built with pathLength=1) */
.adm-spark__line { stroke-dasharray: 1; stroke-dashoffset: 1; animation: draw-line 1200ms var(--ease-out) backwards; animation-delay: calc(300ms + var(--i, 0) * 40ms); animation-fill-mode: forwards; }
@media (prefers-reduced-motion: reduce) { .adm-spark__line { animation: none; stroke-dasharray: none; stroke-dashoffset: 0; } }

/* ============================================================
   SHEETS — the scorecard is the payoff of every call: the sheet springs up,
   its sections land in sequence, the grade ring sweeps to the score while the
   number counts, and the dimension bars grow only once their row is on screen.
   The same stagger serves progress, roleplay personas and the theme picker.
   ============================================================ */
.overlay:not([hidden]) [data-stage] > * {
  animation: stage-in 620ms var(--ease-out) backwards;
  animation-delay: calc(var(--stage-base, 120ms) + var(--i, 0) * var(--stage-step, 70ms));
}
#scorecard[data-stage] { --stage-base: 160ms; --stage-step: 90ms; }
#progress[data-stage] { --stage-base: 120ms; --stage-step: 80ms; }
.persona-grid[data-stage] { --stage-base: 80ms; --stage-step: 40ms; }
.theme-grid[data-stage] { --stage-base: 60ms; --stage-step: 35ms; }
/* the grade ring: rendered empty by scorecard.js, swept to its reading on the next frame */
.sc-grade-ring circle[data-off] { transition: stroke-dashoffset 1400ms var(--ease-out) 320ms; }
/* dimension bars grow when their row lands, not while the row is still invisible */
.sc-dim-fill { animation-delay: calc(var(--stage-base, 160ms) + var(--i, 0) * var(--stage-step, 90ms) + 120ms); animation-fill-mode: backwards; }
/* stars settle with a little pop after the ring starts */
.overlay:not([hidden]) .sc-stars { animation: stars-in 700ms var(--ease-spring) 520ms backwards; transform-origin: left center; display: inline-block; }
@keyframes stars-in { from { opacity: 0; transform: scale(0.6) rotate(-6deg); } to { opacity: 1; transform: none; } }
/* the big score keeps its own count-up pop */
.sc-grade-num b, .sc-close-num, .sc-dim-score { display: inline-block; }
.sc-close-num, .sc-dim-score { transform-origin: right bottom; }
@media (prefers-reduced-motion: reduce) {
  .overlay:not([hidden]) [data-stage] > *, .overlay:not([hidden]) .sc-stars, .sc-dim-fill { animation: none !important; opacity: 1; transform: none; }
  .sc-grade-ring circle[data-off] { transition: none; }
}
