/* Qahwa BLK — charcoal black with a coffee-gold accent.
 *
 * Charcoal rather than pure #000: easier on the eyes over a long shift and it
 * does not show every smudge on the screen. Gold rather than the old green,
 * because the green also means LIVE and branding should not look like a vehicle
 * state.
 *
 * The status colours below are deliberately untouched. They carry meaning, not
 * branding, and the same is true of the banner reds and the amber badges further
 * down. Everything that is only chrome goes through these variables. */
:root {
  --bg: #08090a;
  --panel: #121316;
  --panel-2: #1a1d21;
  --line: #23262b;
  --text: #eef1f4;
  --muted: #8b98a5;
  --accent: #d4a24a;

  /* Translucent forms of the surfaces, for controls that sit over the map. */
  --panel-fade: rgba(18, 19, 22, .92);
  --bg-fade: rgba(8, 9, 10, .9);
  --accent-glow: rgba(212, 162, 74, .16);
  --accent-deep: #a97c2c;
  --accent-deeper: #8a6320;
  /* The map's own backdrop while tiles load, matched to OpenFreeMap's dark
     style so there is no flash of a different colour. */
  --map-bg: #0c0c0c;

  --live: #3fb950;
  --idle: #58a6ff;
  --parked: #8b98a5;
  --offline: #f85149;
  --suspect: #d29922;
  --expired: #a371f7;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.5 -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

body {
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  height: 100vh;
}

/* ---------- top bar ---------- */

.topbar {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 10px 18px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}

.brand { display: flex; align-items: center; gap: 10px; }

.brand .dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-glow);
}

.brand h1 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .2px;
  white-space: nowrap;
}

.brand h1 span { color: var(--muted); font-weight: 400; }

.summary { display: flex; gap: 8px; flex: 1; flex-wrap: wrap; }

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel-2);
  font-size: 12px;
  color: var(--muted);
}

.chip b { color: var(--text); font-weight: 600; }
.chip i { width: 7px; height: 7px; border-radius: 50%; font-style: normal; }

.tabs { display: flex; gap: 4px; }

.tab {
  background: transparent;
  border: 1px solid transparent;
  color: var(--muted);
  padding: 6px 14px;
  border-radius: 7px;
  cursor: pointer;
  font-size: 13px;
  font-family: inherit;
}

.tab:hover { color: var(--text); background: var(--panel-2); }

.tab.active {
  color: var(--text);
  background: var(--panel-2);
  border-color: var(--line);
}

/* ---------- banner ---------- */

.banner {
  padding: 9px 18px;
  background: #3d2a12;
  border-bottom: 1px solid #5a3f1a;
  color: #f0c674;
  font-size: 13px;
}

.banner.error { background: #3d1d1d; border-bottom-color: #5c2626; color: #ff9a94; }
.hidden { display: none !important; }

/* ---------- layout ---------- */

main {
  display: grid;
  grid-template-columns: 1fr 340px;
  min-height: 0;
  /* Containing block for the camera panel, so it sits above the status bar
     rather than over it. */
  position: relative;
}

.stage { position: relative; min-height: 0; display: flex; flex-direction: column; }

/* 65/35 split with .wasslz-panel below. flex-shrink stays enabled (not 0 0)
   so both still yield proportionally to .campanel's fixed 42vh when the
   camera row opens above — a hard-locked basis would force the camera
   panel, the map, and the chart to overflow past 100% of .stage instead. */
#map { flex: 0 1 65%; min-height: 0; background: var(--map-bg); }

/* When the Bingo chart is hidden (the common case — it only shows once
   Bingo's card is selected), the map takes the full stage instead of
   leaving the 35% the chart would have used sitting empty. */
.stage.no-wasslz-chart #map { flex: 1 1 100%; }

.controls {
  display: flex;
  align-items: end;
  gap: 14px;
  padding: 10px 14px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}

.controls label {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 11px; text-transform: uppercase;
  letter-spacing: .5px; color: var(--muted);
}

.controls select, .controls input, #trips-days, #alarms-days {
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 6px 9px;
  border-radius: 6px;
  font-family: inherit;
  font-size: 13px;
}

.controls button {
  background: var(--accent-deep); border: none; color: #0b0b0b;
  padding: 7px 15px; border-radius: 6px; cursor: pointer;
  font-family: inherit; font-size: 13px; font-weight: 500;
}

.controls button:hover { background: var(--accent); }

.route-summary { color: var(--muted); font-size: 13px; padding-bottom: 6px; }

/* The bar above a drawn trip. Its own element rather than the History controls,
   which carried a vehicle picker for a different vehicle. */
.tripbar {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 14px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}

.tripbar-text { font-size: 13px; color: var(--muted); }

.tripbar-clear {
  margin-left: auto;
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 4px 12px; border-radius: 6px; cursor: pointer;
  font-family: inherit; font-size: 12px;
}

.tripbar-clear:hover { border-color: var(--accent); color: var(--accent); }

/* ---------- sidebar ---------- */

.sidebar {
  background: var(--panel);
  border-left: 1px solid var(--line);
  overflow-y: auto;
  padding: 12px;
}

.card {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-left: 3px solid var(--parked);
  border-radius: 8px;
  padding: 11px 12px;
  margin-bottom: 10px;
  cursor: pointer;
  transition: border-color .12s, transform .12s;
}

.card:hover { transform: translateX(-2px); }
.card.selected { border-color: var(--idle); }

.card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-bottom: 8px;
}

.card-name { font-weight: 600; font-size: 14px; }

.badge {
  font-size: 10px; font-weight: 700; letter-spacing: .6px;
  padding: 3px 7px; border-radius: 4px; text-transform: uppercase;
}

.card-reason {
  color: var(--muted); font-size: 12px; margin-bottom: 9px;
  line-height: 1.4;
}

.metrics {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px;
  border-top: 1px solid var(--line); padding-top: 8px;
}

.metric { text-align: center; }
.metric b { display: block; font-size: 13px; font-variant-numeric: tabular-nums; }
.metric span {
  display: block; font-size: 9px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .4px;
}

.attention { border-left-color: var(--offline); }

/* ---------- deliveries ---------- */

/* Bingo's route for the day, above the vehicle cards. Not `.panel` — that
   class is the full-screen absolute overlay used by #trips/#alarms, which
   would blow this small sidebar block up to cover the whole map. */
.deliveries-panel {
  margin-bottom: 12px;
}

.deliveries-header {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--muted);
  padding: 2px 2px 8px;
}

.delivery-stop {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 4px 8px;
  font-size: 0.9em;
}

.deliveries-date {
  background: var(--panel-fade);
  border: 1px solid var(--line);
  color: var(--text);
  font-family: inherit;
  font-size: 11px;
  padding: 2px 6px;
  border-radius: 5px;
  margin-bottom: 6px;
}

.delivery-branch { color: var(--text); }

.delivery-stop-arrived .delivery-status {
  color: var(--live);
}

.delivery-stop-pending .delivery-status {
  color: var(--muted);
}

.deliveries-error {
  color: var(--muted);
  font-size: 0.9em;
  padding: 4px 8px;
}

/* Wasslz temperature chart, under the map. A flex-column sibling of #map
   inside .stage — same mechanism .campanel already uses above the map,
   just below it instead. */
.wasslz-panel {
  flex: 1 1 35%;
  min-height: 0;
  position: relative; /* anchors the hover tooltip */
  background: var(--panel);
  border-top: 1px solid var(--line);
  padding: 10px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.wasslz-tooltip {
  position: absolute;
  pointer-events: none;
  background: var(--panel-fade);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 4px 8px;
  font-size: 11px;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  transform: translate(-50%, -100%);
  margin-top: -8px;
  z-index: 3;
}

/* Only Bingo's tooltip is interactive (it has a button); the
   cooling-room tooltip reuses .wasslz-tooltip for its base styling
   but must stay non-interactive, matching its pre-existing behavior. */
#wasslz-tooltip { pointer-events: auto; }

.wasslz-tooltip-loc {
  color: var(--muted);
  font-size: 10px;
  margin-top: 2px;
}

.wasslz-tooltip-map-btn {
  display: block;
  margin-top: 4px;
  padding: 2px 6px;
  background: none;
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--accent);
  font-size: 10px;
  cursor: pointer;
}

.wasslz-tooltip-map-btn:hover { border-color: var(--accent); background: var(--accent-glow); }

.wasslz-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}

.wasslz-title {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--muted);
}

.wasslz-current {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.wasslz-current.breach { color: #e5484d; }

.wasslz-range {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  color: var(--muted);
}

.wasslz-range label {
  display: flex;
  align-items: center;
  gap: 5px;
}

.wasslz-range input[type="date"] {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--text);
  font-size: 11px;
  padding: 2px 5px;
  font-variant-numeric: tabular-nums;
  color-scheme: dark;
}

.wasslz-range button {
  background: none;
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--muted);
  font-size: 11px;
  padding: 2px 8px;
  cursor: pointer;
}

.wasslz-range button:hover { color: var(--text); border-color: var(--accent); }

.wasslz-canvas {
  flex: 1;
  width: 100%;
  min-height: 0;
}

.wasslz-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 0.9em;
}

/* .wasslz-* classes below are reused verbatim for this tab — they style
   the Wasslz/ThingsBoard platform's chart look in general, not Bingo's
   probe specifically. Only this wrapper is new. */
.cooling-room-body {
  position: relative; /* anchors the hover tooltip, same as .wasslz-panel */
  height: 65vh;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* ---------- panels: trips and alarms ---------- */

.panel {
  position: absolute; inset: 0;
  background: var(--bg);
  overflow-y: auto;
  padding: 18px;
  /* Above MapLibre's own controls. They set their own z-index, which beats this
     panel's `auto` no matter that the panel comes later in the DOM — the zoom
     buttons and scale bar showed through and clipped the heading. */
  z-index: 6;
}

.panel-head {
  display: flex; align-items: center; gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.panel-head h2 { margin: 0; font-size: 15px; font-weight: 600; }
.panel-head label { font-size: 12px; color: var(--muted); }

.panel-head .checkbox {
  display: inline-flex; align-items: center; gap: 6px;
  cursor: pointer;
}

/* Pushed right so the counts read as a summary of the list, not a control. */
.panel-note {
  margin-left: auto;
  display: flex; gap: 6px; flex-wrap: wrap;
  font-size: 12px; color: var(--muted);
}

.panel-note .chip.muted { opacity: .65; }

table { width: 100%; border-collapse: collapse; font-size: 13px; }

th {
  text-align: left; padding: 8px 10px;
  color: var(--muted); font-weight: 500; font-size: 11px;
  text-transform: uppercase; letter-spacing: .5px;
  border-bottom: 1px solid var(--line);
}

td { padding: 9px 10px; border-bottom: 1px solid var(--line); }
td.num { text-align: right; font-variant-numeric: tabular-nums; }
tbody tr:hover { background: var(--panel); }

.empty { color: var(--muted); padding: 30px; text-align: center; }
.empty.error { color: var(--offline); }

/* ---------- trips ---------- */

.trip-vehicle {
  border: 1px solid var(--line);
  border-radius: 8px;
  margin-bottom: 12px;
  overflow: hidden;
}

.trip-vehicle-head {
  display: flex; align-items: baseline; gap: 12px;
  padding: 10px 12px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}

.trip-name { font-weight: 600; }
.trip-tot { font-size: 12px; color: var(--muted); margin-left: auto; }

.trip-day { border-bottom: 1px solid var(--line); }
.trip-day:last-child { border-bottom: 0; }

.trip-day-head {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: 13px;
}

.trip-day-head:hover { background: var(--panel); }
.empty-day .trip-day-head { cursor: default; opacity: .6; }
.empty-day .trip-day-head:hover { background: none; }

.caret {
  color: var(--muted); font-size: 10px;
  transition: transform .12s;
}

.trip-day.open .caret { transform: rotate(90deg); }

.trip-date { font-variant-numeric: tabular-nums; }
.trip-day-tot { color: var(--muted); font-size: 12px; }

/* Tracksolid's own count, kept visible so the numbers stay reconcilable
   against their console rather than quietly disagreeing with it. */
.trip-hidden {
  margin-left: auto;
  font-size: 11px;
  color: var(--muted);
  border: 1px dashed var(--line);
  border-radius: 999px;
  padding: 1px 8px;
  cursor: help;
}

.trip-table { display: none; }
.trip-day.open .trip-table { display: table; }
.trip-table td, .trip-table th { padding-left: 32px; }
.trip-row { cursor: pointer; }

.trip-view-toggle {
  display: flex; gap: 4px;
}

.trip-view-btn {
  background: var(--panel-fade);
  border: 1px solid var(--line);
  color: var(--muted);
  padding: 4px 10px;
  border-radius: 6px;
  font-family: inherit; font-size: 12px;
  cursor: pointer;
}

.trip-view-btn.active {
  color: var(--accent);
  border-color: var(--accent-deep);
}

/* padding-top/right/bottom only, deliberately not the padding shorthand: a
   shorthand here would reset padding-left and cancel .trip-table's 32px
   day-nesting indent above (same specificity, later in the file wins). */
.legs-table th, .legs-table td {
  padding-top: 4px;
  padding-right: 8px;
  padding-bottom: 4px;
  font-size: 0.9em;
}

/* ---------- alarms ---------- */

.alarm-row { cursor: pointer; }
.alarm-row td:first-child { font-variant-numeric: tabular-nums; }

.alarm-row .ago {
  display: block; font-size: 11px; color: var(--muted);
}

.sev-dot {
  display: inline-block;
  width: 8px; height: 8px; border-radius: 50%;
  margin-right: 8px;
  background: var(--muted);
}

.sev-critical .sev-dot { background: var(--offline); }
.sev-warning .sev-dot { background: var(--suspect); }
.sev-critical td:first-child { box-shadow: inset 3px 0 0 var(--offline); }

/* Bingo's fatigue alarm fires every ten minutes while parked. Saying so is the
   difference between a useful list and one that cries wolf. */
.suspect {
  margin-left: 8px;
  font-size: 11px;
  color: var(--suspect);
  border: 1px solid var(--suspect);
  border-radius: 999px;
  padding: 1px 7px;
  cursor: help;
}

/* ---------- notifications ---------- */

/* No rules of its own — the bare `table` selector above already covers
 * everything this table needs. Named separately from .alarm-table (which
 * this table used to borrow, copy-pasted from loadAlarms()) purely so the
 * two tables aren't tied together by a shared class that implies more than
 * "it's a table". */
.notif-table {}

.notif-dry { color: var(--muted); }
.notif-sent { color: var(--live); }
.notif-error { color: var(--offline); cursor: help; }

/* ---------- cameras ---------- */

/* A row inside the stage, above the map, rather than a panel floating over it.
   Pressing Camera shows every vehicle at once — no picking — so the map has to
   stay usable underneath instead of being covered by one enormous frame. */
/* Tall enough for Tracksolid's player to be usable, not just present. Their
   embed carries its own toolbar — including a camera-channel selector we cannot
   preselect, because `jimi.device.live.page.url` takes only imei, type and
   voice — and in a narrow tile that toolbar wraps onto two rows. At 300px the
   video was clipped to a sliver. */
.campanel {
  flex: 0 0 auto;
  height: auto;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}

.campanel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 9px 11px;
  background: var(--panel-2);
  border-bottom: 1px solid var(--line);
}

.campanel-title { display: flex; align-items: baseline; gap: 9px; min-width: 0; }

.campanel-name {
  font-weight: 600; font-size: 14px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.campanel-sub { font-size: 12px; color: var(--muted); }

/* Fixed-size tiles, centered, rather than stretched to fill the row: with
   one vehicle open (the current mode) a full-width/full-height tile made the
   panel look like an oversized banner, most of it empty black letterboxing
   either side of the cropped video. Multiple tiles still lay out side by
   side, just at their own size instead of dividing the row. */
.camgrid {
  flex: 0 0 auto;
  display: flex;
  justify-content: center;
  gap: 1px;
  background: var(--line);
}

.camtile {
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  width: 300px;
  min-width: 0;
  background: var(--panel);
}

.camtile-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
  padding: 6px 9px;
  border-bottom: 1px solid var(--line);
}

.camtile-name {
  font-size: 13px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* One line, with the full text on hover. Three lines of warning in a 290px tile
   took more room than the video it was warning about. */
.camtile-warn {
  padding: 4px 9px;
  background: rgba(210, 153, 34, .12);
  color: var(--suspect);
  font-size: 11px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: help;
}

/* Tracksolid's live page bundles the video with its own map/POI panel on the
   right, taking over half the space with something our own map already
   shows. We cannot remove it server-side (it is their page, not ours — see
   the channel-selector note above), so we render their page wider than this
   tile and clip the excess: the iframe gets 100%/--cam-video-fraction of the
   tile's width, and this container's overflow:hidden crops away everything
   past --cam-video-fraction, which is where their map panel lives.
   --cam-video-fraction is an estimate from a real screenshot (2026-08-18),
   not a measured constant — nudge it if the crop line lands on the wrong
   side of the video/map boundary. */
.camtile-body {
  --cam-video-fraction: 0.6;
  flex: 0 0 auto;
  width: 300px;
  height: 300px;
  background: #000;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  position: relative;
}

.camtile-body iframe {
  position: absolute;
  top: 0; left: 0;
  width: calc(100% / var(--cam-video-fraction));
  height: 100%;
  border: 0;
  display: block;
}

.campanel-actions { display: flex; align-items: center; gap: 8px; }

.camtabs {
  display: flex; gap: 2px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 2px;
}

.camtab {
  background: none; border: none; color: var(--muted);
  padding: 4px 10px; border-radius: 4px; cursor: pointer;
  font-family: inherit; font-size: 12px;
}

.camtab:hover { color: var(--text); }
.camtab.active { background: var(--panel-2); color: var(--text); }

.campanel-close {
  background: none; border: none; color: var(--muted);
  font-size: 20px; line-height: 1; cursor: pointer; padding: 0 4px;
}

.campanel-close:hover { color: var(--text); }

.cam-message {
  color: var(--muted); font-size: 13px; text-align: center;
  padding: 20px; line-height: 1.6;
}

.cam-message.error { color: var(--offline); }

/* Opens the camera without selecting the card underneath it. */
.card-cam {
  margin-top: 8px;
  width: 100%;
  background: var(--panel);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 5px 9px;
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
  font-size: 12px;
}

.card-cam:hover { border-color: var(--accent); color: var(--accent); }

/* ---------- the "?" glossary ---------- */

.helpbtn {
  width: 26px; height: 26px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--muted);
  cursor: pointer;
  font-family: inherit; font-size: 13px; font-weight: 700;
  line-height: 1;
}

.helpbtn:hover { color: var(--accent); border-color: var(--accent); }

.help-backdrop {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(0, 0, 0, .66);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}

.help-panel {
  width: min(760px, 100%);
  max-height: 100%;
  display: flex; flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .6);
  overflow: hidden;
}

.help-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px;
  background: var(--panel-2);
  border-bottom: 1px solid var(--line);
}

.help-head h2 { margin: 0; font-size: 15px; font-weight: 600; }

.help-close {
  background: none; border: none; color: var(--muted);
  font-size: 22px; line-height: 1; cursor: pointer; padding: 0 4px;
}

.help-close:hover { color: var(--text); }

.help-body { overflow-y: auto; padding: 4px 16px 18px; }

.help-body h3 {
  margin: 18px 0 6px;
  font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .6px;
  color: var(--accent);
}

.help-lead {
  margin: 0 0 10px;
  color: var(--muted); font-size: 12.5px; line-height: 1.5;
}

.help-body dl { margin: 0; }

/* Term on the left, meaning on the right — a dictionary, which is what was
   asked for. Collapses to stacked rows on a narrow window. */
.help-row {
  display: grid;
  grid-template-columns: 190px 1fr;
  gap: 14px;
  padding: 8px 0;
  border-top: 1px solid var(--line);
}

.help-row dt {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-weight: 600; font-size: 13px;
}

.help-row dd {
  margin: 0;
  color: var(--muted); font-size: 12.5px; line-height: 1.55;
}

.help-dot {
  width: 9px; height: 9px; border-radius: 50%;
  flex: 0 0 auto;
}

.help-act {
  font-size: 10px; font-weight: 700; letter-spacing: .4px;
  text-transform: uppercase;
  color: var(--offline);
  border: 1px solid var(--offline);
  border-radius: 999px;
  padding: 1px 6px;
}

@media (max-width: 640px) {
  .help-row { grid-template-columns: 1fr; gap: 4px; }
}

/* ---------- status bar ---------- */

.statusbar {
  display: flex; justify-content: space-between; gap: 16px;
  padding: 6px 18px;
  background: var(--panel);
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}

/* ---------- map style switcher ---------- */

.mapstyle {
  position: absolute; top: 10px; right: 10px; z-index: 2;
}

/* The switcher is pinned to the top of the stage, which is where the camera row
   goes. Left visible it sits on top of the Live/Recorded toggle — and it belongs
   to the map, which has moved below. */
.stage.cams .mapstyle { display: none; }

/* With a trip bar showing, drop the switcher below it so it stops covering the
   Clear button. The map is still on screen here, so hiding it outright would
   take away a control the user can still use. */
.stage.trip .mapstyle { top: 52px; }

.mapstyle select, .branch-toggle-btn {
  background: var(--panel-fade);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 6px 10px;
  border-radius: 7px;
  font-family: inherit; font-size: 12.5px;
  cursor: pointer;
  backdrop-filter: blur(4px);
}

.branch-toggle-btn {
  color: var(--muted);
  margin-left: 6px;
}

.branch-toggle-btn.active {
  color: var(--accent);
  border-color: var(--accent-deep);
}

/* ---------- map markers ---------- */

.veh-marker {
  display: flex; align-items: center; gap: 6px;
  white-space: nowrap; cursor: pointer;
}

.veh-dot {
  width: 18px; height: 18px; border-radius: 50%;
  border: 3px solid #fff;
  background: currentColor;
  box-shadow: 0 1px 5px rgba(0, 0, 0, .45);
  flex: none;
}

/* Only live vehicles pulse — it should mean "this is moving right now",
   not "this is a vehicle". */
.veh-dot.pulsing { animation: pulse 2s ease-out infinite; }

@keyframes pulse {
  0%   { box-shadow: 0 1px 5px rgba(0,0,0,.45), 0 0 0 0 currentColor; }
  70%  { box-shadow: 0 1px 5px rgba(0,0,0,.45), 0 0 0 13px transparent; }
  100% { box-shadow: 0 1px 5px rgba(0,0,0,.45), 0 0 0 0 transparent; }
}

.veh-label {
  background: var(--bg-fade);
  padding: 2px 8px; border-radius: 5px;
  font-size: 11.5px; font-weight: 600; color: #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .35);
}

.route-pin,
.endpoint-pin {
  width: 15px; height: 15px; border-radius: 50%;
  border: 3px solid #fff;
  box-shadow: 0 1px 5px rgba(0, 0, 0, .5);
  cursor: pointer;
}

.branch-pin {
  width: 13px; height: 13px;
  background: var(--accent);
  border: 3px solid #fff;
  border-radius: 3px;
  transform: rotate(45deg);
  box-shadow: 0 1px 5px rgba(0, 0, 0, .5);
  cursor: pointer;
}

.maplibregl-canvas-container canvas { outline: none; }

.view-reset-btn {
  font: 700 12px/1 inherit;
  color: #24292f;
  background: #fff;
  border: none;
  cursor: pointer;
}

/* ---------- popups ---------- */

.maplibregl-popup-content {
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 11px 13px;
  font-size: 12.5px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .4);
}

.maplibregl-popup-content b {
  display: block; margin-bottom: 5px; font-size: 13.5px;
}

.maplibregl-popup-content .pk {
  color: var(--muted); display: inline-block; min-width: 74px;
}

.maplibregl-popup-content .pr {
  margin-top: 7px; padding-top: 7px;
  border-top: 1px solid var(--line);
  color: var(--muted); max-width: 220px; white-space: normal;
}

.maplibregl-popup-tip { border-top-color: var(--panel-2) !important; }
.maplibregl-popup-anchor-top .maplibregl-popup-tip { border-bottom-color: var(--panel-2) !important; }
.maplibregl-popup-anchor-left .maplibregl-popup-tip { border-right-color: var(--panel-2) !important; }
.maplibregl-popup-anchor-right .maplibregl-popup-tip { border-left-color: var(--panel-2) !important; }

@media (max-width: 900px) {
  main { grid-template-columns: 1fr; grid-template-rows: 1fr auto; }
  .sidebar { border-left: none; border-top: 1px solid var(--line); max-height: 42vh; }

  /* Three video tiles side by side stop being watchable on a narrow screen, so
     they stack and the row scrolls instead. */
  .camgrid { grid-auto-flow: row; grid-auto-rows: 200px; overflow-y: auto; }
  .campanel { height: 320px; }
}
