/* Tokens, reset, typography and the persistent chrome. */

/*
 * Two worlds, and the argument lives in the gap between them.
 *
 * The film is the machine's world: near-black, lacquer red, gold, everything that glows
 * designed to be looked at. The appendix is the evidence: paper, ruled, printed, sober.
 * When the reader falls out of the room and onto the page, the change in material is the
 * point — one of these is trying to hold your attention, the other is trying to be checked.
 */
:root {
  /* The room is almost black. Everything that glows is either the machine or a screen. */
  --ink-900: #05050a;
  --ink-850: #08080f;
  --ink-800: #0c0d15;
  --ink-700: #14151f;
  --ink-600: #1e202c;
  --ink-500: #2b2e3d;
  /* The two dimmest steps carry label text, so they are set to clear 4.5:1 on --ink-850
     rather than to look pleasing in isolation. */
  --ink-400: #7b8398;
  --ink-300: #9aa2b6;
  --ink-200: #bcc3d2;
  --ink-100: #dde1ea;
  --paper: #f2f0ea;

  /* The printed side. Warm stock, iron-gall ink, a red used only for correction marks. */
  --stock: #efe9dc;
  --stock-shade: #e5ddcb;
  --stock-rule: #c9bfa8;
  --stock-rule-soft: #ddd4c0;
  --print: #1c1a16;
  --print-soft: #4a453c;
  --print-faint: #6d675b;
  --print-red: #a8261a;
  --print-seal: #7a1f14;

  /* Machine palette: lacquer red, three golds, and the orb's cold-hot centre. */
  --red-900: #3d0604;
  --red-800: #5c0d08;
  --red-700: #7d130c;
  --red-600: #a81a10;
  --red-500: #d4241a;
  --red-400: #f04a34;
  --gold-100: #fff8d8;
  --gold-200: #ffe9a8;
  --gold-300: #f7d571;
  --gold-400: #e8b53c;
  --gold-500: #c98a12;
  --gold-600: #8a5807;
  --jade: #28b98a;
  --orb: #ffcf5a;

  /* Screens below the floor. */
  --crt-green: #7dffb2;
  --crt-cyan: #86e5ff;
  --crt-amber: #ffc46b;
  --crt-glass: #050b09;

  --font-display: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
  --font-sans: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, 'SF Mono', 'Cascadia Mono', 'Segoe UI Mono', Consolas, monospace;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --rail-w: 172px;

  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }

/* Several of the film's layers set `display` explicitly, which silently beats the user
   agent's rule for [hidden]. Without this the failure fallback hides nothing. */
[hidden] { display: none !important; }

html {
  background: var(--ink-900);
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  min-height: 100%;
  color: var(--ink-100);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  background:
    radial-gradient(120% 80% at 50% -10%, #101018 0%, transparent 60%),
    var(--ink-900);
}

h1, h2, h3, p, dl, dd, figure, ol, ul { margin: 0; }
ol, ul { padding: 0; list-style: none; }
button { font: inherit; color: inherit; }
a { color: var(--gold-300); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--gold-200); }

.sym-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

:where(button, a, [tabindex]):focus-visible {
  outline: 2px solid var(--gold-300);
  outline-offset: 3px;
  border-radius: 3px;
}

.num { font-variant-numeric: tabular-nums; text-align: right; }

/* -- the scroll spacer ---------------------------------------------------- */
/* Its height is the running time of the film. Everything after it is the appendix. */
#range {
  height: 1150vh;
  pointer-events: none;
}

/* -- no JavaScript -------------------------------------------------------- */
/* The film needs script to run at all, so without it none of its furniture should be on
   the page: the fixed stage would cover the viewport in black and the spacer would push
   the fallback 1150 viewport-heights down. Removing them leaves the <noscript> content at
   the top of the document, which is the whole point of having it. */
html:not(.js) :is(#stage, #title, #facts, #hud, #rail, #mute, #live-badge) {
  display: none;
}
html:not(.js) #range { height: 0; }
html:not(.js) #appendix:empty { display: none; }

/* -- skip link ------------------------------------------------------------ */
.skip {
  position: fixed;
  top: 12px;
  left: 50%;
  translate: -50% -160%;
  z-index: 90;
  padding: 10px 18px;
  background: var(--gold-300);
  color: #1a1204;
  border-radius: 999px;
  font-weight: 600;
  font-size: 14px;
  text-decoration: none;
  transition: translate 0.2s var(--ease);
}
.skip:focus { translate: -50% 0; }

/* -- act rail ------------------------------------------------------------- */
/*
 * Not a dot-and-label sidebar. This is a reel strip standing on its edge in the margin:
 * a thin lacquered column with the current act's number stamped beside it, the way a
 * machine numbers its own positions. It carries the same information and stops looking
 * like every other scrollytelling nav.
 */
.rail {
  position: fixed;
  top: 50%;
  left: 26px;
  translate: 0 -50%;
  z-index: 40;
  width: 34px;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
  pointer-events: none;
}
.has-entered .rail { opacity: 1; pointer-events: auto; }
/* The appendix is a document, not a scene: the rail and the counter step aside for it. */
.is-reading .rail,
.is-reading .live { opacity: 0; pointer-events: none; }

.rail__track {
  position: absolute;
  left: 15px;
  top: 0;
  bottom: 0;
  width: 3px;
  background: #1a1218;
  box-shadow: inset 0 0 0 1px rgba(255, 200, 120, 0.14);
  overflow: hidden;
}
.rail__fill {
  position: absolute;
  inset: 0 0 auto 0;
  height: 100%;
  background: linear-gradient(180deg, var(--gold-200), var(--gold-500) 60%, var(--red-600));
  transform-origin: top;
  transform: scaleY(var(--p, 0));
  transition: transform 0.1s linear;
  box-shadow: 0 0 10px rgba(255, 176, 40, 0.7);
}

.rail__list { display: grid; gap: 0; }
.rail__list button {
  display: grid;
  place-items: center;
  width: 34px;
  min-height: 34px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--ink-500);
  transition: color 0.25s var(--ease);
}
.rail__list button:hover { color: var(--gold-200); }
/* The stop notch sits on the strip; the label only appears for the act you are in.
   The notch grows by scaling a fixed-size bar rather than animating its width, so the
   change stays on the compositor instead of laying the rail out on every frame. */
.rail__dot {
  width: 21px; height: 1px;
  background: currentColor;
  transform: scaleX(0.43);
  transform-origin: left center;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
/* The label only ever appears on the current act or on hover, and in both of those states
   the button is gold. Setting the colour here rather than inheriting the dim notch colour
   means it is legible whenever it is visible, instead of relying on opacity to hide an
   unreadable one. */
.rail__label {
  position: absolute;
  left: 42px;
  white-space: nowrap;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-200);
  opacity: 0;
  translate: -6px 0;
  transition: opacity 0.3s var(--ease), translate 0.3s var(--ease);
  pointer-events: none;
}
.rail__list button.is-on { color: var(--gold-200); }
.rail__list button.is-on .rail__dot { transform: scaleX(1); box-shadow: 0 0 8px var(--gold-400); }
.rail__list button.is-on .rail__label,
.rail__list button:hover .rail__label { opacity: 1; translate: 0 0; }
.rail__list li { position: relative; display: flex; }

/* Sits under the strip and only names itself on approach, so the margin stays a margin. */
.rail__skip {
  position: absolute;
  left: 3px; top: calc(100% + 18px);
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 28px;
  padding: 4px 6px 4px 0;
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink-400);
  text-decoration: none;
}
.rail__skip::before {
  content: '';
  width: 15px; height: 1px;
  background: currentColor;
}
.rail__skip span {
  opacity: 0;
  translate: -6px 0;
  transition: opacity 0.25s var(--ease), translate 0.25s var(--ease);
}
.rail__skip:hover,
.rail__skip:focus-visible { color: var(--gold-300); }
.rail__skip:hover span,
.rail__skip:focus-visible span { opacity: 1; translate: 0 0; }

/* -- sound toggle --------------------------------------------------------- */
.mute {
  position: fixed;
  top: 20px; right: 20px;
  z-index: 45;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 14px 8px 11px;
  background: color-mix(in oklab, var(--ink-800) 82%, transparent);
  border: 1px solid var(--ink-600);
  border-radius: 999px;
  color: var(--ink-200);
  font-size: 12px;
  letter-spacing: 0.05em;
  cursor: pointer;
  backdrop-filter: blur(8px);
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.mute:hover { border-color: var(--gold-500); color: var(--gold-200); }

.mute__icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  height: 14px;
}
.mute__icon i {
  width: 2px;
  border-radius: 1px;
  background: currentColor;
  height: 5px;
}
.mute[data-state='on'] { color: var(--gold-200); border-color: var(--gold-600); }
.mute[data-state='on'] .mute__icon i { animation: bars 1s var(--ease) infinite alternate; }
.mute[data-state='on'] .mute__icon i:nth-child(2) { animation-delay: 0.15s; }
.mute[data-state='on'] .mute__icon i:nth-child(3) { animation-delay: 0.3s; }
.mute[data-state='off'] .mute__icon i { height: 4px; opacity: 0.5; }

@keyframes bars { from { height: 4px; } to { height: 14px; } }

/* -- live national counter ------------------------------------------------ */
/* Bottom left, clear of the machine panel on the right. */
.live {
  position: fixed;
  bottom: 20px; left: 20px;
  z-index: 44;
  padding: 10px 15px;
  background: color-mix(in oklab, var(--ink-800) 86%, transparent);
  border: 1px solid var(--ink-600);
  border-radius: 4px;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-300);
  backdrop-filter: blur(8px);
  opacity: 0;
  translate: 0 12px;
  transition: opacity 0.5s var(--ease), translate 0.5s var(--ease);
  pointer-events: none;
}
.live.is-open { opacity: 1; translate: 0 0; }
/* A live indicator that says something — it pulses because the number is still climbing —
   rather than a coloured stripe down the edge of the card. */
.live__dot {
  display: inline-block;
  width: 6px; height: 6px;
  margin-right: 7px;
  border-radius: 50%;
  background: var(--red-500);
  box-shadow: 0 0 8px rgba(212, 36, 26, 0.9);
  animation: live-pulse 1.9s ease-in-out infinite;
}
@keyframes live-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.72); }
}
@media (prefers-reduced-motion: reduce) {
  .live__dot { animation: none; }
}
.live b {
  display: block;
  margin-top: 2px;
  font-family: var(--font-mono);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--red-400);
  font-variant-numeric: tabular-nums;
}
.live span { display: block; margin-top: 3px; font-size: 10px; color: var(--ink-400); }

/* -- reduced motion ------------------------------------------------------- */
/* A shorter run, but still long enough that each fragment gets read rather than flicked
   past: eleven of them share about 40% of this height. */
.reduced-motion #range { height: 620vh; }

/*
 * Below the width that fits a margin, the strip lies down along the bottom edge — the same
 * eight stops, the same fill, read left to right instead of top to bottom. Removing it
 * outright would leave a phone reader with no way to reach the monitor wall except by
 * scrolling the whole film.
 */
@media (max-width: 1080px) {
  .rail {
    top: auto; bottom: 0; left: 0;
    translate: none;
    width: 100%;
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
    background: linear-gradient(180deg, transparent, rgba(4, 4, 8, 0.92) 42%);
  }
  .rail__track {
    left: 12px; right: 12px;
    top: 10px; bottom: auto;
    width: auto; height: 3px;
  }
  .rail__fill {
    inset: 0 auto 0 0;
    width: 100%; height: 100%;
    transform: scaleX(var(--p, 0));
  }
  .rail__list {
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    margin-top: 12px;
  }
  .rail__list button { width: 100%; min-height: 40px; }
  .rail__dot {
    width: 1px; height: 19px;
    transform: scaleY(0.47);
    transform-origin: center top;
  }
  .rail__list button.is-on .rail__dot { transform: scaleY(1); }
  /* On a narrow screen the current act names itself under the strip rather than beside it. */
  .rail__label {
    left: 50%;
    top: 22px;
    translate: -50% 4px;
    font-size: 8.5px;
    letter-spacing: 0.12em;
  }
  .rail__list button.is-on .rail__label { translate: -50% 0; }
  .rail__list button:hover .rail__label { translate: -50% 0; }
  .rail__skip {
    left: auto; right: 12px;
    top: 10px;
  }
  .rail__skip::before { display: none; }
  .rail__skip span { opacity: 1; translate: 0 0; }
  .live { display: none; }
}
