/* motion system (S4) — motion-design-token-system; 1–2 animated elements per view; stagger 70 ms; ease-out in; reduced motion neutralises scroll-linked motion (prefers-reduced-motion leaf, owner exemption for the ambient scene lives in JS) */
.reveal{opacity:0;transform:translateY(14px);transition:opacity var(--dur-slow) var(--ease-out),transform var(--dur-slow) var(--ease-out)}
.reveal.in{opacity:1;transform:none}
/* the hero copy holds the LCP element (h1 on desktop, the paragraph on phones): it never starts at opacity 0 (Chrome reports a fading LCP only when the fade ends) — it slides in with the fonts, the rest fades after it */
#hero .reveal{opacity:1;transition:transform var(--dur-slow) var(--ease-out)}
.reveal:nth-child(2){transition-delay:var(--stagger)} .reveal:nth-child(3){transition-delay:calc(var(--stagger)*2)} .reveal:nth-child(4){transition-delay:calc(var(--stagger)*3)} .reveal:nth-child(5){transition-delay:calc(var(--stagger)*4)}
.chapter > .wrap > *{opacity:0;transform:translateY(12px);transition:opacity var(--dur-slow) var(--ease-out),transform var(--dur-slow) var(--ease-out)}
.chapter.in > .wrap > *{opacity:1;transform:none} .chapter.in > .wrap > *:nth-child(2){transition-delay:var(--stagger)}
.tilt .card{will-change:transform}
.btn-primary{position:relative;overflow:hidden}
.btn-primary::after{content:"";position:absolute;inset:0;background:radial-gradient(120px 60px at var(--mx,50%) var(--my,50%),rgba(255,255,255,.28),transparent 60%);opacity:0;transition:opacity var(--dur-fast)}
.btn-primary:hover::after{opacity:1}
@media (prefers-reduced-motion: reduce){
  .reveal,.chapter > .wrap > *{opacity:1;transform:none;transition:none}
  .tilt .card{transform:none!important;transition:none}
  .btn,.btn::after,.faq summary::after{transition:none}
  html{scroll-behavior:auto}
}
