/* Nexla motion system. Tokens + 10 moves. transform/opacity/clip-path only. */
:root{--m-ease:cubic-bezier(.22,1,.36,1);--m-wipe:cubic-bezier(.7,0,.2,1);--m-d:700ms;--m-accent:#6154FF}
.js [data-m]{--i:0}
/* 1 Mask Rise: <h1 data-m="rise"><span class="ln"><span>Line</span></span>...</h1> */
.js [data-m=rise] .ln{display:block;overflow:clip;padding-bottom:.08em;margin-bottom:-.08em}
.js [data-m=rise] .ln>span{display:block;transform:translateY(105%);transition:transform 800ms var(--m-ease) calc(var(--i)*90ms)}
.js [data-m=rise].in .ln>span{transform:none}
/* 2 Swipe Reveal: image/chart/card uncovered left to right */
.js [data-m=swipe]{clip-path:inset(0 100% 0 0);transition:clip-path 750ms var(--m-wipe) calc(var(--i)*80ms)}
.js [data-m=swipe].in{clip-path:inset(0 0 0 0)}
/* 3 Section Push: block slides up 28px + fades; children stagger via data-stagger */
.js [data-m=push],.js [data-stagger]>*{opacity:0;transform:translateY(28px);transition:opacity 600ms ease-out calc(var(--i)*70ms),transform 700ms var(--m-ease) calc(var(--i)*70ms)}
.js [data-m=push].in,.js [data-stagger].in>*{opacity:1;transform:none}
/* 4 Slide-In (side swipe for cards/columns): data-m="from-r" or "from-l" */
.js [data-m=from-r],.js [data-m=from-l]{opacity:0;transform:translateX(48px);transition:opacity 500ms ease-out calc(var(--i)*80ms),transform 750ms var(--m-ease) calc(var(--i)*80ms)}
.js [data-m=from-l]{transform:translateX(-48px)}
.js [data-m=from-r].in,.js [data-m=from-l].in{opacity:1;transform:none}
/* 5 Card Swipe Deck: <div class="deck" data-m="deck"> cards stacked, each slides in from right and settles with 8px offset */
.deck{display:grid}.deck>*{grid-area:1/1}
.js [data-m=deck]>*{opacity:0;transform:translateX(64px);transition:opacity 450ms ease-out calc(var(--i)*140ms),transform 800ms var(--m-ease) calc(var(--i)*140ms)}
.js [data-m=deck].in>*{opacity:1;transform:translate(calc(var(--i)*-8px),calc(var(--i)*8px))}
/* 6 Horizontal Rail: native scroll-snap, JS adds mouse drag */
.rail{display:flex;gap:16px;overflow-x:auto;scroll-snap-type:x proximity;overscroll-behavior-x:contain;scrollbar-width:thin}.rail>*{flex:0 0 min(80%,360px);scroll-snap-align:start}.rail.drag{cursor:grabbing;scroll-snap-type:none;user-select:none}
/* 7 Ticker: duplicate children once in markup; pauses on hover/focus */
.ticker{overflow:hidden}.ticker>div{display:flex;gap:48px;width:max-content;animation:tick 40s linear infinite}.ticker:hover>div{animation-play-state:paused}
@keyframes tick{to{transform:translateX(-50%)}}
/* 8 Line Draw: SVG path/line with pathLength="1" */
.js [data-m=draw] path,.js [data-m=draw] line,.js [data-m=draw] polyline{stroke-dasharray:1;stroke-dashoffset:1;transition:stroke-dashoffset 1100ms var(--m-wipe) calc(var(--i)*100ms)}
.js [data-m=draw].in path,.js [data-m=draw].in line,.js [data-m=draw].in polyline{stroke-dashoffset:0}
/* 9 Bar Sweep: scaleX bars, set --w:.327 */
.js [data-m=bar]{transform-origin:0 50%;transform:scaleX(0);transition:transform 900ms var(--m-wipe) calc(var(--i)*80ms)}
.js [data-m=bar].in{transform:scaleX(var(--w,1))}
/* 10 Parallax Drift: JS sets --dy (max +-24px); only on big decorative layers */
[data-drift]{transform:translate3d(0,var(--dy,0),0)}
/* Underline sweep for links/CTA */
.m-link{background:linear-gradient(currentColor,currentColor) 0 100%/0 1px no-repeat;transition:background-size 450ms var(--m-wipe)}.m-link:hover{background-size:100% 1px}
/* Reduced motion: final state instantly */
@media (prefers-reduced-motion:reduce){.js [data-m],.js [data-m] *,.js [data-stagger]>*,.js [data-m=deck]>*{transition:none!important;animation:none!important;opacity:1!important;transform:none!important;clip-path:none!important;stroke-dashoffset:0!important}.js [data-m=bar]{transform:scaleX(var(--w,1))!important}.ticker>div{animation:none}[data-drift]{transform:none}}
