/* Motion. Loaded last on every page.
   - `.js` (set inline in <head>): the hero load sequence (M1), pure CSS.
   - `.motion` (set by /assets/js/site.js only with JS AND no reduced-motion preference):
     every scroll-driven effect. Without it the page renders in its final, static state.
   transform / opacity / clip-path only. Values: design handoff §5. */

/* ---------- M1: hero load sequence ---------- */
.js .display .line, .js .rise { animation: rise 700ms var(--ease-out) backwards; }   /* backwards: S2 owns transform afterwards */
.js .display .line:nth-child(1) { animation-delay: 60ms; }
.js .display .line:nth-child(2) { animation-delay: 140ms; }
.js .rise { animation-delay: calc(var(--d) * 80ms + 120ms); }
.js .converge .cv-in, .js .converge .cv-out { stroke-dasharray: 1; stroke-dashoffset: 1; animation: draw 900ms var(--ease-out) forwards; }
.js .converge .cv-in.d1 { animation-delay: 420ms; }
.js .converge .cv-in.d2 { animation-delay: 520ms; }
.js .converge .cv-in.d3 { animation-delay: 620ms; }
.js .converge .cv-out { animation-delay: 1080ms; animation-duration: 480ms; }
.js .converge-studio .cv-out { animation-duration: 300ms; }
.js .converge .cv-proof, .js .converge .cv-halo { opacity: 0; transform: scale(.6); animation: pop 520ms var(--ease-out) 1480ms forwards; }
.js .converge .cv-halo { animation-name: halo; }
.js .converge .cv-scatter circle { opacity: 0; animation: fade 500ms ease 200ms forwards; }
.js .converge .tile { opacity: 0; transform: scale(.6); animation: pop 520ms var(--ease-out) 1480ms forwards; }
.js .converge .tile.t2 { animation-delay: 1640ms; }
.js .converge .tile.t3 { animation-delay: 1800ms; }
@media (min-width: 900px) {
  .js .lead-in { transform: scaleY(0); animation: grow 600ms var(--ease-out) 1750ms forwards; }
}
@keyframes rise { from { opacity: 0; transform: translateY(10px); } }
@keyframes draw { to { stroke-dashoffset: 0; } }
@keyframes pop { to { opacity: 1; transform: scale(1); } }
@keyframes halo { to { opacity: .18; transform: scale(1); } }
@keyframes fade { to { opacity: 1; } }
@keyframes grow { to { transform: scaleY(1); } }

/* M8: theme crossfade (View Transitions) */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 240ms; animation-timing-function: ease; }

/* ---------- S1: page progress along the header's bottom edge ---------- */
.progress { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; pointer-events: none; }
.progress span { display: block; height: 100%; background: var(--accent); transform-origin: left; transform: scaleX(0); }
html:not(.motion) .progress { display: none; }

/* ---------- S2: the hero drifts apart as you leave it ---------- */
.motion .hero .display { transform: translateY(calc(var(--e, 0) * 120px)); }
.motion .hero .lede, .motion .hero .actions { transform: translateY(calc(var(--e, 0) * 70px)); opacity: calc(1 - var(--e, 0) * 1.1); }
.motion .hero .converge-wrap { transform: translateY(calc(var(--e, 0) * -60px)) scale(calc(1 + var(--e, 0) * .3)); opacity: calc(1 - var(--e, 0) * 1.3); }

/* ---------- the thread follows the reading line ---------- */
@media (min-width: 900px) {
  .motion .beat .wrap::before { transform: scaleY(var(--tip, 0)); }
  .motion .beat:not(.is-in) .node { transform: scale(.6); }
}

/* ---------- S3: the statement fills word by word ---------- */
.motion .words .w { opacity: clamp(.2, calc(var(--p, 1) * var(--n) * 1.7 - var(--n) * .35 - var(--i) + 1), 1); }

/* ---------- S4: the phone tilts upright, the conversation builds ---------- */
.motion .flagship .phone { transform: perspective(900px) translateY(calc((.5 - var(--p, .5)) * 90px)) rotateX(calc(clamp(-.1, .42 - var(--p, .5), .5) * 36deg)); }
.motion .flagship .bubble, .motion .flagship .sched {
  opacity: clamp(0, calc((var(--p, 1) - .3 - var(--i) * .06) * 14), 1);
  transform: translateY(calc(clamp(0, .3 + var(--i) * .06 - var(--p, 1), .07) * 160px));
}

/* ---------- S5: step lines light up; pinned process with the active-step panel (desktop) ---------- */
.motion .step:not(.on) > * { opacity: .35; transform: translateY(8px); }
.motion .step.on::before { transform: scale(1.25); }
@media (min-width: 900px) {
  .motion .beat-process { height: 220vh; padding: 0; }
  .motion .beat-process .pin { position: sticky; top: 66px; min-height: calc(100vh - 66px); display: flex; align-items: center; padding: var(--s-8) 0; }
  .motion .beat-process .pin > .wrap { flex: 1; }
  /* a pinned wrap would drag its rail along, so the rail lives on the section instead */
  .motion .beat-process .wrap::before { display: none; }
  .motion .beat-process::before {
    content: ""; position: absolute; top: 0; bottom: 0; width: 2px; background: var(--thread);
    left: calc(max(0px, (100% - var(--maxw)) / 2) + var(--pad) + 5px);
    transform-origin: top; transform: scaleY(var(--tip, 0));
  }
  .motion .beat-process .process::after { transform: scaleX(var(--pp, 0)); }
  .motion .beat-process .step { padding-top: var(--s-5); }
  .motion .beat-process .step p { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .motion .beat-process .step-detail {
    display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: end; gap: var(--s-7);
    margin-top: var(--s-8); min-height: 1.1em;
  }
  .motion .beat-process .process-next { display: flex; }
  .motion .beat-process .process-foot { margin-top: var(--s-8); padding-top: var(--s-5); border-top: 1px solid var(--line); }
}
@media (max-width: 899px) { .motion .beat-process .process::after { transform: scaleY(var(--pp, 0)); } }

/* ---------- S6: the story band slides both ways ---------- */
.motion .band-row.in { transform: translateX(calc(-30% + var(--p, .5) * 30%)); }   /* drifts right, into the page */
.motion .band-row.out { transform: translateX(calc(var(--p, .5) * -30%)); }        /* drifts left, out of it */

/* ---------- S7: reveals (headings unmask, blocks rise, staggered) ---------- */
.motion [data-reveal] { opacity: 0; transform: translateY(28px); transition: opacity 600ms ease, transform 900ms var(--ease-out); transition-delay: calc(var(--ri, 0) * 70ms); }
.motion .head h2[data-reveal] { clip-path: inset(0 0 100% 0); transform: translateY(.5em); transition: clip-path 900ms var(--ease-out), transform 900ms var(--ease-out), opacity 300ms ease; }
.motion [data-reveal].shown, .motion .head h2[data-reveal].shown { opacity: 1; transform: none; clip-path: inset(-10% -5% -10% -5%); }
.motion [data-reveal]:focus-within { opacity: 1; transform: none; clip-path: none; }   /* keyboard never lands on an invisible link */

/* ---------- S8: the CTA grows into place, the proof dot pulses once ---------- */
.motion .beat-last .cta-title { transform-origin: left bottom; transform: scale(calc(.84 + min(1, var(--p, 1) * 2.2) * .16)); }
.motion .beat-last.is-in .node-proof { animation: proofPulse 1200ms var(--ease-out) 1; }
@keyframes proofPulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent); }
  100% { box-shadow: 0 0 0 8px color-mix(in srgb, var(--accent) 18%, transparent); }
}

/* ---------- reduced motion: final state; only short opacity fades on load ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .display .line, .js .rise, .js .converge .cv-scatter circle { animation: fade 200ms ease forwards; transform: none; }
  .js .converge .cv-in, .js .converge .cv-out { animation: none; stroke-dashoffset: 0; }
  .js .converge .cv-proof, .js .converge .cv-halo { animation: none; opacity: 1; transform: none; }
  .js .converge .cv-halo { opacity: .18; }
  .js .converge .tile { animation: none; opacity: 1; transform: none; }
  .js .lead-in { transform: none !important; animation: none !important; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}
