/* ==================================================================
   WEBLOVE — one object, five skins.
   Engine stylesheet. Everything here is dormant: sections carry
   .is-empty until their real content exists, then wake one by one.

   Buckets (BUILD PROMPT §6):
     MEASURED  — rewritten from their art by scripts, never by hand
     GENERATED — from their brand (P2 onward)
     SHIPPED   — do not edit; tuning constants, each with a reason
   ================================================================== */

:root{
  /* GENERATED (§6-G) — SAMPLED from assets/backdrop.png by scripts/palette.py
     (2026-09-05, final source). Edge rows: top #B0B0B0 · mid #D5D5D5 ·
     bot #B6B6B6; mean luma 190.3/255.
     Ladder: ink 9.02:1 · ink-2 7.63:1 · ink-3 6.73:1 on the studio grey. */
  --surround-top:#B0B0B0;
  --surround:#D5D5D5;
  --surround-bot:#B6B6B6;
  --studio:#BEBEBE;
  --ink:#1E1E1E;
  --ink-2:#2B2B2B;
  --ink-3:#343434;
  --rule:rgba(30,30,30,.26);
  /* GENERATED (M8) — skin 1's own paint, derived from object.png:
     raw #81776B (passed the clamp untouched, L=120.6) · ink 3.54:1 on the
     backdrop. Re-set per skin by app.js label() from P4 on. */
  --accent:#81776B;
  --accent-ink:#6B6359;

  /* GENERATED — font roles (P2), matched from the user's Pinterest pick:
     Young Serif (display) · Instrument Sans (body + digits, tabular-nums)
     · Yellowtail (signature). */
  --font-display:"Young Serif", Georgia, serif;
  --font-body:"Instrument Sans", system-ui, "Segoe UI", sans-serif;
  --font-digits:"Instrument Sans", system-ui, "Segoe UI", sans-serif;
  --font-script:"Yellowtail", "Segoe Script", cursive;

  /* SHIPPED (§6 bucket U) */
  --dissolve:1500ms;      /* the fog lift — shorter reads as a cut */
  --swap:420ms;           /* the recolour — longer lags a fast flick */
  --stage-scale:1;        /* intro sits at 1.02 and eases to 1 */
  --px:0;                 /* pointer parallax, written by app.js */
  --py:0;
  --nav-h:64px;           /* M2's clearance check depends on this */
  --ease:cubic-bezier(.22,.61,.36,1);
}

/* ---------- ground rules ---------- */
*{ box-sizing:border-box; }
/* a skin leaving the stage must never widen the page. overflow-x:clip
   clips WITHOUT forcing overflow-y:auto, so it doesn't break the footer's
   position:sticky (hidden does). hidden is the fallback for old browsers. */
html,body{ margin:0; overflow-x:hidden; overflow-x:clip; }
body{
  background:var(--surround);
  background:linear-gradient(to bottom, var(--surround-top), var(--surround) 50%, var(--surround-bot));
  color:var(--ink);
  font-family:var(--font-body);
  -webkit-font-smoothing:antialiased;
}
img{ max-width:100%; display:block; }

/* Hide with display:none, never the [hidden] attribute — any layout class
   outranks the browser's [hidden] rule. */
.is-empty{ display:none !important; }

.visually-hidden{
  position:absolute; width:1px; height:1px; margin:-1px;
  clip-path:inset(50%); overflow:hidden; white-space:nowrap;
}

:focus-visible{ outline:2px solid var(--accent-ink); outline-offset:2px; }

/* Lock scrolling while the film runs (M9) */
.is-intro, .is-intro body{ overflow:hidden; height:100%; }

/* ---------- nav ---------- */
.nav{
  position:fixed; inset:0 0 auto 0; height:var(--nav-h); z-index:60;
  display:flex; align-items:center; justify-content:space-between;
  padding:0 clamp(16px, 3vw, 40px);
  pointer-events:none;
}
.nav a, .nav button{ pointer-events:auto; }
.nav__mark{
  font-family:var(--font-display); font-weight:800;
  letter-spacing:.06em; text-decoration:none; color:var(--ink);
}
.nav__replay{
  font:inherit; font-size:.8rem; letter-spacing:.08em; text-transform:uppercase;
  background:none; border:1px solid var(--rule); border-radius:999px;
  padding:.5em 1.1em; color:var(--ink-2); cursor:pointer;
}
/* Must be visibility:hidden while the film runs, not merely transparent —
   an invisible but focusable button takes Tab, and Enter restarts the film. */
.is-intro .nav__replay{ visibility:hidden; }
@media (max-width:640px){ :root{ --nav-h:56px; } }

/* ---------- the hero and the stage (M1) ---------- */
.hero{
  position:relative;
  height:100vh;
  height:100svh;
  overflow:hidden;
  touch-action:pan-y pinch-zoom;  /* horizontal drags are ours; vertical pan and pinch stay the browser's */
}

/* SHIPPED as written — the one 16:9 box every layer shares.
   width:max(100%,…) and never 100vw: vw includes the scrollbar, and the
   stage would jump by its width at the exact moment of the handoff. */
.stage{
  position:absolute; top:50%; left:50%;
  width:max(100%, calc(100vh * 16 / 9));
  width:max(100%, calc(100svh * 16 / 9));
  aspect-ratio:16 / 9;
  transform:translate(-50%,-50%) scale(var(--stage-scale));
  transform-origin:50% 50%;
  container-type:inline-size;
  pointer-events:none;
  -webkit-user-select:none; user-select:none;
}
.stage img{ -webkit-user-drag:none; }
.layer, .film, .car{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover; object-position:center;
}

/* Backdrop and skins move TOGETHER under the pointer (M7) — the object's
   shadow is baked into the backdrop, so they must never separate. */
.layer--backdrop, .cars{
  transform:translate3d(calc(var(--px) * 9px), calc(var(--py) * 5px), 0);
}
.cars{ position:absolute; inset:0; }

/* The skins. app.js drives visibility + transform per frame, written
   straight onto each element. .is-on is the no-JS cross-fade fallback. */
.car{ opacity:0; transition:opacity .5s ease; }
.car.is-on{ opacity:1; }
.js .car{ opacity:1; transition:none; visibility:hidden; will-change:transform; }
/* MEASURED (§6-M): scale pivot = ink vertical midpoint 70.5% + 4 points.
   Pivoting at the base would read as sinking into the floor, not receding. */
.car{ transform-origin:50% 74.5%; }

/* ---------- the poster type (M2) — behind the skins, cropped by the body ---------- */
.poster{
  position:absolute; inset:0; opacity:0; pointer-events:none;
  transition:opacity 1.1s ease;
}
.is-lit .poster{ opacity:1; transition-delay:.35s; }
.poster__slide{
  position:absolute; inset:0; transform:translate3d(0,0,0);
  will-change:transform;                /* app.js drives this — the lag (M6) */
}
.poster__n{
  position:absolute;
  left:50%; top:34%;                    /* user-directed (matches the demo): head bleeds off the top of
                                           the frame, foot ends inside the body — never below the car */
  font-family:var(--font-display);
  font-size:55cqw; line-height:.78; letter-spacing:-.02em;  /* user: "as big as the room" */
  /* GENERATED: Young Serif ships one weight (400); asking for 900 would
     synthesize a smeared bold. The reference's 900/125% belonged to its font. */
  font-weight:400; font-stretch:normal;
  color:var(--accent); opacity:.26;
  transition:color var(--swap) ease;
  transform:translate(-50%,-50%)
            translate3d(calc(var(--px) * -26px), calc(var(--py) * -15px), 0);
}
.poster__script{
  position:absolute;
  left:44%; top:42%;                    /* user-directed: riding the roofline (INK_T 50.44%) — the word's
                                           tail tucks behind the roof, the rest rides above it */
  font-family:var(--font-script); font-size:13cqw; line-height:1;
  color:var(--accent);
  transition:color var(--swap) ease;
  transform:translate(-50%,-50%) rotate(-4deg)
            translate3d(calc(var(--px) * -13px), calc(var(--py) * -8px), 0);
}

/* ---------- the freeze frame and the handoff (M9) ---------- */
/* Order matters: .is-dissolving must come after .is-intro — equal
   specificity, source order decides. */
.layer--freeze{ opacity:0; }
.is-intro .layer--freeze{ opacity:1; }
.is-dissolving .layer--freeze{ opacity:0; transition:opacity var(--dissolve) linear; }
/* Belt and braces: once the dissolve should be over, retire the freeze frame
   outright, in case the transition is ever interrupted or throttled. */
.is-settled .layer--freeze{ opacity:0 !important; visibility:hidden; }

/* ---------- the intro overlay (film-gated, mounted in P6) ---------- */
.intro{
  position:fixed; inset:0; z-index:100; background:#000;
  cursor:pointer;
}
html:not(.is-intro) .intro{ display:none; }
.intro .stage{ pointer-events:none; }

/* ---------- hero UI ---------- */
.hero__ui{
  position:absolute; inset:auto 0 0 0; z-index:3;
  display:flex; flex-direction:column; align-items:center; gap:.6rem;
  padding:clamp(40px,8vh,90px) clamp(16px, 4vw, 48px) clamp(16px, 3vh, 30px);
  pointer-events:none;                  /* buttons opt back in */
}
/* Soft scrim so the bottom text stays readable over the car (M13). */
.hero__ui::before{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:linear-gradient(to top,
    var(--surround-bot) 0%,
    color-mix(in srgb, var(--surround-bot) 72%, transparent) 45%,
    transparent 100%);
}
.hero__ui button{ pointer-events:auto; }
/* Eyebrow sits at the TOP of the hero (direct child of .hero, not .hero__ui). */
.hero__eyebrow{
  position:absolute; top:calc(var(--nav-h) + 10px); left:50%;
  transform:translateX(-50%); z-index:3; margin:0;
  font-size:.78rem; letter-spacing:.22em; text-transform:uppercase;
  color:var(--ink-3); text-align:center; white-space:nowrap;
}
.hero__note{ margin:0; font-size:.85rem; color:var(--ink-3); text-align:center; }

/* Fixed three-column grid on a fixed width, so the arrows do not move
   when the skin's name changes length (M13). */
.bar{
  display:grid; grid-template-columns:44px 16ch 44px;
  align-items:center; gap:.5rem;
}
.bar__arrow{
  width:44px; height:44px; min-width:44px;    /* 44×44 minimum */
  display:grid; place-items:center;
  font-size:1.1rem; line-height:1;
  background:none; border:1px solid var(--rule); border-radius:50%;
  color:var(--accent-ink); cursor:pointer;
  transition:color var(--swap) ease, border-color var(--swap) ease;
}
.bar__mid{ text-align:center; }
.bar__name{
  display:block; font-family:var(--font-display); font-weight:700;
  font-size:1rem; color:var(--accent-ink);
  transition:color var(--swap) ease;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.bar__idx{
  display:block; font-family:var(--font-digits);
  font-variant-numeric:tabular-nums;
  font-size:.72rem; letter-spacing:.18em; color:var(--ink-3);
}

/* the cue now sits in the hero__ui flow, below the bar — no overlap */
.cue{ margin:.1rem 0 0; font-size:.72rem; letter-spacing:.16em;
      text-transform:uppercase; color:var(--ink-3); text-align:center; }
.legal{ position:absolute; bottom:12px; right:clamp(16px,3vw,40px);
        font-size:.68rem; color:var(--ink-3); z-index:3; }

/* ---------- the rise (M11) ---------- */
[data-rise]{
  opacity:0; transform:translateY(18px);
  transition:opacity .7s var(--ease), transform .7s var(--ease);
}
[data-rise].up{ opacity:1; transform:none; }
html:not(.js) [data-rise]{ opacity:1; transform:none; }

/* ---------- the detail plate (M12) — a printed plate, per skin ---------- */
.sak{
  /* the five tokens are set per skin by app.js from the derived data */
  --paper:#ffffff; --band:#eeeeee;
  --sak-ink:#131313; --sak-ink-2:#4a4a4a; --sak-accent:#888888;
  position:relative;
  isolation:isolate;      /* lets the art (z:-2) and column (z:-1) paint above this bg */
  background:var(--paper);
  color:var(--sak-ink);
  overflow:hidden;
  box-sizing:border-box;
  /* Fit the whole plate in one viewport: fill the screen and centre the
     content, so the top text and the spec sheet are both visible at once. */
  min-height:100vh;
  display:flex; flex-direction:column; justify-content:center;
  padding:clamp(24px, 4vh, 56px) clamp(16px, 5vw, 72px);
  touch-action:pan-y;     /* horizontal swipes drive the track; vertical scrolls */
  -webkit-user-select:none; user-select:none;
}
.sak__art{
  position:absolute; z-index:-2; inset:0;
  opacity:.14; object-fit:cover; width:100%; height:100%;
}
.sak__band{
  position:absolute; z-index:-1; top:0; bottom:0; left:50%;
  transform:translateX(-50%);
  width:clamp(224px, 25vw, 360px);
  background:var(--band);
}
.sak__inner{ position:relative; z-index:1; max-width:1200px; margin-inline:auto; }

/* The monogram — 既快又出彩 ("both fast and brilliant"), used as a giant
   tint-mask, not a flat background: blown up, bled off the edges, painted in
   this skin's own ink, a faint tone-on-tone editorial watermark. Recolours
   per skin with the palette, so it reads as designed, not stuck-on. */
.sak__monogram{
  position:absolute; z-index:0; pointer-events:none;
  top:50%; right:-14%; transform:translateY(-50%);
  width:min(150%, 1700px); aspect-ratio:1680 / 944;
  background-color:var(--sak-ink);
  -webkit-mask:url(assets/monogram.webp) no-repeat center / contain;
          mask:url(assets/monogram.webp) no-repeat center / contain;
  opacity:.05;
}
@media (max-width:720px){ .sak__monogram{ width:200%; right:-40%; opacity:.045; } }

/* Untreated skins present their one hero photo; treated skins show the grid.
   Exactly one of .sak__solo / .sak__grid is visible per skin. */
.sak__solo{ margin:0 auto; max-width:min(760px, 82vw); text-align:center; }
.sak__solo img{ width:100%; height:auto; }
.sak--treated .sak__solo{ display:none; }
.sak:not(.sak--treated) .sak__grid{ display:none; }
.sak:not(.sak--treated) .sak__band{ display:none; }  /* no orphan colour stripe */
.sak__solo .sak__finish{ margin-top:.75rem; }
.sak__eyebrow{
  margin:0 0 .5rem; text-align:center;
  font-size:.78rem; letter-spacing:.22em; text-transform:uppercase;
  color:var(--sak-ink-2);
}
.sak__wordmark{
  margin:0 0 clamp(12px,2.4vh,30px); text-align:center;
  font-family:var(--font-display); font-weight:400;
  font-size:clamp(1.5rem,3.6vw,2.6rem); letter-spacing:.02em;
  color:var(--sak-ink);
}
.sak__grid{
  display:grid; grid-template-columns:1fr auto 1fr;
  align-items:center; gap:clamp(16px,3vw,48px);
}
/* front/rear grow a little (the middle top-view is now smaller) */
.sak__view img{ width:100%; height:auto; }
.sak__view{ margin:0; max-width:420px; margin-inline:auto; }
.sak__column{ text-align:center; }
.sak__model{
  margin:0 0 .6rem; font-family:var(--font-display);
  font-size:clamp(1rem,1.6vw,1.3rem); color:var(--sak-ink);
}
.sak__finish{
  margin:.6rem 0 0; font-size:.85rem; letter-spacing:.04em;
  color:var(--sak-ink-2);
}
.sak__lead{
  max-width:36ch; margin:clamp(16px,3vh,36px) auto 0; text-align:center;
  font-size:clamp(1rem,1.6vw,1.2rem); line-height:1.45; color:var(--sak-ink);
}
.sak__specs{
  display:flex; justify-content:center; gap:clamp(28px,6vw,88px);
  margin:clamp(16px,2.6vh,32px) 0 0; text-align:center;
}
.sak__specs dt{
  font-size:.72rem; letter-spacing:.16em; text-transform:uppercase;
  color:var(--sak-ink-2);
}
.sak__specs dd{
  margin:.3rem 0 0; font-family:var(--font-digits);
  font-variant-numeric:tabular-nums;
  font-size:clamp(1.2rem,2.4vw,1.8rem); color:var(--sak-accent);
}
@media (max-width:720px){
  .sak__grid{ grid-template-columns:1fr; }
  .sak__view{ max-width:70%; margin-inline:auto; }
}

/* The rotated top view — exact arithmetic, not a guess (M12).
   MEASURED from assets/view-top.webp (cropped to ink 1664×637, 2026-09-06):
   frame ratio = H/W = 637/1664; image widened by W/H = 1664/637. */
.sak__plateframe{
  position:relative; margin-inline:auto;
  /* Sized by HEIGHT so the whole plate fits one viewport; width follows from
     the per-skin aspect ratio (app.js sets --plate-ar). The colour strip
     (.sak__band) keeps its own width — only this image shrinks. */
  height:clamp(260px, 42vh, 480px); width:auto;
  aspect-ratio:var(--plate-ar, 0.3828);
}
.sak__plateframe img{
  position:absolute; top:50%; left:50%;
  /* max-width:none, or the global img{max-width:100%} clamps this back to the
     frame width and the rotation no longer fills it */
  width:calc(100% * var(--plate-wmult, 2.612)); max-width:none; height:auto;
  /* -90deg: nose points RIGHT in the source, so -90 stands it nose-UP */
  transform:translate(-50%,-50%) rotate(-90deg);
  transform-origin:center;
}
.sak__plateframe.is-empty{ display:none !important; }

/* ---------- the footer — scroll scrubs the burnout ---------- */
.burn{
  position:relative;
  height:280vh;                 /* scroll room the scrub is mapped across */
  background:#000;
}
.burn__sticky{
  position:sticky; top:0;
  height:100vh; overflow:hidden;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
}
.burn__video{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover; object-position:center;
}
.burn__scrim{
  position:absolute; inset:0;
  background:
    radial-gradient(120% 90% at 50% 60%, transparent 40%, rgba(0,0,0,.55) 100%),
    linear-gradient(to bottom, rgba(0,0,0,.35), transparent 30%, transparent 60%, rgba(0,0,0,.6));
}
.burn__quote{
  position:relative; margin:0 auto; text-align:center; z-index:2;
  padding:0 clamp(20px, 6vw, 64px);
  max-width:min(900px, 82vw);        /* a paragraph measure, not a stack */
}
.burn__quote blockquote{
  margin:0; color:#fff; text-wrap:balance;
  font-family:var(--font-display);
  font-size:clamp(1.5rem, 3.4vw, 3rem); line-height:1.18;
  text-shadow:0 2px 30px rgba(0,0,0,.6);
}
.burn__quote figcaption{
  margin-top:1.4rem; color:rgba(255,255,255,.75);
  font-size:.72rem; letter-spacing:.2em; text-transform:uppercase; line-height:1.6;
}
.burn__mark{
  position:absolute; left:50%; bottom:clamp(18px,4vh,44px);
  transform:translateX(-50%); z-index:2; margin:0;
  color:rgba(255,255,255,.6);
  font-family:var(--font-display); letter-spacing:.14em;
  font-size:.8rem;
}

/* ---------- portrait (M10) ---------- */
@media (max-aspect-ratio:4/5){
  .stage{ width:138%; }   /* SHIPPED: the letterboxed strip */
  .film, .layer--freeze, .layer--backdrop, .cars{
    mask-image:linear-gradient(to bottom, transparent 0, #000 6%, #000 94%, transparent 100%);
    -webkit-mask-image:linear-gradient(to bottom, transparent 0, #000 6%, #000 94%, transparent 100%);
  }
  /* the poster type stays unmasked — it is meant to spill past the strip */
}

/* ---------- reduced motion (M13) ---------- */
@media (prefers-reduced-motion:reduce){
  .intro{ display:none; }
  .layer--freeze{ opacity:0 !important; visibility:hidden; }
  .poster{ transition:none; }
  [data-rise]{ opacity:1; transform:none; transition:none; }
  .car{ transition:none; }
}
