@import url("../../../tokens/tokens.css");
@import url("../../css/dynamos.css");

/* ==========================================================================
   Site-level tokens (§B) — everything here derives from --dy-* brand tokens.
   No literal colour values in this file.
   ========================================================================== */
:root {
  --site-size-hero: clamp(72px, 9.5vw, 136px);
  --site-size-h1: 64px;
  --site-size-h2: 40px;
  --site-size-h3: 18px;
  --site-size-body: 17px;
  --site-size-label: 12px;
  --site-size-readout: 13px;

  --site-measure: 62ch;
  --site-section: 128px;
  --site-block: 40px;
  --site-para: 16px;
  --site-heading-gap: 24px;
  /* --site-section is the gap BETWEEN sections; each section pads half of it
     so adjacent sections meet at 128 px, not 256 px (audit finding 3). */
  --site-section-pad: calc(var(--site-section) / 2);
  --site-gutter: var(--dy-space-4);
  /* fallback until js/motion.js measures the real sticky-header height;
     the header is in normal flow so the hero must fill 100vh minus it,
     not 100vh on top of it. */
  --site-header-h: 57px;

  /* remap the shared component tokens to the site scale so every .dy-h1 /
     .dy-h2 / .dy-h3 / .dy-label / body-text picks it up, on every page. */
  --dy-size-h1: var(--site-size-h1);
  --dy-size-h2: var(--site-size-h2);
  --dy-size-h3: var(--site-size-h3);
  --dy-size-body: var(--site-size-body);
  --dy-size-label: var(--site-size-label);

  /* Persistent stage (§B) — shared contract with js/stage3d.js. */
  --stage-split: 48%;
  --stage-anchor: 0.74;
  --stage-band: 36vh;

  /* Story chapter pacing (STORYBOARD.md §1/§4) — 200vh holds the text block
     at +50vh with the low camera pass confined to the +150vh transit window;
     --chapter-h-long (.chapter--long) is the 240vh escape hatch for a
     chapter whose text+media block runs past 75vh (§4 names story-03). */
  --chapter-h: 200vh;
  --chapter-h-long: 240vh;
}
/* svh fallback: redefine the custom property itself under @supports, not the
   consuming declaration — a var() that's invalid at compute time resolves
   the whole property to its initial value, not the prior declaration, so a
   two-declaration fallback on `.stage { height: ... }` would silently drop
   the height rather than fall back. */
@supports (height: 1svh) {
  :root { --stage-band: 36svh; }
}

@media (max-width: 899px) {
  :root {
    --site-size-hero: clamp(44px, 13vw, 64px);
    --site-size-h1: 40px;
    --site-size-h2: 28px;
    --site-size-h3: 17px;
    --site-size-body: 16px;
    --site-size-label: 11px;
    --site-size-readout: 12px;

    --site-section: 72px;
    --site-gutter: 20px;

    --dy-size-h1: var(--site-size-h1);
    --dy-size-h2: var(--site-size-h2);
    --dy-size-h3: var(--site-size-h3);
    --dy-size-body: var(--site-size-body);
    --dy-size-label: var(--site-size-label);
  }
}

html, body { overflow-x: clip; }
.nowrap { white-space: nowrap; }

/* measure — every p, li, dd stays under the line-length cap */
p, li, dd { max-width: var(--site-measure); margin: 0; }
p + p { margin-top: var(--site-para); }

/* heading-to-first-paragraph rhythm */
.dy-h1 + p, .dy-h2 + p, .dy-h3 + p,
.dy-h1 + .dy-facts, .dy-h2 + .dy-facts,
.dy-h1 + ol, .dy-h2 + ol, .dy-h1 + ul, .dy-h2 + ul { margin-top: var(--site-heading-gap); }

/* section rhythm — overrides dynamos.css's fixed --dy-space-6 */
/* padding-block only: sections also carry .dy-page, whose gutter is inline.
   dynamos.css's own `.dy-section { padding: 64px 0 }` (line 39) comes after
   its `.dy-page` (line 33) and strips the gutter from any element carrying
   both classes, so the text touched the viewport edge below 1168 px — the
   gutter is restated here (shared-CSS bug, not fixed upstream). */
.dy-section { padding-block: var(--site-section-pad); }
.dy-page.dy-section { padding-inline: var(--site-gutter); }

/* page gutter: 24px desktop (token default), 20px mobile */
@media (max-width: 899px) {
  /* inline only — `padding: 0 20px` here zeroed .dy-section's vertical
     padding on every element carrying both classes (audit finding 9) */
  .dy-page { padding-inline: var(--site-gutter); }
}

/* dynamos.css's night-surface override (`[data-surface="night"] .dy-btn { color:
   var(--dy-night) }`) outranks `.dy-btn--ghost`'s own amber text colour by
   specificity, which makes ghost-button labels render night-on-night
   (invisible) on this site's all-night surface. Restore it here. */
[data-surface="night"] .dy-btn--ghost { color: var(--dy-accent); }

/* focus ring — every interactive element */
a:focus-visible, button:focus-visible, input:focus-visible,
textarea:focus-visible, select:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--dy-amber);
  outline-offset: 3px;
}
/* dynamos.css sets `.dy-field input:focus { outline: none }` at the same
   specificity; text fields show a ring on every focus, so match :focus here
   (this file loads after the import, so it wins). */
.dy-field input:focus, .dy-field textarea:focus, .dy-field select:focus {
  outline: 2px solid var(--dy-amber);
  outline-offset: 3px;
}

/* ==========================================================================
   Header (partials/header.html)
   ========================================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--dy-night) 92%, transparent);
  border-bottom: var(--dy-hair) solid var(--dy-rule-night);
  backdrop-filter: blur(6px);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--dy-space-4);
  padding: var(--dy-space-3) var(--dy-space-4);
}
.site-header__logo { border: 0; display: block; line-height: 0; }
.site-header__logo img { height: 22px; width: auto; display: block; }
.site-header__nav { display: flex; align-items: center; gap: var(--dy-space-4); }
.site-header__nav a { border: 0; color: var(--dy-cream); }
.site-header__nav a[aria-current="page"] { color: var(--dy-accent); }
.site-header__season { flex: none; }

@media (max-width: 899px) {
  .site-header__inner { padding: var(--dy-space-2) var(--site-gutter); flex-wrap: wrap; }
  .site-header__nav { gap: var(--dy-space-3); order: 3; width: 100%; }
}

/* ==========================================================================
   Footer (partials/footer.html)
   ========================================================================== */
.site-footer { padding: var(--site-section-pad) 0 var(--dy-space-5); }
.site-footer .dy-track { border: 0; margin: 0 0 var(--dy-space-5); }
.site-footer__grid {
  display: grid;
  grid-template-columns: auto repeat(3, 1fr);
  gap: var(--dy-space-4);
  align-items: start;
}
.site-footer__lockup { display: flex; align-items: flex-start; }
.site-footer__lockup img { height: 32px; width: auto; display: block; }
.site-footer__col p { margin: 0 0 4px; max-width: none; }
.site-footer__col p:first-child { margin-bottom: var(--dy-space-2); }
.site-footer__tag { margin: var(--dy-space-4) 0 0; max-width: none; }

@media (max-width: 899px) {
  .site-footer__grid { grid-template-columns: 1fr; gap: var(--dy-space-3); }
}

/* ==========================================================================
   Persistent stage (#stage) — §B. Fixed full-viewport 3D layer, a sibling of
   <main> (not nested inside it — see evidence/scene/NOTES.md "DOM position"
   for why: nesting it as main's first child would put main's own in-flow
   children *below* a z-index:0 positioned descendant in CSS2.1's stacking
   order, painting the car over the text). js/stage3d.js (B1) reads
   --stage-split / --stage-anchor / --stage-band and drives every child's
   pose each frame; this file only lays out the boxes.
   ========================================================================== */
.stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: var(--dy-night);
}
main, .site-footer { position: relative; z-index: 1; }

/* Backdrop behind the transparent canvas (scene.background is null). It used to be
   a 58% x 55% box painted at 55% opacity ABOVE the canvas; the token gradient ends in
   opaque night at 62%, so the box read as a hard-edged translucent slab across the
   car (evidence/review/probe/hero2-canvas-off.png). Full-bleed and under the canvas
   it is just the sky glow. */
.stage__glow {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--dy-night-glow);
  opacity: .55; /* horizon warmth, not a spot: at full strength the same orange corner sat in every frame */
}
.stage__canvas { position: relative; z-index: 1; width: 100%; height: 100%; display: block; }
/* dynamos.css's `img { display: block }` outranks the UA `[hidden] {
   display: none }` rule, so the poster would paint over the live canvas at
   all times without this. */
.stage__poster[hidden] { display: none; }
.stage__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 74% 50%;
  margin: 0;
}
/* leaders/labels: positioned every frame by stage3d.js from the projected
   world-space centre of each part; these layers only carry the JS-set
   inline transform/opacity, no static layout beyond the base state. */
.stage__leaders { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2; }
.stage__leader { stroke: var(--dy-rule-night); stroke-width: 1; opacity: 0; }
.stage__labels { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.stage__label {
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%);
  font-size: var(--site-size-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--dy-amber);
  white-space: nowrap;
  opacity: 0;
}
.stage__hud {
  position: absolute;
  z-index: 3;
  right: 24px;
  bottom: 24px;
  margin: 0;
  max-width: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: right;
  font-size: var(--site-size-readout);
  text-transform: none;
}
.stage__act { display: block; color: var(--dy-amber); }
.stage__section { display: block; color: var(--dy-cream); }
.stage__readout { display: block; color: var(--dy-muted-night); }

@media (max-width: 1099px) {
  .stage {
    position: sticky;
    /* inset:auto must come FIRST — it is the shorthand for top/right/bottom/left and
       was resetting the top offset declared above it, so the band never stuck and
       scrolled away with the page (no car anywhere below 1100px,
       evidence/review/probe/w1000-mid.png). */
    inset: auto;
    top: var(--site-header-h);
    height: var(--stage-band);
    z-index: 2; /* above main: the column text scrolls under the band, like the header */
  }
  .stage__poster { object-position: 50% 50%; }
  .stage__hud { right: auto; left: 16px; bottom: 12px; text-align: left; }
}

/* ==========================================================================
   Acts (#act-1 / #act-2) — §B, §C, amended by STORYBOARD.md §5 (index loses
   #act-3; the "ACT 3 · WHAT WE DO" label goes with it — #finale carries no
   act label now). Labels are a <p class="dy-label act__label">, never a
   heading; they carry number · name · years.
   ========================================================================== */
.act__label { margin-bottom: var(--site-block); }
.act + .act { margin-top: var(--site-section-pad); }

/* ==========================================================================
   Stage column (.stage-col) — §B "column rule", now shared by index.html,
   technical.html and contact.html (direction change, 2026-09-16). Everything
   in flow after the hero (acts, process, wind tunnel, finale readout, team,
   sponsors-link, contact body) lives here: 48% of main's width at ≥1100,
   inset from the same page-gutter maths .dy-page uses (see
   evidence/scene/NOTES.md "stage-col box geometry" for the derivation), full
   width below 1100 where the stage becomes the sticky band and nothing needs
   to make room for a car on the right.
   ========================================================================== */
.stage-col {
  box-sizing: border-box;
  padding-inline: var(--site-gutter);
}
@media (min-width: 1100px) {
  .stage-col {
    width: var(--stage-split);
    padding-inline-start: max(var(--site-gutter), calc((100% - 1120px) / 2 + var(--site-gutter)));
    padding-inline-end: 0;
  }
}
@media (min-width: 1440px) {
  .stage-col { --site-measure: 52ch; } /* 56ch ran the copy to 40 px short of the car */
}
/* contact.html's two-column form (.dy-grid--2) is fine at full page width,
   but .stage-col narrows to 48% at ≥1100 — two columns there would give each
   field ~240px. Drop to one column inside the stage column only; the
   ≤800px rule in dynamos.css already covers small screens. */
@media (min-width: 1100px) {
  .stage-col .contact-grid { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Hero (#hero) — §D scene target, §C layout. No canvas of its own any more:
   the car is the persistent #stage showing through this section's
   transparent background.
   ========================================================================== */
.hero {
  position: relative;
  min-height: calc(100vh - var(--site-header-h));
  padding: var(--dy-space-6) var(--dy-space-4);
  display: flex;
  flex-direction: column;
  justify-content: center; /* wordmark mid-left with air above and below, not pinned under the header */
}
.hero__tag {
  margin: 0;
  max-width: none;
  color: var(--dy-cream);
}
/* headline sits directly under the tag line, in the clear space above the
   car's chassis/wheels so its opaque silhouette does not sit under the text */
.hero__content {
  margin-top: 2.4em;
  max-width: 46%;
  display: flex;
  flex-direction: column;
  gap: 0;
}
.hero__name { margin: 0; }
.hero__name-line {
  display: block;
  font-family: var(--dy-font-display);
  font-size: var(--site-size-hero);
  line-height: 0.9;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--dy-cream);
}
.hero__echo {
  color: transparent;
  -webkit-text-stroke: 1px color-mix(in srgb, var(--dy-cream) 45%, transparent);
}
.hero__rule {
  width: 64px;
  height: 3px;
  border: 0;
  margin: var(--dy-space-3) 0 0;
  background: var(--dy-accent);
  transform-origin: left center;
}
/* CTAs move under the rule, in flow (§D risk: ".hero__cta bottom-right
   removed"); no longer absolutely positioned. */
.hero__cta {
  display: flex;
  gap: var(--dy-space-3);
  margin-top: var(--dy-space-4);
}

@media (max-width: 899px) {
  .hero { gap: var(--dy-space-4); }
  .hero__content { max-width: 100%; }
  .hero__cta .dy-btn { flex: 1; text-align: center; }
}

/* ==========================================================================
   Story chapters (#story .chapter × 5) — single column inside .stage-col;
   one photo slot per chapter, under the text (§B slot table).
   ========================================================================== */
.chapter {
  position: relative;
  padding: var(--site-section-pad) 0;
}
.chapter .dy-ghost { z-index: 0; opacity: 0.14; }
.chapter__text { position: relative; z-index: 1; }
/* hairline moves to .chapter__text, not the whole .chapter (§B risk: "so it
   does not cross the car"). */
.chapter + .chapter .chapter__text { border-top: var(--dy-hair) solid var(--dy-line); padding-top: var(--site-block); }
.chapter__meta { display: block; margin-bottom: var(--dy-space-2); }
.chapter__text .dy-h2 { margin-bottom: var(--site-heading-gap); }
.chapter__media { position: relative; z-index: 1; margin-top: var(--site-block); }
.chapter__media img { width: 100%; height: auto; display: block; }
.chapter__slogan {
  font-family: var(--dy-font-display);
  font-size: var(--site-size-h3);
  text-transform: uppercase;
  letter-spacing: -0.01em;
  margin-top: var(--site-para);
}

@media (min-width: 1100px) {
  .chapter { min-height: var(--chapter-h, 200vh); display: grid; align-content: center; }
}
/* §4 Clipping item 3 escape hatch: a chapter whose text+media block exceeds
   ~75vh (story-03, with its CFD figure) eats into the transit window unless
   it gets the longer chapter. */
.chapter--long { --chapter-h: var(--chapter-h-long, 240vh); }

/* ==========================================================================
   Process (#process) — single column inside .stage-col; the list, then its
   media stacked below (cfd full column width, then a/b paired). Lives on
   technical.html only now (STORYBOARD.md §5); pacing (was 60vh/li on index)
   is dropped here — technical.css keeps it a plain list.
   ========================================================================== */
.process {
  position: relative;
  padding-block: var(--site-section-pad);
}
.process__list { list-style: none; margin: 0; padding: 0; }
.process__list li {
  display: grid;
  grid-template-columns: 48px 1fr;
  gap: var(--dy-space-3);
  padding: var(--dy-space-3) 0;
  border-top: var(--dy-hair) solid var(--dy-line);
}
.process__list li:first-child { border-top: none; }
.process__index { font-family: var(--dy-font-mono); color: var(--dy-accent); font-size: var(--site-size-label); }
.process__list h3 { margin: 0 0 4px; }
.process__media { display: grid; gap: var(--dy-space-3); margin-top: var(--site-block); }
.process__media--pair { grid-template-columns: 1fr 1fr; }
.process__media img { width: 100%; height: auto; display: block; }

/* ==========================================================================
   Finale (#finale) — §C row 7. 260vh scroll length for the explode scrub;
   the parts readout sits at the end, in its own .fade (the section's camera/
   explode state is scrub-driven, not IntersectionObserver-driven).
   ========================================================================== */
.finale {
  position: relative;
  min-height: 260vh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-block: var(--site-section-pad);
}
.finale__readout .dy-h2 { margin-bottom: var(--site-heading-gap); }

/* ==========================================================================
   Team (#team)
   ========================================================================== */
.team {
  position: relative;
  padding-block: var(--site-section-pad);
  display: flex;
  flex-direction: column;
  gap: var(--site-block);
}
.team > .dy-h2 { margin: 0; }
.team__facts-group + .team__facts-group { margin-top: var(--dy-space-3); }
.team__strip { display: grid; gap: var(--dy-space-3); }
.team__strip img { width: 100%; height: auto; display: block; }

/* ==========================================================================
   Sponsorships link band (#sponsors-link)
   ========================================================================== */
#sponsors-link {
  padding-block: var(--site-section-pad);
  border-top: var(--dy-hair) solid var(--dy-line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--dy-space-4);
}
#sponsors-link .dy-h2 { margin: 0; }
#sponsors-link p { max-width: var(--site-measure); }

@media (max-width: 899px) {
  #sponsors-link { flex-direction: column; align-items: flex-start; }
}

/* ==========================================================================
   Sponsors page — tiers table wrapper
   ========================================================================== */
.tiers { padding-block: var(--site-section-pad); }
.tiers .dy-table td:first-child { white-space: nowrap; }
.tiers .dy-table td:nth-child(2), .tiers .dy-table th:nth-child(2) {
  text-align: right;
  font-family: var(--dy-font-mono);
}

@media (max-width: 899px) {
  .tiers .dy-table-wrap { overflow-x: auto; }
  .tiers .dy-table { min-width: 560px; }
}

/* ==========================================================================
   Motion — §D fade-in and reduced-motion behaviour
   ========================================================================== */
.fade { opacity: 0; transition: opacity 240ms ease-out; }
.fade.is-in { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .fade { opacity: 1; transition: none; }
}
