/* ---------------------------------------------------------------------------
   Daily Marble — the championship annual.

   The video is a dark arena with neon track lines and coloured spheres. The site is
   not the video: it is the book the video gets written into at the end of the day.
   Ink on paper, hairline rules, tabular figures, and colour used the way a printed
   results book uses it — as a key that identifies a team, never as atmosphere.

   The page was a dark one until this pass, and it was a dark one in the specific way
   that every generated dark page is: a near-black ground, two coloured radial washes
   bleeding up from the corners, a grain overlay, glowing borders, and every block
   fading up as it entered the viewport. Each of those is defensible alone. Together
   they are a house style that belongs to no house — and a championship whose whole
   claim is that the numbers are real is badly served by a page that looks generated.

   So: paper. The constraints that follow from it, which are the actual design:

     - Colour identifies, it does not decorate. A team colour appears as a solid key
       or a rule and never as a glow, because ink does not glow.
     - Rules do the work borders used to. One hairline is a division; two are a
       section break. Nothing needs a box.
     - Figures are tabular everywhere they are compared down a column, which on this
       site is everywhere.
     - Motion is for arrival only, and it is a fade and a few pixels. Paper does not
       slide, scale or blur.
     - The mono face carries only things you could paste into a terminal — seeds,
       track names, section ids. If a number is not verifiable from a seed, it is
       not set in mono. (This rule survived the redesign; it was always the good one.)
   --------------------------------------------------------------------------- */

@import url('https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,500&family=Instrument+Sans:ital,wght@0,400;0,500;0,600;1,400&family=JetBrains+Mono:wght@400;500;600&display=swap');

:root {
  /* Paper. Four steps, not two — a sheet that sits on a card that sits on the page
     needs three distinguishable warms or everything flattens into one slab. They are
     all off-white and all slightly warm, because a neutral grey scale on a page this
     size reads as unprinted screen rather than as stock. */
  --paper: #F1EDE1;   /* the page */
  --tint:  #E8E2D2;   /* an inset: a panel recessed into the page */
  --sheet: #FAF7EF;   /* a sheet laid on it: tables, cards */
  --card:  #FFFDF8;   /* the top of the stack, and the only near-white */
  --rule:  #D6CEBA;   /* hairline */
  --rule-2:#B2A78C;   /* the heavier rule, for section breaks and table heads */

  --ink:   #14110B;   /* body ink, warm black — a true #000 on cream reads as a hole */
  --ink-2: #39332A;
  --dim:   #6C6454;
  --faint: #9A927D;

  /* The house colour, from brand/CHANNEL.md. #FF6B6B is a fill: on cream it sits at
     roughly 2.3:1, which is a fine block of colour and an unreadable word. So the
     brand has two values — the block, and the same hue taken down until it can carry
     text. Both are the brand; only one of them is allowed to be a letterform. */
  --house:     #FF6B6B;
  --house-ink: #B0332E;
  --house-deep:#8A241F;

  /* The plate: the masthead band, reversed out of the paper ramp rather than borrowed
     from somewhere else. It used to be #07080C with cool blue-greys inside it, which
     made the first thing on the page the one cold object in a warm book — and broke
     the rule three lines up, that a true black on this stock reads as a hole. These
     are the same warm hue family with the lightness inverted, so the masthead reads
     as heavy ink on the same sheet. The four steps carry 17.4:1, 11.6:1, 7.1:1 and
     5.1:1 against --plate; the last is the floor because the labels it sets are
     9.5px, and small type is where a ramp like this normally fails. */
  --plate:       #0C0A06;
  --plate-ink:   #F4F0E4;
  --plate-2:     #CFC7B4;
  --plate-dim:   #A39B88;
  --plate-faint: #8A8271;

  /* Signals. Only ever direction and outcome, never decoration. Darkened for paper. */
  --gain: #1C7A4B;
  --loss: #B03626;
  --gold: #8A6A08;

  /* The episode colour — the winner's, set inline by bin/site.js on every page, and
     already pulled down to paper contrast by ui() there. It marks the day; --house
     marks the publisher. Keeping them separate is why the masthead does not change
     colour when Coral wins. */
  --accent: var(--house-ink);

  --display: 'Archivo', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --body: 'Instrument Sans', 'Segoe UI', system-ui, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, Consolas, monospace;

  --gutter: clamp(20px, 5vw, 76px);
  --maxw: 1280px;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --snap: cubic-bezier(0.16, 1, 0.3, 1);
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Stock, not atmosphere.
   What used to be here was two coloured radial gradients and a dot grid — the exact
   background a page gets when it wants to look like something without deciding what.
   This is a paper tooth: fibre noise at 3.5%, multiplied into the ground so it darkens
   rather than veils, and nothing else. On a phone it is invisible and correct; at
   desktop size it is the difference between stock and a flat fill. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.86' numOctaves='4'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

main, header, footer {
  position: relative;
  z-index: 1;
  width: 100%;
  min-width: 0;
}

/* Takes the slack, so the footer lands on the fold of a short page and not above it. */
main { flex: 1 0 auto; }
.foot { flex: none; }

a { color: inherit; text-decoration: none; }

::selection { background: var(--house); color: var(--card); }

/* ------------------------------------------------------------------ atoms */

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding-inline: var(--gutter);
}

/* The section index. A broadcast package numbers its segments and so does a sports
   annual; the number is what gives a long scroll a spine you can feel. It is a real
   boxed chip rather than a floating figure so it reads as furniture, not as content. */
.eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--dim);
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0;
}

.eyebrow i {
  font-style: normal;
  font-weight: 600;
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--accent);
  padding: 5px 8px 4px;
  border: 1px solid color-mix(in srgb, var(--accent) 36%, transparent);
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  border-radius: 2px;
  flex: none;
}

/* The rule trails off after the label rather than leading it — it reads as an
   underline for the whole section rather than a bullet for the word. */
.eyebrow::after {
  content: '';
  height: 1px;
  flex: 1;
  max-width: 180px;
  background: linear-gradient(90deg, var(--rule-2), transparent);
}

.h2 {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(36px, 5.4vw, 70px);
  line-height: 0.94;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  margin: 20px 0 0;
  text-wrap: balance;
}

.lede {
  max-width: 60ch;
  color: var(--dim);
  font-size: clamp(16px, 1.4vw, 19px);
  line-height: 1.6;
  margin: 22px 0 0;
}

.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* Everywhere digits are compared down a column — and nowhere else. */
.mast__strip, .sheet, .standing, .team, .vs, .trace, .foot__receipt, table {
  font-variant-numeric: tabular-nums;
}

.rule {
  height: 1px;
  background: linear-gradient(90deg, var(--rule), transparent);
  border: 0;
  margin: 0;
}

/* Buttons. Two weights, because the page only ever asks for two things: go and watch
   the race, or go and look at the rest of the grid. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 13px 22px;
  border: 1px solid var(--rule-2);
  border-radius: 3px;
  background: var(--sheet);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transition: border-color 0.25s, background 0.25s, transform 0.25s var(--ease);
}
.btn:hover { border-color: var(--house); transform: translateY(-2px); }

.btn--primary {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--card);
  font-weight: 600;
}
.btn--primary:hover {
  background: var(--house-deep);
  border-color: var(--house-deep);
}

/* ---------------------------------------------------------------- rhythm */

section { padding-block: clamp(72px, 9vh, 128px); }

/* Reveal-on-scroll. One transform, one opacity, staggered by index in main.js. */
.reveal {
  opacity: 0;
  transform: translateY(9px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
  transition-delay: var(--d, 0ms);
}
.reveal.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
  * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; }
}

/* ------------------------------------------------------------------- nav */

.topbar {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}

/* A hairline of the day's colour under the whole header. It is the one place the
   episode colour touches the brand furniture, and it is one pixel, which is the
   right amount: the header says Daily Marble, the pixel says which day. */
.topbar::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 1px;
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 18%, transparent) 42%, transparent 78%);
}

.topbar__in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: 74px;
}

/* --- the lockup ---------------------------------------------------------- */

.brand {
  display: flex;
  align-items: center;
  gap: 13px;
  white-space: nowrap;
}

/* The channel avatar, masked to the circle it was drawn for — the same crop YouTube
   applies, so the header and the channel page carry one object rather than two. The
   ring of six team arcs only survives above about 28px, which is why the lockup is
   larger than the old mark and the 32px favicon is cropped past the ring entirely. */
.brand__mark {
  width: 34px;
  height: 34px;
  flex: none;
  display: block;
  border-radius: 50%;
  background: #0A0B10;
}

.brand__type {
  display: flex;
  flex-direction: column;
  gap: 2px;
  line-height: 1;
}

.brand__type b {
  font-family: var(--display);
  font-weight: 800;
  font-size: 21px;
  letter-spacing: -0.025em;
  line-height: 0.9;
  text-transform: uppercase;
}

/* The tagline sits under the name at micro size. It is the channel's one line and it
   costs nine pixels of header to say it on every page. */
.brand__type i {
  font-family: var(--mono);
  font-style: normal;
  font-size: 8.5px;
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
}

@media (max-width: 560px) { .brand__type i { display: none; } }

/* --- links --------------------------------------------------------------- */

.nav {
  display: flex;
  gap: 30px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
}

.nav a { position: relative; padding-block: 6px; transition: color 0.2s; }
.nav a:hover { color: var(--ink); }
.nav a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 0; height: 1px;
  background: var(--house);
  transition: width 0.3s var(--ease);
}
.nav a:hover::after { width: 100%; }

.topbar__meta {
  display: flex;
  align-items: center;
  gap: 18px;
}

/* The one outbound call to action in the header. The site exists to send people to
   the race, so it is a filled pill in the house colour and nothing else here is. */
.topbar__yt {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px 7px;
  border-radius: 100px;
  background: var(--ink);
  color: var(--card);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transition: transform 0.25s var(--snap), box-shadow 0.25s;
}
.topbar__yt i { font-style: normal; font-size: 8px; }
.topbar__yt:hover { background: var(--house-deep); }

.topbar__round {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--faint);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.topbar__round b { color: var(--ink); font-weight: 600; }
.topbar__round u { text-decoration: none; }

@media (max-width: 1040px) { .nav { display: none; } }
@media (max-width: 480px) { .topbar__round { display: none; } }

/* --- the race trace ------------------------------------------------------ */
/*
   The diagram that replaced the autoplaying clip. Everything in it is drawn by
   raceTrace() in bin/site.js from the episode JSON; the stylesheet only decides how
   heavy the ink is, which is the right division — the shape is data, the weight is
   design, and neither should be editing the other. */

/* Every other section puts a .lede under its h2, which carries the gap; the result
   section hangs the chart straight off the heading, so the gap has to come from here or
   a 70px display heading sits flush on the panel's top border. */
.h2 + .trace {
  margin-top: clamp(22px, 2.2vw, 30px);
}

.trace {
  margin: 0;
  background: var(--sheet);
  border: 1px solid var(--rule);
  padding: clamp(14px, 1.8vw, 24px) clamp(10px, 1.4vw, 18px) 0;
}

.trace__svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* The ruled ground the lines are read against: faint enough to sit under the data,
   present enough that a line's position is readable without a legend. */
.trace__rule {
  stroke: var(--rule);
  stroke-width: 1;
}

.trace__line {
  fill: none;
  stroke-width: 1.6;
  stroke-linecap: round;
  opacity: 0.62;
}

/* The winner's line is the only one at full weight. On a chart of ten paths the reader
   needs one to start from, and the page has already said whose it is. */
.trace__line.is-win {
  stroke-width: 3.4;
  opacity: 1;
}

.trace__dot { opacity: 0.62; }
.trace__dot.is-win { opacity: 1; }

.trace__axis {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.18em;
  fill: var(--faint);
}

.trace__tick {
  font-family: var(--mono);
  font-size: 11px;
  fill: var(--faint);
}

.trace__note {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  fill: var(--ink-2);
}

.trace__mark {
  stroke: var(--ink-2);
  stroke-width: 1;
}

.trace__pos {
  font-family: var(--mono);
  font-size: 11px;
  fill: var(--faint);
}

.trace__name {
  font-family: var(--body);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.trace__name.is-win { font-size: 15px; font-weight: 700; }

.trace__cap {
  font-size: 13px;
  line-height: 1.5;
  color: var(--dim);
  margin: 0;
  padding: 14px 6px 16px;
  border-top: 1px solid var(--rule);
}

/* --- the masthead --------------------------------------------------------- */
/*
   The one dark band on the site, and it holds both halves of what the page is about:
   the round on the left, the season on the right. Earlier versions had the atom alone
   in a full-width black field with the headline stranded on paper below it — a small
   object in a lot of empty dark, which read as a hole in the page rather than as a
   masthead. Filling the band is what makes it deliberate.

   Colours come from the --plate ramp rather than from literals. They used to be
   literal, on the reasoning that a dark island in a paper document cannot borrow the
   page's ink — but the greys that arrived that way were cool blue (#9AA5B3, #8892A0,
   #757F8C) inside a document whose every other grey is warm, which made the masthead
   look imported rather than printed. The ramp is the page's own hue family with the
   lightness inverted, which is a different thing from borrowing the page's ink.
*/

.mast {
  position: relative;
  isolation: isolate;
  background: var(--plate);
  border-bottom: 1px solid var(--rule-2);
  padding-block: clamp(32px, 4.4vw, 58px) 0;
}

/* The paper tooth, again, because the sitewide one cannot reach here: body::before
   sits at z-index 0 behind an opaque .mast, and it multiplies, which is a no-op on a
   ground this dark (7 x anything / 255 is still 7). So the largest flat area on the
   site was the only one with no stock in it. Screen lifts instead of darkening. */
.mast::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.055;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.86' numOctaves='4'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

.mast__in {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr);
  gap: clamp(20px, 3vw, 54px);
  align-items: center;
}

@media (max-width: 900px) {
  .mast__in { grid-template-columns: minmax(0, 1fr); }
}

.mast__round { min-width: 0; padding-bottom: clamp(20px, 3vw, 38px); }

/* The round and the date are one line, not two ends of the stack. They answer the
   same question — where are we in the schedule — and splitting them left the block
   closing on its quietest element instead of on the numbers. */
.mast__eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--plate-dim);
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 14px;
}

.mast__eyebrow time {
  color: var(--plate-faint);
  padding-left: 14px;
  border-left: 1px solid #262019;
}

/* Wrapped onto its own line, the rule reads as a stray mark rather than a divider. */
@media (max-width: 460px) {
  .mast__eyebrow time { padding-left: 0; border-left: 0; }
}

/* The wordmark used to be the winner's name, sized by a --len custom property so
   five letters and ten letters both filled the column. It is the brand's own name
   now — fixed text, not fixed to shrink for it — so this is a flat, confident
   clamp instead: big at desktop, still commanding at a phone's width. */
.mast__brand {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(60px, 8.6vw, 140px);
  line-height: 0.86;
  letter-spacing: -0.045em;
  text-transform: uppercase;
  margin: 14px 0 0;
  color: var(--plate-ink);
}

.mast__deck {
  font-family: var(--body);
  font-weight: 500;
  font-size: clamp(18px, 1.65vw, 21px);
  line-height: 1.4;
  margin: 16px 0 0;
  max-width: 34ch;
  color: var(--plate-2);
}

/* The epithet is a caption on the deck, not a rival to it. The accent bar already
   marks it as an aside; at 19px against a 25px deck the size was arguing otherwise,
   and the stat numbers below were louder than both. */
.mast__epithet {
  font-family: var(--body);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(14.5px, 1.2vw, 16.5px);
  line-height: 1.5;
  color: color-mix(in srgb, var(--house) 72%, white);
  margin: 18px 0 0;
  padding-left: 14px;
  border-left: 2px solid var(--house);
  max-width: 46ch;
}

.mast__strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: 22px 0 0;
  border-top: 1px solid #212733;
  border-bottom: 1px solid #161B24;
  font-variant-numeric: tabular-nums;
}

.mast__strip > div {
  padding: 11px 14px 12px;
  border-right: 1px solid #161B24;
  min-width: 0;
}
.mast__strip > div:first-child { padding-left: 0; }
.mast__strip > div:last-child { border-right: 0; padding-right: 0; }

.mast__strip dt {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--plate-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mast__strip dd {
  margin: 6px 0 0;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(24px, 2.4vw, 32px);
  line-height: 1;
  color: var(--plate-ink);
  white-space: nowrap;
}

.mast__strip dd small { font-size: 0.42em; font-weight: 500; color: var(--plate-dim); margin-left: 2px; }

/* The one orchestrated entrance on the page, and the only element above the fold that
   moves. It is a plain animation rather than the .reveal class every section below
   uses, because .reveal starts at opacity 0 and waits for the observer in main.js to
   add .in — fine four screens down, a blank masthead if that script ever fails to
   run. A CSS animation cannot fail to arrive. Four numbers ticking up behind a
   wordmark that does not move is the whole effect; nothing else here needs one. */
@media (prefers-reduced-motion: no-preference) {
  .mast__strip > div { animation: mast-tick 620ms var(--snap) both; }
  .mast__strip > div:nth-child(1) { animation-delay: 140ms; }
  .mast__strip > div:nth-child(2) { animation-delay: 220ms; }
  .mast__strip > div:nth-child(3) { animation-delay: 300ms; }
  .mast__strip > div:nth-child(4) { animation-delay: 380ms; }
}

@keyframes mast-tick {
  from { opacity: 0; transform: translateY(9px); }
}

@media (max-width: 620px) {
  .mast__strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mast__strip > div:nth-child(2) { border-right: 0; padding-right: 0; }
  .mast__strip > div:nth-child(-n + 2) { border-bottom: 1px solid #161B24; }
}

/* --- the atom ------------------------------------------------------------ */

.atom {
  margin: 0;
  position: relative;
  height: clamp(320px, 44vw, 520px);
  min-width: 0;
  align-self: stretch;
}

.atom canvas {
  display: block;
  position: absolute;
  inset: 0 0 34px 0;
  width: 100%;
  height: calc(100% - 34px);
  z-index: 1;
  touch-action: pan-y;
  cursor: grab;
}

.atom canvas:active { cursor: grabbing; }

.atom__flat[hidden] { display: none; }

.atom__flat {
  display: block;
  width: auto;
  height: 68%;
  position: absolute;
  top: 10%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1;
}

/* The caption is the atom's own footing, not the band's: it sits under the object and
   names whichever marble the pointer is on. */
.atom__cap {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  display: flex;
  align-items: baseline;
  gap: 10px 16px;
  flex-wrap: wrap;
  height: 34px;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  /* On the plate, so it takes the plate's ramp. The literal it replaces was #5A6572,
     a cool grey carrying 3.3:1 at 9px — the worst contrast on the site, on the one
     piece of type set smallest. --plate-faint is 5.1:1 at the same size. */
  color: var(--plate-faint);
}

.atom__read { display: flex; align-items: baseline; gap: 8px; }
.atom__read b { color: var(--plate-ink); font-weight: 600; font-size: 11px; letter-spacing: 0.09em; }
.atom__read span { color: var(--plate-dim); }

.atom__note { margin-left: auto; text-align: right; }
.atom__note b { color: var(--house); font-weight: 600; }

@media (max-width: 900px) {
  .atom__note { margin-left: 0; text-align: left; }
}

/* --------------------------------------------------------- the results sheet */
/*
   A real table, ruled like a printed one. The rules to keep if this is ever touched:
   every figure column is tabular and right-aligned, the header is a rule rather than a
   filled bar, and the only fill on the whole table is the winner's row. */

.sheet {
  width: 100%;
  border-collapse: collapse;
  margin: 36px 0 0;
  font-variant-numeric: tabular-nums;
  background: var(--sheet);
}

.sheet thead th {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
  text-align: left;
  padding: 0 12px 9px;
  border-bottom: 2px solid var(--ink);
  white-space: nowrap;
}

.sheet tbody td {
  padding: 13px 12px;
  border-bottom: 1px solid var(--rule);
  vertical-align: middle;
}

.sheet tbody tr { transition: background 0.18s; }
.sheet tbody tr:hover { background: var(--card); }

/* The winner's row is the one filled row on the page. A tint of the day's colour at 9%
   is enough to find from across the room and light enough that the ink on it is still
   the same ink; a glowing border would be neither. */
.sheet tbody tr.is-win {
  background: color-mix(in srgb, var(--c) 9%, var(--card));
}
.sheet tbody tr.is-win:hover {
  background: color-mix(in srgb, var(--c) 14%, var(--card));
}

.sheet__pos,
.sheet__grid,
.sheet__delta,
.sheet__pts { text-align: right; white-space: nowrap; }

td.sheet__pos {
  font-family: var(--display);
  font-weight: 700;
  font-size: 22px;
  line-height: 1;
  color: var(--faint);
  width: 1%;
  padding-right: 18px;
}

tr.is-podium td.sheet__pos { color: var(--ink-2); }
tr.is-win td.sheet__pos { color: var(--c); font-size: 30px; }

.sheet__pos { width: 7%; }
.sheet__racer { width: 29%; }
.sheet__team { width: 28%; }
.sheet__grid { width: 12%; }
.sheet__delta { width: 12%; }
.sheet__pts { width: 12%; }

.sheet__racer a {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  font-size: 15px;
  letter-spacing: 0.03em;
  color: var(--ink);
}
.sheet__racer a:hover { color: var(--c); }

tr.is-win .sheet__racer a { font-size: 17px; font-weight: 700; }

/* The key: a solid block of the team's colour, the printed way to tie a row to a
   legend. Same colour, same job, as the rule in the field index above. */
.sheet__key {
  width: 10px;
  height: 10px;
  flex: none;
  background: var(--c);
}

td.sheet__team {
  font-size: 13px;
  color: var(--dim);
  letter-spacing: 0.02em;
}

td.sheet__grid { color: var(--dim); font-size: 14px; }

.d {
  font-family: var(--mono);
  font-size: 11.5px;
  font-weight: 500;
}
.d--up { color: var(--gain); }
.d--down { color: var(--loss); }
.d--flat { color: var(--faint); }

td.sheet__pts {
  font-family: var(--display);
  font-weight: 700;
  font-size: 20px;
  color: var(--ink);
  width: 1%;
}
tr.is-win td.sheet__pts { color: var(--c); }

.sheet tfoot td {
  padding: 11px 12px;
  border-top: 2px solid var(--ink);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--dim);
}
.sheet tfoot td.sheet__pts {
  font-family: var(--display);
  font-size: 18px;
  letter-spacing: 0;
  color: var(--ink);
}

/* The track, as a printed sequence. */
.track-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 26px 0 0;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--dim);
  letter-spacing: 0.04em;
}

.track-line span:first-child {
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--card);
  background: var(--ink);
  padding: 3px 7px 2px;
}

.track-line u {
  text-decoration: none;
  color: var(--rule-2);
}

/* ----------------------------------------------------------- standings */

.split {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: clamp(28px, 4vw, 64px);
  margin-top: 48px;
  align-items: start;
}
@media (max-width: 940px) { .split { grid-template-columns: 1fr; } }

.panel-title {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
  padding-bottom: 12px;
  border-bottom: 1px solid var(--rule);
  display: flex;
  justify-content: space-between;
}

.standing {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr) 56px;
  align-items: center;
  gap: 14px;
  padding: 10px 0;
  border-bottom: 1px solid rgba(28, 33, 48, 0.55);
  position: relative;
}
.standing__n { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.standing__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 19px;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  line-height: 1;
  display: flex;
  align-items: baseline;
  gap: 9px;
}
.standing__name em {
  font-family: var(--mono);
  font-style: normal;
  font-size: 9px;
  letter-spacing: 0.14em;
  color: var(--c);
}
.standing__bar {
  grid-column: 2 / 3;
  height: 3px;
  margin-top: 7px;
  background: var(--rule);
  overflow: hidden;
}
.standing__bar i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--c);
  transition: width 1.1s var(--ease);
  transition-delay: var(--d, 0ms);
}
.in .standing__bar i { width: var(--w); }
.standing__pts {
  grid-row: 1 / 3;
  grid-column: 3 / 4;
  font-family: var(--display);
  font-weight: 700;
  font-size: 30px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.standing--zero { opacity: 0.42; }
.standing--zero .standing__bar { display: none; }

.teams { display: grid; gap: 12px; }

.team {
  border: 1px solid var(--rule);
  border-left: 4px solid var(--c);
  background: var(--sheet);
  padding: 16px 18px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 4px 16px;
  align-items: center;
  transition: transform 0.4s var(--ease), border-color 0.3s;
}
.team:hover { background: var(--card); border-color: var(--rule-2); }
.team__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 22px;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  line-height: 1;
}
.team__motto {
  grid-column: 1 / 2;
  font-size: 14px;
  font-style: italic;
  color: var(--dim);
}
.team__pts {
  grid-row: 1 / 3;
  grid-column: 2 / 3;
  font-family: var(--display);
  font-weight: 700;
  font-size: 42px;
  color: var(--c);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.team__crew { grid-column: 1 / 3; display: flex; gap: 6px; margin-top: 10px; }
.team__crew img {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: var(--tint);
  transition: transform 0.3s var(--ease);
}
.team:hover .team__crew img { transform: translateY(-3px); }

/* ---------------------------------------------------------------- roster */

/* The story layer. It gets one block on this page and nowhere else — not a frame of
   video, which is the whole deal lore is on: it makes the tenth episode land, and it
   is never allowed to cost a second of the first one. */
.lore {
  margin-top: 40px;
  max-width: 74ch;
  border-left: 2px solid var(--rule);
  padding-left: clamp(16px, 3vw, 30px);
}
.lore__eyebrow {
  font-family: var(--display);
  font-weight: 800;
  font-size: 26px;
  line-height: 1;
  text-transform: uppercase;
  margin: 0 0 12px;
}
.lore__text { font-size: 15.5px; line-height: 1.62; color: var(--dim); margin: 0 0 10px; }
.lore__text--dim { color: var(--faint); }

.aspects {
  margin-top: 30px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
@media (max-width: 900px) { .aspects { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .aspects { grid-template-columns: minmax(0, 1fr); } }
.aspect {
  border: 1px solid var(--rule);
  border-top: 2px solid var(--c);
  border-radius: 4px;
  background: var(--tint);
  padding: 16px 15px 14px;
}
.aspect__name {
  font-family: var(--display);
  font-weight: 900;
  font-size: 25px;
  line-height: 1;
  color: var(--c);
}
.aspect__origin {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-top: 6px;
}
.aspect__doctrine { font-size: 13.5px; line-height: 1.45; color: var(--dim); margin: 11px 0 0; }
.aspect__team {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}

/* The aspect rides along in the filter pill, small and dim, so the six teams read as
   six flavours rather than six colours without costing a second row of controls. */
.filters button i {
  font-style: normal;
  opacity: 0.55;
  margin-left: 5px;
}
.filters button[aria-pressed='true'] i { opacity: 0.7; }


.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 34px;
}
.filters button {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
  background: transparent;
  border: 1px solid var(--rule);
  padding: 8px 15px;
  cursor: pointer;
  transition: all 0.25s;
}
.filters button:hover { color: var(--ink); border-color: var(--faint); }
.filters button[aria-pressed='true'] {
  color: var(--card);
  background: var(--c, var(--ink));
  border-color: var(--c, var(--ink));
  font-weight: 600;
}


.grid {
  margin-top: 26px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 14px;
}

.racer {
  position: relative;
  display: block;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: var(--tint);
  padding: 22px 18px 20px;
  overflow: hidden;
  transition: transform 0.45s var(--ease), border-color 0.35s, opacity 0.35s;
}
.racer::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 3px;
  background: var(--c);
  opacity: 0;
  transition: opacity 0.3s;
  pointer-events: none;
}
.racer:hover { transform: translateY(-3px); background: var(--card); border-color: var(--rule-2); }
.racer:hover::after { opacity: 1; }
.racer[hidden] { display: none; }

.racer__face {
  width: 88px; height: 88px;
  display: block;
  margin-bottom: 14px;
  filter: drop-shadow(0 6px 14px rgba(20, 17, 11, 0.18));
  transition: transform 0.45s var(--ease);
}
.racer:hover .racer__face { transform: scale(1.09) rotate(-4deg); }

.racer__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 25px;
  letter-spacing: -0.02em;
  line-height: 1;
  text-transform: uppercase;
  margin: 0;
}
.racer__tag {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c);
  margin: 8px 0 0;
}
.racer__trait {
  font-size: 14.5px;
  line-height: 1.45;
  color: var(--dim);
  margin: 12px 0 0;
}
.racer__meta {
  gap: 10px;
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}
.racer__meta > span:first-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.racer__meta > span:last-child { white-space: nowrap; flex: none; }
.racer__meta b { color: var(--ink); font-size: 15px; font-weight: 600; }

/* The card is a link to the racer's page, so it has to say so. It stays visible
   rather than appearing on hover, because half this audience is on a phone and
   there is no hover there to reveal it. */
.racer__more {
  display: block;
  margin-top: 13px;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  transition: color 0.3s;
}
.racer__more i { font-style: normal; display: inline-block; transition: transform 0.3s var(--ease); }
.racer:hover .racer__more { color: var(--c); }
.racer:hover .racer__more i { transform: translateX(4px); }

/* --------------------------------------------------------------- footer */

/* A masthead rather than a sign-off. It carries the three things a visitor arriving
   from a search result still needs: who made this, where to watch it, and the seed
   that backs up every claim on the page above. */
.foot {
  border-top: 1px solid var(--rule);
  background:
    radial-gradient(ellipse 70% 100% at 12% 0%, color-mix(in srgb, var(--house) 7%, transparent), transparent 62%),
    var(--tint);
  padding-block: clamp(52px, 7vh, 88px) 0;
  margin-top: clamp(40px, 6vh, 90px);
}

.foot__top {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(30px, 5vw, 72px);
  padding-bottom: clamp(40px, 5vh, 64px);
}

.foot__brand .brand { margin-bottom: 18px; }
.foot__brand .brand__type b { font-size: 32px; }

.foot__tag {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: 26px;
  line-height: 1;
  text-transform: uppercase;
  color: var(--house);
  letter-spacing: 0.005em;
}

.foot__blurb {
  margin: 16px 0 0;
  max-width: 46ch;
  font-size: 14.5px;
  line-height: 1.66;
  color: var(--dim);
}

.foot__cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 22px;
  padding: 12px 20px 11px;
  border-radius: 100px;
  background: var(--ink);
  color: var(--card);
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transition: transform 0.25s var(--snap), box-shadow 0.25s;
}
.foot__cta i { font-style: normal; font-size: 9px; }
.foot__cta:hover { background: var(--house-deep); }

.foot__nav {
  display: flex;
  flex-direction: column;
  gap: 11px;
  align-items: flex-start;
  font-size: 14px;
  color: var(--dim);
}
.foot__nav h3 {
  margin: 0 0 6px;
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
}
.foot__nav a { transition: color 0.2s; }
.foot__nav a:hover { color: var(--house); }
.foot__nav span { color: var(--dim); }

/* The receipt. The site makes exactly one falsifiable claim — that this race is what
   this seed produces — and this strip is where it is printed, in the mono that is
   reserved on this page for things you could paste into a terminal. */
.foot__receipt {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  padding: 18px 20px;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: var(--paper);
}
.foot__rlabel {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--house);
}
.foot__receipt code {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-2);
  padding: 5px 10px 4px;
  border: 1px solid var(--rule-2);
  border-radius: 2px;
  background: var(--sheet);
  word-break: break-all;
}
.foot__rnote {
  font-size: 13px;
  color: var(--faint);
  font-style: italic;
}

.foot__base {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 26px;
  padding-block: 20px 26px;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
}

@media (max-width: 860px) {
  .foot__top { grid-template-columns: 1fr; gap: 34px; }
}

/* The aspect line on a roster card — the team's element and where it is from. */
.racer__aspect {
  margin: 0 0 10px;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
}
.racer__aspect i { font-style: normal; opacity: 0.55; margin-inline: 3px; }

/* Feeds on / comes apart in — two columns, side by side while there is room. */
.taste__col { min-width: 0; }

/* ------------------------------------------------------------------- 404 */

.oops { padding-block: clamp(70px, 14vh, 170px); }
.oops__in { max-width: 720px; }

.oops__code {
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(120px, 26vw, 280px);
  line-height: 0.8;
  letter-spacing: -0.04em;
  margin: 18px 0 0;
  color: var(--ink);
  -webkit-text-stroke: 0;
}

.oops__lede {
  margin: 24px 0 0;
  max-width: 46ch;
  font-size: clamp(16px, 1.5vw, 20px);
  line-height: 1.6;
  color: var(--dim);
}

.oops__acts { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 34px; }

/* ------------------------------------------------- late additions, from looking */

/* Drivers still on nought. A row each would be fourteen rows of the same zero. */
.yet {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 7px;
  padding-top: 18px;
  margin-top: 4px;
}
.yet__label {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
  margin-right: 6px;
}
.yet__n {
  font-family: var(--display);
  font-weight: 700;
  font-size: 17px;
  line-height: 1;
  text-transform: uppercase;
  color: var(--c);
  opacity: 0.62;
  padding: 5px 9px;
  border: 1px solid var(--rule);
  border-radius: 2px;
  transition: opacity 0.25s, border-color 0.25s;
}
.yet__n:hover { opacity: 1; border-color: var(--c); }

/* The track string is one long token (zigzag>boostRamp>…) and is still the widest
   single word on the page, so it keeps its break rule — it now lives in the receipt. */
.foot__receipt code { overflow-wrap: anywhere; max-width: 100%; }


/* ================================================================= rivalries

   Two colours meeting on a diagonal. The seam is a real element rather than a
   gradient stop, because the interesting part is the hard edge: a marble race is
   decided by contact, and a soft blend between two teams says the opposite of
   what the section is about.

   The card is CSS grid rather than three floats so that the head-to-head numerals
   stay optically centred on the seam at every width, which is the one alignment
   in here a reader will notice if it is wrong. */

.vslist { display: grid; gap: 16px; margin-top: 30px; }

.vs {
  position: relative;
  display: grid;
  grid-template-columns: 150px 1fr 150px;
  align-items: center;
  gap: 18px;
  padding: 26px 22px 22px;
  background: var(--sheet);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--a);
  border-right: 4px solid var(--b);
  overflow: hidden;
  transition: background 0.3s var(--ease), border-color 0.3s var(--ease);
}
.vs:hover { background: var(--card); border-top-color: var(--rule-2); border-bottom-color: var(--rule-2); }

/* The seam. Skewed, full-bleed vertically, and lit from both sides. */
.vs__seam {
  position: absolute;
  left: 50%;
  top: -20%;
  width: 1px;
  height: 140%;
  transform: skewX(-11deg);
  background: linear-gradient(180deg, transparent, var(--rule) 18%, var(--rule) 82%, transparent);
}

.vs__side { display: flex; flex-direction: column; align-items: center; gap: 9px; }
.vs__side img {
  width: 72px; height: 72px;
  display: block;
  border-radius: 50%;
  transition: transform 0.35s var(--ease);
}
.vs:hover .vs__side--a img { transform: translateX(5px) scale(1.06); }
.vs:hover .vs__side--b img { transform: translateX(-5px) scale(1.06); }
.vs__side span {
  font-family: var(--display);
  font-weight: 800;
  font-size: 19px;
  line-height: 1;
  letter-spacing: 0.02em;
}
.vs__side--a span { color: var(--a); }
.vs__side--b span { color: var(--b); }

.vs__mid { text-align: center; position: relative; z-index: 1; }
.vs__name {
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(24px, 3.4vw, 38px);
  line-height: 0.98;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  margin: 0;
  /* The rivalry's own name takes both colours, split on the same diagonal as
     the seam behind it. */
  background: linear-gradient(101deg, var(--a) 34%, var(--b) 66%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.vs__line {
  margin: 9px auto 0;
  max-width: 42ch;
  font-size: 14px;
  line-height: 1.45;
  color: var(--dim);
}
.vs__record {
  margin-top: 13px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
}
.vs__score {
  font-family: var(--mono);
  font-weight: 600;
  font-size: 26px;
  line-height: 1;
  color: var(--ink);
  display: flex;
  align-items: baseline;
  gap: 7px;
}
.vs__score u { text-decoration: none; color: var(--faint); font-size: 18px; }
.vs__meetings, .vs__pending {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
}

.vs__body {
  grid-column: 1 / -1;
  border-top: 1px solid var(--rule);
  padding-top: 17px;
  margin-top: 4px;
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: 22px;
}
.vs__body p { margin: 0; font-size: 15px; line-height: 1.6; color: var(--dim); }
.vs__edge {
  font-family: var(--display);
  font-weight: 500;
  font-size: 19px !important;
  line-height: 1.3 !important;
  color: var(--ink) !important;
  border-left: 2px solid var(--rule);
  padding-left: 16px;
}

@media (max-width: 780px) {
  .vs { grid-template-columns: 1fr auto 1fr; gap: 12px; padding: 20px 16px; }
  .vs__side img { width: 56px; height: 56px; }
  .vs__body { grid-template-columns: 1fr; gap: 14px; }
  .vs__edge { border-left: 0; border-top: 2px solid var(--rule); padding: 14px 0 0; }
}

/* ============================================================== racer pages

   Twenty-four pages that must not look like twenty-four copies of one page. The
   only lever that does that honestly is the racer's own colour, which is already
   the thing the audience uses to tell them apart in a race — so it is set once on
   :root and then drives the wash, the rules, the numerals and the chips.

   Everything else stays identical between them on purpose. A page whose *layout*
   changed per racer would stop being a dossier and start being twenty-four
   posters, and the reader could no longer find the cost paragraph without
   reading for it. */

/* The spine. The name set vertically down the left margin, enormous and nearly
   invisible — the thing you notice on the second page, not the first. It is
   decorative, sits behind everything, and disappears the moment the viewport is
   narrow enough to need the space. */
.spine {
  position: fixed;
  left: 14px;
  top: 0;
  height: 100vh;
  z-index: 0;
  display: flex;
  /* In vertical-rl the inline axis runs down the page, so it is justify-content
     that centres this column and align-items that pins it to the left margin. */
  justify-content: center;
  align-items: center;
  gap: 26px;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--display);
  font-weight: 900;
  font-size: 15px;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--c);
  opacity: 0.17;
  pointer-events: none;
  user-select: none;
}
.spine i { width: 1px; height: 34px; background: currentColor; opacity: 0.6; }
@media (max-width: 1180px) { .spine { display: none; } }

.dossier-page { position: relative; }
.dwrap { position: relative; z-index: 1; padding-bottom: clamp(40px, 7vh, 90px); }

/* ---------------------------------------------------------- dossier hero */

.dhero { position: relative; padding: clamp(38px, 6vh, 76px) 0 clamp(30px, 5vh, 58px); overflow: hidden; }
.dhero__wash {
  position: absolute;
  inset: -30% -10% auto -10%;
  height: 150%;
  background:
    radial-gradient(48% 62% at 22% 30%, color-mix(in srgb, var(--c) 11%, transparent), transparent 70%),
    radial-gradient(38% 48% at 82% 8%, color-mix(in srgb, var(--c) 5%, transparent), transparent 72%);
  pointer-events: none;
}

.dhero__in {
  position: relative;
  display: grid;
  grid-template-columns: minmax(220px, 320px) 1fr;
  gap: clamp(26px, 5vw, 68px);
  align-items: center;
}

/* The portrait, presented as a specimen: three rings, one of them turning. A
   marble is a sphere and every other visual on this site is a rectangle, so the
   concentric circles are the one place the page is allowed to agree with the
   subject. */
.dhero__portrait { position: relative; display: grid; place-items: center; aspect-ratio: 1; }
.dhero__portrait img {
  position: relative;
  z-index: 2;
  width: 66%;
  height: auto;
  filter: drop-shadow(0 14px 30px rgba(20, 17, 11, 0.22));
}
.orbit {
  position: absolute;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--c) 40%, transparent);
}
.orbit--1 { inset: 12%; }
.orbit--2 { inset: 2%; border-style: dashed; opacity: 0.5; animation: spin 44s linear infinite; }
.orbit--3 {
  inset: 20%;
  border: 0;
  background: radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--c) 22%, transparent), transparent 68%);
}
@keyframes spin { to { transform: rotate(360deg); } }

.dhero__text {
  position: relative;
  min-width: 0;
  /* The ghost numeral behind the name is a z-index:-1 child. Without an explicit
     stacking context here it paints behind the section wash and the body's dot
     grid as well, and disappears entirely. */
  isolation: isolate;
}
.dhero__text .eyebrow i { font-style: normal; opacity: 0.45; margin: 0 2px; }

.dhero__name {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(52px, 7.4vw, 116px);
  line-height: 0.9;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  margin: 12px 0 0;
  color: var(--ink);
  /* A hairline of the racer's colour under the name, the same cue the leaderboard
     panel uses in the video. */
  text-shadow: 0 3px 0 color-mix(in srgb, var(--c) 34%, transparent);
}
.dhero__tag {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(20px, 2.4vw, 30px);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--c);
  margin: 16px 0 0;
}
.dhero__epithet {
  margin: 14px 0 0;
  max-width: 46ch;
  font-size: clamp(16px, 1.5vw, 19px);
  line-height: 1.5;
  color: var(--dim);
}

/* Championship position, as the page's one piece of ghost typography. */
.dhero__rank {
  position: absolute;
  right: -14px;
  top: -74px;
  z-index: -1;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(110px, 13vw, 200px);
  line-height: 0.7;
  color: color-mix(in srgb, var(--c) 11%, transparent);
  pointer-events: none;
  user-select: none;
}

@media (max-width: 900px) {
  .dhero__in { grid-template-columns: 1fr; gap: 22px; }
  .dhero__portrait { max-width: 230px; }
  .dhero__rank { top: auto; bottom: -30px; right: 0; }
}

/* ---------------------------------------------------------- the shard block */

/* Clipped corners, because the fiction is that every racer is a piece of
   something that broke. It is one clip-path and it is the only non-rectangular
   panel on the site, which is what keeps it meaning something. */
.shard {
  position: relative;
  margin-top: clamp(24px, 4vh, 48px);
  padding: clamp(26px, 3.6vw, 46px) clamp(24px, 4vw, 58px);
  background:
    linear-gradient(135deg, color-mix(in srgb, var(--c) 9%, transparent), transparent 58%),
    var(--tint);
  clip-path: polygon(0 22px, 22px 0, 100% 0, 100% calc(100% - 22px), calc(100% - 22px) 100%, 0 100%);
}
.shard__label {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--c);
  margin: 0 0 14px;
}
.shard__text {
  margin: 0;
  max-width: 62ch;
  font-size: clamp(17px, 1.7vw, 21px);
  line-height: 1.62;
  color: var(--ink);
}

/* ------------------------------------------------------------- the quote */

.pull { margin: clamp(30px, 5vh, 58px) 0; padding: 0; position: relative; }
.pull p {
  font-family: var(--display);
  font-weight: 500;
  font-style: italic;
  font-size: clamp(26px, 4.4vw, 54px);
  line-height: 1.06;
  letter-spacing: -0.005em;
  color: var(--ink);
  margin: 0;
  max-width: 22ch;
  text-indent: -0.42em;
}
.pull p::before { content: '\201C'; color: var(--c); }
.pull p::after { content: '\201D'; color: var(--c); }
.pull cite {
  display: block;
  margin-top: 16px;
  font-family: var(--mono);
  font-style: normal;
  font-size: 10px;
  letter-spacing: 0.22em;
  color: var(--c);
}
/* Ink has no quote. The placeholder is set in the body font so it reads as the
   page talking rather than as the racer. */
.pull--silent p {
  font-family: var(--body);
  font-style: normal;
  font-size: clamp(17px, 1.7vw, 21px);
  line-height: 1.55;
  color: var(--dim);
  max-width: 46ch;
  text-indent: 0;
}
.pull--silent p::before, .pull--silent p::after { content: none; }

/* ------------------------------------------------------- the four blocks */

.dcols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}
.dblock, .sig { background: var(--paper); padding: clamp(22px, 2.6vw, 34px); margin: 0; }
.dblock h2, .sig h2 {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 15px;
}
.dblock p { margin: 0; font-size: 15.5px; line-height: 1.62; color: var(--dim); }

/* The signature move is the one block that gets colour, because it is the one
   thing a viewer will recognise happening on screen. */
.sig { background: color-mix(in srgb, var(--c) 7%, var(--tint)); }
.sig__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(26px, 2.4vw, 34px);
  line-height: 1;
  text-transform: uppercase;
  color: var(--c);
  margin: 0 0 12px;
  overflow-wrap: break-word;
  hyphens: auto;
}
.sig__text { margin: 0; font-size: 15.5px; line-height: 1.6; color: var(--ink); }

/* -------------------------------------------------------- track appetite */

.taste {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: clamp(20px, 3vw, 40px);
  margin-top: clamp(28px, 4vh, 52px);
  padding-top: clamp(26px, 3vh, 38px);
  border-top: 1px solid var(--rule);
}
.taste h3 {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 14px;
}

.chips { display: flex; flex-wrap: wrap; gap: 7px; }
.chips span {
  font-family: var(--mono);
  font-size: 11.5px;
  padding: 6px 11px;
  border: 1px solid var(--rule);
  border-radius: 2px;
  color: var(--dim);
}
.chips--loves span { color: var(--gain); border-color: color-mix(in srgb, var(--gain) 34%, var(--rule)); }
.chips--fears span { color: var(--loss); border-color: color-mix(in srgb, var(--loss) 34%, var(--rule)); }
.chips__none { margin: 0; font-size: 14px; line-height: 1.5; color: var(--faint); max-width: 34ch; }

/* -------------------------------------------------------------- the record */

.drecord { margin-top: clamp(34px, 5vh, 64px); }

.bignums {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  margin-top: 20px;
}
.bignum { background: var(--tint); padding: 20px 18px; }
.bignum b {
  display: block;
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(30px, 3.4vw, 44px);
  line-height: 0.94;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.bignum span {
  display: block;
  margin-top: 8px;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
}
.notyet {
  margin: 18px 0 0;
  max-width: 62ch;
  font-size: 16px;
  line-height: 1.6;
  color: var(--dim);
  border-left: 2px solid var(--c);
  padding-left: 18px;
}

.form { list-style: none; margin: 26px 0 0; padding: 0; }
.form li {
  display: grid;
  grid-template-columns: 52px 62px 54px 1fr auto;
  align-items: baseline;
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--rule);
}
.form__r { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.form__p {
  font-family: var(--display);
  font-weight: 800;
  font-size: 22px;
  line-height: 1;
  color: var(--ink);
}
.form--win .form__p { color: var(--c); }
.form__of { font-family: var(--mono); font-size: 10.5px; color: var(--faint); }
.form__s { font-size: 14px; color: var(--dim); }
.form__t {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  color: var(--gold);
  border: 1px solid color-mix(in srgb, var(--gold) 40%, var(--rule));
  padding: 3px 7px;
}

/* ------------------------------------------------------------- the stands */

.crowd {
  margin-top: clamp(30px, 4vh, 56px);
  padding: clamp(22px, 2.8vw, 34px);
  background: var(--tint);
  border-left: 2px solid var(--c);
}
.crowd h3 {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 12px;
}
.crowd p {
  margin: 0;
  max-width: 60ch;
  font-size: clamp(16px, 1.5vw, 18px);
  line-height: 1.6;
  color: var(--ink);
}

.drivals { margin-top: clamp(38px, 6vh, 72px); }

/* ------------------------------------------------------------------ pager */

.pager {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  margin-top: clamp(40px, 6vh, 78px);
  padding-top: 26px;
  border-top: 1px solid var(--rule);
}
.pager__a { display: block; min-width: 0; }
.pager__a span {
  display: block;
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 6px;
}
.pager__a b {
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(24px, 3vw, 38px);
  line-height: 1;
  text-transform: uppercase;
  color: var(--c);
  transition: opacity 0.2s;
  opacity: 0.82;
}
.pager__a:hover b { opacity: 1; }
.pager__a--next { text-align: right; }
.pager__all {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
  border: 1px solid var(--rule);
  padding: 10px 14px;
  transition: color 0.2s, border-color 0.2s;
}
.pager__all:hover { color: var(--ink); border-color: var(--faint); }

@media (max-width: 620px) {
  .pager { grid-template-columns: 1fr 1fr; row-gap: 22px; }
  .pager__all { grid-column: 1 / -1; text-align: center; order: 3; }
  .form li { grid-template-columns: 44px 54px 1fr; row-gap: 2px; }
  .form__of, .form__t { grid-column: 2 / -1; }
}


/* ================================================================== the voice

   The only interactive thing on a racer's page, so it gets the weight of one. It
   is also the only place on the site where the racer's colour is applied to a
   control — everywhere else saturation is reserved for data. */

.voice {
  margin-top: clamp(28px, 4vh, 52px);
  padding: clamp(24px, 3.2vw, 40px);
  background:
    radial-gradient(70% 130% at 12% 50%, color-mix(in srgb, var(--c) 14%, transparent), transparent 68%),
    var(--tint);
  border: 1px solid var(--rule);
  border-radius: 3px;
}

.voice__in {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: clamp(18px, 3vw, 38px);
  margin-top: 18px;
}

/* --- the button ---------------------------------------------------------- */

.vplay {
  position: relative;
  width: 84px;
  height: 84px;
  flex: none;
  display: grid;
  place-items: center;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  color: var(--c);
  -webkit-tap-highlight-color: transparent;
}
.vplay:focus-visible { outline: 2px solid var(--c); outline-offset: 6px; }

/* Two rings, the same motif as the portrait above. The outer one is the resting
   edge; the inner one only exists while a note is sounding. */
.vplay__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--c) 55%, transparent);
  background: color-mix(in srgb, var(--c) 9%, transparent);
  transition: transform 0.3s var(--ease), background 0.3s, border-color 0.3s;
}
.vplay:hover .vplay__ring {
  transform: scale(1.06);
  background: color-mix(in srgb, var(--c) 16%, transparent);
  border-color: var(--c);
}
.vplay:active .vplay__ring { transform: scale(0.97); }

/* The ping. One ring expanding outward and fading, restarted on every press —
   the visual is the note, so it lasts about as long as the note does. */
.vplay__ring--2 {
  background: none;
  opacity: 0;
  transition: none;
}
.vplay.is-on .vplay__ring--2 { animation: ping 0.85s var(--ease) forwards; }
@keyframes ping {
  from { opacity: 0.85; transform: scale(1); }
  to { opacity: 0; transform: scale(1.85); }
}

.vplay__tri {
  position: relative;
  width: 0;
  height: 0;
  margin-left: 5px;
  border-left: 17px solid currentColor;
  border-top: 11px solid transparent;
  border-bottom: 11px solid transparent;
  transition: transform 0.3s var(--ease);
}
.vplay:hover .vplay__tri { transform: scale(1.1); }
.vplay.is-on .vplay__tri { animation: beat 0.85s var(--ease); }
@keyframes beat {
  0% { transform: scale(0.82); }
  22% { transform: scale(1.12); }
  100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .vplay.is-on .vplay__ring--2, .vplay.is-on .vplay__tri { animation: none; }
}

/* --- the label ----------------------------------------------------------- */

.voice__text { min-width: 0; }
.voice__gesture {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(19px, 2.1vw, 26px);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--ink);
  margin: 0;
}
.voice__why {
  margin: 11px 0 0;
  max-width: 54ch;
  font-size: 15px;
  line-height: 1.55;
  color: var(--dim);
}

.voice__prose {
  margin: clamp(18px, 2.4vh, 26px) 0 0;
  padding-top: 18px;
  border-top: 1px solid var(--rule);
  max-width: 66ch;
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--dim);
}
.voice__note-b {
  margin: 12px 0 0;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  line-height: 1.6;
  color: var(--faint);
}

/* The musical note, demoted to a footnote under the character voice — it is a real
   fact about the racer and it is not what a visitor came here to hear. */
.voice__note-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin-top: clamp(18px, 2.4vh, 26px);
  padding-top: 18px;
  border-top: 1px solid var(--rule);
}
.nplay {
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  padding: 0;
  font-size: 9px;
  color: var(--c);
  background: color-mix(in srgb, var(--c) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--c) 45%, transparent);
  border-radius: 50%;
  cursor: pointer;
  transition: background 0.25s, border-color 0.25s, transform 0.25s var(--ease);
}
.nplay:hover { background: color-mix(in srgb, var(--c) 22%, transparent); border-color: var(--c); transform: scale(1.1); }
.nplay:focus-visible { outline: 2px solid var(--c); outline-offset: 3px; }
.voice__nlabel {
  margin: 4px 0 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--dim);
}
.voice__nlabel b { color: var(--c); }
.voice__nlabel i { font-style: normal; color: var(--ink); }
.voice__nwhy { margin: 7px 0 0; max-width: 62ch; font-size: 13.5px; line-height: 1.55; color: var(--faint); }

@media (max-width: 700px) {
  .voice__in { grid-template-columns: auto 1fr; row-gap: 18px; }
  .vplay { width: 68px; height: 68px; }
}


/* ================================================================= the moves

   Twenty-four entrance gestures, one per racer, played when the page opens and
   again on every click of the portrait.

   These are the character doing the thing the character does — Crimson lunges,
   Jade waits and then goes, Bolt is gone before you looked, and Cinder does
   absolutely nothing, which is the joke and is worth more than any animation
   could have been.

   All of them are transform and opacity only. Nothing here animates a property
   that costs layout, so twenty-four different moves are the same cheap as one.

   The gesture is named in src/voices.js next to the voice, because how a racer
   sounds and how it moves are one performance and should be one entry. */

/* The gesture is this portrait's entrance, so it does NOT also carry the site-wide
   .reveal fade — two entrances fighting over the same element reads as neither. The
   keyframes start from opacity 0 and animation-fill-mode covers the gap before the
   animation is armed; main.js is a blocking script at the end of the body, so that
   arming happens before the first paint and there is nothing to see in between. */
.dhero__portrait img { will-change: transform; }
.dhero__portrait[data-move] img { animation-fill-mode: both; }
.dhero__portrait.go[data-move] img { animation-duration: 1s; animation-timing-function: var(--ease); }

.dhero__portrait { cursor: pointer; }
.dhero__portrait::after {
  content: 'click';
  position: absolute;
  left: 50%;
  bottom: -4px;
  transform: translateX(-50%);
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
  opacity: 0;
  transition: opacity 0.3s;
  pointer-events: none;
}
.dhero__portrait:hover::after { opacity: 1; }

/* --- Ash: everything here arrives hot ----------------------------------- */
.go[data-move='lunge'] img { animation-name: mv-lunge; animation-duration: 0.72s; }
@keyframes mv-lunge {
  0% { transform: translateX(-46%) scale(0.9); opacity: 0; }
  42% { transform: translateX(9%) scale(1.06); opacity: 1; }
  70% { transform: translateX(-3%) scale(0.99); }
  100% { transform: none; }
}
.go[data-move='slam'] img { animation-name: mv-slam; animation-duration: 0.8s; }
@keyframes mv-slam {
  0% { transform: translateY(-60%) scale(1.14); opacity: 0; }
  34% { transform: translateY(0) scale(1.14); opacity: 1; }
  44% { transform: translateY(2%) scaleY(0.82) scaleX(1.2); }
  62% { transform: translateY(-6%) scale(1.02); }
  100% { transform: none; }
}
.go[data-move='flare'] img { animation-name: mv-flare; animation-duration: 0.9s; }
@keyframes mv-flare {
  0% { transform: scale(0.5); opacity: 0; }
  16% { transform: scale(1.22); opacity: 1; }
  24% { transform: scale(1.1) rotate(-5deg); }
  32% { transform: scale(1.16) rotate(5deg); }
  40% { transform: scale(1.08) rotate(-3deg); }
  56% { transform: scale(1.12); }
  100% { transform: scale(1); }
}
.go[data-move='settle'] img { animation-name: mv-settle; animation-duration: 1.15s; }
@keyframes mv-settle {
  0% { transform: translateY(-16%) rotate(-7deg); opacity: 0; }
  45% { opacity: 1; }
  70% { transform: translateY(2%) rotate(2deg); }
  100% { transform: none; }
}

/* --- Deep: discipline, in four different readings ----------------------- */
.go[data-move='tick'] img { animation-name: mv-tick; animation-duration: 1.05s; }
@keyframes mv-tick {
  0% { opacity: 0; }
  10% { opacity: 1; transform: translateX(-7%); }
  30% { transform: translateX(7%); }
  50% { transform: translateX(-7%); }
  70% { transform: translateX(7%); }
  100% { transform: none; }
}
.go[data-move='snap'] img { animation-name: mv-snap; animation-duration: 0.5s; }
@keyframes mv-snap {
  0% { transform: translateX(-22%) scale(0.96); opacity: 0; }
  40% { transform: translateX(0) scale(1); opacity: 1; }
  55% { transform: translateX(0) scale(1.05); }
  100% { transform: none; }
}
.go[data-move='commit'] img { animation-name: mv-commit; animation-duration: 0.85s; }
@keyframes mv-commit {
  0% { transform: translateY(-70%); opacity: 0; }
  55% { transform: translateY(0); opacity: 1; }
  /* No overshoot, no settle. It arrives where it decided to arrive. */
  100% { transform: none; }
}
.go[data-move='drift'] img { animation-name: mv-drift; animation-duration: 1.35s; }
@keyframes mv-drift {
  0% { transform: translateX(-40%) translateY(6%) rotate(-10deg); opacity: 0; }
  50% { opacity: 1; }
  60% { transform: translateX(9%) translateY(-4%) rotate(6deg); }
  100% { transform: none; }
}

/* --- Green ------------------------------------------------------------- */
.go[data-move='surge'] img { animation-name: mv-surge; animation-duration: 1.3s; }
@keyframes mv-surge {
  0% { transform: translateY(26%) scale(0.9); opacity: 0; }
  20% { transform: translateY(22%) scale(0.9); opacity: 0.55; }
  /* Three quarters of the animation is Jade not doing anything. */
  62% { transform: translateY(18%) scale(0.92); opacity: 0.75; }
  80% { transform: translateY(-7%) scale(1.09); opacity: 1; }
  100% { transform: none; }
}
.go[data-move='bounce'] img { animation-name: mv-bounce; animation-duration: 1s; }
@keyframes mv-bounce {
  0% { transform: translateY(-80%) scale(0.86); opacity: 0; }
  25% { transform: translateY(0) scale(1); opacity: 1; }
  35% { transform: translateY(0) scaleY(0.86) scaleX(1.12); }
  50% { transform: translateY(-34%) scale(1.02); }
  65% { transform: translateY(0) scaleY(0.92) scaleX(1.06); }
  80% { transform: translateY(-13%); }
  100% { transform: none; }
}
.go[data-move='creep'] img { animation-name: mv-creep; animation-duration: 1.5s; }
@keyframes mv-creep {
  /* Perfectly linear, because the whole point of Moss is that nothing changes. */
  0% { transform: translateX(-30%); opacity: 0; }
  30% { opacity: 1; }
  100% { transform: none; opacity: 1; }
}
.go[data-move='creep'] img { animation-timing-function: linear; }
.go[data-move='hop'] img { animation-name: mv-hop; animation-duration: 0.85s; }
@keyframes mv-hop {
  0% { transform: translateY(18%) scale(0.94); opacity: 0; }
  40% { transform: translateY(-22%) scale(1.05); opacity: 1; }
  62% { transform: translateY(0) scaleY(0.94) scaleX(1.05); }
  80% { transform: translateY(-8%); }
  100% { transform: none; }
}

/* --- Dusk: none of these want to be looked at --------------------------- */
.go[data-move='weary'] img { animation-name: mv-weary; animation-duration: 1s; }
@keyframes mv-weary {
  0% { transform: rotate(-4deg) translateY(-8%); opacity: 0; }
  50% { opacity: 1; }
  72% { transform: rotate(3deg) translateY(1%); }
  100% { transform: none; }
}
.go[data-move='fade'] img { animation-name: mv-fade; animation-duration: 1.6s; }
@keyframes mv-fade {
  0% { opacity: 0; transform: scale(1.06); filter: blur(7px); }
  55% { opacity: 0.35; filter: blur(3px); }
  100% { opacity: 1; transform: none; filter: blur(0); }
}
.go[data-move='sidle'] img { animation-name: mv-sidle; animation-duration: 1.1s; }
@keyframes mv-sidle {
  0% { transform: translateX(34%) scale(0.95); opacity: 0; }
  55% { transform: translateX(-5%); opacity: 1; }
  78% { transform: translateX(2%) rotate(-3deg); }
  100% { transform: none; }
}
/* Cinder's move is `none`, and there is deliberately no keyframe for it. Same
   face, same pace, every single round — an animation would contradict the
   entire character, so the portrait simply appears. */

/* --- Bloom: all performance --------------------------------------------- */
.go[data-move='flourish'] img { animation-name: mv-flourish; animation-duration: 1.15s; }
@keyframes mv-flourish {
  0% { transform: scale(0.4) rotate(-190deg); opacity: 0; }
  55% { transform: scale(1.14) rotate(12deg); opacity: 1; }
  72% { transform: scale(1.02) rotate(-7deg); }
  86% { transform: scale(1.05) rotate(3deg); }
  100% { transform: none; }
}
.go[data-move='breathe'] img { animation-name: mv-breathe; animation-duration: 1.5s; }
@keyframes mv-breathe {
  0% { transform: scale(0.93); opacity: 0; }
  40% { opacity: 1; }
  55% { transform: scale(1.05); }
  78% { transform: scale(0.985); }
  100% { transform: none; }
}
.go[data-move='swoon'] img { animation-name: mv-swoon; animation-duration: 1.35s; }
@keyframes mv-swoon {
  0% { transform: translateY(-30%) rotate(20deg) scale(1.08); opacity: 0; }
  45% { transform: translateY(4%) rotate(-13deg); opacity: 1; }
  68% { transform: translateY(0) rotate(9deg); }
  85% { transform: rotate(-4deg); }
  100% { transform: none; }
}
.go[data-move='nudge'] img { animation-name: mv-nudge; animation-duration: 1s; }
@keyframes mv-nudge {
  0% { transform: translateX(30%); opacity: 0; }
  40% { transform: translateX(6%); opacity: 1; }
  /* The shove, which is the only part Fuchsia came for. */
  55% { transform: translateX(-12%) rotate(-6deg); }
  72% { transform: translateX(4%) rotate(3deg); }
  100% { transform: none; }
}

/* --- Light -------------------------------------------------------------- */
.go[data-move='jitter'] img { animation-name: mv-jitter; animation-duration: 0.95s; }
@keyframes mv-jitter {
  0% { transform: scale(0.8) rotate(0); opacity: 0; }
  15% { transform: translate(7%, -5%) rotate(9deg); opacity: 1; }
  28% { transform: translate(-8%, 4%) rotate(-11deg); }
  40% { transform: translate(6%, 6%) rotate(7deg); }
  52% { transform: translate(-5%, -6%) rotate(-6deg); }
  64% { transform: translate(4%, 3%) rotate(4deg); }
  78% { transform: translate(-2%, -2%) rotate(-2deg); }
  100% { transform: none; }
}
.go[data-move='descend'] img { animation-name: mv-descend; animation-duration: 1.05s; }
@keyframes mv-descend {
  /* Straight down, no rotation, no overshoot, no wobble. */
  0% { transform: translateY(-64%); opacity: 0; }
  60% { opacity: 1; }
  100% { transform: none; opacity: 1; }
}
.go[data-move='zip'] img { animation-name: mv-zip; animation-duration: 0.62s; }
@keyframes mv-zip {
  0% { transform: translateX(-120%) scaleX(1.5) scaleY(0.8); opacity: 0; }
  14% { transform: translateX(26%) scaleX(1.2) scaleY(0.9); opacity: 1; }
  28% { transform: translateX(-8%) scale(1); }
  42% { transform: translateX(3%); }
  /* And then nothing at all for the rest of the race. */
  100% { transform: none; }
}
.go[data-move='tilt'] img { animation-name: mv-tilt; animation-duration: 1.2s; }
@keyframes mv-tilt {
  0% { transform: scale(0.9); opacity: 0; }
  45% { transform: scale(1); opacity: 1; }
  /* The curious head-tilt, after arriving. Pearl has not been told yet. */
  68% { transform: rotate(11deg); }
  86% { transform: rotate(-4deg); }
  100% { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .dhero__portrait img { animation: none !important; }
}

/* ---------------------------------------------------------------- fixtures
   The half of the season that has not happened yet.

   Everything above this in the book is a record: ink that cannot change. A fixture
   list is the opposite kind of page, so it is drawn one weight lighter — outlines
   rather than fills, and no colour block anywhere except the key that names a racer.
   The next race is the exception, because tomorrow is the only future round anybody
   is actually planning around.

   This section was twice this size and had a 24-by-30 dot matrix in the middle of it.
   The matrix was accurate and unreadable: four mark states, tinted tier columns and a
   legend written into the caption, all to answer a question — how far in are we, who
   is up next — that one row of thirty boxes answers with no legend at all. What is
   left is three things in plain order: the next race, where we are, the list. */

/* The second line of the lede: the tier rule, set smaller than the sentence above it
   because it is a footnote to that sentence rather than a second claim. It measures
   the same width as the paragraph it is a footnote to — .lede is capped in ch, so a
   smaller face would otherwise wrap short and read as a different column. */
.lede--small {
  font-size: 15px;
  max-width: 76ch;
  color: var(--faint);
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--rule);
}
.lede--small b {
  font-family: var(--mono);
  font-size: 0.86em;
  font-weight: 600;
  color: var(--gold);
}

/* --- the next race -------------------------------------------------------- */

.next {
  margin-top: clamp(30px, 3.4vw, 48px);
  background: var(--card);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--ink);
  padding: clamp(18px, 2.4vw, 30px);
}

.next__head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px 18px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--rule);
  margin: 0;
}

.next__label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--house-ink);
  margin: 0;
}

.next__round {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(26px, 3.2vw, 38px);
  letter-spacing: -0.03em;
  line-height: 1;
  margin: 0;
  font-variant-numeric: tabular-nums;
}

/* "of 30" is context, not headline. Same line, a third of the size. */
.next__round u {
  text-decoration: none;
  font-size: 0.42em;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--faint);
}

/* Only ever set on a round that pays double or triple, and it always carries the
   words "double points" or "triple points" with it — see the note in bin/site.js
   about "Finale" on its own reading as a play-off. */
.next__tier {
  margin: 0 0 0 auto;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
}

/* Six across on a desk, two across on a phone. Never one — a column of six full-width
   rows is the fixture list again, and this panel exists to be a grid. */
.next__grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  margin-top: 18px;
}

.next__racer {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 3px;
  padding: 16px 8px 14px;
  background: var(--card);
  transition: background 0.18s, transform 0.24s var(--ease);
}
.next__racer:hover { background: var(--sheet); transform: translateY(-2px); }

.next__racer img {
  width: 56px;
  height: 56px;
  margin-bottom: 5px;
  transition: transform 0.3s var(--ease);
}
.next__racer:hover img { transform: scale(1.08) rotate(-4deg); }

.next__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
  line-height: 1.1;
}
.next__racer:hover .next__name { color: var(--c); }

.next__team {
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c);
}

.next__stand {
  font-size: 11.5px;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}

/* --- where the season has got to ------------------------------------------ */
/*
   Thirty boxes, filled in as they are raced. It is the progress bar of a league
   season and it is meant to be understood without being studied. */

.strip { margin: clamp(28px, 3.2vw, 42px) 0 clamp(30px, 3.6vw, 48px); }

.strip__row {
  display: grid;
  grid-template-columns: repeat(30, minmax(0, 1fr));
  gap: 3px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.strip__r {
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  background: var(--sheet);
  border: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 10px;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

/* Raced: solid ink, the same "this is a record" weight the results sheet uses. */
.strip__r.is-done {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--plate-2);
}

/* Next: the house colour, and the only outlined box on the row that is heavy. */
.strip__r.is-next {
  border: 2px solid var(--house-ink);
  color: var(--house-ink);
  font-weight: 600;
}

/* Worth more than a point apiece. A tint, so it reads under the other two states
   rather than competing with them — a raced Grand Prix is still just "raced". */
.strip__r.is-big { background: var(--tint); }
.strip__r.is-big.is-done { background: var(--ink); }

.strip__key {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin: 12px 0 0;
  font-size: 12px;
  color: var(--dim);
}

.strip__sw {
  width: 11px;
  height: 11px;
  border: 1px solid var(--rule);
  background: var(--sheet);
  margin-right: -10px;   /* the gap belongs between pairs, not inside one */
}
.strip__sw--done { background: var(--ink); border-color: var(--ink); }
.strip__sw--next { background: var(--card); border: 2px solid var(--house-ink); }
.strip__sw--big { background: var(--tint); }

/* --- the fixture list ----------------------------------------------------- */
/*
   One line per round: the number, and the six who start it. Nothing else — the
   rivalries have their own section four down, and a fixture that has not been raced
   has no other facts about it. */

.fxr {
  list-style: none;
  margin: 0;
  padding: 0;
}

.fxr__row {
  display: grid;
  grid-template-columns: 74px minmax(0, 1fr);
  align-items: baseline;
  gap: 16px;
  padding: 13px 10px;
  border-bottom: 1px solid var(--rule);
}
.fxr__row:hover { background: var(--sheet); }

.fxr__row.is-next {
  background: var(--card);
  border-bottom-color: var(--rule-2);
}
.fxr__row.is-big { background: var(--tint); }

/* The round number, with its one qualifier under it: "Next", or the multiplier. */
.fxr__no {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.fxr__no b {
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold);
}
.fxr__row.is-next .fxr__no { color: var(--ink); font-weight: 600; }
.fxr__row.is-next .fxr__no b { color: var(--house-ink); }

.fxr__six { display: flex; flex-wrap: wrap; gap: 4px 16px; }

/* The colour key in front of a name is the same 6px block that sits in front of a
   name on the results sheet. The colour means "this marble" everywhere on the site
   or it means nothing. */
.fxr__m {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--display);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.01em;
  color: var(--ink-2);
  white-space: nowrap;
  transition: color 0.18s;
}
.fxr__m i {
  width: 6px;
  height: 6px;
  background: var(--c);
  flex: none;
}
.fxr__m:hover { color: var(--c); }

@media (max-width: 900px) {
  .next__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 620px) {
  .next__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .next__tier { margin-left: 0; width: 100%; }

  /* Thirty boxes across a phone is 11px a box, which is a row of confetti. Fifteen
     and fifteen is two readable lines and still one glance. */
  .strip__row { grid-template-columns: repeat(15, minmax(0, 1fr)); }

  /* The round number moves above its six names rather than beside them: a 74px
     column plus six names is what forces the sideways scroll this list does not
     otherwise need. */
  .fxr__row { grid-template-columns: 1fr; gap: 7px; }
}

/* ------------------------------------------------------------ small screens
   Most of this audience arrives on a phone, which is also the only place the
   videos are watched. The eyebrow's leading rule orphans onto its own line at
   390px, and the racer name in a dossier hero overflows its own column. */

@media (max-width: 620px) {
  .eyebrow {
    font-size: 10px;
    letter-spacing: 0.14em;
    align-items: baseline;
  }
  .eyebrow::before { width: 18px; align-self: center; }

  /* The results sheet drops the two columns a phone can infer: the team is on the
     racer's own page, and the grid slot is what the +/- is measured from. Everything
     that decides the result stays. */
  .sheet__team, .sheet__grid { display: none; }
  .sheet tbody td { padding: 11px 8px; }
  .sheet tfoot td[colspan] { font-size: 9px; }

  .h2 { font-size: clamp(40px, 12vw, 62px); }
  .racer__trait { font-size: 14px; }
  .dhero__name { font-size: clamp(58px, 17vw, 96px); }
}
