/* ==========================================================================
   ForecloseVA - Virginia Foreclosure Intelligence
   "Obsidian Cartographer" design system
   Tokens per DESIGN_SPEC.md section 1
   ========================================================================== */

:root {
  /* Surfaces & ink (dark is the base theme) */
  --void: #080b11;
  --basemap-ink: #12151a; /* tracks --map-land (pin strokes sit on land) */
  --surface-1: #111a27;
  --surface-2: #182434;
  --surface-3: #22303f;
  --line: #26374a;
  --line-strong: #33495f;
  --text-primary: #eaf1f8;
  --text-secondary: #a2b4c6;
  --text-muted: #8296ab; /* >=4.5:1 on surface-2; the old #647a8f failed WCAG AA */
  --scrim: rgba(8, 11, 17, 0.72);
  --glass: rgba(17, 26, 39, 0.88);
  --glass-border: rgba(255, 255, 255, 0.06);

  /* Basemap recolor palette (map.js reads these, so the map flips with the
     theme). Natural dark cartography: water reads unmistakably as water,
     parks keep a hint of green, roads keep their hierarchy (Tim review
     2026-08-20 - the old flat single-navy recolor made waterways invisible). */
  /* Art-director round 2 (2026-08-20): the lever is CHROMA, not luminance.
     Land is neutralized (charcoal, not navy) so water's blue reads as a
     different substance; roads go neutral grey so infrastructure stops
     outranking water; waterway lines carry ~3.1:1 so statewide rivers show. */
  --map-land: #12151a;         /* neutral charcoal - de-tinted from navy */
  --map-land-2: #171a20;       /* residential/landuse - one step lifted */
  --map-park: #13251a;         /* parks, reserves, vegetation - green cast */
  --map-water: #1c4460;        /* real blue: 1.78:1 vs land + 60-unit chroma gap */
  --map-waterway: #2d6a94;     /* river/stream lines - 3.14:1 (1px needs ~2x a fill) */
  --map-road-minor: #24272c;   /* service/minor/secondary + rail (neutral) */
  --map-road-major: #3b3f45;   /* mot/trunk/pri fills - just below water's salience */
  --map-road-case: #0b0d10;
  --map-building: #1a1d23;
  --map-admin-state: #4a5568;  /* state boundaries stay readable */
  --map-admin-county: #262a31; /* county lines drop to a whisper at city zoom */
  --map-label: #909aa4;
  --map-label-water: #95b3ce;  /* 4.7:1 on the new water fill */
  --map-label-halo: #12151a;

  /* Accent (chrome only). --accent = text/icon/border accent;
     --accent-fill = solid button surfaces. Dark uses one gold for both;
     light splits them so filled CTAs stay jewel, not mud. */
  --accent: #e0b44c;
  --accent-hover: #eec463;
  --accent-press: #c99e3a;
  --accent-tint: rgba(224, 180, 76, 0.14);
  --accent-ink: #1a1400;
  --accent-fill: #e0b44c;
  --accent-fill-hover: #eec463;
  --accent-fill-ink: #1a1400;

  /* Urgency ramp (map only) - urg-1 kept well clear of the gold accent so
     "soon" pins never read as chrome */
  --urg-0: #ff5d4b;
  --urg-1: #ff8324;
  --urg-2: #4aa3e0;
  --urg-3: #6d8299;
  --urg-p: #4d5a6e; /* past-dated - dimmer than distant, distinct from cancelled */
  --urg-x: #556072;

  /* Status */
  --status-active: #3ddc84;
  --status-active-bg: rgba(61, 220, 132, 0.12);
  --status-cancelled: #ff6a5a;
  --status-cancelled-bg: rgba(255, 106, 90, 0.12);

  /* Typography */
  --font-display: 'Space Grotesk', ui-sans-serif, system-ui, sans-serif;
  --font-body: 'Inter', ui-sans-serif, system-ui, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', monospace;

  --fs-2xs: 0.6875rem;
  --fs-xs: 0.8rem;
  --fs-sm: 0.875rem;
  --fs-base: 1rem;
  --fs-md: 1.25rem;
  --fs-lg: 1.5625rem;
  --fs-xl: 1.95rem;

  /* Spacing */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;
  --sp-8: 64px;

  /* Radius */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-pill: 999px;

  /* Shadow / elevation */
  --e-1: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 2px 8px rgba(4, 8, 14, 0.4);
  --e-2: 0 8px 28px rgba(4, 8, 14, 0.55), 0 1px 0 rgba(255, 255, 255, 0.04) inset;
  --e-3: 0 20px 60px rgba(4, 8, 14, 0.7), 0 1px 0 rgba(255, 255, 255, 0.05) inset;

  /* Z-index */
  --z-map: 0;
  --z-map-controls: 10;
  --z-rail: 20;
  --z-statstrip: 20;
  --z-detail: 30;
  --z-header: 40;
  --z-dropdown: 50;
  --z-scrim: 55;
  --z-sheet: 60;
  --z-toast: 70;

  /* Motion */
  --ease-standard: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 120ms;
  --dur-base: 200ms;
  --dur-panel: 260ms;
}

/* ==========================================================================
   Light theme - warm paper surfaces, same accent family darkened for
   contrast on light ground. Applied via data-theme="light" on <html>.
   ========================================================================== */

:root[data-theme="light"] {
  --void: #eceae4;
  --basemap-ink: #f2f0ea;
  --surface-1: #faf9f5;
  --surface-2: #f1efe9;
  --surface-3: #e6e3da;
  --line: #d8d4c8;
  --line-strong: #b9b4a4;
  --text-primary: #1d232b;
  --text-secondary: #4a545f;
  --text-muted: #576470;
  --scrim: rgba(29, 35, 43, 0.4);
  --glass: rgba(250, 249, 245, 0.9);
  --glass-border: rgba(29, 35, 43, 0.08);

  --map-land: #f2f0ea;
  --map-land-2: #edeae2;
  --map-park: #e3e9d9;
  --map-water: #c4d8e8;
  --map-waterway: #aecbe0;
  --map-road-minor: #ded9ca;
  --map-road-major: #d6d0bf;   /* art-director round 2: roads were 1.04:1 - invisible */
  --map-road-case: #c8c2b0; /* at z5-8 the _case layers ARE the road line - must read vs land */
  --map-building: #e7e4da;
  --map-admin-state: #b3ad9c;
  --map-admin-county: #d8d3c6;
  --map-label: #8d877a;
  --map-label-water: #7d9cb8;
  --map-label-halo: #f2f0ea;

  --accent: #a07514;
  --accent-hover: #8a640f;
  --accent-press: #75540c;
  --accent-tint: rgba(160, 117, 20, 0.12);
  --accent-ink: #ffffff;
  --accent-fill: #d8a52a;
  --accent-fill-hover: #c8961f;
  --accent-fill-ink: #2a1f00;

  --urg-0: #c62828;
  --urg-1: #d97706; /* true amber: dE ~26 from urg-0, ~22 from the gold accent */
  --urg-2: #2374b5;
  --urg-3: #6f7f90;
  --urg-p: #97a1ad;
  --urg-x: #a3a99f;

  --status-active: #1d7a44;
  --status-active-bg: rgba(29, 122, 68, 0.12);
  --status-cancelled: #c0392b;
  --status-cancelled-bg: rgba(192, 57, 43, 0.1);

  --e-1: 0 1px 0 rgba(255, 255, 255, 0.6) inset, 0 2px 8px rgba(80, 74, 60, 0.14);
  --e-2: 0 8px 28px rgba(80, 74, 60, 0.18), 0 1px 0 rgba(255, 255, 255, 0.5) inset;
  --e-3: 0 20px 60px rgba(80, 74, 60, 0.24), 0 1px 0 rgba(255, 255, 255, 0.5) inset;
}

/* ==========================================================================
   Reset
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  background: var(--void);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--fs-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

button, input {
  font-family: inherit;
  color: inherit;
}

button {
  cursor: pointer;
  background: none;
  border: none;
  padding: 0;
}

button:disabled { cursor: not-allowed; opacity: 0.5; }

ul, ol { margin: 0; padding: 0; list-style: none; }

h1, h2, h3, p { margin: 0; }

.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

.mobile-only { display: none; }

/* The `hidden` attribute must always win over component `display:` rules of equal
   specificity (e.g. `.empty-state{display:flex}` vs the UA `[hidden]{display:none}`
   both being a single class/attribute selector - later stylesheet wins by default,
   which silently defeats `hidden`). Force it explicitly, everywhere. */
[hidden] { display: none !important; }

/* Anchor-tag controls must not keep the browser underline - an underlined
   filled button is the loudest "unfinished" signal on the page */
a.cta-btn, a.ghost-btn { text-decoration: none; }

/* Scrollbars on dark glass chrome: thin and token-colored, never the stock
   light-grey desktop bar with arrow buttons */
.rail-scroll, .detail-scroll, .sheet-scroll, .search-results, .combobox-results {
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.rail-scroll::-webkit-scrollbar, .detail-scroll::-webkit-scrollbar,
.sheet-scroll::-webkit-scrollbar, .search-results::-webkit-scrollbar,
.combobox-results::-webkit-scrollbar { width: 6px; }
.rail-scroll::-webkit-scrollbar-button, .detail-scroll::-webkit-scrollbar-button,
.sheet-scroll::-webkit-scrollbar-button, .search-results::-webkit-scrollbar-button,
.combobox-results::-webkit-scrollbar-button { display: none; height: 0; }
.rail-scroll::-webkit-scrollbar-thumb, .detail-scroll::-webkit-scrollbar-thumb,
.sheet-scroll::-webkit-scrollbar-thumb, .search-results::-webkit-scrollbar-thumb,
.combobox-results::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: var(--r-pill);
}
.rail-scroll::-webkit-scrollbar-track, .detail-scroll::-webkit-scrollbar-track,
.sheet-scroll::-webkit-scrollbar-track, .search-results::-webkit-scrollbar-track,
.combobox-results::-webkit-scrollbar-track { background: transparent; }

/* Focus ring - visible everywhere, WCAG per accessibility-baseline */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* Reduced motion backstop */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
}

/* Reduced transparency fallback */
@media (prefers-reduced-transparency: reduce) {
  .header-bar, .filter-rail, .stat-tile, .detail-panel, .legend-chip,
  .search-results, .combobox-results, .bottom-sheet {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    background: var(--surface-1) !important;
  }
}

/* ==========================================================================
   Self-hosted fonts and icons
   Both used to come from other hosts (fonts.googleapis.com, fonts.gstatic.com,
   unpkg.com), each a DNS lookup and a TLS handshake that blocked the first
   paint. The fonts are the same latin woff2 files Google serves, kept under
   /vendor/fonts/ with Google's version in the name. The icons are the 13
   Phosphor glyphs this page actually uses (of 1,200 in the icon font it was
   loading), each an SVG mask painted with currentColor, so every existing
   `<i class="ph ph-x">` keeps working with no markup change. Phosphor is MIT;
   licence at /vendor/phosphor-LICENSE.txt.
   ========================================================================== */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 500 700;
  font-display: swap;
  src: url(/vendor/fonts/space-grotesk-v22-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url(/vendor/fonts/inter-v20-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/fonts/ibm-plex-mono-v20-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/vendor/fonts/ibm-plex-mono-v20-500-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

.ph {
  display: inline-block;
  width: 1em;
  height: 1em;
  line-height: 1;
  vertical-align: -0.125em; /* where a glyph would sit on the baseline */
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: var(--ph-icon) center / contain no-repeat;
  mask: var(--ph-icon) center / contain no-repeat;
}
.ph-caret-left { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M165.66,202.34a8,8,0,0,1-11.32,11.32l-80-80a8,8,0,0,1,0-11.32l80-80a8,8,0,0,1,11.32,11.32L91.31,128Z%22/%3E%3C/svg%3E"); }
.ph-caret-up { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M213.66,165.66a8,8,0,0,1-11.32,0L128,91.31,53.66,165.66a8,8,0,0,1-11.32-11.32l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,213.66,165.66Z%22/%3E%3C/svg%3E"); }
.ph-check { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z%22/%3E%3C/svg%3E"); }
.ph-compass { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216ZM172.42,72.84l-64,32a8.05,8.05,0,0,0-3.58,3.58l-32,64A8,8,0,0,0,80,184a8.1,8.1,0,0,0,3.58-.84l64-32a8.05,8.05,0,0,0,3.58-3.58l32-64a8,8,0,0,0-10.74-10.74ZM138,138,97.89,158.11,118,118l40.15-20.07Z%22/%3E%3C/svg%3E"); }
.ph-copy { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z%22/%3E%3C/svg%3E"); }
.ph-funnel { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M230.6,49.53A15.81,15.81,0,0,0,216,40H40A16,16,0,0,0,28.19,66.76l.08.09L96,139.17V216a16,16,0,0,0,24.87,13.32l32-21.34A16,16,0,0,0,160,194.66V139.17l67.74-72.32.08-.09A15.8,15.8,0,0,0,230.6,49.53ZM40,56h0Zm106.18,74.58A8,8,0,0,0,144,136v58.66L112,216V136a8,8,0,0,0-2.16-5.47L40,56H216Z%22/%3E%3C/svg%3E"); }
.ph-magnifying-glass { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z%22/%3E%3C/svg%3E"); }
.ph-map-pin { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M128,64a40,40,0,1,0,40,40A40,40,0,0,0,128,64Zm0,64a24,24,0,1,1,24-24A24,24,0,0,1,128,128Zm0-112a88.1,88.1,0,0,0-88,88c0,31.4,14.51,64.68,42,96.25a254.19,254.19,0,0,0,41.45,38.3,8,8,0,0,0,9.18,0A254.19,254.19,0,0,0,174,200.25c27.45-31.57,42-64.85,42-96.25A88.1,88.1,0,0,0,128,16Zm0,206c-16.53-13-72-60.75-72-118a72,72,0,0,1,144,0C200,161.23,144.53,209,128,222Z%22/%3E%3C/svg%3E"); }
.ph-moon { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M233.54,142.23a8,8,0,0,0-8-2,88.08,88.08,0,0,1-109.8-109.8,8,8,0,0,0-10-10,104.84,104.84,0,0,0-52.91,37A104,104,0,0,0,136,224a103.09,103.09,0,0,0,62.52-20.88,104.84,104.84,0,0,0,37-52.91A8,8,0,0,0,233.54,142.23ZM188.9,190.34A88,88,0,0,1,65.66,67.11a89,89,0,0,1,31.4-26A106,106,0,0,0,96,56,104.11,104.11,0,0,0,200,160a106,106,0,0,0,14.92-1.06A89,89,0,0,1,188.9,190.34Z%22/%3E%3C/svg%3E"); }
.ph-navigation-arrow { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M237.33,106.21,61.41,41l-.16-.05A16,16,0,0,0,40.9,61.25a1,1,0,0,0,.05.16l65.26,175.92A15.77,15.77,0,0,0,121.28,248h.3a15.77,15.77,0,0,0,15-11.29l.06-.2,21.84-78,78-21.84.2-.06a16,16,0,0,0,.62-30.38ZM149.84,144.3a8,8,0,0,0-5.54,5.54L121.3,232l-.06-.17L56,56l175.82,65.22.16.06Z%22/%3E%3C/svg%3E"); }
.ph-share-network { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M176,160a39.89,39.89,0,0,0-28.62,12.09l-46.1-29.63a39.8,39.8,0,0,0,0-28.92l46.1-29.63a40,40,0,1,0-8.66-13.45l-46.1,29.63a40,40,0,1,0,0,55.82l46.1,29.63A40,40,0,1,0,176,160Zm0-128a24,24,0,1,1-24,24A24,24,0,0,1,176,32ZM64,152a24,24,0,1,1,24-24A24,24,0,0,1,64,152Zm112,72a24,24,0,1,1,24-24A24,24,0,0,1,176,224Z%22/%3E%3C/svg%3E"); }
.ph-sun { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M120,40V16a8,8,0,0,1,16,0V40a8,8,0,0,1-16,0Zm72,88a64,64,0,1,1-64-64A64.07,64.07,0,0,1,192,128Zm-16,0a48,48,0,1,0-48,48A48.05,48.05,0,0,0,176,128ZM58.34,69.66A8,8,0,0,0,69.66,58.34l-16-16A8,8,0,0,0,42.34,53.66Zm0,116.68-16,16a8,8,0,0,0,11.32,11.32l16-16a8,8,0,0,0-11.32-11.32ZM192,72a8,8,0,0,0,5.66-2.34l16-16a8,8,0,0,0-11.32-11.32l-16,16A8,8,0,0,0,192,72Zm5.66,114.34a8,8,0,0,0-11.32,11.32l16,16a8,8,0,0,0,11.32-11.32ZM48,128a8,8,0,0,0-8-8H16a8,8,0,0,0,0,16H40A8,8,0,0,0,48,128Zm80,80a8,8,0,0,0-8,8v24a8,8,0,0,0,16,0V216A8,8,0,0,0,128,208Zm112-88H216a8,8,0,0,0,0,16h24a8,8,0,0,0,0-16Z%22/%3E%3C/svg%3E"); }
.ph-x { --ph-icon: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20256%20256%22%3E%3Cpath%20d=%22M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z%22/%3E%3C/svg%3E"); }

/* ==========================================================================
   Map
   ========================================================================== */

#map {
  position: fixed;
  inset: 0;
  z-index: var(--z-map);
  background: var(--void);
  opacity: 0;
  transition: opacity 500ms ease-out;
}

#map.is-loaded { opacity: 1; }

/* Restyle native MapLibre controls to tokens */
.maplibregl-ctrl-top-right { top: auto; bottom: 20px; right: 20px; }
.maplibregl-ctrl-group {
  background: var(--surface-1) !important;
  border: 1px solid var(--line) !important;
  border-radius: var(--r-md) !important;
  box-shadow: var(--e-2) !important;
  overflow: hidden;
}
.maplibregl-ctrl-group button {
  background: transparent !important;
  width: 40px !important;
  height: 40px !important;
}
.maplibregl-ctrl-group button + button {
  border-top: 1px solid var(--line) !important;
}
.maplibregl-ctrl-icon { filter: invert(0.9) brightness(1.4); }
:root[data-theme="light"] .maplibregl-ctrl-icon { filter: none; }
.maplibregl-ctrl-attrib {
  background: transparent !important;
  border-radius: var(--r-sm) 0 0 0 !important;
  font-size: var(--fs-2xs) !important;
  color: var(--text-muted) !important;
  opacity: 0.75;
}
.maplibregl-ctrl-attrib a { color: var(--text-muted) !important; }
.maplibregl-ctrl-attrib-button {
  background-color: var(--glass) !important;
  border-radius: var(--r-pill) !important;
}
.maplibregl-popup { display: none; } /* we use a custom tooltip */

.reset-view-ctrl button {
  display: flex !important;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: 18px;
}
.reset-view-ctrl button:hover { color: var(--text-primary); }

/* Loading skeleton */
.loading-skeleton {
  position: fixed;
  inset: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--void);
  transition: opacity 300ms ease-out;
  pointer-events: none;
}
.loading-skeleton.is-hidden { opacity: 0; }
.loading-arc {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 2px solid rgba(224, 180, 76, 0.18);
  border-top-color: var(--accent);
  animation: spin 900ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* The map's own failure line. Sits where the map would be, under the chrome. */
.map-fallback {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 6; /* over the skeleton, under every piece of chrome */
  max-width: min(420px, calc(100vw - 32px));
  margin: 0;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: var(--surface-2);
  border: 1px solid var(--line);
  box-shadow: var(--e-1);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  text-align: center;
  line-height: 1.5;
}
.map-fallback a { color: var(--text-primary); font-weight: 600; text-decoration: underline; }
@media (prefers-reduced-motion: reduce) {
  .loading-arc { animation: none; border-top-color: rgba(224, 180, 76, 0.18); }
}

.basemap-offline-notice {
  position: fixed;
  left: 20px;
  bottom: 20px;
  z-index: var(--z-toast);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  box-shadow: var(--e-1);
}

/* Custom hover tooltip on pins */
.pin-tooltip {
  position: fixed;
  z-index: var(--z-dropdown);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  box-shadow: var(--e-2);
  padding: var(--sp-2) var(--sp-3);
  pointer-events: none;
  transform: translate(-50%, calc(-100% - 12px));
  opacity: 0;
  transition: opacity var(--dur-fast) ease-out, transform var(--dur-fast) ease-out;
  will-change: transform;
  max-width: 240px;
}
.pin-tooltip.is-visible { opacity: 1; transform: translate(-50%, calc(-100% - 16px)); }
.pin-tooltip-address { font-family: var(--font-body); font-weight: 600; font-size: var(--fs-xs); color: var(--text-primary); }
.pin-tooltip-meta { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--text-secondary); margin-top: 2px; display: flex; align-items: center; gap: 6px; }
.pin-tooltip-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }

/* ==========================================================================
   Glass chrome base
   ========================================================================== */

.header-bar, .filter-rail, .stat-strip, .detail-panel, .legend-chip,
.bottom-sheet {
  background: var(--glass);
  /* Prefixed first: Safari before 18 ignores the plain property, and the
     panels were see-through on an iPhone - the map's zoom buttons showed
     through the filter sheet as buttons that did nothing. */
  -webkit-backdrop-filter: blur(18px) saturate(120%);
  backdrop-filter: blur(18px) saturate(120%);
  border: 1px solid var(--glass-border);
}

/* Dropdowns hold TEXT over text - glass cannot win that fight. Solid surface. */
.search-results, .combobox-results {
  background: var(--surface-1);
  border: 1px solid var(--line);
  box-shadow: var(--e-3);
}

/* ==========================================================================
   Header bar
   ========================================================================== */

.header-bar {
  position: fixed;
  top: 12px;
  left: 12px;
  right: 12px;
  height: 64px;
  z-index: var(--z-header);
  border-radius: var(--r-md);
  box-shadow: var(--e-2);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--sp-4);
  /* The header is the first thing a visitor should see, and it needs no
     data, so it is visible from the first frame. It used to start at
     opacity 0 and wait for JavaScript to add .is-in, which meant the page
     had no contentful paint at all until the map library had run: seven
     seconds measured, and Lighthouse refusing to grade the page. The
     entrance is now a transform-only keyframe, which counts as painted. */
  animation: header-in 400ms var(--ease-standard) both;
}
@keyframes header-in {
  from { transform: translateY(-12px); }
  to { transform: translateY(0); }
}

.header-brand { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }

.brand-mark {
  width: 16px;
  height: 16px;
  background: var(--accent-fill);
  transform: rotate(45deg);
  border-radius: 3px;
  flex-shrink: 0;
}

/* An h1 for the document outline, styled as the brand lockup it has always
   been on screen - so no margin, no heading size, no visual change. */
.brand-text { display: flex; flex-direction: column; line-height: 1.15; min-width: 0;
  margin: 0; font-size: inherit; font-weight: inherit; }
.brand-name { font-family: var(--font-display); font-weight: 700; font-size: var(--fs-md); color: var(--text-primary); }
.brand-sub { font-family: var(--font-body); font-weight: 500; font-size: var(--fs-xs); color: var(--text-secondary); display: none; white-space: nowrap; }

@media (min-width: 1280px) {
  .brand-sub { display: block; }
}

.header-actions { display: flex; align-items: center; gap: var(--sp-2); }

.search-field { position: relative; width: 280px; transition: width var(--dur-base) var(--ease-standard); }
/* Desktop only. On a phone the box is 40px until .is-expanded takes it over
   the whole header; this rule applied there too and a focused box 360px wide
   pushed the theme and filter buttons off a 375px screen. */
@media (min-width: 768px) {
  .search-field:focus-within { width: 360px; }
}

.search-icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
  font-size: 16px;
  pointer-events: none;
}

.search-input {
  width: 100%;
  height: 40px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-3) 0 36px;
  font-size: var(--fs-sm);
  color: var(--text-primary);
}
.search-input::placeholder { color: var(--text-muted); }
.search-input:focus { border-color: var(--line-strong); outline: none; box-shadow: 0 0 0 2px var(--accent); }

.search-results, .combobox-results {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  border-radius: var(--r-md);
  box-shadow: var(--e-2);
  z-index: var(--z-dropdown);
  overflow: hidden;
  max-height: 420px; /* guard rail only - JS caps results at 6 so the list
                        always fits and no row is ever sliced */
  overflow-y: auto;
}


.search-result-row, .combobox-result-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--line);
  text-align: left;
  width: 100%;
}
.search-result-row:last-child, .combobox-result-row:last-child { border-bottom: none; }
.search-result-row:hover, .search-result-row.is-active,
.combobox-result-row:hover, .combobox-result-row.is-active { background: var(--surface-3); }

.search-result-address { font-family: var(--font-body); font-weight: 600; font-size: var(--fs-sm); color: var(--text-primary); display: flex; align-items: center; gap: var(--sp-2); }
.search-result-meta { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--text-muted); }
.search-result-empty { padding: var(--sp-3); font-size: var(--fs-sm); color: var(--text-muted); }

.result-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }

.icon-btn {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--text-secondary);
  font-size: 18px;
  position: relative;
  flex-shrink: 0;
}
.icon-btn:hover { background: var(--surface-3); color: var(--text-primary); }
.icon-btn[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); }

.filter-badge {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: var(--r-pill);
  background: var(--accent-fill);
  color: var(--accent-fill-ink);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Freshness stamp: rail footer on desktop (always on the default screen),
   floating glass pill on mobile where there is no rail. */
.rail-stamp {
  padding: var(--sp-2) var(--sp-4);
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  flex-shrink: 0;
}
.rail-stamp.is-stale, .data-stamp.is-stale { color: var(--urg-1); font-weight: 600; }
.data-stamp {
  position: fixed;
  left: 8px;
  bottom: 52px;
  z-index: var(--z-map-controls);
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-pill);
  padding: 4px 10px;
  pointer-events: none;
  display: none;
}

/* ==========================================================================
   Filter rail
   ========================================================================== */

.filter-rail {
  position: fixed;
  top: 88px;
  left: 12px;
  width: 320px;
  max-height: calc(100vh - 100px);
  max-height: calc(100dvh - 100px);
  z-index: var(--z-rail);
  border-radius: var(--r-lg);
  box-shadow: var(--e-3);
  display: flex;
  flex-direction: column;
  opacity: 0;
  transform: translateX(-12px);
  transition: opacity 400ms var(--ease-standard), transform 400ms var(--ease-standard), width var(--dur-base) var(--ease-standard);
}
.filter-rail.is-in { opacity: 1; transform: translateX(0); transition-delay: 60ms; }

.filter-rail.is-collapsed {
  width: 48px;
}
.filter-rail.is-collapsed .rail-scroll { opacity: 0; pointer-events: none; }
.filter-rail.is-collapsed .rail-collapse-btn i { transform: rotate(180deg); }

.rail-collapse-btn {
  position: absolute;
  top: var(--sp-2);
  right: var(--sp-2);
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  border-radius: var(--r-sm);
}
.rail-collapse-btn:hover { background: var(--surface-3); color: var(--text-primary); }
.rail-collapse-btn i { transition: transform var(--dur-base) var(--ease-standard); }
.filter-rail.is-collapsed .rail-collapse-btn { right: 8px; }

.rail-scroll {
  overflow-y: auto;
  padding: var(--sp-4);
  padding-top: var(--sp-6);
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  transition: opacity var(--dur-fast) ease-out;
}

.filter-group { display: flex; flex-direction: column; gap: var(--sp-2); }

.filter-label {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-xs);
  color: var(--text-secondary);
}

.segmented {
  display: flex;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 2px;
  gap: 2px;
}
.segmented-btn {
  flex: 1;
  padding: var(--sp-2) 0;
  text-align: center;
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--text-secondary);
  border-radius: 6px;
  min-height: 32px;
}
.segmented-btn.is-active { background: var(--accent-tint); color: var(--accent); }
.segmented-btn:hover:not(.is-active) { color: var(--text-primary); }

.legend-list, .legend-chip-body { display: flex; flex-direction: column; gap: var(--sp-2); }

.legend-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-2);
  border-radius: var(--r-sm);
  min-height: 32px;
  text-align: left;
  width: 100%;
}
.legend-row:hover { background: var(--surface-3); }
.legend-row.is-off .legend-swatch { opacity: 0.3; }
.legend-row.is-off .legend-label,
.legend-row.is-off .legend-count { color: var(--text-muted); opacity: 0.8; }

.legend-swatch {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  flex-shrink: 0;
}
.legend-swatch.is-cancelled {
  border-radius: 50%;
  background: linear-gradient(90deg, var(--urg-x) 50%, transparent 50%);
  border: 1px solid var(--urg-x);
  opacity: 0.7;
}

.legend-label { font-size: var(--fs-xs); color: var(--text-secondary); flex: 1; }
.legend-count { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--text-muted); }

.source-list { display: flex; flex-direction: column; gap: 2px; }
.source-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-2);
  border-radius: var(--r-sm);
  min-height: 36px;
  width: 100%;
  text-align: left;
}
.source-row:hover { background: var(--surface-3); }
.source-check {
  width: 16px;
  height: 16px;
  border-radius: 4px;
  border: 1px solid var(--line-strong);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.source-row.is-active .source-check { background: var(--accent-fill); border-color: var(--accent-fill); }
.source-row.is-active .source-check i { color: var(--accent-fill-ink); font-size: 11px; }
.source-name { font-size: var(--fs-xs); color: var(--text-secondary); flex: 1; }
.source-row.is-active .source-name { color: var(--text-primary); }
.source-count { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--text-muted); }

.combobox { position: relative; }
.combobox-input {
  width: 100%;
  height: 36px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--text-primary);
}
.combobox-input:focus { border-color: var(--line-strong); outline: none; box-shadow: 0 0 0 2px var(--accent); }
.combobox-input::placeholder { color: var(--text-muted); }

.chip-row { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--accent-tint);
  color: var(--accent);
  border: 1px solid rgba(224, 180, 76, 0.3);
  border-radius: var(--r-pill);
  padding: 4px var(--sp-2);
  font-size: var(--fs-2xs);
  font-weight: 500;
}
.chip-remove { font-size: 12px; display: flex; }
.chip-remove:hover { color: var(--accent-hover); }

.preset-row { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.preset-btn {
  padding: 6px var(--sp-3);
  min-height: 32px;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  border: 1px solid var(--line);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
}
.preset-btn:hover { color: var(--text-primary); border-color: var(--line-strong); }
.preset-btn.is-active { background: var(--accent-tint); border-color: rgba(224, 180, 76, 0.4); color: var(--accent); }

.checkbox-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
  min-height: 32px;
}
.checkbox-row input { width: 16px; height: 16px; accent-color: var(--accent-fill); }

.filter-footnote {
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  line-height: 1.4;
  margin-top: var(--sp-1);
}

.disclosure summary {
  font-size: var(--fs-xs);
  color: var(--text-secondary);
  cursor: pointer;
  padding: var(--sp-1) 0;
  min-height: 32px;
  display: flex;
  align-items: center;
}
.disclosure summary:hover { color: var(--text-primary); }
.date-range-row { display: flex; gap: var(--sp-2); padding-top: var(--sp-2); }
.date-field { display: flex; flex-direction: column; gap: 4px; flex: 1; font-size: var(--fs-2xs); color: var(--text-muted); }
.date-field input {
  height: 36px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-2);
  color: var(--text-primary);
  font-size: var(--fs-xs);
  color-scheme: dark;
}

/* Dual-handle range slider (two overlapping native inputs) */
.range-slider { position: relative; height: 44px; display: flex; align-items: center; }
.range-track {
  position: absolute;
  left: 0;
  right: 0;
  height: 4px;
  background: var(--surface-2);
  border-radius: var(--r-pill);
}
.range-track-fill {
  position: absolute;
  height: 100%;
  background: var(--accent-fill);
  border-radius: var(--r-pill);
}
.range-input {
  position: absolute;
  width: 100%;
  left: 0;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  pointer-events: none;
  margin: 0;
  height: 44px;
}
.range-input::-webkit-slider-thumb {
  -webkit-appearance: none;
  pointer-events: auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--accent-fill);
  border: 2px solid var(--void);
  box-shadow: var(--e-1);
  cursor: pointer;
  margin-top: -7px;
}
.range-input::-moz-range-thumb {
  pointer-events: auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--accent-fill);
  border: 2px solid var(--void);
  cursor: pointer;
}
.range-input::-webkit-slider-runnable-track { height: 4px; background: transparent; }
.range-input::-moz-range-track { height: 4px; background: transparent; }
.range-input:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--accent); outline-offset: 2px; }

.range-readout {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-primary);
}

.reset-btn {
  width: 100%;
  min-height: 40px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--text-secondary);
  font-size: var(--fs-xs);
  font-weight: 500;
  background: transparent;
}
.reset-btn:hover { border-color: var(--accent); color: var(--accent); }
.reset-btn-solid { background: var(--accent-fill); color: var(--accent-fill-ink); border: none; font-weight: 600; }
.reset-btn-solid:hover { background: var(--accent-fill-hover); }

/* ==========================================================================
   Stat strip
   ========================================================================== */

.stat-strip {
  position: fixed;
  top: 88px;
  right: 12px;
  z-index: var(--z-statstrip);
  display: flex;
  gap: var(--sp-2);
  border-radius: var(--r-md);
  box-shadow: var(--e-2);
  padding: var(--sp-2);
  opacity: 0;
  transform: translateX(12px);
  transition: opacity 400ms var(--ease-standard), transform 400ms var(--ease-standard), opacity var(--dur-base) ease-out;
}
.stat-strip.is-in { opacity: 1; transform: translateX(0); transition-delay: 120ms; }
/* Detail panel open: the strip slides clear of the panel's 380px + gutter and
   dims, instead of being sliced mid-tile underneath it. */
.stat-strip.is-dimmed { right: 404px; opacity: 0.85; }
/* Search dropdown open: the strip vanishes entirely - numerals under numerals
   was a reviewed defect. Placed after .is-in so it wins the source-order tie. */
.stat-strip.is-hidden { opacity: 0; pointer-events: none; }

.search-scrim {
  position: fixed;
  inset: 0;
  z-index: 30; /* over map/rail/strip, under the header that hosts the input */
  background: var(--scrim);
  opacity: 0.35;
  pointer-events: none;
}


.stat-tile {
  min-width: 132px;
  height: 72px;
  border-radius: var(--r-md);
  background: var(--surface-2);
  border: 1px solid var(--line);
  box-shadow: var(--e-1);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 2px;
  padding: var(--sp-2);
}

.stat-value {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-lg);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.stat-value.stat-accent { color: var(--accent); }
.stat-fraction { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-muted); margin-left: 4px; }

.stat-sub {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  white-space: nowrap;
}

/* ==========================================================================
   Legend chip
   ========================================================================== */

.legend-chip {
  position: fixed;
  left: 20px;
  bottom: 20px;
  z-index: var(--z-map-controls);
  border-radius: var(--r-md);
  box-shadow: var(--e-2);
  min-width: 200px;
  overflow: hidden;
}
/* Desktop already carries the legend in the filter rail; the chip there was
   invisible dead weight underneath it. Mobile is where it earns its place. */
@media (min-width: 768px) {
  .legend-chip { display: none; }
}

.legend-chip-toggle {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  min-height: 36px;
}
.legend-chip-toggle i { transition: transform var(--dur-base) ease-out; font-size: 12px; }
.legend-chip.is-collapsed .legend-chip-toggle i { transform: rotate(180deg); }
.legend-chip.is-collapsed .legend-chip-body { display: none; }

.legend-chip-body { padding: 0 var(--sp-3) var(--sp-3); }
.legend-chip-body .legend-row:hover { background: transparent; }
/* Chip rows are interactive filters - they keep the 44px mobile touch floor
   (a tighter row here previously overrode it down to ~24px). */

/* ==========================================================================
   Detail panel (desktop)
   ========================================================================== */

.detail-panel {
  position: fixed;
  right: 12px;
  top: 88px;
  bottom: 12px;
  width: 380px;
  z-index: var(--z-detail);
  border-radius: var(--r-lg);
  box-shadow: var(--e-3);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform: translateX(24px);
  opacity: 0;
  transition: transform var(--dur-panel) var(--ease-standard), opacity var(--dur-panel) var(--ease-standard);
  will-change: transform;
}
.detail-panel.is-open { transform: translateX(0); opacity: 1; }
.detail-panel.is-closing { transform: translateX(24px); opacity: 0; transition-duration: 180ms; }

.detail-close-btn {
  /* Vertically centered on the ~32px urgency ribbon rather than straddling its edge */
  position: absolute;
  top: 4px;
  right: var(--sp-2);
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  border-radius: var(--r-sm);
  z-index: 2;
  background: var(--surface-2);
}
.detail-close-btn:hover { color: var(--text-primary); background: var(--surface-3); }

.detail-scroll { overflow-y: auto; flex: 1; }

.detail-block { opacity: 0; transform: translateY(8px); transition: opacity var(--dur-panel) var(--ease-standard), transform var(--dur-panel) var(--ease-standard); }
.detail-panel.is-open .detail-block { opacity: 1; transform: translateY(0); }
.detail-block:nth-child(1) { transition-delay: 0ms; }
.detail-block:nth-child(2) { transition-delay: 40ms; }
.detail-block:nth-child(3) { transition-delay: 80ms; }
.detail-block:nth-child(4) { transition-delay: 120ms; }
.detail-block:nth-child(5) { transition-delay: 160ms; }
.detail-block:nth-child(6) { transition-delay: 200ms; }
.detail-block:nth-child(7) { transition-delay: 240ms; }
.detail-block:nth-child(8) { transition-delay: 280ms; }

/* The mobile sheet is .detail-sheet, never .detail-panel.is-open - without
   this release its content sits at opacity 0 forever (the blank-sheet bug). */
.detail-sheet .detail-block { opacity: 1; transform: none; }

/* Neutral bar + urgency-colored dot. The urgency ramp lives on the map; a
   saturated ribbon here stacked a fourth loud hue into a 380px column. */
.urgency-ribbon {
  padding: var(--sp-2) var(--sp-4);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.ribbon-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }

.detail-body { padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-4); }

.detail-address { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-md); color: var(--text-primary); }
.detail-locality { font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--text-secondary); margin-top: 4px; }

.status-pill {
  display: inline-flex;
  align-items: center;
  padding: 4px var(--sp-3);
  border-radius: var(--r-pill);
  font-size: var(--fs-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  width: fit-content;
}
.status-pill.is-active { color: var(--status-active); background: var(--status-active-bg); }
.status-pill.is-cancelled { color: var(--status-cancelled); background: var(--status-cancelled-bg); }

.facts-grid { display: flex; flex-direction: column; }
.fact-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-top: 1px solid var(--line);
}
.fact-row:first-child { border-top: none; }
.fact-row.is-wide { grid-template-columns: 1fr; }
.fact {
  padding: var(--sp-2) 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.fact-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.fact-value { font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--text-primary); font-variant-numeric: tabular-nums; }
.fact-value.is-accent { color: var(--accent); }

.missing-note { font-size: var(--fs-2xs); color: var(--text-muted); font-style: italic; }

/* Block kicker: a rule-flanked divider label, not a heading. Keeps exactly one
   display-tier heading family in a 380px column. */
.enrich-head {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-weight: 600;
  color: var(--text-muted);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--line-strong);
}

/* Group heading: sentence case, display face - the tier the caps ladder lacked. */
.enrich-sub {
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  line-height: 1.3;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 600;
  color: var(--text-primary);
  border-top: 1px solid var(--line);
  padding-top: var(--sp-4);
  padding-bottom: var(--sp-2);
  margin-top: var(--sp-4);
}
/* First group sits under the kicker's own rule - no double line. */
.enrich-head + .enrich-sub {
  border-top: none;
  padding-top: 0;
  margin-top: var(--sp-3);
}

/* Inside the intelligence block the hairline belongs at the group boundary.
   Row rules here competed with it and out-shouted it. */
.enrich-sub + .facts-grid .fact-row { border-top: none; }

/* A lone fact runs as a ledger line, not a half-empty two-column row. */
.fact-row.is-solo { grid-template-columns: 1fr; }
.fact-row.is-solo .fact {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
}
.fact-row.is-solo .fact-value { text-align: right; }

.enrich-disclaimer {
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  text-wrap: pretty;
  /* italic microtype at 11px is unreadable; the rule + muted color already say "aside" */
  font-style: normal;
  line-height: 1.55;
  border-top: 1px solid var(--line);
  padding-top: var(--sp-3);
  margin-top: var(--sp-5);
}
.enrich-disclaimer strong { color: var(--text-secondary); font-weight: 600; }

.cta-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: 48px;
  background: var(--accent-fill);
  color: var(--accent-fill-ink);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  font-weight: 600;
  transition: background var(--dur-fast) ease-out, transform var(--dur-fast) ease-out;
}
.cta-btn:hover { background: var(--accent-fill-hover); transform: translateY(-1px); }
.cta-btn:active { transform: scale(0.98); }

.secondary-actions { display: flex; gap: var(--sp-2); }
.ghost-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-height: 48px;
  border-radius: var(--r-sm);
  color: var(--text-secondary);
  font-size: var(--fs-2xs);
  border: 1px solid var(--line);
  position: relative;
}
.ghost-btn:hover { color: var(--text-primary); border-color: var(--line-strong); }
.ghost-btn i { font-size: 16px; }
.ghost-btn.is-copied { color: var(--status-active); border-color: var(--status-active); }

.provenance {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  padding-top: var(--sp-2);
  border-top: 1px solid var(--line);
}

.past-due-note {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
}

/* ==========================================================================
   Empty state
   ========================================================================== */

.empty-state {
  position: fixed;
  inset: 0;
  z-index: var(--z-toast);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.empty-state-card {
  pointer-events: auto;
  background: var(--surface-1);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--e-2);
  padding: var(--sp-6);
  max-width: 340px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  align-items: center;
}
.empty-state-card h2 { font-family: var(--font-display); font-weight: 600; font-size: var(--fs-md); color: var(--text-primary); }
.empty-state-card p { font-size: var(--fs-sm); color: var(--text-secondary); }
.empty-state-card .reset-btn-solid { width: auto; padding: 0 var(--sp-5); }

/* ==========================================================================
   Bottom sheets (mobile)
   ========================================================================== */

.sheet-scrim {
  position: fixed;
  inset: 0;
  z-index: var(--z-scrim);
  background: var(--scrim);
}

.bottom-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-sheet);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--e-3);
  display: flex;
  flex-direction: column;
  transition: transform 260ms var(--ease-standard);
  will-change: transform;
}

.sheet-handle {
  width: 36px;
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--line-strong);
  margin: var(--sp-2) auto;
  flex-shrink: 0;
}
/* The listing sheet's handle is the only one with drag and tap code behind it.
   A drag that starts here is ours, not Safari's page scroll. */
.detail-sheet .sheet-handle {
  position: relative;
  touch-action: none;
}
/* The bar is 4px tall; a thumb is not. This is the part that answers a tap:
   about 276 by 24px, no further down than the bar's own margin so it never
   covers the first line of the sheet, and clear of the close button. */
.detail-sheet .sheet-handle::before {
  content: '';
  position: absolute;
  top: -12px;
  bottom: -8px;
  left: -120px;
  right: -120px;
}

.sheet-close-btn {
  position: absolute;
  top: var(--sp-2);
  right: var(--sp-2);
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  z-index: 2;
}
.sheet-close-btn:hover { color: var(--text-primary); background: var(--surface-3); }

.filter-sheet {
  max-height: 88vh;
  max-height: 92dvh;
  padding-bottom: env(safe-area-inset-bottom, 0);
}
.sheet-scroll { overflow-y: auto; padding: 0 var(--sp-4) var(--sp-6); flex: 1; display: flex; flex-direction: column; gap: var(--sp-5); }
.sheet-cta {
  margin: var(--sp-3) var(--sp-4);
  min-height: 48px;
  position: relative;
  background: var(--accent-fill);
  color: var(--accent-fill-ink);
  border-radius: var(--r-sm);
  font-weight: 600;
  font-size: var(--fs-sm);
  margin-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom, 0));
}
.sheet-cta::before {
  content: '';
  position: absolute;
  left: calc(-1 * var(--sp-4));
  right: calc(-1 * var(--sp-4));
  top: -40px;
  height: 28px;
  background: linear-gradient(transparent, var(--glass));
  pointer-events: none;
}

.detail-sheet {
  height: 88vh;
  height: 92dvh;
  /* Peek detent ends below the status pill - 180px sliced it mid-badge */
  transform: translateY(calc(100% - 232px));
}
/* Each twice: dvh is iOS 15.4 and later, and on anything older a declaration
   it cannot read is dropped whole - the sheet then never opened past peek.
   The fallbacks say 88 where dvh says 92: older iOS measures vh with the
   toolbar hidden, and 92 of that put the top of the sheet under it. */
.detail-sheet[data-detent="half"] {
  transform: translateY(calc(100% - 50vh));
  transform: translateY(calc(100% - 50dvh));
}
.detail-sheet[data-detent="full"] {
  transform: translateY(calc(100% - 88vh));
  transform: translateY(calc(100% - 92dvh));
}
.detail-sheet .sheet-scroll { padding: 0; }

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 1279px) {
  /* Below wide desktop the strip keeps only the two tiles a buyer acts on. */
  .stat-tile-secondary { display: none; }
  .stat-tile { min-width: 132px; }
}

@media (min-width: 768px) and (max-width: 1023px) {
  .detail-panel { width: 60vw; }
  /* Not enough width to slide the strip clear of a 60vw panel - hide it instead */
  .stat-strip.is-dimmed { opacity: 0; pointer-events: none; right: 12px; }
  /* The 60vw panel would slice the rail mid-glyph - rail yields while open. */
  body.detail-open .filter-rail { opacity: 0; pointer-events: none; }
}

@media (max-width: 767px) {
  .mobile-only { display: flex; }

  body { overflow: auto; }

  .header-bar { height: 52px; top: 8px; left: 8px; right: 8px; padding: 0 var(--sp-3); }
  .brand-name { font-size: var(--fs-base); }
  .header-actions { gap: var(--sp-1); }
  .search-field { width: 40px; overflow: hidden; }
  .search-field.is-expanded {
    position: fixed;
    left: 8px;
    right: 8px;
    top: 8px;
    width: auto;
    z-index: var(--z-header);
  }
  .search-field:not(.is-expanded) .search-input { opacity: 0; pointer-events: none; }
  .search-icon { cursor: pointer; pointer-events: auto; z-index: 1; }

  /* Filter rail is not shown floating on mobile - its content is cloned into
     the full-height bottom sheet instead (opened via the header funnel icon). */
  .filter-rail { display: none; }

  /* On a phone the strip carries only the two tiles a buyer acts on (upcoming
     count, next sale date), sized to fit with no scrolling and no scrollbar.
     The secondary tiles live on desktop only. */
  .stat-strip {
    position: fixed;
    top: 64px;
    left: 0;
    right: 0;
    z-index: var(--z-statstrip);
    display: flex;
    flex-wrap: nowrap;
    width: auto;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
    background: transparent;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border: none;
    box-shadow: none;
    border-radius: 0;
    overflow: visible;
  }
  .stat-strip.is-dimmed { right: 0; opacity: 1; }
  .stat-tile-secondary { display: none; }
  .stat-strip .stat-tile {
    flex: 1;
    height: 60px;
    min-width: 0;
    background: var(--glass);
    -webkit-backdrop-filter: blur(18px) saturate(120%);
    backdrop-filter: blur(18px) saturate(120%);
  }

  .data-stamp { display: block; }

  .legend-chip { left: 8px; bottom: 8px; min-width: 44px; }

  .reset-view-btn { right: 8px; bottom: 96px; }
  .maplibregl-ctrl-top-right { right: 8px; bottom: 8px; }

  .detail-panel { display: none; }

  .empty-state-card { max-width: calc(100vw - 48px); }
}

/* Touch target floor on mobile */
@media (max-width: 767px) {
  button, .icon-btn, .segmented-btn, .preset-btn, .chip-remove, .source-row, .legend-row {
    min-height: 44px;
  }
}

/* Link to the static locality pages. The map is JavaScript, so this anchor is
   the only crawlable route into the listings - it must stay in the markup. */
.counties-link {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-secondary);
  text-decoration: none;
  white-space: nowrap;
  padding: 0 4px;
}
.counties-link:hover { color: var(--text-primary); text-decoration: underline; }
@media (max-width: 720px) {
  .counties-link { display: none; }
}

/* A browser with no blur at all gets solid panels. 90% opaque with nothing
   behind it to soften is see-through, and what shows through a sheet reads as
   buttons. Last in the file so it wins over the glass rules above. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .header-bar, .filter-rail, .stat-strip, .detail-panel, .legend-chip, .bottom-sheet {
    background: var(--surface-1);
  }
  @media (max-width: 767px) {
    .stat-strip { background: transparent; }
    .stat-strip .stat-tile { background: var(--surface-1); }
  }
}
