/* ==========================================================================
   ANIMATIONS — every effect here is triggered by a JS-added class
   (`.is-visible`, `.is-active`, `.is-lit`) via IntersectionObserver in
   main.js. No animation libraries, no scroll-jacking: CSS transitions only,
   and every rule is skipped entirely under prefers-reduced-motion (base.css).
   ========================================================================== */

/* ---- Generic reveal: fade + rise, used for most one-off blocks ---- */
.reveal{ opacity: 0; transform: translateY(18px); transition: opacity var(--duration-md) var(--ease-out), transform var(--duration-md) var(--ease-out); }
.reveal.is-visible{ opacity: 1; transform: translateY(0); }

/* ---- Word-by-word reveal (hero: "Business Platform" arriving a beat later) ----
   Wrap each word/phrase in <span style="--i:0"> … <span style="--i:1"> and
   add class="word-reveal" to the parent heading. */
.word-reveal span{
  display: inline-block; opacity: 0; transform: translateY(.35em);
  transition: opacity var(--duration-md) var(--ease-out), transform var(--duration-md) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 140ms);
}
.word-reveal.is-visible span{ opacity: 1; transform: translateY(0); }

/* ---- A single word that quietly catches color (Insight: "confidence") ---- */
.word-lit{ color: var(--ink-soft); transition: color var(--duration-slow) var(--ease-in-out); }
.word-lit.is-visible{ color: var(--accent); font-style: italic; }

/* ---- SVG line-draw (Roadmap / Team connections) ----
   Set --len to the path's actual length (Math.ceil of getTotalLength()) so
   the draw-in is precise rather than guessed. */
.line-draw{ stroke-dasharray: var(--len, 300); stroke-dashoffset: var(--len, 300); transition: stroke-dashoffset var(--duration-slow) var(--ease-out); transition-delay: calc(var(--i, 0) * 90ms); }
.line-draw.is-visible{ stroke-dashoffset: 0; }
.node-draw{ opacity: 0; transform: scale(.6); transform-box: fill-box; transform-origin: center; transition: opacity var(--duration-fast) ease, transform var(--duration-fast) var(--ease-out); transition-delay: calc(var(--i, 0) * 90ms); }
.node-draw.is-visible{ opacity: 1; transform: scale(1); }

/* ---- Sticky media stack: several frames occupy the same box; JS toggles
   which one is active as the reader scrolls past each story-step. ---- */
.media-stack{ position: relative; }
.media-stack .media-frame{ position: absolute; inset: 0; opacity: 0; transition: opacity var(--duration-md) ease; }
.media-stack .media-frame:first-child{ position: relative; } /* reserves the box's height */
.media-stack .media-frame.is-active{ opacity: 1; position: absolute; inset: 0; }
.media-stack .media-frame.is-active ~ .media-frame:first-child{ position: absolute; }

/* ---- Count-up number: the digits themselves are swapped by JS (main.js);
   this just keeps them from jittering the layout while it happens. ---- */
.impact-number{ font-variant-numeric: tabular-nums; }

/* ---- Benchmark chart: 64 progress rings that fill in together as the
   chart scrolls into view (class="bench-chart reveal" reuses the existing
   .reveal observer — no JS changes needed). Each ring's true fill target
   is set inline via --target; --i staggers the fill for a cascading feel. ---- */
.bench-ring{ transition: stroke-dashoffset 1.1s var(--ease-out); transition-delay: calc(var(--i, 0) * 12ms); }
.bench-chart.is-visible .bench-ring{ stroke-dashoffset: var(--target); }
@media (prefers-reduced-motion: reduce){
  .bench-ring{ transition: none; stroke-dashoffset: var(--target) !important; }
}
