/* ============================================================================
   OpenDrop — first-class light + dark theming.
   Light tokens live on :root; dark tokens override on :root[data-theme="dark"].
   The active theme is resolved before first paint by an inline script in index.html.
   ========================================================================== */
:root {
  color-scheme: light;

  /* surfaces */
  --surface: #ffffff;
  --surface-2: #f4f7f9;
  --surface-3: #e7edf1;
  --map-base: #eef2f5;

  /* text */
  --ink: #18242a;
  --ink-2: #51606a;
  --ink-3: #69767f;  /* ~4.66:1 on --surface — meets WCAG AA for the small muted text it labels */

  /* lines */
  --border: #d8dee3;
  --border-2: #c6ced5;

  /* brand + action */
  --brand: #15616d;
  --accent: #2b6cb0;
  --accent-hover: #2c5282;
  --accent-soft: #ecf3fb;
  --on-accent: #ffffff;

  /* confidence */
  --high: #1a9850;
  --medium: #e08c2e;
  --low: #d73027;
  --high-soft: #e7f5ec;
  --low-soft: #fdecea;
  /* Legend caption TEXT needs 4.5:1 on the white legend surface; the vivid --high/--medium above
     are too light for 12.5px bold words (2.6–3.7:1), so captions use deeper on-white variants that
     still read green/orange/red. The bar swatches keep the vivid tokens. Dark theme overrides these
     back to the bright tokens (which already pass on the dark surface). */
  --cap-high: #15803d;
  --cap-medium: #b45309;
  --cap-low: #c62828;

  /* elevation */
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.08);
  --shadow: 0 8px 28px rgba(16, 24, 40, 0.14), 0 2px 6px rgba(16, 24, 40, 0.08);

  /* shape */
  --r-card: 16px;
  --r-ctl: 12px;
  --r-input: 10px;
  --r-pill: 999px;
  --focus: 0 0 0 3px rgba(43, 108, 176, 0.35);

  /* type scale — the ONLY five sizes used in un-zoomed overlays (popover, sheets, modals).
     16px on all form fields kills iOS Safari's focus-zoom. Zoomed chrome keeps authored sizes. */
  --fs-caption: 13px;
  --fs-small: 14px;
  --fs-body: 16px;
  --fs-h3: 18px;
  --fs-title: 21px;

  /* toasts */
  --toast-bg: #2a3640;
  --toast-ok: #1a7f4b;
  --toast-err: #b03030;
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --surface: #17242a;
  --surface-2: #1e2e35;
  --surface-3: #294049;
  --map-base: #0e1418;

  --ink: #e8eef1;
  --ink-2: #abbac2;
  --ink-3: #7d8f99;

  --border: #2c424b;
  --border-2: #3a525c;

  --brand: #2a8597;
  --accent: #4a92d8;
  --accent-hover: #6aa9e3;
  --accent-soft: #16314a;
  --on-accent: #ffffff;

  --high: #3fbf76;
  --medium: #eaa94e;
  --low: #ef6a5d;
  --high-soft: #143425;
  --low-soft: #3a1f1d;
  --cap-high: var(--high);
  --cap-medium: var(--medium);
  --cap-low: var(--low);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);
  --shadow: 0 10px 32px rgba(0, 0, 0, 0.6), 0 2px 8px rgba(0, 0, 0, 0.45);

  --focus: 0 0 0 3px rgba(74, 146, 216, 0.45);

  --toast-bg: #0e1418;
  --toast-ok: #17693f;
  --toast-err: #9c2b2b;
}

* { box-sizing: border-box; }
html, body {
  margin: 0; height: 100%; overflow: hidden;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color: var(--ink); background: var(--map-base);
}
/* body is the containing block for the absolute overlay panels (below). The place/list panels sit
   translated 100% OFF the right/left edge when closed; contained by this position:relative +
   overflow:hidden body, that off-canvas box is CLIPPED instead of extending the document's
   scrollable width — which is what produced a horizontal scrollbar and a "phantom sidebar" strip of
   dead map space that only vanished while a panel was open. Absolutely-positioned children do not
   expand their containing block, so body stays viewport-width and the panels still dock flush. */
body { position: relative; }
/* THE LAYOUT CONTRACT (responsive redesign): the map is a FIXED base layer -- never a layout
   child, so no rail/sheet/panel can ever squeeze it. Everything else overlays it on one documented
   z-scale:  map 0  <  map controls / FABs 10  <  rails & sheets 20  <  top bar 30  <  popovers 40
   (modals/toasts keep their historical 1500+ values -- always above every band). */
:root { --z-ctl: 10; --z-rail: 20; --z-bar: 30; --z-pop: 40; }
#map { position: fixed; inset: 0; width: 100dvw; height: 100dvh; z-index: 0; background: var(--map-base); }

/* Screen-reader-only text (page h1, search live region). Both class names resolve to one rule so
   markup can use either; new markup standardizes on .sr-only. */
.sr-only, .visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

:focus-visible { outline: none; box-shadow: var(--focus); border-radius: 6px; }

/* ---- shared button system ------------------------------------------------ */
.btn {
  appearance: none; border: 1px solid transparent; border-radius: var(--r-input);
  padding: 9px 14px; font-size: 14px; font-weight: 600; cursor: pointer;
  background: var(--surface-3); color: var(--ink); line-height: 1.2;
  transition: background .15s, border-color .15s, color .15s, transform .05s;
}
.btn:hover { background: var(--surface-2); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .6; cursor: default; }
.btn.primary { background: var(--accent); color: var(--on-accent); }
.btn.primary:hover { background: var(--accent-hover); }
.btn.ghost { background: var(--surface); border-color: var(--border); color: var(--ink); }
.btn.ghost:hover { background: var(--surface-2); border-color: var(--border-2); }
.btn.tiny { padding: 6px 10px; font-size: 12.5px; border-radius: 8px; }
/* quiet = borderless text button for utility rows; danger is quiet-red, never filled red */
.btn.quiet { background: none; border-color: transparent; color: var(--accent); }
.btn.quiet:hover { background: var(--accent-soft); }
.btn.danger { background: none; border-color: var(--border); color: var(--low); }
.btn.danger:hover { background: var(--low-soft); border-color: var(--low); }
.btn.quiet.danger { border-color: transparent; }
@media (pointer: coarse) {
  .btn, .btn.tiny { min-height: 44px; }
  .search-results li, .list-item { min-height: 44px; }
  /* Report chips / remove links to a comfortable 44px. (search-clear must beat its own base rule,
     so it lives in the MOBILE COMPAT section at the end of the file.) */
  .chip, .rate-clear, .linklike { min-height: 44px; display: inline-flex; align-items: center; }
}
.sheet-x, .list-close, .modal-close { width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; padding: 0; border-radius: 8px; }
.sheet-x:hover, .list-close:hover, .modal-close:hover { color: var(--ink); background: var(--surface-2); }
.sheet-gps input, .ph-low input { width: 20px; height: 20px; accent-color: var(--accent); }
.seg button:focus-visible { box-shadow: var(--focus); }
.seg button.on:focus-visible, .seg button.chosen:focus-visible { box-shadow: var(--focus), var(--shadow-sm); }

/* ---- shared inputs ------------------------------------------------------- */
label { display: block; font-size: 12px; margin: 10px 0 3px; color: var(--ink-2); font-weight: 600; }
input, select, textarea {
  width: 100%; padding: 9px 11px; font-size: var(--fs-body); color: var(--ink);
  background: var(--surface); border: 1px solid var(--border-2); border-radius: var(--r-input);
}
/* Inside the 1.25x-zoomed desktop panel, 13px authored = 16.25px effective — keeps the token scale
   without double-scaling. (Phones un-zoom the panel, so they get the full 16px.) */
@media (min-width: 481px) {
  .panel input, .panel select { font-size: 13px; }
}
input::placeholder { color: var(--ink-3); }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus); }

/* ---- segmented control --------------------------------------------------- */
.seg { display: inline-flex; background: var(--surface-3); border-radius: 10px; padding: 3px; gap: 2px; }
.seg button {
  flex: 1; border: none; background: transparent; color: var(--ink-2);
  padding: 7px 10px; border-radius: 8px; font-size: 13px; font-weight: 600; cursor: pointer;
}
.seg button.on, .seg button.chosen { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm); }

/* ---- lazy Turnstile host: takes space only once a widget is live ----------
   (collapsed via max-height, not display:none, so the arrival can animate — see the
   "Turnstile arrival" rules further down) */
.ts.ts-live { margin: 8px 0; min-height: 62px; }

/* ============================ UI SCALE ==================================== */
/* Whole-chrome enlargement uses the CSS `zoom` property (standardized; Baseline since May 2024,
   Firefox 126+). Unlike transform:scale it participates in layout, so hit targets and stacking
   scale correctly. CRITICAL: zoom is applied ONLY to chrome that floats over the map — NEVER to
   #map, its ancestors, or anything Leaflet position-computes inside .leaflet-map-pane (popups/
   markers), where scaling breaks coordinate math (Leaflet #2795). Inside a zoomed subtree every
   used length INCLUDING vw/vh is multiplied by the factor, so viewport-derived formulas below are
   pre-divided by it. Full-height drawers/modals (.list-panel, .photo-modal, .pin-sheet) are
   excluded (inset-anchored boxes don't zoom cleanly) and get explicit font bumps instead. */
.map-status, .toast-host, .panel { zoom: 1.25; }

/* Keyboard focus rings for the chrome buttons: their box-shadow declarations out-cascade the
   global :focus-visible rule, so restore the ring explicitly (ring stacked over the elevation). */
.locate-btn:focus-visible, .add-btn:focus-visible, .bar-btn:focus-visible,
.zoom-btn:focus-visible, .list-close:focus-visible { box-shadow: var(--focus), var(--shadow); }

/* Phones: the zoomed submit panel's content box would drop below Cloudflare Turnstile's hard
   300px minimum width and clip the widget in the Add flow — un-zoom it there. */
@media (max-width: 480px) {
  /* un-zoom the Add panel on phones; its width/height/padding are set in MOBILE COMPAT (end of file)
     because the base .panel rule is defined later and would otherwise win the cascade. */
  .panel { zoom: 1; }
}

/* ============================ MAP CHROME ================================== */

/* Popovers (z40) + the shared click-away backdrop (z39). The confidence legend and the basemap
   picker both live here now, opened from the top-bar action cluster (or the mobile FABs) — nothing
   is permanently parked over the map corners anymore. */
.pop-backdrop { position: fixed; inset: 0; z-index: calc(var(--z-pop) - 1); background: transparent; }
/* Popovers open to the LEFT of the menu rail (which is ~44px wide at the right gutter). */
.map-pop {
  position: fixed; top: calc(env(safe-area-inset-top) + 64px); right: 66px; z-index: var(--z-pop);
  width: 236px; padding: 8px; background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow);
  animation: rail-drop .16s ease-out;
}
#legend-pop { width: 254px; padding: 16px; }
.pop-row {
  width: 100%; min-height: 44px; display: flex; align-items: center; gap: 10px; padding: 10px 12px;
  border: none; border-radius: 10px; background: transparent; color: var(--ink);
  font-size: 15px; font-weight: 600; text-align: left; cursor: pointer;
}
.pop-row:hover { background: var(--surface-2); }
.pop-row.active { background: var(--accent-soft); color: var(--accent); }
.pop-swatch { width: 20px; height: 20px; border-radius: 5px; flex: none; border: 1px solid var(--border); }

/* The legend content (unchanged markup) — its own card chrome is gone; the popover IS the card. */
.legend { color: var(--ink); font-size: 14px; line-height: 1.7; text-align: center; }
.legend strong { display: block; font-size: 17px; margin-bottom: 5px; color: var(--brand); white-space: nowrap; }
.legend-row { display: flex; align-items: center; justify-content: center; gap: 7px; }

/* Horizontal confidence scale: three color bars with a colored word centered under each. Colors
   track the marker palette (--high/--medium/--low) so the legend reads as the map's key. */
/* grid with three equal 1fr tracks => the bars are exactly the same length regardless of label
   width; each label is centered within its own track, directly under its bar. */
.conf-scale { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 9px 2px 3px; }
.conf-col { display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0; }
.conf-bar { width: 100%; height: 7px; border-radius: 4px; }
.conf-bar.low { background: var(--low); }
.conf-bar.medium { background: var(--medium); }
.conf-bar.high { background: var(--high); }
.conf-cap { font-size: 12px; font-weight: 700; line-height: 1; white-space: nowrap; }
.conf-cap.low { color: var(--cap-low); }
.conf-cap.medium { color: var(--cap-medium); }
.conf-cap.high { color: var(--cap-high); }

.dot { display: inline-block; width: 11px; height: 11px; border-radius: 50%; border: 1px solid var(--surface); box-shadow: 0 0 0 1px rgba(0, 0, 0, .12); }
/* Legend swatches are pinned to the fixed marker palette (BUCKET_COLORS in config.js)
   so they always match the pins on the map, in either theme. The semantic --high/--medium/
   --low tokens drive themeable UI accents (soft fills, hover borders) and may differ. */
.dot.high { background: #1a9850; }
.dot.medium { background: #f1a340; }
.dot.low { background: #d73027; }

/* Legend swatch for the "awaiting feedback" pins: a violet RING (transparent center) mirroring the
   .odc-pending marker stroke (#a855f7). Fixed violet in both themes to stay pinned to the marker
   color; a soft glow keeps it visible on the surface without matching the animated pin's pulse. */
.legend-pending { margin-top: 5px; }
.ring-swatch { display: inline-block; width: 11px; height: 11px; border-radius: 50%; }
.ring-swatch.pending { background: transparent; border: 2px solid #a855f7; box-shadow: 0 0 3px rgba(168, 85, 247, .7); }

/* Top-bar action buttons (layers / legend "i" / theme) — 44px targets per the redesign. On mobile
   the legend+theme pair (#util-cluster) is relocated to a bottom-left FAB stack by js/chrome.js. */
.bar-btn {
  width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; flex: none;
  background: var(--surface); color: var(--ink-2); border: 1px solid var(--border);
  border-radius: 12px; font-size: 16px; font-weight: 700; line-height: 1; cursor: pointer;
}
.bar-btn:hover { background: var(--surface-2); color: var(--ink); }
.bar-btn[aria-expanded="true"] { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.theme-toggle { font-size: 17px; }

/* Menu: the hamburger sits in the bar; the rail drops straight down from it (top-right), and the
   legend/layers popovers open to the rail's LEFT (see .map-pop). */
.topbar-actions { position: relative; display: flex; align-items: center; flex: none; }
.menu-rail {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: var(--z-pop);
  display: flex; flex-direction: column; gap: 8px; animation: rail-drop .16s ease-out;
}
.menu-rail[hidden] { display: none; }
.rail-btn { box-shadow: var(--shadow); }
@keyframes rail-drop { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* ---- TOP BAR (z30) -----------------------------------------------------------------------------
   The redesign's chrome bar: brand | flex search | action cluster. Desktop (>=1024px): a full-width
   64px surface the side rails tuck under (rails start at top:64). Mobile: a floating row on a
   gradient scrim with safe-area padding. Flex means nothing can overlap — the search shrinks, never
   slides. pointer-events:none keeps any empty band click-through; children re-enable events. */
.topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-bar);
  display: flex; align-items: center; gap: 12px; height: 64px; padding: 0 16px;
  background: color-mix(in srgb, var(--surface) 96%, transparent);
  border-bottom: 1px solid var(--border);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  pointer-events: none;
}
.topbar > * { pointer-events: auto; }

/* Brand lockup. Desktop shows the wordmark; mobile shows the dedicated square icon (below). */
.brand { flex: 0 0 auto; line-height: 0; }
.brand-icon { display: none; }
.brand-logo { display: block; height: 44px; width: auto; }
/* Mobile: the bar floats on a scrim instead of being a solid surface, and the brand crops to a
   44px icon-only card (the wordmark art is the right two-thirds of the image). */
@media (max-width: 1023px) {
  .topbar {
    height: auto; align-items: center; gap: 8px;
    padding: calc(env(safe-area-inset-top) + 10px) 12px 10px;
    background: linear-gradient(180deg, color-mix(in srgb, var(--surface) 94%, transparent),
                color-mix(in srgb, var(--surface) 40%, transparent) 80%, transparent);
    border-bottom: none; backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  /* Mobile: the dedicated square icon in a card — no more cropping the wordmark into an "icon". */
  .brand {
    display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; padding: 6px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 12px; box-shadow: var(--shadow-sm);
  }
  .brand-wordmark { display: none; }
  .brand-icon { display: block; width: 100%; height: 100%; object-fit: contain; }
}

/* Bottom-right control stack (z10): zoom pill above locate above the Add CTA. The attribution
   lives in the OPPOSITE corner (bottom-left) now, so no height-tracking is needed — separate
   corners simply cannot collide. Desktop: shifts left when the detail rail opens. */
.map-ctl-br {
  position: fixed; right: 16px; bottom: 16px; z-index: var(--z-ctl);
  display: flex; flex-direction: column; align-items: flex-end; gap: 12px;
}

/* Add button (primary CTA) — 48px per the redesign */
.add-btn {
  display: flex; align-items: center; justify-content: center; height: 48px;
  background: var(--accent); color: var(--on-accent); border: none; border-radius: 14px;
  padding: 0 20px; font-size: 15px; font-weight: 700; cursor: pointer; box-shadow: var(--shadow);
}
.add-btn:hover { background: var(--accent-hover); }

/* "Show my location" — 46px circular target (WCAG 2.5.5 AAA / Apple HIG ≥44px), icon centered,
   themed via CSS variables in both light and dark. */
.locate-btn {
  position: relative; width: 46px; height: 46px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface); color: var(--accent); border: 1px solid var(--border);
  box-shadow: var(--shadow); cursor: pointer; padding: 0;
  transition: background .15s, color .15s, border-color .15s, transform .15s;
}
/* hover:hover only — on touch, :hover sticks after a tap, leaving the button enlarged with its
   tooltip lingering until the next tap elsewhere. */
@media (hover: hover) {
  .locate-btn:hover { background: var(--accent-soft); border-color: var(--accent); transform: scale(1.08); }
}
.locate-btn:active { transform: scale(0.96); }
.locate-btn svg { display: block; }
.locate-btn.busy svg { animation: odc-spin 1s linear infinite; }
@keyframes odc-spin { to { transform: rotate(360deg); } }
@keyframes odc-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

/* Theme-aware tooltip (the native title attribute can't be styled and is invisible to keyboard/
   touch users — MDN's guidance is a custom tooltip; aria-label carries the accessible name). */
.locate-btn::after {
  content: attr(data-tooltip);
  position: absolute; right: calc(100% + 10px); top: 50%; transform: translateY(-50%);
  background: var(--surface); color: var(--ink); border: 1px solid var(--border);
  box-shadow: var(--shadow-sm); border-radius: 8px; padding: 6px 10px;
  font-size: 12.5px; font-weight: 600; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity .15s;
}
.locate-btn:focus-visible::after { opacity: 1; }
@media (hover: hover) { .locate-btn:hover::after { opacity: 1; } }

/* Search box — a FLEX ITEM in .topbar (no longer absolute + translateX centering, which overlapped
   the logo/panels). It grows to fill the space between the brand and the reserved right corner
   (flex:1 1 auto), is capped so it doesn't sprawl on wide screens (max-width), and can shrink below
   its content on narrow screens (min-width:0). `margin:0 auto` centers it within whatever free space
   remains after the brand + reservations — so it stays optically centered on wide screens yet
   recenters inside the shrunk track when a side panel reserves margin (see panel-open rules below).
   Plain 44px field per the redesign (the old 1.3x zoom scaling is gone with the floating card). */
.search { flex: 1 1 auto; min-width: 0; margin: 0 auto; max-width: 520px; position: relative; }
.search .search-field input { height: 44px; font-size: 15px; }
/* the clear button centers on the INPUT (not the whole search wrapper, which grows with the
   results list) — hence the .search-field positioning context around just the input + button */
.search-field { position: relative; }
.search-field input { padding-right: 40px; }
.search-clear {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; border-radius: 50%; padding: 0;
  border: none; background: var(--surface-3); color: var(--ink-2); font-size: 9px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.search-clear:hover { background: var(--surface-2); color: var(--ink); }
.search input {
  width: 100%; padding: 11px 16px; border: 1px solid var(--border); border-radius: var(--r-pill);
  font-size: 14px; box-shadow: var(--shadow); background: var(--surface); color: var(--ink);
}
.search input:focus { border-color: var(--accent); box-shadow: var(--focus), var(--shadow); }
.search-results {
  list-style: none; margin: 6px 0 0; padding: 5px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-ctl); box-shadow: var(--shadow); max-height: 50vh; overflow-y: auto;
}
.search-results:empty { display: none; }
.search-results li { padding: 9px 11px; border-radius: 8px; font-size: 13px; cursor: pointer; color: var(--ink); }
.search-results li[data-pick]:hover, .search-results li[data-pick]:focus,
.search-results li[data-pick].active { background: var(--accent-soft); outline: none; }
.search-results li[data-pick].active { box-shadow: inset 2px 0 0 var(--accent); }
.search-results li.empty { color: var(--ink-3); cursor: default; }

/* Loading / empty / error banner */
.map-status {
  position: fixed; top: calc(env(safe-area-inset-top) + 76px); left: 50%; transform: translateX(-50%); z-index: var(--z-bar); /* just under the 64px bar */
  background: rgba(16, 24, 40, 0.92); color: #fff; padding: 9px 16px; border-radius: var(--r-pill);
  font-size: 13px; box-shadow: var(--shadow); max-width: calc(80vw / 1.25); text-align: center;
}
.map-status[hidden] { display: none; }

/* Cluster icon */
.odc-cluster {
  display: flex; align-items: center; justify-content: center; border-radius: 50%;
  background: var(--brand); color: #fff; font-weight: 700; font-size: 13px;
  border: 2px solid #ffffff; box-shadow: 0 1px 6px rgba(0, 0, 0, 0.45);
}
.odc-cluster:hover { filter: brightness(1.1); }
/* National-view AK / HI / CONUS totals — bolder than ordinary grid bubbles */
.odc-cluster.odc-region { background: var(--accent); font-size: 15px; font-weight: 800; }

/* A pin with an OPEN community proposal (pin move / detail change) awaiting confirmations —
   a gentle stroke pulse so contributors can spot spots that need a vote. */
/* a pin with an OPEN community proposal: SAME ring weight as every other pin (2px), just recolored
   violet with a gentle pulsing glow — distinct against light, detailed, AND satellite basemaps
   without being visually heavier than its neighbors. */
.odc-pending { stroke: #a855f7 !important; stroke-width: 2 !important; animation: odc-need-vote 1.6s ease-in-out infinite; }
@keyframes odc-need-vote {
  0%, 100% { filter: drop-shadow(0 0 0.5px rgba(168, 85, 247, 0.55)); }
  50% { filter: drop-shadow(0 0 4px rgba(168, 85, 247, 0.95)); }
}

/* Drag-pin tooltip */
.pin-tip { font-weight: 600; }

/* ===== PLACE-PANEL BODY (content blocks ported from the retired Leaflet popup) ===== */
.po-skel { font-size: var(--fs-body); color: var(--ink-2); padding: 10px 4px; min-width: 220px; }
.po-meta { display: flex; flex-direction: column; gap: 6px; }

.po-row { display: flex; gap: 8px; font-size: 16px; line-height: 1.45; color: var(--ink); }
.po-ic { flex: none; width: 18px; text-align: center; }
.po-links { display: flex; gap: 14px; font-size: 16px; }
.po-links a { color: var(--accent); text-decoration: none; font-weight: 600; }
.po-links a:hover { text-decoration: underline; }

/* Full-width hairline dividers sit ABOVE each major section (~16px breathing room: the body's 16px
   flex gap above the line + this 16px padding below it). Empty/absent sections collapse and take
   their divider with them. The photo section is first, so the header's border-bottom is its top
   boundary — no divider above it. */
.po-meta, .pp-status, .signals-area, .po-foot-wrap {
  border-top: 1px solid var(--border); padding-top: 16px;
}
/* Community-proposed changes: a single grouped section (field edits + pin moves). Collapses whole —
   divider and all — until one of its areas actually renders a proposal card (.po-fixes). */
.pp-changes { display: none; }
.pp-changes:has(.po-fixes) {
  display: flex; flex-direction: column; gap: 16px;
  border-top: 1px solid var(--border); padding-top: 16px;
}
.corrections-area:empty, .fieldedits-area:empty { display: none; }

/* Status + presence: one centered block — glowing status dot + label, tally, question, vote pair. */
.pp-status { text-align: center; }
.conf-head { display: inline-flex; align-items: center; justify-content: center; gap: 9px; }
.conf-dot {
  flex: none; width: 9px; height: 9px; border-radius: 50%; background: var(--dot, var(--medium));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--dot, var(--medium)) 22%, transparent);
}
.conf-label { font-size: 16px; font-weight: 700; color: var(--ink); }
.conf-tally { font-size: 13px; color: var(--ink-2); margin-top: 5px; }

.vote-q { font-size: 14px; color: var(--ink-2); margin: 18px 0 12px; font-weight: 400; }
.vote-btns { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.vote-btns .btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px; font-size: 15px; font-weight: 700; padding: 12px; border-radius: 11px; }
/* persistent semantic tints — touch users never see :hover (--cap-* text stays readable in light) */
.vote-btns .confirm { background: var(--high-soft); border-color: var(--high); color: var(--cap-high); }
.vote-btns .deny { background: var(--low-soft); border-color: var(--low); color: var(--cap-low); }
.vote-done { font-size: var(--fs-small); font-weight: 600; color: var(--cap-high); padding: 6px 0; text-align: center; } /* --cap-high ~4.9:1 on white (--high was 3.72:1); dark aliases cap-high→high */

/* community-proposed changes: each proposal is a centered card (field edits + pin moves share it) */
.po-fixes-t { font-size: 14px; font-weight: 700; color: var(--ink); margin: 0 0 12px; } /* margin-top:0 so the <h3> promotion adds no UA top-margin */
.po-fix { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-card); padding: 16px; margin: 0; text-align: center; }
.po-fix + .po-fix { margin-top: 10px; }
.fix-what { font-size: 15px; font-weight: 600; color: var(--ink); margin-bottom: 4px; }
.fix-note { font-size: 14px; font-style: italic; color: var(--ink); margin-bottom: 6px; }
.fix-view { width: 100%; margin-bottom: 10px; }
.fix-meter { font-size: 13px; color: var(--ink-2); margin-bottom: 14px; }
.fix-bar { height: 4px; border-radius: 2px; background: var(--surface-3); overflow: hidden; margin-bottom: 5px; }
.fix-bar span { display: block; height: 100%; background: var(--accent); border-radius: 2px; }
.fix-btns { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.fix-btns .btn { min-height: 44px; padding: 12px; font-size: 15px; font-weight: 700; border-radius: 11px; }
/* persistent tints: "looks right" reads positive at rest; "not right" is neutral (per the design) */
.fix-btns .confirm { background: var(--high-soft); border: 1px solid var(--high); color: var(--cap-high); } /* --cap-high text ~4.9:1 on the soft tint (--high was 3.30:1) */
.fix-btns .deny { background: var(--surface-3); border: 1px solid var(--border); color: var(--ink); }
.fix-btns .deny:hover, .fix-btns .deny:active { background: var(--surface-2); border-color: var(--border-2); color: var(--ink); }
.fix-priv { font-size: var(--fs-caption); color: var(--ink-3); margin: 10px 0 0; line-height: 1.5; }

/* community signals — a card of hairline-separated rows, then the full-width rate CTA */
.sig-title { font-size: 14px; font-weight: 700; color: var(--ink); margin-bottom: 12px; }
.sig-none { margin: 0; font-size: var(--fs-small); color: var(--ink-2); text-align: center; padding: 4px 0 12px; }
.sig-rows { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-card); padding: 0 16px; display: flex; flex-direction: column; }
.sig-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 15px; padding: 11px 0; }
.sig-row + .sig-row { border-top: 1px solid var(--border); }
.sig-label { color: var(--ink); display: inline-flex; align-items: center; gap: 11px; }
.sig-row .sig-ic { color: var(--ink-2); margin: 0; }
.sig-row .sig-ic svg { width: 18px; height: 18px; }
.sig-data { display: inline-flex; align-items: center; gap: 10px; color: var(--ink-2); }
.sig-empty { color: var(--ink-3); font-style: normal; }
.sig-val { color: var(--ink-2); font-weight: 600; font-size: 14px; }
.sig-n { min-width: 24px; height: 22px; display: inline-flex; align-items: center; justify-content: center; padding: 0 7px; background: var(--surface-3); color: var(--ink-2); border-radius: var(--r-pill); font-size: 12px; font-weight: 600; }
.sig-you { background: var(--accent-soft); color: var(--accent); border-radius: var(--r-pill); padding: 1px 8px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.sig-meter { display: inline-flex; gap: 4px; }
.seg-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--border-2); }
.seg-dot.on { background: var(--accent); }

.sig-rate-toggle { margin-top: 8px; }
.sig-rate { margin-top: 8px; display: flex; flex-direction: column; gap: 9px; }
/* The rate panel is collapsed via the `hidden` attribute until the toggle opens it.
   This class rule would otherwise out-cascade [hidden]'s display:none, so restore it. */
.sig-rate[hidden] { display: none; }
.rate-fields { border: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 9px; }
.rate-grp { display: flex; flex-direction: column; gap: 5px; }
.rate-l-row { display: flex; align-items: baseline; }
.rate-l { color: var(--ink-2); font-weight: 600; display: inline-flex; align-items: center; }
.rate-clear { margin-left: auto; background: none; border: none; color: var(--accent); font-size: 12px; font-weight: 600; cursor: pointer; padding: 2px 4px; }
.rate-clear:hover { text-decoration: underline; }
.seg button { padding: 10px 10px; min-height: 40px; font-size: var(--fs-small); }
.seg button.chosen { background: var(--accent-soft); color: var(--accent); box-shadow: inset 0 0 0 1.5px var(--accent); font-weight: 700; }
/* bins is now the same 4-across segmented control; tighten side padding so "Four+" fits */
.seg-4 button { padding-left: 6px; padding-right: 6px; }
/* Rate footer: optional Remove on its own centered row, then an equal-width Cancel | Save pair */
.rate-foot { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.rate-foot .rate-remove { align-self: center; }
.rate-foot-actions { display: flex; gap: 8px; }
.rate-foot-actions .btn { flex: 1; }

/* popover footer: quiet utility row, not button soup */
.po-foot { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.po-foot .btn { flex: 0 0 auto; }
.po-foot-l { font-size: var(--fs-caption); color: var(--ink-3); margin-right: auto; }
.ph-top img.ph-thumb { width: 100%; height: 158px; object-fit: cover; border-radius: 12px; cursor: pointer; display: block; }
.ph-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 12px; }
.ph-actions .btn { display: flex; align-items: center; justify-content: center; padding: 11px; border-radius: 11px; font-size: 14px; font-weight: 600; }

/* ============================ PIN SHEET (correction) ===================== */
.pin-sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 1600;
  display: flex; justify-content: center; padding: 0 12px 18px; pointer-events: none;
}
.sheet-card {
  pointer-events: auto; width: min(420px, 100%); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-card); box-shadow: var(--shadow);
  padding: 16px 18px; max-height: min(78vh, 620px); overflow-y: auto;
}
.sheet-card:focus-visible { box-shadow: var(--shadow); outline: none; }
.sheet-head { display: flex; align-items: center; margin-bottom: 4px; }
.sheet-head strong { font-size: var(--fs-h3); flex: 1; }
.sheet-x { background: none; border: none; cursor: pointer; color: var(--ink-3); }
.sheet-sub { margin: 0 0 6px; font-size: var(--fs-body); color: var(--ink-2); }
.sheet-tier { margin: 0 0 10px; font-size: var(--fs-small); color: var(--ink); background: var(--accent-soft); border-radius: var(--r-input); padding: 10px 12px; }
.sheet-card label { font-size: var(--fs-small); }
.sheet-card input, .sheet-card select, .sheet-card textarea { font-size: var(--fs-body); padding: 11px 13px; }
.sheet-card .btn { font-size: var(--fs-body); padding: 12px 16px; }
.sheet-card .seg button { font-size: var(--fs-small); padding: 9px 12px; }
.sheet-note { resize: vertical; min-height: 44px; font-family: inherit; }
.sheet-note-l { margin-top: 0; }
.opt { color: var(--ink-3); font-weight: 400; }
.sheet-gps { display: flex; align-items: flex-start; gap: 8px; margin: 12px 0 4px; font-size: var(--fs-body); color: var(--ink); font-weight: 500; cursor: pointer; }
.sheet-gps input { width: 20px; margin-top: 2px; }
.sheet-priv { margin: 0 0 4px; font-size: var(--fs-small); color: var(--ink-3); line-height: 1.5; }
.sheet-actions { display: flex; gap: 8px; margin-top: 10px; }
.sheet-actions .primary { flex: 1; }

/* snap-to-my-location buttons (drop-a-pin + correction sheets) */
.pin-snap, .corr-snap { width: 100%; margin-top: 8px; }

/* ---- detail-change ("edit details") sheet + popover rows ---- */
.fe-seg { display: flex; width: 100%; margin-bottom: 2px; }
.fe-input { margin-top: 2px; }
.fe-input .row { display: flex; gap: 8px; }
.fe-input .row > * { flex: 1; }
.fe-input .row > *:nth-child(2) { flex: 0 0 64px; }   /* state: just two letters */
.fe-org-new { margin-top: 6px; }

/* dirty-tab dot on the detail-change sheet (edits preserved across tabs) */
.fe-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); margin-left: 5px; vertical-align: 2px; }
.fe-hint { font-size: var(--fs-small); color: var(--ink-3); margin: 4px 0 0; }
.gps-status { display: block; font-size: var(--fs-small); margin: 2px 0 0 26px; color: var(--ink-2); }
.gps-status.ok { color: var(--high); }

/* inline form errors (submit panel) */
.field-err { margin: 3px 0 0; font-size: 12.5px; color: var(--low); }
input[aria-invalid="true"], select[aria-invalid="true"] { border-color: var(--low); box-shadow: 0 0 0 3px var(--low-soft); }
.pin-readout { font-size: 12.5px; color: var(--ink-2); margin: 6px 0 0; }

/* report reason chips */
.rep-label { font-size: 12.5px; margin: 6px 0 4px; }
.rep-label .opt { color: var(--ink-3); font-weight: 400; }
.rep-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip { border: 1px solid var(--border-2); background: var(--surface); color: var(--ink-2); border-radius: var(--r-pill); padding: 6px 12px; font-size: 12.5px; cursor: pointer; }
.chip[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

/* Turnstile arrival: caption + soft reflow so the challenge doesn't just pop in */
.ts { overflow: hidden; max-height: 0; transition: max-height .2s ease; }
.ts.ts-live { max-height: 120px; }
.ts.ts-live::before { content: "Quick security check — takes a moment"; display: block; font-size: var(--fs-caption); color: var(--ink-2); margin-bottom: 4px; }

/* report reasons are textareas now — user asked for room to explain */
.po-report-reason, .ph-report-reason { resize: vertical; min-height: 60px; font-family: inherit; font-size: var(--fs-body); }

/* token remaps for popover stragglers */
.rate-l, .po-report-hint { font-size: var(--fs-small); }
.sig-n { font-size: 12px; padding: 0 7px; min-width: 24px; }

/* icon slots (SVG icon language — no emoji) */
.po-ic { flex: none; width: 18px; display: inline-flex; justify-content: center; color: var(--ink-3); }
.sig-ic { color: var(--ink-3); vertical-align: -2px; margin-right: 3px; flex: none; }

/* ============================ SUBMIT PANEL ============================== */
/* z ladder: chrome 1000-1100 < search 1200 < list 1300 < submit panel 1400 < pin sheet 1600
   < photo modal 2000 < toast 2200 */
.panel {
  position: absolute; right: 16px; bottom: 80px; z-index: 1400; width: 340px;
  max-width: calc((100vw - 32px) / 1.25);
  max-height: calc((100dvh - 110px) / 1.25); overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-card); box-shadow: var(--shadow); padding: 16px 18px;
}
.panel.hidden { display: none; }
.panel h2 { margin: 0 0 12px; font-size: 18px; color: var(--ink); }
.panel .mode-seg { display: flex; width: 100%; }
.panel .row { display: flex; gap: 8px; }
.panel .row > * { flex: 1; }
.panel .pin-hint { font-size: 12.5px; color: var(--ink); background: var(--accent-soft); border-radius: var(--r-input); padding: 9px 11px; margin: 12px 0 2px; }
.panel .actions { display: flex; gap: 8px; margin-top: 14px; }
.panel .actions .primary { flex: 1; }

/* ============================ TOASTS ==================================== */
.toast-host { position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%); z-index: 2200; display: flex; flex-direction: column; gap: 8px; align-items: center; }
.toast { background: var(--toast-bg); color: #fff; padding: 11px 18px; border-radius: var(--r-pill); font-size: 13px; opacity: 0; transform: translateY(8px); transition: all .25s; box-shadow: var(--shadow); max-width: calc(90vw / 1.25); text-align: center; }
.toast.show { opacity: 1; transform: translateY(0); }
.toast.success { background: var(--toast-ok); }
.toast.error { background: var(--toast-err); }

/* ============================ PLACE PANEL =============================== */
/* The single location-details surface: desktop = 408px left dock (Google's own full-detail cap),
   mobile = two-snap bottom sheet. Non-modal overlay — the map div NEVER resizes (no
   invalidateSize churn); the camera offset-pans instead. Hidden panels leave the tab order via
   visibility riding the transition (same pattern as .list-panel). */
.place-panel {
  /* absolute, contained by the position:relative + overflow:hidden <body>: body is the panel's
     containing block, so it docks flush to the viewport edge AND its closed (translated 100%
     off-screen) box is CLIPPED by body rather than extending the document's scrollable width — the
     source of the horizontal scrollbar / phantom-sidebar dead space. (position:fixed instead
     anchors to the ICB, which the off-canvas box keeps expanding to viewport+panel — a feedback
     loop that mis-docks the panel to the wrong edge.) */
  position: absolute; z-index: 1350; background: var(--surface); color: var(--ink);
  display: flex; flex-direction: column;
  visibility: hidden; transition: transform .2s ease, visibility 0s linear .2s;
}
.place-panel.open { visibility: visible; transition: transform .2s ease; }
.pp-head { padding: 18px 18px 16px; border-bottom: 1px solid var(--border); background: var(--surface); flex: none; }
.pp-head-row { display: flex; align-items: flex-start; gap: 12px; }
.pp-title { margin: 0; font-size: 22px; font-weight: 700; line-height: 1.15; letter-spacing: -0.01em; flex: 1; outline: none; }
.pp-close {
  width: 36px; height: 36px; flex: none; display: inline-flex; align-items: center; justify-content: center;
  margin: -2px -6px 0 0; background: none; border: none; border-radius: 8px; color: var(--ink-2); cursor: pointer;
}
.pp-close:hover { color: var(--ink); background: var(--surface-2); }
.pp-body {
  flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  padding: 18px; display: flex; flex-direction: column; gap: 16px;
}
.pp-photos:empty { display: none; }
.pp-essentials { display: flex; flex-direction: column; gap: 10px; }
/* The owner's T-sketch: one column at the 408px dock / phone widths; details|community columns
   appear automatically wherever the panel is >=560px wide (tablet full-width sheet). */
.pp-grid { container-type: inline-size; }
.pp-grid { display: grid; gap: 12px; }
.pp-col { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
@container (min-width: 560px) {
  .pp-grid { grid-template-columns: 1fr 1fr; gap: 16px; }
}
/* selected-marker ring (SVG path stroke takes var() via CSS rules, unlike presentation attrs) */
.marker-selected { stroke: var(--accent) !important; stroke-width: 3.5 !important; }

/* desktop dock — RIGHT rail below the 64px top bar (list rail on the left; both can be open). */
@media (min-width: 1024px) {
  /* --pp-w: the rail's rendered width, reused by the tab so it slides EXACTLY the panel's distance. */
  .place-panel { --pp-w: min(400px, 40vw); }
  .place-panel {
    top: 64px; right: 0; bottom: 0; width: var(--pp-w);
    border-left: 1px solid var(--border); box-shadow: var(--shadow);
    transform: translateX(100%);
  }
  .place-panel.open { transform: translateX(0); }
  .place-panel.open.collapsed { transform: translateX(100%); visibility: hidden; transition: transform .2s ease, visibility 0s linear .2s; }
  .pp-grab { display: none; }
  /* The tab is a DOM CHILD of the panel, sitting just off its left edge (left:-28px). The panel's
     OWN transform slides the tab with it, so there is a SINGLE animation — the tab physically cannot
     desync from the dock. Collapsing (panel translateX(100%)) carries the 28px tab exactly to the
     screen edge, where it stays clickable: visibility:visible overrides the collapsed panel's
     visibility:hidden. No own transition — it inherits the panel's slide. */
  .pp-tab {
    position: absolute; top: 50%; left: -28px; z-index: 1349;
    width: 28px; height: 56px; border: 1px solid var(--border); border-right: none;
    border-radius: 10px 0 0 10px; background: var(--surface); color: var(--ink-2);
    font-size: 18px; cursor: pointer; box-shadow: var(--shadow-sm); padding: 0;
    transform: translateY(-50%);
    visibility: visible;
  }
  .pp-tab:hover { color: var(--ink); background: var(--surface-2); }
}

/* Mobile (<1024px): ONE bottom sheet, two modes. Both surfaces are sheet-styled and mutually
   exclusive — the list sheet is always resting (peek ≈ 13dvh); opening a place hides it and the
   place sheet takes over at the same dock. Heights are driven by js/sheet.js (3 snaps:
   peek / half 55dvh / full 92dvh); CSS only supplies the shell. */
@media (max-width: 1023px) {
  .place-panel, .list-panel {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; width: auto;
    height: max(92px, 13dvh);
    border: 1px solid var(--border); border-bottom: none; border-radius: 22px 22px 0 0;
    box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.35);
    transform: none; padding-bottom: env(safe-area-inset-bottom);
  }
  .place-panel.dragging, .list-panel.dragging { transition: none; }
  /* .open beats the base visibility:hidden regardless of file order (the base rule sits later) */
  .list-panel.open { visibility: visible; }
  /* one sheet at a time: an open place panel supersedes the resting list sheet */
  body:has(.place-panel.open) .list-panel { visibility: hidden; }
  /* drag handles (pointer wiring in sheet.js); the pp-head also drags the place sheet */
  .pp-head { touch-action: none; }
  .pp-grab, .list-grab {
    display: block; flex: none; width: 100%; height: 30px;
    background: none; border: none; cursor: grab; padding: 0; touch-action: none;
  }
  .pp-grab::before, .list-grab::before {
    content: ""; display: block; width: 44px; height: 5px; border-radius: 3px;
    background: var(--border-2); margin: 12px auto 0;
  }
  .pp-tab, .list-tab { display: none; }
  /* FABs ride above the resting sheet; the zoom pill yields to pinch on touch layouts */
  .zoom-ctl { display: none; }
  .map-ctl-br { bottom: calc(env(safe-area-inset-bottom) + max(92px, 13dvh) + 12px); }
  /* (legend/theme/layers live in the top-right menu rail on all sizes now — no bottom-left FABs) */
  /* attribution pill sits just above the resting sheet */
  .leaflet-bottom.leaflet-left { bottom: calc(max(92px, 13dvh) + 8px); }
  /* welcome greeting is a centered overlay at all sizes now — nothing sheet-specific to do */
}

/* ===== T26: overlay-panel chrome behavior ===== */
/* Collapse-tab chevron: a single glyph that ROTATES 180° in sync with the .2s panel slide, instead
   of the instant ‹/› text swap that teleported while the sidebar animated. Only the glyph rotates
   (an inner span) — rotating the whole tab would flip its rounded edge away from the map. */
.pp-chev { display: inline-block; transition: transform .2s ease; }
.pp-tab.is-collapsed .pp-chev { transform: rotate(180deg); }

/* Desktop chrome yields to the rails: the bottom-right stack shifts left of the open detail rail,
   and the attribution pill shifts right of the open list rail. The top bar spans full width ABOVE
   both rails (they start at top:64), so it never needs to reserve anything. */
@media (min-width: 1024px) {
  .map-ctl-br { transition: right .2s ease; }
  body:has(.place-panel.open:not(.collapsed)) .map-ctl-br { right: calc(min(400px, 40vw) + 16px); }
  .leaflet-bottom.leaflet-left { transition: left .2s ease; }
  body:has(.list-panel.open:not(.collapsed)) .leaflet-bottom.leaflet-left { left: 340px; }
}

/* ============================ LIST PANEL ================================ */
.list-close { margin-left: 4px; background: none; border: none; font-size: 16px; cursor: pointer; color: var(--ink-2); padding: 2px 6px; border-radius: 6px; }
.list-close:hover { color: var(--ink); background: var(--surface-3); }
.list-panel {
  position: fixed; z-index: var(--z-rail); background: var(--surface);
  display: flex; flex-direction: column; visibility: hidden;
}

/* list edge tab — mirror of .pp-tab, on the list's RIGHT edge (desktop only); chevron rotates in
   sync with the slide, same pattern as the detail panel's tab. */
.list-chev { display: inline-block; transition: transform .2s ease; }
.list-tab.is-collapsed .list-chev { transform: rotate(180deg); }
@media (min-width: 1024px) {
  /* Left rail: below the 64px bar, 340px wide (redesign), slides out behind the child tab. */
  .list-panel {
    top: 64px; left: 0; bottom: 0; width: 340px;
    box-shadow: var(--shadow); border-right: 1px solid var(--border);
    transform: translateX(-100%);
  }
  /* visibility rides the transition so the COLLAPSED rail's controls leave the tab order */
  .list-panel { transition: transform .2s ease, visibility 0s linear .2s; }
  .list-panel.open { transform: translateX(0); visibility: visible; transition: transform 0.2s ease; }
  .list-panel.open.collapsed { transform: translateX(-100%); visibility: hidden; transition: transform .2s ease, visibility 0s linear .2s; }
  /* Child of the drawer (same technique as .pp-tab): the drawer's own transform slides the tab in
     lockstep — one animation, no desync. Sits just off the drawer's right edge (right:-28px);
     collapsing (drawer translateX(-100%)) carries it to the screen's left edge. */
  .list-tab {
    position: absolute; top: 50%; right: -28px; z-index: 1299;
    width: 28px; height: 56px; border: 1px solid var(--border); border-left: none;
    border-radius: 0 10px 10px 0; background: var(--surface); color: var(--ink-2);
    font-size: 18px; cursor: pointer; box-shadow: var(--shadow-sm); padding: 0;
    transform: translateY(-50%);
    visibility: visible;
  }
  .list-tab:hover { color: var(--ink); background: var(--surface-2); }
}
.list-head { display: flex; align-items: center; gap: 8px; padding: 12px; border-bottom: 1px solid var(--border); }
.list-head label { margin: 0; font-size: 16px; color: var(--ink-2); }
.list-head select { flex: 1; font-size: 16px; }
.list-count { font-size: 14px; color: var(--ink-3); white-space: nowrap; }
.list-results { list-style: none; margin: 0; padding: 6px; overflow-y: auto; flex: 1; }
.list-empty { padding: 16px; color: var(--ink-3); font-size: 16px; text-align: center; }
.list-item {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: none; border: none; border-radius: 8px; padding: 11px 12px; cursor: pointer; font-size: 16px; color: var(--ink);
}
/* split hover from focus so mouse clicks don't leave a permanent outline on rows; hover:hover only
   so a tapped row doesn't stay tinted on touch */
@media (hover: hover) { .list-item:hover { background: var(--accent-soft); } }
.list-item:focus-visible { background: var(--accent-soft); outline: 2px solid var(--accent); outline-offset: -2px; }
.li-name { font-weight: 600; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.li-type { color: var(--ink-3); font-size: 13.5px; white-space: nowrap; }

/* ============================ PHOTO MODAL ============================== */
.photo-modal { position: absolute; inset: 0; z-index: 2000; display: none; align-items: center; justify-content: center; background: rgba(0, 0, 0, .5); }
.photo-modal.open { display: flex; }
.photo-card { background: var(--surface); color: var(--ink); border: 1px solid var(--border); border-radius: var(--r-card); box-shadow: var(--shadow); width: min(560px, 92vw); max-height: 88vh; overflow: auto; padding: 16px; }
.modal-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.modal-head strong { font-size: 16px; flex: 1; }
.modal-close { background: none; border: none; font-size: 18px; cursor: pointer; color: var(--ink-2); }
.modal-close:hover { color: var(--ink); }
.ph-low { font-size: var(--fs-caption); color: var(--ink-2); display: flex; align-items: center; gap: 6px; margin: 0; font-weight: 500; }
.ph-low input { width: 20px; }
.ph-low-sub { color: var(--ink-3); font-weight: 400; }
.ph-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
/* single modal-level Turnstile host: the 300px widget can never fit a 150px grid cell */
.ph-modal-ts.ts-live { position: sticky; bottom: 0; margin-top: 10px; padding: 8px 0; background: var(--surface); border-top: 1px solid var(--border); }
.ph-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 28px 16px; grid-column: 1 / -1; }
.ph-empty p { margin: 0; font-size: var(--fs-small); color: var(--ink); }
.ph-empty-sub { font-size: 12.5px !important; color: var(--ink-3) !important; }
.ph-card-item { border: 1px solid var(--border); border-radius: var(--r-input); overflow: hidden; background: var(--surface-2); }
.ph-card-item img { width: 100%; height: 110px; object-fit: cover; display: block; }
.ph-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 6px 8px; font-size: 11.5px; color: var(--ink); }
.ph-badge { background: var(--accent-soft); color: var(--accent); border-radius: var(--r-pill); padding: 1px 8px; font-size: 12px; font-weight: 600; }
.ph-badge.low { background: var(--low-soft); color: var(--cap-low); } /* light theme ~5:1 (--low was 4.23:1); dark override below unchanged */
:root[data-theme="dark"] .ph-badge { color: var(--accent-hover); }
:root[data-theme="dark"] .ph-badge.low { color: #f4948a; }
/* "You: …" and selected report chips: lift text off --accent-soft in dark theme (was ~4.07:1). */
:root[data-theme="dark"] .sig-you,
:root[data-theme="dark"] .chip[aria-pressed="true"] { color: var(--accent-hover); }
.ph-votes { display: flex; align-items: center; gap: 6px; padding: 6px 8px 8px; }
.ph-votes .btn { min-height: 40px; flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
.ph-votes .btn[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.linklike { background: none; border: none; padding: 6px 4px; font-size: 12px; color: var(--ink-3); cursor: pointer; margin-left: auto; }
.linklike:hover { color: var(--low); text-decoration: underline; }
/* upload drop zone + preview */
.ph-drop { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 22px 12px; border: 2px dashed var(--border-2); border-radius: var(--r-input); color: var(--ink-2); cursor: pointer; text-align: center; }
.ph-drop:hover, .ph-drop:focus-within { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.ph-drop-t { font-weight: 600; font-size: var(--fs-small); color: var(--ink); }
.ph-drop-s { font-size: 12px; color: var(--ink-3); }
.ph-preview img { width: 100%; max-height: 180px; object-fit: cover; border-radius: var(--r-input); }
.ph-preview-name { font-size: 12px; color: var(--ink-2); }
.ph-rule { margin: 8px 0 0; font-size: var(--fs-caption); font-weight: 600; color: var(--ink); }
.ph-hint { font-size: 12.5px; color: var(--ink-2); background: var(--surface-2); border-radius: var(--r-input); padding: 9px 11px; }
/* gallery entry points */
.ph-thumb-btn { position: relative; display: block; width: 100%; padding: 0; border: 0; background: none; cursor: pointer; border-radius: var(--r-input); overflow: hidden; }
.ph-count { position: absolute; right: 8px; bottom: 8px; font-size: var(--fs-caption); font-weight: 600; color: #fff; background: rgba(0, 0, 0, 0.6); border-radius: var(--r-pill); padding: 2px 9px; }
.modal-actions { display: flex; gap: 8px; margin-top: 12px; }
.modal-actions .primary { flex: 1; }
img.ph-broken { object-fit: contain; background: var(--surface-2); }

/* ---- Picture-of-the-day placeholder (shown when a place has no community photos) ---- */
.potd-placeholder { margin-top: 0; }
.potd-placeholder a { display: block; border-radius: 12px; overflow: hidden; }
.potd-placeholder img {
  width: 100%; height: 158px; object-fit: cover; display: block;
  background: var(--surface-2); border: 1px solid var(--border);
}
/* Photo-of-the-day caption inside the detail panel: match the design's 12px muted centered line
   (the shared .potd-credit stays 11px in the welcome hero). */
.pp-photos .potd-credit { font-size: 12px; margin-top: 8px; }
/* Shared credit line (also used inside the welcome hero). Small + muted, license/source linked. */
.potd-credit {
  margin: 6px 0 0; font-size: 11px; line-height: 1.45; color: var(--ink-3);
  text-align: center; text-wrap: pretty; /* center + avoid an orphaned "Wikimedia Commons" line */
}
.potd-credit a { color: var(--ink-2); text-decoration: none; }
.potd-credit a:hover { color: var(--accent); text-decoration: underline; }

/* Collapsible map attribution: the required ODbL + source credits collapse to ONE compact line so
   they don't eat a strip of the map (worst on mobile, where they wrapped to ~5 lines); tapping the
   strip (not a credit link) toggles the full text. ODbL only requires attribution be visible/
   accessible — this keeps it so, just out of the way. */
/* Attribution: a compact themed pill, bottom-left (Leaflet corner), collapsed to one line —
   tap to expand the full ODbL + source credits (required attribution stays accessible). */
.leaflet-control-attribution {
  background: color-mix(in srgb, var(--surface) 72%, transparent) !important;
  color: var(--ink-3); border-radius: 6px; padding: 3px 8px; font-size: 11px;
}
.leaflet-control-attribution.attr-collapsible {
  max-width: 52vw; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer;
}
.leaflet-control-attribution.attr-collapsible.attr-open {
  max-width: min(94vw, 680px); white-space: normal; cursor: default;
}
@media (max-width: 1023px) {
  .leaflet-control-attribution.attr-collapsible { max-width: 62vw; font-size: 10px; }
}

/* ============================ WELCOME HERO ============================= */
/* First-visit greeting: a CENTERED card on a light scrim, dismissible every way (✕ / Got it /
   Escape / scrim click), shown once. Full-aspect POTD image (not cropped); centered copy. */
.welcome-overlay {
  position: fixed; inset: 0; z-index: 1600; display: flex; align-items: center; justify-content: center;
  padding: 20px; background: rgba(8, 12, 18, 0.5);
  backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
  animation: welcome-fade 0.2s ease-out;
}
.welcome-hero {
  position: relative; width: min(400px, 100%); max-height: calc(100dvh - 40px); overflow: auto;
  background: var(--surface); color: var(--ink); text-align: center;
  border: 1px solid var(--border); border-radius: var(--r-card); box-shadow: var(--shadow);
  animation: welcome-in 0.22s cubic-bezier(.2, .7, .3, 1);
}
.welcome-x {
  position: absolute; top: 8px; right: 8px; z-index: 1;
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: none; border-radius: 8px; cursor: pointer;
  font-size: 16px; line-height: 1; color: #fff; background: rgba(0, 0, 0, 0.45);
}
.welcome-x:hover { background: rgba(0, 0, 0, 0.65); }
.welcome-x:focus-visible { box-shadow: var(--focus); outline: none; }
/* Full aspect ratio: letterboxed within a height cap, centered — never cropped to a banner. */
.welcome-media { display: block; padding: 16px 16px 0; }
.welcome-media img {
  display: block; max-width: 100%; max-height: 46vh; width: auto; height: auto; margin: 0 auto;
  border-radius: var(--r-input); background: var(--surface-2);
}
.welcome-body { padding: 14px 20px 20px; }
.welcome-title { margin: 0 0 8px; font-size: 22px; font-weight: 700; color: var(--brand); text-align: center; }
.welcome-blurb { margin: 0 auto 12px; font-size: var(--fs-small); line-height: 1.55; color: var(--ink); max-width: 40ch; text-align: center; }
.welcome-body .potd-credit { margin: 0 0 10px; }
.welcome-author { margin: 0 0 14px; font-size: var(--fs-small); color: var(--ink-2); }
.welcome-author a { color: var(--accent); font-weight: 600; text-decoration: none; }
.welcome-author a:hover { text-decoration: underline; }
.welcome-ok { width: 100%; }
@keyframes welcome-in { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes welcome-fade { from { opacity: 0; } to { opacity: 1; } }

/* ---- report (location + photo) ---- */
.btn.danger { color: var(--low); border-color: var(--low-soft); }
.btn.danger:hover { background: var(--low-soft); border-color: var(--low); }
.po-report:not(:empty) { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.po-report-hint { font-size: 12px; color: var(--ink-2); }
.po-report-reason, .ph-report-reason {
  /* 16px (var(--fs-body)), not 13px: this textarea lives in the never-zoomed place/photo panels, so
     a smaller size would trip iOS Safari's focus-zoom. (An earlier rule already sets --fs-body; this
     duplicate used to silently override it back down to 13px.) */
  width: 100%; box-sizing: border-box; padding: 7px 9px; font-size: var(--fs-body);
  border: 1px solid var(--border-2); border-radius: var(--r-input);
  background: var(--surface); color: var(--ink);
}
.po-report-actions, .ph-report-actions { display: flex; gap: 6px; }
.po-report-actions .btn, .ph-report-actions .btn { flex: 1; }
.ph-report:not(:empty) { display: flex; flex-direction: column; gap: 6px; padding: 0 8px 8px; }

/* ============================ BASEMAP SWITCHER ========================= */
/* Restyle Leaflet's default top-right layers control into a tidy titled card with
   segmented-looking radio rows. The native control keeps doing the work (radios,
   baselayerchange); this is purely cosmetic. */
/* (the old in-map Leaflet layers card was replaced by the top-bar layers popover) */

/* ============================ LEAFLET DARK CHROME ====================== */
:root[data-theme="dark"] .leaflet-bar a { border-bottom-color: var(--border); }
:root[data-theme="dark"] .leaflet-bar a:hover { background: var(--surface-2); }
:root[data-theme="dark"] .leaflet-control-layers-toggle { filter: invert(0.9) hue-rotate(180deg); }
:root[data-theme="dark"] .leaflet-control-layers-expanded { color: var(--ink); }
:root[data-theme="dark"] .leaflet-control-attribution {
  background: rgba(23, 36, 42, 0.85) !important; color: var(--ink-2) !important;
}
:root[data-theme="dark"] .leaflet-control-attribution a { color: var(--accent); }
/* Leaflet tooltips (pin-drag label, "You are here") default to a white bubble — theme it for dark,
   including the directional CSS-triangle callout arrows so a white arrow doesn't point off the bubble. */
:root[data-theme="dark"] .leaflet-tooltip {
  background: var(--surface); color: var(--ink); border-color: var(--border); box-shadow: var(--shadow-sm);
}
:root[data-theme="dark"] .leaflet-tooltip-top::before { border-top-color: var(--surface); }
:root[data-theme="dark"] .leaflet-tooltip-bottom::before { border-bottom-color: var(--surface); }
:root[data-theme="dark"] .leaflet-tooltip-left::before { border-left-color: var(--surface); }
:root[data-theme="dark"] .leaflet-tooltip-right::before { border-right-color: var(--surface); }
/* Lift the basemap a touch so the dark UI doesn't sit on stark white tiles.
   EXEMPT imagery basemaps (Satellite/Hybrid): they're already dark, and a whole-pane filter
   forces the GPU to recomposite every tile layer on every pan frame — with Hybrid's three
   stacked layers that read as serious lag. */
:root[data-theme="dark"] .leaflet-tile-pane { filter: brightness(0.92) contrast(0.96); }
:root[data-theme="dark"] .satellite-active .leaflet-tile-pane { filter: none; }

/* Stronger card separation over dark satellite imagery (both themes) */
.satellite-active .legend,
.satellite-active .search input,
.satellite-active .panel { box-shadow: 0 2px 18px rgba(0, 0, 0, 0.55); }

/* ============================ DETAIL-PANEL + CHROME REFINEMENTS ========= */
/* zoom buttons in the bottom-right stack (above the locate button) */
.zoom-ctl { display: flex; flex-direction: column; border-radius: 12px; overflow: hidden; border: 1px solid var(--border); box-shadow: var(--shadow); }
.zoom-btn { width: 46px; height: 46px; border: none; background: var(--surface); color: var(--ink); font-size: 22px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }
.zoom-btn:hover { background: var(--surface-2); }
.zoom-btn + .zoom-btn { border-top: 1px solid var(--border); }

/* header identity row: a 36px blue chip (also the Directions link) beside the address + category
   lines, matching the redesign's header block. */
.pp-identity { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
.pp-idtext { min-width: 0; }
.pp-dir-btn { flex: none; width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-input); background: var(--accent); color: var(--on-accent); border: none; cursor: pointer; text-decoration: none; }
.pp-dir-btn:hover { background: var(--accent-hover); }
.pp-addr { font-size: 14px; color: var(--ink); line-height: 1.35; }
.pp-sub { font-size: 13px; color: var(--ink-3); margin-top: 2px; }

/* centered section headers + proposal-card contents inside the detail panel */
.po-fixes-t, .vote-q, .sig-title { text-align: center; }
/* The proposals header carries an inline "?" help tip. Flex-center it so the round tip sits
   vertically centered against the header TEXT (not riding low on the baseline) — a structural
   alignment instead of a magic vertical-align offset. */
.po-fixes-t { display: flex; align-items: center; justify-content: center; }
.fix-what, .po-fix .fix-note, .po-fix .fix-meter { text-align: center; }
.po-fix .fix-bar { margin-left: auto; margin-right: auto; max-width: 180px; }

/* faded "?" help tooltip (themed, multi-line, keyboard-reachable) */
/* flex-centered glyph: align-items/justify-content center the "?" in the circle; the parent
   .po-fixes-t (also flex) centers this circle against the header text — no vertical-align hack. */
.help-tip { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 20px; height: 20px; border-radius: 50%; background: var(--surface-3); color: var(--ink-2); font-size: 12px; line-height: 1; font-weight: 700; cursor: help; position: relative; margin-right: 9px; }
/* Bubble opens DOWN-RIGHT, anchored to the tip's left edge. The tip now sits left of the centered
   header, so a left-anchored bubble stays inside the panel (the old center-anchored bubble spilled
   past the right edge and gave the sidebar a horizontal scrollbar). Opening downward avoids clipping
   against the panel header above. .pp-body also clips overflow-x as a belt-and-suspenders. */
.help-tip::after {
  content: attr(data-tip); position: absolute; top: calc(100% + 8px); left: 0; transform: none;
  width: 230px; max-width: 72vw; background: var(--surface); color: var(--ink); border: 1px solid var(--border);
  box-shadow: var(--shadow); border-radius: 8px; padding: 9px 11px; font-size: 12.5px; font-weight: 400;
  line-height: 1.5; text-align: left; opacity: 0; pointer-events: none; transition: opacity .15s; z-index: 10;
}
.help-tip:hover::after, .help-tip:focus::after, .help-tip:focus-visible::after { opacity: 1; } /* :focus too — a touch tap sets :focus but not :focus-visible */

/* Rate CTA — blue primary, full width, stars flanking the text */
.sig-rate-toggle.rate-cta { width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 10px; margin-top: 12px; padding: 14px; border-radius: 12px; font-size: 15px; font-weight: 700; }

/* "Something wrong?" — its own centered muted caption, three text actions spanning the row */
.po-foot-h { text-align: center; font-size: 13px; font-weight: 400; color: var(--ink-3); margin: 0 0 12px; }
.po-foot { display: flex; gap: 8px; }
.po-foot .btn { flex: 1; }
.po-foot .btn.tiny { font-size: 14px; padding: 8px 4px; }

/* themed file "Browse" button in the add-photo modal */
.ph-file { font-size: var(--fs-small); color: var(--ink-2); }
.ph-file::file-selector-button { font: inherit; padding: 8px 14px; margin-right: 10px; border-radius: var(--r-input); border: 1px solid var(--border); background: var(--surface-2); color: var(--ink); font-weight: 600; cursor: pointer; }
.ph-file::file-selector-button:hover { background: var(--surface-3); }

/* list dropdown: custom chevron with a right-inset matching the option left-inset (12px) */
.list-head select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23808a92' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; padding-right: 34px;
}

/* ============================ MOTION =================================== */
@media (prefers-reduced-motion: reduce) {
  .toast { transition: opacity 0.01ms; }
  .list-panel, .list-tab, .list-chev { transition: none; }
  .place-panel, .pp-tab, .pp-chev { transition: none; }
  .topbar, .map-ctl-br { transition: none; }
  .ts { transition: none; }
  .btn:active { transform: none; }
  .locate-btn, .locate-btn:hover { transform: none; }
  .locate-btn.busy svg { animation: odc-pulse 1s ease-in-out infinite; }
  .odc-pending { animation: none; stroke: #a855f7 !important; stroke-width: 2 !important; filter: drop-shadow(0 0 3px rgba(168, 85, 247, 0.9)); }
  .welcome-hero, .welcome-overlay, .menu-rail, .map-pop { animation: none; }
  html { scroll-behavior: auto; }
}

/* ============================ LEGEND LINKS ============================= */
.legend-links {
  margin-top: 7px; padding-top: 7px; border-top: 1px solid var(--border);
  font-size: 11px; color: var(--ink-3); line-height: 1.5;
}
.legend-links a { color: var(--ink-2); text-decoration: none; }
.legend-links a:hover { color: var(--accent); text-decoration: underline; }

/* The "you agree to…" consent line under submit / upload actions */
.consent-line {
  margin: 8px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--ink-2);
}
.consent-line a { color: var(--accent); text-decoration: none; }
.consent-line a:hover { text-decoration: underline; }

/* ============================ LEGAL PAGES ============================= */
.legal-body {
  margin: 0; background: var(--surface-2); color: var(--ink);
  font: 16px/1.65 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
.legal {
  max-width: 760px; margin: 0 auto; padding: 40px 22px 72px;
  background: var(--surface);
  border-left: 1px solid var(--border); border-right: 1px solid var(--border);
  min-height: 100vh;
}
.legal h1 { font-size: 28px; line-height: 1.2; margin: 6px 0 4px; color: var(--brand); }
.legal h2 { font-size: 18px; margin: 30px 0 8px; color: var(--ink); }
.legal p, .legal li { color: var(--ink); }
.legal a { color: var(--accent); }
.legal ul { padding-left: 22px; }
.legal li { margin: 6px 0; }
.legal code {
  background: var(--surface-3); padding: 1px 5px; border-radius: 5px;
  font-size: 0.9em; font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
.legal-top { margin: 0 0 18px; }
.legal-top a, .legal-foot a { color: var(--accent); text-decoration: none; }
.legal-top a:hover, .legal-foot a:hover { text-decoration: underline; }
.legal-meta { color: var(--ink-3); font-size: 13.5px; margin: 0 0 18px; }
.legal-foot {
  margin-top: 40px; padding-top: 18px; border-top: 1px solid var(--border);
  font-size: 14px; color: var(--ink-3);
}

/* ============================ MOBILE COMPAT ============================ */
/* Deliberately LAST in the file. Each rule below overrides a component base rule defined earlier at
   equal specificity, so the cascade resolves them by source order — putting them here is what makes
   them win, without resorting to !important or id-inflated selectors. */

/* iOS Safari auto-zooms the page when a focused field is < 16px. The search input is 14px and its
   wrapper is un-zoomed at <=900px, so bump the input itself to 16px there. */
@media (max-width: 900px) {
  .search input { font-size: 16px; }
}

@media (max-width: 480px) {
  /* Widen the Add panel so the 300px-min Turnstile widget fits (base rule divides the width by the
     1.25 chrome zoom, leaving ~274px content on a 375px phone), and switch to dvh so iOS Safari's
     dynamic toolbar can't clip the form's heading. */
  .panel { max-width: calc(100vw - 20px); max-height: calc(100dvh - 110px); padding: 14px; }
}

/* search-clear ✕ is a 16px visual inside the input; give touch users a >=24px hit target (WCAG
   2.5.8). Must follow the base .search-clear rule to win, hence its home here. */
@media (pointer: coarse) {
  .search-clear { width: 28px; height: 28px; font-size: 12px; }
}
