/* The landing as one side of a record. A fixed stage of particles fills the screen; the page
   scrolls a row of scenes over it, each pinning its copy while the stage holds that scene's
   shape. The deck at the foot of the screen plays the side: track, runtime, progress.
   Scenes read --enter, --progress and --leave (0–1) from stage/story.js; without it they sit
   fully shown. */

:root {
  --gutter: var(--space-16);
  --header-height: 72px;
  --deck-height: 52px;
  --deck-bottom: env(safe-area-inset-bottom, 0px);
  --pin-top: calc(var(--header-height) + var(--space-6));
  --pin-bottom: calc(var(--deck-height) + var(--deck-bottom) + var(--space-12));
  --scrim: color-mix(in oklab, var(--surface) 82%, transparent);
  --hairline: color-mix(in oklab, var(--accent) 55%, transparent);
}

html {
  scroll-padding-top: var(--header-height);
}

body {
  background: var(--surface);
  overflow-x: clip;
}

/* The stage: fixed behind everything, sized to the large viewport so mobile bars never
   uncover an edge. */
.stage {
  position: fixed;
  inset: 0 0 auto;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100lvh;
  pointer-events: none;
}

.pins {
  position: fixed;
  inset: 0;
  z-index: 1;
  margin: 0;
  padding: 0;
  list-style: none;
  pointer-events: none;
  visibility: hidden;
}

html[data-stage-ready] .pins {
  visibility: visible;
}

.pins li {
  --x: -999;
  --y: -999;
  --o: 0;
  position: absolute;
  top: 0;
  left: 0;
  padding: 0 0 var(--space-6) var(--space-2);
  border-left: 1px solid var(--hairline);
  /* Half the screen less the edge: whichever side of its point a label takes, it fits. */
  max-width: calc(50vw - var(--space-6));
  font-size: 10px;
  line-height: 1.4;
  letter-spacing: .12em;
  text-wrap: balance;
  color: var(--accent);
  opacity: var(--o);
  transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px)) translateY(-100%);
}

/* The second label of a shape stands taller, so two at one height stack instead of overlapping. */
.pins li[data-pin$=" 1"] {
  padding-bottom: calc(var(--space-6) * 2);
}

.pins li::before {
  content: "";
  position: absolute;
  left: -3px;
  bottom: -3px;
  width: 5px;
  height: 5px;
  background: var(--accent);
  border-radius: 50%;
}

/* A label near the right edge hangs to the left of its point instead. */
.pins li[data-flip] {
  padding-right: var(--space-2);
  padding-left: 0;
  border-left: 0;
  border-right: 1px solid var(--hairline);
  transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px)) translate(-100%, -100%);
}

.pins li[data-flip]::before {
  left: auto;
  right: -3px;
}

main,
footer {
  position: relative;
  z-index: 2;
}

/* Header: floats over the stage; a fade keeps it legible over bright shapes. */
.site-header {
  position: fixed;
  inset: 0 0 auto;
  z-index: 3;
  height: var(--header-height);
  padding: 0 var(--gutter);
  border-bottom: 0;
  background: linear-gradient(var(--surface), transparent);
}

.wordmark {
  font-size: 24px;
}

.wordmark img {
  width: 28px;
  height: 28px;
}

/* Scenes: each is as tall as the scroll it plays over; its pin holds the copy on screen. */
.scene {
  --enter: 1;
  --progress: 0;
  --leave: 0;
  --length: 2;
  position: relative;
  height: calc(var(--length) * 100svh);
}

.scene-long {
  --length: 2.8;
}

.scene-pin {
  position: sticky;
  top: 0;
  height: 100svh;
  padding: var(--pin-top) var(--gutter) var(--pin-bottom);
  display: grid;
}

.cue {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin: 0 0 var(--space-3);
  font-size: 11px;
  letter-spacing: .12em;
  color: var(--text-muted);
}

.cue-n {
  color: var(--accent);
  font-weight: 700;
}

.cue-time {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

/* Side B tracks have no running time yet: the slot says so instead. */
.cue-soon {
  margin-left: auto;
  color: var(--accent);
}

/* Lead-in: the title fills the lower left, the record spins on the right. */
.scene-lead {
  --length: 1.6;
  /* The rows the lead keeps around its headline, which the headline's height cap leaves room for.
     Both are set outright on their rows, so the cap and the rows can never disagree: the edition
     line is its 10px type at line-height 1.6 (style.css), and the cue's 13px line is what WebKit
     gave its 10px type at normal line-height, so every engine matches it. */
  --lead-edition-line: 16px;
  --lead-cue-gap: var(--space-8);
  --lead-cue-line: 13px;
}

.scene-lead .scene-pin {
  grid-template-columns: minmax(0, 1fr) minmax(280px, 380px);
  grid-template-rows: auto 1fr auto auto;
  column-gap: var(--space-12);
  opacity: calc(1 - var(--leave) * 1.4);
}

.lead-edition {
  grid-column: 1;
  grid-row: 1;
  margin: 0;
  line-height: var(--lead-edition-line);
}

.lead-liner {
  grid-column: 2;
  grid-row: 1 / 3;
  align-self: start;
  justify-self: end;
  margin: 0;
  display: grid;
  gap: var(--space-1);
  font-size: 10px;
  letter-spacing: .1em;
  color: var(--text-muted);
}

.lead-liner div {
  display: grid;
  grid-template-columns: 7ch auto;
  gap: var(--space-3);
}

.lead-liner dt {
  color: var(--text-placeholder);
}

.lead-liner dd {
  margin: 0;
}

/* The headline sizes to its own column at about a chapter heading's size and stops at their
   ceiling, so the stage behind it keeps the first screen. It never crosses the aside; each half
   breaks into balanced lines rather than overflowing. On a short window the height binds first,
   so the aside's button and the scroll cue stay above the deck. */
.lead-title {
  --title-floor: 44px;
  /* The share of its column the headline takes: 89px at 1366, 97px at 1440. */
  --title-fill: 11cqi;
  /* The chapter headings' ceiling (`.scene h2`). */
  --title-max: 104px;
  --title-leading: .84;
  /* Two lines per half at most: the layout test holds each half to that. */
  --title-lines: 4;
  /* The pin's free height shared out over the headline's lines. The pin is 100svh less its
     padding and the rows around the headline. At 1366×650 that leaves 650 − 96 − 100 − 16 − 32 −
     13 = 393px, a cap of 393 / (4 × .84) = 117px, above the column's 89px; the cap drops below the
     ceiling on windows shorter than about 606px. */
  --title-height-cap: calc((100svh - var(--pin-top) - var(--pin-bottom) - var(--lead-edition-line) - var(--lead-cue-gap) - var(--lead-cue-line)) / (var(--title-lines) * var(--title-leading)));
  container-type: inline-size;
  grid-column: 1;
  grid-row: 3;
  align-self: end;
  line-height: var(--title-leading);
  transform: translateY(calc(var(--leave) * -12vh));
}

/* The tracking and the optical nudge left sit on the halves, so their em is the halves' own
   size; set on the heading, it would resolve against the heading's size and be inherited as px. */
.lead-title > span,
.lead-title > em {
  display: block;
  font-size: max(var(--title-floor), min(var(--title-fill), var(--title-height-cap), var(--title-max)));
  letter-spacing: -.08em;
  margin-left: -.04em;
  text-wrap: balance;
}

.lead-aside {
  grid-column: 2;
  grid-row: 3;
  align-self: end;
  padding: var(--space-6);
  background: var(--scrim);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  backdrop-filter: blur(6px);
}

.lead-description {
  font-size: 13px;
  line-height: 1.8;
  color: var(--text-muted);
  margin: 0 0 var(--space-6);
}

.lead-aside .button-link {
  width: 100%;
}

.lead-cue {
  grid-column: 1 / -1;
  grid-row: 4;
  margin: var(--lead-cue-gap) 0 0;
  font-size: 10px;
  line-height: var(--lead-cue-line);
  letter-spacing: .14em;
  color: var(--text-muted);
}

.lead-cue span {
  display: inline-block;
  color: var(--accent);
  animation: cue-bob 1.6s var(--ease-out) infinite alternate;
}

@keyframes cue-bob {
  to {
    transform: translateY(4px);
  }
}

/* Chapters: copy on one side, the shape on the other; the stage alternates with the copy. */
.scene-chapter .scene-pin {
  grid-template-columns: repeat(12, minmax(0, 1fr));
  align-items: center;
}

.scene-copy {
  grid-column: 1 / span 5;
  opacity: clamp(0, calc(var(--enter) * 1.6 - .6 - var(--leave) * 1.8), 1);
  transform: translateY(calc((1 - var(--enter)) * 8vh - var(--leave) * 8vh));
}

[data-copy="right"] .scene-copy {
  grid-column: 8 / span 5;
}

.cue-bar {
  display: block;
  height: 1px;
  margin-bottom: var(--space-8);
  background: var(--border);
  overflow: hidden;
}

.cue-bar::after {
  content: "";
  display: block;
  height: 100%;
  background: var(--accent);
  transform-origin: left;
  transform: scaleX(var(--progress));
}

.scene h2 {
  font-size: clamp(44px, 5.8vw, 104px);
  font-weight: 500;
  line-height: .94;
  letter-spacing: -.07em;
  text-wrap: balance;
  margin: 0 0 var(--space-6);
}

.scene-body {
  max-width: 46ch;
  font-size: 14px;
  line-height: 1.8;
  color: var(--text-muted);
  margin: 0 0 var(--space-6);
}

.liner {
  max-width: 46ch;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 11px;
  letter-spacing: .04em;
}

.liner li {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-top: 1px solid var(--border);
}

.liner li::before {
  content: "—";
  color: var(--accent);
}

/* The tour: the app window rises out of the halo, at full width. */
.scene-tour {
  height: auto;
  padding: calc(var(--header-height) + var(--space-16)) var(--gutter) var(--space-16);
}

.tour-head {
  max-width: 1280px;
  margin: 0 auto var(--space-8);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--space-2) var(--space-8);
}

.tour-head .cue {
  grid-column: 1 / -1;
}

.tour-head h2 {
  margin: 0;
}

.tour-head .scene-body {
  margin: 0;
}

.scene-tour .shot-wrap {
  max-width: 1280px;
  margin: 0 auto;
}

/* Run-out: the record lies flat below the copy and the pilot card sits beside it. */
.scene-runout {
  height: auto;
  min-height: 100svh;
  padding: calc(var(--header-height) + var(--space-16)) var(--gutter) var(--pin-bottom);
  align-content: center;
}

.scene-runout .pilot-card {
  background: var(--scrim);
  backdrop-filter: blur(6px);
}

footer {
  padding: var(--space-8) var(--gutter) var(--pin-bottom);
  background: var(--surface);
}

/* The deck: one side of a record at the foot of the screen. */
.deck {
  --side: 0;
  container: deck / inline-size;
  position: fixed;
  inset: auto 0 0;
  z-index: 3;
  height: calc(var(--deck-height) + var(--deck-bottom));
  padding: 0 var(--gutter) var(--deck-bottom);
  display: flex;
  align-items: center;
  gap: var(--space-8);
  font-size: 10px;
  letter-spacing: .1em;
  color: var(--text-muted);
  background: var(--scrim);
  border-top: 1px solid var(--border);
  backdrop-filter: blur(10px);
}

.deck-progress {
  position: absolute;
  inset: -1px 0 auto;
  height: 1px;
  background: var(--accent);
  transform-origin: left;
  transform: scaleX(var(--side));
}

.deck-label {
  display: flex;
  gap: var(--space-3);
  margin: 0;
  white-space: nowrap;
}

.deck-side {
  color: var(--text);
}

.deck-tracks {
  display: flex;
  flex: 1;
  justify-content: center;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.deck-tracks a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-control);
  transition: color var(--duration-fast) var(--ease-out);
}

.deck-tracks a:hover {
  color: var(--text);
}

.deck-tracks a[aria-current] {
  color: var(--text);
}

.deck-tracks a[aria-current] .deck-n {
  color: var(--accent);
}

/* Side B is not out yet: its tracks sit a shade back until one is playing. */
.deck-tracks .deck-later {
  color: var(--text-placeholder);
}

.deck-soon {
  padding: var(--space-1);
  line-height: 1;
  color: var(--text-placeholder);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
}

.deck-clock {
  margin: 0;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.deck-clock-now {
  color: var(--text);
}

/* The deck gives way in steps as it narrows. Widths measured in WebKit at 10px: the label is
   201px with its RPM and 140px without (SIDE B · COMING SOON is the longest side), the tracks
   834px named and 367px numbered (211px at phone padding), the clock 91px. Each step adds 8px of
   slack to its measured width, since other engines may set the same type a pixel or so wider. */

/* 201 + 834 + 91 + two 32px gaps, plus 8px slack: below this the RPM goes first. */
@container deck (width < 1198px) {
  .deck-rpm {
    display: none;
  }
}

/* 140 + 834 + 91 + two 32px gaps, plus 8px slack: below this the tracks keep only their numbers. */
@container deck (width < 1137px) {
  .deck-name:not(.deck-name-keep),
  .deck-soon {
    display: none;
  }
}

/* 140 + 211 + 91 + two 12px gaps at phone spacing, plus 8px slack: below this the side label goes. */
@container deck (width < 474px) {
  .deck-label {
    display: none;
  }
}

/* 211 + 91 + one 12px gap, plus 8px slack: below this only the numbered tracks are left. */
@container deck (width < 322px) {
  .deck-clock {
    display: none;
  }
}

@media (max-width: 1100px) {
  :root {
    --gutter: var(--space-8);
  }
}

/* Portrait and phones: the shape centres in the top of the screen and the copy sits below it,
   over a scrim. */
@media (max-width: 760px), (max-aspect-ratio: 9/10) {
  .scene-chapter .scene-pin {
    grid-template-columns: minmax(0, 1fr);
    align-items: end;
  }
  .scene-copy,
  [data-copy="right"] .scene-copy {
    grid-column: 1;
    margin: 0 calc(var(--gutter) * -1) calc(var(--pin-bottom) * -1);
    padding: var(--space-16) var(--gutter) calc(var(--deck-height) + var(--deck-bottom) + var(--space-4));
    background: linear-gradient(transparent, var(--surface) var(--space-16));
  }
  .scene-lead .scene-pin {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 1fr auto auto auto;
  }
  .lead-liner {
    display: none;
  }
  .lead-title {
    grid-row: 3;
  }
  .lead-aside {
    grid-column: 1;
    grid-row: 4;
    margin-top: var(--space-6);
  }
  .lead-cue {
    grid-row: 5;
  }
}

@media (max-width: 760px) {
  :root {
    --gutter: var(--space-6);
    --header-height: 64px;
    --deck-height: 48px;
    --pin-bottom: calc(var(--deck-height) + var(--deck-bottom) + var(--space-6));
  }
  .scene-pin {
    padding-top: var(--header-height);
  }
  .scene-lead {
    --lead-cue-gap: var(--space-4);
  }
  .lead-title {
    --title-floor: 36px;
  }
  .lead-aside {
    padding: var(--space-4);
  }
  .cue-bar {
    margin-bottom: var(--space-4);
  }
  .scene h2 {
    font-size: clamp(36px, 10.5vw, 56px);
    margin-bottom: var(--space-4);
  }
  .scene-body {
    font-size: 13px;
    line-height: 1.65;
    margin-bottom: 0;
  }
  .liner {
    display: none;
  }
  .tour-head {
    grid-template-columns: minmax(0, 1fr);
  }
  .deck {
    gap: var(--space-3);
  }
  .deck-tracks {
    justify-content: space-between;
    gap: 0;
  }
  .deck-tracks a {
    padding: 0 var(--space-1);
  }
}

@media (max-width: 390px) {
  :root {
    --gutter: var(--space-4);
  }
}

@media (prefers-reduced-motion: reduce) {
  .scene-copy,
  .lead-title {
    transform: none;
  }
  .lead-cue span {
    animation: none;
  }
  .deck-tracks a {
    transition: none;
  }
}
