/* Motion layer M1-M8 (DESIGN §9). Everything here is opt-in: hidden start states exist only under html.motion (set by the inline head script when reduced
   motion is off and the URL has no ?static) or on [data-reveal] (added by JS). Only transform, translate, scale and opacity animate. */
:root{--ease-out:cubic-bezier(.16,1,.3,1);--ease-soft:cubic-bezier(.4,0,.2,1)}

/* Slab blur is one property: the frosted-glass re-blur is the costly part on phones, so it drops to 8px on small and touch screens. */
@media (max-width:920px),(hover:none){html.motion{--slab-blur:8px}}

/* M2 · Role slabs assemble on load (home roster, role-page slab and its ghosts). The art layer (M1) is styled in styles.css. */
html.motion .roster .slab,html.motion .slab-wrap .slab,html.motion .slab-wrap .slab-ghost{opacity:0;translate:var(--from,0 24px);scale:.94}
html.motion .roster .slab:nth-of-type(1){--from:-90px -40px}
html.motion .roster .slab:nth-of-type(2){--from:110px 30px}
html.motion .roster .slab:nth-of-type(3){--from:-40px 110px}
html.motion .slab-wrap .slab-ghost{--from:-26px -22px;scale:1}
html.motion .slab-wrap .slab-ghost:nth-child(2){--from:-52px -44px}
html.motion.is-assembled .roster .slab,html.motion.is-assembled .slab-wrap .slab,html.motion.is-assembled .slab-wrap .slab-ghost{opacity:1;translate:0 0;scale:1;transition:opacity .5s var(--ease-soft) var(--d,0ms),translate .95s var(--ease-out) var(--d,0ms),scale .95s var(--ease-out) var(--d,0ms)}
html.motion.is-assembled .slab-wrap .slab-ghost:nth-child(2){opacity:.6}
html.motion .roster .slab:nth-of-type(1),html.motion .slab-wrap .slab{--d:180ms}
html.motion .roster .slab:nth-of-type(2),html.motion .slab-wrap .slab-ghost:nth-child(2){--d:300ms}
html.motion .roster .slab:nth-of-type(3),html.motion .slab-wrap .slab-ghost:nth-child(3){--d:420ms}
/* After assembly JS drives translate per frame (M3), so the transition goes. */
html.motion.is-settled .roster .slab,html.motion.is-settled .slab-wrap .slab{transition:none}
/* Failsafe: if motion.js never runs (blocked, failed), the head script adds motion-failsafe after 2.5 s and the slabs show. */
html.motion-failsafe:not(.motion-on) .roster .slab,html.motion-failsafe:not(.motion-on) .slab-wrap .slab,html.motion-failsafe:not(.motion-on) .slab-wrap .slab-ghost{opacity:1;translate:0 0;scale:1}
html.motion-failsafe:not(.motion-on) .slab-wrap .slab-ghost:nth-child(2){opacity:.6}

/* "Embedded" badge: one soft pulse on the dot, twice per slab, after it lands */
.slab-role small{position:relative}
html.motion .slab-role small::before{content:"";position:absolute;left:-12px;top:50%;width:6px;height:6px;margin-top:-3px;border-radius:50%;background:#7cf3ff;opacity:0}
html.motion.is-assembled .slab-role small::before{animation:live-dot 1.6s var(--ease-soft) calc(var(--d,0ms) + 900ms) 2 both}
@keyframes live-dot{0%{opacity:0;scale:.6}30%{opacity:1;scale:1}100%{opacity:0;scale:2.4}}

/* M4 · Proof figures roll into place (odometer); the plain text stays in .visually-hidden */
.odo{display:inline-block;height:1em;overflow:hidden;vertical-align:top;line-height:1}
.odo-col{display:block;transform:translateY(0)}
.odo-wrap.is-rolling .odo-col{will-change:transform}
.odo-col>span{display:block;height:1em;line-height:1}
.odo-wrap{font-variant-numeric:tabular-nums;white-space:nowrap}
.odo-wrap.is-in .odo-col{transform:translateY(calc(var(--n) * -1em));transition:transform 1.5s var(--ease-out) var(--od,0ms)}
.hero-figures b .odo{height:1.1em}
.hero-figures b .odo-col>span{height:1.1em;line-height:1.1}
.hero-figures b .odo-wrap.is-in .odo-col{transform:translateY(calc(var(--n) * -1.1em))}
.hero-figures b span{display:inline;font-size:inherit;color:inherit;line-height:inherit}
.hero-figures b .odo-wrap{display:inline}
.hero-figures b .odo{display:inline-block}
.hero-figures b .odo-col,.hero-figures b .odo-col>span{display:block}

/* M6 · Sections and cards rise in once as they enter the viewport */
html.motion [data-reveal]{opacity:0;translate:0 18px}
html.motion [data-reveal].is-in{opacity:1;translate:0 0;transition:opacity .6s var(--ease-soft) var(--rd,0ms),translate .8s var(--ease-out) var(--rd,0ms)}

html.motion [data-reveal].is-in.is-instant{transition:none}

/* M7 · How it works: each step's own rule fills in turn as you scroll; steps not reached yet are dimmed. Desktop only. */
.step-fill{display:none}
@media (min-width:921px){
  html.motion .step-fill{display:block;position:absolute;top:8px;left:36px;right:18px;height:2px;background:var(--blue);transform-origin:left center;transform:scaleX(var(--f,0));pointer-events:none}
  html.motion .steps li h3,html.motion .steps li p{transition:opacity .4s var(--ease-soft)}
  html.motion .steps.is-tracking li:not(.is-on) h3,html.motion .steps.is-tracking li:not(.is-on) p{opacity:.38}
}

/* M8 · The three ownership split bars grow together, once, so no option reads as selected */
html.motion .ownership .split i{transform:scaleX(0)}
html.motion .ownership.is-in .split i{transform:scaleX(var(--db));transition:transform .9s var(--ease-out) .15s}

/* ≤720px: smaller travel, no live-dot pulse (M3 and M7 are already off on touch and below 921px) */
@media (max-width:720px){
  html.motion .roster .slab:nth-of-type(1){--from:-30px -16px}
  html.motion .roster .slab:nth-of-type(2){--from:40px 12px}
  html.motion .roster .slab:nth-of-type(3){--from:-16px 40px}
  html.motion [data-reveal]{translate:0 10px}
  html.motion.is-assembled .slab-role small::before{animation:none}
}

/* Reduced motion: JS never adds html.motion in this case; if the preference flips mid-visit everything snaps to its end state. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
  .blue-cover .cover-art::before{transform:none!important}
  html.motion [data-reveal],html.motion .roster .slab,html.motion .slab-wrap .slab,html.motion .slab-wrap .slab-ghost{opacity:1!important;translate:none!important;scale:none!important}
  html.motion .ownership .split i{transform:scaleX(var(--db))}
  .step-fill{display:none!important}
  html.motion .steps li h3,html.motion .steps li p{opacity:1!important}
}

/* QA: ?static (html.no-motion) forces every end state, no animation. */
html.no-motion *,html.no-motion *::before,html.no-motion *::after{animation:none!important;transition:none!important}
html.no-motion .blue-cover .cover-art::before{transform:none!important}
