/* Theme tokens. Light is the default; dark only when the rider switches to it
   (the header toggle, saved on the device). index.html sets html[data-theme]
   to light or dark before first paint, so the [data-theme] rules decide; the
   dark values below are the base the light ones override. */
:root { color-scheme: dark;
  /* Original Backseat design language (see the main app's index.html) */
  --font: 'Roboto Condensed', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --bg: #1a1a1a; --fg: #ffffff; --muted: #b0b0b0; --accent: #d4a017; --accent-fg: #fec940;
  --surface: #1a1a1a; --surface-2: #2d2d2d; --surface-3: #404040; --border: #3a3a3a; --handle: #6a6a6a;
  --outline: #ffffff; /* Backseat's signature bold outlines: white in dark, black in light */
  --shadow: 0 12px 40px rgba(0,0,0,.55), 0 2px 8px rgba(0,0,0,.35);
  --ok: #7ee787; --late: #ffb86b; --early: #8cc8ff;
  --occ-bg: #2a2a2a; --occ-off: rgba(255,255,255,.25);
  --tab-on-bg: #f2f2f2; --tab-on-fg: #111;
  --occ-seats: #7ee787; --occ-few: #ffd33d; --occ-standing: #ff9b3d; --occ-full: #ff6b6b; }
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; background: var(--bg); color: var(--fg); font: 15px/1.4 var(--font); letter-spacing: -0.02em;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
button, input, select { font-family: var(--font); letter-spacing: inherit; }
body { display: flex; flex-direction: column; }
.bar { display: flex; align-items: center; gap: 8px; padding: 10px 16px; padding-top: max(10px, env(safe-area-inset-top)); }
.tag { color: var(--muted); font-size: 12px; white-space: nowrap; }
@media (max-width: 440px) { .tag { display: none; } }
.bar select { margin-left: auto; background: var(--surface-2); color: var(--fg); border: 1px solid var(--border); border-radius: 8px; padding: 6px 8px; }
#map { position: absolute; inset: 0; z-index: 0; isolation: isolate; } /* markers' z-indexes stay inside the map, under the UI */
#map .leaflet-top, #map .leaflet-bottom { z-index: 3000; } /* zoom + credit over the markers */
.mapwrap { position: relative; flex: 1; }
.status { display: none; padding: 8px 16px; padding-bottom: max(8px, env(safe-area-inset-bottom)); font-size: 12px; color: var(--muted); }
.status.err { color: #ff6b6b; }


.chip { padding: 8px 12px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface-2); color: var(--fg); font: inherit; cursor: pointer; white-space: nowrap; }
.search-results, .stop-list, .departures { list-style: none; margin: 8px 0; padding: 0; display: grid; gap: 4px;
  grid-template-columns: minmax(0, 1fr); /* long names must not stretch the column */ }
.search-results button, .stop-list button, .departures button { width: 100%; display: flex; align-items: center; gap: 10px; text-align: left; padding: 9px 10px;
  border: 0; border-radius: 10px; background: var(--surface-2); color: var(--fg); font: inherit; cursor: pointer; }
.search-results .code, .stop-list .code { color: var(--muted); font-variant-numeric: tabular-nums; min-width: 3.5em; }
.search-results .name, .stop-list .name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stop-list .muted { margin-left: auto; }
#stop-note, #dep-note { padding: 4px 0; }
.stop-pin { font-size: 18px; line-height: 22px; text-align: center; filter: drop-shadow(0 1px 2px rgba(0,0,0,.6)); }
@keyframes pulse { 50% { outline-offset: 5px; } }


/* One bottom sheet, three views */
.sheet { position: absolute; z-index: 1000; overflow-y: auto; overscroll-behavior: contain;
  /* Floating card: inset from the edges so the map shows around it */
  left: 12px; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom));
  min-height: 0; max-height: 45%; /* compact: fits its content, drag the handle up for more */
  background: var(--surface); border: 1px solid var(--border); border-radius: 24px; padding: 0 18px 18px;
  box-shadow: var(--shadow);
  transition: min-height .25s ease, max-height .25s ease; }
.sheet.collapsed { min-height: 0; max-height: 100px; overflow: hidden; }
/* Full: fills the screen, leaving a strip of map at the top so you keep your bearings */
.sheet.full { min-height: calc(100% - 72px); max-height: calc(100% - 72px); }
/* Wider screens: a floating side panel instead of a full-width card */
@media (min-width: 720px) {
  .sheet { left: 16px; right: auto; width: 400px; top: 16px; bottom: 16px; min-height: 0; max-height: none; }
  .sheet.collapsed { top: auto; }
  .sheet.full { min-height: 0; max-height: none; }
}
.handle { touch-action: none; position: sticky; top: 0; display: block; width: 100%; padding: 12px 0 10px; background: var(--surface); border: 0; cursor: pointer; z-index: 1; }
.handle span { display: block; width: 40px; height: 5px; margin: 0 auto; border-radius: 3px; background: var(--handle); }
.view { display: none; }
.sheet[data-view="stops"] .view[data-view="stops"],
.sheet[data-view="departures"] .view[data-view="departures"],
.sheet[data-view="bus"] .view[data-view="bus"] { display: block; animation: slidein .18s ease-out; }
@keyframes slidein { from { opacity: 0; transform: translateY(6px); } }
.view h2 { margin: 0; font-size: 17px; }
.view-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.view-head .grow { flex: 1; min-width: 0; }
.back { background: var(--surface-2); border: 0; color: var(--fg); font-size: 18px; width: 36px; height: 36px; border-radius: 50%; cursor: pointer; flex: none; }
.link { background: none; border: 0; color: var(--accent); font: inherit; cursor: pointer; padding: 4px 0; }
.form-label { font-weight: 600; }
.muted { color: var(--muted); font-size: 12px; }
.report-form { display: grid; gap: 10px; border-top: 1px solid var(--border); padding-top: 12px; }
.post[hidden] { display: none; }
.emojis { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
.emojis button { min-width: 0; overflow: hidden; text-overflow: ellipsis; flex: 1; padding: 8px 6px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface-2); color: var(--fg); font: inherit; cursor: pointer; white-space: nowrap; }
.emojis button { font-size: 14px; }
.emojis button.on { background: #2d7ff9; border-color: #2d7ff9; color: #fff; }
.textrow { position: relative; }
.textrow input { width: 100%; padding: 10px 48px 10px 12px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface-2); color: var(--fg); font: inherit; }
.textrow .count { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: 11px; color: var(--muted); }
.post { width: 100%; padding: 11px; border-radius: 10px; border: 0; background: var(--accent); color: #111; font: 600 15px var(--font); cursor: pointer; }
.post:disabled { opacity: .5; cursor: default; }
.leaflet-top.leaflet-left .leaflet-control-attribution { margin: 6px 0 0 6px; border-radius: 6px; font-size: 10px; max-width: 60vw; }

.home-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.stop-list:empty { display: none; }
.star { background: none; border: 0; color: var(--muted); font-size: 26px; line-height: 1; cursor: pointer; padding: 2px 4px; flex: none; }
.star.on { color: var(--accent); }
body.debug .status { display: block; }

/* Search inside the card: sticks under the handle; results replace the view while typing */
.search-sticky { position: sticky; top: 27px; z-index: 1; background: var(--surface); padding-bottom: 12px; }
.search-field { display: flex; align-items: center; gap: 8px;
  padding: 0 10px 0 12px; border-radius: 14px; background: var(--surface-2); border: 1px solid var(--border); }
.search-field input { flex: 1; min-width: 0; padding: 12px 0; border: 0; background: none; color: var(--fg); font: inherit; font-size: 16px; outline: none; }
.search-field button { background: none; border: 0; color: var(--muted); font-size: 15px; cursor: pointer; padding: 6px; }
.search-field button[hidden] { display: none; }
.search-results { margin: 0 0 8px; }
.search-results:empty { display: none; }
.sheet.searching .view { display: none !important; }

/* Bus markers: scale with zoom (--zs), readable route pill, heading arrow */
.veh-wrap { position: absolute; transform: translate(-50%, -50%) scale(var(--zs, 1)); transition: transform .2s ease; }
.veh { position: relative; }
.veh .dot { min-width: 32px; height: 32px; padding: 0 7px; box-sizing: border-box; border-radius: 16px; background: var(--c, #333); color: var(--t, #fff);
  border: 2.5px solid #fff; display: grid; place-items: center; font: 800 13px/1 var(--font); white-space: nowrap;
  box-shadow: 0 2px 6px rgba(0,0,0,.55); }
.veh.train .dot { border-radius: 8px; }
/* The heading arrow: its base sits at (--ax, --ay) from the pin's centre, just
   off the pill's edge (worked out in app.js), pointing along the heading */
.veh .arrow { position: absolute; left: 50%; top: 50%; width: 0; height: 0; pointer-events: none;
  transform: translate(var(--ax, 0px), var(--ay, -19px)) rotate(var(--b, 0deg)); }
.veh .arrow::before { content: ''; position: absolute; left: -6px; top: -8px;
  border: 6px solid transparent; border-top: 0; border-bottom: 8px solid var(--c, #333); filter: drop-shadow(0 0 1px #fff); }
.veh.selected .dot { outline: 3px solid var(--accent); outline-offset: 2px; animation: pulse 1.4s ease-in-out infinite; }

/* PtvAlert-style vibe bubble floating above a bus */
.vibe-bubble { position: absolute; bottom: calc(100% + 10px); left: 50%; transform: translateX(-50%); white-space: nowrap;
  background: #fff; color: #111; border-radius: 14px; padding: 1px 7px; font-size: 16px; line-height: 24px;
  box-shadow: 0 3px 10px rgba(0,0,0,.45); pointer-events: none; }
.vibe-bubble::after { content: ''; position: absolute; left: 50%; top: 100%; transform: translateX(-50%);
  border: 5px solid transparent; border-top-color: #fff; }
.vibe-bubble small { font: 700 10px var(--font); margin-left: 2px; color: #555; }
.vibe-bubble.stale { opacity: .6; }
/* Plays once, the moment a vibe genuinely first appears (app.js tracks which
   ones it's already shown) — not on every marker redraw, e.g. a bus turning. */
.vibe-bubble.enter { animation: vibe-in .4s cubic-bezier(.34, 1.56, .64, 1); }
@keyframes vibe-in { from { opacity: 0; transform: translateX(-50%) translateY(4px) scale(.7); } }
@media (prefers-reduced-motion: reduce) { .vibe-bubble.enter { animation: none; } }

/* Transit-style departure rows, coloured by route */
.departures { gap: 8px; }
/* Every row is the same height: destination + at most one extra line (the vibe) */
.departures .dep { gap: 12px; padding: 10px 14px; border-radius: 14px; background: var(--rc); color: var(--rt); height: 84px; overflow: hidden; }
.dep-route { font: 800 26px/1 var(--font); min-width: 1.4em; flex: none; }
.dep-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.dep-dest { font-weight: 600; font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dep-vibe { display: flex; align-items: center; gap: 6px; font-size: 13px; background: rgba(0,0,0,.28); color: #fff;
  border-radius: 8px; padding: 3px 8px; min-width: 0; }
.dep-vibe .e { font-size: 15px; flex: none; }
.dep-vibe .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.dep-vibe .ago { opacity: .75; white-space: nowrap; flex: none; }
.dep-eta { flex: none; display: flex; flex-direction: column; align-items: flex-end; line-height: 1; gap: 3px; min-width: 3.4em; }
.dep-eta b { font: 800 26px/1 var(--font); font-variant-numeric: tabular-nums; }
.dep-eta small { font-size: 11px; opacity: .85; }
/* Live: a dot with a ripple (livestream-style). Untracked: no dot, lighter "~25" */
.eta-top { display: inline-flex; align-items: center; gap: 7px; }
/* Bright green with a white ring so it reads on every route colour, greens included */
.ping { position: relative; width: 10px; height: 10px; border-radius: 50%; background: #2ee66b; box-shadow: 0 0 0 2px #fff; flex: none; }
.ping::after { content: ''; position: absolute; inset: -2px; border-radius: 50%; border: 2px solid #2ee66b; animation: ping 1.8s ease-out infinite; }
@keyframes ping { from { transform: scale(1); opacity: .9; } to { transform: scale(2.8); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .ping::after { animation: none; opacity: 0; } }
.dep-eta.untracked b { font-weight: 500; opacity: .8; }
.dep-eta.untracked small { opacity: .7; }

/* Punctuality pill under the countdown */
.punct { margin-top: 2px; font-size: 11px; font-weight: 700; white-space: nowrap; border-radius: 999px; padding: 2px 7px; background: rgba(0,0,0,.35); }
.punct.ontime { color: #7ee787; }
.punct.late { color: #ffb86b; }
.punct.early { color: #8cc8ff; }
.dep.cancelled { opacity: .6; }
.cancelled-eta s { font: 700 18px/1 var(--font); font-variant-numeric: tabular-nums; text-decoration-thickness: 2px; }
.punct.cancel { color: #ff8a8a; margin-top: 6px; }

/* Feedback cards on the bus view */
.section-label { margin: 14px 0 8px; font-size: 12px; letter-spacing: .01em; color: var(--muted); }
.tally { display: flex; flex-wrap: wrap; gap: 6px; }
.tally:empty { display: none; }
.tally-chip { background: var(--surface-3); border-radius: 999px; padding: 4px 11px; font-size: 18px; }
.tally-chip b { font-size: 13px; }


/* Seat meter from Metlink occupancy */
.occ { display: inline-flex; align-items: center; align-self: flex-start; background: rgba(0,0,0,.28); border-radius: 999px; padding: 4px 8px; }
.occ .bars { display: inline-flex; align-items: flex-end; gap: 2px; height: 13px; }
.occ .bars i { width: 5px; border-radius: 1px; background: rgba(255,255,255,.3); }
.occ .bars i:nth-child(1) { height: 6px; } .occ .bars i:nth-child(2) { height: 9px; } .occ .bars i:nth-child(3) { height: 13px; }
.occ .bars i.on { background: #7ee787; }
.occ-few .bars i.on { background: #ffd33d; }
.occ-standing .bars i.on { background: #ff9b3d; }
.occ-full .bars i.on { background: #ff5c5c; }
.dep-line { display: flex; align-items: center; gap: 8px; min-width: 0; }
.dep-line .occ { flex: none; align-self: center; }

/* Light theme: explicit choice, or the OS setting when no choice was made */
:root[data-theme="light"] { color-scheme: light;
  --bg: #f8f9fa; --fg: #000000; --muted: #6c757d; --accent: #fec940; --accent-fg: #b88a00;
  --surface: #ffffff; --surface-2: #f8f9fa; --surface-3: #e9ecef; --border: #dee2e6; --handle: #adb5bd;
  --outline: #000000;
  --shadow: 0 12px 36px rgba(20,30,40,.18), 0 2px 8px rgba(20,30,40,.10); --ok: #1a8a4a; --late: #b85a00; --early: #1f6fc4;
  --occ-bg: #e9ecef; --occ-off: rgba(0,0,0,.16);
  --tab-on-bg: #ffffff; --tab-on-fg: #111;
  --occ-seats: #1a8a4a; --occ-few: #9a7200; --occ-standing: #c05a00; --occ-full: #c62828; }
:root[data-theme="light"] .star.on { color: var(--accent-fg); }
:root[data-theme="light"] .link { color: var(--accent-fg); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) { color-scheme: light;
    --bg: #f8f9fa; --fg: #000000; --muted: #6c757d; --accent: #fec940; --accent-fg: #b88a00;
    --surface: #ffffff; --surface-2: #f8f9fa; --surface-3: #e9ecef; --border: #dee2e6; --handle: #adb5bd;
    --outline: #000000;
    --shadow: 0 12px 36px rgba(20,30,40,.18), 0 2px 8px rgba(20,30,40,.10); --ok: #1a8a4a; --late: #b85a00; --early: #1f6fc4;
  --occ-bg: #e9ecef; --occ-off: rgba(0,0,0,.16);
  --tab-on-bg: #ffffff; --tab-on-fg: #111;
  --occ-seats: #1a8a4a; --occ-few: #9a7200; --occ-standing: #c05a00; --occ-full: #c62828; }
  :root:not([data-theme="dark"]) .star.on { color: var(--accent-fg); }
  :root:not([data-theme="dark"]) .link { color: var(--accent-fg); }
}

/* Theme toggle in the header */
.theme-toggle { background: var(--surface-2); border: 1px solid var(--border); color: var(--fg); border-radius: 8px; width: 34px; height: 32px; cursor: pointer; font-size: 15px; flex: none; }

/* Bus view: the tapped route card, opened up. The top of the sheet (handle
   included) takes the route colour full-width, then bleeds down into the card. */
.handle { margin: 0 -18px; width: calc(100% + 36px); }
.sheet[data-view="bus"] .handle { background: transparent; z-index: 5; } /* sits over the pinned top */
.sheet[data-view="bus"] .handle span { background: rgba(0,0,0,.28); }
.sheet[data-view="bus"] .search-sticky, .sheet[data-view="bus"] .search-results { display: none; } /* no search inside a bus */

/* Pinned top of the bus view: route colour (under the handle too), header,
   stats, then the colour eases into the card around the tabs. Content scrolls
   underneath and fades out just below the tabs. */
.bus-top { position: sticky; top: 0; z-index: 4; margin: -27px -18px 0; padding-top: 27px; background: var(--rc); }
.bus-top::after { content: ''; position: absolute; left: 0; right: 0; top: 100%; height: 26px; pointer-events: none;
  background: linear-gradient(to bottom, var(--surface), transparent); }
.bus-hero { padding: 0 18px 12px; color: var(--rt);
  display: grid; gap: 10px; grid-template-columns: auto minmax(0, 1fr); align-items: center; }
.bus-tabs { padding: 10px 18px 12px; background: linear-gradient(to bottom,
    var(--rc) 0%,
    color-mix(in srgb, var(--rc) 70%, var(--surface)) 30%,
    color-mix(in srgb, var(--rc) 38%, var(--surface)) 60%,
    color-mix(in srgb, var(--rc) 12%, var(--surface)) 85%,
    var(--surface) 100%); }
.bus-hero .back { background: rgba(0,0,0,.18); color: inherit; width: 30px; height: 30px; font-size: 15px; }
.hero-main { display: flex; align-items: center; gap: 12px; min-width: 0; }
.hero-route { font: 800 32px/1 var(--font); letter-spacing: -1px; flex: none; }
.bus-hero h2 { font-size: 17px; line-height: 1.15; margin: 0; min-height: 1.15em; }
.hero-meta { font-size: 11px; opacity: .75; margin-top: 2px; letter-spacing: .01em; font-variant-numeric: tabular-nums; }

/* Bus dashboard: compact 2x2 chips between the header and the vibes */
.dash { position: relative; z-index: 1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; margin-top: 12px; }
.tile { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 6px 10px 7px; min-width: 0;
  box-shadow: 0 1px 6px rgba(0,0,0,.10); }
.tile .k { font-size: 11px; letter-spacing: .01em; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tile .v { font-size: 14px; font-weight: 700; line-height: 1.25; margin-top: 1px; display: flex; align-items: center; gap: 5px; min-width: 0; white-space: nowrap; }
.tile .v .main { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.tile .v .tick { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
/* Too long for the chip: scroll to the end and back, pausing at each end */
.tile .main.ticker { text-overflow: clip; }
.tile .main.ticker .tick { animation: ticker var(--dur, 6s) ease-in-out infinite alternate; }
@keyframes ticker { 0%, 18% { transform: translateX(0); } 82%, 100% { transform: translateX(var(--dist, 0)); } }
@media (prefers-reduced-motion: reduce) {
  .tile .main.ticker { text-overflow: ellipsis; }
  .tile .main.ticker .tick { animation: none; display: inline; }
}
.tile .v .s { flex: none; font-weight: 500; color: var(--muted); } /* detail always visible; the name truncates */
/* Seats chip: pill and label follow the theme; label takes the status colour */
.tile .occ { background: var(--occ-bg); padding: 2px 5px; flex: none; }
.tile .occ .bars i { background: var(--occ-off); }
.tile .occ-seats .bars i.on { background: var(--occ-seats); } .tile .occ-few .bars i.on { background: var(--occ-few); }
.tile .occ-standing .bars i.on { background: var(--occ-standing); } .tile .occ-full .bars i.on { background: var(--occ-full); }
.occ-t-seats { color: var(--occ-seats); } .occ-t-few { color: var(--occ-few); }
.occ-t-standing { color: var(--occ-standing); } .occ-t-full { color: var(--occ-full); }
.tile .occ .bars { height: 10px; } .tile .occ .bars i { width: 4px; }
.tile .occ .bars i:nth-child(1) { height: 4px; } .tile .occ .bars i:nth-child(2) { height: 7px; } .tile .occ .bars i:nth-child(3) { height: 10px; }
.tile.punct-ontime .v { color: var(--ok); } .tile.punct-late .v { color: var(--late); } .tile.punct-early .v { color: var(--early); }

/* Vibe rows on the bus view */
.vibes { list-style: none; margin: 10px 0 14px; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; }
.vibes li { display: flex; gap: 12px; align-items: flex-start; background: var(--surface-2); border-radius: 14px; padding: 11px 8px 11px 12px; }
.vibes li.stale { opacity: .55; }
.vibes .e { font-size: 26px; line-height: 1; flex: none; }
.vibes .t { flex: 1; min-width: 0; }
.vibes .txt { font-size: 15px; line-height: 1.3; overflow-wrap: break-word; }
.vibes .meta { font-size: 12px; color: var(--muted); margin-top: 2px; }
.vibes .flag { width: 30px; height: 30px; flex: none; display: grid; place-items: center; border: 0; border-radius: 50%; background: none;
  color: var(--muted); cursor: pointer; }
.vibes .flag:hover { color: var(--fg); background: var(--surface-3); }
.vibes .empty { background: none; padding: 0; color: var(--muted); }

/* Tabs in the bus view: Vibes / Stops */
.tabs { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin: 10px 0 8px; padding: 4px; border-radius: 12px; background: var(--surface-2); border: 1px solid var(--border); }
.tabs button { border: 0; border-radius: 9px; padding: 6px; background: none; color: var(--muted); font: 600 14px var(--font); cursor: pointer; }
.tabs button[aria-selected="true"] { background: var(--tab-on-bg); color: var(--tab-on-fg); box-shadow: 0 1px 4px rgba(0,0,0,.18); }
.tabs .count { font-weight: 500; color: var(--muted); }
.view[data-view="bus"]:not([data-tab="stops"]) .tabpanel[data-tab="stops"],
.view[data-view="bus"][data-tab="stops"] .tabpanel[data-tab="vibes"] { display: none; }

/* Stops tab: a metro-map line in the route colour */
.trip-stops { list-style: none; margin: 4px 0 8px; padding: 0; }
.trip-stops button { width: 100%; display: grid; grid-template-columns: 62px 22px minmax(0, 1fr) auto auto; align-items: center; gap: 0 8px;
  min-height: 42px; padding: 0 4px; border: 0; background: none; color: var(--fg); font: inherit; text-align: left; cursor: pointer; border-radius: 10px; }
.trip-stops button:hover { background: var(--surface-2); }
.trip-stops .time { font-size: 13px; font-variant-numeric: tabular-nums; text-align: right; line-height: 1.1; }
.trip-stops .time s { display: block; font-size: 11px; color: var(--muted); }
.trip-stops .node { position: relative; align-self: stretch; }
.trip-stops .node::before { content: ''; position: absolute; left: 50%; top: 0; bottom: 0; width: 4px; transform: translateX(-50%); background: var(--rc); }
.trip-stops li:first-child .node::before { top: 50%; }
/* Earlier stops hidden: the line fades in from above to show the route continues */
.trip-stops.truncated li:first-child .node::before { top: 0; background: linear-gradient(to bottom, transparent, var(--rc) 50%); }
.trip-stops li:last-child .node::before { bottom: 50%; }
.trip-stops .node::after { content: ''; position: absolute; left: 50%; top: 50%; width: 12px; height: 12px; transform: translate(-50%, -50%);
  border-radius: 50%; background: var(--surface); border: 3px solid var(--rc); box-sizing: border-box; }
.trip-stops .stop-name { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.trip-stops li.passed { opacity: .45; }
.trip-stops li.passed .node::after { background: var(--rc); }
.trip-stops li.next .node::after { width: 18px; height: 18px; background: var(--rc); border-color: #fff; box-shadow: 0 0 0 3px var(--rc); animation: nodepulse 1.8s ease-out infinite; }
@keyframes nodepulse { 0% { box-shadow: 0 0 0 3px var(--rc); } 70% { box-shadow: 0 0 0 10px transparent; } 100% { box-shadow: 0 0 0 3px var(--rc); } }
@media (prefers-reduced-motion: reduce) { .trip-stops li.next .node::after { animation: none; } }
.trip-stops li.next .stop-name, .trip-stops li.mine .stop-name { font-weight: 700; }
.trip-stops .stop-tag { font-size: 11px; font-weight: 700; border-radius: 999px; padding: 2px 7px; margin-left: 4px; white-space: nowrap; }
.trip-stops .stop-tag.now { background: var(--rc); color: var(--rt); }
.trip-stops .stop-tag.you { background: var(--accent); color: #111; }
#trip-note { margin: 4px 0 8px; }

/* Header status (timing, seats): no cards, straight on the route colour */
.bus-hero .dash { grid-column: 1 / -1; margin: 2px 0 0; gap: 4px 16px; }
.bus-hero .tile { background: none; border: 0; box-shadow: none; padding: 0; }
.bus-hero .tile .k { color: inherit; opacity: .75; font-size: 11px; }
.bus-hero .tile .v { color: inherit; font-size: 15px; }
.bus-hero .tile .occ { background: rgba(0,0,0,.28); }
.bus-hero .tile .occ .bars i { background: rgba(255,255,255,.3); }
.bus-hero .tile .occ-seats .bars i.on { background: #7ee787; } .bus-hero .tile .occ-few .bars i.on { background: #ffd33d; }
.bus-hero .tile .occ-standing .bars i.on { background: #ff9b3d; } .bus-hero .tile .occ-full .bars i.on { background: #ff6b6b; }
.status-dot { width: 10px; height: 10px; border-radius: 50%; flex: none; margin: 0 2px 0 2px; box-shadow: 0 0 0 2px rgba(255,255,255,.85); }
.bus-hero .tile .v .main { overflow: visible; } /* short values; don't clip the dot's ring */
.status-dot.ontime { background: #2ee66b; } .status-dot.late { background: #ff9b3d; } .status-dot.early { background: #4da3ff; }

/* Tabs on the route colour: translucent dark track, white active pill with
   the route colour as its text (same in both themes — they always sit on colour) */
.bus-tabs .tabs { margin: 0; background: color-mix(in srgb, var(--rc) 62%, #000); border-color: transparent; } /* solid, so inactive text reads the same in both themes */
.bus-tabs .tabs button { color: rgba(255,255,255,.88); }
.bus-tabs .tabs .count { color: inherit; opacity: .8; }
.bus-tabs .tabs button[aria-selected="true"] { background: #fff; color: var(--rc); box-shadow: 0 2px 8px rgba(0,0,0,.25); }
.view[data-view="bus"] > .tabpanel { padding-top: 6px; }

/* ---- Backseat design language -------------------------------------------
   Bold outlines (3px buttons, 2px cards/inputs), 14px corners on controls and
   25px on cards, the yellow primary button with a lift + hard shadow on hover,
   bold condensed type. Mirrors PostButton / FilterButton / TransportCard. */
.sheet { border: 2px solid var(--outline); border-radius: 25px; }
.search-field { border: 2px solid var(--outline); border-radius: 14px; background: var(--surface); }
.chip, .theme-toggle, .bar select { border: 2px solid var(--outline); border-radius: 14px; background: var(--surface); color: var(--fg); font-weight: 700; }
.bar select { padding: 6px 10px; }
.theme-toggle { width: 38px; height: 34px; }
.view > .view-head .back, #stop-back { border: 2px solid var(--outline); background: var(--surface); }
.departures .dep { border: 2px solid var(--outline); border-radius: 20px; }
.vibes li { border: 2px solid var(--outline); border-radius: 18px; background: var(--surface); }
.vibes .txt { font-weight: 600; font-size: 16px; }
.emojis button { border: 2px solid var(--outline); border-radius: 14px; background: var(--surface); font-weight: 700; padding: 10px 6px; }
.emojis button.on { background: var(--accent); border-color: var(--outline); color: #000; }
.textrow input { border: 2px solid var(--outline); border-radius: 14px; background: var(--surface); padding-top: 12px; padding-bottom: 12px; }
.post { border: 3px solid var(--outline); border-radius: 14px; background: var(--accent); color: #000; font: 700 16px var(--font);
  height: 48px; transition: transform .2s ease, box-shadow .2s ease; }
.post:not(:disabled):hover { transform: translateY(-2px); box-shadow: 0 2px 0 0 var(--outline); }
.post:disabled { background: var(--surface-3); color: var(--muted); opacity: 1; }
.chip, .emojis button, .theme-toggle { transition: transform .2s ease, box-shadow .2s ease; }
.chip:hover, .emojis button:hover, .theme-toggle:hover { transform: translateY(-1px); box-shadow: 0 1px 0 0 var(--outline); }
.view h2, .form-label, .home-row h2 { font-weight: 700; }
.tally-chip { border: 2px solid var(--outline); background: var(--surface); }

/* Header: the animated Backseat logo + wordmark */
.brand { display: inline-flex; align-items: center; gap: 8px; color: var(--fg); }
.brand svg { width: 30px; height: 34px; flex: none; }
.brand b { font: 700 24px/1 var(--font); letter-spacing: -0.03em; }
.brand { flex: none; }

/* No header bar: the brand sits in a floating pill top-left over the map,
   the theme toggle in a matching circle top-right. */
.bar { position: fixed; top: 0; left: 0; right: 0; z-index: 1000; pointer-events: none; justify-content: space-between;
  padding: 12px 14px; padding-top: var(--top-gap); background: none; }
.bar > * { pointer-events: auto; }
.brand { background: var(--surface); border: 2px solid var(--outline); border-radius: 999px; padding: 4px 14px 4px 8px; gap: 6px;
  box-shadow: 0 2px 0 0 var(--outline); }
.brand svg { width: 24px; height: 28px; }
.brand b { font-size: 19px; }
.theme-toggle { border-radius: 999px; width: 40px; height: 40px; box-shadow: 0 2px 0 0 var(--outline); }
@media (max-width: 380px) { .brand b { font-size: 19px; } .brand svg { width: 24px; height: 28px; } }
/* Map controls tuck under the floating pill (left) and theme toggle (right) */
.leaflet-top.leaflet-left .leaflet-control-attribution { margin: calc(var(--top-gap) + 48px) 0 0 14px; max-width: calc(100vw - 90px); }
.leaflet-top.leaflet-right .leaflet-control-zoom { margin: calc(var(--top-gap) + 52px) 14px 0 0; }
.leaflet-control-zoom { border: 2px solid var(--outline) !important; border-radius: 14px !important; overflow: hidden; box-shadow: 0 2px 0 0 var(--outline) !important; }
.leaflet-control-zoom a { background: var(--surface) !important; color: var(--fg) !important; border-color: var(--outline) !important; }
/* Empty states: plain muted text, no card */
.vibes li.empty { border: 0; background: none; padding: 14px 0 6px; display: block; text-align: center; color: var(--muted); font-size: 14px; }
/* Leave room for the floating pill */
/* Full card stops just below the logo pill: take off the pill (--top-gap + ~52px)
   and the card's own bottom offset (12px + home-bar inset) */
.sheet { --full-h: calc(100% - var(--top-gap) - 52px - 12px - env(safe-area-inset-bottom)); }
.sheet.full { min-height: var(--full-h); max-height: var(--full-h); }
@media (min-width: 720px) { .sheet { top: calc(var(--top-gap) + 60px); } .sheet.full { min-height: 0; max-height: none; } }
/* Hints in lists line up with the other helper text */
.stop-list > li.muted { margin-left: 0; }
.stop-list > li.muted, #stop-note, #dep-note, #trip-note { font-size: 13px; }
.leaflet-container .leaflet-control-attribution { background: color-mix(in srgb, var(--surface) 80%, transparent); color: var(--muted); border-radius: 8px; padding: 1px 6px; }
.leaflet-container .leaflet-control-attribution a { color: inherit; }

/* Clear the notch and iOS Safari's blurred status bar, which extends a bit
   past the safe area. On screens with no notch this is just 12px. */
:root { --status-h: env(safe-area-inset-top, 0px); --top-gap: max(12px, calc(env(safe-area-inset-top) + 12px)); }
/* Home-screen app (iOS 26+): the status bar blur runs deeper than the
   reported safe area, and some versions report 0. Keep a generous floor. */
@media (display-mode: standalone), (display-mode: fullscreen) {
  /* Dynamic Island phones report ~59px; the blur runs ~12px past that */
  :root { --status-h: max(62px, calc(env(safe-area-inset-top) + 12px)); --top-gap: calc(var(--status-h) + 18px); }
  /* No browser bars, so 100vh is the whole screen (100% can stop a status
     bar's height short of the bottom on older iOS) */
  html, body { height: 100vh; }
}
/* Feel like an app: no grey flash on tap, no long-press callout on buttons and the map */
html { -webkit-tap-highlight-color: transparent; }
button, a, .map-marker, #map { -webkit-touch-callout: none; }
/* Map is covered when the card is full: hide the credits rather than show them half-clipped */
body:has(#sheet.full) .leaflet-control-zoom { visibility: hidden; }

/* Busy badge: a small person on the pin's lower-right, only when standing
   (orange) or full (red). White edge so it separates from any route colour. */
.veh .dot { position: relative; }
.busy-badge { position: absolute; right: -7px; bottom: -6px; width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid #fff; display: grid; place-items: center; box-shadow: 0 1px 2px rgba(0,0,0,.45); }
.busy-badge svg { width: 7px; height: 10px; fill: #111; }
.busy-badge.seats { background: #7ee787; } .busy-badge.few { background: #ffd33d; }
.busy-badge.standing { background: #ff9b3d; } .busy-badge.full { background: #ff5c5c; }
/* In rows and the bus card: same badge, in the text flow */
.busy-badge.inline { position: static; width: 20px; height: 20px; flex: none; align-self: center; }
.busy-badge.inline svg { width: 8px; height: 12px; }

/* Vibe composer: pinned to the bottom of the card like a chat box. The
   negative margins/bottom cancel the sheet's 18px side and bottom padding. */
.composer { position: sticky; bottom: -18px; z-index: 3; margin: 14px -18px -18px; padding: 10px 18px 12px;
  background: var(--surface); border-top: 2px solid var(--outline); display: grid; gap: 6px; }
.composer-bar { display: flex; align-items: center; gap: 8px; }
.composer-bar input { flex: 1; min-width: 0; height: 46px; padding: 0 14px; border: 2px solid var(--outline); border-radius: 23px;
  background: var(--surface); color: var(--fg); font: 500 16px var(--font); } /* 16px stops iOS zooming in */
.pick-btn, .send { flex: none; width: 46px; height: 46px; border-radius: 50%; border: 2px solid var(--outline); cursor: pointer;
  display: grid; place-items: center; font-size: 24px; line-height: 1; padding: 0; background: var(--surface); color: var(--fg); }
/* No emoji yet: a dashed smiley slot that says "pick me" */
.pick-btn.empty { border-style: dashed; }
.pick-btn.empty { color: var(--muted); }
.composer.open .pick-btn { background: var(--accent); color: #000; }
.send { background: var(--accent); color: #000; font: 800 22px/1 var(--font); border-width: 3px; }
.send:disabled { background: var(--surface-3); color: var(--muted); cursor: default; }
.composer-note { font-size: 12px; text-align: center; min-height: 16px; }

/* The palette: grouped rows of six, outlined tiles like the main app's picker */
.picker { display: grid; gap: 10px; padding: 4px 0 6px; }
.picker[hidden] { display: none; }
.picker-head { display: flex; align-items: baseline; justify-content: space-between; font-size: 15px; }
.pick-name { font-size: 12px; font-weight: 700; color: var(--muted); margin: 4px 0 5px; }
.pick-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 6px; }
.pick-grid button { aspect-ratio: 1; border: 2px solid var(--outline); border-radius: 14px; background: var(--surface); cursor: pointer;
  display: grid; place-items: center; padding: 0; transition: transform .15s ease; }
.pick-grid button span { font-size: 26px; line-height: 1; }
.pick-grid button small { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); } /* for screen readers */
/* Suggestions: one row of the most-used emojis lately, emoji only */
.pick-row { grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 5px; }
.pick-row button { border-radius: 12px; font-size: 24px; line-height: 1; }
@media (max-width: 360px) { .pick-row button { font-size: 20px; } }
/* The emoji slot is a text field that only takes one emoji: the smile icon
   shows while it's empty, the typed/picked emoji fills it after */
.pick-btn { position: relative; overflow: hidden; }
.pick-btn .pick-ico { pointer-events: none; }
.pick-btn:not(.empty) .pick-ico { display: none; }
.composer-bar .pick-btn input { position: absolute; inset: 0; width: 100%; height: 100%; flex: none; margin: 0; padding: 0; border: 0;
  border-radius: 50%; background: transparent; color: var(--fg); font: 24px/1 var(--font); text-align: center; caret-color: transparent; }
.composer-bar .pick-btn input:focus { outline: none; }
.pick-btn:focus-within { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 70%, transparent); }
.pick-btn.bad { animation: emoji-nope .35s ease; border-color: #e5484d; }
@keyframes emoji-nope { 20%, 60% { transform: translateX(-4px); } 40%, 80% { transform: translateX(4px); } }
@media (prefers-reduced-motion: reduce) { .pick-btn.bad { animation: none; } }
/* Send with no emoji: looks not-ready, but still tappable (it nudges you) */
.send.waiting { background: var(--surface-3); color: var(--muted); }
.composer-note.nudge { color: var(--fg); font-weight: 700; font-size: 13px; }
.pick-grid button:active { transform: scale(.92); }
.pick-grid button.on { background: var(--accent); }
.composer { grid-template-columns: minmax(0, 1fr); } /* the input's natural width mustn't widen the column */
/* Full-height bus card: stretch the vibes tab so the composer sits at the very
   bottom even when there are only a couple of vibes above it */
.sheet.full[data-view="bus"] { display: flex; flex-direction: column; }
.sheet.full[data-view="bus"] .view[data-view="bus"] { display: flex; flex-direction: column; flex: 1 0 auto; }
.sheet.full[data-view="bus"] .view[data-view="bus"]:not([data-tab="stops"]) .tabpanel[data-tab="vibes"] { display: flex; flex-direction: column; flex: 1 0 auto; }
.sheet.full[data-view="bus"] .composer { margin-top: auto; }

/* Favourite heart (replaces the star): outlined when off, filled when on */
.star { padding: 4px; transition: transform .15s ease; }
.star:active { transform: scale(.85); }
.stop-list .icon .heart { width: 18px; height: 18px; stroke-width: 2.4; }
.stop-list li.muted .heart { display: inline-block; width: 15px; height: 15px; vertical-align: -3px; stroke: var(--muted); }
/* Your stops / search rows: outlined cards in the Backseat style */
.search-results button, .stop-list button { border: 2px solid var(--outline); border-radius: 14px; background: var(--surface); padding: 11px 12px; font-weight: 600; }
.search-results .code, .stop-list .code { font-weight: 500; }
.stop-list, .search-results { gap: 8px; }

/* SVG icon set (icons.js): sized by font-size, coloured by currentColor */
.ico { width: 1.15em; height: 1.15em; display: inline-block; vertical-align: -0.2em; fill: none; stroke: currentColor;
  stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; flex: none; }
.back, #search-clear, .send, .pick-btn, .theme-toggle, .flag, .star { display: inline-grid; place-items: center; }
.back .ico { width: 20px; height: 20px; stroke-width: 2.6; }
.bus-hero .back .ico { width: 17px; height: 17px; }
.send .ico { width: 22px; height: 22px; stroke-width: 3; }
.pick-btn .ico { width: 26px; height: 26px; }
.theme-toggle .ico { width: 20px; height: 20px; }
.flag .ico { width: 16px; height: 16px; }
.search-ico { display: inline-grid; color: var(--muted); } .search-ico .ico { width: 20px; height: 20px; }
#search-clear .ico { width: 16px; height: 16px; stroke-width: 2.6; }
.chip { display: inline-flex; align-items: center; gap: 6px; } .chip .ico { width: 18px; height: 18px; }
.vibes .meta .ico { width: 13px; height: 13px; vertical-align: -2px; }
.composer-note .ico, .departures .muted .ico { width: 14px; height: 14px; vertical-align: -2px; }
.leaflet-control-zoom a { display: grid !important; place-items: center; } .leaflet-control-zoom .ico { width: 16px; height: 16px; stroke-width: 2.6; }

/* Favourite heart: outlined when off, filled when on */
.heart { width: 26px; height: 26px; stroke-width: 2.2; }
.heart.on { fill: #ff4d6d; stroke: var(--outline); }
[hidden] { display: none !important; } /* icon buttons set display; hidden must still win */
/* Bus header: the route number is the hero */
.hero-route { font: 800 50px/0.9 var(--font); letter-spacing: -2px; }
.bus-hero h2 { font-size: 19px; }

/* Favourite routes: chips in the route colour on the home view */
.routes-head { font-size: 17px; margin: 14px 0 8px; }
.route-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.route-chips .muted { margin: 0; font-size: 13px; }
.route-chips .muted .heart { width: 15px; height: 15px; vertical-align: -3px; stroke: var(--muted); }
.route-chip { display: inline-flex; align-items: center; gap: 6px; min-width: 52px; height: 42px; justify-content: center; padding: 0 14px;
  border-radius: 21px; border: 2px solid var(--outline); background: var(--rc); color: var(--rt); font: 800 19px/1 var(--font); cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease; }
.route-chip:active { transform: scale(.94); }
/* Showing only this route on the map: lifted, with a ✕ to go back to all */
.route-chip.on { box-shadow: 0 3px 0 0 var(--outline); transform: translateY(-2px); outline: 3px solid var(--accent); outline-offset: 2px; }
.route-chip .ico { width: 15px; height: 15px; stroke-width: 3; }
/* Heart in the bus header, on the route colour */
.bus-hero .route-fav { color: inherit; margin-left: auto; align-self: center; }
.bus-hero .route-fav .heart { stroke: currentColor; }
.bus-hero .route-fav .heart.on { fill: #ff4d6d; stroke: #fff; }
.routes-head { margin: 4px 0 8px; }
.route-chips { padding: 5px 5px 4px; margin: -5px -5px 12px; } /* room for the active chip's ring */
/* On the route colour, a filled heart uses the header's text colour so it reads on any route */
.bus-hero .route-fav .heart.on { fill: currentColor; stroke: currentColor; }

/* Collapsed bus card: just back, route number, destination (and the heart).
   Timing/seats, tabs and the composer wait until the card is opened. */
.sheet.collapsed[data-view="bus"] { max-height: none; }
.sheet.collapsed[data-view="bus"] #dash,
.sheet.collapsed[data-view="bus"] .bus-tabs,
.sheet.collapsed[data-view="bus"] .tabpanel { display: none !important; }
.sheet.collapsed[data-view="bus"] .bus-hero { padding-bottom: 16px; }
.sheet.collapsed[data-view="bus"] { padding-bottom: 0; background: var(--rc); }
.sheet.collapsed[data-view="bus"] .bus-top::after { display: none; }

/* The page itself never scrolls (only the card does). Pinning body to the
   visible screen avoids iOS home-screen apps sizing it taller than the
   screen, which left a blank band at the bottom. */
html, body { overflow: hidden; overscroll-behavior: none; }
body { position: fixed; inset: 0; height: auto; }

/* CARTO Dark Matter is near-black. Lower contrast lifts its black to charcoal
   (about the card's own dark grey) and makes roads/land easier to read. */
.dark-tiles .leaflet-tile-pane { filter: contrast(.8) brightness(1.25); }

/* Vibes tab: more room for entries */
/* Header fade only once content has scrolled under the pinned header */
.bus-top::after { opacity: 0; transition: opacity .15s ease; }
.sheet.scrolled .bus-top::after { opacity: 1; }
/* Slimmer composer so it covers less of the list */
.composer { padding: 8px 18px 10px; gap: 4px; }
.composer-bar input { height: 42px; border-radius: 21px; }
.pick-btn, .send { width: 42px; height: 42px; }
.composer-note[hidden] { display: none; }
/* A bus card at normal size is a bit taller than other views, so a couple of vibes fit above the bar */
@media (max-width: 719px) { .sheet[data-view="bus"]:not(.full):not(.collapsed) { max-height: 48%; } }
/* Your new vibe flashes in */
@keyframes just-posted { from { background: var(--accent); } }
.vibes li.just-posted { animation: just-posted 1.4s ease-out; }

/* Map credits: small, centred in the strip under the card. The card sits a
   little higher (20px + home-bar inset) to make room. */
:root { --card-bottom: calc(20px + env(safe-area-inset-bottom)); }
.sheet { bottom: var(--card-bottom); --full-h: calc(100% - var(--top-gap) - 52px - var(--card-bottom)); }
.leaflet-bottom.leaflet-right { left: 0; right: 0; display: flex; justify-content: center; pointer-events: none; }
.leaflet-container .leaflet-bottom .leaflet-control-attribution { float: none; margin: 0 0 calc(env(safe-area-inset-bottom) + 4px) !important;
  padding: 0; background: none; border-radius: 0; font-size: 9px; line-height: 12px; max-width: none; white-space: nowrap;
  color: var(--muted); text-shadow: 0 0 3px var(--bg), 0 0 3px var(--bg); pointer-events: auto; }
@media (min-width: 720px) {
  .sheet { bottom: 16px; }
  .leaflet-bottom.leaflet-right { justify-content: flex-end; padding-right: 12px; }
}

/* Home tabs: Stops | Vibes */
.home-tabs { margin: 2px 0 12px; border: 2px solid var(--outline); border-radius: 14px; background: var(--surface); }
.home-tabs button { font-weight: 700; font-size: 15px; padding: 7px; }
.home-tabs button[aria-selected="true"] { background: var(--accent); color: #000; box-shadow: none; }
.home-tabs button[aria-selected="true"] .count { color: #000; opacity: .7; }

/* Feed: square cards in the route colour, two per row (like the original app) */
.feed { list-style: none; margin: 0 0 6px; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.vcard { aspect-ratio: 1; box-sizing: border-box; display: flex; flex-direction: column; gap: 6px; padding: 10px 10px 11px 12px;
  border: 2px solid var(--outline); border-radius: 25px; background: var(--rc); color: var(--rt); overflow: hidden; }
.vcard.live { cursor: pointer; transition: transform .15s ease, box-shadow .15s ease; }
.vcard.live:hover { transform: translateY(-2px); box-shadow: 0 2px 0 0 var(--outline); }
.vcard.live:active { transform: scale(.97); }
.vcard.stale { opacity: .6; }
/* Same idea as .vibe-bubble.enter: only the genuinely-new card in a
   freshly-rendered feed gets this, not the whole list on every refresh. */
.vcard.enter { animation: vcard-in .38s cubic-bezier(.34, 1.56, .64, 1); }
@keyframes vcard-in { from { opacity: 0; transform: translateY(8px) scale(.96); } }
@media (prefers-reduced-motion: reduce) { .vcard.enter { animation: none; } }
.vcard header { display: flex; align-items: center; gap: 5px; min-width: 0; }
.vc-route { font: 800 26px/1 var(--font); letter-spacing: -1px; flex: none; }
.vc-dest { font-size: 12px; line-height: 1.1; opacity: .8; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vcard .flag { color: inherit; opacity: .7; width: 28px; height: 28px; margin: -4px -2px 0 0; }
.vcard .flag:hover { opacity: 1; background: rgba(0,0,0,.18); }
.vc-body { flex: 1; min-height: 0; margin: 0; display: flex; align-items: center; gap: 4px; }
.vc-text { flex: 1; min-width: 0; font: 700 16px/1.08 var(--font); letter-spacing: -0.02em; overflow-wrap: break-word;
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
.vc-emoji { flex: none; font-size: 34px; line-height: 1; }
.vc-body.bare { justify-content: center; } /* an emoji on its own: centred, bigger */
.vc-body.bare .vc-emoji { font-size: 52px; }
.vcard footer { font-size: 11px; line-height: 1.2; opacity: .85; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vcard footer .ico { width: 11px; height: 11px; vertical-align: -1px; }
.feed-empty { grid-column: 1 / -1; text-align: center; color: var(--muted); font-size: 14px; padding: 24px 0 18px; line-height: 1.5; }

/* ---- Dark mode: true black UI (the map keeps its own colours) ---- */
:root { --bg: #000; --surface: #000; --surface-2: #141414; --surface-3: #262626; --border: #262626; --occ-bg: #141414;
  --control: #1c1c1c; --control-hover: #2a2a2a; }
:root[data-theme="light"] { --control: #edf0f3; --control-hover: #e2e6ea; }
@media (prefers-color-scheme: light) { :root:not([data-theme="dark"]) { --control: #edf0f3; --control-hover: #e2e6ea; } }

/* ---- Controls in the card get a fill, so they stand apart from the card ---- */
.search-field, .view-head .back, #stop-back, .chip, .home-tabs,
.stop-list button, .search-results button,
.composer-bar input, .pick-btn, .pick-grid button { background: var(--control); }
.stop-list button:hover, .search-results button:hover, .chip:hover, .view-head .back:hover { background: var(--control-hover); }
.home-tabs button:not([aria-selected="true"]) { color: var(--fg); opacity: .7; }
/* The one primary action on the home view gets Backseat yellow */
#stop-near { background: var(--accent); color: #000; }
#stop-near:hover { background: var(--accent); filter: brightness(1.05); }
.pick-grid button.on { background: var(--accent); }
.pick-btn.empty { background: var(--control); }

/* Feed card polish */
.vcard .flag { background: none; border: 0; border-radius: 50%; display: grid; place-items: center; padding: 0; cursor: pointer; }
.vc-text { font-size: 15px; line-height: 1.1; -webkit-line-clamp: 5; overflow-wrap: normal; word-break: normal; hyphens: auto; }
.vc-emoji { font-size: 30px; }

/* Home card: same size whichever tab is showing (content scrolls inside),
   so switching Vibes <-> Stops doesn't make the card jump */
@media (max-width: 719px) { .sheet[data-view="stops"]:not(.full):not(.collapsed) { min-height: 45%; max-height: 45%; } }
/* Long words wrap (with a hyphen where the browser can) rather than hiding under the emoji */
.vc-text { overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto; }
.vc-emoji { font-size: 28px; }

/* Top bar: logo pill with the theme toggle beside it (left); zoom and
   find-me stack on the right */
.bar { justify-content: flex-start; }
.leaflet-top.leaflet-right .leaflet-control-zoom { margin: var(--top-gap) 14px 0 0; }
.map-btn { position: fixed; right: 14px; z-index: 1000; width: 40px; height: 40px; border-radius: 50%; padding: 0; cursor: pointer;
  display: grid; place-items: center; background: var(--surface); color: var(--fg); border: 2px solid var(--outline);
  box-shadow: 0 2px 0 0 var(--outline); pointer-events: auto; }
.map-btn .ico { width: 20px; height: 20px; }
/* under the zoom buttons (2 x 30px + borders) */
.locate { top: calc(var(--top-gap) + 86px); right: 14px; }
.locate.busy .ico { animation: locate-spin 1s linear infinite; }
.locate.denied { color: #ff5c5c; }
@keyframes locate-spin { to { transform: rotate(360deg); } }
body:has(#sheet.full) .locate { visibility: hidden; }
/* Zoom buttons at 36px (+2px borders) so the stack lines up with find-me (40px) */
.leaflet-control-zoom a { width: 36px !important; height: 36px !important; line-height: 36px !important; }

/* The open stop on the map: yellow dot, dark ring, white halo, slow pulse,
   and its name in a pill above */
.stop-sel { position: absolute; transform: translate(-50%, -50%); display: grid; justify-items: center; pointer-events: none; }
.stop-sel-dot { width: 26px; height: 26px; border-radius: 50%; background: #fec940; border: 3px solid #111;
  box-shadow: 0 0 0 3px #fff, 0 3px 8px rgba(0,0,0,.45); display: grid; place-items: center; position: relative; }
.stop-sel-dot::after { content: ''; position: absolute; inset: -9px; border-radius: 50%; border: 3px solid #fec940; animation: stop-pulse 1.8s ease-out infinite; }
@keyframes stop-pulse { from { opacity: .9; transform: scale(.7); } to { opacity: 0; transform: scale(1.35); } }
@media (prefers-reduced-motion: reduce) { .stop-sel-dot::after { animation: none; opacity: 0; } }
.stop-sel-dot .heart { width: 14px; height: 14px; stroke-width: 2.4; }
.stop-sel-dot .heart.on { fill: #ff4d6d; stroke: #111; }
.stop-sel-label { position: absolute; bottom: calc(100% + 8px); white-space: nowrap; max-width: 200px; overflow: hidden; text-overflow: ellipsis;
  background: var(--surface); color: var(--fg); border: 2px solid var(--outline); border-radius: 999px; padding: 3px 10px;
  font: 700 13px/1.2 var(--font); box-shadow: 0 2px 0 0 var(--outline); }
/* Favourite stops on the map: a heart instead of a dot (34px tap area) */
.map-heart { width: 34px; height: 34px; display: grid; place-items: center; cursor: pointer; }
.map-heart .heart { width: 24px; height: 24px; filter: drop-shadow(0 1px 2px rgba(0,0,0,.5)); }
.map-heart .heart.on { fill: #ff4d6d; stroke: #fff; stroke-width: 2.4; }
.stop-pin { display: none; }
.stop-sel-label { left: 50%; transform: translateX(-50%); }

/* Stop names along an open bus's route (zoomed in): small pills beside each dot */
.leaflet-tooltip.route-stop-label { background: var(--surface); color: var(--fg); border: 2px solid var(--outline); border-radius: 999px;
  padding: 1px 8px; font: 700 11px/1.3 var(--font); box-shadow: none; white-space: nowrap; }
.leaflet-tooltip.route-stop-label::before { display: none; } /* no arrow */
.leaflet-tooltip.route-stop-label.next { background: var(--route-bg); color: var(--route-fg); font-size: 12px; }
.leaflet-tooltip.route-stop-label.mine { background: #fec940; color: #000; }
.stop-sel.quiet .stop-sel-label { display: none; }

/* Long stop names wrap onto two lines (then …) instead of running off screen.
   The clamp is on the inner text so the pill's padding stays clean. */
.leaflet-tooltip.route-stop-label, .stop-sel-label {
  white-space: normal; width: max-content; max-width: 150px; border-radius: 12px; line-height: 1.2; overflow: visible; }
.route-stop-label .lbl, .stop-sel-label .lbl { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.leaflet-tooltip.route-stop-label { padding: 2px 8px; }
.stop-sel-label { max-width: 170px; text-align: center; padding: 3px 10px; }
/* "Next" / "Your stop" sits on its own small line above the name, so a long name can't cut it off */
.route-stop-label .lbl-tag { display: block; color: inherit; font-size: 10px; font-weight: 700; opacity: .85; line-height: 1.15; }

/* Desktop: the side panel is always full height, so pin the vibe bar to its
   bottom the same way as the full-size phone card */
@media (min-width: 720px) {
  .sheet[data-view="bus"]:not(.collapsed) { display: flex; flex-direction: column; }
  .sheet[data-view="bus"]:not(.collapsed) .view[data-view="bus"] { display: flex; flex-direction: column; flex: 1 0 auto; }
  .sheet[data-view="bus"]:not(.collapsed) .view[data-view="bus"]:not([data-tab="stops"]) .tabpanel[data-tab="vibes"] { display: flex; flex-direction: column; flex: 1 0 auto; }
  .sheet[data-view="bus"]:not(.collapsed) .composer { margin-top: auto; }
}
/* Recentre: a labelled pill in the open bus's route colour. Phones: centred
   just above the card (bottom set from JS); desktop: bottom right. */
.follow { position: fixed; z-index: 1001; left: 50%; transform: translateX(-50%); display: inline-flex; align-items: center; gap: 8px;
  height: 44px; padding: 0 18px 0 14px; border-radius: 22px; border: 2px solid var(--outline); cursor: pointer;
  background: var(--rc, var(--accent)); color: var(--rt, #000); font: 800 16px/1 var(--font); letter-spacing: -0.01em;
  box-shadow: 0 3px 0 0 var(--outline), 0 6px 18px rgba(0,0,0,.3); animation: follow-in .2s ease-out; }
.follow[hidden] { display: none; }
.follow .ico { width: 18px; height: 18px; stroke: none; }
.follow:active { transform: translateX(-50%) translateY(2px); box-shadow: 0 1px 0 0 var(--outline); }
@keyframes follow-in { from { opacity: 0; transform: translateX(-50%) translateY(8px); } }
body:has(#sheet.full) .follow { display: none; }
@media (min-width: 720px) {
  .follow { left: auto; right: 16px; bottom: 36px; transform: none; }
  .follow:active { transform: translateY(2px); }
  @keyframes follow-in { from { opacity: 0; transform: translateY(8px); } }
  body:has(#sheet.full) .follow { display: inline-flex; }
  body:has(#sheet.full) .follow[hidden] { display: none; }
}
/* A third header tile (minutes to your stop) when a stop is selected */
.bus-hero .dash.three { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px 12px; }

/* A handle you can actually see: wider, thicker, higher contrast */
.handle { padding: 10px 0 12px; }
.handle span { width: 52px; height: 6px; border-radius: 3px; background: var(--muted); opacity: .9; }
.sheet[data-view="bus"] .handle span { background: var(--rt, #fff); opacity: .75; }
.handle:hover span { opacity: 1; }

/* ---- Compact bus header ---- */
.handle span { width: 40px; height: 5px; }
.handle { padding: 8px 0 8px; }
.hero-route { font-size: 40px; letter-spacing: -1.5px; }
.bus-hero h2 { font-size: 17px; }
.bus-hero .back { width: 28px; height: 28px; }
.bus-hero { padding-bottom: 8px; row-gap: 8px; }
/* Info as a row of chips (no titles): status dot, busyness badge, pin */
.bus-hero .dash, .bus-hero .dash.three { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; }
.bus-hero .tile { background: rgba(0,0,0,.22); border-radius: 999px; padding: 4px 11px 4px 6px; flex: none; max-width: 100%; }
.bus-hero .tile .v { font-size: 14px; font-weight: 700; gap: 6px; }
.bus-hero .tile .k { display: none; }
.bus-hero .tile .status-dot { margin-left: 2px; }
.bus-hero .tile .busy-badge.inline { width: 18px; height: 18px; }
.bus-hero .tile.your-stop .ico { width: 15px; height: 15px; margin-left: 2px; }
/* Tabs: slimmer, tucked closer to the header */
.bus-tabs { padding: 4px 18px 8px; }
.bus-tabs .tabs { padding: 3px; border-radius: 10px; }
.bus-tabs .tabs button { padding: 5px 6px; font-size: 14px; border-radius: 8px; }
/* One row: chips shrink (long text scrolls, like the ticker) rather than wrap */
.bus-hero .dash, .bus-hero .dash.three { flex-wrap: nowrap; gap: 5px; }
.bus-hero .tile { flex: 0 1 auto; min-width: 0; padding: 3px 10px 3px 5px; }
.bus-hero .tile .v { font-size: 13px; gap: 5px; }
.bus-hero .tile .v .main { overflow: hidden; }
.bus-hero .tile .busy-badge.inline { width: 17px; height: 17px; }

/* ---- Compact home card ---- */
/* The search sticks right under the (now 21px) handle; it was 27px, which
   pushed it 6px down over the Vibes/Stops control */
.search-sticky { top: 21px; padding-bottom: 8px; }
.search-field input { padding: 9px 0; }
.search-ico .ico { width: 18px; height: 18px; }
/* Segmented control: slim, like the bus card's */
.home-tabs { margin: 0 0 10px; padding: 3px; border-radius: 12px; }
.home-tabs button { font-size: 14px; padding: 5px 6px; border-radius: 9px; }
/* Section headings and chips */
.routes-head, .home-row h2 { font-size: 15px; }
.routes-head { margin: 2px 0 6px; }
.route-chips { margin-bottom: 8px; gap: 6px; }
.route-chip { height: 34px; min-width: 44px; padding: 0 12px; font-size: 16px; border-radius: 17px; }
.chip { padding: 5px 10px; font-size: 14px; }
.chip .ico { width: 16px; height: 16px; }
.home-row { margin-bottom: 6px; }
/* Stop rows */
.stop-list, .search-results { gap: 6px; }
.stop-list button, .search-results button { padding: 8px 10px; font-size: 14px; border-radius: 12px; }
.stop-list .icon .heart { width: 16px; height: 16px; }
.view .routes-head { font-size: 15px; }

/* ---- Compact departure rows ---- */
.departures { gap: 6px; }
.departures .dep { height: 62px; padding: 8px 12px 8px 14px; gap: 12px; border-radius: 16px; }
.dep-route { font-size: 22px; min-width: 1.5em; }
.dep-main { gap: 4px; justify-content: center; }
.dep-dest { font-size: 15px; font-weight: 700; line-height: 1.15; }
/* Pills under the destination (same look as the bus card's chips) */
.dep-pills { display: flex; align-items: center; gap: 5px; min-width: 0; overflow: hidden; }
.dpill { display: inline-flex; align-items: center; gap: 5px; flex: none; height: 20px; padding: 0 8px 0 5px; border-radius: 999px;
  background: rgba(0,0,0,.25); color: inherit; font-size: 12px; font-weight: 700; white-space: nowrap; }
.dpill .status-dot { width: 8px; height: 8px; box-shadow: 0 0 0 1.5px rgba(255,255,255,.85); margin: 0 1px 0 2px; }
.dpill.cancel { padding: 0 8px; color: #ffb3b3; }
.dpill.vibe { flex: 0 1 auto; min-width: 0; padding: 0 8px 0 6px; font-weight: 600; }
.dpill.vibe .e { flex: none; font-size: 13px; }
.dpill.vibe .t { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.dep-pills .busy-badge.inline { width: 20px; height: 20px; }
/* Countdown: big number with "min" right beside it */
.dep-eta { flex-direction: row; align-items: baseline; gap: 3px; min-width: 0; }
.dep-eta .ping { align-self: center; margin-right: 6px; }
.dep-eta b { font-size: 28px; }
.dep-eta small { font-size: 13px; font-weight: 700; opacity: .9; }
.dep-eta.cancelled-eta s { font: 700 16px/1 var(--font); opacity: .8; }
/* Vibe pill: emojis + count */
.dpill.vibe { flex: none; gap: 4px; padding: 0 8px 0 6px; }
.dpill.vibe .e { letter-spacing: 1px; }
.dpill.vibe .n { font-size: 12px; font-weight: 800; }

/* Home segmented control: same style as the bus card's (filled track, no
   outline, white active pill with a soft shadow) */
.home-tabs { border: 0; background: var(--control); padding: 3px; border-radius: 10px; }
.home-tabs button { border-radius: 8px; padding: 5px 6px; font-size: 14px; }
.home-tabs button:not([aria-selected="true"]) { color: var(--fg); opacity: .75; background: none; }
.home-tabs button[aria-selected="true"] { background: #fff; color: #000; box-shadow: 0 2px 8px rgba(0,0,0,.25); opacity: 1; }
.home-tabs button[aria-selected="true"] .count { color: #000; opacity: .6; }
:root[data-theme="light"] .home-tabs { background: #e2e6ea; }
@media (prefers-color-scheme: light) { :root:not([data-theme="dark"]) .home-tabs { background: #e2e6ea; } }

/* Bus card at normal size on phones: one fixed height whichever tab is open
   (Vibes pins its bar to the bottom, Stops scrolls inside) */
@media (max-width: 719px) {
  .sheet[data-view="bus"]:not(.full):not(.collapsed) { min-height: 48%; max-height: 48%; display: flex; flex-direction: column; }
  .sheet[data-view="bus"]:not(.full):not(.collapsed) .view[data-view="bus"] { display: flex; flex-direction: column; flex: 1 0 auto; }
  .sheet[data-view="bus"]:not(.full):not(.collapsed) .view[data-view="bus"]:not([data-tab="stops"]) .tabpanel[data-tab="vibes"] { display: flex; flex-direction: column; flex: 1 0 auto; }
  .sheet[data-view="bus"]:not(.full):not(.collapsed) .composer { margin-top: auto; }
}

/* Trip preview (no live bus): note pill above the card, no vibe bar */
.trip-note-pill { position: fixed; z-index: 1001; left: 50%; transform: translateX(-50%); display: inline-flex; align-items: center; gap: 6px;
  height: 34px; padding: 0 14px; border-radius: 17px; border: 2px solid var(--outline); background: var(--surface); color: var(--fg);
  font: 700 14px/1 var(--font); box-shadow: 0 2px 0 0 var(--outline); pointer-events: none; white-space: nowrap; }
.trip-note-pill[hidden] { display: none; }
.trip-note-pill::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
body:has(#sheet.full) .trip-note-pill { display: none; }
@media (min-width: 720px) { .trip-note-pill { left: auto; right: 16px; bottom: 36px; transform: none; } }
.sheet.preview .composer { display: none; }
/* Departures shown over an open bus: the back arrow wears the route colour
   (it goes back to that bus) */
.sheet.peeking #stop-back { background: var(--rc); color: var(--rt); }

/* Stop kinds: a coloured dot in lists (same colours as the map) */
.kind-dot { width: 10px; height: 10px; border-radius: 50%; flex: none; border: 2px solid var(--outline); background: #fff; }
.kind-dot.k-train { background: #3b6fe0; } .kind-dot.k-ferry { background: #12a3a0; } .kind-dot.k-cablecar { background: #e2474e; }
/* Station departures: which platform */
.dpill.plat { padding: 0 8px; background: rgba(255,255,255,.9); color: #111; }
.dpill.plat { padding: 0 6px; font-size: 11px; }
.dpill.cancel { flex: none; }

/* Collapsed (not a bus): just the search bar. Content is hidden rather than
   squeezed in, and the space below the bar matches the handle area above it
   (21px: sheet 13px + the search's own 8px). */
.sheet.collapsed:not([data-view="bus"]) { max-height: none; padding-bottom: 13px; }
.sheet.collapsed:not([data-view="bus"]) .view,
.sheet.collapsed:not([data-view="bus"]) .search-results { display: none !important; }

/* ---- Stop open: no search bar; a colour-block header like the bus card ---- */
.sheet[data-view="departures"] .search-sticky,
.sheet[data-view="departures"] .search-results { display: none; }
.sheet[data-view="departures"] .handle { background: transparent; z-index: 5; }
.sheet[data-view="departures"] .handle span { background: var(--st, #111); opacity: .6; }
/* The header: solid stop colour under the handle, fading into the card */
.sheet[data-view="departures"] .view[data-view="departures"] > .view-head {
  position: sticky; top: 0; z-index: 4; margin: -21px -18px 8px; padding: 29px 18px 26px; align-items: center;
  color: var(--st, #111);
  background: linear-gradient(to bottom, var(--sc) 0, var(--sc) 72%,
    color-mix(in srgb, var(--sc) 45%, var(--surface)) 88%, var(--surface) 100%); }
.sheet[data-view="departures"] .view-head h2 { font-size: 20px; line-height: 1.15; }
.sheet[data-view="departures"] .view-head .muted { color: inherit; opacity: .75; font-size: 12px; }
.sheet[data-view="departures"] #stop-back { background: rgba(0,0,0,.16); color: inherit; border: 0; width: 34px; height: 34px; }
.sheet[data-view="departures"] #stop-back:hover { background: rgba(0,0,0,.24); }
.sheet[data-view="departures"] #stop-fav { color: inherit; }
.sheet[data-view="departures"] #stop-fav .heart { stroke: currentColor; }
.sheet[data-view="departures"] #stop-fav .heart.on { fill: #ff4d6d; stroke: currentColor; }
/* Over an open bus the back arrow still says "back to that bus" */
.sheet.peeking[data-view="departures"] #stop-back { background: var(--rc); color: var(--rt); }
/* Collapsed: just the coloured header */
.sheet.collapsed[data-view="departures"] { background: var(--sc); padding-bottom: 0; }
.sheet.collapsed[data-view="departures"] .view[data-view="departures"] { display: block !important; }
.sheet.collapsed[data-view="departures"] .departures,
.sheet.collapsed[data-view="departures"] #dep-note { display: none; }
.sheet.collapsed[data-view="departures"] .view[data-view="departures"] > .view-head { margin-bottom: 0; padding-bottom: 16px; background: var(--sc); }

/* List dots: bus stops are Backseat yellow too */
.kind-dot { background: #fec940; }

/* Cancelled trip preview */
.bus-hero .tile.cancelled .ico { width: 14px; height: 14px; margin-left: 2px; stroke-width: 3; }
.trip-note-pill.cancelled::before { background: #ff5c5c; }

/* ---- Tighter top: shorter handle area, content moves up ---- */
.handle { padding: 7px 0 4px; }                     /* 16px tall */
.search-sticky { top: 16px; }
.bus-top { margin-top: -16px; padding-top: 22px; }  /* colour block from the very top; content 22px down */
.sheet[data-view="departures"] .view[data-view="departures"] > .view-head { margin-top: -16px; padding-top: 22px; }

/* Stop card: the colour carries on fading down behind the first departures
   (the card's own background, so it stays put as the list scrolls) */
.sheet[data-view="departures"]:not(.collapsed) {
  background: linear-gradient(to bottom, var(--sc) 0, var(--sc) 96px,
    color-mix(in srgb, var(--sc) 55%, var(--surface)) 150px,
    color-mix(in srgb, var(--sc) 18%, var(--surface)) 220px, var(--surface) 300px); }
.sheet[data-view="departures"] .view[data-view="departures"] > .view-head { background: var(--sc); padding-bottom: 14px; margin-bottom: 10px; }

/* Bus card tabs: readable on every route colour.
   Dark routes (white text): darkened track, white labels, active pill shows
   the route colour on white (the route colour already passes 4.5:1 on white).
   Light routes (black text, e.g. yellow): lightly darkened track with black
   labels, and black text on the white active pill. */
.sheet[data-route-tone="dark"] .bus-tabs .tabs button:not([aria-selected="true"]) { color: #fff; }
.sheet[data-route-tone="light"] .bus-tabs .tabs { background: color-mix(in srgb, var(--rc) 78%, #000); }
.sheet[data-route-tone="light"] .bus-tabs .tabs button:not([aria-selected="true"]) { color: #111; }
.sheet[data-route-tone="light"] .bus-tabs .tabs button[aria-selected="true"] { color: #111; }
.bus-tabs .tabs .count { opacity: .75; }

/* ---- Home: content on top, then the search, then the bottom nav ----
   The dock is pinned to the bottom of the card (thumb reach, and it matches
   the bus card's composer). It only shows on home; opening a stop, route or
   bus hides it (drill in, Back to return). */
.dock { display: none; }
.sheet[data-view="stops"] { display: flex; flex-direction: column; padding-bottom: 0; }
.sheet[data-view="stops"] > * { flex-shrink: 0; }
.sheet[data-view="stops"] .dock { display: block; position: sticky; bottom: 0; z-index: 3; margin: auto -18px 0;
  padding: 8px 18px 8px; background: var(--surface); }
/* While typing, results sit just above the search */
.sheet[data-view="stops"].searching .search-results { margin-top: auto; }
.sheet[data-view="stops"].searching .dock { margin-top: 0; }
.bottom-nav { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 6px; }
.bottom-nav button { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 4px 0 2px; border: 0; background: none;
  color: var(--muted); font: 700 12px/1.1 var(--font); cursor: pointer; }
.bottom-nav [data-icon] { display: grid; place-items: center; width: 56px; height: 28px; border-radius: 999px; transition: background .15s ease; }
.bottom-nav .ico { width: 21px; height: 21px; }
.bottom-nav button[aria-selected="true"] { color: var(--fg); }
.bottom-nav button[aria-selected="true"] [data-icon] { color: #000; }
/* The yellow pill: one shared element under the icons, slid to the chosen tab
   (--i = its column) with a small overshoot. Columns are equal, so one column
   + gap is (100% + gap) of the pill's own width. */
.bottom-nav { position: relative; }
.bottom-nav button { position: relative; z-index: 1; }
.nav-pill { position: absolute; top: 4px; left: 0; width: calc((100% - 12px) / 3); height: 28px; pointer-events: none;
  transform: translateX(calc(var(--i, 1) * (100% + 6px))); }
.nav-pill::before { content: ''; position: absolute; left: 50%; top: 0; width: 56px; height: 28px; margin-left: -28px; border-radius: 999px; background: var(--accent); }
.bottom-nav.ready .nav-pill { transition: transform .42s cubic-bezier(.34, 1.45, .55, 1); }
@media (prefers-reduced-motion: reduce) { .bottom-nav.ready .nav-pill { transition-duration: .01s; } }
.bottom-nav button:not([aria-selected="true"]):hover [data-icon] { background: var(--control-hover); }
.bottom-nav .count { font-weight: 600; opacity: .8; }
/* Collapsed: just the search and the nav */
.sheet.collapsed[data-view="stops"] { padding-bottom: 0; }
.sheet.collapsed[data-view="stops"] .dock { margin-top: 0; padding-top: 0; }

/* Route search results: the route number in its colour */
.search-results .route-hit .dep-route { background: var(--rc); color: var(--rt); border-radius: 8px; padding: 5px 7px;
  font-size: 16px; min-width: 2.3em; text-align: center; }

/* ---- Route card: colour-block header like the stop card ---- */
.sheet[data-view="route"] .view[data-view="route"] { display: block; animation: slidein .18s ease-out; }
.sheet[data-view="route"] .search-results { display: none; }
.sheet[data-view="route"] .handle { background: transparent; z-index: 5; }
.sheet[data-view="route"] .handle span { background: var(--rt, #fff); opacity: .6; }
.sheet[data-view="route"]:not(.collapsed) {
  background: linear-gradient(to bottom, var(--rc) 0, var(--rc) 96px,
    color-mix(in srgb, var(--rc) 55%, var(--surface)) 150px,
    color-mix(in srgb, var(--rc) 18%, var(--surface)) 220px, var(--surface) 300px); }
.route-head { position: sticky; top: 0; z-index: 4; margin: -16px -18px 4px; padding: 22px 18px 14px; align-items: center;
  color: var(--rt); background: var(--rc); }
.route-head .hero-route { font-size: 34px; letter-spacing: -1px; min-width: 0; }
.route-head h2 { font-size: 16px; line-height: 1.2; }
.route-head #rc-back { background: rgba(0,0,0,.16); color: inherit; border: 0; width: 34px; height: 34px; flex: none; }
.route-head #rc-back:hover { background: rgba(0,0,0,.24); }
.route-head #rc-fav { color: inherit; }
.route-head #rc-fav .heart { stroke: currentColor; }
.route-head #rc-fav .heart.on { fill: #ff4d6d; stroke: currentColor; }
.rc-dir { display: flex; align-items: center; gap: 6px; font-size: 12px; opacity: .85; margin-top: 2px; }
.rc-dir:empty { display: none; }
.rc-both { font: 700 11px/1 var(--font); border: 0; border-radius: 999px; padding: 4px 8px; background: rgba(0,0,0,.18); color: inherit; cursor: pointer; }
.rc-sub { font-size: 13px; font-weight: 700; margin: 12px 2px 6px; color: var(--fg); }
.rc-live { margin-top: 0; }
.rc-live .dep-eta { display: flex; align-items: center; }
.rc-live .dep-eta small { font-size: 12px; font-weight: 700; }
/* Collapsed: just the coloured header */
.sheet.collapsed[data-view="route"] { background: var(--rc); padding-bottom: 0; }
.sheet.collapsed[data-view="route"] .view[data-view="route"] { display: block !important; }
.sheet.collapsed[data-view="route"] .view[data-view="route"] > :not(.route-head) { display: none; }
.sheet.collapsed[data-view="route"] .route-head { margin-bottom: 0; padding-bottom: 16px; }
@media (max-width: 719px) { .sheet[data-view="route"]:not(.full):not(.collapsed) { min-height: 45%; max-height: 45%; } }
/* Feed cards: history is full colour; a live dot marks a bus still running */
.vcard header .ping { width: 8px; height: 8px; margin-right: 2px; }

/* ---- Welcome + terms panel: a full-height card over the sheet ---- */
button.brand { font: inherit; cursor: pointer; }
button.brand:active { transform: scale(.97); }
.panel { z-index: 1100; display: flex; flex-direction: column; padding: 0 18px; }
.panel[hidden] { display: none; }
.panel > * { flex-shrink: 0; }
.panel-head { position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 10px; margin: 0 -18px; padding: 14px 14px 10px 18px;
  background: var(--surface); }
.panel-head h2 { flex: 1; margin: 0; font-size: 15px; font-weight: 700; color: var(--muted); }
.panel-head .back { width: 34px; height: 34px; flex: none; }
.panel-head .back[hidden] { display: none; }
.panel-close { width: 36px; height: 36px; border: 0; border-radius: 50%; background: var(--control); color: var(--fg); display: grid; place-items: center; cursor: pointer; flex: none; }
.panel-close:hover { background: var(--control-hover); }
.panel-body { padding-bottom: 12px; }
.panel-foot { position: sticky; bottom: 0; margin: auto -18px 0; padding: 10px 18px calc(14px + env(safe-area-inset-bottom) * .3);
  background: var(--surface); display: grid; gap: 6px; justify-items: center; }
.panel-foot:empty { display: none; }
.panel-cta { width: 100%; padding: 14px; border: 2px solid var(--outline); border-radius: 16px; background: #fec940; color: #111;
  font: 800 17px/1 var(--font); cursor: pointer; box-shadow: 0 3px 0 0 var(--outline); }
.panel-cta:active { transform: translateY(2px); box-shadow: 0 1px 0 0 var(--outline); }
/* Report form (panel mode "report") */
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.panel-cta:disabled { background: var(--surface-3); color: var(--muted); cursor: default; }
/* "Get the Backseat app" card (install.js): over everything, bottom of the screen */
.install { position: fixed; left: 12px; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom)); z-index: 1200; max-width: 440px; margin: 0 auto;
  background: var(--surface); color: var(--fg); border: 2px solid var(--outline); border-radius: 22px; padding: 16px 16px 12px;
  box-shadow: var(--shadow); animation: install-in .45s cubic-bezier(.34, 1.4, .6, 1); }
@keyframes install-in { from { transform: translateY(24px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .install { animation: none; } }
.i-head { display: flex; gap: 12px; align-items: flex-start; }
.i-head img { width: 48px; height: 48px; border-radius: 12px; flex: none; border: 2px solid var(--outline); }
.i-head b { display: block; font-size: 17px; }
.i-head span { font-size: 14px; color: var(--muted); line-height: 1.35; }
.i-steps { margin: 12px 0 4px; padding-left: 22px; display: grid; gap: 8px; font-size: 15px; }
.i-ico { display: inline-grid; place-items: center; width: 26px; height: 26px; border-radius: 8px; background: var(--control); vertical-align: middle; margin: 0 2px; }
.i-ico .ico { width: 17px; height: 17px; }
.i-acts { display: flex; align-items: center; gap: 14px; margin-top: 12px; }
.i-acts .panel-cta { flex: 1; padding: 11px; }

.t-agreed { margin: 0; text-align: center; font-weight: 700; font-size: 14px; }
/* About panel (the (i) button in the top bar) */
.a-h { font-size: 17px; margin: 18px 0 8px; }
.a-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.a-steps li { display: flex; gap: 12px; align-items: flex-start; padding: 12px 14px; border: 2px solid var(--outline); border-radius: 16px; }
.a-steps b { display: block; font-size: 15px; margin-bottom: 2px; }
.a-steps span { font-size: 14px; line-height: 1.4; }
.a-emoji { font-size: 24px; line-height: 1; flex: none; }
.a-tips { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; font-size: 14px; line-height: 1.4; }
.a-terms { margin: 20px 0 12px; padding: 14px; border-radius: 16px; background: var(--control); }
.a-terms p { margin: 4px 0 12px; font-size: 14px; }
.panel-cta.ghost { background: var(--surface); color: var(--fg); }
.a-credits { font-size: 12px; color: var(--muted); line-height: 1.5; margin: 0 0 18px; }
.r-quote { display: flex; align-items: center; gap: 12px; margin: 4px 0 12px; padding: 12px 14px; border-radius: 14px; border: 2px solid var(--outline); }
.r-quote .r-emoji { font-size: 32px; line-height: 1; flex: none; }
.r-quote small { display: block; margin-top: 3px; color: var(--muted); font-size: 12px; }
.r-reasons { border: 0; margin: 0 0 14px; padding: 0; display: grid; gap: 8px; }
.r-reason { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border: 2px solid var(--border); border-radius: 14px; cursor: pointer; }
.r-reason:has(input:checked) { border-color: var(--outline); background: color-mix(in srgb, #fec940 30%, transparent); }
.r-reason input { accent-color: #111; width: 20px; height: 20px; margin: 0; flex: none; }
.r-reason b { display: block; font-size: 15px; }
.r-reason small { display: block; color: var(--muted); font-size: 12px; margin-top: 1px; }
.r-details { display: grid; gap: 6px; font-weight: 700; font-size: 14px; }
.r-details textarea { font: 15px/1.4 var(--font); padding: 10px 12px; border: 2px solid var(--outline); border-radius: 14px; background: var(--control); color: var(--fg); resize: vertical; }
.r-count { justify-self: end; font-weight: 500; }
.panel .link { background: none; border: 0; padding: 4px; font: 700 14px var(--font); color: var(--fg); text-decoration: underline; cursor: pointer; }
/* Welcome */
.w-title { font: 800 26px/1.1 var(--font); letter-spacing: -.02em; margin: 4px 0 10px; }
.w-desc { font-size: 15px; line-height: 1.45; margin: 0 0 16px; color: var(--fg); opacity: .85; }
.w-features { list-style: none; margin: 0 0 14px; padding: 0; display: grid; gap: 10px; }
.w-features li { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border: 2px solid var(--outline); border-radius: 18px;
  background: var(--fb); color: #111; font-weight: 700; font-size: 15px; line-height: 1.25; }
.w-emoji { font-size: 26px; flex: none; }
.w-foot { font-size: 13px; color: var(--muted); text-align: center; margin: 4px 0 0; }
.w-foot .link { font-size: 13px; padding: 0; color: inherit; }
/* Terms */
.t-lede { font-size: 15px; line-height: 1.4; margin: 2px 0 14px; padding: 12px 14px; border-radius: 14px; background: var(--control); font-weight: 600; }
.t-sec h3 { font-size: 16px; margin: 14px 0 4px; }
.t-sec p, .t-sec li, .t-sec .t-p { font-size: 14px; line-height: 1.5; opacity: .9; }
.t-sec p { margin: 0; }
.t-p + .t-p, .t-p div + div, .w-desc div + div { margin-top: 8px; } /* the editor's lines as paragraphs */
.t-sec .t-p ul { margin: 6px 0; padding-left: 20px; }
.t-sec ul { margin: 6px 0 0; padding-left: 20px; }
.t-bottom { margin: 18px 0 4px; padding: 14px; border: 2px solid var(--outline); border-radius: 16px; background: #fec940; color: #111; }
.t-bottom .t-p { margin: 4px 0 0; font-size: 14px; line-height: 1.45; }
.t-bottom p { margin: 4px 0 0; font-size: 14px; line-height: 1.45; }
.t-lede { font-weight: 500; font-size: 14px; }
.panel .panel-foot .link, .panel .w-foot .link { color: var(--fg); }
.panel .w-foot .link { color: inherit; }

/* ---- Search tab: one box, routes + stops ---- */
.search-hint { font-size: 14px; line-height: 1.45; margin: 6px 2px; }
.search-results { display: grid; gap: 6px; margin: 4px 0 8px; }
.sr-head { font-size: 12px; font-weight: 700; color: var(--muted); margin: 8px 2px 0; text-transform: uppercase; letter-spacing: .04em; }
.sr-item { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 7px 12px; border-radius: 12px; background: var(--control);
  font-weight: 600; font-size: 15px; cursor: pointer; }
.sr-item:hover { background: var(--control-hover); }
.sr-item[aria-selected="true"] { outline: 2px solid var(--fg); outline-offset: -2px; }
.sr-route { flex: none; min-width: 2.3em; text-align: center; padding: 5px 7px; border-radius: 8px; background: var(--rc); color: var(--rt); font: 800 16px/1 var(--font); }
.sr-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sr-code { flex: none; color: var(--muted); font-weight: 500; font-variant-numeric: tabular-nums; }
#search::-webkit-search-cancel-button { display: none; }

/* ---- Tab bar: three tabs, frosted so content scrolls behind it ---- */
.bottom-nav { grid-template-columns: repeat(3, 1fr); margin-top: 0; }
.sheet[data-view="stops"] .dock { padding: 8px 18px 10px;
  background: color-mix(in srgb, var(--surface) 70%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.5); backdrop-filter: blur(14px) saturate(1.5); }
.sheet.collapsed[data-view="stops"] .dock { background: var(--surface); padding-top: 2px; }

/* ---- Resize arrows: no pill, 42px touch target ---- */
.size-btn { flex: none; width: 42px; height: 42px; margin: -4px -10px -4px -4px; padding: 0; border: 0; background: none; color: inherit;
  display: grid; place-items: center; cursor: pointer; border-radius: 50%; }
.size-btn .ico { width: 24px; height: 24px; stroke-width: 2.6; }
.size-btn:hover { background: rgba(127,127,127,.14); }
.size-btn:active { transform: scale(.9); }
.size-btn.floating { position: fixed; z-index: 1050; margin: 0; color: var(--fg); }
.size-btn.floating[hidden] { display: none; }
body.panel-open .size-btn.floating { display: none; }
/* In a header row the heart and the arrow sit together on the right */
.bus-hero .route-fav { margin-left: auto; }
/* Search box sits below the floating arrow */
[data-home-panel="search"] .search-sticky { padding-top: 12px; }
/* Picked route chips have a ring: give it room under the heading */
.route-chips { margin-top: 0; padding-top: 9px; row-gap: 10px; }

/* Toast: short messages above the card (wraps; shows even over a full card) */
.toast { white-space: normal; text-align: center; width: max-content; max-width: min(340px, calc(100vw - 40px)); line-height: 1.3; padding-top: 9px; padding-bottom: 9px; }
.toast::before { display: none; }
body:has(#sheet.full) .toast:not([hidden]) { display: inline-flex; }
/* The chevron spins to point up/down, with the same little overshoot as the card */
.size-btn .ico { transition: transform .45s cubic-bezier(.3, 1.35, .55, 1); }
.size-btn.up .ico { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .size-btn .ico { transition: none; } }
/* Map controls stay behind the welcome/terms card */
body.panel-open .leaflet-control-zoom, body.panel-open .locate { visibility: hidden; }

/* Desktop: the side panel collapses up to the top-left (under the logo),
   the way dragging its handle down already pulls its bottom edge up. So the
   arrow flips: down expands it, up collapses it. */
@media (min-width: 720px) {
  .sheet.collapsed { top: calc(var(--top-gap) + 60px); bottom: auto; }
  .size-btn .ico { transform: rotate(180deg); }
  .size-btn.up .ico { transform: none; }
}

/* No accidental page zoom from pinching or double-tapping the UI: the card,
   buttons and pills only pan/scroll. (The map handles its own pinch-zoom.) */
html, body, .sheet, .panel, .bar, .map-btn, .follow, .trip-note-pill, .size-btn { touch-action: pan-x pan-y; }

/* Timetabled vehicles (the cable car has no GPS): a dashed ring on the map
   pin and a hollow dot in the card's chip, so they don't read as tracked */
.veh.sim .dot { outline: 2px dashed var(--t, #fff); outline-offset: -5px; }
.status-dot.sim { background: transparent; box-shadow: inset 0 0 0 2px currentColor; }

/* ---- Vibes filters: one scrolling row of chips (types, then routes) ---- */
.feed-filters { display: flex; gap: 6px; align-items: center; overflow-x: auto; scrollbar-width: none;
  margin: 0 -18px 10px; padding: 6px 18px 4px; }
.feed-filters::-webkit-scrollbar { display: none; }
.feed-filters:empty { display: none; }
.fchip { flex: none; height: 32px; padding: 0 12px; border-radius: 16px; border: 2px solid transparent; background: var(--control); color: var(--fg);
  font: 700 14px/1 var(--font); cursor: pointer; white-space: nowrap; }
.fchip:hover { background: var(--control-hover); }
.fchip.on { border-color: var(--fg); }
.fchip.route { background: var(--rc); color: var(--rt); min-width: 40px; }
.fchip.route.on { border-color: var(--rt); box-shadow: 0 0 0 2px var(--fg); }
.fchip.clear { background: none; border-color: var(--border); color: var(--muted); }
.fsep { flex: none; width: 2px; height: 20px; border-radius: 1px; background: var(--border); margin: 0 2px; }

/* Cards lift on hover: room above the first row so the handle doesn't clip them */
.feed { padding-top: 4px; }

/* ---- Desktop: the side panel is always open (no resizing or collapsing) ---- */
@media (min-width: 720px) {
  .handle { pointer-events: none; cursor: default; }
  .handle span, .size-btn { display: none !important; }
}
/* Phones: the chip row starts below the corner arrow */
@media (max-width: 719px) { .feed-filters { margin-top: 14px; } }

/* Route filter: picked routes carry an ×; the Route button opens a type-ahead */
.fchip { display: inline-flex; align-items: center; gap: 5px; }
.fchip .ico { width: 14px; height: 14px; stroke-width: 2.6; }
.fchip.add { background: none; border-color: var(--border); }
.fchip.add:hover { background: var(--control); }
.froute { flex: 1; min-width: 0; display: grid; gap: 8px; }
.froute-field { display: flex; align-items: center; gap: 8px; height: 38px; padding: 0 6px 0 12px; border-radius: 19px; background: var(--control); }
.froute-field > .ico { width: 16px; height: 16px; color: var(--muted); flex: none; }
.froute-field input { flex: 1; min-width: 0; border: 0; background: none; color: var(--fg); font: 600 16px var(--font); outline: none; }
.froute-field input::-webkit-search-cancel-button { display: none; }
.froute-cancel { width: 30px; height: 30px; border: 0; border-radius: 50%; background: none; color: var(--muted); display: grid; place-items: center; cursor: pointer; }
.froute-cancel .ico { width: 16px; height: 16px; }
.froute-hits { display: flex; flex-wrap: wrap; gap: 6px; }
.froute-hits:empty { display: none; }
.froute-none { font-size: 13px; padding: 4px 2px; }
.feed-filters:has(.froute) { overflow: visible; }

/* Cable car: the line's vibes, once, above its departures */
.dir-tabs { list-style: none; }
/* Day divider in departures: a thin rule with the day in the middle */
.dep-day { list-style: none; display: flex; align-items: center; gap: 10px; margin: 8px 2px 2px; color: var(--muted); font-size: 12px; font-weight: 700; letter-spacing: .02em; }
.dep-day::before, .dep-day::after { content: ''; flex: 1; height: 1px; background: var(--border); }
/* Same look as the bus card's Vibes/Stops tabs, on the stop's colour */
.dir-tabs .tabs { margin: 0 0 4px; background: color-mix(in srgb, var(--sc) 62%, #000); border-color: transparent; }
.dir-tabs .tabs button { color: rgba(255,255,255,.88); }
.dir-tabs .tabs button[aria-selected="true"] { background: #fff; color: color-mix(in srgb, var(--sc) 80%, #000); box-shadow: 0 2px 8px rgba(0,0,0,.25); }
/* (the departures list makes its buttons left-aligned flex rows: undo that here) */
.dir-tabs .tabs button { display: block; width: 100%; gap: 0; padding: 8px 6px; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.line-vibe { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 14px; background: var(--control); font-size: 13px; line-height: 1.3; }
.line-vibe .e { flex: none; font-size: 20px; letter-spacing: 1px; }
.line-vibe .t { min-width: 0; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
/* Favourite routes offered as quick picks: outlined in the route colour, with a heart */
.fchip.route.fav { background: none; border-color: var(--rc); color: var(--fg); }
.fchip.route.fav:hover { background: var(--control); }
.fchip .heart { width: 13px; height: 13px; fill: #ff4d6d; stroke: #ff4d6d; }

/* ---- Vibes: the filter row stays pinned at the top ----
   It sits over the handle area too, on a frosted layer that fades out
   downwards, so cards slide behind it instead of hitting a hard edge. */
.sheet[data-view="stops"][data-home-tab="feed"] .handle { background: transparent; z-index: 6; }
.feed-top { position: sticky; top: 0; z-index: 5; margin: -16px -18px 0; padding: 30px 0 14px; isolation: isolate; }
.feed-top::before { content: ''; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(to bottom, var(--surface) 0%, var(--surface) 45%, color-mix(in srgb, var(--surface) 70%, transparent) 72%, transparent 100%);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent); mask-image: linear-gradient(to bottom, #000 60%, transparent); }
.feed-top .feed-filters { margin: 0; padding: 4px 18px; }
@media (max-width: 719px) { .feed-top .feed-filters { margin-top: 0; } }
.feed-top:has(.feed-filters:empty) { padding-top: 16px; padding-bottom: 0; }
/* Solid behind the chips, then a short blurred fade below them (the fade
   overlaps the first cards rather than pushing them down) */
.feed-top { padding-bottom: 26px; margin-bottom: -18px; }
.feed-top::before {
  background: linear-gradient(to bottom, var(--surface) calc(100% - 26px), color-mix(in srgb, var(--surface) 60%, transparent) calc(100% - 12px), transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent); mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent); }
.fchip.add, .fchip.route.fav, .fchip.clear { background: var(--surface); }
/* Same frosted glass as the tab bar at the bottom (cards pass behind the
   solid chips), and only once the feed is scrolled: at rest it's plain */
.feed-top { padding-bottom: 8px; margin-bottom: 0; }
.feed-top::before {
  background: color-mix(in srgb, var(--surface) 70%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.5); backdrop-filter: blur(14px) saturate(1.5);
  -webkit-mask-image: none; mask-image: none;
  opacity: 0; transition: opacity .2s ease; }
.sheet.scrolled .feed-top::before { opacity: 1; }
.feed-top .fchip:not(.route) { background: var(--control); } /* solid */
/* One height for the filter row: chips and the route search box are both 38px */
.fchip { height: 38px; border-radius: 19px; padding: 0 14px; }
.froute-field { height: 38px; border-radius: 19px; }
/* Desktop has no corner arrow, so the row doesn't need the extra room above it */
@media (min-width: 720px) { .feed-top { padding-top: 10px; } }

/* ---- Phones with the keyboard up: the card fits the visible area ----
   It sits just above the keyboard and never taller than what's visible, so a
   focused search box or vibe composer stays on screen. The tab bar tucks away
   while typing, like native apps. */
@media (max-width: 719px) {
  body.kb-open #sheet { bottom: calc(var(--kb, 0px) + 8px); min-height: 0; max-height: calc(var(--vv-h, 100%) - 16px); transition: none; }
  body.kb-open #sheet.full { min-height: calc(var(--vv-h, 100%) - 16px); }
  body.kb-open #sheet .dock { display: none; }
  body.kb-open .follow, body.kb-open .trip-note-pill, body.kb-open .size-btn.floating { display: none; }
}

/* North-up button (with ?rotate): under the locate button; the needle's red
   end points north */
.compass { top: calc(var(--top-gap) + 136px); }
.compass[hidden] { display: none; }
.compass .needle { width: 10px; height: 26px; transition: transform .1s linear;
  background: linear-gradient(to bottom, #e2474e 0 50%, var(--fg) 50% 100%); clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); }
body:has(#sheet.full) .compass, body.panel-open .compass { visibility: hidden; }
/* Route stop names are markers now (they move with the map when it rotates):
   the label sits to the right of its stop, centred on it */
.route-stop-label-anchor { overflow: visible; }
.route-stop-label-anchor .route-stop-label { position: absolute; top: 0; transform: translateY(-50%); }
/* Stop names with map rotation on: our own layer over the map (see app.js) */
.route-label-overlay { position: absolute; inset: 0; z-index: 450; pointer-events: none; overflow: hidden; }
.route-label-overlay.zooming { visibility: hidden; }
.route-label-slot { position: absolute; left: 0; top: 0; width: 0; height: 0; }
.route-label-slot .route-stop-label { position: absolute; top: 0; transform: translateY(-50%); }
/* Rotation mode's SVG route layer: only its shapes take taps, not the empty area */
.leaflet-pane > svg { pointer-events: none; }
.leaflet-pane > svg .leaflet-interactive { pointer-events: visiblePainted; }
