/* ---------- The Phoenix Clock · design system ----------
   peat near-black · oxide/ember red · cold bone-white
   register: a scientific instrument built by a heretic. restraint. */

/* ---------- self-hosted type: Spectral (titles) · IBM Plex Mono (instrument data) ---------- */
@font-face { font-family: "Spectral"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/spectral-400.woff2") format("woff2"); }
@font-face { font-family: "Spectral"; font-style: normal; font-weight: 500; font-display: swap; src: url("fonts/spectral-500.woff2") format("woff2"); }
@font-face { font-family: "Spectral"; font-style: normal; font-weight: 600; font-display: swap; src: url("fonts/spectral-600.woff2") format("woff2"); }
@font-face { font-family: "Spectral"; font-style: italic; font-weight: 400; font-display: swap; src: url("fonts/spectral-400i.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/plexmono-400.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("fonts/plexmono-500.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 600; font-display: swap; src: url("fonts/plexmono-600.woff2") format("woff2"); }

:root {
  --ae-mark: "ae-phoenixclock-f5fec6"; /* guard fingerprint — invisible, do not remove */
  --bg:        #0a0806;
  --bg-2:      #130d09;
  --ink:       #ece5d8;   /* bone-white */
  --bone:      #f2ecdf;
  --muted-c:   #8a8074;   /* ~4.9:1 on --bg, clears AA for small caption text */
  --ember:     #d0451f;   /* oxide red */
  --ember-hot: #ff6a33;   /* live ember */
  --hair:      rgba(236,229,216,0.12);

  --serif: "Spectral", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --mono:  "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --sans:  system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  --pad: clamp(1.25rem, 4vw, 3rem);
}

* { box-sizing: border-box; }

/* overflow-anchor: none — this page loads fonts/frames after scroll is forced to 0;
   without this, CSS scroll anchoring drifts scrollY away from 0 to compensate for
   the layout shift, so a reload can open mid-scrub instead of at the cold open. */
html { scroll-behavior: auto; overflow-anchor: none; -webkit-tap-highlight-color: transparent; }
body { overflow-anchor: none; }

/* touch: no double-tap-zoom delay on the controls, no grey tap flash;
   the canvas is tappable (markers) but pinch/pan must still work on the page */
a, button, .chip { touch-action: manipulation; }
#clockCanvas { touch-action: manipulation; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.hot { color: var(--ember-hot); }
.eyebrow {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--muted-c);
  margin: 0 0 1.4rem;
}

/* ---------- ARCHAIX BRAND ---------- */
.brand {
  display: inline-flex; align-items: center; gap: 0.5em;
  text-decoration: none; color: var(--ink);
  font-family: var(--sans); font-weight: 700;
  letter-spacing: 0.34em; text-transform: uppercase;
  transition: color 0.2s;
}
.brand__mark { width: 1.05em; height: 1.05em; color: var(--ember-hot); flex: none; display: block; }
.brand__x { color: var(--ember-hot); }
.brand:hover { color: #fff; }
.brand--lg { font-size: 1.1rem; margin: 0 auto 0.7rem; }
/* padding, not height: the wordmark is a standalone link and needs a 44px box */
.brand--lock { font-size: 1.35rem; margin-top: 0.02rem; padding: 0.5rem; }
.brand__expand {
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted-c);
  margin: 0 auto 2rem; max-width: 42rem; line-height: 1.6;
  text-wrap: balance;
}
.tagline {
  font-family: var(--serif); font-style: italic;
  font-size: clamp(1rem, 2.2vw, 1.35rem);
  color: var(--ember-hot); letter-spacing: 0.01em;
  margin: 1.8rem 0 0;
}
.tagline--lock { margin: 0.5rem 0 1.8rem; }

/* symbol-only svg container (kept off-layout; a class not an inline style so the CSP stays strict) */
.svg-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.statsbar__brand {
  display: inline-flex; align-items: center; gap: 0.45em;
  font-family: var(--sans); font-weight: 700; letter-spacing: 0.24em;
  font-size: 0.72rem; color: var(--ink);
  padding-right: clamp(0.9rem, 2.6vw, 2rem);
  border-right: 1px solid var(--hair);
}
.statsbar__brand .brand__mark { width: 0.95em; height: 0.95em; }

/* ---------- entry loader ---------- */
.loader {
  position: fixed; inset: 0; z-index: 100; background: var(--bg);
  display: grid; place-items: center;
  transition: opacity 0.45s ease, visibility 0.45s ease;
}
.loader.is-hidden { opacity: 0; visibility: hidden; }
.loader__inner { display: flex; flex-direction: column; align-items: center; gap: 1.5rem; }
.loader__mark {
  display: inline-flex; align-items: center; gap: 0.5em;
  font-family: var(--sans); font-weight: 700; letter-spacing: 0.34em; text-transform: uppercase;
  font-size: 1.05rem; color: var(--ink);
}
.loader__mark .brand__mark { width: 1.05em; height: 1.05em; color: var(--ember-hot); }
.loader__track { width: min(46vw, 280px); height: 1px; background: var(--hair); position: relative; overflow: hidden; }
.loader__bar {
  position: absolute; inset: 0 auto 0 0; width: 0%;
  background: linear-gradient(90deg, var(--ember), var(--ember-hot));
  transition: width 0.3s cubic-bezier(.22,1,.36,1);
}
.loader__pct {
  font-family: var(--mono); font-size: 0.68rem; color: var(--muted-c);
  font-variant-numeric: tabular-nums; letter-spacing: 0.18em;
}
.loader__pct::after { content: "%"; opacity: 0.5; margin-left: 0.15em; }


/* ---------- COLD OPEN · cinematic scrub hero ----------
   Height sets the scrub pace: the film's frames are spread evenly across it,
   so scroll length and frame count have to move together. It went 750 -> 900
   when the film grew from 156 frames to 221 - at 750 the cold open, which is
   unchanged footage, played 30% faster than it was cut to. */
.film { position: relative; height: 900vh; background: var(--bg); }
.film__stage { position: sticky; top: 0; height: 100svh; overflow: hidden; background: #000; }
.film__canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* scrim: darken top + bottom so the title and turn stay legible over the bright dawn */
.film__scrim {
  position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background:
    radial-gradient(130% 90% at 50% 42%, transparent 44%, rgba(10,8,6,0.5) 100%),
    linear-gradient(to bottom, rgba(10,8,6,0.72) 0%, transparent 26%, transparent 58%, rgba(10,8,6,0.84) 100%);
}
/* fine grain to kill banding on the near-black gradients */
.film__grain {
  position: absolute; inset: 0; pointer-events: none; z-index: 1; opacity: 0.045; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 140px 140px;
}
/* the padding is the tap target: the wordmark itself is only ~18px tall, which
   is under the 44px minimum, so the box is grown by padding and pulled back by
   the same amount so it sits where it always sat */
.film__brand {
  position: absolute; z-index: 3; font-size: 0.8rem;
  top: calc(clamp(1.1rem, 3vh, 2rem) - 0.85rem);
  left: calc(var(--pad) - 0.85rem);
  padding: 0.85rem;
}

.beats { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.beat {
  position: absolute; left: 0; right: 0; bottom: clamp(4rem, 15vh, 10rem);
  padding-inline: var(--pad); text-align: center; opacity: 0; will-change: opacity, transform;
}
.beat--title { inset: 0; bottom: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.beat--title .brand__expand { margin: 0 auto 1.5rem; }
.beat__h {
  font-family: var(--serif); font-weight: 500;
  font-size: clamp(2.6rem, 8vw, 5.4rem); line-height: 0.98; letter-spacing: -0.01em;
  margin: 0; text-wrap: balance; text-shadow: 0 2px 50px rgba(10,8,6,0.85);
}
.beat__sub {
  font-family: var(--serif); font-size: clamp(1.1rem, 2.4vw, 1.5rem); line-height: 1.5;
  color: #d6cebf; max-width: 34rem; margin: 1.2rem auto 0; text-wrap: balance;
  text-shadow: 0 1px 30px rgba(10,8,6,0.9);
}
.beat--counts .counts { margin: 0; }
.beat__line {
  font-family: var(--serif); font-size: clamp(1.4rem, 3.4vw, 2.3rem); line-height: 1.2;
  color: var(--bone); max-width: 24ch; margin: 0 auto; text-wrap: balance;
  text-shadow: 0 2px 34px rgba(10,8,6,0.9);
}
.beat--close .tagline { margin-top: 1.4rem; }

.scroll-cue {
  position: absolute; left: 50%; bottom: max(2rem, calc(env(safe-area-inset-bottom, 0px) + 1.2rem)); transform: translateX(-50%); z-index: 3; margin: 0;
  font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--muted-c); transition: opacity 0.6s ease;
}
.scroll-cue.is-gone { opacity: 0; }

.counts {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1rem, 4vw, 2.6rem);
  justify-content: center;
  margin-bottom: 3rem;
}
.count { display: flex; flex-direction: column; align-items: center; min-width: 8rem; }
.count__num {
  font-family: var(--mono);
  font-size: clamp(1.6rem, 4vw, 2.4rem);
  font-variant-numeric: tabular-nums;
  color: var(--ember-hot);
  line-height: 1;
}
.count--muted .count__num { color: var(--ink); opacity: 0.72; }
.count__lab {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-c);
  margin-top: 0.55rem;
  max-width: 11rem;
  text-align: center;
}
/* ---------- THE CLOCK / INSTRUMENT ---------- */
/* Descent length: scrub travel is (height - 100vh). ~30 revolutions of the
   well live in the first ~72% of it, so this is the one number that sets the
   fall speed — raise it for a slower, calmer descent. */
.clock { position: relative; height: 1000vh; }
.clock__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: radial-gradient(130% 110% at 70% 48%, #150d08 0%, var(--bg) 62%);
}

/* stats bar */
.statsbar {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1rem, 3vw, 2.4rem);
  justify-content: center;
  align-items: baseline;
  padding: clamp(0.7rem, 2vw, 1rem) var(--pad);
  border-bottom: 1px solid var(--hair);
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-c);
}
.statsbar .stat { white-space: nowrap; }  /* a stat that wraps mid-figure reads broken */
.statsbar .stat b {
  color: var(--ink);
  font-weight: 400;
  font-size: 1.1rem;
  font-variant-numeric: tabular-nums;
  margin-right: 0.45em;
}
.statsbar .stat b.hot { color: var(--ember-hot); }

/* 3-zone instrument */
.instrument {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(132px, 178px) minmax(290px, 29rem) 1fr;
  gap: clamp(1rem, 2.4vw, 2rem);
  padding: clamp(0.9rem, 2.4vw, 1.5rem) var(--pad);
}

/* index rail */
.index {
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  scrollbar-width: thin;
  scrollbar-color: var(--ember) transparent;
  border-right: 1px solid var(--hair);
  padding-right: 0.5rem;
}
.index__item {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.26rem 0.4rem;
  background: none;
  border: none;
  border-left: 2px solid transparent;
  text-align: left;
  cursor: pointer;
  font-family: var(--mono);
  color: var(--muted-c);
  transition: color 0.15s, border-color 0.15s;
  width: 100%;
}
.index__item:hover { color: var(--ink); }
.index__item:focus-visible { outline: 2px solid var(--ember-hot); outline-offset: 2px; color: var(--ink); }
.index__item .yr { font-size: 0.78rem; font-variant-numeric: tabular-nums; flex: none; }
/* era titles clamp at a word boundary over two lines — a mid-word ellipsis
   reads as unfinished; the full title also lives in the button's title attr */
.index__item .er {
  font-size: 0.56rem;
  line-height: 1.4;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.index__item.is-active { color: var(--ember-hot); border-left-color: var(--ember-hot); }
.index__item.is-next .yr { color: var(--ember-hot); }  /* hot: --ember misses AA at this size */

/* live readout. Entries crossfade in rather than hard-cutting — the swap
   starts at 0.45 opacity, so rapid scrubbing reads as a shimmer over the
   text, never a strobe to black. Opacity ONLY, on a promoted layer: the
   swap fires 30+ times per descent and must stay compositor-cheap. */
@keyframes readoutIn {
  from { opacity: 0.45; }
  to   { opacity: 1; }
}
.readout.is-swap { animation: readoutIn 0.18s cubic-bezier(0.22, 1, 0.36, 1); }
.readout {
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  align-content: start;
  padding-right: 0.6rem;
  scrollbar-width: thin;
  scrollbar-color: var(--ember) transparent;
}
.readout__date {
  font-family: var(--mono); font-size: 0.9rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ember-hot); margin: 0;
}
.readout__eyebrow {
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--muted-c); margin: 0;
}
.readout__title {
  font-family: var(--serif); font-weight: 500;
  font-size: clamp(1.55rem, 2.6vw, 2.3rem); line-height: 1.08; margin: 0.1rem 0 0;
}
.readout__lead {
  font-family: var(--serif); font-size: 1.04rem; line-height: 1.45; color: var(--bone); margin: 0;
}
.readout__context { font-size: 0.92rem; line-height: 1.7; color: #c7bfb1; margin: 0; }
.readout__world { margin: 0.1rem 0 0; }
.readout__world[hidden] { display: none; }
.readout__worldhead {
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--ember-hot); margin: 0 0 0.5rem;
  /* --ember is 4.33:1 on the near-black stage — under AA for text this small */
}
.readout__world ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.readout__world li {
  font-family: var(--mono); font-size: 0.76rem; line-height: 1.45; color: #b7afa2;
  padding-left: 0.85rem; position: relative;
}
.readout__world li::before {
  content: ""; position: absolute; left: 0; top: 0.5em;
  width: 4px; height: 4px; border-radius: 50%; background: var(--ember);
}
.readout__src {
  font-family: var(--mono); font-size: 0.64rem; letter-spacing: 0.04em;
  line-height: 1.5; color: var(--muted-c); margin: 0.3rem 0 0;
  padding-top: 0.7rem; border-top: 1px solid var(--hair);
}

/* the spiral */
.clockwrap { position: relative; min-height: 0; }
/* the well melts into the peat: corner vignette + the film's grain (same
   data-URI — it exists to kill banding on near-black gradients, which the
   furnace glow at the vanishing point would otherwise show). Both sit above
   the canvas, below the legend, and never intercept clicks. */
.clockwrap::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background: radial-gradient(140% 140% at 50% 50%, transparent 64%, rgba(10,8,6,0.6) 100%);
}
.clockwrap::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 1;
  opacity: 0.05; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 140px 140px;
}
#clockCanvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
#clockCanvas.is-hot { cursor: pointer; }

/* WebGL renderer (clock3d.js): its canvas stacks exactly over the 2D fallback
   (same sizing, same mobile fades below), so swapping renderers moves nothing.
   The 2D canvas keeps its own labels; in 3D mode year labels are DOM spans in
   .clocklabels, projected from the camera each frame — same IBM Plex Mono,
   same collision rules, pointer-transparent so clicks reach the canvas. */
.clockgl { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: manipulation; }
.clockwrap.is-hot #clockCanvas, .clockwrap.is-hot .clockgl { cursor: pointer; }
.clocklabels { position: absolute; inset: 0; z-index: 1; overflow: hidden; pointer-events: none; }
.clocklabels span {
  position: absolute; left: 0; top: 0;
  font-family: var(--mono); font-size: 0.625rem; line-height: 1; /* 10px at base root size — follows the wide-breakpoint root scaling like the canvas labels' uiScale */
  color: rgba(242,236,223,0.5); white-space: nowrap;
  transform: translateY(-50%);
  will-change: transform;
}
.clocklabels span.is-active { color: var(--ember-hot); font-weight: 600; }

.legend {
  position: absolute;
  left: 0; bottom: 0;
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  gap: 0.4rem;
  z-index: 2;
  opacity: 0.85;
}
.legend li {
  display: flex; align-items: center; gap: 0.5rem;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: var(--muted-c);
  /* the key sits in the bottom-left of the canvas, where the outermost coil
     sweeps through on the wide beats — a peat-coloured halo keeps the labels
     readable where the strand crosses them, without a visible plate */
  text-shadow: 0 0 6px var(--bg), 0 0 12px var(--bg);
}
.dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex: none; }
.dot--pulse { background: var(--bone); }
.dot--next  { background: transparent; border: 1.5px solid var(--ember-hot); box-shadow: 0 0 8px var(--ember-hot); }
.dot--vanishing { background: #7f95ad; }  /* bluer than bone so the two filled dots read apart at a glance */
.dot--red   { background: var(--ember); }
.dot--silent { background: transparent; border: 1px dashed rgba(242,236,223,0.45); } /* broken ring = unrecorded return */
.dot--today { width: 2px; height: 10px; border-radius: 0; background: var(--bone); opacity: 0.7; } /* a tick, not a node */

/* ---------- desktop: give the extra pixels somewhere to go ----------
   Up to ~1400 the instrument is width-bound and the three zones fill the row
   with ~50px of slack. Past that, every extra pixel used to land in the third
   column alone while the rail (178px) and the readout (29rem) stayed frozen at
   laptop size: measured, the well sat in a 1118px box at 1920 (269px of dead
   air), 1758px at 2560 (680px) and 2638px at 3440 (1560px) — a small circle
   marooned in a vignette, with a widening void between it and the text.
   Two levers, in order of what each screen actually needs. */

/* 1 · cap the well's BOX near its own height. The spiral is a circle sized off
   the short axis of this box (main.js: reach = min(w, h)), so width past that
   is dead air carrying a vignette. Centring the capped box in the column moves
   the canvas, the vignette, the grain and the legend together. Below ~1600 the
   column is already narrower than this cap, so it is a no-op there. */
@media (min-width: 1200px) {
  /* width:100% before the cap on purpose — the canvas and the legend are both
     absolutely positioned, so the wrap has no in-flow content: auto margins
     alone drop it to a 0px box and the clock vanishes. */
  /* 90vh, not 100vh: the wrap never gets the whole viewport height — the stats
     bar and the stage padding take a cut, so the box measures ~83-88vh tall in
     practice. Capping the width near that keeps the box roughly square (the
     shape the spiral wants) instead of leaving a fifth of it as dead air on
     short-and-wide panels: measured at 2560x1080 this takes the surplus from
     210px to 145px without shrinking the well, which is height-bound there. */
  .clockwrap { width: 100%; max-width: min(100%, 90vh); margin-inline: auto; }
}

/* 2 · scale the instrument itself. A percentage on the root (not a px size) so
   a visitor who runs a larger default font still multiplies through it, and the
   text zones grow by vw as well as by rem so a 1600x900 panel — same physical
   size as a 1440 laptop — keeps its proportions instead of eating the well. */
@media (min-width: 1700px) {
  :root { font-size: 108.5%; }
  .instrument {
    grid-template-columns:
      minmax(150px, min(208px, 12.5vw))
      minmax(300px, min(32rem, 30vw))
      minmax(0, 1fr);
  }
}
@media (min-width: 2200px) {
  :root { font-size: 116%; }
  .instrument {
    grid-template-columns:
      minmax(150px, min(232px, 11vw))
      minmax(300px, min(34rem, 28vw))
      minmax(0, 1fr);
  }
}
/* ultrawide: past ~2400 the row is wider than any instrument wants to be. Cap
   and centre it so the leftover sits in outer gutters rather than as a canyon
   between the readout and the well. */
@media (min-width: 2400px) {
  .instrument { max-width: 2280px; margin-inline: auto; width: 100%; }
}

/* responsive: stack on narrow.
   The instrument is a scroll-scrubbed, pinned viewport, so everything (year
   strip, spiral, readout) must fit ONE screen height. The budget is tight on a
   phone, so: slim the decorative stats to a single row, shrink the spiral, and
   give the readout the reclaimed room with tighter type + a bottom fade so a
   long entry ends gracefully instead of being cut behind a hidden scrollbar. */
@media (max-width: 900px) {
  .clock { height: 800vh; }

  /* shorter hero scrub on a phone: proportionally less thumb-scroll before
     the instrument; 744vh keeps the film cinematic but brisk. Holds the same
     ratio to the desktop height as the original 620:750 did. */
  .film { height: 744vh; }

  /* the expanded acronym wraps to two lines on a phone, so start the title
     beat below the fixed brand (top-left) instead of letting the line run
     underneath it */
  .beat--title { justify-content: flex-start; padding-top: calc(env(safe-area-inset-top, 0px) + 4.2rem); }
  .beat--title .brand__expand { max-width: 30ch; }

  /* deeper top/bottom scrim: the tall portrait crop keeps the bright dawn
     behind the beats, so the counts need more shade to stay legible */
  .film__scrim {
    background:
      radial-gradient(130% 90% at 50% 42%, transparent 40%, rgba(10,8,6,0.6) 100%),
      linear-gradient(to bottom, rgba(10,8,6,0.78) 0%, transparent 30%, transparent 46%, rgba(10,8,6,0.92) 100%);
  }

  /* stats: keep only the brand + the countdown that matters; the corpus
     figures are decorative here and cost a whole extra wrapped row. */
  .clock .statsbar { gap: 0.5rem 1.1rem; font-size: 0.58rem; padding-block: 0.5rem; flex-wrap: nowrap; justify-content: flex-start; min-height: 46px; align-items: center; }
  .clock .statsbar .stat b { font-size: 0.9rem; }
  .clock .statsbar .stat:nth-child(2),
  .clock .statsbar .stat:nth-child(3),
  .clock .statsbar .stat:nth-child(4) { display: none; }

  .instrument {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto minmax(0, 1fr);
    gap: 0.5rem;
    padding: 0.4rem var(--pad) 0.5rem;
  }

  /* the hint is the only place that says the returns are tappable, so it stays —
     but at a sliver of its desktop height, and clear of the home indicator */
  .hint {
    padding: 0.1rem var(--pad) calc(0.3rem + env(safe-area-inset-bottom, 0px));
    font-size: 0.55rem; letter-spacing: 0.14em; line-height: 1.3;
  }

  /* index → horizontal jump strip; items tall enough for a finger and
     unselectable so a scrub gesture never starts a text selection */
  .index {
    grid-row: 1;
    flex-direction: row;
    align-items: stretch;
    overflow-x: auto;
    overflow-y: hidden;
    border-right: none;
    border-bottom: 1px solid var(--hair);
    padding: 0 0 0.35rem;
    gap: 0;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    user-select: none;
    -webkit-user-select: none;
    /* quiet track: an ember scrollbar under the strip reads as a second
       active-state underline; keep the ember for the active item only */
    scrollbar-color: rgba(242, 236, 223, 0.22) transparent;
  }
  .index__item {
    width: auto; flex: none;
    flex-direction: column; align-items: center; justify-content: center; gap: 0.05rem;
    border-left: none; border-bottom: 2px solid transparent;
    padding: 0.45rem 0.8rem; min-height: 44px; min-width: 44px; scroll-snap-align: center;  /* 44px: the thumb minimum */
  }
  .index__item.is-active { border-left-color: transparent; border-bottom-color: var(--ember-hot); }
  .index__item .er { display: none; }
  .index__item .yr { font-size: 0.72rem; }

  /* The well is bled to the screen edges instead of boxed.
     A square box was the single worst mobile defect: the well's near rings
     project far wider than the wound spiral, so on every phone they ended on a
     hard straight line at the box edge. Full width lets a ring run off the side
     of the phone (reads as bleed, not as a cut), and the canvas mask fades the
     top and bottom so the vertical edges stop being lines too. Short-and-wide
     also hands ~70px of height back to the readout. */
  .clockwrap {
    grid-row: 2;
    width: auto; aspect-ratio: auto;
    height: min(32svh, 52vw);
    margin-inline: calc(var(--pad) * -1);
  }
  #clockCanvas, .clockgl {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 6%, #000 94%, transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 6%, #000 94%, transparent 100%);
  }
  .legend { display: none; }  /* dot meanings live in the readout eyebrow on mobile */

  /* readout fills the rest; tighter type + a bottom fade so a long entry
     that overruns ends softly rather than clipping behind a scrollbar.
     The fade is a FIXED sliver matched by padding-bottom, so when content
     fits, the last line sits above the fade instead of being dimmed by it. */
  .readout {
    grid-row: 3; padding-right: 0; gap: 0.55rem; padding-bottom: 1.5rem;
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 1.3rem), transparent 100%);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 1.3rem), transparent 100%);
  }
  .readout__date { font-size: 0.82rem; }
  .readout__title { font-size: 1.4rem; }
  .readout__lead { font-size: 0.98rem; line-height: 1.4; }
  .readout__context { font-size: 0.86rem; line-height: 1.55; }
  .readout__world li { font-size: 0.72rem; }
  .readout__src { display: none; }  /* longest, least essential line; drop on mobile */
}


/* short phones (SE-class): the one-screen budget is ~630px, so shrink the
   spiral further and show two "around this time" entries instead of three —
   better two readable than three with the last lost under the fade */
@media (max-width: 900px) and (max-height: 700px) {
  .clock .statsbar { padding-block: 0.4rem; }
  .clockwrap { height: min(28svh, 46vw); }
  .readout__lead { display: none; }  /* the title + context carry it on a short screen */
  .readout__world li:nth-child(n + 3) { display: none; }
}

/* ---------- phone in landscape ----------
   Stacked portrait rows collapse to nothing here (a 390px-tall screen left the
   readout 95px and the spiral 117px). Landscape has width to spare and no
   height, so the instrument turns on its side: year strip across the top, well
   and readout side by side under it. --pad also picks up the notch inset,
   which only bites on this axis. */
@media (max-width: 1000px) and (max-height: 520px) and (orientation: landscape) {
  :root {
    --pad: calc(clamp(1.25rem, 4vw, 3rem) + max(env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px)));
  }

  .clock .statsbar { min-height: 0; padding-block: 0.3rem; font-size: 0.55rem; }
  .clock .statsbar .stat b { font-size: 0.82rem; }

  .instrument {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    grid-template-rows: auto minmax(0, 1fr);
    gap: 0.25rem 1rem;
    padding: 0.3rem var(--pad) calc(0.25rem + env(safe-area-inset-bottom, 0px));
  }
  .index { grid-column: 1 / -1; grid-row: 1; padding-bottom: 0.25rem; }
  /* 44px survives even here — it costs the well and the readout 4px each, which
     is nothing, and a moving thumb in landscape needs the target more, not less */
  .index__item { min-height: 44px; padding-block: 0.3rem; }  /* landscape has no height to spend; 40px stays thumb-sized */

  /* the well keeps the left column and bleeds off the left edge only — the
     right edge is a live boundary against the readout, so it is faded, not cut */
  .clockwrap {
    grid-column: 1; grid-row: 2;
    width: auto; height: auto; aspect-ratio: auto;
    margin: 0 0 0 calc(var(--pad) * -1);
    -webkit-mask-image: linear-gradient(to right, #000 78%, transparent 100%);
    mask-image: linear-gradient(to right, #000 78%, transparent 100%);
  }
  #clockCanvas, .clockgl {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 6%, #000 94%, transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 6%, #000 94%, transparent 100%);
  }

  .readout { grid-column: 2; grid-row: 2; padding-bottom: 1.2rem; gap: 0.4rem; }
  .readout__title { font-size: 1.2rem; }
  .readout__lead { display: none; }
  .readout__context { font-size: 0.8rem; line-height: 1.5; }
  .readout__world li:nth-child(n + 3) { display: none; }

  .hint { display: none; }  /* the strip is right there and the height is gone */

  /* the film's title beat has no vertical room for the two-line acronym here */
  .beat--title { justify-content: center; padding-top: 0; }
  .beat { bottom: clamp(1.2rem, 6vh, 3rem); }
}

/* ---------- THE LOCK ---------- */
.lock {
  min-height: 100svh;
  display: grid;
  place-items: center;
  text-align: center;
  padding: var(--pad);
  background: radial-gradient(120% 80% at 50% -20%, #1a0f08 0%, var(--bg) 55%);
}
.lock > * { max-width: 40rem; }
.lock__kicker {
  font-family: var(--serif);
  font-size: clamp(1.3rem, 3.2vw, 2rem);
  line-height: 1.35;
  color: var(--bone);
  margin: 0 0 2.4rem;
}
.lock__count { margin-bottom: 2.6rem; }
.lock__note a { color: var(--ember-hot); text-underline-offset: 3px; }
.lock__num {
  display: block;
  font-family: var(--mono);
  font-size: clamp(3rem, 12vw, 6.5rem);
  font-variant-numeric: tabular-nums;
  color: var(--ember-hot);
  line-height: 0.95;
  text-shadow: 0 0 60px rgba(255,106,51,0.35);
}
.lock__lab {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted-c);
}
.lock__note {
  font-size: 0.95rem;
  line-height: 1.7;
  color: #b3aa9c;
}
.lock__note strong { color: var(--ink); font-weight: 600; }
.credit {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--muted-c);
  margin-top: 2rem;
}
/* these two sit inside a sentence, so the 44px rule doesn't apply to them — but a
   14px line is still a mean thing to ask a thumb for, so the box is padded out and
   the padding pulled back out of the flow so the credit block reads exactly as before */
.credit a { color: var(--ember); text-decoration: none; display: inline-block; padding: 0.5rem 0.3rem; margin: -0.5rem -0.3rem; }
.credit a:hover { text-decoration: underline; }

/* "click" is wrong on a touch screen, and the scroll half is already taught by
   the film's scroll cue — so a finger gets the short, true half of the line */
.hint__touch { display: none; }
@media (pointer: coarse) {
  .hint__pointer { display: none; }
  .hint__touch { display: inline; }
}
.hint {
  flex: none;
  text-align: center;
  padding: 0.5rem var(--pad) 0.8rem;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-c);
}

.dataerror {
  position: fixed; inset: auto 0 0 0;
  padding: 1rem var(--pad);
  background: #2a0f08; color: var(--ink);
  font-family: var(--mono); font-size: 0.8rem;
  z-index: 40; text-align: center;
}
.dataerror code { color: var(--ember-hot); }

/* canvas cursor when a node is hovered (set by JS) */
#clockCanvas.is-hot { cursor: pointer; }

/* ---------- reduced motion ----------
   The scrub itself stays: it is user-controlled (the film and spiral only move
   as far as the visitor scrolls, never on their own), and collapsing it left
   reduce-motion phones with a dead page. Only self-running animation goes. */
@media (prefers-reduced-motion: reduce) {
  .loader { transition: none; }
  .scroll-cue { transition: none; }
  .readout.is-swap { animation: none; }
}
