/* The app's shell (P2.22 step 2): the navigation, the panel/sheet wrapper,
   the map's own position, and Leaflet's zoom/locate controls — everything
   docs/design.md's "Layout" table governs. Split out of unified.css in the
   same commit (docs/architecture.md: unified.css was already past the
   800-line must-split threshold before this step added anything).

   Mobile-first: the rules below with no media query are the "under 600 px"
   column (phone — the map full screen, a floating pill, a bottom sheet);
   `@media (min-width: 600px)` is the "600 px and wider" column (a fixed
   ~400 px left panel). One breakpoint, matching docs/design.md's own
   table — every other file in this app that cared about a breakpoint used
   640px; this step moves all of them (map.js's _centerForSheet, ui.js's
   sheet-drag isMobile()) to the same 600px, named here once. */

/* ── The map ──────────────────────────────────────────────────────────────
   Phone: full screen — the navigation and the board float/sheet over it.
   Panel: starts right of the fixed-width panel, fills the rest. */
#map { position: fixed; inset: 0; z-index: 1; }

/* ── #panel: a plain wrapper, inert on phones ───────────────────────────
   On phones it has no layout rules of its own — #navbar and #sidebar stay
   position:fixed (viewport-relative; a static ancestor with no transform
   doesn't break that) and float independently over the map. From 600px it
   becomes the actual fixed, sized panel, and #navbar/#sidebar turn into
   plain static flex children stacked inside it — this is what lets one
   markup serve both the floating-pill-over-sheet layout and the
   nav-card-over-board panel without a JS-driven layout switch. */
#panel { display: block; }

@media (min-width: 600px) {
  /* Both #panel and #map read the same clamp() — #panel's own width and
     the left edge Leaflet's #map div (and so its own internal coordinate
     system, tile grid and control corners) starts from — but only once
     #sidebar actually has something open (stop/plan/favourites, or a
     tapped bus). The *default* (no #sidebar.open — map mode, including
     the very first paint, since the implicit map-mode load with no URL
     params never calls setMode()) keeps #map full width and shrinks
     #panel to just the floating navy nav card: until step 4 ("Map and
     list") gives this panel real content, reserving a permanent ~400px
     blank white rectangle under the card would cost every desktop/tablet
     rider a third of their map for nothing (review round 1, finding #3).
     Keyed off #panel's own child via :has(), not a JS-toggled class, so
     it's correct before any JS has run; ui.js's _sidebarOpen() still
     calls map.invalidateSize() on every #sidebar.open toggle, since a
     CSS width change alone doesn't fire Leaflet's own resize listener —
     without it the tile grid and the zoom/locate controls stay sized for
     whichever width was current when the map was first measured. */
  #map { left: 0; }
  #panel {
    position: fixed; top: 0; left: 0; bottom: 0;
    width: auto;
    display: flex; flex-direction: column;
    background: transparent;
    box-shadow: none;
    z-index: 1000;
    overflow: hidden;
  }
  #panel:has(#sidebar.open) {
    width: clamp(360px, 26vw, 420px);
    background: var(--surface);
    box-shadow: var(--shadow-panel);
  }
  /* #map isn't inside #panel (it comes first in the DOM, so a sibling
     combinator can't reach it from #panel either way) — keyed off body
     instead, same #sidebar.open condition. */
  body:has(#sidebar.open) #map { left: clamp(360px, 26vw, 420px); }
}

/* ── Navigation ───────────────────────────────────────────────────────────
   Phone: a floating navy pill, 12px from the top, inset 16px each side.
   Panel: the top of the panel — a navy card (mark, title, tabs) on a plain
   white strip, so it reads as the panel's own header rather than a second
   floating element glued above it. */
#navbar {
  position: fixed; top: 12px; left: 16px; right: 16px;
  background: var(--navy);
  border-radius: var(--radius-pill);
  padding: 4px;
  box-shadow: var(--shadow-nav);
  z-index: 1000;
}
.nav-card { display: flex; align-items: center; gap: 8px; }
.nav-brand { display: flex; align-items: center; gap: 8px; flex-shrink: 0; margin-left: 4px; }
.nav-mark { width: 28px; height: 28px; border-radius: 6px; flex-shrink: 0; }
.nav-title { display: none; }   /* phone: no room — mark + tabs only, per the canvas */
/* Theme toggle + the stale-feed pill: not drawn on the canvas's nav (it
   shows only the mark and the four tabs), but dark mode and the staleness
   notice are both still load-bearing (dark mode is in the browser tier's
   checklist at every viewport) — kept reachable as a small trailing
   cluster rather than invented a new home mid-step. Neither is in the
   44px GATE list (map controls / nav tabs / stop header buttons /
   departure rows) — same INFO-only treatment step 1 already reported them
   under. */
.nav-utility { display: flex; align-items: center; gap: 2px; margin-left: auto; flex-shrink: 0; }
.nav-tabs { display: flex; gap: 4px; flex: 1; min-width: 0; }

.nav-tab, .nav-tab.active {
  flex: 1; text-align: center;
  font-size: 12.5px; font-weight: 600;
  border: none; border-radius: var(--radius-pill);
  min-height: 44px; display: flex; align-items: center; justify-content: center;
  cursor: pointer; background: transparent; color: var(--nav-tab-ink);
  transition: background .15s, color .15s;
}
.nav-tab.active { font-weight: 700; color: var(--navy); background: #fff; }
.nav-tab:hover:not(.active) { background: rgba(255,255,255,.12); color: #fff; }

@media (min-width: 600px) {
  #navbar {
    position: static; background: transparent; box-shadow: none; border-radius: 0;
    /* Map mode (no panel): 16px in, top and left only. Panel mode
       (below): the usual 14px panel gutter on every side but the
       bottom. Either way #navbar itself still just wraps .nav-card at
       that card's own width (below) — #panel's width:auto in map mode
       no longer has anything to do with the card's size (review round
       1's second finding: it used to, and the card collapsed to its
       own min-content, squashing "Map" into a circle and running
       "Stop Plan Saved" together with no pill spacing). */
    padding: 16px 0 0 16px; flex-shrink: 0;
  }
  .nav-card {
    flex-direction: column; align-items: stretch; gap: 10px;
    background: var(--navy); border-radius: var(--radius-card); padding: 14px;
    /* design.md's floating-nav shadow — the same one the phone pill uses
       — since in map mode this card *is* the floating nav, not a panel
       header sitting on var(--shadow-panel)'s own depth. */
    box-shadow: var(--shadow-nav);
    /* Review round 1, finding #2: in map mode nothing upstream of here
       gives the card a real width any more (#panel is width:auto, see
       above), so without this it shrinks to its own min-content — "Map"
       becomes a tiny circle, the other three tabs run together with no
       pill spacing. Match the panel's own content width exactly: the
       same clamp() #panel/#map use, minus the 14px+14px #navbar side
       padding panel mode gives it (body:has(#sidebar.open) below reverts
       to auto, where panel mode's own stretch-to-#panel's-width already
       produces this identical number — this is only needed as an
       explicit value where that stretch has nothing to size against). */
    width: calc(clamp(360px, 26vw, 420px) - 32px);
  }
  /* The card does not move or resize between modes (owner, review r1): the
     same 16px inset and the same explicit width with the panel open or shut. */
  .nav-brand { margin-left: 0; }
  .nav-mark { width: 26px; height: 26px; }
  .nav-title { display: block; color: #fff; font-size: 15px; font-weight: 800; }
  .nav-tabs { background: rgba(255,255,255,.1); border-radius: var(--radius-pill); padding: 4px; }
}

/* The quick nav-search dropdown: superseded, for now, by the Stop tab's own
   search (#stop-search) — there's no room for it in the exact floating
   pill the canvas draws (mark + tabs only), and the full-screen Search view
   that would give it a proper home is step 6. Elements kept in the DOM
   (ui.js's setupNavSearch() wires them unconditionally at load time; an
   absent #nav-search would throw there, taking down every other module
   that imports ui.js) — just never shown. */
#nav-search-wrap { display: none; }
#nav-dropdown {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0;
  background: var(--surface); border: 1.5px solid var(--line); border-radius: 8px;
  max-height: 280px; overflow-y: auto; display: none;
  box-shadow: 0 6px 20px rgba(0,0,0,.2); z-index: 1100;
}
.nav-drop-item {
  padding: 9px 12px; cursor: pointer; font-size: 13px; color: var(--ink);
  display: flex; justify-content: space-between; align-items: center;
  border-bottom: 1px solid var(--line);
}
.nav-drop-item:last-child { border-bottom: none; }
.nav-drop-item:hover, .nav-drop-item.active { background: var(--surface-raised); }
.nav-drop-item .nm { font-weight: 500; }
.nav-drop-item .cd { font-size: 11px; color: var(--ink-muted); background: var(--surface-raised); padding: 2px 6px; border-radius: 4px; white-space: nowrap; }
#nav-search-clear {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 20px; height: 20px; border-radius: 50%; border: none;
  background: rgba(255,255,255,.15); color: #cbd5e1;
  font-size: 14px; line-height: 1; cursor: pointer;
  display: none; padding: 0;
}

/* Stale-feed pill + dark-mode toggle — moved byte-identical from
   unified.css (still navy-background styles, now sitting in .nav-utility
   instead of the old full-width #navbar). */
#stale-pill {
  display: none; align-items: center; gap: 6px;
  padding: 3px 9px; border-radius: var(--radius-pill);
  background: rgba(220, 38, 38, .15); color: #fecaca;
  font-size: 11px; font-weight: 700;
  border: 1px solid rgba(220, 38, 38, .35);
}
#stale-pill.visible { display: inline-flex; }
#stale-pill .dot {
  width: 6px; height: 6px; border-radius: 50%; background: #ef4444;
  animation: stale-pulse 1.6s ease-in-out infinite;
}
@keyframes stale-pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
#theme-toggle {
  background: transparent; border: 1.5px solid rgba(255,255,255,.2);
  color: #cfe1f8; cursor: pointer; padding: 6px 9px;
  border-radius: 7px; font-size: 14px; line-height: 1;
  transition: background .15s;
}
#theme-toggle:hover { background: rgba(255,255,255,.1); color: #fff; }
body.dark #theme-toggle:hover { background: rgba(255,255,255,.05); }

/* ── The board: a bottom sheet on phones, the rest of the panel from
   600px ──────────────────────────────────────────────────────────────── */
#sidebar {
  position: fixed; left: 0; right: 0; bottom: 0; top: auto;
  width: 100%; max-width: 100%;
  height: 45vh; max-height: 92vh;
  background: var(--surface); color: var(--ink);
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  box-shadow: var(--shadow-sheet);
  transform: translateY(100%);
  transition: transform .25s ease;
  z-index: 1000;
  display: flex; flex-direction: column; overflow: hidden;
}
#sidebar.open { transform: translateY(0); }
/* The map-overlay is never shown: the sheet leaves the map visible above
   it and it stays fully pannable/zoomable — dismissal is the handle, the
   back arrow or dragging down, not a tap-anywhere scrim. */
#map-overlay { display: none !important; }

/* A real 44px button (design.md: "a 40×4 px handle inside a 44 px
   button"), not the old decorative ::before — tappable for keyboard/click
   users, and still the touch-drag start zone ui.js's setupSheetDrag()
   already detects by touch Y position, unchanged. */
.sheet-handle {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 44px; flex-shrink: 0;
  border: none; background: transparent; cursor: pointer; padding: 0;
}
.sheet-handle span {
  display: block; width: 40px; height: 4px; border-radius: 2px;
  background: rgba(0,0,0,.18);
}
body.dark .sheet-handle span { background: rgba(255,255,255,.22); }

.sb-header {
  padding: 12px 14px; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between;
  background: var(--surface); flex-shrink: 0;
}
.sb-title { font-size: 14px; font-weight: 700; color: var(--ink); }
.sb-close {
  background: none; border: none; cursor: pointer; color: var(--ink-muted);
  font-size: 20px; padding: 0 var(--space-8); line-height: 1; border-radius: 5px;
}
.sb-close:hover { background: var(--surface-raised); color: var(--ink); }
.sb-body { flex: 1; padding: 14px; overflow-y: auto; }

/* The vehicle popup and (new) the stop board replace the generic header
   with their own — same pattern, two consumers. */
.sb-body.vehicle-mode { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.sb-body.vehicle-mode .veh-header { margin: 0; flex-shrink: 0; }
.sb-body.vehicle-mode .veh-stops-scroll { flex: 1; overflow-y: auto; padding: 8px 8px 16px; }
#sidebar.vehicle-open .sb-header { display: none; }
/* From 600px the bus sheet leaves the same 16px gap under the navigation card
   as the stop board (the Bus_* boards), instead of sitting flush. */
@media (min-width: 600px) { .sb-body.vehicle-mode { margin-top: 16px; } }
#sidebar.stop-board-open .sb-header { display: none; }

@media (min-width: 600px) {
  /* #sidebar without .open: collapsed, not just off-screen. On phones the
     base rule's transform:translateY(100%) already hides a closed sheet;
     naively resetting that to transform:none here (as an earlier draft of
     this rule did) made the panel permanently show whatever #sb-body last
     held — "Map" mode calls _sidebarOpen(false) (ui.js) and never touches
     #sb-body again, so the panel was left showing a stale "Stop
     departures" header with nothing under it, found by screenshotting
     this step's own 844x390/map run. #navbar (the nav card) stays outside
     this rule and is always visible, matching every canvas board — only
     the *board* collapses with the map, not the nav. This same .open
     state (via :has(), above) also decides whether #panel/#map are in
     map mode (no board at all — full-width map, a floating nav card) or
     panel mode (a board is open — the fixed-width panel and the map
     offset) — round 1's fix for the panel's own blank space in map mode. */
  #sidebar:not(.open) { display: none; }
  #sidebar.open {
    position: static; width: auto; max-width: none;
    height: auto; max-height: none;
    transform: none; transition: none;
    box-shadow: none; border-radius: 0;
    flex: 1; min-height: 0;
  }
  .sheet-handle { display: none; }   /* no handle in the panel layout */
}

/* ── Map controls: zoom and locate, both 44×44 ───────────────────────────
   Phone: zoom top-left 16px in, just under the nav pill; locate top-right,
   mirroring it. Panel: zoom top-left of the *map* 24px in; locate moves to
   the map's bottom-right 24px in. Real Leaflet controls, restyled and
   repositioned via their own corner containers (map.js/live.js create
   them; nothing else here uses the top-right corner, so moving that whole
   container at 600px is safe — docs/testing.md's worktree-serving note
   doesn't apply to pure CSS). */
.leaflet-top.leaflet-left,
.leaflet-top.leaflet-right {
  top: 68px;
}
.leaflet-top.leaflet-left { left: 16px; }
.leaflet-top.leaflet-right { right: 16px; }
.leaflet-top.leaflet-left .leaflet-control,
.leaflet-top.leaflet-right .leaflet-control {
  margin: 0;
}

.leaflet-control-zoom {
  border-radius: var(--radius-zoom-group) !important;
  overflow: hidden;
  box-shadow: var(--shadow-map-control);
  border: none !important;
}
.leaflet-control-zoom a {
  /* !important: leaflet.min.css's own .leaflet-touch .leaflet-bar a rule
     (0,2,1) is more specific than this selector (0,1,1) and would
     otherwise win regardless of source order, pinning these back to its
     30px touch-device size (measured: every touch-capable viewport in the
     browser tier still showed 30x30 without this). Same reason for
     background/color: unified.css's existing `body.dark .leaflet-bar a`
     override (0,2,2) also matches these <a> tags (descendants of this
     control's own .leaflet-bar class) and otherwise wins over this
     selector's (0,1,1) in dark mode, pinning them to its own literal
     #25272f/#e6e8eb instead of the tokens below. */
  width: 44px !important; height: 44px !important; line-height: 44px !important;
  background: var(--surface) !important;
  color: var(--ink-muted) !important;
  border: none !important;
}
.leaflet-control-zoom a:hover { background: var(--surface-raised) !important; color: var(--navy) !important; }
body.dark .leaflet-control-zoom a:hover { color: #93c5fd !important; }
/* Same specificity reason as the width/height rule above: leaflet.min.css
   sets its own touch-device font-size on these two classes at (0,2,1). */
.leaflet-control-zoom-in { font-size: 19px !important; border-bottom: 1px solid var(--line) !important; }
.leaflet-control-zoom-out { font-size: 21px !important; }

.locate-control {
  background: var(--surface);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-map-control);
  cursor: pointer;
}
.locate-control button {
  width: 44px; height: 44px; padding: 0; margin: 0;
  background: transparent; border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-muted);
}
.locate-control button:hover { color: var(--navy); }
body.dark .locate-control button:hover { color: #93c5fd; }
.locate-control.loading button svg { animation: spin .8s linear infinite; color: var(--navy); }
.locate-control button svg { width: 20px; height: 20px; }

@media (min-width: 600px) {
  /* Map mode (no panel, just the floating nav card): zoom sits clear
     below the card, at the same 16px inset the card itself uses — the
     card's own rendered height (mark+title row, a 10px gap, the 44px tab
     row, 14px padding all round, 16px above it) comes to ~132px; 160px
     leaves comfortable clearance. Panel mode: zoom moves to the map's own
     top-left, 24px in, since the panel already reserves the space the
     card used to need room for. */
  .leaflet-top.leaflet-left { top: 160px; left: 16px; }
  body:has(#sidebar.open) .leaflet-top.leaflet-left { top: 24px; left: 24px; }
  /* The locate control is created with options:{position:'topright'}
     (map.js) so it stays a real Leaflet control; from 600px its corner
     container is simply anchored to the opposite edge — bottom-right of
     the map, 24px in, per docs/design.md's Layout table — rather than
     re-registered under a different Leaflet position on every resize.
     Unaffected by map-vs-panel mode: the floating card and the panel both
     only ever occupy the left side. */
  .leaflet-top.leaflet-right { top: auto; bottom: 24px; right: 24px; }
}
