/* translink.css — TransLink patronage analyser.
   Dark theme; palette mirrors the main dantine.dev site and /frequencies
   (bg #0E0E10, text #EDEAE4, DM Sans/Serif, accent #7EB3E8). Self-contained:
   does not import the root stylesheet.

   This is an analysis tool, not a dashboard: the layout is a persistent filter
   rail beside a dense content column, so the controls that define what you are
   looking at never scroll away from the thing you are looking at. Wider than
   the other tools (1500px) because a route with ninety stops needs the room. */

:root {
  --bg:            #0E0E10;
  --surface:       #17171A;
  --surface-2:     #1E1E22;
  --surface-3:     #232327;
  --ink:           #EDEAE4;
  --muted:         #9A9896;
  --faint:         #5A5858;
  --line:          #252528;
  --line-strong:   #363639;
  --accent:        #7EB3E8;
  --accent-dark:   #5A96D4;
  --good:          #7ED39B;
  --warn:          #E8C87E;
  --bad:           #E8746A;
  /* A third evidentiary tier, distinct from --warn (derived from observed
     figures) and --good (observed): a modelled allocation, chiefly the rail
     service-family split, which is neither a straight aggregation nor a
     simple calculation on one. */
  --inferred:      #C79BEE;

  --font-display: 'DM Serif Display', Georgia, 'Times New Roman', serif;
  --font-sans:    'DM Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono:    ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 10px;
  --max-w: 1500px;
  --rail-w: 280px;
  --t-fast: 140ms ease;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* Light theme, picked explicitly via the header toggle (`data-theme="light"`
   on <html>) or followed automatically when nothing is stored and the OS
   prefers light. --accent/--good/--warn/--bad/--inferred are mid-tone enough
   to read on both backgrounds (see lib/chart.mjs's note on fadeLine) and stay
   unchanged; only the surfaces and text tones flip. */
:root[data-theme="light"] {
  --bg:          #FAFAF8;
  --surface:     #FFFFFF;
  --surface-2:   #F1F0EC;
  --surface-3:   #E7E5DF;
  --ink:         #1B1A18;
  --muted:       #5C5A56;
  --faint:       #8B8983;
  --line:        #DEDCD5;
  --line-strong: #C8C6BD;
  --accent-dark: #3E75B8;
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    --bg:          #FAFAF8;
    --surface:     #FFFFFF;
    --surface-2:   #F1F0EC;
    --surface-3:   #E7E5DF;
    --ink:         #1B1A18;
    --muted:       #5C5A56;
    --faint:       #8B8983;
    --line:        #DEDCD5;
    --line-strong: #C8C6BD;
    --accent-dark: #3E75B8;
  }
}

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

/* A class-level `display` beats the user-agent rule for [hidden], so the boot
   screen and the fatal panel would stay visible after being hidden. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

.container {
  width: 100%;
  max-width: var(--max-w);
  margin-inline: auto;
  padding-inline: 1rem;
}
@media (min-width: 640px) { .container { padding-inline: 1.5rem; } }

/* ---------- Accessibility ---------- */
.skip-link {
  position: absolute;
  left: 0; top: -3rem;
  background: var(--accent);
  color: #0A0A0C;
  padding: 0.5rem 1rem;
  border-radius: 0 0 var(--radius-sm) 0;
  font-weight: 600;
  z-index: 100;
  transition: top var(--t-fast);
}
.skip-link:focus { top: 0; }

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

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- Header ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.header-inner {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  height: 52px;
}
.wordmark {
  font-family: var(--font-display);
  font-size: 1.1rem;
  color: var(--ink);
  text-decoration: none;
}
.wordmark:hover { color: var(--accent); }
.header-tool {
  color: var(--muted);
  font-size: 0.85rem;
  border: 0;
  border-left: 1px solid var(--line-strong);
  border-radius: 0;
  background: none;
  padding: 0 0 0 0.75rem;
  font-family: inherit;
  cursor: pointer;
  text-align: left;
}
.header-tool:hover { color: var(--accent); }

.theme-toggle {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--muted);
  font-size: 0.8rem;
  font-family: inherit;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: none;
  padding: 0.3rem 0.55rem;
  cursor: pointer;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--faint); }
.theme-toggle svg { width: 15px; height: 15px; flex: none; }

/* ---------- Boot and fatal states ---------- */
.boot {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 60vh;
  padding: 2rem 1.25rem;
}
.boot-inner { max-width: 30rem; text-align: center; }
.boot-inner h1 {
  font-family: var(--font-display);
  font-size: 1.9rem;
  font-weight: 400;
  margin-bottom: 0.5rem;
}
.boot-status { color: var(--muted); font-size: 0.9rem; }
.boot-bar {
  margin-top: 1rem;
  height: 3px;
  background: var(--surface-2);
  border-radius: 2px;
  overflow: hidden;
}
.boot-bar span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width 240ms var(--ease-out);
}
.boot-error, .fatal {
  color: var(--bad);
  font-size: 0.9rem;
  margin-top: 1rem;
}
.fatal {
  max-width: 42rem;
  margin: 3rem auto;
  padding: 1.25rem;
  border: 1px solid var(--bad);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
}
.fatal h2 { font-size: 1.05rem; margin-bottom: 0.5rem; color: var(--bad); }
.fatal code { font-family: var(--font-mono); font-size: 0.85em; color: var(--muted); }

/* ---------- App layout ---------- */
.app {
  max-width: var(--max-w);
  margin-inline: auto;
  padding: 1rem;
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  align-items: start;
}
@media (min-width: 1000px) {
  .app {
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
    padding: 1.25rem 1.5rem 3rem;
    gap: 1.5rem;
  }
}

.rail { min-width: 0; }

/* The filter disclosure exists only below the two-column breakpoint. */
.rail-toggle {
  display: block;
  width: 100%;
  font: inherit;
  font-size: 0.85rem;
  text-align: left;
  padding: 0.45rem 0.65rem;
  margin-bottom: 0.6rem;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  cursor: pointer;
}
.rail-toggle::after { content: ' ▾'; color: var(--muted); }
.rail-toggle[aria-expanded='true']::after { content: ' ▴'; }
.rail-toggle:hover { background: var(--surface-2); }
.rail-collapsible { display: none; }
.rail.is-open .rail-collapsible { display: block; }

@media (min-width: 1000px) {
  .rail-toggle { display: none; }
  .rail-collapsible { display: block; }
}
@media (min-width: 1000px) {
  /*
   * The grid is start-aligned so content panels do not stretch. Stretch the
   * rail back to the full content row so its sticky child can remain pinned
   * while the main column continues below it.
   */
  .rail { align-self: stretch; }

  .rail-inner {
    position: sticky;
    top: 68px;
    max-height: calc(100vh - 84px);
    overflow-y: auto;
    padding-right: 0.25rem;
    scrollbar-width: thin;
  }
}

.content { min-width: 0; display: flex; flex-direction: column; gap: 1rem; }

/* ---------- Search ---------- */
.search { position: relative; margin-bottom: 1rem; }
.field-label {
  display: block;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin-bottom: 0.35rem;
}
.search-input {
  width: 100%;
  padding: 0.5rem 0.65rem;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: inherit;
  font-size: 0.9rem;
}
.search-input::placeholder { color: var(--faint); }
.search-input:focus { border-color: var(--accent); outline: none; }

.search-results {
  position: absolute;
  z-index: 30;
  top: calc(100% + 4px);
  left: 0; right: 0;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.5);
  list-style: none;
}
.search-result {
  padding: 0.45rem 0.6rem;
  cursor: pointer;
  border-bottom: 1px solid var(--line);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.5rem;
  align-items: baseline;
}
.search-result:last-child { border-bottom: none; }
.search-result:hover, .search-result.is-active { background: var(--surface-3); }
.search-kind {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bg);
  background: var(--faint);
  border-radius: 2px;
  padding: 0.05rem 0.3rem;
}
.search-kind.kind-route { background: var(--accent); }
.search-kind.kind-facility { background: var(--good); }
.search-kind.kind-corridor { background: var(--warn); }
/* A service family is an analytical identity, not a route the source names, so
   it is badged apart from the routes it sits beside in the results. */
.search-kind.kind-family { background: var(--accent-dark); }
.search-label {
  font-size: 0.88rem;
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.search-context {
  display: block;
  font-size: 0.78rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- Filters ---------- */
.filters { display: flex; flex-direction: column; gap: 0.9rem; }
.filter-group { border-top: 1px solid var(--line); padding-top: 0.75rem; }
.filter-group:first-child { border-top: none; padding-top: 0; }

.chips { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.chip, .seg {
  font: inherit;
  font-size: 0.8rem;
  padding: 0.32rem 0.6rem;
  min-height: 2rem;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  color: var(--muted);
  cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}
.chip:hover, .seg:hover { background: var(--surface-2); color: var(--ink); }
.chip.is-active {
  background: rgba(126, 179, 232, 0.15);
  border-color: var(--accent);
  color: var(--accent);
}
/* Disabled means "this filter cannot reach this view" — still worth reading
   on a phone, so it dims rather than fading to near-invisibility. */
.chip[disabled], .seg[disabled] { opacity: 0.55; cursor: not-allowed; color: var(--faint); }

.segmented {
  display: inline-flex;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface);
}
.segmented .seg {
  border: none;
  border-radius: 0;
  border-right: 1px solid var(--line-strong);
  padding: 0.35rem 0.65rem;
  min-height: 2.1rem;
}
.segmented .seg:last-child { border-right: none; }
.segmented .seg.is-active { background: var(--accent); color: #0A0A0C; font-weight: 500; }

select.control, input.control {
  width: 100%;
  padding: 0.4rem 0.5rem;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: inherit;
  font-size: 0.85rem;
}
select.control:focus, input.control:focus { border-color: var(--accent); outline: none; }

.field-hint { font-size: 0.8rem; color: var(--muted); margin-top: 0.3rem; line-height: 1.5; }

/* ---------- Selection list ---------- */
.selection { margin-top: 1rem; border-top: 1px solid var(--line); padding-top: 0.75rem; }
.selection:empty { display: none; }
.selected-list { list-style: none; display: flex; flex-direction: column; gap: 0.25rem; }
.selected-item {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.28rem 0.4rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 0.82rem;
}
.selected-swatch { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.selected-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.selected-remove {
  font: inherit;
  background: none;
  border: none;
  color: var(--faint);
  cursor: pointer;
  line-height: 1;
  padding: 0 0.15rem;
}
.selected-remove:hover { color: var(--bad); }

/* ---------- View tabs ---------- */
.views { border-bottom: 1px solid var(--line); }
/* Six tabs do not fit a phone's width on one row. Wrapping keeps every view
   visible and reachable without a hidden scroll region or a hamburger menu —
   a horizontally-scrolling row with no scrollbar was how the later tabs went
   unnoticed. Desktop has the room, so it stays a single row there. */
.view-tabs { display: flex; flex-wrap: wrap; gap: 0.15rem; list-style: none; }
@media (min-width: 640px) {
  .view-tabs { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .view-tabs::-webkit-scrollbar { display: none; }
}
.view-tab {
  font: inherit;
  font-size: 0.88rem;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  padding: 0.6rem 0.7rem;
  min-height: 2.5rem;
  cursor: pointer;
  white-space: nowrap;
}
.view-tab:hover { color: var(--ink); }
.view-tab.is-active { color: var(--accent); border-bottom-color: var(--accent); }

/* ---------- Panels ---------- */
.view { display: flex; flex-direction: column; gap: 1rem; }

/* A one-sentence orientation at the top of a view, and the section headings
   that break Overview's panels into conceptual groups. Both are deliberately
   restrained — a border-bottom rule and a little top margin, not a banner
   or a card — so the page gains structure without gaining visual weight. */
.view-intro {
  font-size: 0.95rem;
  color: var(--muted);
  max-width: 62rem;
  line-height: 1.6;
}
.view-section {
  font-family: var(--font-sans);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--faint);
  margin-top: 0.5rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--line);
}
.view-section:first-child { margin-top: 0; }

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.panel-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.panel-title { font-size: 0.95rem; font-weight: 600; }
.subheading {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.panel-subtitle { font-size: 0.85rem; color: var(--muted); line-height: 1.5; }

/* A compact row naming exactly which filters reach this panel's figures — the
   month, and only the dimensions the underlying cube actually carries. Sits
   directly under the subtitle so a reader can tell at a glance what changed
   the numbers below without re-reading a paragraph of caveats. Muted rather
   than accent-coloured: it is context, not a call to action. */
.panel-context {
  font-size: 0.78rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  margin-top: 0.15rem;
}
.panel-context .is-inactive { color: var(--faint); }
.panel-actions { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; }
/* A labelled control sitting among a panel's actions — the station's stop
   picker. The label sits above the control as it does in the filter rail, so
   the two read the same way, but it must not push the row taller than the
   chips beside it. */
.panel-actions .field { display: flex; flex-direction: column; gap: 0.15rem; }
.panel-actions .field .field-label { margin-bottom: 0; }
.panel-actions .field .control { max-width: 16rem; }
.panel-body { padding: 0.9rem; }
.panel-body > * + * { margin-top: 0.8rem; }

.panel-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}
@media (min-width: 900px) {
  .panel-grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---------- Stats ---------- */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 0.75rem;
}
.stat {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.7rem;
}
.stat-label {
  display: block;
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
}
.stat-value {
  display: block;
  font-size: 1.32rem;
  font-weight: 500;
  line-height: 1.25;
  font-variant-numeric: tabular-nums;
  margin-top: 0.15rem;
}
.stat-value.is-up { color: var(--good); }
.stat-value.is-down { color: var(--bad); }
.stat-sub { display: block; font-size: 0.74rem; color: var(--muted); }

/* ---------- Badges and caveats ---------- */
.badge {
  display: inline-block;
  font-size: 0.63rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.06rem 0.32rem;
  border-radius: 2px;
  vertical-align: middle;
  cursor: help;
}
.badge-derived {
  background: rgba(232, 200, 126, 0.16);
  color: var(--warn);
  border: 1px solid rgba(232, 200, 126, 0.35);
}
.badge-observed {
  background: rgba(126, 211, 155, 0.14);
  color: var(--good);
  border: 1px solid rgba(126, 211, 155, 0.3);
}
/* Modelled allocation, not a straight aggregation or calculation — kept
   visually apart from --warn (derived) so the rail service-family split reads
   as its own, weaker, evidentiary tier rather than as one more "derived"
   figure among load profiles and corridor segments. */
.badge-inferred {
  background: rgba(199, 155, 238, 0.16);
  color: var(--inferred);
  border: 1px solid rgba(199, 155, 238, 0.4);
}

.fact-note {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--muted);
}

.fact-note + .fact-note { margin-top: 0.4rem; }

.caveat {
  font-size: 0.82rem;
  line-height: 1.55;
  color: var(--muted);
  border-left: 2px solid var(--line-strong);
  padding-left: 0.6rem;
}
.caveat strong { color: var(--warn); font-weight: 500; }

.warning {
  display: block;
  font-size: 0.82rem;
  color: var(--ink);
  background: rgba(232, 200, 126, 0.09);
  border: 1px solid rgba(232, 200, 126, 0.4);
  border-radius: var(--radius-sm);
  padding: 0.55rem 0.7rem;
}
.warning-heading {
  display: block;
  color: var(--warn);
  font-weight: 600;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 0.15rem;
}

/* The rail view's modelling-basis banner: same shape as .warning, but in the
   inferred tier's colour, because this is not a data-quality caution — it is
   the view's basic evidentiary status, true of every panel on it. */
.warning.is-inferred {
  background: rgba(199, 155, 238, 0.08);
  border-color: rgba(199, 155, 238, 0.4);
}
.warning.is-inferred .warning-heading { color: var(--inferred); }

.note-inline { font-size: 0.78rem; color: var(--muted); }

/* A line's journey total, under the families that make it up. It is not the
   sum of the column above it — see `netLineTotal` — so it is set apart from
   the rows rather than styled as one more of them. */
.line-total {
  font-size: 0.82rem;
  color: var(--muted);
  margin: 0.4rem 0 1.1rem;
  padding-left: 0.6rem;
  border-left: 2px solid var(--border);
}
.line-total strong { color: var(--text); font-weight: 600; }

/* ---------- Charts ---------- */
.chart-holder { width: 100%; overflow-x: auto; }
.chart { display: block; }
.chart-axis-label { fill: var(--muted); font-size: 11px; font-family: var(--font-sans); }
.chart-axis-title { fill: var(--muted); font-size: 11.5px; font-family: var(--font-sans); }
.chart-stop-label { fill: var(--muted); font-size: 11px; font-family: var(--font-sans); }
.chart-hit { cursor: crosshair; }
.chart-hit:hover { fill: rgba(126, 179, 232, 0.08); }

.legend { display: flex; flex-wrap: wrap; gap: 0.5rem 0.9rem; font-size: 0.78rem; }
.legend-item { display: inline-flex; align-items: center; gap: 0.3rem; color: var(--muted); }
.legend-swatch { width: 12px; height: 3px; border-radius: 2px; flex: none; }
.legend-swatch.is-box { height: 10px; width: 10px; }
/* The right-hand-axis series is drawn dotted, so its key is too. */
.legend-swatch.is-dotted {
  background: none !important;
  height: 0;
  border-top: 2px dotted currentColor;
  opacity: 0.8;
}

/* --- timeline annotations --------------------------------------------------
   Curated notes about what happened to the network, drawn behind the data so a
   marker never obscures the line it explains. Kinds are styled apart because
   they mean different things: a `break` is a change in what the source counts,
   everything else is a change in the world the source observed. */

.chart-annotation-rule {
  stroke: var(--line-strong);
  stroke-width: 1;
  stroke-dasharray: 3 3;
}

.chart-annotation-band {
  fill: var(--ink);
  opacity: 0.035;
}

.chart-annotation-marker {
  fill: var(--muted);
  stroke: var(--surface);
  stroke-width: 2;
  cursor: help;
}

.chart-annotation.is-break .chart-annotation-rule { stroke: var(--warn); stroke-dasharray: none; }
.chart-annotation.is-break .chart-annotation-marker { fill: var(--warn); }
.chart-annotation.is-fare .chart-annotation-marker { fill: var(--good); }
.chart-annotation.is-disruption .chart-annotation-marker { fill: var(--bad); }

.event-legend {
  margin-top: 0.85rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--line);
}

.event-legend-title {
  margin: 0 0 0.5rem;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
}

.event-legend-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 0.5rem; }

.event-item {
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--muted);
  padding-left: 0.7rem;
  border-left: 2px solid var(--line-strong);
}

.event-item.is-break { border-left-color: var(--warn); }
.event-item.is-fare { border-left-color: var(--good); }
.event-item.is-disruption { border-left-color: var(--bad); }

.event-month {
  display: inline-block;
  min-width: 6.5em;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

.event-label { color: var(--ink); font-weight: 500; margin-right: 0.4rem; }

@media (max-width: 640px) {
  .event-month { display: block; min-width: 0; }
}

.chart-readout {
  font-size: 0.8rem;
  color: var(--muted);
  min-height: 1.4em;
  font-variant-numeric: tabular-nums;
}
.chart-readout strong { color: var(--ink); font-weight: 500; }

/* ---------- Rankings ---------- */
.rank-list { list-style: none; display: flex; flex-direction: column; gap: 1px; }
.rank-item {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.5rem;
  align-items: baseline;
  padding: 0.28rem 0.45rem;
  font-size: 0.83rem;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.rank-item:hover { background: var(--surface-2); }
.rank-bar {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  border-radius: var(--radius-sm);
  opacity: 0.16;
  pointer-events: none;
}
.rank-label { position: relative; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-sublabel { color: var(--faint); font-size: 0.76rem; }
.rank-value { position: relative; font-variant-numeric: tabular-nums; color: var(--muted); }

/* A ranking carrying a second figure — growth beside a total — gets a third
   column. The width is fixed so the percentages line up down the page rather
   than shuffling with the longest one. */
.rank-list.has-trailing .rank-item { grid-template-columns: 1fr auto 4.6rem; }
.rank-trailing {
  position: relative;
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem;
  text-align: right;
  color: var(--faint);
}
.rank-trailing.is-up { color: var(--good); }
.rank-trailing.is-down { color: var(--bad); }
.rank-trailing-head {
  text-align: right;
  font-size: 0.72rem;
  color: var(--faint);
  padding: 0 0.45rem 0.2rem;
}

/* ---------- Tables ---------- */
.table-wrap { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; font-size: 0.83rem; }
.data-table caption { text-align: left; color: var(--muted); font-size: 0.78rem; padding-bottom: 0.4rem; }
.data-table th, .data-table td {
  text-align: left;
  padding: 0.34rem 0.5rem;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
@media (max-width: 640px) {
  .data-table tr.is-clickable td { padding-block: 0.55rem; }
  .search-result { padding: 0.6rem 0.65rem; }
  .rank-item { padding-block: 0.4rem; }
}
.data-table th {
  color: var(--faint);
  font-weight: 500;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  position: sticky;
  top: 0;
  background: var(--surface);
}
.data-table td { font-variant-numeric: tabular-nums; }
.data-table td:first-child { white-space: normal; min-width: 12rem; }
.data-table tr.is-clickable { cursor: pointer; }
.data-table tr.is-clickable:hover { background: var(--surface-2); }
.align-right { text-align: right !important; }

/* A sortable column header. The button carries the label so the whole thing is
   reachable by keyboard and announces itself; the arrow is drawn only on the
   column actually in force, because four arrows say nothing about which one
   the table is ordered by. */
.data-table th button.sort {
  font: inherit;
  color: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}
.data-table th button.sort:hover { color: var(--ink); }
.data-table th button.sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.data-table th.align-right button.sort { flex-direction: row-reverse; }
.data-table th.is-sorted { color: var(--ink); }
.data-table th.is-sorted button.sort::after { content: '\2193'; font-size: 0.9em; }
.data-table th.is-ascending button.sort::after { content: '\2191'; }

/* ---------- Map ---------- */
.map-holder {
  height: 380px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--surface-2);
}
@media (min-width: 900px) { .map-holder { height: 460px; } }
.maplibregl-popup-content {
  background: var(--surface-2);
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  font-family: var(--font-sans);
  font-size: 0.82rem;
  padding: 0.5rem 0.6rem;
}
.maplibregl-popup-tip { border-top-color: var(--surface-2) !important; border-bottom-color: var(--surface-2) !important; }
.maplibregl-ctrl-group { background: var(--surface-2); }
.maplibregl-ctrl-group button + button { border-top-color: var(--line); }
.map-popup-title { font-weight: 600; display: block; }
.map-popup-line { color: var(--muted); display: block; font-size: 0.78rem; }

/* ---------- Empty and loading ---------- */
.empty {
  padding: 1.75rem 1rem;
  text-align: center;
  color: var(--muted);
}
.empty-title { font-size: 0.92rem; color: var(--ink); }
.empty-detail { font-size: 0.8rem; margin-top: 0.3rem; max-width: 34rem; margin-inline: auto; }

.loading { color: var(--faint); font-size: 0.82rem; padding: 0.5rem 0; }

/* ---------- Footer ---------- */
.site-footer {
  border-top: 1px solid var(--line);
  margin-top: 2rem;
  padding: 1.25rem 0 2rem;
}
.footer-inner { display: flex; flex-direction: column; gap: 0.4rem; }
.footer-provenance, .footer-links { font-size: 0.76rem; color: var(--faint); }
.footer-links a { color: var(--muted); text-decoration: none; }
.footer-links a:hover { color: var(--accent); text-decoration: underline; }

/* ---------- Print ---------- */
@media print {
  .rail, .views, .site-header, .site-footer { display: none; }
  .app { grid-template-columns: 1fr; }
  .panel { break-inside: avoid; border-color: #999; }
  body { background: #fff; color: #000; }
}

/* ---------- Donut ---------- */
.donut-row {
  display: flex;
  gap: 1.2rem;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 0.9rem;
}
.donut-holder { flex: none; }
.donut-legend {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.8rem;
  color: var(--muted);
  min-width: 0;
}
.chart-donut .chart-hit { cursor: default; }
.chart-slice-label {
  font-size: 0.66rem;
  fill: var(--bg);
  font-weight: 600;
}
.chart-donut-value {
  font-size: 0.95rem;
  fill: var(--ink);
  font-variant-numeric: tabular-nums;
}
.chart-donut-label {
  font-size: 0.66rem;
  fill: var(--faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* --- rail: the family-pair flow matrix ----------------------------------- */
/* A heat-shaded cell rather than a chart primitive: twelve families by twelve
   is a grid of labelled figures, and every other matrix on this site (the
   route and stop band matrices) is a data table for the same reason. --heat
   runs 0..1 and only the alpha moves, so the figure stays readable at every
   intensity. The site is dark-only, so the ramp is the accent at low alpha. */
.matrix-heat {
  display: block;
  padding: 0.15rem 0.35rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) calc(var(--heat, 0) * 45%), transparent);
  font-variant-numeric: tabular-nums;
}

.matrix-blank {
  color: var(--muted, #6b7280);
}

/* The matrix is thirteen columns wide and is the one table here allowed to
   scroll sideways, so the page body never does. */
.table-scroll {
  overflow-x: auto;
}

.subhead {
  margin: 1.1rem 0 0.35rem;
  font-size: 0.95rem;
  font-weight: 600;
}

/* The T number a rail service family carries today, in the network's own
   colour. The colour is set inline from `LINE_COLORS`, because it belongs to
   the line rather than to the theme; everything that makes it legible against
   either background lives here. `color-mix` keeps the fill and border derived
   from that one value, so a line recoloured in the palette needs no CSS. */
.line-chip {
  display: inline-block;
  min-width: 1.85rem;
  text-align: center;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.04rem 0.3rem;
  border-radius: 3px;
  vertical-align: middle;
  color: var(--line-color, var(--ink));
  background: color-mix(in srgb, var(--line-color, transparent) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--line-color, transparent) 45%, transparent);
}
