/* Self-hosted rather than fetched from a font CDN: one fewer third party
   watching who reads this, and the files are latin-subset only, so all four
   together weigh less than a photograph. `swap` because a train that is
   invisible until a font arrives is worse than one in the fallback face. */
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("fonts/IBMPlexSans-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("fonts/IBMPlexSans-600.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("fonts/IBMPlexMono-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("fonts/IBMPlexMono-500.woff2") format("woff2");
}

/* via-pulse: clean and functional.
 *
 * One colour system for lateness, shared by the map layers and every badge, so
 * a train that reads amber on the map reads amber in the list. The accent is
 * VIA's own yellow. */

/* Two schemes with different characters, not one scheme at two brightnesses:
   warm paper against midnight blue. The attribute wins over the media query, so
   an explicit choice survives the system changing under it. */
:root {
  --bg: #f4f1ea;
  --surface: #fffefb;
  --surface-2: #e9e3d6;
  --border: #d8d0be;
  --text: #17130c;
  --text-dim: #6d6455;
  --accent: #ffcb06;
  --good: #17754a;
  --med: #9a6a00;
  --bad: #b53a2f;
  --good-bg: #dff0e5;
  --med-bg: #fbeeca;
  --bad-bg: #fae2de;
  /* The map's own colours, so the basemap belongs to the scheme too. */
  --map-style: "https://tiles.openfreemap.org/styles/positron";
  /* Squarer than the 8px every dashboard uses. A timetable is printed matter,
     not a card. */
  --radius: 4px;
  --header-h: 52px;
  /* Plex was drawn for engineering documentation and it shows: it is plainly
     not the system stack, its figures are unmistakable at a glance, and the
     sans and the mono are the same design, which matters on a page where a
     station name sits beside its clock. */
  --font: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #05091a;
    --surface: #0d1630;
    --surface-2: #182448;
    --border: #33447a;
    --text: #eef2ff;
    --text-dim: #9aabd8;
    --accent: #ffcb06;
    --good: #3ee08a;
    --med: #ffc73d;
    --bad: #ff7a7a;
    --good-bg: #06301f;
    --med-bg: #3a2c07;
    --bad-bg: #3f1620;
  }
}

:root[data-theme="dark"] {
  --bg: #05091a;
  --surface: #0d1630;
  --surface-2: #182448;
  --border: #33447a;
  --text: #eef2ff;
  --text-dim: #9aabd8;
  --accent: #ffcb06;
  --good: #3ee08a;
  --med: #ffc73d;
  --bad: #ff7a7a;
  --good-bg: #06301f;
  --med-bg: #3a2c07;
  --bad-bg: #3f1620;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

body { display: flex; flex-direction: column; }
main { flex: 1; min-height: 0; display: flex; flex-direction: column; position: relative; }

/* Reference pages cover the map rather than replacing it, so the map survives
   the navigation and is still where the viewer left it on the way back. */
.overlay {
  position: absolute; inset: 0; z-index: 5;
  background: var(--bg); overflow-y: auto;
  animation: fade-in 140ms ease-out;
}
a { color: inherit; text-decoration: none; }

/* ── header ─────────────────────────────────────────────────────────── */

/* Scoped to a class, not the bare element: a detail view also renders a
   <header>, and inheriting the top bar's fixed height clipped its contents. */
.topbar {
  display: flex;
  align-items: center;
  gap: 18px;
  height: var(--header-h);
  padding: 0 14px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  flex: 0 0 auto;
  position: relative;
  z-index: 20;
}

.brand { display: flex; align-items: center; gap: 8px; font-weight: 650; letter-spacing: -0.01em; white-space: nowrap; flex: 0 0 auto; }
.brand .mark { width: 20px; height: 20px; border-radius: 5px; display: block; }

.topbar nav { display: flex; gap: 4px; }
.topbar nav a {
  padding: 5px 11px;
  border-radius: 999px;
  color: var(--text-dim);
  font-size: 14px;
  font-weight: 500;
}
.topbar nav a:hover { background: var(--surface-2); color: var(--text); }
.topbar nav a.active { background: var(--surface-2); color: var(--text); }

.header-end { margin-left: auto; display: flex; align-items: center; gap: 10px; }

.feed { font-size: 12.5px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.feed.bad { color: var(--bad); font-weight: 550; }

.search-button {
  display: flex; align-items: center; gap: 7px;
  padding: 5px 9px 5px 8px;
  border: 1px solid var(--border); border-radius: 7px;
  background: var(--bg); color: var(--text-dim);
  font: 13px var(--font); cursor: pointer;
}
.search-button:hover { color: var(--text); border-color: var(--text-dim); }
.search-button .glass {
  width: 13px; height: 13px; flex: 0 0 auto;
  border: 1.6px solid currentColor; border-radius: 50%;
  position: relative;
}
.search-button .glass::after {
  content: ""; position: absolute; right: -4px; bottom: -3px;
  width: 5px; height: 1.6px; background: currentColor; transform: rotate(45deg);
}
.search-button kbd {
  font: 11px var(--mono); padding: 1px 4px; border-radius: 4px;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim);
}

.palette-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: rgb(0 0 0 / 0.42);
  display: flex; justify-content: center; align-items: flex-start;
  padding: 12vh 16px 16px;
  animation: fade-in 120ms ease-out;
}
.palette {
  width: 100%; max-width: 540px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; box-shadow: 0 18px 50px rgb(0 0 0 / 0.3);
  overflow: hidden; display: flex; flex-direction: column;
  max-height: 70vh;
  animation: rise 140ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
@keyframes fade-in { from { opacity: 0; } }
@keyframes rise { from { opacity: 0; transform: translateY(-8px); } }

.palette-input {
  border: 0; border-bottom: 1px solid var(--border);
  padding: 15px 16px; font: 16px var(--font);
  background: none; color: var(--text); outline: none;
}
.palette-results { overflow-y: auto; padding: 6px 0; }
.palette-group {
  margin: 8px 0 3px; padding: 0 16px;
  font-size: 10.5px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--text-dim);
}
.palette-hit {
  display: flex; align-items: baseline; gap: 10px; width: 100%;
  padding: 8px 16px; border: 0; background: none; cursor: pointer;
  font: inherit; color: var(--text); text-align: left;
}
.palette-hit.on { background: var(--surface-2); }
.palette-hit .kind { font-weight: 600; }
.palette-hit .kind.train {
  font-family: var(--mono); font-size: 13px;
  background: var(--surface-2); padding: 2px 6px; border-radius: 5px; min-width: 34px;
  text-align: center;
}
.palette-hit.on .kind.train { background: var(--bg); }
.palette-hit .detail { color: var(--text-dim); font-size: 13px; }
.palette-hint {
  margin: 0; padding: 8px 16px; border-top: 1px solid var(--border);
  font-size: 11.5px; color: var(--text-dim); background: var(--bg);
}

.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 26px; padding: 0;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text-dim);
  cursor: pointer; font-size: 13px; line-height: 1;
}
.theme-toggle:hover { color: var(--text); border-color: var(--text-dim); }

/* Freshness as a colour rather than a sentence: it is the same words on every
   screen and only matters when it goes wrong. The wording is on hover, and the
   number beside it is how many browsers are connected. */
.feed { position: relative; display: inline-flex; }
.feed-trigger {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 6px; margin: -4px -6px; border-radius: var(--radius);
  cursor: default; outline: none;
}
.feed:hover .feed-trigger, .feed:focus-within .feed-trigger { background: var(--surface-2); }
.pip {
  width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto;
  background: var(--text-dim);
}
.pip.good { background: var(--good); box-shadow: 0 0 0 3px color-mix(in srgb, var(--good) 22%, transparent); }
.pip.stale { background: var(--med); box-shadow: 0 0 0 3px color-mix(in srgb, var(--med) 22%, transparent); }
.pip.bad { background: var(--bad); box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad) 22%, transparent); }
.watching { font: 600 12px var(--mono); color: var(--text-dim); font-variant-numeric: tabular-nums; }
/* Present for a screen reader, absent to everyone else. */
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* Attribution for VIA's own data, on hover or focus. Focus is what makes this
   reachable by keyboard and usable on a phone, where a tap focuses the button
   and there is no hover to speak of. */
.about { position: relative; display: inline-flex; }
.about-button {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface); color: var(--text-dim);
  cursor: help; font: 600 13px var(--font); line-height: 1;
}
.about-button:hover, .about:focus-within .about-button {
  color: var(--text); border-color: var(--text-dim);
}
.feed-card, .about-card {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  width: max-content; max-width: min(320px, calc(100vw - 24px));
  display: flex; flex-direction: column; gap: 7px;
  padding: 11px 13px;
  background: var(--surface); color: var(--text-dim);
  border: 1px solid var(--border); border-radius: 9px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.22);
  font-size: 12px; line-height: 1.5;
  /* Hidden without `display: none`, so it can fade rather than snap. */
  opacity: 0; visibility: hidden; transform: translateY(-3px);
  transition: opacity 120ms ease, transform 120ms ease, visibility 120ms;
}
.about-card strong { color: var(--text); font-size: 12.5px; font-weight: 650; }
.about:hover .about-card, .about:focus-within .about-card,
.feed:hover .feed-card, .feed:focus-within .feed-card {
  opacity: 1; visibility: visible; transform: translateY(0);
}
.feed-card { width: max-content; max-width: min(260px, calc(100vw - 24px)); }
.feed-line { display: flex; align-items: center; gap: 7px; color: var(--text); font-weight: 600; }
.feed-dim { color: var(--text-dim); }

.about-feedback {
  align-self: flex-start; margin-top: 2px;
  padding: 0; border: 0; background: none;
  color: var(--text); font: 600 12px var(--font);
  text-decoration: underline; text-underline-offset: 2px;
  cursor: pointer;
}
.about-feedback:hover { color: var(--accent-ink, var(--text)); }

/* A small centred panel. Its own element rather than the map's overlay, since
   it sits above every view including the reference pages. */
.sheet-backdrop {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: rgba(0, 0, 0, 0.42);
  animation: fade-in 120ms ease-out;
}
.sheet {
  width: 100%; max-width: 420px;
  display: flex; flex-direction: column; gap: 10px;
  padding: 18px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.32);
  animation: rise 140ms ease-out;
}
.sheet h2 { margin: 0; font-size: 15px; font-weight: 650; }
.sheet-hint, .sheet-done { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--text-dim); }
.sheet-done { color: var(--text); }
.sheet-input {
  width: 100%; box-sizing: border-box; resize: vertical;
  padding: 9px 10px;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px;
  font: 13px var(--font); line-height: 1.45;
}
.sheet-input:focus { outline: none; border-color: var(--text-dim); }
.sheet-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sheet-note { flex: 1; min-width: 0; font-size: 11px; color: var(--text-dim); }
.sheet-cancel, .sheet-send {
  padding: 6px 12px; border-radius: 7px; cursor: pointer;
  font: 600 12.5px var(--font);
  border: 1px solid var(--border); background: var(--surface); color: var(--text-dim);
}
.sheet-cancel:hover { color: var(--text); border-color: var(--text-dim); }
.sheet-send { background: var(--accent); border-color: var(--accent); color: #17130c; }
.sheet-send:disabled { opacity: 0.45; cursor: default; }

.lang {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-dim);
  border-radius: 6px;
  padding: 3px 8px;
  font: 600 12px var(--font);
  cursor: pointer;
}
.lang:hover { color: var(--text); border-color: var(--text-dim); }

.banner {
  position: absolute; top: var(--header-h); left: 0; right: 0;
  margin: 0; padding: 7px 14px;
  font-size: 13px;
}
.banner.error { background: var(--bad-bg); color: var(--bad); }

/* ── network view ───────────────────────────────────────────────────── */

.network { flex: 1; min-height: 0; display: grid; grid-template-columns: 1fr 372px; position: relative; }

/* Desktop shows both halves at once, so the switch has nothing to do. */
.pane-toggle { display: none; }

/* Says which train the camera is holding, over the map rather than in the
   panel, because the map is the thing behaving unusually. */
.following-badge {
  position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
  z-index: 6; pointer-events: none;
  padding: 5px 12px; border-radius: 999px;
  background: var(--accent); color: #17130c;
  font: 600 12px var(--font); letter-spacing: 0.01em;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
}

.map { width: 100%; height: 100%; background: var(--surface-2); }
.maplibregl-ctrl-attrib { font-size: 10px; }

.panel {
  border-left: 1px solid var(--border);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* Whatever the panel is showing, the list, a train, a station, fills it and
   scrolls on its own. The slide is small on purpose: it should read as the
   panel changing subject, not as a page transition. */
.panel-body {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
  animation: panel-in 190ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
@keyframes panel-in { from { opacity: 0; transform: translateX(10px); } }

/* This is the actual scroll container, so it is the one whose top padding would
   push the sticky `.back` down and let content scroll through the gap above it.
   Zero here for the same reason as on `.detail`. */
.panel .detail {
  max-width: none; width: 100%;
  padding: 0 14px 40px;
  flex: 1; min-height: 0; overflow-y: auto;
}
.panel .detail-head h1 { font-size: 17px; }
.panel .detail-head { flex-direction: column; gap: 6px; }
.panel .detail-head .stats { margin-left: 0; }
.panel .chart { height: 124px; }
.panel .next-up { font-size: 13px; padding: 8px 10px; }

.panel-head { padding: 12px 14px 8px; border-bottom: 1px solid var(--border); }

.search {
  width: 100%;
  padding: 8px 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: 14px var(--font);
}
.search:focus { outline: 2px solid color-mix(in srgb, var(--accent) 60%, transparent); outline-offset: -1px; }

.count { margin: 7px 2px 0; font-size: 12.5px; color: var(--text-dim); }

.shortcuts { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 14px; border-bottom: 1px solid var(--border); }

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit; color: inherit;
  padding: 4px 9px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg);
  font-size: 12.5px;
  cursor: pointer;
}
/* Only the interactive ones react to a pointer. A chip that merely names an
   affected train is not a thing to click, so it should not look like one. */
.chip:not(.small):hover { border-color: var(--text-dim); }
.chip.small { cursor: default; }
.chip-count { color: var(--text-dim); font-variant-numeric: tabular-nums; font-size: 11px; }
.chip.small { padding: 2px 8px; font: 600 12px var(--mono); }

.list { overflow-y: auto; flex: 1; }

.train-row {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 14px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}
.train-row:hover { background: var(--surface-2); }

.num {
  min-width: 34px;
  padding: 2px 6px;
  border-radius: 5px;
  background: var(--surface-2);
  font: 600 13px var(--mono);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.num.big { font-size: 17px; padding: 3px 9px; }

.od { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.places { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sub { font-size: 12px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.delay {
  flex: 0 0 auto;
  padding: 2px 7px;
  border-radius: 5px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  /* Signed numbers in a column want to line up on the digits, and the mono face
     is the same design as the rest, so this reads as emphasis rather than as a
     different typeface. */
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
.delay.good { background: var(--good-bg); color: var(--good); }
.delay.med  { background: var(--med-bg);  color: var(--med); }
.delay.bad  { background: var(--bad-bg);  color: var(--bad); }

.empty { padding: 26px 16px; color: var(--text-dim); font-size: 14px; text-align: center; }

/* ── detail views ───────────────────────────────────────────────────── */

/* No top padding: `.back` below is sticky, and `top: 0` sticks it to this
   element's padding box: any top padding here becomes a gap the content
   scrolls through above the button, which reads as the bar floating loose
   rather than being pinned to the top. The spacing lives on `.back` instead. */
.detail { max-width: 780px; width: 100%; margin: 0 auto; padding: 0 14px 48px; }
.back {
  position: sticky; top: 0; z-index: 3;
  display: block; margin: 0 -14px 13px; padding: 13px 14px 11px;
  background: var(--surface); border-bottom: 1px solid var(--border);
  color: var(--text-dim); font-size: 13.5px; font-weight: 550;
}
.back:hover { color: var(--text); }

.detail-head { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 14px; }
.detail-head h1 { margin: 0; font-size: 20px; font-weight: 650; display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.detail-head .sub { margin: 3px 0 0; }
.detail-head .stats { margin-left: auto; display: flex; align-items: center; gap: 10px; }

/* A finished run must never be mistaken for a running one. The header carries a
   left rule and a labelled date rather than relying on the reader noticing that
   the speed and progress bar are missing. */
.detail-head.archived {
  border-left: 3px solid var(--text-dim);
  padding-left: 11px;
}
.detail-head.archived .sub {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  white-space: normal;
}
.run-tag {
  font: 600 10px/1 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bg);
  background: var(--text-dim);
  border-radius: 3px;
  padding: 3px 6px;
}
.detail-head.archived .ago { color: var(--text-dim); font-size: 11.5px; }

.archive-note {
  margin: 0 0 14px;
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.45;
}

/* Dates in the run list read as data, not prose. */
.when { font: 500 12px var(--mono); font-variant-numeric: tabular-nums; }
.stat { font: 600 17px var(--mono); font-variant-numeric: tabular-nums; }
.stat small { font-size: 11px; color: var(--text-dim); font-weight: 400; }
.access { color: var(--good); }

.follow {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--text-dim); font: 600 12px var(--font); cursor: pointer;
  white-space: nowrap;
}
.follow:hover { color: var(--text); border-color: var(--text-dim); }
.follow .pip {
  width: 7px; height: 7px; border-radius: 50%;
  border: 1.6px solid currentColor;
}
.follow.on { background: var(--accent); border-color: var(--accent); color: #1a1400; }
.follow.on .pip { background: currentColor; border-color: currentColor; animation: pulse 1.8s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.35; } }

.journey { height: 5px; border-radius: 999px; background: var(--surface-2); overflow: hidden; margin-bottom: 18px; }
.journey-fill { height: 100%; background: var(--accent); }

/* Next stop, stated once at the top rather than left to be found in the table. */
.next-up {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin: 0 0 18px; padding: 9px 12px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  font-size: 14px;
}
.next-up .label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim); }
.next-up strong { font-weight: 600; }
.next-up .eta { color: var(--accent-text, var(--text)); font-variant-numeric: tabular-nums; font-weight: 600; }
.next-up .dist { color: var(--text-dim); font-size: 12.5px; font-variant-numeric: tabular-nums; }

.tabs { display: flex; gap: 4px; margin: 0 0 14px; }
.tabs button {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 999px;
  border: 1px solid transparent; background: none;
  color: var(--text-dim); font: 600 13px var(--font); cursor: pointer;
}
.tabs button:hover { color: var(--text); }
.tabs button.on { background: var(--surface-2); color: var(--text); border-color: var(--border); }
/* The bare `.count` above carries a 7px top margin for the itinerary headings,
   and this inherited it, which is what dropped the badge below the word beside
   it. Centred in its own right rather than relying on the text baseline. */
.tabs .count {
  margin: 0;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 17px; height: 17px; padding: 0 5px; box-sizing: border-box;
  font: 600 11px var(--mono); line-height: 1; border-radius: 999px;
  background: var(--bad-bg); color: var(--bad);
}

.train-alerts { margin-bottom: 20px; }
.train-alerts .alert { background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--bad); border-radius: var(--radius); padding: 11px 13px; }
.train-alerts .alert h2 { margin: 0 0 5px; font-size: 14px; font-weight: 600; text-transform: none; letter-spacing: 0; color: var(--text); }
.train-alerts .alert p { margin: 0; font-size: 13px; white-space: pre-wrap; color: var(--text-dim); }

.profile { margin-bottom: 22px; }
.itinerary h2 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim); margin: 0 0 8px; font-weight: 600; }

/* The plot stretches to the panel width, so anything that must not be
   distorted, every label, is HTML positioned over it rather than text inside
   the viewBox. */
.chart {
  position: relative;
  height: 128px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
/* The plot is its own box so that every percentage: the line, the ticks, the
   cursor: is measured against the same area the pointer is. */
.plot {
  position: absolute; inset: 0 10px 0 10px;
  touch-action: none; cursor: crosshair;
}
.chart svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.chart .zero { stroke: var(--text-dim); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0.55; vector-effect: non-scaling-stroke; }
/* Colour carries the thing the chart is for: which legs cost time and which
   gave some back. */
.chart .leg { stroke-width: 2.2; stroke-linecap: round; vector-effect: non-scaling-stroke; fill: none; }
.chart .leg.lost { stroke: var(--bad); }
.chart .leg.gained { stroke: var(--good); }
.chart .leg.level { stroke: var(--text-dim); opacity: 0.75; }
.chart .node { fill: var(--text-dim); opacity: 0.9; }
.chart .node.future { fill: none; stroke: var(--text-dim); stroke-width: 0.6; opacity: 0.5; }

/* Inside the plot rather than in a gutter: a gutter wide enough for "7h14 late"
   is width the chart cannot spare in a 372px panel. */
.plot .axis-top, .plot .axis-zero {
  position: absolute; left: 0; z-index: 1; pointer-events: none;
  font: 11px var(--mono); color: var(--text-dim);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  padding: 0 4px; border-radius: 3px;
  background: color-mix(in srgb, var(--surface) 85%, transparent);
}
.plot .axis-top { top: 2px; }
.plot .axis-zero { transform: translateY(-115%); }

.plot .tick {
  position: absolute; bottom: 3px; transform: translateX(-50%);
  font: 600 11px var(--font); color: var(--text);
  white-space: nowrap; pointer-events: none;
  padding: 0 3px; background: color-mix(in srgb, var(--surface) 82%, transparent);
  border-radius: 3px;
}
/* Where the train has reached, distinct from the station ticks. */
.plot .now {
  position: absolute; top: 0; bottom: 0; width: 1px;
  background: var(--accent); opacity: 0.9; pointer-events: none;
}
.plot .now span {
  position: absolute; top: 1px; left: 3px;
  font: 600 10px var(--font); text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text); background: color-mix(in srgb, var(--surface) 85%, transparent);
  padding: 0 3px; border-radius: 3px;
}
.plot .now:where([style*="left:9"], [style*="left:100"]) span { left: auto; right: 3px; }

.plot .cursor {
  position: absolute; top: 0; bottom: 0; width: 1px;
  background: var(--text); opacity: 0.45; pointer-events: none;
}

.profile .readout {
  margin: 7px 2px 0; font-size: 13px; color: var(--text);
  min-height: 1.4em; font-weight: 550;
}
.profile .callout { margin: 3px 2px 0; font-size: 12.5px; color: var(--text-dim); }

.itinerary { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); overflow: auto; }
.itinerary h2 { padding: 11px 14px 0; margin-bottom: 6px; display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.itinerary h2 .counts { text-transform: none; letter-spacing: 0; font-weight: 400; font-size: 11.5px; }

/* A spine down the left: filled behind the train, hollow ahead of it, so which
   stops are done is readable at a glance rather than inferred from grey text. */
.stops { list-style: none; margin: 0; padding: 2px 0 4px; }
.stops li { position: relative; display: flex; align-items: center; gap: 10px; padding: 7px 14px 7px 34px; }
.stops li .dot {
  position: absolute; left: 15px; top: 50%; width: 9px; height: 9px; margin-top: -4.5px;
  border-radius: 50%; background: var(--surface); border: 2px solid var(--border); z-index: 1;
}
/* The connecting rail, drawn as a border on a pseudo-element. */
.stops li::before {
  content: ""; position: absolute; left: 19px; top: 0; bottom: 0; width: 2px;
  background: var(--border);
}
.stops li:first-child::before { top: 50%; }
.stops li:last-child::before { bottom: 50%; }

.stop-row.called .dot { background: var(--text-dim); border-color: var(--text-dim); }
.stop-row.called::before { background: var(--text-dim); }
.stop-row.called .name { color: var(--text-dim); }
.stop-row.next .dot { background: var(--accent); border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 28%, transparent); }
.stop-row.next { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.stop-row.next .name { font-weight: 650; }
.stop-row.scheduled-only .name, .stop-row.scheduled-only .sched { color: var(--text-dim); }

.stops .name { flex: 1; min-width: 0; font-size: 14px; }
.stops .times { display: flex; align-items: baseline; gap: 10px; font: 13px var(--mono); font-variant-numeric: tabular-nums; }
.stops .sched { color: var(--text-dim); }
.stops .est { font-weight: 600; }
.stops .est .yours { display: block; font-size: 10.5px; font-weight: 400; color: var(--text-dim); text-align: right; }
.stops .badges { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
/* How much lateness this leg added: the "where it went wrong" marker. */
.lost { font: 600 11px var(--mono); color: var(--bad); }

/* The train's own position, standing in the gap between two stops.
   Tall on purpose: `--at` slides the locomotive down the leg as the run
   progresses, and that only reads if there is room to slide. */
.stops li.here {
  display: block; position: relative;
  min-height: 78px; padding: 0 14px 0 34px;
}
/* Rail drawn as travelled up to the train, pending beyond it. */
.stops li.here::before {
  background: linear-gradient(to bottom, var(--text-dim) var(--at), var(--border) var(--at));
}
.stops li.here .loco {
  position: absolute; left: 8px; top: var(--at); transform: translateY(-50%);
  width: 25px; height: 25px; display: grid; place-items: center;
  border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 3px var(--surface);
  z-index: 2;
}
/* The accent is a fixed yellow in both schemes, so the glyph on it is dark in
   both: `--text` would vanish into it on the dark palette. */
.stops li.here .loco svg { width: 15px; height: 15px; display: block; fill: #17130c; }
.stops li.here .text {
  position: absolute; left: 42px; right: 14px; top: var(--at);
  transform: translateY(-50%);
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 12.5px; font-weight: 600; color: var(--text); letter-spacing: 0.01em;
}
.stops li.here .pace {
  font: 600 11.5px var(--mono); font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}
.itinerary table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.itinerary thead th { text-align: left; padding: 6px 12px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); border-bottom: 1px solid var(--border); }
.itinerary td { padding: 7px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
/* Clocks and badges take what they need and no more, so the destination gets
   the rest of the row instead of whatever is left over from the headings. */
.itinerary th.time, .itinerary th.tight, .itinerary td.time { width: 1%; white-space: nowrap; }
/* A heading that will explain itself, marked so that it is worth hovering. */
.itinerary th.help { cursor: help; text-decoration: underline dotted; text-underline-offset: 3px; }
.itinerary tr:last-child td { border-bottom: none; }
.itinerary tr.passed { color: var(--text-dim); }
.itinerary tr.future td { color: var(--text-dim); font-style: italic; }
.itinerary tr.call { cursor: pointer; }
.itinerary tr.call:hover { background: var(--surface-2); }
.itinerary .stop { font-weight: 500; }
.itinerary .time { font: 13px var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.itinerary .yours { display: block; font-size: 11px; color: var(--text-dim); }
/* The station name is the link now, so it takes the row's own type and only
   marks itself on hover. */
.to-station { color: inherit; }
.stops .name.to-station:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.note { padding: 9px 14px; margin: 0; font-size: 12px; color: var(--text-dim); border-top: 1px solid var(--border); }

.alerts { display: flex; flex-direction: column; gap: 12px; }
.alert { background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--bad); border-radius: var(--radius); padding: 12px 14px; }
.alert.muted { border-left-color: var(--border); color: var(--text-dim); }
.alert h2 { margin: 0 0 6px; font-size: 15px; font-weight: 600; }
.alert p { margin: 0; font-size: 13.5px; white-space: pre-wrap; }
.affecting { margin-top: 9px !important; display: flex; align-items: center; flex-wrap: wrap; gap: 5px; font-size: 12px; color: var(--text-dim); }

/* ── narrow screens ─────────────────────────────────────────────────── */

@media (max-width: 860px) {
  /* One pane at a time, each getting the whole screen. Splitting it gave a map
     too small to read and a list too short to scroll. */
  .network { grid-template-columns: 1fr; grid-template-rows: 1fr; }
  .network > .map, .network > .panel { grid-area: 1 / 1; min-height: 0; }
  .network[data-pane="map"] .panel { display: none; }
  .network[data-pane="list"] .map { display: none; }
  .panel { border-left: none; }
  .list { padding-bottom: 64px; }

  .pane-toggle {
    display: flex;
    position: absolute;
    bottom: 14px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 10;
    padding: 3px;
    gap: 3px;
    border-radius: 999px;
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: 0 3px 14px rgb(0 0 0 / 0.18);
  }
  .pane-toggle button {
    border: 0;
    background: none;
    color: var(--text-dim);
    font: 600 13px var(--font);
    padding: 7px 16px;
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
  }
  .pane-toggle button.on { background: var(--text); color: var(--bg); }

  /* Stack MapLibre's own controls above the switch rather than under it: the
     attribution is full width until it is collapsed, and they collided. */
  .network .maplibregl-ctrl-bottom-left,
  .network .maplibregl-ctrl-bottom-right { bottom: 56px; }
  header { gap: 12px; }
  .topbar nav a { padding: 5px 8px; }
  .detail-head { flex-direction: column; gap: 8px; }
  .detail-head .stats { margin-left: 0; }
}

/* On a phone the header has to stay one line: the brand wordmark and the
   freshness label are the two things that can go without losing anything the
   viewer cannot get elsewhere. */
@media (max-width: 560px) {
  /* Even without the search button the name does not fit beside the nav and
     the status on a 414px screen. The mark carries the identity there. */
  .brand span:not(.mark) { display: none; }
  .feed { max-width: none; }
  .topbar { gap: 8px; padding: 0 10px; }
  /* The button keeps its icon; the word and the shortcut go. */
  .search-button .label, .search-button kbd { display: none; }
  .search-button { padding: 6px; }
  .palette-backdrop { padding: 6vh 10px 10px; }

  /* Reclaim the ~55px the itinerary was overflowing by. The delay badge shrinks
     rather than wrapping: "44 min late" over two lines made every late row a
     different height and the column ragged. */
  .itinerary td { padding: 7px 8px; }
  .itinerary thead th { padding: 6px 8px; }
  .itinerary .time { font-size: 12.5px; }
  .itinerary .delay { font-size: 11px; padding: 2px 5px; }
  .stops li { padding-left: 30px; padding-right: 10px; gap: 7px; }
  .stops li .dot { left: 12px; }
  .stops li::before { left: 16px; }
  .stops li.here { padding-left: 30px; min-height: 72px; }
  .stops li.here .loco { left: 5px; }
  .stops li.here .text { left: 38px; right: 10px; }
  .stops .times { gap: 7px; font-size: 12px; }
  .stops .name { font-size: 13.5px; }
  .itinerary h2 { flex-direction: column; gap: 2px; }
  .detail { padding: 0 12px 32px; }
  .back { margin: 0 -12px 12px; padding: 12px 12px 10px; }
}
