/* ═══════════════════════════════════════════════════════════════════════
   It is not the trains: TTC subway reliability, 2014–2026.

   The page is built as a ride. Sections alternate between two lit
   environments: `tunnel` (dark, where the data moves) and `platform`
   (light, tiled, where it is explained). The terminus is lit `signal` red,
   because it is where the caveats live. Every environment is the same set
   of custom properties re-declared, so nothing below this block needs to
   know which one it is in.

   Subsystem colours are Okabe-Ito derived, separable under all three
   common forms of colour blindness. Colour never carries meaning alone:
   tile counts, dot size and bar length carry it too, and every legend
   entry is labelled.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  /* Line liveries. Fixed by the TTC, not by this design. */
  --yu:  #F0C419;   /* Line 1 Yonge–University */
  --bd:  #3C9E52;   /* Line 2 Bloor–Danforth   */
  --shp: #9B4B8F;   /* Line 4 Sheppard         */
  --ttc-red: #DA291C;  /* PMS 485, the livery red. Not --red, which is a
                          warning colour this design chose. */

  /* Subsystems */
  --patron:   #E69F00;
  --oper:     #CC79A7;
  --infra:    #009E73;
  --rolling:  #56B4E9;
  --signalc:  #0072B2;
  --external: #D55E00;
  --other:    #7A8494;

  --lamp: #F0C419;  /* sodium tunnel light, deliberately Line 1's yellow */
  --red:  #E0472A;

  --tile-size: 46px;
  --ease: cubic-bezier(.22,.61,.36,1);
}

/* ── environments ─────────────────────────────────────────────────── */
.env-tunnel, body[data-env="tunnel"] {
  --bg:#0B0E11; --bg2:#141A1F; --fg:#E9EEEB; --dim:#8593A0;
  --rule:#232C34; --accent:var(--lamp); --grout:rgba(255,255,255,.035);
}
.env-platform, body[data-env="platform"] {
  --bg:#D5DDD7; --bg2:#E5EAE5; --fg:#0F1513; --dim:#4C5A54;
  --rule:#B4BFB7; --accent:#0F1513; --grout:rgba(15,21,19,.038);
}
.env-signal, body[data-env="signal"] {
  --bg:#150907; --bg2:#20100C; --fg:#F6E7E2; --dim:#B78C82;
  --rule:#3C1D16; --accent:var(--red); --grout:rgba(224,71,42,.05);
}

/* ── base ─────────────────────────────────────────────────────────── */
* { box-sizing: border-box; }
/* display:flex on .legend/.scale/.panel-body would otherwise beat the UA
   sheet's [hidden] rule and leave both map keys on screen at once. */
[hidden] { display: none !important; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: #0B0E11;
  color: var(--fg);
  font-family: Petrona, Georgia, serif;
  font-size: 18px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, svg, canvas { max-width: 100%; }
a { color: inherit; text-underline-offset: .18em; text-decoration-thickness: 1px; }

.wrap {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 clamp(1.1rem, 4vw, 2.6rem);
}

/* ── type ─────────────────────────────────────────────────────────── */
h1, h2, h3, .eyebrow, .num, .lbl, dt, dd, button, select, input, .fineprint, code {
  font-family: Archivo, "Helvetica Neue", Arial, sans-serif;
}
h1 {
  margin: 0;
  font-size: clamp(2.4rem, 6.4vw, 5.5rem);
  font-weight: 900;
  font-stretch: 112%;
  line-height: .86;
  letter-spacing: -.035em;
  text-transform: uppercase;
}
h2 {
  margin: .1rem 0 1rem;
  font-size: clamp(1.85rem, 4.4vw, 3.2rem);
  font-weight: 800;
  font-stretch: 104%;
  line-height: 1.02;
  letter-spacing: -.026em;
  max-width: 18ch;
}
h3 { margin: 0 0 .5rem; font-size: 1.02rem; font-weight: 700; font-stretch: 104%; letter-spacing: -.01em; }

.eyebrow {
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .7rem;
  font-weight: 400;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 1.4rem;
}
.prose {
  max-width: 44ch; margin: 0 0 1.35rem; color: var(--fg); opacity: .94;
  font-size: 1.06rem; line-height: 1.66;
}
.prose em { font-style: italic; }
code {
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .86em; padding: .08em .3em;
  background: var(--grout); border: 1px solid var(--rule); border-radius: 3px;
}
.fineprint {
  font-size: .82rem; line-height: 1.55; color: var(--dim);
  font-weight: 400; letter-spacing: .005em; margin: .7rem 0 0; max-width: 46ch;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--lamp); color: #0B0E11; padding: .6rem 1rem; font-weight: 700;
}
.skip:focus { left: .5rem; top: .5rem; }

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

/* ── custom cursor: a track circuit ───────────────────────────────── */
/* A signalling panel locates a train by which block it occupies. This
   locates the reader the same way: the block snaps to whatever is under
   the pointer and names it. Fine pointers only; a finger already has a
   position indicator. */
.cursor { display: none; }
@media (pointer: fine) {
  body.cursor-on { cursor: none; }
  body.cursor-on a, body.cursor-on button, body.cursor-on select,
  body.cursor-on input, body.cursor-on .st { cursor: none; }
  .cursor { display: block; position: fixed; inset: 0; z-index: 90; pointer-events: none; }
  .cursor .blk {
    position: absolute; left: 0; top: 0;
    width: 26px; height: 26px;
    transform: translate3d(-50%,-50%,0);
    transition: width .16s var(--ease), height .16s var(--ease), opacity .2s;
    color: var(--accent);
  }
  .cursor .blk i {
    position: absolute; width: 9px; height: 9px;
    border: 1.5px solid currentColor;
  }
  .cursor .blk i:nth-child(1) { left:0; top:0; border-right:0; border-bottom:0; }
  .cursor .blk i:nth-child(2) { right:0; top:0; border-left:0; border-bottom:0; }
  .cursor .blk i:nth-child(3) { right:0; bottom:0; border-left:0; border-top:0; }
  .cursor .blk i:nth-child(4) { left:0; bottom:0; border-right:0; border-top:0; }
  .cursor .tag {
    position: absolute; left: 0; top: calc(100% + 7px);
    font-family: "Chivo Mono", ui-monospace, monospace;
    font-size: .62rem; letter-spacing: .14em; text-transform: uppercase;
    white-space: nowrap; color: currentColor; opacity: 0;
    transition: opacity .15s;
  }
  .cursor.is-lock .tag { opacity: 1; }
  .cursor .dot {
    position: absolute; left: 0; top: 0; width: 5px; height: 5px;
    margin: -2.5px 0 0 -2.5px; border-radius: 50%;
    background: var(--accent);
  }
  .cursor.is-lock .dot { opacity: .45; }
  /* .fig-dark is a lit island inside a pale section. The cursor is a fixed
     overlay on <body>, so it cannot inherit those tokens; JS flags it. */
  .cursor.is-ondark .blk { color: #E9EEEB; }
  .cursor.is-ondark .dot { background: #E9EEEB; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .cursor .blk { transition: none; }
}

/* ── strip map rail ───────────────────────────────────────────────── */
/* The diagram above the doors: where you are, what is next. */
.rail {
  position: fixed; z-index: 60;
  left: 0; top: 0; bottom: 0; width: 78px;
  display: flex; align-items: center; justify-content: center;
  color: var(--fg);
}
.rail-line {
  position: absolute; left: 27px; top: 50%; transform: translateY(-50%);
  width: 2px; height: 58vh; background: var(--rule); border-radius: 2px;
}
.rail-line { overflow: visible; }
.rail-run {
  position: absolute; left: 0; top: 0; width: 100%; height: 0;
  background: var(--accent); border-radius: 2px;
}
/* A train, not a marker. Front-on and nose-down, because that is the
   direction it travels as the page scrolls. Red and white is the livery,
   so it stays constant while the environment around it changes. */
.rail-train {
  position: absolute; left: 50%; top: 0; width: 17px; height: 30px;
  /* The stop pips come later in the DOM, so without this the train slides
     underneath every stop it passes. It is the moving part; it goes on top. */
  z-index: 3;
  transform: translate(-50%, -50%);
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--ttc-red) 42%, transparent));
}
.rail-train svg { display: block; width: 100%; height: 100%; }
.rail-stops {
  position: relative; z-index: 1; list-style: none; margin: 0; padding: 0;
  height: 58vh; width: 100%;
}
.rail-stops li {
  position: absolute; left: 22.5px; transform: translateY(-50%);
  /* The gap clears the train, which is wider than the pip it passes over.
     At .7rem the two touched. */
  display: flex; align-items: center; gap: 1.05rem;
}
.rail-stops .pip {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  border: 1.5px solid var(--rule); background: var(--bg);
  transition: background .3s, border-color .3s, transform .3s var(--ease);
}
.rail-stops .name {
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .62rem; letter-spacing: .13em; text-transform: uppercase;
  white-space: nowrap; opacity: 0; transform: translateX(-4px);
  transition: opacity .3s, transform .3s var(--ease);
  color: var(--dim);
}
.rail-stops li.done .pip { border-color: var(--accent); }
.rail-stops li.here .pip { background: var(--accent); border-color: var(--accent); transform: scale(1.5); }
.rail-stops li.here .name { opacity: 1; transform: none; color: var(--fg); }
.rail:hover .name { opacity: 1; transform: none; }
@media (max-width: 900px) { .rail { display: none; } }

/* ── sections ─────────────────────────────────────────────────────── */
main { position: relative; }
.stop {
  position: relative;
  background: var(--bg);
  color: var(--fg);
  padding: clamp(4.5rem, 11vh, 8.5rem) 0 clamp(4.5rem, 11vh, 8.5rem);
}
@media (min-width: 901px) { .stop > .wrap, footer > .wrap { padding-left: 96px; } }

/* Platform sections are tiled. The grout is real: 46px square, the size of
   the glazed tile in a 1966 Bloor–Danforth station wall. */
.env-platform {
  background-image:
    linear-gradient(var(--grout) 1px, transparent 1px),
    linear-gradient(90deg, var(--grout) 1px, transparent 1px);
  background-size: var(--tile-size) var(--tile-size);
}
.env-tunnel::before, .env-signal::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 55% at 50% 0%,
              color-mix(in srgb, var(--accent) 8%, transparent), transparent 70%);
}
.stop > .wrap { position: relative; z-index: 1; }

/* ── 1 · the wall ─────────────────────────────────────────────────── */
.wall-wrap { padding-top: clamp(1rem, 5vh, 3rem); }
.wall {
  position: relative;
  border: 1px solid var(--rule);
  background: #0F1418;
  padding: 10px;
  box-shadow: 0 40px 90px -50px #000 inset, 0 0 0 6px rgba(255,255,255,.015);
}
.wall-grid {
  display: grid;
  grid-template-columns: repeat(var(--cols, 42), 1fr);
  gap: 2px;
}
.wall-grid b {
  display: block; aspect-ratio: 1; border-radius: 1px;
  background: var(--c);
  opacity: .92;
  transition: transform .12s var(--ease), opacity .12s;
}
.wall-grid b:hover { transform: scale(1.55); opacity: 1; position: relative; z-index: 2; }

/* The name plate. Mounted on the wall, exactly like the real thing. */
.plate {
  position: absolute; left: 0; top: 47%;
  width: min(74%, 830px);
  transform: translateY(-50%);
  background: #0B0E11;
  border-top: 1px solid rgba(255,255,255,.16);
  border-bottom: 1px solid rgba(0,0,0,.7);
  padding: clamp(1rem, 3.5vw, 2.6rem) clamp(1.1rem, 4vw, 3rem);
  box-shadow: 0 24px 60px -18px rgba(0,0,0,.85);
}
.plate h1 { color: #F4F7F5; }
.plate-tiny { font-size: .28em; font-weight: 500; letter-spacing: 0; vertical-align: middle; opacity: .7; }
.plate h1::after {
  content: ""; display: block; width: 100%; height: 4px; margin-top: clamp(.7rem,1.6vw,1.2rem);
  background: linear-gradient(90deg, var(--yu) 0 33.3%, var(--bd) 33.3% 66.6%, var(--shp) 66.6%);
}

/* One column, read top to bottom: what a tile is, then the three figures,
   then the key. It was two columns of very different length, and shortening
   the sentence left a column of nothing beside a stack of numbers. */
.wall-foot { margin-top: 1.7rem; }
.lede { margin: 0; font-size: clamp(1rem, 1.35vw, 1.16rem); color: var(--fg); opacity: .9; max-width: 58ch; }
.lede b { font-family: Archivo, sans-serif; font-weight: 700; font-variant-numeric: tabular-nums; }

/* The three headline figures as one strip, the way a platform board carries
   its rows: rule, label, number. Label above number rather than beside it,
   because "incidents that cost service time" needs the width. */
.topline {
  margin: 1.5rem 0 0; display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.4rem;
}
/* Label rows can run to one line or two. The number is pinned to the bottom
   of the cell so the three figures share a baseline either way. */
.topline div {
  border-top: 1px solid var(--rule); padding-top: .6rem;
  display: grid; grid-template-rows: 1fr auto;
}
.topline dt {
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .64rem;
  letter-spacing: .13em; text-transform: uppercase; color: var(--dim);
  max-width: 22ch;
}
.topline dd {
  margin: .35rem 0 0; font-weight: 800; font-stretch: 108%;
  font-size: clamp(1.5rem, 3vw, 2.3rem);
  letter-spacing: -.02em; font-variant-numeric: tabular-nums; line-height: 1;
}
@media (max-width: 560px) { .topline { grid-template-columns: 1fr; gap: .9rem; } }

/* Seven boxed chips wrapped to a ragged five-and-two. The swatch already
   separates one entry from the next, so the boxes came off and the key
   fits on one line. */
.legend-wall {
  display: flex; flex-wrap: wrap; gap: .4rem 1.15rem; margin-top: 1.5rem;
}
.legend-wall span {
  display: inline-flex; align-items: center; gap: .4rem;
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .62rem; letter-spacing: .07em; text-transform: uppercase; color: var(--dim);
}
.legend-wall i { width: 9px; height: 9px; background: var(--c); flex: none; }
.legend-wall u { text-decoration: none; color: var(--fg); font-variant-numeric: tabular-nums; }

.scrollcue {
  margin: 2.2rem 0 0; display: flex; align-items: center; gap: .7rem;
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .68rem; letter-spacing: .2em; text-transform: uppercase; color: var(--dim);
}
.scrollcue span { width: 34px; height: 1px; background: var(--accent); animation: creep 2.6s var(--ease) infinite; }
@keyframes creep { 0% { transform: scaleX(0); transform-origin: left; }
                   55% { transform: scaleX(1); transform-origin: left; }
                   56%,100% { transform: scaleX(0); transform-origin: right; } }
@media (prefers-reduced-motion: reduce) { .scrollcue span { animation: none; } }

@media (max-width: 780px) {
  .wall-foot { grid-template-columns: 1fr; }
  /* The headline has to arrive before the wall on a phone, so the wall
     becomes a column and the plate takes the first slot. */
  .wall { display: flex; flex-direction: column; }
  .plate { order: -1; position: static; transform: none; width: auto;
           margin: -10px -10px 10px; }
  /* Stacked, the plate covers nothing, so there is nothing to reveal. */
  .wall-bare { display: none; }
}

/* ── 1b · rows of tiles ───────────────────────────────────────────── */
.rows { margin: 2.2rem 0 3rem; display: grid; gap: .55rem; }
.row {
  display: grid; grid-template-columns: 130px minmax(0,1fr) auto;
  gap: .9rem; align-items: center;
}
.row .lbl {
  font-size: .74rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  text-align: right; color: var(--dim);
}
/* The gap between tiles is painted inside each tile rather than laid out
   as a flex `gap`. A 267-tile row with a 1px gap and a 1px tile floor needs
   534px of min-content, which is wider than this column at most window
   sizes, so the tiles overflowed to the right and painted over the row's
   own figure. As a gradient stop in percent it scales with the tile: the
   row fits any width, tiles stay separable while there is room for them,
   and below that they fade into a solid bar whose width still carries the
   proportion. */
.row .strip { display: flex; height: 22px; min-width: 0; }
.row .strip b {
  flex: 1 1 0; min-width: 0;
  background: linear-gradient(90deg, var(--c) 0 68%, transparent 68%);
}
.row .num {
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .78rem;
  font-variant-numeric: tabular-nums; color: var(--fg); white-space: nowrap;
}
.row .num small { color: var(--dim); }
.row.is-key .lbl { color: var(--fg); }
@media (max-width: 640px) {
  .row { grid-template-columns: 92px minmax(0,1fr) auto; gap: .5rem; }
  .row .lbl { font-size: .62rem; }
}

/* ── two lanes ─────────────────────────────────────────────────────
   Left is the story: plain language and the picture, and it has to read
   start to finish on its own. Right is the same claim in engineering
   terms -- method, definitions, what the alternative call would have
   said. The method notes used to live in two folded blocks at the foot
   of the page, which meant every stop said its caveats twice: once in
   prose and once again 900 lines later. In the lane they sit beside the
   claim they qualify, and get said once. */
.dual {
  display: grid; gap: clamp(1.6rem, 4vw, 3.5rem); margin-top: 1.6rem;
  grid-template-columns: minmax(0, 1fr) minmax(0, 310px);
}
.lane-plain { min-width: 0; }
/* align-self is load-bearing: a grid item stretches to the row by
   default, and a stretched box has nothing left to stick against. */
.lane-tech {
  min-width: 0; position: sticky; top: 2.2rem; align-self: start;
  border-left: 1px solid var(--rule); padding-left: clamp(.9rem, 1.6vw, 1.4rem);
}
.lane-tech h4 {
  margin: 1.8rem 0 .5rem; font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 600; color: var(--accent);
}
.lane-tech > h4:first-child { margin-top: 0; }
.lane-tech p {
  margin: 0 0 .7rem; font-size: .92rem; line-height: 1.66; color: var(--dim);
  max-width: 36ch;
}
.lane-tech b { color: var(--fg); font-weight: 600; }
.lane-tech code { font-size: .82em; }
.lane-tech a { color: var(--fg); }

/* The sentence that carries the reader from one stop to the next. It is
   the argument's join, so it is set apart from the prose it follows. */
.handoff {
  margin-top: 2.2rem; padding-top: 1rem; border-top: 1px solid var(--rule);
  font-size: 1.02rem; color: var(--fg); opacity: 1; max-width: 52ch;
}
.handoff::after { content: " \2192"; color: var(--accent); }

.stop-turn { margin-top: clamp(2.5rem, 6vh, 4rem); }

/* The ask. Numbered because each one names the stop that earned it. */
.beats { list-style: none; counter-reset: beat; margin: 1.8rem 0 0; padding: 0;
  display: grid; gap: 1.1rem; max-width: 60ch; }
.beats li {
  counter-increment: beat; position: relative; padding-left: 2.4rem;
  font-size: 1.06rem; line-height: 1.62;
}
.beats li::before {
  content: counter(beat); position: absolute; left: 0; top: .05rem;
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .7rem;
  color: var(--accent); border: 1px solid var(--rule); width: 1.5rem;
  height: 1.5rem; display: grid; place-items: center;
}
.beats b { font-family: Archivo, sans-serif; font-weight: 700; color: var(--fg); }

@media (max-width: 900px) {
  .dual { grid-template-columns: 1fr; }
  .lane-tech {
    position: static;
    border-left: 0; border-top: 1px solid var(--rule);
    padding-left: 0; padding-top: 1.2rem;
  }
  .lane-tech p { max-width: 60ch; }
}

.note { border-top: 2px solid var(--fg); padding-top: 1rem; }
.note p { font-size: 1rem; line-height: 1.62; max-width: 42ch; }

/* Permutation importance, drawn. Printed as three numbers in a sentence it
   read as a footnote; the whole point is that one bar is the width of the
   card and the other three are stubs, which is what the predictor above
   feels like when you change the station and nothing moves. */
.weights { list-style: none; margin: 1.1rem 0 1.2rem; padding: 0; display: grid; gap: .5rem; }
.weights li {
  display: grid; grid-template-columns: 7.5rem 1fr 3rem;
  gap: .7rem; align-items: center;
}
.w-name {
  font-size: .6rem; letter-spacing: .1em; text-transform: uppercase; color: var(--dim);
}
.weights .w-track {
  display: block; min-width: 0; height: 10px;
  border-left: 1px solid var(--rule); padding-left: 2px;
}
.weights .w-track i {
  display: block; height: 100%; background: var(--accent); opacity: .8;
}
.weights li:first-child .w-track i { opacity: 1; }
.weights b {
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .72rem;
  font-variant-numeric: tabular-nums; text-align: right; color: var(--fg);
}
.weights li + li b { color: var(--dim); }
@media (max-width: 400px) { .weights li { grid-template-columns: 6rem 1fr 2.6rem; } }

/* 100%-stacked bars, one row per subsystem. Each bar is the whole of that
   subsystem's incidents, so the row carries its own denominator and there
   is no cross-row total to misread. The earlier dot plot avoided that by
   removing the whole entirely, which traded one confusion for another. */
.zero { margin: 1.2rem 0 0; display: grid; gap: .45rem; }
.zero div { display: grid; grid-template-columns: 116px 1fr 76px; gap: .6rem; align-items: center; }
.zero .lbl { text-align: right; font-size: .76rem; }
.zero .v {
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .8rem;
  font-variant-numeric: tabular-nums; text-align: right;
}
/* The track is the unshaded remainder: what the bar does not fill is the
   share that did cause a delay, so the two segments need no gap.
   It has to be visible or the redesign is pointless -- the first pass used
   the page's own rule colour and measured 1.15:1 against the background,
   which drew the filled part alone and left the whole invisible. */
.ztrack {
  position: relative; height: 15px; border-radius: 2px;
  background: rgba(255, 255, 255, .16);
  overflow: hidden;
}
.zfill {
  position: absolute; inset: 0 auto 0 0; background: var(--c); border-radius: 2px 0 0 2px;
}
/* One tick per row at the network rate. Every row is the same width, so
   the seven segments line up into a single readable column. */
/* The tick crosses the coloured fill on the rows above the network rate
   and the empty track on those below, and no single colour reads well on
   both: white measured 12.5:1 on the empty track but 1.5:1 on the bright
   fills, black the other way round. Difference blending inverts whatever
   is under it, so it cannot lose contrast against either. */
.zref {
  position: absolute; top: 0; bottom: 0; width: 1px;
  background: #fff; mix-blend-mode: difference;
}
.zero-foot { margin-top: .1rem; }
.zero-foot .ztrack { height: auto; background: none; overflow: visible; }
/* Lower case and no tracking: uppercased with letter-spacing this ran
   wider than the track has room for once the lane narrows, and a label
   that overflows its own axis is worse than a quiet one. */
.zref-lab {
  position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap;
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .58rem;
  color: var(--dim);
}

/* A key, not an axis: with the whole drawn, the question is which segment
   is which, not where the scale starts. */
.zero .zero-head { margin-bottom: .3rem; }
.zero .zero-head .zkey {
  display: flex; gap: 1rem; align-items: center;
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .6rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dim);
}
.zero .zero-head .zkey i { font-style: normal; display: flex; align-items: center; gap: .35rem; }
.zero .zero-head .sw { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
.zero .zero-head .sw.on { background: var(--dim); }
.zero .zero-head .sw.off { background: rgba(255, 255, 255, .16); }

@media (max-width: 640px) {
  .zero div { grid-template-columns: 92px minmax(0, 1fr) 68px; gap: .45rem; }
  .zero .zero-head .zkey { gap: .6rem; font-size: .55rem; }
}


/* ── 2 · reliability in engineering units ─────────────────────────── */
.block-head {
  margin: 2.6rem 0 0; padding-top: .55rem; border-top: 1px solid var(--rule);
  font-size: .68rem; letter-spacing: .18em; text-transform: uppercase;
  font-weight: 600; color: var(--dim);
}

.avail { margin: 1.4rem 0 1rem; display: grid; gap: .9rem; }
.avail-row {
  display: grid; grid-template-columns: 140px 1fr 150px;
  gap: 1rem; align-items: center;
}
.avail-name b { display: block; font-size: .95rem; letter-spacing: -.01em; }
.avail-name span, .avail-num span {
  display: block; font-size: .68rem; color: var(--dim);
  font-variant-numeric: tabular-nums;
}
/* The bar is the whole of one line's operating hours. It used to fill that
   mass at 12% opacity, which is nearly invisible against the page, so a
   reader's eye landed on the small saturated red cut and read the bar as
   mostly bad rather than mostly fine. The clean majority now carries a fill
   of its own, so 94% *looks* like 94% before anyone reads the number, and it
   runs first: hours come clean until an incident interrupts them, so the
   green leads and the red cut closes the bar. */
.avail-bar {
  position: relative; height: 26px; overflow: hidden;
  background: color-mix(in srgb, var(--fg) 8%, transparent);
  border: 1px solid var(--rule);
}
.avail-bar .avail-ok {
  position: absolute; inset: 0; display: block;
  /* Same hatch as the cut, in the running livery: one bar, one drawing
     language, read left to right as the hours actually pass. */
  background:
    repeating-linear-gradient(-45deg,
      color-mix(in srgb, var(--bd) 40%, transparent) 0 2px, transparent 2px 5px),
    color-mix(in srgb, var(--bd) 34%, var(--bg2));
}
.avail-bar .avail-cut {
  position: absolute; inset: 0 0 0 auto; display: block;
  background:
    repeating-linear-gradient(-45deg,
      var(--red) 0 2px, transparent 2px 5px),
    color-mix(in srgb, var(--red) 55%, transparent);
  border-left: 2px solid var(--red);
  /* A true-to-scale sliver at 94%+ availability is a few pixels wide and
     would vanish under the hatch spacing above. Floored so the "there is
     a cut here" read survives even when the real share is under 1%. */
  min-width: 6px;
}
.avail-num { text-align: right; font-variant-numeric: tabular-nums; }
.avail-num b { font-family: "Chivo Mono", ui-monospace, monospace; font-size: .92rem; }

/* These two sit under full-width blocks, so the column measure that suits a
   note would strand them. */
#stop-rams > .wrap > .fineprint { max-width: 74ch; }

.mtbf-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap; margin-top: 1.4rem;
}
.prose.narrow { max-width: 46ch; margin: 0; }

.mtbf { margin: 1.2rem 0 0; display: grid; gap: .5rem; }
.mtbf-row {
  display: grid; gap: .6rem; align-items: center;
  grid-template-columns: 112px 1fr 62px 78px;
}
/* Column labels, so the three numeric columns are not left to be guessed
   at from their units alone. */
.mtbf-key {
  font-size: .6rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--dim); padding-bottom: .2rem; margin-bottom: .1rem;
  border-bottom: 1px solid var(--rule);
}
.mtbf-key > span:nth-child(4) { text-align: right; }
.mtbf-gauge-head { text-align: left; }
.mtbf-label { text-align: right; font-size: .76rem; }
.mtbf-gauge { height: 10px; background: var(--grout); border: 1px solid var(--rule); }
.mtbf-gauge i { display: block; height: 100%; background: var(--c); }
.mtbf-v {
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .8rem;
  font-variant-numeric: tabular-nums; text-align: right;
}
.mtbf-v.dim { color: var(--dim); }

@media (max-width: 760px) {
  .avail-row { grid-template-columns: 1fr; gap: .4rem; }
  .avail-num { text-align: left; }
  .mtbf-row { grid-template-columns: 78px 1fr 54px 66px; gap: .45rem; }
}


/* The simulated panel is drawn as a diagram, not as data: dashed border
   and a hatched ground, so it cannot be mistaken for a measurement even
   in a screenshot with the caption cropped off. */
/* Rolling stock blue: these are rolling-stock failures, so the cloud
   carries the same colour it does everywhere else on the page. */

/* ── 4b · the trend reversal ──────────────────────────────────────── */

/* ── 6 · what the model knows ─────────────────────────────────────── */
/* Which years taught the model, and which judged it. Drawn to scale because
   the proportion is the argument: ten years in, six months held back. */
.span { margin: 1.8rem 0 0; }
.span-lab {
  margin: 0 0 .6rem; font-size: .64rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--dim);
}
.span-bar { display: flex; gap: 2px; height: 14px; }
.span-bar i { display: block; height: 100%; }
.span-bar .sp-learn { background: color-mix(in srgb, var(--fg) 34%, transparent); }
.span-bar .sp-tune  { background: color-mix(in srgb, var(--fg) 18%, transparent); }
.span-bar .sp-test  { background: var(--red); }
.span-key {
  list-style: none; margin: .8rem 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;
}
@media (max-width: 620px) { .span-key { grid-template-columns: 1fr; gap: .7rem; } }
/* A swatch, not a top rule. A rule spanning an equal-width column sits
   directly under the to-scale bar above and reads as the bar's own axis
   continuing -- as if each caption were a tick pointing at a position on it
   -- when the columns are in fact even thirds standing in for 80/16/4,
   unrelated to where they sit under the bar. A swatch says "this colour
   means this" without implying position, matching the wall's own legend. */
.span-key li { display: flex; gap: .5rem; align-items: flex-start; }
.span-key i { width: 9px; height: 9px; margin-top: .3rem; flex: none; }
.span-key li.sp-learn i { background: color-mix(in srgb, var(--fg) 34%, transparent); }
.span-key li.sp-tune  i { background: color-mix(in srgb, var(--fg) 18%, transparent); }
.span-key li.sp-test  i { background: var(--red); }
.span-key b {
  display: block; font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .74rem; color: var(--fg);
}
.span-key li.sp-test b { color: var(--red); }
.span-key span { display: block; margin-top: .2rem; font-size: .76rem; color: var(--dim); }

.predict {
  margin: 1.6rem 0 0; border: 1px solid var(--rule); background: var(--bg2);
}

/* The controls used to stand in a 300px column beside a tall output panel,
   which left most of that column empty at every viewport that fit the card.
   Across the top they take the width they need and none of the height. */
.pred-controls {
  display: grid; grid-template-columns: 2.2fr 1fr 1fr; gap: 1rem;
  padding: 1.3rem 1.6rem; border-bottom: 1px solid var(--rule);
}
@media (max-width: 720px) { .pred-controls { grid-template-columns: 1fr; } }
/* 138 cause descriptions, some of them long. Side-by-side label and
   control truncates them to uselessness, so the label sits above. */
.pred-controls .selbox {
  width: 100%; flex-direction: column; align-items: stretch; gap: .3rem;
}
.pred-controls .selbox::after { top: auto; bottom: .85rem; }
.pred-controls label { font-size: .6rem; letter-spacing: .14em; }
.pred-controls select { width: 100%; max-width: 100%; text-overflow: ellipsis; }

.pred-out { padding: 1.5rem 1.6rem; }
.pred-lab {
  margin: 0; font-size: .64rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--dim);
}
.pred-range {
  margin: .45rem 0 .9rem; display: flex; align-items: baseline; gap: .7rem;
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
}
.pred-range b { font-size: clamp(2.4rem, 7vw, 3.6rem); font-weight: 500; line-height: 1; }
.pred-unit { font-size: .8rem; color: var(--dim); font-family: "Petrona", serif; }

/* The band, drawn. Zero at the left edge, so the distance between the
   typical delay and the long tail is a length rather than two numbers the
   reader has to subtract. */
.pred-strip { display: block; }
.strip-track {
  position: relative; display: block; height: 12px;
  background: var(--grout); border-bottom: 1px solid var(--rule);
}
.strip-track i {
  position: absolute; top: 0; bottom: 0; display: block;
  background: color-mix(in srgb, var(--accent) 34%, transparent);
}
.strip-track u {
  position: absolute; top: -3px; bottom: -3px; width: 2px; display: block;
  background: var(--accent); transform: translateX(-1px);
}
.strip-ends {
  display: flex; justify-content: space-between; gap: 1rem; margin-top: .4rem;
  font-size: .66rem; letter-spacing: .06em; text-transform: uppercase; color: var(--dim);
}
.strip-ends b {
  font-family: "Chivo Mono", ui-monospace, monospace; color: var(--fg); font-weight: 500;
}
.strip-note { margin: .5rem 0 0; font-size: .74rem; line-height: 1.5; color: var(--dim); }

#stop-model > .wrap > .fineprint { max-width: 74ch; }

.pred-moved {
  margin: 1.1rem 0 0; padding-top: .7rem; border-top: 1px solid var(--rule);
  font-size: .8rem; color: var(--fg);
}
.pred-moved u { text-decoration: none; font-family: "Chivo Mono", ui-monospace, monospace; }
.pred-moved.is-flat { color: var(--dim); }

/* The bucket the current estimate falls into. */
.err-note { margin: 0; font-size: .74rem; line-height: 1.5; color: var(--dim); }
.err-note b { color: var(--fg); }
.err-note u { text-decoration: none; font-family: "Chivo Mono", ui-monospace, monospace; color: var(--fg); }

#stop-null figcaption { max-width: 78ch; }

.scores { width: 100%; border-collapse: collapse; margin: .9rem 0 .3rem; font-size: .76rem; }
.scores th {
  text-align: right; font-weight: 600; font-size: .6rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--dim);
  border-bottom: 1px solid var(--rule); padding: 0 0 .35rem .9rem;
}
.scores th:first-child { text-align: left; padding-left: 0; }
.scores td + td { padding-left: .9rem; }
.scores td {
  padding: .4rem 0; text-align: right;
  font-family: "Chivo Mono", ui-monospace, monospace; font-variant-numeric: tabular-nums;
}
.scores td:first-child { text-align: left; font-family: inherit; }
.scores tr + tr td { border-top: 1px solid var(--grout); }

/* ── 3 · the replay ───────────────────────────────────────────────── */
.stage {
  display: grid; grid-template-columns: minmax(0,1fr) 330px;
  gap: 1.4rem; align-items: start; margin-top: 2rem;
}
@media (max-width: 980px) { .stage { grid-template-columns: 1fr; } }

.modes { display: flex; gap: 0; margin-bottom: 1rem; border: 1px solid var(--rule); width: max-content; }
.mode {
  background: transparent; color: var(--dim); border: 0; border-right: 1px solid var(--rule);
  padding: .5rem 1.1rem; font-size: .7rem; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; cursor: pointer;
}
.mode:last-child { border-right: 0; }
.mode.is-on { background: var(--accent); color: var(--bg); }

.mapwrap { position: relative; border: 1px solid var(--rule); background: var(--bg2); }
#map, #fx { width: 100%; display: block; }
/* left/top/width/height are all written by resizeCanvas() so the canvas
   tracks the SVG's own box rather than the wrapper's. `inset: 0` would pin it
   to the wrapper, which is wrong the moment the wrapper has padding or the
   SVG is narrower than it: the dots would paint at an offset from the
   stations they belong to. */
#fx { position: absolute; left: 0; top: 0; pointer-events: none; }

/* A 70-station schematic drawn into a two-column grid is about 660px wide on
   a laptop, which is under half the width the labelling was drawn for. The
   map takes over the screen on request and the replay keeps running inside
   it, so nothing has to be re-found afterwards. */
/* The map and its panel go full screen together: the panel is where a
   selected station reports, and leaving it outside made clicking a station
   in full screen do nothing a reader could see. */
.stage:fullscreen {
  background: var(--bg); padding: 1rem; gap: 1rem;
  grid-template-columns: minmax(0, 1fr) 330px; align-items: center;
}
.stage:fullscreen .stage-main {
  display: flex; flex-direction: column; justify-content: center; align-items: center;
}
.stage:fullscreen .modes, .stage:fullscreen .caption { display: none; }
.stage:fullscreen .mapwrap { border: 0; width: 100%; }
.stage:fullscreen .panel { max-height: 94vh; overflow-y: auto; }
@media (max-width: 980px) {
  .stage:fullscreen { grid-template-columns: 1fr; align-items: start; }
}
/* Clamp by width, never by height. `max-height` on an SVG with a viewBox
   letterboxes the drawing inside its own box, which silently decouples the
   box width resizeCanvas() measures from the scale the drawing is actually
   at. Width-only clamping keeps the two identical.
   1.6129 is the viewBox ratio, 1000/620. */
.stage:fullscreen #map { width: 100%; max-width: calc(92vh * 1.6129); }
.stage:fullscreen .map-hint { display: none; }
.fs {
  position: absolute; right: 8px; top: 8px; z-index: 3;
  display: inline-flex; align-items: center; gap: .4rem;
  background: var(--bg); color: var(--dim);
  border: 1px solid var(--rule); padding: .34rem .6rem; cursor: pointer;
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .6rem; letter-spacing: .13em; text-transform: uppercase;
}
.fs:hover { color: var(--fg); border-color: var(--fg); }
.fs svg { width: 10px; height: 10px; fill: none; stroke: currentColor; stroke-width: 1.6; }

/* Stations are 5px dots. The prompt to click one has to live on the map,
   not only in the empty panel beside it. */
.map-hint {
  position: absolute; left: 12px; bottom: 10px; z-index: 2; pointer-events: none;
  display: flex; align-items: center; gap: .45rem;
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .62rem; letter-spacing: .13em; text-transform: uppercase;
  color: var(--dim); transition: opacity .4s;
}
.map-hint i {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  border: 1.5px solid var(--accent);
  animation: ping 2.4s var(--ease) infinite;
}
.map-hint.is-done { opacity: 0; }
@keyframes ping {
  0%, 70%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
  35% { box-shadow: 0 0 0 6px transparent; }
}
@media (prefers-reduced-motion: reduce) { .map-hint i { animation: none; } }

/* ── readout: one tooltip, shared by every chart ──────────────────── */
/* Modelled on the platform indicator: mono, hard-edged, dark whatever it is
   over, so a reading never changes appearance between sections. */
.tip {
  position: fixed; z-index: 95; pointer-events: none; opacity: 0;
  transform: translate(-50%, -100%);
  background: #0B0E11; color: #E9EEEB;
  border: 1px solid #2A343C; border-left: 3px solid var(--tc, #F0C419);
  padding: .5rem .65rem; max-width: 260px;
  transition: opacity .12s;
  box-shadow: 0 18px 40px -20px rgba(0,0,0,.9);
}
.tip.is-on { opacity: 1; }
.tip b {
  display: block; font-family: Archivo, sans-serif; font-weight: 700;
  font-size: .88rem; letter-spacing: -.01em; margin-bottom: .15rem;
}
.tip span {
  display: block; font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .62rem; letter-spacing: .08em; line-height: 1.6; color: #8593A0;
  font-variant-numeric: tabular-nums;
}
.tip span u { text-decoration: none; color: #E9EEEB; }
.clock {
  position: absolute; left: 12px; top: 10px;
  font-family: Archivo, sans-serif; font-weight: 800; font-stretch: 110%;
  font-size: clamp(1.4rem, 3.2vw, 2.4rem); letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; color: var(--fg); opacity: .28;
  pointer-events: none;
}

.st { cursor: pointer; }
.st circle { transition: r .12s ease; }
/* The visible dot stays 5px because 70 of them at 14px would be a blob. The
   hit area is 14px and invisible, so the target matches the intent rather
   than the drawing. */
.st .hit { fill: transparent; stroke: none; }
.st:hover .dotc, .st.is-sel .dotc { stroke: var(--fg); stroke-width: 3; }
.st text { font-size: 10.5px; font-weight: 500; fill: var(--dim); pointer-events: none;
  font-family: Archivo, sans-serif; letter-spacing: .01em; }
.st:hover text, .st.is-sel text { fill: var(--fg); }
.st .ring { fill: none; stroke: var(--accent); stroke-width: 1.5; opacity: 0;
  transition: opacity .2s, r .2s var(--ease); pointer-events: none; }
.st.is-sel .ring { opacity: .9; }

.transport { display: flex; align-items: center; gap: 1rem; margin-top: 1rem; }
.play {
  flex: none; width: 46px; height: 46px; cursor: pointer;
  background: var(--accent); color: var(--bg); border: 0; font-size: .95rem;
}
.scrubwrap { flex: 1; }
#scrub { width: 100%; accent-color: var(--accent); cursor: pointer; }
.ticks {
  display: flex; justify-content: space-between; margin-top: .1rem;
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .6rem;
  letter-spacing: .1em; color: var(--dim);
}
/* Seven year labels collide once the scrub is phone-width; drop every
   other one rather than shrinking them past reading size. */
@media (max-width: 760px) { .ticks span:nth-child(even) { display: none; } }
.speed {
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .64rem;
  letter-spacing: .13em; text-transform: uppercase; color: var(--dim);
  display: flex; gap: .5rem; align-items: center;
}


/* ── selects ──────────────────────────────────────────────────────── */
.selbox { position: relative; display: inline-flex; align-items: center; gap: .5rem; }
.selbox::after {
  content: ""; position: absolute; right: .58rem; top: 50%; margin-top: -1px;
  width: 0; height: 0; pointer-events: none;
  border-left: 3.5px solid transparent;
  border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor;
}
select {
  appearance: none; -webkit-appearance: none;
  background: transparent; color: var(--fg);
  border: 1px solid var(--rule); border-radius: 0;
  padding: .36rem 1.6rem .36rem .6rem;
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .64rem; font-weight: 500; letter-spacing: .12em;
  text-transform: uppercase; cursor: pointer;
  transition: border-color .15s, background .15s;
}
select:hover { border-color: var(--fg); background: var(--grout); }
/* The dropped list is the browser's own widget and only takes these two
   properties, so it is set to the section's colours and left alone. */
select option {
  background: var(--bg2); color: var(--fg);
  font-family: "Chivo Mono", ui-monospace, monospace;
  text-transform: none; letter-spacing: 0;
}

.odo { display: flex; gap: 2rem; margin-top: .9rem; flex-wrap: wrap;
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .66rem;
  letter-spacing: .13em; text-transform: uppercase; color: var(--dim); }
.odo b { color: var(--fg); font-family: Archivo, sans-serif; font-weight: 700;
  font-size: 1.1rem; letter-spacing: -.01em; font-variant-numeric: tabular-nums; margin-left: .5rem; }

.legend { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: 1.2rem; }
.chip {
  display: inline-flex; align-items: center; gap: .45rem; cursor: pointer;
  background: transparent; border: 1px solid var(--rule); padding: .32rem .65rem;
  font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: .64rem; letter-spacing: .1em; text-transform: uppercase; color: var(--dim);
}
.chip[aria-pressed="true"] { color: var(--fg); border-color: var(--fg); }
.chip[aria-pressed="false"] { opacity: .35; }
.chip .swatch { width: 9px; height: 9px; }

.scale { display: flex; align-items: center; gap: .6rem; margin-top: 1.2rem; flex-wrap: wrap;
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .62rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--dim); }
.scale .ramp { width: 190px; height: 9px;
  background: linear-gradient(90deg, var(--infra), var(--rule) 50%, var(--external)); }
.scale .nodata { display: inline-flex; align-items: center; gap: .35rem; margin-left: .4rem; }
.scale .nodata i { width: 9px; height: 9px; border-radius: 50%; border: 1px dashed var(--dim); }

.caption { margin: 1.1rem 0 0; font-size: .88rem; color: var(--dim); max-width: 66ch; }

.panel { border: 1px solid var(--rule); background: var(--bg2); padding: 1.2rem; min-height: 320px; }
.panel h3 { font-size: 1.25rem; font-weight: 800; font-stretch: 106%; letter-spacing: -.02em; }
.panel p { color: var(--dim); font-size: .9rem; }
.panel .lines { font-family: "Chivo Mono", ui-monospace, monospace; font-size: .64rem;
  letter-spacing: .13em; text-transform: uppercase; color: var(--dim); margin: -.2rem 0 .9rem; }
.stat { display: flex; justify-content: space-between; align-items: baseline;
  padding: .45rem 0; border-bottom: 1px solid var(--rule); }
.stat span { font-family: "Chivo Mono", ui-monospace, monospace; font-size: .64rem;
  letter-spacing: .11em; text-transform: uppercase; color: var(--dim); }
.stat b { font-family: Archivo, sans-serif; font-weight: 700; font-size: 1.05rem;
  font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.bars { margin: 1rem 0; }
.bar-row { display: grid; grid-template-columns: 84px 1fr 34px; gap: .5rem; align-items: center;
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .62rem;
  letter-spacing: .08em; text-transform: uppercase; margin-bottom: .3rem; color: var(--dim); }
.bar-row .track { background: var(--grout); height: 8px; }
.bar-row .fill { height: 100%; display: block; }
.bar-row .pct { text-align: right; font-variant-numeric: tabular-nums; }
.codes { margin: .8rem 0 0; padding: 0; list-style: none; font-size: .8rem; }
.codes li { padding: .35rem 0; border-bottom: 1px solid var(--rule); color: var(--dim);
  display: flex; justify-content: space-between; gap: 1rem; }
.codes b { color: var(--fg); font-family: "Chivo Mono", ui-monospace, monospace; font-size: .72rem; }
.codes u { text-decoration: none; font-variant-numeric: tabular-nums; }
.verdict { margin-top: 1rem; padding: .8rem; font-size: .84rem; line-height: 1.45;
  border-left: 3px solid var(--c); background: var(--grout); color: var(--fg); }

/* ── figures ──────────────────────────────────────────────────────── */
.fig { margin: 2.2rem 0 2rem; }
.fig figcaption { margin-top: .9rem; font-family: Archivo, sans-serif;
  font-size: .74rem; line-height: 1.55; color: var(--dim); max-width: 56ch; }
.fig svg { display: block; width: 100%; height: auto; overflow: visible; }
/* Shared by every figure on the page, not by any one chart. They sat below
   the ATC step buttons and were deleted with them; SVG text takes no colour
   from `color`, so losing the fill left every axis label and station name
   painting default black on a dark section. */
.svg-lbl { font-family: "Chivo Mono", ui-monospace, monospace; font-size: 10.5px;
  letter-spacing: .1em; text-transform: uppercase; fill: var(--dim); }
.svg-name { font-family: Archivo, sans-serif; font-size: 11px; font-weight: 600; fill: var(--fg); }
/* A halo, the way a map labels a street over whatever it crosses. The
   scatter's dots crowd tightest exactly where the named stations sit, so a
   label with no backdrop reads as letters tangled in circles. Stroke first,
   fill over it, so the outline sits behind the glyph instead of fattening
   it. */
.scatter .svg-name {
  paint-order: stroke fill;
  stroke: var(--bg); stroke-width: 3.5px; stroke-linejoin: round;
}
.svg-num { font-family: Archivo, sans-serif; font-size: 13px; font-weight: 700; fill: var(--fg); }
/* ── 3b · scatter ─────────────────────────────────────────────────── */
/* Every station was drawn and only 5 were named, which left most of the plot
   as positions nobody could put a name to. Every dot answers now. */
.scatter .pt { cursor: pointer; }
.scatter .pt .hit { fill: transparent; }
.scatter .pt:hover .mark, .scatter .pt.is-sel .mark { stroke: var(--fg); stroke-width: 2.5; }
.scatter .pt:focus-visible { outline: none; }
.scatter .pt:focus-visible .mark { stroke: var(--accent); stroke-width: 3; }
.scatter .pt.is-dim { opacity: .25; }
.scatter-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 1.2rem;
  margin-bottom: .7rem;
}
.scatter-hint {
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .6rem;
  letter-spacing: .13em; text-transform: uppercase; color: var(--dim);
}
.scatter-find { min-width: 190px; }

.versus { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px,1fr));
  gap: 1px; background: var(--rule); border: 1px solid var(--rule); margin: 2rem 0; }
.versus > div { background: var(--bg); padding: 1.1rem 1.2rem; }
.versus .lbl { font-family: "Chivo Mono", ui-monospace, monospace; font-size: .64rem; letter-spacing: .13em;
  text-transform: uppercase; color: var(--dim); }
.versus .big { font-family: Archivo, sans-serif; font-weight: 800; font-stretch: 108%;
  font-size: 2.1rem; letter-spacing: -.03em; line-height: 1.1; margin: .3rem 0 .2rem; }
.versus p { margin: 0; font-size: .84rem; color: var(--dim); }

.year-fig { display: grid; grid-template-columns: minmax(0,1fr) 250px; gap: clamp(1.5rem,4vw,3rem);
  align-items: center; }
@media (max-width: 820px) { .year-fig { grid-template-columns: 1fr; } }
.year-side { display: grid; gap: .3rem; align-content: start; }
.year-pick {
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .6rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--dim); margin: 0 0 .35rem;
}
/* These were bottom-ruled rows, which reads as a legend. They are controls,
   so they take the same bordered chip the map filters use. One vocabulary
   for "you can press this" across the page. */
.year-side button {
  display: grid; grid-template-columns: 10px 1fr auto; gap: .6rem; align-items: center;
  background: transparent; border: 1px solid var(--rule);
  padding: .45rem .6rem; cursor: pointer; text-align: left; color: var(--dim);
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .66rem; letter-spacing: .1em;
  text-transform: uppercase; transition: border-color .15s, color .15s, background .15s;
}
.year-side button i { width: 9px; height: 9px; background: var(--c); opacity: .35; }
.year-side button .sw { font-variant-numeric: tabular-nums; }
.year-side button:hover { color: var(--fg); border-color: var(--dim); }
.year-side button:hover i { opacity: .8; }
.year-side button[aria-pressed="true"] {
  color: var(--fg); border-color: var(--c); background: color-mix(in srgb, var(--c) 12%, transparent);
}
.year-side button[aria-pressed="true"] i { opacity: 1; }

/* The ring answers the pointer too: a month spoke lights and reports its
   index, so a reader who never touches the list still gets the numbers. */
.year .spoke { cursor: pointer; }
.year .spoke .hitwedge { fill: transparent; }
/* Belt and braces with the paint order: nothing drawn inside the ring should
   be able to intercept a pointer aimed at a month. */
.year polygon, .year .readout, .year .readout-v, .year .svg-num,
.year circle { pointer-events: none; }
.year .spoke:hover .rayline, .year .spoke.is-on .rayline { stroke: var(--fg); stroke-width: 1.5; }
.year .spoke:hover .mlabel, .year .spoke.is-on .mlabel { fill: var(--fg); }
.year .readout {
  fill: var(--dim); font-family: "Chivo Mono", ui-monospace, monospace;
  font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase;
}
.year .readout-v { font-family: Archivo, sans-serif; font-size: 22px; font-weight: 800;
  letter-spacing: -.02em; }

/* ── 7 · the ask ──────────────────────────────────────────────────── */
.big-final {
  font-family: Archivo, sans-serif; font-weight: 700; font-stretch: 104%;
  font-size: clamp(1.25rem, 2.6vw, 1.9rem); line-height: 1.2; letter-spacing: -.02em;
  max-width: 24ch; margin-top: 2.4rem; color: var(--fg);
}


/* ── footer ───────────────────────────────────────────────────────── */
footer { background: var(--bg); color: var(--fg); border-top: 1px solid var(--rule);
  padding: clamp(3rem,7vh,5rem) 0 clamp(2rem,5vh,3rem); }
footer p { font-size: .84rem; color: var(--dim); margin: 0 0 .7rem; max-width: 44ch; }
footer p:last-child { margin-bottom: 0; }
footer a { color: var(--lamp); }

/* ── method ───────────────────────────────────────────────────────── */
/* Set smaller than the story and folded shut. Anyone checking the work wants
   it complete; nobody reading the story wants it in the way. */

.colophon {
  max-width: none;
  margin-top: clamp(2rem,5vh,3rem); padding-top: 1.3rem;
  border-top: 1px solid var(--rule);
  display: flex; flex-wrap: wrap; gap: .5rem 1.6rem; align-items: baseline;
  font-family: "Chivo Mono", ui-monospace, monospace; font-size: .62rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--dim);
}

/* The plate hides about a third of the tiles, which contradicts the claim
   printed beside it that the proportion is checkable by counting. It lifts
   rather than being removed: the wall is the page's opening image. */
.wall-bare {
  position: absolute; right: 10px; bottom: 10px; z-index: 3;
  font: inherit; font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--dim); background: rgba(11,14,17,.86);
  border: 1px solid var(--rule); padding: .45rem .7rem; cursor: pointer;
  transition: color .12s var(--ease), border-color .12s var(--ease);
}
.wall-bare:hover, .wall-bare:focus-visible { color: var(--fg); border-color: var(--fg); }
.wall .plate { transition: opacity .35s var(--ease), transform .35s var(--ease); }
.wall.is-bare .plate {
  opacity: 0; pointer-events: none; transform: translateY(-50%) scale(.985);
}

/* The methodological note under a myth card: how the fleet count was
   arrived at. Set smaller than the claim it supports. */

/* ── the counterfactuals: choice, reasoning, and the number it costs ── */
