/* ═══════════════════════════════════════════════════════════════════════════
   WULING KENDARI — motion.css
   Every animation here is either scroll-authored (JS writes a transform) or a
   one-shot entrance. Nothing loops forever, nothing bobs, nothing floats.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── chapter durations. Only applied when JS confirms the device can pin. ── */
.can-pin .ch1{height:300svh}
.can-pin .ch2{height:210svh}
.can-pin .ch3{height:290svh}
.can-pin .pin{position:sticky;top:0;height:100svh;min-height:0}

/* ── 00 · IGNITION ─────────────────────────────────────────────────────────
   Headline arrives through a mask, one line at a time. Runs from CSS so the
   type is guaranteed to land even if no JavaScript executes at all.          */
.msk>span{display:block;transform:translateY(104%);animation:mskUp .95s var(--ease) .55s forwards}
.msk:nth-child(2)>span{animation-delay:.66s}
.msk:nth-child(3)>span{animation-delay:.77s}
@keyframes mskUp{to{transform:none}}

.hero-sub,.hero-cta,.drive,.hero-place,.ch0 .ch__no{
  opacity:0;animation:quietIn .9s var(--ease) 1.15s forwards
}
.ch0 .ch__no{animation-delay:.35s}
.hero-place{animation-delay:1.3s}
@keyframes quietIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

/* the vehicle wakes up with the signal, not with a bounce */
.veh-cam{opacity:0;animation:vehIn 1.25s var(--ease) .30s forwards}
@keyframes vehIn{from{opacity:0;transform:translate3d(-50%,-46%,0) scale(1.06)}
                 to{opacity:1;transform:translate3d(-50%,-50%,0) scale(1)}}

/* the signal draws itself out from the centre before anything else appears */
.signal path{stroke-dasharray:120;stroke-dashoffset:120;animation:sigDraw 1.5s var(--ease-io) .1s forwards}
@keyframes sigDraw{to{stroke-dashoffset:0}}

/* ── 01/02 · scroll-revealed beats ────────────────────────────────────────
   State is toggled by the scroll engine at authored camera moments.          */
.beat,.scan__i{opacity:.16;transform:translateX(-14px);
  transition:opacity .7s var(--ease),transform .7s var(--ease)}
.beat.is-on,.scan__i.is-on{opacity:1;transform:none}
.beat.is-on .beat__v b{color:var(--sig)}
.beat__v b{transition:color .7s var(--ease)}

/* leader line that ties a beat to the point it describes on the car */
.beat.is-on::after{
  content:"";position:absolute;right:-26px;top:50%;width:0;height:1px;
  background:linear-gradient(90deg,var(--sig),transparent);
  animation:leader .8s var(--ease) forwards;
}
.beat{position:relative}
@keyframes leader{to{width:clamp(40px,8vw,120px)}}

/* ── 03 · RUNWAY ─────────────────────────────────────────────────────────
   Lanes share one visual centre: the leaving lane hands its scale to the next
   one, so the transition reads as a camera move rather than a card swap.     */
/* Scoped to .can-pin. Unscoped this rule loads after style.css and wins the
   cascade, which would hide every lane in the mobile scroll-snap strip. */
.can-pin .lane{
  opacity:0;
  transition:opacity .55s var(--ease),transform .7s var(--ease);
}
.can-pin .lane.is-near{opacity:.22}
.can-pin .lane.is-on{opacity:1}

/* ── 04 · ROAD ───────────────────────────────────────────────────────────── */
.rb{opacity:0;transform:translateY(26px);
  transition:opacity .9s var(--ease),transform .9s var(--ease)}
.rb.is-in{opacity:1;transform:none}

/* ── 05 · FINALE — the signal comes to rest under the CTA ─────────────────── */
.finale__u{transition:width .9s var(--ease)}
.finale.is-in .finale__u{width:min(320px,72vw)}

/* ── LEAD DRAWER ─────────────────────────────────────────────────────────── */
.lead__scrim{opacity:0;transition:opacity .4s var(--ease)}
.lead__panel{opacity:0;transform:translateY(24px) scale(.985);
  transition:opacity .5s var(--ease),transform .6s var(--ease)}
.lead.is-open .lead__scrim{opacity:1}
.lead.is-open .lead__panel{opacity:1;transform:none}
.step.is-on{animation:stepIn .45s var(--ease)}
@keyframes stepIn{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}

/* ═══════════════════════════════════════════════════════════════════════════
   BOOT FAILURE SAFETY NET
   If app.js never runs — the page was opened straight off disk so ES modules
   are blocked, a script 404'd, or something threw — an inline timer adds
   .no-boot. Everything that was waiting to be revealed by JavaScript is shown
   immediately. The page stays a complete, readable, converting document.
   ═════════════════════════════════════════════════════════════════════════ */
.no-boot .beat,
.no-boot .scan__i,
.no-boot .rb,
.no-boot .lane{opacity:1;transform:none}
.no-boot .beat.is-on::after{display:none}
.no-boot .finale__u{width:min(320px,72vw)}
.no-boot .veh-cam{opacity:1}
.no-boot .msk>span{transform:none;animation:none}

/* ═══════════════════════════════════════════════════════════════════════════
   REDUCED MOTION
   Every chapter collapses into a static editorial composition. No pinning, no
   scrubbing, no parallax — but every fact, image and CTA stays on the page.
   ═════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important;scroll-behavior:auto!important
  }
  .msk>span{transform:none}
  .veh-cam{opacity:1;transform:translate3d(-50%,-50%,0)}
  .signal path{stroke-dasharray:none;stroke-dashoffset:0}
  .hero-sub,.hero-cta,.drive,.hero-place,.ch0 .ch__no{opacity:1;transform:none}
  .beat,.scan__i,.rb{opacity:1;transform:none}
  .beat.is-on::after{display:none}
  .grain{display:none}
  .finale__u{width:min(320px,72vw)}

  /* chapters become stacked static scenes */
  .can-pin .ch1,.can-pin .ch2,.can-pin .ch3{height:auto}
  .can-pin .pin{position:static;height:auto;min-height:0;padding:70px 0}
  .veh-layer{position:relative;inset:auto;height:auto;margin:0 auto}
  .veh-cam{position:relative;left:auto;top:auto;transform:none;width:min(92vw,760px);margin:0 auto}
  .lane{position:relative;left:auto;top:auto;width:auto;opacity:1;height:auto;
    padding-bottom:30px;border-bottom:1px solid var(--hair-2);margin-bottom:30px}
  .runway{height:auto;display:grid;gap:0}
}
