/* ===== CSS Variables ===== */
:root {
  --bg-primary: #0a0a0a;
  --bg-secondary: #141414;
  --bg-tertiary: #222222;
  --bg-card: #1a1a1a;
  --text-primary: #ffffff;
  /* Both raised to Motocamp's values (05: A2). #5a6f82 was 2.9:1 on the
     panel and 2.5:1 on a field, for every secondary line on a phone read at
     arm's length in sunlight; WCAG AA for body text is 4.5:1. Now 5.7:1 and
     7.3:1 on the panel (pinned by scripts/qa/phone-readability-test.js). */
  --text-secondary: #b8b8b8;
  --text-muted: #a0a0a0;
  /* --text, --border and --bg-elev were used (reports panel, guide, recorder)
     but never declared. A var() with no value and no fallback invalidates its
     whole declaration, so the user-reports box rendered with NO border: the
     block that must never pass for the land manager's notice lost its frame. */
  --text: var(--text-primary);
  --border: rgba(255, 255, 255, 0.1);
  --bg-elev: rgba(0, 0, 0, 0.2);
  --accent: #c8f53a;
  --accent-hover: #d9ff5c;
  --accent-dim: rgba(200, 245, 58, 0.15);
  --danger: #ff5252;
  --warning: #ffab40;
  --info: #40c4ff;
  --verizon-color: #ee0000;
  --att-color: #00a8e0;
  --tmobile-color: #e20074;
  --sidebar-width: 380px;
  /* Height of the fixed notification bars stacked at the top of the viewport
     (#free-tier-bar, #coverage-bar). They are position:fixed at top:0 with a
     z-index above the whole UI, so anything else anchored near the top is
     simply BURIED under them — measured with the coverage bar up: the info
     panel's own title sat at y=25 under a bar whose bottom edge was y=35.
     app.js's syncTopBarInset() writes the real measured value here whenever a
     bar appears, changes copy (it re-wraps, so its height changes) or goes;
     0px is the truth when no bar is up. */
  --top-bar-height: 0px;
  /* THE SYSTEM BAR AT THE BOTTOM OF THE SCREEN (Android's gesture/home bar, an
     iPhone's home indicator). Measured 48px on a Galaxy S23 in the Capacitor
     app; 0px on desktop and on 3-button-nav devices. EVERY bottom-anchored
     fixed container adds it, and the rule is absolute (Josh, 2026-08-05: "do
     not have any buttons below or covered by that bottom bar ever") — so it is
     a token rather than a repeated env() call, and the boot smoke overrides
     this one property to a test value and then asserts that NOTHING
     interactive is left inside the zone. */
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  /* THE STATUS BAR AT THE TOP. index.html sets viewport-fit=cover with a
     translucent status bar, so on an installed iPhone the page runs under the
     clock and the Dynamic Island; only the bottom inset was read, and the Menu
     button and nav's only exit sat under them (05: A3; Motocamp USA lesson
     11699). 0px everywhere without a notch. The header grows by it, so every
     element offset by --mobile-header-height moves down with it. NOT verified
     on an iPhone (none here; iOS is an open item in CLAUDE.md). */
  --safe-top: env(safe-area-inset-top, 0px);
  --mobile-header-height: calc(56px + var(--safe-top));
  --radius: 8px;
  --radius-sm: 4px;
  --shadow: 0 4px 24px rgba(0,0,0,0.4);
  --transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

/* ===== Reset ===== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; overflow: hidden; }
body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--bg-primary);
  color: var(--text-primary);
  -webkit-font-smoothing: antialiased;
}

/* ===== Login Screen ===== */
#login-screen.mode-pending .login-card > * { visibility: hidden; }
#login-screen {
  position: fixed; inset: 0;
  background: var(--bg-primary);
  display: flex; align-items: center; justify-content: center;
  z-index: 10001;
  transition: opacity 0.4s ease;
}
#login-screen.fade-out { opacity: 0; pointer-events: none; }
/* A phone that already holds a session never sees the card while the session
   is checked (the <head> script in index.html sets the class before first
   paint and takes it off again if the card turns out to be needed). The CARD is
   hidden; the SCREEN stays, transparent, so it still swallows taps until Auth
   has admitted the account. Do not change this to display:none or
   pointer-events:none: an un-admitted tap on a paid control opens the upgrade
   sheet on a paying customer. */
html.stored-session #login-screen { background: transparent; }
html.stored-session #login-screen .login-card { visibility: hidden; }
.login-card {
  display: flex; flex-direction: column; align-items: center;
  padding: 32px 28px;
  background: var(--bg-secondary);
  border-radius: 16px;
  border: 1px solid var(--border, rgba(255,255,255,0.06));
  width: min(360px, 90vw);
}
.login-input {
  width: 100%; padding: 10px 12px;
  border-radius: var(--radius-sm, 6px);
  border: 1px solid var(--border, rgba(255,255,255,0.1));
  background: var(--bg-tertiary); color: var(--text-primary);
  /* 16 px text and a 48 px box (2026-11-04 e2e: 39 px): a thumb-sized
     target, and under 16 px an iPhone zooms the page into the field. */
  font-size: 16px; font-family: inherit; min-height: 48px; box-sizing: border-box;
  margin-bottom: 8px; outline: none;
}
.login-input:focus { border-color: var(--accent); }
.login-error {
  font-size: 0.75rem; color: #ff5252;
  margin-bottom: 8px; display: none;
  text-align: center;
}
.login-btn {
  width: 100%; padding: 10px; font-size: 0.88rem;
  border-radius: var(--radius-sm, 6px);
  cursor: pointer; margin-bottom: 6px;
}
#login-submit { min-height: 48px; font-size: 1rem; }   /* was 33 px tall (2026-11-04 e2e) */
/* Social sign-in ("Continue with …") — the #login-social block is injected-into
   by Auth._renderSocialButtons and stays display:none unless a provider is
   enabled in Auth.AUTH_PROVIDERS, so regions with none configured render the
   card exactly as before. Brand-minimal buttons: each provider's canonical
   colours, vendored FontAwesome brand glyphs, no external assets. */
#login-social { width: 100%; }
.login-divider {
  display: flex; align-items: center; gap: 10px;
  width: 100%; margin: 10px 0 12px;
  font-size: 0.68rem; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.06em;
  white-space: nowrap;
}
.login-divider::before, .login-divider::after {
  content: ''; flex: 1; height: 1px; background: rgba(255, 255, 255, 0.12);
}
.social-btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; padding: 10px; margin-bottom: 8px;
  border-radius: var(--radius-sm, 6px);
  border: 1px solid transparent;
  font-size: 0.85rem; font-weight: 600; font-family: inherit;
  cursor: pointer; transition: filter 0.15s ease;
}
.social-btn:hover { filter: brightness(1.08); }
.social-btn i { font-size: 1rem; }
.social-btn-google   { background: #fff;     color: #1f1f1f; border-color: #dadce0; }
.social-btn-google i { color: #4285f4; }
.social-btn { text-decoration: none; }
.social-btn-facebook { background: #1877f2;  color: #fff; }
.social-btn-apple    { background: #000;     color: #fff; border-color: rgba(255, 255, 255, 0.25); }
/* ===== Loading Screen ===== */
#loading-screen {
  position: fixed; inset: 0;
  background: var(--bg-primary);
  display: flex; align-items: center; justify-content: center;
  z-index: 10000;
  transition: opacity 0.5s ease;
}
#loading-screen.fade-out { opacity: 0; pointer-events: none; }
.loader-content { text-align: center; }
.loader-icon { font-size: 3rem; color: var(--accent); margin-bottom: 1rem; animation: pulse 1.5s infinite; }
.loader-content h2 { font-size: 1.5rem; font-weight: 700; margin-bottom: 0.5rem; }
.loader-content p { color: var(--text-secondary); margin-bottom: 1.5rem; }
.loader-bar { width: 200px; height: 3px; background: var(--bg-tertiary); border-radius: 3px; overflow: hidden; margin: 0 auto; }
.loader-progress { height: 100%; width: 30%; background: var(--accent); border-radius: 3px; animation: loading 1.5s ease-in-out infinite; }
@keyframes loading { 0% { transform: translateX(-100%); } 100% { transform: translateX(400%); } }
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }

/* ===== Mobile Header ===== */
#mobile-header {
  display: none;
  position: fixed; top: 0; left: 0; right: 0;
  height: var(--mobile-header-height);
  background: var(--bg-secondary);
  border-bottom: 1px solid var(--bg-tertiary);
  align-items: center;
  justify-content: space-between;
  padding: var(--safe-top) calc(12px + env(safe-area-inset-right, 0px)) 0 calc(12px + env(safe-area-inset-left, 0px));
  z-index: 1001;
}
#mobile-header h1 { font-size: 1.1rem; font-weight: 700; }
#mobile-header button {
  background: none; border: none; color: var(--text-primary);
  font-size: 1.2rem; padding: 8px; cursor: pointer;
}
/* The word next to the hamburger, which names the destination ("Menu" over the
   map, "Map" over the menu — js/app.js syncSidebarToggle). Sized down from the
   icon so it reads as a caption on the control rather than competing with the
   title beside it, and the fixed min-width stops the header shifting as the
   word changes length. */
#sidebar-toggle {
  display: flex; align-items: center; gap: 6px;
}
#sidebar-toggle-label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  min-width: 34px;
  text-align: left;
}

/* ===== Sidebar ===== */
.sidebar {
  position: fixed; top: var(--top-bar-height); left: 0; bottom: 0;
  width: var(--sidebar-width);
  background: var(--bg-secondary);
  border-right: 1px solid var(--bg-tertiary);
  display: flex; flex-direction: column;
  z-index: 1000;
  transition: transform var(--transition);
  overflow: hidden;
  /* Runs to bottom:0, so without this the footer's own links — the OSM/DOC
     attribution, Privacy, Terms, Contact — sit UNDER the system bar. Measured
     on the phone: 10px, 35px, 35px, 35px of overlap. */
  padding-bottom: var(--safe-bottom);
}
.sidebar.collapsed { transform: translateX(-100%); }

/* ===== Desktop sidebar collapse (ported from motocamp-all) =====
   ONE body-level custom-property override does the whole job: zeroing
   --sidebar-width reflows #map, the map control rails, the basemap/3D pills
   and #info-panel together, because every one of them is already positioned
   off that variable. Desktop only — mobile keeps the .open overlay pattern,
   where the sidebar is already off-canvas by default. */
@media (min-width: 769px) {
  body.sidebar-collapsed { --sidebar-width: 0px; }
  body.sidebar-collapsed .sidebar { transform: translateX(-100%); }
  /* With the sidebar gone #info-panel would land at left:12px — the same
     corner the fixed #sidebar-expand (») button occupies. Clear the 38px
     button plus its gaps. */
  body.sidebar-collapsed #info-panel { left: 62px; }
  /* The route tiles (#route-hud) sit in the same corner and were added after
     this rule: the » button covered "Distance" (live, 2026-10-29). */
  body.sidebar-collapsed #route-hud { left: 62px; }
  /* MapLibre's top-left control container (the 3D compass) lives INSIDE the
     map, which now starts at x=0 — so it would sit underneath #sidebar-expand,
     which is fixed and on a higher stacking layer. Vertline-specific: the
     motocamp original has no top-left map control to collide with. */
  body.sidebar-collapsed .maplibregl-ctrl-top-left { margin-left: 50px; }
}
#sidebar-collapse {
  background: none; border: none; color: var(--text-secondary);
  font-size: 1rem; cursor: pointer; padding: 4px 8px;
}
#sidebar-collapse:hover { color: var(--text-primary); }
#sidebar-expand {
  position: fixed; top: calc(var(--top-bar-height) + 12px); left: 12px; z-index: 1001;
  width: 38px; height: 38px; border-radius: var(--radius-sm);
  background: var(--bg-secondary); border: 1px solid var(--bg-tertiary);
  color: var(--text-primary); font-size: 1rem; cursor: pointer;
  display: none; align-items: center; justify-content: center;
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
}
body.sidebar-collapsed #sidebar-expand { display: flex; }

.sidebar-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--bg-tertiary);
  flex-shrink: 0;
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand i { font-size: 1.5rem; color: var(--accent); }
.brand h1 { font-size: 1.25rem; font-weight: 700; }
#sidebar-close {
  background: none; border: none; color: var(--text-secondary);
  font-size: 1.2rem; cursor: pointer; padding: 4px;
  display: none;
}

.sidebar-body {
  flex: 1; overflow-y: auto; overflow-x: hidden;
  padding: 0;
  scrollbar-width: thin;
  scrollbar-color: var(--bg-tertiary) transparent;
}
.sidebar-body::-webkit-scrollbar { width: 6px; }
.sidebar-body::-webkit-scrollbar-track { background: transparent; }
.sidebar-body::-webkit-scrollbar-thumb { background: var(--bg-tertiary); border-radius: 3px; }

.sidebar-footer {
  padding: 12px 20px;
  border-top: 1px solid var(--bg-tertiary);
  flex-shrink: 0;
}
.sidebar-footer p { font-size: 0.7rem; color: var(--text-muted); line-height: 1.5; }

/* ===== Tab Navigation ===== */
.tab-nav {
  display: flex;
  border-bottom: 1px solid var(--bg-tertiary);
  flex-shrink: 0;
  /* Five tabs (Friends joined on 2026-08-10) do not fit a narrow phone. The
     strip SCROLLS rather than squeezing every label down to illegibility —
     a tab you cannot read is worse than one you have to swipe to. */
  overflow-x: auto;
  scrollbar-width: none;
}
.tab-nav::-webkit-scrollbar { display: none; }
.tab-btn {
  flex: 1 0 auto;
  white-space: nowrap;
  padding: 12px 8px;
  background: none; border: none;
  color: var(--text-secondary);
  font-size: 0.8rem; font-weight: 500;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: all var(--transition);
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
.tab-btn:hover { color: var(--text-primary); background: var(--bg-tertiary); }
.tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }
.tab-btn i { font-size: 0.9rem; }

/* SIX TABS MUST ALL BE SEEN (Josh, 2026-11-06: "friends overwrote the
   history tab"). With Friends and History both on, the strip scrolled and
   History sat off the right edge behind a hidden scrollbar: nothing on a
   desktop says you can swipe a tab row. Icon above label, equal shares, so
   six fit the 380 px sidebar and a 360 px phone with no scrolling. */
.tab-nav:has(.tab-btn[data-tab="history"]:not([hidden])) .tab-btn {
  flex: 1 1 0; min-width: 0;
  flex-direction: column; gap: 3px;
  padding: 9px 2px 8px; font-size: 0.68rem;
}
.tab-nav:has(.tab-btn[data-tab="history"]:not([hidden])) .tab-btn i { font-size: 1rem; }

/* .tab-btn's display:flex beats the bare [hidden] attribute, so say it: the
   Friends tab ships hidden until Auth.SOCIAL is on (js/app.js). */
.tab-btn[hidden] { display: none; }
.tab-content { display: none; }
.tab-content.active { display: block; }

/* ===== Sidebar Sections ===== */
.sidebar-section {
  padding: 16px 20px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
}
/* In-panel notice (#coverage-notice). Deliberately NOT position:fixed: this
   replaced a top-fixed banner that sat above the whole UI and covered the map's
   own control rail. Inside the panel it can only ever push content down, so it
   cannot bury a button no matter how far the copy wraps. `hidden` does the
   hiding, so there is no display value to keep in sync. */
.panel-notice {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 12px 20px 0;
  padding: 10px 12px;
  background: rgba(79,195,247,0.08);
  border: 1px solid rgba(79,195,247,0.35);
  border-radius: 8px;
  font-size: 0.78rem; line-height: 1.4;
  color: var(--text-primary, #fff);
}
.panel-notice[hidden] { display: none; }
/* .layer-toggle's display:flex beats the bare [hidden] attribute; region-only
 * rows (RegionsConfig.syncRegionOnly) must really go. */
[data-region-only][hidden] { display: none !important; }
.panel-notice i { color: #4fc3f7; flex-shrink: 0; margin-top: 1px; }
.panel-notice span { flex: 1; min-width: 0; }
.sidebar-section h3 {
  font-size: 0.8rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-secondary);
  margin-bottom: 12px;
  display: flex; align-items: center; gap: 8px;
}
.sidebar-section h3 i { color: var(--accent); font-size: 0.85rem; }
.sidebar-section h4 {
  font-size: 0.78rem; font-weight: 600;
  color: var(--text-secondary);
  margin: 12px 0 8px;
}
.hint { font-size: 0.78rem; color: var(--text-muted); margin-bottom: 12px; line-height: 1.4; }

/* ===== Search ===== */
.search-box {
  display: flex; align-items: center;
  background: var(--bg-tertiary);
  border-radius: var(--radius);
  padding: 0 12px;
  border: 1px solid transparent;
  transition: border-color var(--transition);
}
.search-box:focus-within { border-color: var(--accent); }
.search-box i { color: var(--text-muted); font-size: 0.85rem; }
.search-box input {
  flex: 1; background: none; border: none; outline: none;
  color: var(--text-primary); padding: 10px 10px;
  font-size: 0.85rem; font-family: inherit;
}
.search-box input::placeholder { color: var(--text-muted); }
.search-box button { background: none; border: none; color: var(--text-muted); cursor: pointer; padding: 4px; }
.search-results {
  margin-top: 8px;
  background: var(--bg-tertiary);
  border-radius: var(--radius);
  max-height: 240px;
  overflow-y: auto;
}
.search-results.hidden { display: none; }
.search-result-item {
  padding: 10px 14px;
  cursor: pointer;
  font-size: 0.82rem;
  border-bottom: 1px solid rgba(255,255,255,0.04);
  transition: background var(--transition);
  display: flex; align-items: center; gap: 8px;
}
.search-result-item:hover { background: rgba(255,255,255,0.05); }
.search-result-item i { color: var(--accent); width: 16px; text-align: center; }
.search-result-item:last-child { border-bottom: none; }

/* ===== Layer Toggles ===== */
.layer-toggle {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 0;
  cursor: pointer;
  font-size: 0.85rem;
}
.layer-toggle input { display: none; }
.toggle-slider {
  width: 36px; height: 20px;
  background: var(--bg-tertiary);
  border-radius: 10px;
  position: relative;
  transition: background var(--transition);
  flex-shrink: 0;
}
.toggle-slider::after {
  content: '';
  position: absolute;
  width: 16px; height: 16px;
  background: var(--text-secondary);
  border-radius: 50%;
  top: 2px; left: 2px;
  transition: all var(--transition);
}
.layer-toggle input:checked + .toggle-slider { background: var(--accent-dim); }
.layer-toggle input:checked + .toggle-slider::after { transform: translateX(16px); background: var(--accent); }
.toggle-label { flex: 1; }

/* Vehicle picker in the route planner. What you're in changes which roads get
   planned and which clearances get flagged, so it sits ABOVE the gravel/highway
   toggles — it's a bigger decision than either of them. */
.route-vehicle {
  padding: 4px 0 10px;
  border-bottom: 1px solid var(--bg-tertiary);
  margin-bottom: 4px;
}
.route-vehicle-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-bottom: 7px;
}
.route-vehicle-title {
  font-size: 0.68rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-muted, #888);
}
.route-vehicle-reset {
  background: none; border: none; padding: 0; cursor: pointer;
  font-size: 0.66rem; color: var(--accent);
  text-decoration: underline; text-underline-offset: 2px;
}
.route-vehicle-reset[hidden] { display: none; }
.route-vehicle-picker {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px;
}
.route-vehicle-btn {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 7px 3px;
  border: 1px solid var(--bg-tertiary);
  border-radius: 6px;
  background: transparent;
  color: var(--text-secondary);
  font-size: 0.62rem;
  cursor: pointer;
  transition: all var(--transition);
  min-width: 0;
}
.route-vehicle-btn i { font-size: 0.92rem; }
.route-vehicle-btn span { overflow-wrap: anywhere; text-align: center; line-height: 1.2; }
.route-vehicle-btn:hover { border-color: var(--text-muted, #888); color: var(--text-primary); }
.route-vehicle-btn[aria-pressed="true"] {
  border-color: var(--accent);
  background: rgba(200, 245, 58, 0.12);
  color: var(--text-primary);
}
.route-vehicle-btn[aria-pressed="true"] i { color: var(--accent); }
.route-vehicle-tow {
  display: flex; align-items: center; gap: 7px;
  margin-top: 8px;
  font-size: 0.76rem;
  cursor: pointer;
}
.route-vehicle-tow[hidden] { display: none; }
.route-vehicle-note {
  font-size: 0.66rem;
  color: var(--text-muted, #888);
  margin-top: 7px;
  line-height: 1.45;
}
.route-vehicle-note:empty { display: none; }
.route-vehicle-note .warn { color: #ffab40; }

.badge {
  font-size: 0.65rem; font-weight: 600;
  padding: 2px 6px; border-radius: 3px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.badge-green { background: rgba(200,245,58,0.15); color: #c8f53a; }
.badge-blue { background: rgba(64,196,255,0.15); color: #40c4ff; }
.badge-orange { background: rgba(255,171,64,0.15); color: #ffab40; }
.badge-purple { background: rgba(176,128,255,0.15); color: #b080ff; }
.badge-teal { background: rgba(38,198,178,0.15); color: #26c6b2; }
.badge-yellow { background: rgba(255,230,0,0.15); color: #ffe600; }
.badge-red { background: rgba(230,0,0,0.15); color: #ff5252; }

/* ===== Base Map Selector ===== */
.basemap-selector { display: flex; gap: 6px; }
.basemap-btn {
  flex: 1; padding: 8px;
  background: var(--bg-tertiary);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 0.75rem; font-weight: 500;
  cursor: pointer;
  transition: all var(--transition);
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}
.basemap-btn i { font-size: 1rem; }
.basemap-btn:hover { color: var(--text-primary); background: rgba(255,255,255,0.08); }
.basemap-btn.active { border-color: var(--accent); color: var(--accent); background: var(--accent-dim); }

/* ===== Filter Chips ===== */
.filter-group { display: flex; flex-wrap: wrap; gap: 6px; }
.filter-chip {
  display: flex; align-items: center; gap: 4px;
  padding: 6px 10px;
  background: var(--bg-tertiary);
  border-radius: 20px;
  font-size: 0.75rem;
  cursor: pointer;
  transition: all var(--transition);
  border: 1px solid transparent;
  user-select: none;
}
.filter-chip:hover { background: rgba(255,255,255,0.08); }
.filter-chip input { display: none; }
.filter-chip:has(input:checked) { border-color: var(--accent); background: var(--accent-dim); color: var(--accent); }
.filter-chip i { font-size: 0.7rem; }

/* ===== Route Planning ===== */
.route-inputs { display: flex; flex-direction: column; gap: 4px; }
.route-point {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg-tertiary);
  border-radius: var(--radius-sm);
  padding: 0 12px;
}
.route-point input {
  flex: 1; background: none; border: none; outline: none;
  color: var(--text-primary); padding: 10px 0;
  font-size: 0.85rem; font-family: inherit;
}
.route-point input::placeholder { color: var(--text-muted); }
/* Route search results dropdown. `top: 100%` is load-bearing: the list sits in
   a flex row (.route-point) that centres its children, and an absolutely
   positioned box with no `top` takes its static position, which here is ON TOP
   of the field being typed into. That exact rule stopped a paying Motocamp
   trial user on an iPad from entering a start point. */
.route-search-results {
  position: absolute;
  top: 100%;
  left: 0; right: 0;
  z-index: 9999;
  margin-top: 2px;
  max-height: 200px;
  overflow-y: auto;
  box-shadow: 0 6px 20px rgba(0,0,0,0.4);
  border: 1px solid rgba(255,255,255,0.08);
}
/* On a phone the list stays anchored under its own field (it used to be pinned
   52px from the top of the screen, nowhere near the field it belonged to), with
   its height capped so it ends above the on-screen keyboard. Flipping it above
   the field when the keyboard leaves no room needs JS positioning in route.js;
   see docs/port/boot-ui.md. */
@media (max-width: 768px) and (orientation: portrait) {
  .route-search-results { max-height: 35vh; }
}
@media (max-width: 768px) and (orientation: landscape) {
  .route-search-results { max-height: 30vh; }
}
.route-start-icon { color: var(--accent); font-size: 0.6rem; }
.route-locate-btn {
  flex-shrink: 0;
  padding: 6px 8px;
  border: none;
  color: var(--accent);
  background: none;
  font-size: 1rem;
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.2s;
}
.route-locate-btn:hover { opacity: 1; }
.route-locate-btn.locating { animation: pulse-locate 1s infinite; }
@keyframes pulse-locate { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } }
.route-end-icon { color: var(--danger); font-size: 0.85rem; }
.route-waypoint-icon { color: var(--info); font-size: 0.6rem; }
/* "Stay here overnight" on a stop row: grey until set, then the amber of the
   bed pin it puts on the map (route.js WP_STYLE.night). */
.route-night-btn {
  flex-shrink: 0; padding: 4px; border: none; background: none;
  color: var(--text-muted); cursor: pointer; opacity: 0.6;
}
.route-night-btn:hover { opacity: 1; }
.route-night-btn.on { color: #ffb300; opacity: 1; }
/* A point dragged onto the route: a small hollow white ring, never the blue
   stop dot (route.js WP_STYLE.drag). */
.route-drag-ring {
  display: block; width: 12px; height: 12px; margin: 3px;
  border-radius: 50%; border: 2.5px solid #fff; box-sizing: border-box;
  box-shadow: 0 0 0 2px rgba(10,15,26,0.85);
}

.route-actions { display: flex; gap: 8px; margin-top: 8px; }
.route-dates { margin-top: 4px; }
.date-inputs { display: flex; gap: 8px; }
.date-field { flex: 1; }
.date-field label { display: block; font-size: 0.72rem; color: var(--text-muted); margin-bottom: 4px; }
.date-field input {
  width: 100%; padding: 8px 10px;
  background: var(--bg-tertiary);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 0.82rem; font-family: inherit;
  outline: none;
}
.date-field input:focus { border-color: var(--accent); }
/* The Route tab's "Leaving" date and time. Left unstyled they were the
   browser's white boxes on a dark panel (Josh, 2026-10-29: "white boxes, it's
   ugly"). Same field look as .date-field; color-scheme dark makes the native
   calendar/clock icons and pickers dark too, not just the box. The send
   sheet's date, time and safety boxes (.sheet-field, js/trip-share.js) were
   white for the same reason (Josh, 2026-10-30: "white box, make it system
   colours"), so they share the look. */
.route-when input, .sheet-field {
  padding: 8px 10px;
  background: var(--bg-tertiary);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-size: 0.82rem; font-family: inherit;
  outline: none;
  color-scheme: dark;
}
.route-when input:focus, .sheet-field:focus { border-color: var(--accent); }

/* ===== Buttons ===== */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px;
  background: var(--bg-tertiary);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 0.8rem; font-weight: 500;
  cursor: pointer;
  transition: all var(--transition);
  font-family: inherit;
}
.btn:hover { background: rgba(255,255,255,0.08); color: var(--text-primary); }
.btn-sm { padding: 6px 10px; font-size: 0.75rem; }
.btn-primary { background: var(--accent); color: #000; border-color: var(--accent); font-weight: 600; }
.btn-primary:hover { background: var(--accent-hover); color: #000; }
.btn-danger { border-color: rgba(255,82,82,0.3); color: var(--danger); }
.btn-danger:hover { background: rgba(255,82,82,0.1); }
.btn-block { width: 100%; justify-content: center; margin-top: 12px; }

/* ===== Weather Controls ===== */

/* ===== Map ===== */
#map {
  position: fixed;
  top: 0; bottom: 0;
  left: var(--sidebar-width);
  right: 0;
  z-index: 1;
}

/* ===== Map Controls ===== */
#map-controls {
  position: fixed;
  top: calc(var(--top-bar-height) + 16px); right: 16px;
  display: flex; flex-direction: column; gap: 8px;
  z-index: 500;
}
.map-btn {
  width: auto; height: auto;
  padding: 8px 10px;
  background: var(--bg-secondary);
  border: 1px solid var(--bg-tertiary);
  border-radius: var(--radius);
  color: var(--text-primary);
  font-size: 1rem;
  cursor: pointer;
  display: flex; align-items: center; gap: 6px;
  transition: all var(--transition);
  box-shadow: var(--shadow);
}
.map-btn-label {
  font-size: 0.65rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-secondary);
}
.map-btn:hover { background: var(--bg-tertiary); color: var(--accent); }
.map-btn:hover .map-btn-label { color: var(--accent); }
.map-btn.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.map-btn.active .map-btn-label { color: #fff; }

/* ===== 2D/3D Mode Switch (experiment/3d-earth PROTOTYPE) ===== */
/* Deliberately PROMINENT (Josh): a Google-Maps-style on-map segmented pill —
   big tap targets, unmistakable active state. Bottom-left of the map, clear
   of #map-controls (top-right) and attribution (bottom-right). Known
   prototype overlap: the weather timeline, when opened, slides over this
   corner on mobile. */
/* ===== On-map view controls: ONE ROW, TWO SINGLE-CLICK BUTTONS =====
   Josh, 2026-08-05: "make the 2d3d button and toposat button half the size,
   and just a click rather than a toggle... both on the same row at the bottom,
   don't stack them on top of each other... smaller on mobile, they take up too
   much room."

   They were two stacked segmented pills, each ~46px tall with two 76px
   segments — together they owned the whole bottom-left corner of a phone
   screen. Now: one row, one button each, roughly half the footprint, and each
   button labels the state you are IN (the units-button convention) rather than
   the state it would move you to. */
/* EVERY BOTTOM-ANCHORED MAP CONTROL CLEARS THE SYSTEM BAR.
   Measured on a Galaxy S23 in the Capacitor app (2026-08-05):
   env(safe-area-inset-bottom) = 48px, and #map-mode-bar sat with a 14px gap —
   i.e. 34px INSIDE Android's gesture bar, which covered the 2D/3D and Topo/Sat
   buttons (Josh: "the android home bar covers them"). The viewport meta already
   carries viewport-fit=cover, so the inset is reported; nothing was reading it.
   Desktop and 3-button-nav devices report 0px and are unaffected, which is why
   this is added to the offset rather than replacing it. */
#map-mode-bar {
  position: fixed;
  bottom: calc(22px + var(--safe-bottom));
  left: calc(var(--sidebar-width) + 22px);
  z-index: 500;
  display: flex;
  gap: 6px;
}
/* READABLE OVER ANYTHING, IN EITHER STATE (Josh: "those buttons are hard to see
   if they're in one or the other mode — make those buttons visible whatever mode
   they're in"). These sit directly on map content, which swings from near-white
   glacier and topo paper to dark satellite forest, so:
     - the label is --text-primary, never the dimmer --text-secondary it used to
       be. That dimming was the "hard to see" state: the button showing the mode
       you are IN was the quiet one.
     - the background is a near-opaque plate rather than a flat panel colour, so
       bright imagery cannot bleed through and wash the text out,
     - the border is a light hairline instead of --bg-tertiary (#243342, which is
       invisible against a dark map), and the shadow gives an edge on light
       ground. One treatment, both states, no mode reads as disabled. */
.map-mode-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 30px;
  padding: 0 12px;
  border: 1px solid rgba(232, 237, 242, 0.3);
  border-radius: 999px;
  background: rgba(15, 25, 35, 0.93);
  color: var(--text-primary);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.6px;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}
.map-mode-btn i { font-size: 0.82rem; }
/* The imagery-quality chip. Same plate as the buttons so it reads as part of
   the bar, but plainly not a control: no hover, no pointer, lighter weight.
   Amber when the tiles are being enlarged past what the source holds — the one
   state where what you see is not what the data supports. */
.map-mode-chip {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  padding: 0 10px;
  border: 1px solid rgba(232, 237, 242, 0.22);
  border-radius: 999px;
  background: rgba(15, 25, 35, 0.93);
  color: var(--text-secondary);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.3px;
  white-space: nowrap;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}
#battery-chip { gap: 5px; }
.map-mode-chip.is-coarse {
  color: #ffb74d;
  border-color: rgba(255, 183, 77, 0.45);
}
.map-mode-btn:hover { background: rgba(36, 51, 66, 0.96); border-color: rgba(232, 237, 242, 0.5); }
/* 3D is a MODE you are in, not a preference — mark it, so a glance at the
   corner answers "why is the map pitched?". The accent goes on the BORDER only:
   tinting the label green traded one legibility problem for another, since
   #c8f53a text is thinner against imagery than plain white is. */
#map-mode-bar.is-3d #btn-mode-cycle {
  border-color: var(--accent);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(200, 245, 58, 0.45);
}
#map-mode-bar.is-3d #btn-mode-cycle i { color: var(--accent); }
#exag-pill {
  position: fixed;
  /* clears the single #map-mode-bar row, plus the system bar under it */
  bottom: calc(62px + var(--safe-bottom));
  left: calc(var(--sidebar-width) + 22px);
  z-index: 500;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: var(--bg-secondary);
  border: 1px solid var(--bg-tertiary);
  border-radius: 999px;
  box-shadow: var(--shadow);
  color: var(--text-primary);
}
#exag-pill input[type="range"] { width: 110px; accent-color: var(--accent); }

/* ===== Current-location puck (shown while recording a trip) =====
   A big green dot + heading arrow that marks where you ARE now (the breadcrumb
   line shows where you've been). The arrow points up in the element and the
   maplibre marker is rotation-aligned to the map + rotated to the GPS heading,
   so it points your direction of travel; .no-heading hides it when stationary. */
.loc-puck { width: 30px; height: 30px; position: relative; pointer-events: none; }
.loc-puck .puck-pulse {
  position: absolute; inset: 0; margin: auto; width: 20px; height: 20px;
  border-radius: 50%; background: #d9ff5c; opacity: 0.4;
  animation: puckPulse 2s ease-out 3; /* 3 beats, then still: an endless animation keeps the screen redrawing all walk */
}
.loc-puck .puck-dot {
  position: absolute; inset: 0; margin: auto; width: 18px; height: 18px;
  border-radius: 50%; background: #d9ff5c; border: 3px solid #fff;
  box-shadow: 0 0 3px rgba(0,0,0,0.5);
}
.loc-puck .puck-arrow {
  position: absolute; top: -3px; left: 50%; transform: translateX(-50%);
  width: 0; height: 0; border-left: 6px solid transparent;
  border-right: 6px solid transparent; border-bottom: 9px solid #d9ff5c;
  filter: drop-shadow(0 -1px 0 #fff);
}
.loc-puck.no-heading .puck-arrow { display: none; }
@keyframes puckPulse {
  0% { transform: scale(1); opacity: 0.45; }
  100% { transform: scale(2.6); opacity: 0; }
}
.mode-switch-btn {
  min-width: 76px;
  min-height: 46px; /* 44px+ tap target — mobile-friendly */
  padding: 0 20px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--text-secondary);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 1.5px;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  transition: all var(--transition);
}
.mode-switch-btn i { font-size: 0.95rem; }
.mode-switch-btn:hover { color: var(--text-primary); background: rgba(255,255,255,0.08); }
.mode-switch-btn.active { background: var(--accent); color: #fff; }

/* One-time dismissible notices (software-GL warning, 3D quality floor) */
.mode-notice {
  position: fixed;
  top: calc(var(--top-bar-height) + var(--safe-top) + 18px); left: 50%;
  transform: translateX(-50%);
  z-index: 2500;
  max-width: min(92vw, 560px);
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px;
  background: var(--bg-secondary);
  border: 1px solid var(--warning);
  border-radius: var(--radius);
  color: var(--text-primary);
  font-size: 0.85rem;
  box-shadow: var(--shadow);
}
.mode-notice i { color: var(--warning); }
.mode-notice .mode-notice-ok {
  margin-left: 6px;
  padding: 8px 16px;
  min-height: 40px;
  background: var(--accent); color: #fff;
  border: none; border-radius: var(--radius-sm);
  font-weight: 700; cursor: pointer;
}

/* ===== Weather Tile Overlays ===== */
.weather-tile-precipitation { filter: saturate(2.0) contrast(1.5); }

/* The Weather Timeline bar was removed 2026-07-22 along with its markup.
   It was a fixed full-width bar at bottom:16px spanning the map, which sat
   directly over the 2D/3D switcher — and its play button never drove the
   radar anyway (see js/weather.js). */

/* ===== Info Panel =====
   Vertical side-loading card (ported from motocamp-all). Was a bottom-anchored
   horizontal strip eating 68vh of a map you are trying to read a track off;
   now a column beside the sidebar, over the map. Mobile keeps the bottom-sheet
   geometry — a 380px column on a phone is the whole screen — overridden in the
   768px block, which must reset top/right/width or they leak in from here. */
#info-panel {
  position: fixed;
  /* Height follows CONTENT, capped — not pinned to the viewport bottom as in
     the motocamp original. Two reasons, both vertline-specific: a three-line
     hut card would otherwise render as a 876px mostly-empty column, and the
     lower-left controls (#map-mode-bar 22px + ~30px tall, #exag-pill 62px;
     was a three-pill stack of #mode-3d-switch 26px, #basemap-switch 88px,
     #exag-pill 140px + ~48px tall) lives under this panel's footprint — a
     full-height card buries the map's own basemap and 2D/3D controls. 212px
     clears the tallest of them. */
  top: calc(var(--top-bar-height) + var(--clear-route-inset, 0px) + 12px); bottom: auto;
  max-height: calc(100vh - var(--top-bar-height) - var(--clear-route-inset, 0px) - 212px);
  left: calc(var(--sidebar-width) + 12px);
  width: 380px;
  max-width: calc(100vw - var(--sidebar-width) - 24px);
  background: var(--bg-secondary);
  border: 1px solid var(--bg-tertiary);
  border-radius: var(--radius);
  z-index: 600;
  overflow-y: auto;
  box-shadow: 0 8px 30px rgba(0,0,0,0.45);
  transition: transform var(--transition), opacity var(--transition);
}
/* The phone's popup card and the cards' drag handle (js/sheet.js) exist only
   in the phone layout below; a desktop keeps MapLibre popups. */
#map-sheet, #route-sheet, #layers-sheet, .sheet-handle, .route-quick-actions, #btn-layers, #map-settings { display: none; }
/* Map settings (phone only, step 3): two-way switches as buttons, 44 px. */
#map-settings .setting-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 44px; font-size: 0.88rem; }
#map-settings .seg { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
#map-settings .seg button { min-height: 44px; min-width: 68px; padding: 0 12px; background: none; border: none; color: var(--text-secondary); font: inherit; cursor: pointer; }
#map-settings .seg button[aria-pressed="true"] { background: var(--accent-dim); color: var(--accent); font-weight: 700; }
#map-settings input[type=checkbox] { width: 22px; height: 22px; accent-color: var(--accent); }
#info-panel.hidden {
  transform: translateX(calc(-100% - 420px));
  opacity: 0;
  pointer-events: none;
}
#info-close {
  position: sticky; top: 0; right: 0;
  float: right; margin: 12px 12px 0 0;
  background: var(--bg-tertiary); border: none;
  z-index: 10;
  color: var(--text-secondary); width: 44px; height: 44px;   /* the 44 px floor (05: K2); was 32 */
  border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.9rem;
}
#info-content { padding: 12px 16px 16px; }

/* Info Panel Content */
.info-header { margin-bottom: 8px; }
.info-header h2 { font-size: 1.15rem; font-weight: 700; margin-bottom: 4px; }
.info-header .info-type { font-size: 0.78rem; color: var(--accent); font-weight: 500; text-transform: uppercase; letter-spacing: 0.03em; }
.info-header .info-status { font-size: 0.75rem; padding: 2px 8px; border-radius: 3px; margin-left: 8px; }
.info-header .info-status.open { background: rgba(200,245,58,0.15); color: #c8f53a; }
.info-header .info-status.closed { background: rgba(255,82,82,0.15); color: #ff5252; }

.info-facilities {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-bottom: 10px;
}
.facility-badge {
  display: flex; align-items: center; gap: 4px;
  padding: 3px 8px;
  background: var(--bg-tertiary);
  border-radius: var(--radius-sm);
  font-size: 0.72rem;
  color: var(--text-secondary);
}
.facility-badge.available { color: var(--accent); background: var(--accent-dim); }
.facility-badge i { font-size: 0.8rem; }

.info-details { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px; margin-bottom: 10px; }
.info-detail { }
.info-detail-label { font-size: 0.7rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 2px; }
.info-detail-value { font-size: 0.85rem; color: var(--text-primary); }

.info-weather {
  background: var(--bg-tertiary);
  border-radius: var(--radius);
  padding: 14px;
  margin-bottom: 16px;
}
.info-weather h4 { font-size: 0.78rem; color: var(--text-secondary); margin-bottom: 8px; }
.weather-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); gap: 8px;
}
.weather-item { text-align: center; }
.weather-item .wx-icon { font-size: 1.3rem; margin-bottom: 2px; }
.weather-item .wx-temp { font-size: 1rem; font-weight: 600; }
.weather-item .wx-desc { font-size: 0.68rem; color: var(--text-muted); }
.weather-item .wx-day { font-size: 0.68rem; color: var(--text-secondary); font-weight: 500; }

.cell-bar-container { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.cell-carrier { font-size: 0.72rem; width: 64px; color: var(--text-secondary); }
.cell-bar { flex: 1; height: 6px; background: var(--bg-tertiary); border-radius: 3px; overflow: hidden; }
.cell-bar-fill { height: 100%; border-radius: 3px; transition: width 0.5s ease; }
.cell-tech { font-size: 0.68rem; color: var(--text-muted); width: 30px; }
.cell-bar-fill.verizon { background: var(--verizon-color); }
.cell-bar-fill.att { background: var(--att-color); }
.cell-bar-fill.tmobile { background: var(--tmobile-color); }
.cell-tech { font-size: 0.64rem; color: var(--text-muted); width: 52px; text-align: right; white-space: nowrap; }

.info-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.info-actions .btn i { font-size: 0.8rem; }

/* ===== Route Summary Styles ===== */
.route-overview {
  display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px;
  margin-bottom: 16px;
}
.route-stat {
  text-align: center;
  padding: 10px;
  background: var(--bg-tertiary);
  border-radius: var(--radius-sm);
}
.route-stat-value { font-size: 1.2rem; font-weight: 700; color: var(--accent); }
.route-stat-label { font-size: 0.68rem; color: var(--text-muted); margin-top: 2px; }

.route-segment {
  padding: 12px 0;
  border-bottom: 1px solid rgba(255,255,255,0.04);
}
.route-segment:last-child { border-bottom: none; }
.route-segment-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 6px;
}
.route-segment-header h4 { font-size: 0.85rem; font-weight: 600; }
.route-segment-header span { font-size: 0.75rem; color: var(--text-muted); }
.route-segment-weather {
  display: flex; gap: 12px;
  margin-bottom: 6px;
}
.route-segment-weather .wx-mini {
  display: flex; align-items: center; gap: 4px;
  font-size: 0.78rem; color: var(--text-secondary);
}
.route-segment-services { display: flex; flex-wrap: wrap; gap: 4px; }
.route-segment-services .service-tag {
  font-size: 0.68rem;
  padding: 2px 6px;
  background: var(--bg-tertiary);
  border-radius: 3px;
  color: var(--text-muted);
}
.route-segment-services .service-tag.available { color: var(--accent); background: var(--accent-dim); }

/* ===== Elevation Profile ===== */
.elevation-profile {
  margin-bottom: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(255,255,255,0.04);
}
.elevation-stats {
  display: flex; gap: 12px;
  margin-bottom: 8px;
  font-size: 0.75rem;
  color: var(--text-secondary);
}
.elevation-stats span {
  display: flex; align-items: center; gap: 4px;
}
.elevation-chart-wrap {
  position: relative;
  background: var(--bg-tertiary);
  border-radius: var(--radius-sm);
  padding: 4px;
  cursor: crosshair;
}
.elevation-chart-wrap canvas {
  display: block;
  width: 100%;
}
.elevation-tooltip {
  position: absolute;
  background: var(--bg-secondary);
  border: 1px solid var(--bg-tertiary);
  border-radius: var(--radius-sm);
  padding: 4px 8px;
  font-size: 0.72rem;
  color: var(--text-primary);
  pointer-events: none;
  white-space: nowrap;
  z-index: 10;
  box-shadow: var(--shadow);
  line-height: 1.3;
}
.elevation-map-tooltip {
  background: var(--bg-secondary) !important;
  border: 1px solid var(--accent) !important;
  color: var(--text-primary) !important;
  font-size: 0.75rem !important;
  font-weight: 600 !important;
  padding: 2px 6px !important;
  border-radius: 4px !important;
  box-shadow: var(--shadow) !important;
}
.elevation-map-tooltip::before {
  border-top-color: var(--accent) !important;
}

/* ===== Weather Overlay Legend ===== */
.weather-legend-bar {
  display: flex; height: 12px;
  border-radius: 6px; overflow: hidden;
  margin-bottom: 4px;
}
.weather-legend-labels { display: flex; justify-content: space-between; font-size: 0.68rem; color: var(--text-muted); }

/* ===== MapLibre GL Overrides =====
   (Leaflet-era cluster-icon CSS removed 2026-07-19: clusters are GL circle
   paint now — colors live in Utils.clusterColors, sizes in map.clusterLayer.) */
.maplibregl-ctrl-attrib { font-size: 0.6rem !important; background: rgba(15,25,35,0.8) !important; color: var(--text-muted) !important; }
.maplibregl-ctrl-attrib a { color: var(--text-secondary) !important; }
/* Attribution lives in the lower-left map bar as one (i), compact (Josh,
   2026-10-28: in the sidebar footer, with the data credits, it took a quarter
   of a phone's menu). maplibre still owns the node and rewrites it as sources
   come and go; this only restyles it. Closed: a round button matching the bar.
   Open (maplibre's [open], on load and on tap): the text is a panel ABOVE the
   bar, positioned against the bar itself (position:static here), so it never
   pushes or covers the buttons. Full list: the Credits screen. */
.map-attribution { display: contents; }
.maplibregl-ctrl-attrib.in-bar {
  position: static !important; margin: 0 !important; padding: 0 !important;
  min-height: 0 !important; background: none !important; float: none !important;
  display: flex; align-items: center;
  /* maplibre gives every control transform: translate(0), which would make
     THIS node the open panel's anchor, so a wide panel ran off a phone screen.
     None: the panel anchors to the fixed #map-mode-bar instead. */
  transform: none !important;
}
.maplibregl-ctrl-attrib.in-bar .maplibregl-ctrl-attrib-button {
  position: static !important; display: inline-flex !important; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0; list-style: none; cursor: pointer;
  border: 1px solid rgba(232, 237, 242, 0.3); border-radius: 50%;
  background: rgba(15, 25, 35, 0.93) !important; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
  color: var(--text-primary);
}
.maplibregl-ctrl-attrib.in-bar .maplibregl-ctrl-attrib-button::-webkit-details-marker { display: none; }
.maplibregl-ctrl-attrib.in-bar .maplibregl-ctrl-attrib-button::before {
  content: '\f05a'; font-family: 'Font Awesome 6 Free'; font-weight: 900; font-size: 0.85rem;
}
.maplibregl-ctrl-attrib.in-bar .maplibregl-ctrl-attrib-inner { display: none; }
.maplibregl-ctrl-attrib.in-bar[open] .maplibregl-ctrl-attrib-inner {
  display: block; position: absolute; left: 0; bottom: calc(100% + 8px);
  /* 100vw - 160px leaves the camera pad's column clear on a phone. */
  width: max-content; max-width: min(360px, calc(100vw - 160px));
  padding: 8px 10px; border-radius: 10px; border: 1px solid rgba(232, 237, 242, 0.3);
  background: rgba(15, 25, 35, 0.95); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
  font-size: 0.68rem; line-height: 1.5; color: var(--text-secondary);
}
.maplibregl-popup-content {
  background: var(--bg-secondary) !important;
  color: var(--text-primary) !important;
  border-radius: var(--radius) !important;
  box-shadow: var(--shadow) !important;
  border: 1px solid var(--bg-tertiary) !important;
  padding: 12px 14px !important;
  font-family: 'Inter', sans-serif !important;
  /* The map container is overflow:hidden, so a tall popup (e.g. a verbose
     council-closure description) gets clipped at the map edge. Cap the box to
     the viewport and scroll inside it so the WHOLE description is always
     reachable instead of cut off. */
  max-height: min(64vh, calc(100dvh - 96px)) !important;
  overflow-y: auto !important;
  overscroll-behavior: contain;
}
/* The popup tip is a CSS border triangle — theme every anchor direction */
.maplibregl-popup-anchor-bottom .maplibregl-popup-tip { border-top-color: var(--bg-secondary) !important; }
.maplibregl-popup-anchor-top .maplibregl-popup-tip { border-bottom-color: var(--bg-secondary) !important; }
.maplibregl-popup-anchor-left .maplibregl-popup-tip { border-right-color: var(--bg-secondary) !important; }
.maplibregl-popup-anchor-right .maplibregl-popup-tip { border-left-color: var(--bg-secondary) !important; }
.maplibregl-popup-close-button { color: var(--text-secondary) !important; font-size: 20px !important; right: 4px !important; top: 2px !important; }

/* Custom marker styles */
.custom-marker {
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  /* Tight shadow: large blur radii multiply paint cost across hundreds of
     markers when a dense area re-renders after a zoom */
  box-shadow: 0 1px 3px rgba(0,0,0,0.5);
  font-size: 14px;
  transition: transform 0.2s ease;
}
.custom-marker:hover { transform: scale(1.2); }

/* ===== Responsive ===== */
@media (max-width: 768px) {
  #mobile-header { display: flex; }
  #sidebar-close { display: block; }

  .sidebar {
    width: 100%;
    transform: translateX(-100%);
    top: 0;
  }
  .sidebar.open { transform: translateX(0); }
  .sidebar-header { padding-top: calc(var(--mobile-header-height) + 8px); }

  /* Full-bleed: the bar at the top floats over the map (step 3, below). */
  #map {
    left: 0;
    top: 0;
  }

  /* ===== THE MAP RAIL ON A PHONE =====
     Josh, 2026-08-05: "remove the reset and fullscreen buttons from the main
     map on mobile... the layout needs to be better."

     It was SEVEN labelled buttons stacked from the top-right, each ~40px tall
     with an uppercase caption — a column down the most valuable third of the
     screen, in the hardest place on a phone to reach one-handed. Three changes,
     all standard practice for mobile maps:

       1. FEWER CONTROLS. Reset and Fullscreen are hidden. Fullscreen is close
          to meaningless here — a phone browser is already fullscreen, and in
          the Capacitor app there is no chrome to hide, so it is a button that
          appears to do nothing. Reset is low-frequency and lives in the
          sidebar. That leaves Rec / Rain / km / Locate: four, inside the
          3-5 guidance for a mobile map.
       2. ICON ONLY. The captions go, so each control is a 44px square rather
          than a labelled slab — 44px being the Apple minimum and the reason
          the size does NOT shrink even though the button does. The Rec caption
          is the one exception: while recording it reads "Stop", and hiding
          that would leave a red button with no statement of what it does.
       3. THUMB REACH. The rail moves to the BOTTOM right, above the mode bar,
          because bottom-aligned controls measurably cut interaction time for
          one-handed use and the top-right corner needs a hand-shuffle.
     Desktop is untouched: it has the room and a cursor. */
  /* TOP right, and the PTZ pad owns the bottom right (Josh, 2026-09-11, looking
     at an S23: "put the buttons top right, and the nav bottom right"). This
     reverses the thumb-reach move noted in point 3 above — bottom-aligned
     controls do cut one-handed interaction time, but only while they are not
     stacked under the pad, and the corner is not big enough for both. The two
     surfaces now have a corner each and cannot collide at any column height. */
  #map-controls {
    /* --mobile-header-height, NOT --top-bar-height: the latter is 0px and the
       phone's chrome is #mobile-header, so anchoring to it put the first
       button (Record) underneath the header bar. Measured on an S23: rail top
       was 12px against a 56px header. */
    top: calc(var(--mobile-header-height) + 12px);
    bottom: auto;
    right: 12px;
    flex-direction: column;
    gap: 8px;                           /* >= the 8px anti-mis-tap spacing */
  }
  #map-controls .map-btn {
    width: 44px; height: 44px;          /* Apple 44px / Android 48px minimum */
    padding: 0;
    justify-content: center;
    border-radius: 50%;
  }
  #map-controls .map-btn-label { display: none; }
  /* …except while recording, where the label IS the state. */
  #map-controls .map-btn.recording { width: auto; border-radius: 999px; padding: 0 12px; gap: 6px; }
  #map-controls .map-btn.recording .map-btn-label { display: inline; }

  #btn-reset, #btn-fullscreen { display: none !important; }

  /* Smaller again on a phone: this corner is the most contested space on the
     screen and these two buttons were eating it. */
  #map-mode-bar {
    left: 12px;
    bottom: calc(14px + var(--safe-bottom));
    gap: 5px;
    /* Satellite + the amber imagery chip + the battery's time left is wider
       than a 360 px phone. Wrap UPWARD rather than run under the edge; the
       3D-height pill then moves up a row (.is-wrapped, set in js/battery.js). */
    flex-wrap: wrap-reverse;
    max-width: calc(100vw - 24px);
  }
  body:has(#map-mode-bar.is-wrapped) #exag-pill { bottom: calc(81px + var(--safe-bottom)); }
  .map-mode-btn {
    min-height: 26px;
    padding: 0 9px;
    font-size: 0.7rem;
    gap: 4px;
  }
  .map-mode-chip { min-height: 26px; padding: 0 8px; font-size: 0.68rem; }
  /* Drawn at 26 px to keep the most contested corner clear, but TOUCHED at
     44: an invisible hit area 9 px above and below (05: K2). Vertical only,
     so neighbouring buttons never share a tap; the 3D-height pill above sits
     clear of it (50 px vs 26 + 9 + 14). */
  .map-mode-btn::after { content: ''; position: absolute; inset: -9px 0; }
  .map-mode-btn i { font-size: 0.74rem; }

  #exag-pill {
    left: 12px;
    bottom: calc(50px + var(--safe-bottom));   /* clears the single button row + the system bar */
  }


  /* ===== One card at the bottom: #info-panel and #map-sheet (js/sheet.js) =====
     Josh, 2026-11-01: the campsite popup and trail info "take up too much
     screen real estate". It opened at 60vh and covered the hut it described.
     Both cards are now one sheet: a peek that ends under the actions row, a
     pull up to 88% for the rest. Its height is --sheet-h, set by Sheet;
     nothing here sizes it. top/right/width are RESET explicitly, not
     inherited: the desktop rule above sets all three. */
  #info-panel, #map-sheet, #route-sheet, #layers-sheet {
    position: fixed;
    top: auto; bottom: 0; left: 0; right: 0;
    width: 100%; max-width: 100%;
    height: var(--sheet-h, 0px); max-height: none;
    background: var(--bg-secondary); color: var(--text-primary);
    border: none; border-top: 1px solid var(--bg-tertiary);
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -12px 32px rgba(0, 0, 0, 0.42);
    z-index: 600;
    /* The last row would sit under the system bar: padding, not an offset,
       so no strip of map shows beneath the card. */
    padding-bottom: var(--safe-bottom);
    /* In the peek nothing scrolls: a vertical drag moves the card (Sheet),
       a sideways one still scrolls the facilities row. */
    overflow: hidden; overscroll-behavior: contain; touch-action: pan-x;
    transition: height 0.22s ease, transform 0.22s ease;
  }
  #map-sheet, #route-sheet, #layers-sheet { display: block; }
  #info-panel.sheet-full, #map-sheet.sheet-full, #route-sheet.sheet-full, #layers-sheet.sheet-full { overflow-y: auto; touch-action: auto; }
  /* The peek ends under the actions; the next section's first line would
     show cut in half (S10: "FEE"). A short fade says "more below" instead. */
  #info-panel.sheet-peek, #map-sheet.sheet-peek, #route-sheet.sheet-peek, #layers-sheet.sheet-peek {
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 16px), transparent);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 16px), transparent);
  }
  /* Inside the Android app the page runs edge to edge: the system buttons sit
     over the sheet's bottom env(safe-area-inset-bottom) (48 px on the S23), and
     the sheet's content showed through that strip ("Reported by walkers" under
     the nav bar, 2026-11-07). Cover exactly that strip in the sheet's own colour:
     the peek's buttons end where it starts, so a fade above it would dim them.
     Zero height in a browser. */
  #info-panel.sheet-peek::after, #map-sheet.sheet-peek::after, #route-sheet.sheet-peek::after, #layers-sheet.sheet-peek::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; pointer-events: none;
    height: env(safe-area-inset-bottom, 0px);
    background: var(--bg-secondary);
    display: var(--sheet-inset-band, none);
  }
  @supports (bottom: env(safe-area-inset-bottom)) {
    html.native-app { --sheet-inset-band: block; }
  }
  #info-panel.sheet-dragging, #map-sheet.sheet-dragging, #route-sheet.sheet-dragging, #layers-sheet.sheet-dragging { transition: none; }
  #info-panel.hidden, #map-sheet.hidden, #route-sheet.hidden, #layers-sheet.hidden { transform: translateY(100%); opacity: 1; pointer-events: none; }
  .sheet-handle {
    display: block; position: sticky; top: 0; z-index: 2;
    height: 22px; background: inherit; cursor: grab; touch-action: none;
  }
  /* The bar is 38 x 5; the tap target around it is 44 px tall (WCAG 2.5.5). */
  .sheet-handle::before {
    content: ''; position: absolute; left: 50%; top: 9px; transform: translateX(-50%);
    width: 38px; height: 5px; border-radius: 3px; background: rgba(255, 255, 255, 0.28);
  }
  .sheet-handle::after { content: ''; position: absolute; left: 30%; right: 30%; top: -11px; height: 44px; }
  #info-close, .sheet-close { margin-top: 2px; }
  #map-sheet .sheet-close, #route-sheet .sheet-close, #layers-sheet .sheet-close {
    position: sticky; top: 22px; float: right; z-index: 3; margin-right: 10px;
    width: 44px; height: 44px; border-radius: 50%; border: none; cursor: pointer;
    background: var(--bg-tertiary); color: var(--text-secondary);
  }
  #map-sheet .sheet-content, #route-sheet .sheet-content, #layers-sheet .sheet-content { padding: 4px 18px 14px; font-size: 0.95rem; }
  /* Desktop popups size themselves inline; in the card they take its width. */
  #map-sheet .sheet-content > * { min-width: 0 !important; max-width: none !important; }

  /* The hut card's order on a phone: what it is, how to book, what it has,
     what you can do, then the rest. CSS order only: the builders and the
     desktop card are untouched. The two outside links wait for the full card. */
  #info-content { display: flex; flex-direction: column; }
  #info-content > * { order: 5; }
  #info-content > .info-header { order: 1; }
  #info-content > .place-direct { order: 2; }
  #info-content > .info-facilities { order: 3; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  #info-content > .info-facilities > * { flex: 0 0 auto; }
  #info-content > .info-actions { order: 4; }
  #info-panel:not(.sheet-full) .info-actions > a { display: none; }
  /* The card's buttons are its main targets: 44 px (they measured 28 on the S10). */
  #info-content > .info-actions .btn, #info-content > .place-direct .btn {
    min-height: 44px; display: inline-flex; align-items: center; gap: 6px;
  }

  /* The track card: the name and its numbers first, then BOTH closure notes
     in full (golden rule 4: no warning is cut short), the actions, and the
     description last. !important only where the desktop popup sets inline
     sizes the card must not keep. */
  #map-sheet .track-pop { display: flex; flex-direction: column; }
  #map-sheet .track-pop > * { order: 6; }
  #map-sheet .track-pop-title { order: 1; font-size: 1.15rem !important; margin: 0 40px 4px 0 !important; }
  #map-sheet .track-pop-meta { order: 2; font-size: 0.85rem !important; }
  #map-sheet .track-pop-warn { order: 3; font-size: 0.8rem !important; }
  #map-sheet .track-pop-note { order: 4; font-size: 0.78rem !important; }
  #map-sheet .track-pop-actions { order: 5; margin: 8px 0 10px !important; }
  #map-sheet .track-pop-actions .btn { min-height: 44px; display: inline-flex; align-items: center; gap: 6px; }

  /* One surface at a time: the pan pad and the bottom pills wait while a card
     is up, and the right-hand buttons while it is full. */
  body:has(#info-panel:not(.hidden)) #map-ptz, body:has(#map-sheet:not(.hidden)) #map-ptz, body:has(#route-sheet:not(.hidden)) #map-ptz,
  body:has(#info-panel:not(.hidden)) #map-mode-bar, body:has(#map-sheet:not(.hidden)) #map-mode-bar,
  body:has(#route-sheet:not(.hidden)) #map-mode-bar, body:has(#layers-sheet:not(.hidden)) #map-ptz,
  body:has(#layers-sheet:not(.hidden)) #map-mode-bar { display: none; }
  body:has(#info-panel.sheet-full) #map-controls, body:has(#map-sheet.sheet-full) #map-controls,
  body:has(#route-sheet.sheet-full) #map-controls, body:has(#layers-sheet.sheet-full) #map-controls { display: none; }

  /* The route card (step 2): the summary moved in from the menu. Its own name
     leads, so the section title goes; the four figures sit in one row; the
     first view ends under Navigate and Save, which only this card shows. */
  #route-sheet #route-summary { padding: 0; margin: 0; border: none; background: none; }
  /* The figures row already says climb and descent, and the chart's top label
     the high point: in the card the chart's own heading and figures go, so
     Navigate and Save make the first view (they missed it by 22 px on the S10). */
  #route-sheet #route-summary > h3, #route-sheet .elevation-profile h4,
  #route-sheet .elevation-profile .elevation-stats { display: none; }
  #route-sheet .elevation-profile { margin-bottom: 0; padding-bottom: 0; border-bottom: none; }
  #route-sheet .route-overview { grid-template-columns: repeat(4, 1fr); gap: 6px; margin-bottom: 8px; }
  #route-sheet .route-stat { padding: 6px 4px; }
  #route-sheet .route-stat-value { font-size: 1rem; }
  #route-sheet .route-stat-label { font-size: 0.62rem; }
  #route-sheet .route-quick-actions { display: flex; gap: 8px; margin: 10px 0 12px; }
  #route-sheet .route-quick-actions .btn { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
  #route-sheet .route-quick-actions .btn-primary { flex: 1; }
  /* The route figures at the top are the way back to a closed route card
     (js/sheet.js): a button on a phone, not click-through. */
  #route-hud[role="button"] { pointer-events: auto; cursor: pointer; }   /* outranks the base rule's none, below */

  /* ===== The map chrome (phone UX step 3) =====
     Josh, 2026-11-01: the title bar becomes a floating search bar with the
     menu inside it, the map runs full-bleed under it, and the right-hand
     column is Layers and Locate. Rain, units, place names and the pan pad
     moved into Layers (a chip in the bar while the radar is on). The
     header-height token keeps its meaning, the space the bar takes, so the
     column, the route figures and the menu's padding follow it. */
  :root { --mobile-header-height: calc(68px + var(--safe-top)); }
  #mobile-header {
    top: calc(var(--safe-top) + 10px); left: 12px; right: 12px;
    height: 48px; padding: 0 6px; gap: 4px;
    border: none; border-radius: 24px;
    background: rgba(26, 38, 50, 0.96);
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
    justify-content: flex-start;
  }
  #mobile-header h1, #mobile-header #mobile-locate, #mobile-header #sidebar-toggle-label { display: none; }
  #mobile-header #sidebar-toggle { width: 44px; height: 44px; padding: 0; border-radius: 50%; flex: 0 0 auto; }
  #mobile-header #mobile-search {
    flex: 1; min-width: 0; height: 44px; padding: 0 4px;
    display: flex; align-items: center; gap: 10px;
    text-align: left; font-size: 0.95rem; color: var(--text-secondary);
    white-space: nowrap; overflow: hidden;
  }
  #mobile-header #mobile-search span { overflow: hidden; text-overflow: ellipsis; }
  #mobile-header #rain-chip {
    flex: 0 0 auto; height: 32px; margin-right: 4px; padding: 0 12px;
    display: inline-flex; align-items: center; gap: 6px; border-radius: 16px;
    background: rgba(200, 245, 58, 0.95); color: #00120a; font-size: 0.8rem; font-weight: 700;
  }
  #mobile-header #rain-chip[aria-pressed="false"] {
    background: transparent; color: var(--text-secondary);
    box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.35);
  }
  #mobile-header #rain-chip[aria-pressed="false"] .fa-xmark { display: none; }
  #map-controls #btn-layers { display: flex; }
  #map-controls #btn-radar, #map-controls #btn-units, #map-controls #btn-lang { display: none; }
  /* Nav is a route's own action: on the map only while there is a route, like
     the clear button (the tour's walk step rings it; S10: hidden, it lost its ring). */
  body:not(:has(#route-summary:not(.hidden))) #map-controls #btn-nav { display: none; }
  #map-settings { display: block; }
  body.ptz-off #map-ptz { display: none; }
  /* Anything pinned to the top starts below the floating bar. The map runs
     full-bleed under it, so MapLibre's own top-left corner (the 3D compass)
     sat under the bar, and with a route on the Distance tile (Josh, S10,
     2026-11-01); the margin-top below the route figures still applies.
     #map outranks maplibre-gl.css, which loads after this file. The mode
     notice sat on the bar (and on the old title bar before it). */
  #map .maplibregl-ctrl-top-left { top: var(--mobile-header-height); }
  .mode-notice { top: calc(var(--mobile-header-height) + 12px); }

  /* The layers card (step 4): the two maps as pictures, chips for what is on
     the map, and the full Layers tab one tap away. Targets 44 px. Sized so
     "More layers" makes the first view (at 80 px pictures it missed on the S10). */
  #layers-sheet .lc-title { font-size: 1.05rem; font-weight: 700; margin: 0 52px 8px 0; }
  #layers-sheet .lc-maps { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  #layers-sheet .lc-map {
    position: relative; height: 64px; padding: 0; cursor: pointer; background: none;
    border: 2px solid transparent; border-radius: 12px; overflow: hidden;
  }
  #layers-sheet .lc-map img { width: 100%; height: 100%; object-fit: cover; display: block; }
  #layers-sheet .lc-map span {
    position: absolute; left: 8px; bottom: 6px; font-weight: 700; font-size: 0.85rem;
    color: #fff; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85);
  }
  #layers-sheet .lc-map[aria-pressed="true"] { border-color: var(--accent); }
  #layers-sheet .lc-h { font-size: 0.72rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); margin: 12px 0 6px; }
  #layers-sheet .lc-chips { display: flex; flex-wrap: wrap; gap: 8px; }
  #layers-sheet .lc-chip {
    min-height: 44px; padding: 0 14px; border-radius: 22px; cursor: pointer;
    display: inline-flex; align-items: center; gap: 8px; font: inherit; font-size: 0.85rem;
    background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid transparent;
  }
  #layers-sheet .lc-chip[aria-pressed="true"] { background: var(--accent-dim); color: var(--accent); border-color: rgba(200, 245, 58, 0.5); }
  #layers-sheet .lc-more {
    margin-top: 4px; min-height: 44px; padding: 0; cursor: pointer; background: none; border: none;
    display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: 0.9rem; color: var(--accent);
  }
  /* Collapse is a desktop affordance: on mobile the sidebar is already
     off-canvas behind the hamburger. */
  #sidebar-collapse, #sidebar-expand { display: none !important; }

  .info-details { grid-template-columns: 1fr; }
  .route-overview { grid-template-columns: 1fr 1fr; }

  /* Inputs must be ≥16px to prevent iOS Safari from zooming on focus */
  .route-point input,
  .search-box input,
  .date-field input {
    font-size: 1rem;
  }
}

/* In landscape on mobile: compact the sidebar so content fits above the keyboard */
@media (max-width: 768px) and (orientation: landscape) {
  .sidebar-header { padding-top: calc(var(--mobile-header-height) + 4px); padding-bottom: 6px; }
  .sidebar-section { padding: 10px 16px; }
  .sidebar-section h3 { margin-bottom: 8px; }
  .tab-btn { padding: 8px 4px; }
}

@media (max-width: 480px) {
  .tab-btn { font-size: 0.72rem; padding: 10px 4px; }
  .basemap-selector { flex-direction: column; }
  .weather-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ===== Context Menu ===== */
#context-menu {
  position: fixed;
  background: var(--bg-secondary);
  border: 1px solid var(--bg-tertiary);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  z-index: 2000;
  min-width: 200px;
  padding: 4px 0;
  overflow: hidden;
}
.ctx-item {
  padding: 10px 16px;
  font-size: 0.82rem;
  cursor: pointer;
  display: flex; align-items: center; gap: 10px;
  transition: background 0.15s;
  color: var(--text-primary);
}
.ctx-item:hover { background: rgba(255,255,255,0.06); }
.ctx-item i { color: var(--accent); width: 16px; text-align: center; font-size: 0.85rem; }
.ctx-divider { height: 1px; background: var(--bg-tertiary); margin: 4px 0; }

/* ===== Hidden utility ===== */
.hidden { display: none !important; }

/* Name tag above the active (panel-open) site — pairs with Layers._highlightActive */
.active-site-tag {
  background: rgba(13, 21, 32, 0.95);
  border: 1px solid rgba(200, 245, 58, 0.6);
  color: #fff;
  font-weight: 600;
  font-size: 0.75rem;
  border-radius: 6px;
  padding: 3px 9px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
  white-space: nowrap;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.active-site-tag::before { border-top-color: rgba(200, 245, 58, 0.6); }

/* ===== Trip recording (js/ride-recorder.js) ===== */
.map-btn.recording {
  background: #d32f2f;
  border-color: #d32f2f;
  color: #fff;
}
.map-btn.recording .map-btn-label { color: #fff; }
.map-btn.recording i { animation: rec-pulse 1.6s ease-in-out 3; }
@keyframes rec-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
/* ===== Live recording panel (js/rec-panel.js, 2026-11-08) =====
   Phone: the bottom half of the screen while a recording runs; the map takes
   the top half (body.rec-live moves #map's bottom edge up by --rec-h, which
   the panel sets to its own height). Small mode is one row. Desktop: a card
   over the map, which keeps its full size. */
#rec-panel {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 1500;
  height: 58vh; height: 58dvh;
  display: flex; flex-direction: column; gap: 8px;
  padding: 10px 14px max(12px, env(safe-area-inset-bottom));
  background: var(--bg-secondary); color: var(--text-primary);
  border-top: 1px solid var(--border); border-radius: 16px 16px 0 0;
  box-shadow: 0 -6px 24px rgba(0,0,0,0.35);
}
#rec-panel[hidden] { display: none; }
body.rec-live #map { bottom: var(--rec-h, 50vh); }
body.nav-active #rec-panel { display: none; }
.rec-head { display: flex; align-items: center; gap: 8px; min-height: 32px; }
.rec-state { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 0.9rem; flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rec-dot { width: 10px; height: 10px; border-radius: 50%; background: #ff5252; flex: none; animation: rec-pulse 1.6s ease-in-out 3; }
#rec-panel.is-paused .rec-dot { background: #ffab40; animation: none; }
.rec-gps { font-size: 0.72rem; color: var(--text-muted); white-space: nowrap; }
.rec-icon { width: 40px; height: 36px; border-radius: 10px; border: 1px solid var(--border); background: var(--bg-tertiary); color: var(--text-primary); cursor: pointer; flex: none; }
.rec-wx { font-size: 0.78rem; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rec-wx.is-warn { color: #ffab40; }
.rec-wx[hidden] { display: none; }
.rec-hero { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.rec-big { font-size: 2rem; font-weight: 800; line-height: 1.05; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.rec-lbl { font-size: 0.68rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.rec-sub { text-transform: none; letter-spacing: 0; color: var(--text-secondary); }
.rec-chart { position: relative; height: 56px; padding-left: 56px; flex: none; }
.rec-chart svg { width: 100%; height: 100%; display: block; overflow: visible; }
.rec-chart-line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
.rec-chart-fill { fill: var(--accent); opacity: 0.18; }
.rec-chart-axis { position: absolute; left: 0; top: 0; bottom: 0; width: 52px; display: flex; flex-direction: column; justify-content: space-between; font-size: 0.62rem; white-space: nowrap; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.rec-chart-dot { position: absolute; width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 2px var(--bg-secondary); }
.rec-chart-empty { font-size: 0.75rem; color: var(--text-muted); line-height: 56px; margin-left: -56px; }
.rec-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; overflow-y: auto; flex: 1; min-height: 0; align-content: start; }
.rec-tile { background: var(--bg-tertiary); border-radius: 10px; padding: 6px 8px; min-width: 0; }
.rec-val { font-size: 1.05rem; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rec-tile .rec-lbl { line-height: 1.25; }
.rec-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.rec-btn { min-height: 52px; border-radius: 12px; font-size: 1rem; font-weight: 700; display: flex; align-items: center; justify-content: center; gap: 8px; cursor: pointer; border: 1px solid var(--border); background: var(--bg-tertiary); color: var(--text-primary); }
.rec-btn.is-resume { background: var(--accent); border-color: var(--accent); color: #fff; }
.rec-end { background: #c62828; border-color: #c62828; color: #fff; }
#rec-panel.is-min { height: auto; display: grid; grid-template-columns: 1fr auto; grid-template-areas: "head head" "hero actions"; gap: 6px 10px; }
#rec-panel.is-min .rec-head { grid-area: head; }
#rec-panel.is-min .rec-hero { grid-area: hero; }
#rec-panel.is-min .rec-actions { grid-area: actions; grid-template-columns: auto auto; }
#rec-panel.is-min .rec-chart, #rec-panel.is-min .rec-grid, #rec-panel.is-min .rec-wx, #rec-panel.is-min .rec-gps, #rec-panel.is-min .rec-lbl { display: none; }
#rec-panel.is-min .rec-big { font-size: 1.3rem; white-space: nowrap; }
#rec-panel.is-min .rec-hero { display: flex; gap: 14px; align-items: center; min-width: 0; }
#rec-panel.is-min .rec-btn { min-height: 44px; padding: 0 14px; }
@media (min-width: 769px) {
  html:not(.force-phone) #rec-panel { left: auto; right: 12px; bottom: 12px; width: 380px; height: auto; max-height: 75vh; border: 1px solid var(--border); border-radius: 16px; }
  html:not(.force-phone) body.rec-live #map { bottom: 0; }
}
.rec-sheet-wrap { position: fixed; inset: 0; z-index: 10003; background: rgba(0,0,0,0.65); display: flex; align-items: flex-end; justify-content: center; }
.rec-sheet { width: min(520px, 100vw); background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 16px 16px 0 0; padding: 18px 18px max(18px, env(safe-area-inset-bottom)); display: flex; flex-direction: column; gap: 10px; }
.rec-sheet h2 { margin: 0; font-size: 1.15rem; }
.rec-sheet p { margin: 0; font-size: 0.88rem; color: var(--text-secondary); }
.rec-sheet .rec-sheet-btn { min-height: 50px; margin-top: 0; }
.rec-sheet-input { display: block; width: 100%; box-sizing: border-box; margin-top: 4px; min-height: 46px; padding: 0 12px; border-radius: 10px; border: 1px solid var(--border); background: var(--bg-tertiary); color: var(--text-primary); font-size: 1rem; }
.rec-sheet-input:focus { outline: none; border-color: var(--accent); }
.rec-sheet-check { display: flex; gap: 12px; align-items: center; min-height: 44px; font-size: 0.9rem; color: var(--text-primary); cursor: pointer; }
.rec-sheet-check input { width: 22px; height: 22px; accent-color: var(--accent); flex: none; margin: 0; }
.rec-sheet-note { display: flex; gap: 10px; align-items: flex-start; font-size: 0.82rem; line-height: 1.4; color: var(--text-secondary); background: var(--bg-tertiary); border-radius: 10px; padding: 10px 12px; }
.rec-sheet-note i { color: var(--accent); margin-top: 3px; }
.rec-danger { color: #ff6b6b; border: 1px solid rgba(255,107,107,0.5); background: transparent; }
.rec-danger-solid { background: #c62828; border: 1px solid #c62828; color: #fff; }
@media (prefers-reduced-motion: reduce) { .rec-dot { animation: none; } }


/* Clear route — a button in the map rail with the rest of the map's controls
   (Josh, 2026-09-11: "put a clear button on the main map, just like motocamp").
   It used to be a lone pill fixed to the map's top-LEFT corner. Two reasons it
   moved: that made it the only map control not in the rail, and the top-left
   corner is now where the route stats sit (#route-hud), which is the corner a
   route's own numbers want. Red, because it is the one destructive control on
   the map; everything else about it is a .map-btn like its neighbours. */
#btn-clear-route { color: var(--danger, #ff5252); }
#btn-clear-route:hover { border-color: var(--danger, #ff5252); }
#btn-clear-route:focus-visible { outline: 2px solid var(--danger, #ff5252); outline-offset: 2px; }

/* ===== The welcome tour (js/onboarding.js) =====
 *
 * Three surfaces, all non-modal: the offer card (bottom centre, once), the step
 * card with its ring, and the done card. On a phone the step card is a SHEET
 * docked to the bottom (or top) edge and the map is framed into the rest; on a
 * desktop it floats in a clear spot. Josh, 2026-10-31, on the phone tour: "the
 * steps are covering the routes ... looks very unprofessional". So: one quiet
 * surface, one green action, a still ring, and nothing glowing. The spotlight is
 * pointer-events:none, so the control it rings stays usable; the cards turn
 * pointers back on for themselves alone. Every button is a 44px target (WCAG
 * 2.5.5), on every screen: the first thing a new user taps must not be a miss.
 * Position and width are set by _place()/_dock() in the module; this is looks. */

/* The two help buttons at the top of the menu. The tour is the loud one. */
.menu-help-row { display: flex; gap: 8px; margin-bottom: 12px; }
.menu-help-row .btn {
  flex: 1 1 0; display: flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 10px; font-size: 0.88rem;
}
.menu-help-tour { background: var(--accent, #c8f53a); color: #00120a; font-weight: 700; border: none; }
.menu-help-tour:hover { background: var(--accent-hover, #d9ff5c); }

.onboard-offer, .onboard-card {
  position: fixed;
  background: var(--bg-secondary, #1a2632);
  color: var(--text-primary, #e8edf2);
  border: 1px solid var(--border, rgba(255, 255, 255, 0.12));
  border-radius: 14px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
  font-size: 0.9rem; line-height: 1.5;
}
.onboard-offer {
  left: 50%; transform: translateX(-50%);
  bottom: calc(24px + var(--safe-bottom, 0px));
  width: min(420px, calc(100vw - 24px));
  padding: 16px 18px;
}
/* On a phone the offer and the done card are the same docked sheet as the
 * tour's steps: full width, square to the bottom edge, nothing glowing. The
 * pan pad's collapsed tab would peek out above them, so it waits. */
@media (max-width: 768px) {
  body:has(#onboard-offer) #map-ptz, body:has(#onboard-done) #map-ptz { display: none; }
  .onboard-offer {
    left: 0; right: 0; bottom: 0; transform: none; width: auto;
    border-radius: 16px 16px 0 0; border-width: 1px 0 0;
    padding: 16px 18px calc(14px + var(--safe-bottom, 0px));
    box-shadow: 0 -12px 32px rgba(0, 0, 0, 0.42);
  }
}
.onboard-offer-title { font-weight: 700; margin-bottom: 6px; }
.onboard-offer-body, .onboard-body { margin: 0 0 12px; color: var(--text-secondary, #a9b6c2); }
.onboard-actions { display: flex; gap: 8px; justify-content: flex-end; align-items: center; }
.onboard-btn {
  min-height: 44px; padding: 0 16px; border-radius: 8px; cursor: pointer; font: inherit; font-size: 0.85rem;
  background: none; color: var(--text-primary, #e8edf2);
  border: 1px solid var(--border, rgba(255, 255, 255, 0.18));
}
.onboard-btn[hidden] { display: none; }
.onboard-btn-go { background: var(--accent, #c8f53a); border-color: transparent; color: #00120a; font-weight: 700; }
.onboard-btn-go:hover { background: var(--accent-hover, #d9ff5c); }
.onboard-btn.is-busy, .onboard-btn:disabled { opacity: 0.55; cursor: default; }
.onboard-btn:focus-visible, .onboard-skip:focus-visible, .onboard-x:focus-visible {
  outline: 2px solid var(--accent, #c8f53a); outline-offset: 2px;
}

.onboard-card { padding: 10px 16px 12px; }
/* The card takes focus so a screen reader starts on it; it is not a control,
 * so it draws no ring of its own (the green outline read as a glowing box).
 * Its buttons keep theirs. */
.onboard-card:focus, .onboard-card:focus-visible { outline: none; }
.onboard-card-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.onboard-card-head strong { font-size: 1rem; font-weight: 650; line-height: 1.3; }
.onboard-card-meta { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }
#onboard-count { font-size: 0.76rem; color: var(--text-muted, #8b95a5); white-space: nowrap; font-variant-numeric: tabular-nums; }
/* The X is the exit a phone user looks for; the Skip text alone read as part
 * of the body copy (Motocamp, Josh 2026-10-05). */
.onboard-x {
  min-width: 44px; min-height: 44px; margin-right: -12px;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; cursor: pointer; font-size: 1.3rem; line-height: 1;
  color: var(--text-primary, #e8edf2);
}
/* One mark per step (the tour is a sequence); the done ones and this one lit. */
.onboard-progress { display: flex; gap: 3px; margin: 2px 0 10px; }
.onboard-progress > span { flex: 1 1 0; height: 3px; border-radius: 2px; background: rgba(255, 255, 255, 0.13); }
.onboard-progress > span.on { background: var(--accent, #c8f53a); }
.onboard-card .onboard-body { font-size: 0.9rem; line-height: 1.45; }
.onboard-card-foot { justify-content: space-between; }
/* Skip must be findable, never shrunk to nothing; it is the quiet way out,
 * beside the X, so it does not compete with Next. */
.onboard-skip {
  min-height: 44px; padding: 0 8px 0 0; background: none; border: none; cursor: pointer;
  font: inherit; font-size: 0.85rem; color: var(--text-muted, #8b95a5);
}
.onboard-skip:hover { color: var(--text-primary, #e8edf2); }
.onboard-card #onboard-back { border-color: transparent; color: var(--text-secondary, #a9b6c2); }

/* PHONE: the sheet. Full width, square to the edge it is docked to, and the
 * safe area under it kept clear of buttons. */
.onboard-card.is-sheet {
  border-radius: 16px 16px 0 0; border-width: 1px 0 0;
  padding: 10px 18px calc(10px + var(--safe-bottom, 0px));
  box-shadow: 0 -12px 32px rgba(0, 0, 0, 0.42);
}
.onboard-card.is-sheet.dock-top {
  border-radius: 0 0 16px 16px; border-width: 0 0 1px;
  padding-bottom: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.42);
}
/* While the tour runs on a phone, the map loses the clutter beside the sheet:
 * the pan/tilt pad and the 3D pill (the user's map is handed back at the end),
 * and, with the sheet at the top, the right-hand buttons that would peek out
 * from under it. */
body.onboard-on #map-ptz, body.onboard-on #exag-pill { display: none !important; }
body.onboard-on.onboard-top #map-controls { display: none !important; }

/* The spotlight: a 9999px shadow is the dim and the ring is the hole's edge.
 * It used to be a breathing neon glow, because a plain hole in a dim wash
 * "barely reads on a dark app" (Motocamp, Josh 2026-08-06). On 2026-10-31 Josh
 * called the tour "very unprofessional", so: a crisp 2px ring with a soft halo
 * (it still reads, without glowing), settling once as it lands and then still.
 * .no-dim rings a control without dimming the map it sits on (a step whose
 * subject is the map). */
.onboard-spot {
  position: fixed; pointer-events: none; border-radius: 12px;
  box-shadow: 0 0 0 9999px rgba(3, 9, 13, 0.58), 0 0 0 2px var(--accent, #c8f53a), 0 0 0 6px rgba(200, 245, 58, 0.22);
  transition: left 0.25s ease, top 0.25s ease, width 0.25s ease, height 0.25s ease;
}
.onboard-spot.no-dim { box-shadow: 0 0 0 2px var(--accent, #c8f53a), 0 0 0 6px rgba(200, 245, 58, 0.26), 0 4px 18px rgba(0, 0, 0, 0.35); }
@keyframes onboard-spot-in { from { transform: scale(1.06); } }
.onboard-spot.lit { animation: onboard-spot-in 0.32s ease-out; }
/* Less motion asked for: the ring neither settles nor slides. */
@media (prefers-reduced-motion: reduce) {
  .onboard-spot { transition: none; }
  .onboard-spot.lit { animation: none; }
}

/* ===== The home-screen card (js/install-prompt.js) =====
 * Asked on every open until the icon exists, so it has to be quiet: the same
 * surface as the tour's offer, the icon the walker will see on their home
 * screen, one green action. On a phone it is a sheet docked to the bottom edge,
 * as the tour's is. Above every panel; below the sign-in and paywall screens
 * (it never shows over them anyway) and the tour (Onboarding.Z). */
.install-card {
  position: fixed; z-index: 9990;
  left: 50%; transform: translateX(-50%);
  bottom: calc(24px + var(--safe-bottom, 0px));
  width: min(420px, calc(100vw - 24px));
  display: grid; grid-template-columns: 44px 1fr; column-gap: 12px; align-items: start;
  padding: 16px 18px;
  background: var(--bg-secondary, #1a2632);
  color: var(--text-primary, #e8edf2);
  border: 1px solid var(--border, rgba(255, 255, 255, 0.12));
  border-radius: 14px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
  font-size: 0.9rem; line-height: 1.5;
}
.install-card-icon { grid-row: span 2; width: 44px; height: 44px; border-radius: 10px; }
.install-card-title { font-weight: 700; margin-bottom: 2px; }
.install-card-body { color: var(--text-secondary, #a9b6c2); }
.install-card-body b { color: var(--text-primary, #e8edf2); font-weight: 600; }
.install-glyph { vertical-align: -3px; margin: 0 1px; }
.install-card-actions {
  grid-column: 1 / -1; margin-top: 12px;
  display: flex; gap: 8px; justify-content: flex-end; align-items: center;
}
.install-btn {
  min-height: 44px; padding: 0 16px; border-radius: 8px; cursor: pointer; font: inherit; font-size: 0.85rem;
  background: none; color: var(--text-secondary, #a9b6c2);
  border: 1px solid var(--border, rgba(255, 255, 255, 0.18));
}
.install-btn-go { background: var(--accent, #c8f53a); border-color: transparent; color: #00120a; font-weight: 700; }
.install-btn-go:hover { background: var(--accent-hover, #d9ff5c); }
.install-btn:focus-visible { outline: 2px solid var(--accent, #c8f53a); outline-offset: 2px; }
@media (max-width: 768px) {
  .install-card {
    left: 0; right: 0; bottom: 0; transform: none; width: auto;
    border-radius: 16px 16px 0 0; border-width: 1px 0 0;
    padding: 16px 18px calc(14px + var(--safe-bottom, 0px));
    box-shadow: 0 -12px 32px rgba(0, 0, 0, 0.42);
  }
  /* The pan pad's collapsed tab would peek out above the sheet, as it does
     over the tour's. */
  body:has(#install-card) #map-ptz { display: none; }
}

/* ===== Condition reports =================================================
   Walkers' own notes about a place. The visual job here is provenance: this
   block must never be mistaken for something the land manager published, so it
   is boxed, tinted differently from the official blocks above it, and led by
   its own heading rather than blending into the card. */
.reports-section {
  margin-top: 14px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--text-muted);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.03);
}
.reports-head h4 {
  margin: 0 0 4px;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
}
.reports-provenance {
  margin: 0 0 8px;
  font-size: 0.7rem;
  line-height: 1.45;
  color: var(--text-muted);
}
/* An official closure outranks everything below it, and says so. */
.reports-official-wins {
  margin: 0 0 8px;
  padding: 6px 8px;
  border-radius: 6px;
  background: rgba(220, 38, 38, 0.12);
  color: var(--danger, #f87171);
  font-size: 0.72rem;
  line-height: 1.45;
}
.reports-loading, .reports-empty, .reports-signin {
  margin: 0;
  font-size: 0.78rem;
  color: var(--text-muted);
}
/* "Couldn't check" is styled unlike "nothing here" on purpose — the two mean
   opposite things and must never look the same. */
.reports-unknown {
  padding: 6px 8px;
  border-radius: 6px;
  background: rgba(234, 179, 8, 0.12);
  color: var(--warning, #facc15);
}
.reports-pending {
  margin: 0 0 8px;
  font-size: 0.73rem;
  color: var(--text-muted);
  font-style: italic;
}
.reports-counts {
  margin: 0 0 8px;
  font-size: 0.72rem;
  color: var(--text-muted);
  line-height: 1.6;
}
.reports-counts b { color: var(--text); font-weight: 600; }
.reports-counts em { font-style: normal; opacity: 0.8; }
.reports-list { list-style: none; margin: 0; padding: 0; }
.reports-row {
  padding: 7px 0;
  border-top: 1px solid var(--border);
}
.reports-row:first-child { border-top: 0; }
.reports-row-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
/* Age FIRST and unmuted: how old a claim is decides what it is worth, so it
   leads the row rather than trailing it as metadata. */
.reports-age {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text);
}
.reports-tag { font-size: 0.75rem; color: var(--text-muted); }
.reports-stale .reports-age { color: var(--text-muted); }
.reports-note {
  margin: 4px 0 0;
  font-size: 0.78rem;
  line-height: 1.45;
  overflow-wrap: anywhere;   /* a 280-char word must not widen the panel */
}
.reports-flag, .reports-flagged {
  margin-top: 4px;
  font-size: 0.66rem;
  color: var(--text-muted);
}
.reports-flag {
  background: none;
  border: 0;
  padding: 2px 0;
  cursor: pointer;
  text-decoration: underline;
}
.reports-history { margin-top: 8px; }
.reports-history summary {
  cursor: pointer;
  font-size: 0.72rem;
  color: var(--text-muted);
}
.reports-add { margin-top: 10px; }
.reports-form-q { margin: 0 0 6px; font-size: 0.78rem; font-weight: 600; }
.reports-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.reports-chip input { position: absolute; opacity: 0; pointer-events: none; }
.reports-chip span {
  display: inline-block;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.73rem;
  cursor: pointer;
  min-height: 44px;   /* the 44 px floor (05: K2); was 32 */
  line-height: 30px;
}
.reports-chip input:checked + span {
  border-color: var(--accent);
  background: rgba(34, 197, 94, 0.15);
  color: var(--text);
}
.reports-chip input:focus-visible + span { outline: 2px solid var(--accent); }
.reports-note-label { display: block; font-size: 0.73rem; color: var(--text-muted); }
.reports-optional { opacity: 0.7; }
.reports-note-input {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-elev, rgba(0, 0, 0, 0.2));
  color: var(--text);
  font: inherit;
  font-size: 0.78rem;
  resize: vertical;
}
.reports-counter { margin: 3px 0 0; font-size: 0.66rem; color: var(--text-muted); text-align: right; }
.reports-form-hint { margin: 6px 0 8px; font-size: 0.68rem; line-height: 1.45; color: var(--text-muted); }
.reports-form-actions { display: flex; gap: 6px; }
.reports-form-msg { margin: 6px 0 0; font-size: 0.72rem; min-height: 1em; }
/* The author's own report while it is held. Tinted apart from the published
   list so it can never be read as "this is live" — it is visible to one person. */
.reports-mine {
  margin: 0 0 8px;
  padding: 6px 8px;
  border: 1px dashed var(--border);
  border-radius: 6px;
}
.reports-mine-head { margin: 0 0 4px; font-size: 0.7rem; color: var(--text-muted); }
.reports-mine-badge {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 6px;
}
.reports-row-mine { border-top: 0; padding: 4px 0; }
.reports-edit {
  background: none; border: 0; padding: 2px 0; margin-top: 4px;
  font-size: 0.66rem; color: var(--text-muted);
  cursor: pointer; text-decoration: underline;
}
/* Which part of a long track a report is about. Stated, never assumed — a
   74 km trail cannot carry "gated" as a whole. */
.reports-point { margin: 0 0 8px; font-size: 0.68rem; color: var(--text-muted); font-style: italic; }


/* ===== Pan / tilt / zoom pad (index.html #map-ptz, wired in js/app.js) =====
   Ported from the Motocamp fleet 2026-08-10. Bottom-RIGHT: the lower-left
   corner already carries #map-mode-bar (Topo/Sat + 2D/3D) and the exag pill,
   and the top-right is #map-controls — at EVERY width, as of 2026-09-11.

   It was not always. The phone block used to move #map-controls to the
   bottom-right for thumb reach, so from 768px down the pad and the rail both
   claimed this corner. Measured on an S23 over adb: rail x 328-372, pad
   x 258-368 — a 40px overlap that buried the Rain button completely and
   clipped two more, and had since the pad shipped (7bf709f). Josh settled it
   by giving each surface its own corner: "put the buttons top right, and the
   nav bottom right". So the sentence above is now load-bearing in both
   stylesheets — if the rail ever moves back down, this pad has to move. */
#map-ptz {
  position: fixed;
  right: 16px;
  /* + --safe-bottom like every bottom-anchored control (the rule at the top of
     this file). The bare 44px left the pad's bottom row 9 px inside the boot
     smoke's test inset on every run. */
  bottom: calc(44px + var(--safe-bottom));
  z-index: 500;
  display: flex; flex-direction: column; gap: 6px;
  padding: 6px;
  background: var(--bg-secondary);
  border: 1px solid var(--bg-tertiary);
  border-radius: var(--radius);
  box-shadow: 0 4px 16px rgba(0,0,0,0.35);
}
#map-ptz .ptz-pad {
  display: grid;
  grid-template-columns: repeat(3, 38px);
  gap: 4px;
}
/* Column-first, so each pair (in/out, tilt/flatten, left/right) is one column.
   Laid out across instead, the pairs split apart as soon as a phone drops the
   grid to two columns. */
#map-ptz .ptz-row {
  display: grid;
  grid-template-rows: repeat(2, 38px);
  grid-auto-flow: column;
  gap: 4px;
}
#map-ptz button {
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-tertiary);
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--text-primary);
  font-size: 0.95rem;
  cursor: pointer;
  touch-action: manipulation;
}
#map-ptz button:hover { border-color: var(--accent); color: var(--accent); }
#map-ptz button:active { transform: scale(0.94); }
#map-ptz .ptz-home { color: var(--accent); }

/* Phones: the pad is the biggest piece of on-map furniture there is, and the
   thumb is at the bottom of the screen. Sit it above the mode bar rather than
   over the attribution, and shrink the hit targets to 34px — still over the
   44px-with-gap comfortable minimum once the 4px gaps are counted. */
@media (max-width: 768px) {
  /* Clear of #map-controls, which is a 44px column at right:12px on a phone.
     12 (its offset) + 44 (its width) + 8 (the anti-mis-tap gap the rail itself
     uses) = 64px, so the two sit side by side instead of on top of each other.
     Written as the sum rather than as `64px` so it stays legible if the rail's
     width or offset ever moves.
     The bottom offset goes through --safe-bottom like every other
     bottom-anchored control (see the safe-area note above): a bare 96px put
     the pad inside the system-bar zone on any device reporting an inset. */
  /* Down onto the same baseline as #map-mode-bar (Josh, 2026-09-11: "move the
     nav pane down further"). Safe below 96px because the bottom-right corner
     is genuinely empty on a phone — measured on an S23, maplibre's
     .maplibregl-ctrl-bottom-right is a 0x0 box, and the mode bar is
     bottom-LEFT (x 12-132) against this pad's x 253-374. */
  #map-ptz {
    right: 10px;
    bottom: calc(14px + var(--safe-bottom));
    padding: 5px; gap: 5px;
  }
  #map-ptz .ptz-pad { grid-template-columns: repeat(3, 34px); }
  #map-ptz .ptz-row { grid-template-rows: repeat(2, 34px); }
  #map-ptz button { width: 34px; height: 34px; font-size: 0.88rem; }
}

/* ===== On-trail navigation (js/nav.js) =====
 *
 * Chrome only. Nav borrows the MAIN map rather than building a second one, so
 * everything here floats over the map already on screen — which is why the
 * overlay itself must not eat pointer events: the map underneath stays
 * draggable, and only the controls take taps.
 *
 * Sized for a phone held at arm's length on a track, in sun: big numbers, hard
 * contrast, nothing smaller than the turn distance needs to be.
 */
#nav-overlay {
  position: fixed;
  inset: 0;
  z-index: 2600;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  font-variant-numeric: tabular-nums;
}
#nav-overlay.hidden { display: none; }
#nav-overlay button,
#nav-overlay .nav-chip { pointer-events: auto; }

/* While nav owns the screen the planning chrome goes away — it is the same
   map underneath, and a sidebar over a heading-up view is just clutter. */
body.nav-active #sidebar,
body.nav-active #map-ptz,
body.nav-active #map-controls,
body.nav-active #map-mode-bar,
body.nav-active #sidebar-expand,
body.nav-active #btn-clear-route { display: none !important; }
body.nav-active #map { inset: 0 !important; width: 100% !important; }

#nav-top {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  /* Below the status bar and clear of a landscape notch: #nav-close is the
     only way out of nav (05: A3). */
  padding: calc(10px + var(--safe-top)) calc(10px + env(safe-area-inset-right, 0px)) 0 calc(10px + env(safe-area-inset-left, 0px));
}
.nav-chip {
  background: rgba(14, 22, 32, 0.88);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 10px;
  min-width: 44px;
  height: 44px;
  padding: 0 12px;
  font-size: 1rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
#nav-clock { margin-left: auto; cursor: default; }

/* The turn banner. The one thing that has to be readable without stopping. */
#nav-turn {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  background: rgba(14, 22, 32, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  padding: 10px 14px;
  color: #fff;
  max-width: 520px;
}
#nav-turn.hidden { display: none; }
.nav-turn-icon {
  font-size: 1.7rem;
  width: 44px;
  text-align: center;
  flex: none;
}
/* Proximity, as colour: near is the turn you are taking NOW. */
.nav-turn-icon.prox-near { color: #ff5252; }
.nav-turn-icon.prox-mid  { color: #ffab40; }
.nav-turn-icon.prox-far  { color: #b0bec5; }
#nav-turn-dist { font-size: 1.5rem; font-weight: 700; line-height: 1.1; }
#nav-turn-road {
  font-size: 0.92rem;
  color: #cfd8dc;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 60vw;
}

/* Stale-route notice. Pairs with the amber dashed line on the map — one owner
   sets both (js/nav.js showStale), so they can never disagree. */
#nav-offline-note {
  align-self: center;
  margin: 8px 10px;
  background: rgba(255, 171, 64, 0.94);
  color: #1a1400;
  font-weight: 600;
  font-size: 0.86rem;
  border-radius: 10px;
  padding: 8px 12px;
  max-width: 560px;
  text-align: center;
}
#nav-offline-note.hidden { display: none; }
#nav-gps-note {
  align-self: center; margin: 4px 10px; padding: 6px 12px; max-width: 560px;
  background: rgba(14, 22, 32, 0.92); color: #fff; font-weight: 600; font-size: 0.84rem;
  border: 1px solid rgba(255, 255, 255, 0.12); border-left: 3px solid #ffab40;
  border-radius: 10px; text-align: center;
}
#nav-gps-note i { color: #ffab40; margin-right: 4px; }
#nav-gps-note.hidden { display: none; }
#nav-ahead {
  align-self: center; margin: 4px 10px; padding: 5px 12px; max-width: 560px;
  background: rgba(14, 22, 32, 0.88); color: #e0f7fa; font-weight: 600; font-size: 0.82rem;
  border: 1px solid rgba(255, 255, 255, 0.12); border-left: 3px solid #00bcd4; border-radius: 10px;
}
#nav-ahead.hidden { display: none; }
#nav-eta-arrive.nav-eta-dark { color: #ffab40; font-weight: 700; }
/* Battery time left (js/battery.js): amber when it is shorter than the walk
   left, or the phone is at 20% or less. */
#nav-battery:empty { display: none; }
#nav-battery.nav-eta-dark { color: #ffab40; font-weight: 700; }
#nav-climb { color: #ffcc80; white-space: nowrap; }
#nav-climb.hidden { display: none; }

#nav-bottom {
  display: flex;
  align-items: stretch;
  gap: 8px;
  padding: 0 10px calc(10px + var(--safe-bottom));
}
.nav-stat,
#nav-eta {
  background: rgba(14, 22, 32, 0.9);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  padding: 8px 12px;
  color: #fff;
  text-align: center;
  min-width: 84px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
#nav-eta { flex: 1; }
#nav-eta.hidden { display: none; }
.nav-stat-value { font-size: 1.35rem; font-weight: 700; line-height: 1.15; }
.nav-stat-label { font-size: 0.7rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.nav-eta-time { font-size: 1.5rem; font-weight: 700; line-height: 1.15; color: var(--accent); }
.nav-eta-sub {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 10px;
  font-size: 0.8rem;
  color: #cfd8dc;
}

#nav-recentre {
  position: absolute;
  right: 12px;
  bottom: calc(96px + var(--safe-bottom));
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: rgba(14, 22, 32, 0.92);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.16);
  font-size: 1.1rem;
  cursor: pointer;
}

#nav-arrived {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background: var(--bg-card);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 14px;
  padding: 18px 22px;
  text-align: center;
  color: #fff;
  pointer-events: auto;
  max-width: 88vw;
}
#nav-arrived.hidden { display: none; }
.nav-arrived-title { font-size: 1.25rem; font-weight: 700; margin-bottom: 6px; }
.nav-arrived-title i { color: var(--accent); }
.nav-arrived-sub { font-size: 0.9rem; color: #cfd8dc; margin-bottom: 14px; }
.nav-arrived-actions { display: flex; gap: 8px; justify-content: center; }

/* The walker mark: a translucent beam for the direction AND how vague it is,
   with a solid chevron at its leading edge — the part you can actually read at
   a glance on a bright screen. Both marks, the way Maps draws them; the beam
   alone is too quiet and the chevron alone loses the honesty about accuracy.
   Drawn pointing UP and rotated by the map (rotation-alignment: map), so it
   tracks the real-world bearing under any map rotation. */
.nav-walker { width: 46px; height: 46px; position: relative; }
.nav-walker-beam {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 46px;
  height: 23px;
  margin: -23px 0 0 -23px;
  background: radial-gradient(closest-side at 50% 100%,
    rgba(0, 150, 255, 0.55), rgba(0, 150, 255, 0.22) 55%, rgba(0, 150, 255, 0) 100%);
  clip-path: polygon(50% 100%, 6% 0, 94% 0);
}
.nav-walker-chevron {
  position: absolute;
  left: 50%;
  top: 6px;
  width: 0;
  height: 0;
  margin-left: -8px;
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
  border-bottom: 12px solid #0096ff;
  filter: drop-shadow(0 0 1.5px #fff) drop-shadow(0 0 1.5px #fff);
}
.nav-walker-dot {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: 50%;
  background: #0096ff;
  border: 2.5px solid #fff;
  box-shadow: 0 0 4px rgba(0, 0, 0, 0.5);
}

@media (max-width: 480px) {
  #nav-turn-dist { font-size: 1.3rem; }
  .nav-stat-value { font-size: 1.15rem; }
  .nav-eta-time { font-size: 1.3rem; }
  .nav-stat, #nav-eta { min-width: 72px; padding: 7px 9px; }
}

/* ===== Route HUD (js/route-hud.js) =====
 *
 * Three tiles in the map's TOP-LEFT — Distance, Time, Arrive — up only while a
 * route is drawn. The corner is free because #btn-clear-route moved into the
 * map rail; before that this was the one spot they would have collided.
 */
#route-hud {
  position: fixed;
  top: calc(var(--top-bar-height) + 16px);
  left: calc(var(--sidebar-width, 0px) + 16px);
  /* Bounded on the RIGHT as well as the left. #map-controls is fixed top-right
     at the same z-index and later in the DOM, so it paints ON TOP: a long route
     ("1429.2 mi", "28h 30m", "Wed 2:20 pm") grew the row straight under the
     button column and buried the Arrive tile. The column's width is not a
     constant — it depends on the font, and on whether Clear is showing, which
     is precisely when these tiles are showing too — so route-hud.js MEASURES
     it and sets --map-controls-w. 116px is only the fallback. Plus the
     column's own 16px offset, plus 8px of air. */
  right: calc(16px + var(--map-controls-w, 116px) + 8px);
  width: fit-content;
  z-index: 500;
  display: flex;
  flex-wrap: wrap;              /* whole tiles wrap; a value never breaks mid-string */
  gap: 6px;
  pointer-events: none;         /* never steal a tap meant for the map */
}
#route-hud.hidden { display: none !important; }
/* A wrapped tile goes to the RIGHT end of its row, not the left, where the
   map's own top-left controls sit one row down. On a single row this costs
   nothing: a fit-content box has no spare width for an auto margin to take. */
.route-hud-tile:last-child { margin-left: auto; }
.route-hud-tile {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 64px;
  padding: 5px 9px;
  background: var(--bg-secondary);
  border: 1px solid var(--bg-tertiary);
  border-radius: var(--radius-sm, 6px);
  box-shadow: var(--shadow);
}
.route-hud-label {
  font-size: 0.58rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.route-hud-value {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
}

/* The 3D compass is added to the map's own top-left corner, which is where the
   tiles sit. Stepped down only while they are up. */
body.route-hud-on .maplibregl-ctrl-top-left { margin-top: 52px; }

@media (max-width: 768px) {
  /* Phone: the sidebar overlays the map rather than offsetting it, so anchor to
     the viewport, and clear #mobile-header the way the map rail does. */
  #route-hud {
    top: calc(var(--mobile-header-height) + 12px);
    left: 12px;
    right: calc(12px + var(--map-controls-w, 60px) + 8px);
    gap: 5px;
  }
  .route-hud-tile { min-width: 0; padding: 4px 7px; }
  .route-hud-label { font-size: 0.54rem; }
  .route-hud-value { font-size: 0.82rem; }
}

/* Nav owns the whole screen and carries its own distance/ETA readouts, so the
   planning tiles would be a second, quieter copy of the same numbers. */
body.nav-active #route-hud { display: none !important; }

/* Cell coverage estimate in the info panel: Motocamp NZ's look, one bar per
   carrier (colour set in js by carrier name, see Layers.CARRIER_COLORS). */
.info-cell-coverage { margin-bottom: 16px; }
.cell-bar-container { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.cell-carrier { font-size: 0.78rem; width: 70px; color: var(--text-secondary); }
.cell-bar { flex: 1; height: 6px; background: var(--bg-tertiary); border-radius: 3px; overflow: hidden; }
.cell-bar-fill { height: 100%; border-radius: 3px; transition: width 0.5s ease; }
.cell-tech { font-size: 0.68rem; color: var(--text-muted); width: 30px; }

/* ===== Iconic routes tab (js/iconic.js) ===== */
.iconic-region { border-bottom: 1px solid var(--border, rgba(255,255,255,0.08)); }
.iconic-region summary { cursor: pointer; padding: 10px 2px; font-weight: 600; font-size: 0.9rem; list-style-position: inside; }
.iconic-count { color: var(--text-muted); font-weight: 400; font-size: 0.75rem; margin-left: 4px; }
.iconic-route { display: block; width: 100%; text-align: left; background: var(--bg-tertiary); color: var(--text-primary);
  border: 1px solid transparent; border-radius: 8px; padding: 9px 11px; margin: 0 0 6px; cursor: pointer; font: inherit; }
.iconic-route:hover, .iconic-route:focus-visible { border-color: var(--accent); }
.iconic-name { display: block; font-size: 0.85rem; font-weight: 600; }
.iconic-meta { display: block; font-size: 0.72rem; color: var(--text-muted); margin-top: 2px; }
.iconic-place { display: block; font-size: 0.74rem; color: var(--text-secondary, #b8c7d3); margin-top: 1px; }

/* ===== History (js/history.js) — full-screen trip history + replay bar ===== */
#history-view {
  position: fixed; inset: 0; z-index: 9000; overflow-y: auto;
  background: var(--bg-primary); color: var(--text-primary);
  -webkit-overflow-scrolling: touch;
}
#history-view[hidden] { display: none; }
.hist-head {
  position: sticky; top: 0; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: max(12px, env(safe-area-inset-top)) 16px 12px; background: var(--bg-secondary); border-bottom: 1px solid var(--border);
}
.hist-head h2 { margin: 0; font-size: 1.1rem; }
.hist-head h2 i { color: var(--accent); }
.hist-body { max-width: 960px; margin: 0 auto; padding: 16px 16px 48px; display: flex; flex-direction: column; gap: 14px; }
.hist-card { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 12px; padding: 14px; min-width: 0; }
.hist-card h3 { margin: 0 0 8px; font-size: 0.85rem; color: var(--text-secondary); font-weight: 600; }
.hist-muted { color: var(--text-muted); font-size: 0.78rem; }
.hist-note { margin: 8px 0 0; font-size: 0.75rem; color: #ffab40; display: flex; gap: 6px; align-items: flex-start; }
.hist-btn {
  min-height: 40px; padding: 8px 12px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg-tertiary); color: var(--text-primary); cursor: pointer; font-size: 0.8rem;
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
}
.hist-btn:hover { border-color: var(--text-muted); }
.hist-btn.on { border-color: var(--accent); color: var(--accent); }
.hist-primary { background: var(--accent); color: #062b14; border-color: var(--accent); font-weight: 600; }
.hist-danger { color: var(--danger); }
.hist-exit { margin-left: auto; }
.hist-totals { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.hist-total { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 12px; padding: 12px; min-width: 0; }
.hist-total-label { font-size: 0.7rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.hist-total-big { font-size: 1.45rem; font-weight: 700; margin: 4px 0 2px; font-variant-numeric: tabular-nums; }
.hist-total-sub { font-size: 0.72rem; color: var(--text-secondary); }
.hist-records { grid-column: 1 / -1; font-size: 0.75rem; color: var(--text-secondary); }
.hist-records a { color: var(--accent); }
.hist-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; justify-content: space-between; }
.hist-seg { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.hist-seg button { background: none; border: 0; color: var(--text-secondary); padding: 8px 12px; min-height: 36px; cursor: pointer; font-size: 0.78rem; }
.hist-seg button.on { background: var(--accent-dim); color: var(--accent); font-weight: 600; }
.hist-chart { margin-top: 10px; overflow: hidden; }
.hist-chart svg { display: block; max-width: 100%; height: auto; }
.hist-chart-sum { font-size: 0.85rem; font-weight: 600; margin-bottom: 4px; }
.hist-axis { stroke: var(--border); }
.hist-tick { fill: var(--text-muted); font-size: 10px; }
.hist-bar { cursor: pointer; }
.hist-hit { fill: transparent; }
.hist-fill { fill: #29b6f6; opacity: 0.75; }
.hist-bar.now .hist-fill { opacity: 1; }
.hist-bar:hover .hist-fill { opacity: 1; }
.hist-bar.on .hist-fill { fill: #ff7043; opacity: 1; }
#hist-q, #hist-act {
  min-height: 40px; background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border);
  border-radius: 8px; padding: 0 10px; font-size: 0.85rem;
}
#hist-q { flex: 1; min-width: 140px; }
.hist-chip { margin-top: 10px; background: rgba(255,112,67,0.15); color: #ff8a65; border: 1px solid rgba(255,112,67,0.4); border-radius: 999px; padding: 6px 12px; cursor: pointer; font-size: 0.78rem; }
.hist-count { margin: 10px 0 6px; }
.hist-trip {
  width: 100%; display: flex; gap: 12px; align-items: center; text-align: left; padding: 10px; margin-bottom: 6px;
  background: var(--bg-tertiary); border: 1px solid transparent; border-radius: 10px; color: var(--text-primary); cursor: pointer;
}
.hist-trip:hover { border-color: var(--border); }
.hist-thumb { flex-shrink: 0; background: var(--bg-primary); border-radius: 8px; }
.hist-thumb path, .hist-shape-svg path { fill: none; stroke: #ff7043; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.hist-thumb-none { display: flex; align-items: center; justify-content: center; color: var(--text-muted); }
.hist-trip-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.hist-trip-title { font-weight: 600; font-size: 0.9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hist-trip-title i { color: var(--text-muted); }
.hist-trip-date { font-size: 0.72rem; color: var(--text-muted); }
.hist-trip-stats { display: flex; gap: 14px; font-size: 0.8rem; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.hist-trip-stats b { color: var(--text-primary); }
.hist-more { width: 100%; justify-content: center; margin-top: 4px; }
.hist-empty { text-align: center; padding: 48px 16px; }
.hist-empty > i { font-size: 2.4rem; color: var(--text-muted); }
.hist-trip-head h2 { margin: 4px 0 12px; font-size: 1.5rem; }
.hist-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 10px 16px; }
.hist-stat-v { font-size: 1.25rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.hist-stat-l { font-size: 0.7rem; color: var(--text-muted); }
.hist-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.hist-split-grid { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: 16px; align-items: start; }
.hist-shape-svg { width: 100%; max-width: 260px; background: var(--bg-primary); border-radius: 10px; }
.hist-start { fill: var(--accent); stroke: #fff; stroke-width: 2; }
.hist-end { fill: #ff5252; stroke: #fff; stroke-width: 2; }
.hist-elev { width: 100%; height: auto; display: block; }
.hist-elev-area { fill: rgba(41,182,246,0.15); }
.hist-elev-line { fill: none; stroke: #29b6f6; stroke-width: 2; }
.hist-cursor { stroke: #ff5722; stroke-width: 2; }
.hist-splits { width: 100%; border-collapse: collapse; font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.hist-splits th { text-align: left; color: var(--text-muted); font-weight: 500; padding: 4px 6px; }
.hist-splits td { padding: 5px 6px; border-top: 1px solid var(--border); }
.hist-pacebar { width: 45%; }
.hist-pacebar span { display: block; height: 10px; border-radius: 3px; background: #29b6f6; opacity: 0.7; }
.hist-fast td { color: var(--accent); }
.hist-fast .hist-pacebar span { background: var(--accent); opacity: 1; }
/* replay on the map */
.hist-dot { width: 18px; height: 18px; border-radius: 50%; background: #ff5722; border: 3px solid #fff; box-shadow: 0 0 0 4px rgba(255,87,34,0.35), 0 2px 6px rgba(0,0,0,0.5); }
#history-replay {
  position: fixed; z-index: 8000; left: 50%; transform: translateX(-50%); bottom: max(12px, env(safe-area-inset-bottom));
  width: min(640px, calc(100vw - 24px)); background: var(--bg-secondary); border: 1px solid var(--border);
  border-radius: 14px; padding: 10px 12px; box-shadow: 0 8px 28px rgba(0,0,0,0.5); display: flex; flex-direction: column; gap: 8px;
}
.hr-top, .hr-ctl { display: flex; align-items: center; gap: 8px; }
.hr-title { flex: 1; min-width: 0; font-weight: 600; font-size: 0.88rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: center; }
.hr-elev { cursor: pointer; touch-action: none; }
.hr-elev .hist-tick { font-size: 9px; }
.hr-slider { flex: 1; accent-color: #ff5722; min-width: 0; }
.hr-speed { min-height: 40px; background: var(--bg-tertiary); color: var(--text-primary); border: 1px solid var(--border); border-radius: 8px; }
.hr-read { flex: 1; font-size: 0.78rem; color: var(--text-secondary); font-variant-numeric: tabular-nums; min-width: 0; }
.hr-modes { display: flex; gap: 6px; }
.hr-modes .hist-btn { min-height: 36px; padding: 6px 9px; font-size: 0.74rem; }
@media (max-width: 720px) {
  .hist-totals { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hist-split-grid { grid-template-columns: minmax(0, 1fr); }
  .hist-shape-svg { max-width: 220px; margin: 0 auto; display: block; }
  .hist-wide { display: none; }
  .hr-ctl:last-child { flex-wrap: wrap; }
  .hr-read { flex-basis: 100%; }
  .hist-head .hist-btn { padding: 8px 10px; }
}
/* The replay bar carries its own Topo/Sat and 2D/3D buttons and sits where the
   map's mode pills are, so those step aside while a replay is on screen. */
body.history-replaying #map-mode-bar,
body.history-replaying #exag-pill { display: none !important; }
/* History topo previews: the mini map parks off-screen as the thumbnail
   renderer (it must keep a real size to render), or lives in the trip page. */
.hist-mini-parked { position: fixed; left: -10000px; top: 0; pointer-events: none; }
.hist-mini-live { position: relative; border-radius: 10px; overflow: hidden; }
#hist-trip-map { height: 340px; border-radius: 10px; overflow: hidden; background: var(--bg-primary); }
img.hist-thumb { object-fit: cover; }
@media (min-width: 721px) { .hist-split-grid { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); } }
@media (max-width: 720px) { #hist-trip-map { height: 260px; } }
/* Replay pane: the trip page's stats, compact (Josh, 2026-11-06). */
.hr-title { text-align: left; }
.hr-when { font-size: 0.7rem; font-weight: 400; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hr-name { font-size: 0.95rem; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hr-stats { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px 10px; }
.hr-stats .hist-stat-v { font-size: 0.95rem; }
.hr-stats .hist-stat-l { font-size: 0.64rem; }
@media (max-width: 720px) {
  .hr-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .hr-stats .hist-stat-v { font-size: 0.85rem; }
}
/* History: tagged-by-a-friend inbox and the "With" line */
.hist-tag { display: flex; gap: 12px; align-items: center; justify-content: space-between; flex-wrap: wrap; border-color: rgba(200,245,58,0.4); margin-bottom: 10px; }
.hist-tag-main { min-width: 0; font-size: 0.85rem; display: flex; flex-direction: column; gap: 3px; }
.hist-tag-act { display: flex; gap: 8px; }
.hist-with { font-size: 0.8rem; color: var(--text-secondary); margin: -6px 0 12px; }
.hist-with i { color: var(--text-muted); margin-right: 4px; }

