/*
 * Control room wallboard.
 *
 * Dark by default, unlike the rest of the site: this is built to be left on a
 * screen in a room, where a white page is a lamp. It still honours a light
 * preference, because the same page gets read on a laptop.
 *
 * Tokens follow the same roles as performance.css so the two agree, and the
 * chart colours are the validated palette: series blue for magnitude, series
 * orange as the second magnitude context, and the fixed status four for state.
 * The delay mix is a diverging encoding -- cool early, neutral on time, two
 * warm steps for late -- and the two warm steps are the only adjacent pair
 * that needed checking (normal-vision dE 15.7, clear of the 15 floor).
 */

.viz-root {
  color-scheme: dark;

  --surface-0: #0d1216;
  --surface-1: #141a1f;
  --surface-2: #1c242b;
  --rule: #2a343d;
  --rule-soft: #212a31;

  --text-primary: #f2f5f7;
  --text-secondary: #b3bfc9;
  --text-muted: #7d8b96;

  --series-1: #ff8f45;
  --brand-orange: #ff8f45;
  --accent: #ff8f45;
  --accent-soft: #2a2119;
  --ink: #e7e6e0;
  --ink-soft: #a8a79f;
  --fault: #f97066;
  --series-2: #d95926;

  --status-good: #0ca30c;
  --status-warning: #fab219;
  --status-serious: #ec835a;
  --status-critical: #d03b3b;
  --status-absent: #56636d;

  /* Diverging: early <- on time -> late. */
  /* Early, on time and late need three colours that can be told apart, so
     this scale is not the brand palette and should not be folded into it.
     Orange here would collide with the accent and stop meaning "early". */
  --band-early: #3987e5;
  --band-on-time: #383835;
  --band-late: #ec835a;
  --band-very-late: #d03b3b;

  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: light) {
  :root:where(:not([data-theme="dark"])) .viz-root {
    color-scheme: light;
    --surface-0: #f4f6f8;
    --surface-1: #ffffff;
    --surface-2: #eef1f4;
    --rule: #d8dee4;
    --rule-soft: #e6eaee;
    --text-primary: #0b0b0b;
    --text-secondary: #52514e;
    --text-muted: #898781;
    --series-1: #cf5f12;
    --brand-orange: #cf5f12;
    --accent: #cf5f12;
    --accent-soft: #fdf1e8;
    --ink: #33322f;
    --ink-soft: #6b6a66;
    --fault: #b42318;
    --series-2: #eb6834;
    --band-early: #2a78d6;
    --band-on-time: #d5d2cb;
    --status-absent: #a9b2ba;
  }
}

:root[data-theme="light"] .viz-root {
  color-scheme: light;
  --surface-0: #f4f6f8;
  --surface-1: #ffffff;
  --surface-2: #eef1f4;
  --rule: #d8dee4;
  --rule-soft: #e6eaee;
  --text-primary: #0b0b0b;
  --text-secondary: #52514e;
  --text-muted: #898781;
  --series-1: #cf5f12;
  --brand-orange: #cf5f12;
  --accent: #cf5f12;
  --accent-soft: #fdf1e8;
  --ink: #33322f;
  --ink-soft: #6b6a66;
  --fault: #b42318;
  --series-2: #eb6834;
  --band-early: #2a78d6;
  --band-on-time: #d5d2cb;
  --status-absent: #a9b2ba;
}

* { box-sizing: border-box; }

body.viz-root {
  margin: 0;
  background: var(--surface-0);
  color: var(--text-primary);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.board {
  max-width: 1680px;
  margin: 0 auto;
  padding: 18px 20px 56px;
}

/* ---------- head ---------- */

.board-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 12px 22px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--rule);
}
.board-head h1 {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0;
}
.board-head .where { color: var(--text-muted); font-size: 13px; }
.board-head nav { margin-left: auto; display: flex; gap: 16px; font-size: 13px; }
.board-head nav a { color: var(--text-secondary); text-decoration: none; }
.board-head nav a:hover { color: var(--text-primary); text-decoration: underline; }

.tick {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.tick::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--status-good);
}
.tick[data-stale="1"]::before { background: var(--status-critical); }

/* ---------- hero row ---------- */

.headline-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px;
  margin: 18px 0;
}
.hero-stat {
  background: var(--surface-1);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 14px 16px;
}
.hero-stat .n {
  font-family: var(--mono);
  font-size: clamp(28px, 3.4vw, 42px);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  display: block;
}
.hero-stat .k {
  font-size: 12.5px;
  color: var(--text-muted);
  margin-top: 5px;
  display: block;
}
.hero-stat .sub { font-size: 12px; color: var(--text-secondary); margin-top: 7px; }

/* ---------- panels ---------- */

.grid {
  display: grid;
  /* min() so the track can fall below its preferred 400px on a narrower
     screen. A bare minmax(400px, 1fr) is a floor the grid cannot go under,
     which scrolled the wallboard sideways on a phone -- not its main audience,
     but a page that overflows is a page nobody checks twice. */
  grid-template-columns: repeat(auto-fit, minmax(min(400px, 100%), 1fr));
  gap: 14px;
  align-items: start;
}
.grid.wide { grid-template-columns: 2fr 1fr; }
@media (max-width: 1100px) { .grid.wide { grid-template-columns: 1fr; } }

.panel {
  background: var(--surface-1);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 15px 16px 17px;
  min-width: 0;
}
.panel > h2 {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0 0 3px;
}
.panel > .note {
  font-size: 12.5px;
  color: var(--text-muted);
  margin: 0 0 14px;
}

/* ---------- feed cards ---------- */

.capability { border-top: 1px solid var(--rule-soft); padding: 12px 0; }
.capability:first-of-type { border-top: 0; padding-top: 2px; }
.capability > .cap-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.capability .cap-name { font-size: 14.5px; font-weight: 600; }
.capability .cap-powers { font-size: 12px; color: var(--text-muted); }

.source {
  display: grid;
  grid-template-columns: 20px 1fr auto;
  gap: 10px;
  align-items: baseline;
  padding: 7px 0 0;
}
.source .dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-top: 5px;
  background: var(--status-absent);
}
.source[data-status="good"] .dot { background: var(--status-good); }
.source[data-status="warning"] .dot { background: var(--status-warning); }
.source[data-status="serious"] .dot { background: var(--status-serious); }
.source[data-status="critical"] .dot { background: var(--status-critical); }
.source .label { font-size: 13.5px; }
.source .provider { color: var(--text-muted); font-size: 12px; }
/* Status never rests on colour alone -- every dot has this beside it. */
.source .state {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
}
.source .figures {
  grid-column: 2 / -1;
  font-size: 12.5px;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.source .figures b { color: var(--text-primary); font-weight: 600; }
.source .why {
  grid-column: 2 / -1;
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 3px;
  max-width: 62ch;
}
.source[data-active="0"] .label { color: var(--text-secondary); }
.source[data-active="0"] .state { color: var(--text-muted); }

.gap-note {
  grid-column: 1 / -1;
  font-size: 12.5px;
  color: var(--text-secondary);
  background: var(--surface-2);
  border-left: 2px solid var(--status-warning);
  padding: 9px 12px;
  margin-top: 8px;
  max-width: 78ch;
}

/* ---------- alerts ---------- */

.alert {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 10px;
  padding: 10px 0;
  border-top: 1px solid var(--rule-soft);
}
.alert:first-of-type { border-top: 0; }
.alert .icon { font-size: 14px; line-height: 1.35; }
.alert[data-status="critical"] .icon { color: var(--status-critical); }
.alert[data-status="serious"] .icon { color: var(--status-serious); }
.alert[data-status="warning"] .icon { color: var(--status-warning); }
.alert .t { font-size: 13.5px; font-weight: 600; }
.alert .d { font-size: 12.5px; color: var(--text-muted); }
.all-clear {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13.5px;
  color: var(--text-secondary);
}
.all-clear .icon { color: var(--status-good); }

/* ---------- charts ---------- */

.chart { width: 100%; display: block; overflow: visible; }
.chart text { font-family: var(--font); fill: var(--text-muted); font-size: 11px; }
.chart .grid-line { stroke: var(--rule-soft); stroke-width: 1; }
.chart .axis { stroke: var(--rule); stroke-width: 1; }
.chart .series-line { fill: none; stroke: var(--series-1); stroke-width: 2; }
.chart .series-area { fill: var(--series-1); opacity: 0.12; }
.chart .marker { fill: var(--series-1); stroke: var(--surface-1); stroke-width: 2; }
.chart .crosshair { stroke: var(--text-muted); stroke-width: 1; stroke-dasharray: 3 3; }
.chart .value-label {
  fill: var(--text-primary);
  font-family: var(--mono);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}

.bars { display: grid; gap: 7px; }
.bar-row {
  display: grid;
  grid-template-columns: 200px 1fr 74px;
  gap: 12px;
  align-items: center;
  font-size: 13px;
}
.bar-row .name { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-row .name .region { color: var(--text-muted); font-size: 11.5px; }
.bar-row .track { height: 16px; background: var(--surface-2); border-radius: 3px; overflow: hidden; }
.bar-row .fill { height: 100%; background: var(--series-1); border-radius: 0 3px 3px 0; }
.bar-row.dwell .fill { background: var(--series-2); }
.bar-row .n {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  font-size: 12.5px;
}
.bar-row[data-folded="1"] .name { font-style: italic; color: var(--text-muted); }

/* Stacked delay mix. A 2px surface gap between segments, per the mark spec. */
.mix { display: grid; gap: 8px; }
.mix-row { display: grid; grid-template-columns: 200px 1fr 58px; gap: 12px; align-items: center; }
.mix-row .name { font-size: 13px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mix-bar { display: flex; height: 18px; border-radius: 3px; overflow: hidden; background: var(--surface-2); }
.mix-bar span {
  display: block;
  height: 100%;
  border-right: 2px solid var(--surface-1);
  position: relative;
}
.mix-bar span:last-child { border-right: 0; }
.mix-bar .early { background: var(--band-early); }
.mix-bar .on-time { background: var(--band-on-time); }
.mix-bar .late { background: var(--band-late); }
.mix-bar .very-late { background: var(--band-very-late); }
.mix-row .pct {
  font-family: var(--mono);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin: 0 0 12px;
  font-size: 12px;
  color: var(--text-secondary);
}
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i {
  width: 11px;
  height: 11px;
  border-radius: 2px;
  display: inline-block;
}
.legend .early i { background: var(--band-early); }
.legend .on-time i { background: var(--band-on-time); }
.legend .late i { background: var(--band-late); }
.legend .very-late i { background: var(--band-very-late); }

/* ---------- disruptions ---------- */

.sit { padding: 9px 0; border-top: 1px solid var(--rule-soft); font-size: 13px; }
.sit:first-of-type { border-top: 0; }
.sit .top { display: flex; gap: 8px; align-items: baseline; }
.sit .sev {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 3px;
  background: var(--surface-2);
  color: var(--text-secondary);
  white-space: nowrap;
}
.sit[data-sev="severe"] .sev,
.sit[data-sev="verysevere"] .sev { background: var(--status-critical); color: #fff; }
.sit .who { color: var(--text-muted); font-size: 12px; }
.sit .what { color: var(--text-primary); }

/* ---------- table view ---------- */

details.table-view { margin-top: 14px; }
details.table-view summary {
  font-size: 12px;
  color: var(--text-secondary);
  cursor: pointer;
}
details.table-view table { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 12.5px; }
details.table-view th {
  text-align: left;
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 500;
  padding: 0 10px 6px 0;
  border-bottom: 1px solid var(--rule);
}
details.table-view td {
  padding: 5px 10px 5px 0;
  border-bottom: 1px solid var(--rule-soft);
  font-variant-numeric: tabular-nums;
}
.scroller { overflow-x: auto; }

.tooltip {
  position: fixed;
  pointer-events: none;
  z-index: 20;
  background: var(--surface-2);
  border: 1px solid var(--rule);
  border-radius: 5px;
  padding: 7px 10px;
  font-size: 12.5px;
  color: var(--text-primary);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  opacity: 0;
  transition: opacity 0.1s;
}
.tooltip[data-show="1"] { opacity: 1; }
.tooltip .tv { font-family: var(--mono); font-variant-numeric: tabular-nums; }

.loading { color: var(--text-muted); font-size: 13px; }
.err {
  background: var(--surface-2);
  border-left: 2px solid var(--status-critical);
  padding: 9px 12px;
  font-size: 12.5px;
  color: var(--text-secondary);
}

/* Marks an area where every stop is recorded. Dwell at all stops is not
   comparable with dwell at timing points only -- most stops are a few seconds,
   timing points are where a bus holds -- so the difference has to be visible. */
.tag-all-stops {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--series-2);
  border: 1px solid var(--series-2);
  border-radius: 2px;
  padding: 0 3px;
  margin-left: 5px;
  vertical-align: 1px;
}

/* Area picker in the board header. The board is read across a room, so the
   control is small and the scope it resolves to is stated in full beside it
   rather than left to be inferred from a dropdown nobody can see from there. */
.scope-pick select {
  background: var(--surface-1, transparent);
  border: 1px solid var(--border);
  border-radius: 7px;
  color: var(--text-primary);
  font: inherit;
  font-size: 13.5px;
  max-width: 260px;
  padding: 5px 9px;
}

.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;
}
