/* =========================================================
   Foundation tokens
   ========================================================= */
:root{
  /* Surface stack — deepest to brightest */
  --bg-deep:#080b14;
  --bg-base:#0a0d18;
  --surf-1:rgba(16,20,32,0.78);     /* outer panel */
  --surf-2:rgba(22,28,44,0.55);      /* inner cards */
  --surf-3:rgba(28,34,52,0.92);      /* popovers / dropdowns */
  --surf-hi:rgba(40,48,72,0.55);     /* hover */
  /* Fully-opaque stand-ins for surf-1/surf-3 on desktop, where the
     over-map chrome carries NO backdrop blur (see the no-blur media
     block after the Leaflet overrides). Fully opaque on purpose:
     even a 3% alpha reads as a glassy shimmer while the map pans
     underneath (moving labels ghost through), then "solidifies"
     again at rest — worse than honest solid surfaces. */
  --surf-op-1:#101420;
  --surf-op-3:#1c2234;
  /* Borders / specular */
  --line:rgba(255,255,255,0.06);
  --line-strong:rgba(255,255,255,0.12);
  --specular:linear-gradient(180deg,rgba(255,255,255,0.08),rgba(255,255,255,0) 40%);
  /* Type */
  --txt-1:#f4f6fb;
  --txt-2:#d6dde9;
  --txt-3:#a4adc4;
  --txt-4:#8a91a8;
  --accent:#88c0d0;
  --accent-warm:#f0883e;
  /* Severity (kept untouched — these are intuitive, don't break) */
  --sev-4:#f85149;
  --sev-3:#db6d28;
  --sev-2:#e3b341;
  --sev-1:#56d364;
  /* Radius scale */
  --r-sm:8px;
  --r-md:12px;
  --r-lg:14px;
  --r-pill:999px;
  /* Shadow vocabulary */
  --shadow-sm:0 1px 2px rgba(0,0,0,0.3);
  --shadow-md:0 4px 12px rgba(0,0,0,0.35),0 1px 2px rgba(0,0,0,0.2);
  --shadow-lg:0 10px 30px rgba(0,0,0,0.5),0 2px 6px rgba(0,0,0,0.3);
  --glow-cool:0 0 24px rgba(136,192,208,0.18);
  --glow-warm:0 0 28px rgba(248,81,73,0.22);
  /* Shared site chrome (masthead) — values mirror the SEO pages'
     editorial theme in pipeline/seo/template.mjs so the map app and
     the generated pages carry one identical brand system. */
  --paper:#101218;
  --chrome-ink:#e9ecf2;
  --chrome-ink-soft:rgba(233,236,242,.80);
  --chrome-ink-faint:rgba(233,236,242,.56);
  --chrome-rule:rgba(233,236,242,.12);
  --chrome-rule-strong:rgba(233,236,242,.40);
  --display:'Fraunces',Georgia,serif;
  --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  /* Condensed app masthead height (topband + one row). The map, the
     sidebar panel and the collapsed rail all size against it so the
     app fills exactly the rest of the viewport. Zeroed on mobile,
     where the masthead is replaced by the in-app top bar. */
  --mast-h:52px; /* single-line masthead (was 60px with the two-line kicker lockup) */
}

*{margin:0;padding:0;box-sizing:border-box}
html,body{height:100%}
body{
  font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  background:var(--bg-base);
  color:var(--txt-2);
  overflow:hidden;
  font-feature-settings:"cv02","cv03","cv04","cv11";
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
#map{width:100vw;height:calc(100vh - var(--mast-h))}

/* Wraps the whole existing app (map + every overlay control) so the
   absolutely-positioned controls anchor to this box, not the
   document, once the page scrolls under the splash. On desktop it's
   simply a viewport-sized relative box — visually inert. */
#appScreen{position:relative}

/* =========================================================
   Site chrome — the shared masthead. One brand system, two
   densities:
     .sc-app  — condensed one-row bar for the map app (desktop
                only; phones get the in-app top bar instead)
     .sc-full — the SEO pages' full editorial masthead, used by
                about.html
   Class names differ from the generated pages but every value
   mirrors siteHeader()/chromeCSS() in pipeline/seo/template.mjs —
   change them in lockstep.
   ========================================================= */
.site-chrome{
  position:relative;z-index:1100;
  background:var(--paper);
  border-top:4px solid var(--accent);          /* the topband */
  border-bottom:1.5px solid var(--chrome-rule-strong);
  font-family:'Hanken Grotesk',system-ui,sans-serif;
}
.sc-kicker{
  display:block;font-family:var(--mono);font-size:9px;letter-spacing:.30em;
  text-transform:uppercase;color:var(--accent);
}
.sc-word{
  display:block;font-family:var(--display);font-optical-sizing:auto;
  font-weight:900;letter-spacing:-.02em;line-height:.95;color:var(--chrome-ink);
  text-decoration:none;white-space:nowrap;
}
.sc-nav{display:flex;align-items:center}
.sc-nav a{
  position:relative;font-size:12.5px;font-weight:600;letter-spacing:.04em;
  text-transform:uppercase;color:var(--chrome-ink-soft);text-decoration:none;
  padding-bottom:3px;transition:color .2s;
}
.sc-nav a::after{content:"";position:absolute;left:0;bottom:0;height:2px;width:0;background:var(--chrome-ink);transition:width .25s ease}
.sc-nav a:hover{color:var(--chrome-ink)}.sc-nav a:hover::after{width:100%}
.sc-nav a.active{color:var(--chrome-ink)}.sc-nav a.active::after{width:100%;background:var(--accent)}
/* keep the tap target tall without changing the visual */
.sc-nav a::before{content:"";position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);height:100%;min-height:44px}

/* Condensed density — one single-line bar above the app frame.
   Wordmark left, nav pushed right; no kicker, no edition note —
   the same lockup as the mobile header at desktop scale. */
.sc-app{height:var(--mast-h)}
.sc-app .sc-row{
  height:100%;
  display:flex;align-items:center;
  padding:0 22px;
}
.sc-app .sc-brand{display:flex;align-items:center;text-decoration:none;flex:0 0 auto}
.sc-app .sc-word{font-size:19px}
.sc-app .sc-nav{margin-left:auto;gap:24px}
@media (max-width:768px){.sc-app{display:none}}

/* Full density — the editorial masthead from the SEO pages. */
.sc-full{border-top-width:7px}
.sc-wrap{max-width:1180px;margin:0 auto;padding:0 28px}
.sc-mast{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;padding:26px 0 18px;flex-wrap:wrap}
.sc-full .sc-kicker{font-size:11px;letter-spacing:.34em;margin-bottom:6px}
.sc-full .sc-word{font-size:clamp(30px,5vw,50px);line-height:.92;white-space:normal}
.sc-full .sc-nav{gap:30px;padding-bottom:8px}
.sc-full .sc-nav a{font-size:13px}
.sc-meta{
  border-top:1px solid var(--chrome-rule);
  display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;
  padding:9px 0;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--chrome-ink-faint);
}
.sc-crumb a{color:var(--chrome-ink-faint);text-decoration:none;transition:color .2s}
.sc-crumb a:hover{color:var(--chrome-ink)}
.sc-sep{margin:0 8px;color:rgba(233,236,242,.20)}
.sc-here{color:var(--chrome-ink-soft)}
/* Contact rides the desktop nav only — the phone row is full at five,
   so on phones /contact is folded under About: the About and Contact
   pages carry a .sc-subnav tab row beneath the metabar (hidden on
   desktop, where the main nav already has the item), and every footer
   links it. Mirrors template.mjs chromeCSS (.nav-desk). */
.sc-subnav{display:none}
@media (max-width:768px){
  .sc-desk{display:none}
  .sc-subnav{
    display:flex;align-items:center;gap:22px;
    padding:9px 0 11px;border-top:1px solid var(--chrome-rule);
  }
  .sc-subnav a{
    position:relative;font-family:'Hanken Grotesk',system-ui,sans-serif;
    font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
    color:var(--chrome-ink-soft);text-decoration:none;padding-bottom:3px;
  }
  .sc-subnav a.active{color:var(--chrome-ink)}
  .sc-subnav a.active::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--accent)}
  /* ≥44px tap target without changing the painted link */
  .sc-subnav a::before{content:"";position:absolute;left:-6px;right:-6px;top:50%;transform:translateY(-50%);height:100%;min-height:44px}
}
/* five nav items now (Get Help joined) — wrap + tighten so the row
   never overflows a narrow phone. Mirrors template.mjs chromeCSS. */
@media (max-width:620px){.sc-full .sc-nav{gap:14px;flex-wrap:wrap;row-gap:2px}.sc-full .sc-nav a{font-size:11px;letter-spacing:.03em}.sc-wrap{padding:0 20px}}

/* =========================================================
   Splash / hero — ported from the supplied "Know Your Block"
   design (Hanken Grotesk, cyan/teal heat palette). display:none
   here; the mobile media query turns it on. All .sp-* rules are
   inert on desktop because #splash never renders there.
   Height: 70svh so a meaningful slice of the live heatmap (the
   actual hook) sits above the fold instead of a dead gap; a 560px
   floor guarantees the pitch/search never clip on short phones.
   ========================================================= */
#splash{
  display:none;
  flex-direction:column;
  min-height:max(560px,64vh);min-height:max(560px,64svh);
  position:relative;overflow:hidden;
  text-align:left;
  background:#0a0e15;color:#f3f5f9;
  font-family:'Hanken Grotesk',system-ui,sans-serif;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
}
/* ambient heatmap glow */
.sp-glow{position:absolute;pointer-events:none;filter:blur(20px)}
.sp-glow-a{
  top:-100px;left:-60px;width:320px;height:320px;
  background:radial-gradient(circle,rgba(139,195,212,0.18),rgba(139,195,212,0) 65%);
}
.sp-glow-b{
  top:100px;right:-80px;width:280px;height:280px;
  background:radial-gradient(circle,rgba(174,218,229,0.10),rgba(174,218,229,0) 65%);
}
.sp-grain{
  position:absolute;inset:0;pointer-events:none;opacity:0.4;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/></svg>");
}
/* (The kicker + nav eyebrow row moved into the persistent mobile
   header, #mhead — see the mobile media block.) */
.sp-main{
  position:relative;z-index:2;
  padding:26px 22px calc(env(safe-area-inset-bottom) + 22px);
  flex:1;display:flex;flex-direction:column;
  border-bottom-left-radius:32px;border-bottom-right-radius:32px;
  border-bottom:1px solid rgba(255,255,255,0.16);
  box-shadow:0 1px 0 rgba(255,255,255,0.04),0 18px 36px -10px rgba(0,0,0,0.6);
}
/* Wordmark — the brand face (Fraunces 900), same as the masthead
   on every other page; sized as a hero rather than a title bar. */
.sp-title{
  margin:0;font-size:clamp(40px,12.4vw,48px);line-height:0.98;
  letter-spacing:-0.02em;font-weight:900;color:#f3f5f9;
  font-family:var(--display);font-optical-sizing:auto;
}
.sp-accent{margin-top:14px;height:3px;width:40px;background:#8bc3d4;border-radius:2px}
.sp-lede{
  margin:18px 0 0;font-size:15px;line-height:1.5;letter-spacing:-0.15px;
  color:rgba(243,245,249,0.66);font-weight:400;max-width:340px;
}
.sp-lede strong{color:#f3f5f9;font-weight:500}
/* Proof block — the live count popping big (count-up), with the
   "official City of Columbus" credibility claim as the supporting
   label beside it. Condensed; emphasizes the official source. */
/* Barrier between the search/try idea-group and the proof/
   credibility idea-group — they were reading as one mushed block. */
.sp-rule{
  height:1px;width:100%;margin:18px 0;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,0.13) 14%,rgba(255,255,255,0.13) 86%,transparent);
}
.sp-proof{
  margin-top:2px;display:flex;align-items:center;gap:13px;
}
.sp-proof-n{
  font-size:30px;font-weight:800;letter-spacing:-1px;color:#f3f5f9;
  line-height:1;font-variant-numeric:tabular-nums;flex:0 0 auto;
}
.sp-proof-l{
  font-size:11px;font-weight:700;line-height:1.35;
  letter-spacing:0.6px;text-transform:uppercase;
  color:rgba(243,245,249,0.5);
}
.sp-search{
  margin-top:14px;position:relative;display:flex;align-items:center;
  background:rgba(255,255,255,0.04);
  border:1px solid rgba(255,255,255,0.08);border-radius:999px;
  transition:all 180ms ease;
}
.sp-search:focus-within{
  background:rgba(255,255,255,0.07);border-color:#8bc3d4;
  box-shadow:0 0 0 4px rgba(139,195,212,0.14);
}
.sp-search-ic{
  padding-left:18px;padding-right:10px;display:flex;
  color:rgba(243,245,249,0.42);
}
.sp-search:focus-within .sp-search-ic{color:#8bc3d4}
.sp-search input{
  flex:1;min-width:0;border:none;outline:none;background:transparent;
  color:#f3f5f9;font-family:inherit;font-size:16px;font-weight:500;
  letter-spacing:-0.2px;padding:14px 0;
}
.sp-search input::placeholder{color:rgba(243,245,249,0.42)}
/* The splash dropdown must read as a solid panel sitting OVER the
   bright splash. The shared .DD uses a translucent fill + backdrop
   blur (tuned for the dark map under the panel search); over the
   splash's big title/lede/glows that ghosts content through and
   looks buggy. Force an opaque fill, kill the blur, and lift the
   whole search above the sibling splash content below it. */
.sp-search{z-index:20}
#splashDd{
  background:#161b29;
  -webkit-backdrop-filter:none;backdrop-filter:none;
  z-index:30;
  box-shadow:0 18px 44px -8px rgba(0,0,0,0.78),0 0 0 1px rgba(0,0,0,0.45);
}
.sp-go{
  appearance:none;border:none;cursor:pointer;
  width:38px;height:38px;margin:5px;border-radius:50%;flex:0 0 auto;
  background:rgba(255,255,255,0.06);color:rgba(243,245,249,0.42);
  display:flex;align-items:center;justify-content:center;
  transition:all 200ms ease;
}
.sp-search input:not(:placeholder-shown) ~ .sp-go{
  background:#8bc3d4;color:#0a0e15;
}
.sp-spacer{flex:1;min-height:16px}
/* Explore CTA — a defined footer at the bottom of the hero card:
   a separator rule above it, then the label + a bobbing down
   chevron, sitting just above the card's outlined bottom edge. */
.sp-explore{
  appearance:none;cursor:pointer;width:100%;box-sizing:border-box;
  margin-top:6px;
  display:flex;align-items:center;justify-content:center;gap:10px;
  padding:18px 8px 4px;
  background:none;border:none;
  border-top:1px solid rgba(255,255,255,0.12);
  font-family:inherit;color:#f3f5f9;
  transition:transform 160ms ease,opacity 160ms ease;
}
.sp-explore > span:first-child{
  font-weight:700;font-size:15px;letter-spacing:-0.1px;white-space:nowrap;
}
.sp-explore:active{transform:scale(0.99);opacity:0.8}
.sp-explore-ic{
  width:26px;height:26px;border-radius:50%;flex:0 0 auto;
  border:1px solid rgba(243,245,249,0.28);background:rgba(243,245,249,0.08);
  display:flex;align-items:center;justify-content:center;color:#f3f5f9;
  animation:spBob 1.8s ease-in-out infinite;
}
@keyframes spBob{0%,100%{transform:translateY(0)}50%{transform:translateY(3px)}}
@keyframes spFall{
  0%{opacity:0;transform:translateY(-18px);filter:blur(6px)}
  60%{opacity:1;filter:blur(0)}
  100%{opacity:1;transform:translateY(0);filter:blur(0)}
}
.sp-fall{animation:spFall 720ms cubic-bezier(.22,.9,.28,1) both;will-change:transform,opacity,filter}
.sp-fall-1{animation-delay:40ms}
.sp-fall-2{animation-delay:110ms}
.sp-fall-3{animation-delay:180ms}
.sp-fall-4{animation-delay:240ms}
.sp-fall-5{animation-delay:310ms}
.sp-fall-6{animation-delay:380ms}
.sp-fall-7{animation-delay:450ms}
.sp-fall-8{animation-delay:520ms}
.sp-fall-9{animation-delay:590ms}
@media (prefers-reduced-motion:reduce){
  .sp-explore-ic{animation:none}
  .sp-fall{animation:none}
  /* Snap the splash away instead of the long glide. */
  #splash.swept{transition:none}
}

/* Subtle radial vignette to focus attention on the center of the map.
   Sits above tiles but below the panel/controls (z>800, z<1000). */
.map-vignette{
  position:fixed;inset:0;pointer-events:none;z-index:850;
  background:
    radial-gradient(ellipse 90% 70% at center,transparent 45%,rgba(8,11,20,0.55) 100%),
    radial-gradient(ellipse at top left,rgba(80,120,200,0.04),transparent 40%),
    radial-gradient(ellipse at bottom right,rgba(140,80,160,0.03),transparent 40%);
}

/* Frame rate: while the map is being dragged/zoomed (html.map-moving,
   set by app.js), suspend the remaining backdrop-filters — re-blurring
   a moving backdrop costs a large share of each frame, and the frosted
   look is invisible mid-gesture anyway. Restored ~150ms after the
   gesture. Scoped to the elements that actually carry a blur — this
   was `html.map-moving *`, which style-recalced the ENTIRE document
   (and tore down / rebuilt the composited layers) at every gesture
   boundary: every wheel tick, every drag start and stop. In practice
   this now only bites on phones — desktop drops these blurs outright
   in the no-blur media block after the Leaflet overrides. */
html.map-moving .P,html.map-moving .P-tog,html.map-moving .rail,
html.map-moving .DD,html.map-moving .mode-bar,html.map-moving .legend,
html.map-moving .ctx-box,html.map-moving .ctx,html.map-moving .tip,
html.map-moving .br-panel,
html.map-moving .leaflet-popup-content-wrapper,
html.map-moving .leaflet-control-zoom a{
  backdrop-filter:none !important;
  -webkit-backdrop-filter:none !important;
}

/* =========================================================
   Sidebar panel — "outer glass"
   ========================================================= */
.P{
  position:absolute;top:0;left:0;width:460px;height:calc(100vh - var(--mast-h));z-index:1000;
  display:flex;flex-direction:column;
  background:var(--surf-1);
  backdrop-filter:blur(24px) saturate(140%);
  -webkit-backdrop-filter:blur(24px) saturate(140%);
  border-right:3px solid rgba(255,255,255,0.13);
  box-shadow:var(--shadow-lg);
  transition:transform .3s cubic-bezier(.22,.61,.36,1);
}
.P::before{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:var(--specular);
  border-radius:0;
}
.P.hide{transform:translateX(-460px)}
.P.resizing,.P.sheet-dragging{transition:none}
.panel-cols{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
.panel-results{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
.col-resize{display:none}
.panel-resize{display:none}

.P-tog{
  position:absolute;right:-30px;top:18px;width:30px;height:38px;
  background:var(--surf-1);
  backdrop-filter:blur(24px) saturate(140%);
  -webkit-backdrop-filter:blur(24px) saturate(140%);
  border:1px solid var(--line);border-left:none;
  border-radius:0 var(--r-sm) var(--r-sm) 0;
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  color:var(--txt-3);font-size:13px;z-index:5;
  box-shadow:var(--shadow-sm);
}
.P-tog:hover{color:var(--txt-1);background:var(--surf-hi)}

/* Visually-hidden text — keeps descriptive/keyword-rich copy in the H1 for
   search engines & screen readers without altering the visual design. */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}

/* =========================================================
   Collapsed rail
   ========================================================= */
.rail{
  position:absolute;top:0;left:0;width:54px;height:calc(100vh - var(--mast-h));z-index:999;
  background:var(--surf-1);
  backdrop-filter:blur(24px) saturate(140%);
  -webkit-backdrop-filter:blur(24px) saturate(140%);
  border-right:1px solid var(--line);
  display:flex;flex-direction:column;align-items:center;
  padding:16px 0;gap:14px;
  opacity:0;pointer-events:none;
  transition:opacity .25s;
}
.rail.show{opacity:1;pointer-events:auto}
.rail-btn{
  width:36px;height:36px;border-radius:var(--r-sm);
  background:var(--surf-2);border:1px solid var(--line-strong);
  color:var(--txt-2);cursor:pointer;font-size:13px;
  display:flex;align-items:center;justify-content:center;
}
.rail-btn:hover{background:var(--surf-hi);color:var(--txt-1)}
.rail-stats{display:flex;flex-direction:column;gap:6px;align-items:center;width:100%;padding:0 6px}
.rail-stats .rs{width:100%;padding:6px 0;text-align:center;border-radius:var(--r-sm);background:var(--surf-2)}
.rail-stats .rs .n{font-size:14px;font-weight:700;line-height:1;letter-spacing:-.01em}
.rail-stats .rs .l{font-size:9px;color:var(--txt-3);margin-top:2px;text-transform:uppercase;letter-spacing:.06em}
.rail-active{margin-top:6px;font-size:10px;color:var(--accent);text-align:center;padding:0 4px;line-height:1.3}
/* Mobile-only launcher-pill state (address + filter count + clear).
   Hidden on desktop, where the rail is a glyph + stat column. */
.rail-mlabel,.rail-mcount,.rail-ic{display:none}
/* Reports pill and the mode-bar "?" sub-button are mobile-only;
   on desktop reports live in the stat column and the explainer
   is the always-open top-right panel. */
#railReports,.mb-help,.mbd-help,
.rail-flabel,.rail-fdiv,.rail-chips,.rail-fcount{display:none}

/* =========================================================
   Search bar — hero treatment
   ========================================================= */
.SB{margin:6px 16px 10px;position:relative}
.SB input{
  width:100%;padding:14px 36px 14px 40px;
  font-family:inherit;font-size:15px;font-weight:500;
  background-color:#0c1019;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2388c0d0' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cline x1='20' y1='20' x2='16.05' y2='16.05'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:13px center;
  border:1px solid rgba(136,192,208,0.45);border-radius:var(--r-md);
  color:var(--txt-1);outline:none;
  box-shadow:0 0 0 3px rgba(136,192,208,0.05);
  transition:border-color .15s,box-shadow .2s,background-color .15s;
}
.SB input::placeholder{color:var(--txt-2);font-weight:500}
.SB input:focus{
  border-color:rgba(136,192,208,0.55);
  background-color:rgba(22,28,44,0.95);
  box-shadow:0 0 0 4px rgba(136,192,208,0.10),var(--glow-cool);
}
.si-clr{
  position:absolute;right:8px;top:50%;transform:translateY(-50%);
  width:24px;height:24px;border-radius:var(--r-pill);
  border:none;background:rgba(255,255,255,0.06);color:var(--txt-3);
  cursor:pointer;font-size:14px;line-height:1;
  display:none;align-items:center;justify-content:center;
}
.si-clr.show{display:flex}
.si-clr:hover{background:rgba(248,81,73,0.18);color:var(--sev-4)}
.DD{
  position:absolute;top:calc(100% + 4px);left:0;right:0;
  background:var(--surf-3);
  backdrop-filter:blur(20px) saturate(140%);
  -webkit-backdrop-filter:blur(20px) saturate(140%);
  border:1px solid var(--line-strong);
  border-radius:var(--r-md);
  max-height:260px;overflow:auto;display:none;z-index:10;
  box-shadow:var(--shadow-lg);
}
.DD.show{display:block}
.DD button{
  display:block;width:100%;padding:9px 14px;
  border:none;background:none;color:var(--txt-2);
  font-family:inherit;font-size:13px;text-align:left;cursor:pointer;
  border-bottom:1px solid var(--line);
}
.DD button:last-child{border-bottom:none}
.DD button:hover,.DD button.kbd{background:var(--surf-hi);color:var(--txt-1)}
.DD .ct{color:var(--txt-3);font-size:11px;float:right;font-weight:500}
/* Inline note shown when the typed query has zero autocomplete
   matches. Makes it clear that the absence of matches means "no
   reports filed at matching addresses" rather than "search broke".
   Pairs with the geo button below to point to the next step. */
.DD .dd-note{
  padding:10px 14px;font-size:12px;line-height:1.55;
  color:var(--txt-3);
  border-bottom:1px solid var(--line);
  background:rgba(0,0,0,0.18);
}
.DD .dd-note strong{color:var(--txt-2);font-weight:600}
/* Geocoder option. Two-line layout: bold title + descriptive
   subtitle, with a chevron on the right. When it's the only
   actionable option (no autocomplete matches), it gets an
   accented background (.dd-geo-primary) so the user's eye lands
   on it immediately. */
.DD .dd-geo{
  display:flex;align-items:center;gap:10px;
  border-top:1px solid var(--line-strong);
  background:rgba(136,192,208,0.06);
  color:var(--txt-2);
  padding:11px 14px;
}
.DD .dd-geo.dd-geo-primary{
  background:rgba(136,192,208,0.14);
  border-top-color:rgba(136,192,208,0.30);
}
.DD .dd-geo:hover,.DD .dd-geo.kbd,
.DD .dd-geo-primary:hover,.DD .dd-geo-primary.kbd{
  background:rgba(136,192,208,0.22);color:var(--txt-1);
}
.DD .dd-geo strong{color:var(--txt-1);font-weight:600;font-size:13px}
.dd-geo-icon{color:var(--accent);font-size:16px;flex-shrink:0;line-height:1}
.dd-geo-text{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
.dd-geo-sub{font-size:11.5px;color:var(--txt-3);line-height:1.4}
.dd-geo-sub em{font-style:normal;color:var(--txt-2)}
.dd-geo-chev{color:var(--accent);opacity:0.7;flex-shrink:0;font-size:14px}
.DD .dd-geo:hover .dd-geo-chev{opacity:1}
/* "Use my location" row — always the first item, so it must not
   carry .dd-geo's top divider; a bottom divider separates it from
   the typed matches/footer below. */
.DD .dd-loc{
  border-top:none;
  border-bottom:1px solid var(--line-strong);
}
.DD .dd-status{
  padding:11px 14px;font-size:12.5px;line-height:1.5;
  color:var(--txt-3);
}
.DD .dd-loading{color:var(--txt-2)}
.DD .dd-loading::before{
  content:'';display:inline-block;width:10px;height:10px;
  margin-right:8px;vertical-align:-1px;
  border:2px solid var(--accent);border-right-color:transparent;
  border-radius:50%;
  animation:dd-spin 0.8s linear infinite;
}
.DD .dd-error{color:var(--sev-3)}
@keyframes dd-spin{to{transform:rotate(360deg)}}

/* =========================================================
   Block Report card — primary entry point for the feature
   ========================================================= */
/* Always visible. The full pitch (eyebrow, title, desc, samples,
   divider, search) shows at the homepage baseline. Once the user
   has searched or filtered, JS adds .collapsed which hides every
   inner section except the search bar — same border, same width,
   so the affordance persists at the top of the panel and the
   user can search a new address at any time. */
.hero{
  display:block;
  /* Right margin is 21 (vs left 16) so the BlockReport card's
     right edge aligns with the Results column's card content
     right edge (which sits 16 + 5 scrollbar-gutter = 21 from
     the panel's inner right edge). The 5px asymmetry isn't
     visible at glance but keeps the right edges of BlockReport,
     stat-row tiles, and report cards on the same vertical line. */
  margin:6px 21px 12px 16px;
  padding:14px 14px 12px;
  background:linear-gradient(135deg,rgba(136,192,208,0.12) 0%,rgba(136,192,208,0.04) 60%,rgba(136,192,208,0.02) 100%);
  border:3px solid rgba(136,192,208,0.40);
  border-radius:var(--r-md);
  position:relative;
}
/* Collapsed state — only the search input survives. Padding
   tightens so the collapsed card doesn't carry empty space. */
.hero.collapsed{padding:8px 8px 8px}
.hero.collapsed .hero-title,
.hero.collapsed .hero-desc,
.hero.collapsed .hero-example{display:none}
.hero.collapsed .SB{margin:0}
.hero-title{
  font-size:19px;font-weight:700;color:var(--txt-1);
  letter-spacing:-0.01em;line-height:1.2;margin-bottom:6px;
}
.hero-desc{
  font-size:13px;color:var(--txt-2);line-height:1.55;
  margin-bottom:11px;
}
/* The "you're in the right place" phrase — accent-colored so a cold
   landing immediately reads the city + data source. Replaces the
   short-lived masthead tagline. */
.hero-hl{color:var(--accent);font-weight:600}
/* Quiet demo path under the search — a text link, deliberately
   small so it reads as a footnote to the input, not a second CTA. */
.hero-example{
  display:block;margin-top:8px;padding:0;
  background:none;border:none;cursor:pointer;
  font-family:inherit;font-size:11.5px;color:var(--txt-3);
  text-align:left;
}
.hero-example strong{font-weight:600;color:var(--accent)}
.hero-example:hover strong{text-decoration:underline}
/* Search bar inside the hero card — strip the outer margins
   that .SB carries when standalone since the card provides its
   own padding. */
.hero .SB{margin:0;position:relative}
/* When collapsed, the input strips its outer chrome so the card
   border carries the visual frame. :not(:focus) preserves the
   bright focus state — when the user clicks back in, the input
   lights up with the normal focus ring instead of vanishing. */
.hero.collapsed .SB input:not(:focus){
  background-color:transparent;
  border-color:transparent;
  box-shadow:none;
}

/* =========================================================
   Active filter pills row
   ========================================================= */
.active-bar{
  display:none;align-items:flex-start;justify-content:space-between;gap:8px;
  margin:0 16px 10px;padding:8px 8px 8px 12px;
  background:rgba(136,192,208,0.08);
  border:1px solid rgba(136,192,208,0.22);border-radius:var(--r-md);
  min-height:36px;
}
/* Hidden by default; only shown once at least one filter is
   active — when there's nothing to clear there's nothing to
   display, and the "Showing N" count in the status strip
   already conveys the unfiltered state. */
.active-bar.has-filters{display:flex;align-items:center}
.ab-label{
  font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--txt-3);flex-shrink:0;
  user-select:none;
}
.pills{flex:1;display:flex;flex-wrap:wrap;gap:5px;align-items:center;min-height:22px}
.pills-empty{font-size:12px;color:var(--txt-4);padding:3px 0;letter-spacing:.01em}
.pill{
  display:inline-flex;align-items:center;gap:4px;
  padding:3px 4px 3px 10px;
  background:rgba(136,192,208,0.13);
  border:1px solid rgba(136,192,208,0.32);
  border-radius:var(--r-pill);
  font-size:11px;font-weight:500;color:var(--txt-1);line-height:1.4;
}
.pill .pl{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:160px}
.pill .px{
  display:inline-flex;align-items:center;justify-content:center;
  width:16px;height:16px;border-radius:50%;border:none;
  background:transparent;color:var(--txt-3);
  cursor:pointer;font-size:14px;line-height:1;padding:0;
}
.pill .px:hover{background:rgba(248,81,73,0.25);color:var(--sev-4)}
/* Auto badge — shown on pills that were engaged as a side-
   effect of another filter (e.g. Hotspot Locations auto-
   engages when you pick a category). Subdued, uppercase,
   sits between the label and the × so the user can tell at a
   glance that the pill wasn't a deliberate choice. */
.pill .pl-auto{
  font-size:9px;font-weight:700;letter-spacing:.10em;text-transform:uppercase;
  color:var(--txt-4);
  background:rgba(255,255,255,0.06);
  border:1px solid var(--line);
  border-radius:var(--r-pill);
  padding:1px 6px;
  margin-left:4px;
}
.pill.pill-auto{border-style:dashed}
/* Inline circular clear — same shape family as the rail X, not a
   text "Clear All" link. Clears every active filter. */
.clear-btn{
  flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;
  width:24px;height:24px;padding:0;
  background:transparent;border:1px solid var(--line-strong);color:var(--txt-3);
  border-radius:50%;cursor:pointer;align-self:center;
}
.clear-btn:hover:not(:disabled){
  background:rgba(248,81,73,0.1);border-color:rgba(248,81,73,0.35);color:var(--sev-4);
}
.clear-btn:disabled{opacity:.4;cursor:default}

/* =========================================================
   Stat tiles — hero numbers
   ========================================================= */
/* =========================================================
   Status strip — total count header + Hotspot toggle
   ========================================================= */
/* Parent-count tile lives inside .sev-row now so the parent
   stat and its severity breakdown read as one row that adds
   up. Styled neutral so it stands apart from the four colored
   severity tiles to its right. */

/* Featured filter row — the single Hotspot Locations toggle.
   Full-width since it's the only featured filter now. Zero
   bottom margin lets the section header below sit as tight to
   the button as possible; the text's own line-height leading
   gives the remaining visual breathing room. */
.featured-filters{
  display:block;margin:0;
}
.hs-btn{
  display:flex;align-items:center;justify-content:center;width:100%;
  padding:10px 14px;min-height:42px;
  border-radius:var(--r-md);
  font-family:inherit;text-align:center;
  cursor:pointer;outline:none;
  background:rgba(219,109,40,0.07);
  border:1px solid rgba(219,109,40,0.24);
  transition:transform .12s ease,background .15s,border-color .15s,box-shadow .15s;
}
.hs-btn::before{display:none}
.hs-btn:hover,.hs-btn:focus-visible{
  transform:translateY(-1px);
  background:rgba(219,109,40,0.13);
  border-color:rgba(219,109,40,0.45);
}
.hs-btn:focus-visible{box-shadow:0 0 0 2px rgba(136,192,208,0.5)}
.hs-btn.on{
  background:rgba(219,109,40,0.20);
  box-shadow:inset 0 0 0 1px rgba(248,81,73,0.5);
  border-color:rgba(248,81,73,0.5);
}
.hs-btn-label{
  font-size:13px;font-weight:700;color:var(--accent-warm);
  line-height:1.2;letter-spacing:-.005em;
}
.hs-btn.on .hs-btn-label{color:#fff}


/* =========================================================
   Severity breakdown row — secondary, no border
   ========================================================= */
/* Same .st elements as the old stat-tile grid, just restyled
   smaller / flatter since they're context, not anchors. The
   click-to-filter affordance stays via the existing handler.

   Each tile is a tinted card matching its severity color — much
   stronger visual hierarchy than the old transparent treatment,
   and the proportion bar at the bottom (driven by --pct, set in
   JS) gives an at-a-glance view of each tier's share of the
   visible total. */
.sev-row{
  display:grid;
  /* parent | "=" | four severity tiles. Parent is 1.7fr so the
     "REPORTS SHOWING" label fits on one line; the four siblings
     share the remaining width equally. The narrow "=" column
     carries an actual equals sign so the math relationship
     reads at a glance. Right padding is 21 (vs left 16) so the
     Low tile's right edge lines up with the report cards on
     the right (which lose 5px to the scrollbar gutter). */
  grid-template-columns:1.7fr auto repeat(4,1fr);
  gap:8px;padding:0 21px 14px 16px;align-items:stretch;
}
/* Filters chip — mobile PEEK sheet only; inert on desktop. */
.peek-filters{display:none}
.sev-row .ss-total{
  padding:7px 9px 9px;
  border-radius:var(--r-md);
  background:rgba(255,255,255,0.02);
  border:1px solid var(--line);
  display:flex;flex-direction:column;justify-content:flex-start;
  text-align:left;cursor:default;min-width:0;
}
/* Parent number matches severity-tile number exactly (size,
   weight, line-height, padding) so all five numbers sit on the
   same baseline and all five labels sit on the same baseline.
   The parent is differentiated by being wider (1.7fr column),
   neutral-tinted, and having the "=" next to it. */
.sev-row .ss-n{
  font-size:15px;font-weight:700;color:var(--txt-1);
  line-height:1.05;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
}
.sev-row .ss-l{
  font-size:9px;color:var(--txt-3);margin-top:3px;
  text-transform:uppercase;letter-spacing:.07em;font-weight:600;
  line-height:1.1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.sev-row .sev-eq{
  display:flex;align-items:center;justify-content:center;
  font-size:14px;font-weight:500;color:var(--txt-4);
  padding:0 1px;user-select:none;
  /* Vertically center the "=" against the row of numbers
     (not the row of labels). Numbers occupy roughly the top
     60% of each tile, so nudge the equals sign up a touch. */
  align-self:flex-start;margin-top:8px;
}
.sev-row .st{
  position:relative;text-align:left;min-width:0;
  padding:7px 9px 9px;
  border-radius:var(--r-md);
  background:rgba(255,255,255,0.02);
  border:1px solid var(--line);
  cursor:pointer;outline:none;overflow:hidden;
  transition:transform .12s ease,background .15s,border-color .15s,box-shadow .15s;
}
/* Per-severity tints — soft wash that still reads on dark bg. */
.sev-row .st[data-sev="4"]{background:rgba(248,81,73,0.07);border-color:rgba(248,81,73,0.24)}
.sev-row .st[data-sev="3"]{background:rgba(219,109,40,0.07);border-color:rgba(219,109,40,0.24)}
.sev-row .st[data-sev="2"]{background:rgba(227,179,65,0.07);border-color:rgba(227,179,65,0.24)}
.sev-row .st[data-sev="1"]{background:rgba(86,211,100,0.07);border-color:rgba(86,211,100,0.24)}
/* Hover/focus — lift and deepen the wash so the filter
   affordance reads. translateY is the smallest "this is a
   button" cue that doesn't shift layout. */
.sev-row .st:hover{transform:translateY(-1px)}
.sev-row .st[data-sev="4"]:hover,.sev-row .st[data-sev="4"]:focus-visible{background:rgba(248,81,73,0.13);border-color:rgba(248,81,73,0.45)}
.sev-row .st[data-sev="3"]:hover,.sev-row .st[data-sev="3"]:focus-visible{background:rgba(219,109,40,0.13);border-color:rgba(219,109,40,0.45)}
.sev-row .st[data-sev="2"]:hover,.sev-row .st[data-sev="2"]:focus-visible{background:rgba(227,179,65,0.13);border-color:rgba(227,179,65,0.45)}
.sev-row .st[data-sev="1"]:hover,.sev-row .st[data-sev="1"]:focus-visible{background:rgba(86,211,100,0.13);border-color:rgba(86,211,100,0.45)}
.sev-row .st:focus-visible{box-shadow:0 0 0 2px rgba(136,192,208,0.5)}
/* Active filter state — solid saturated edge so it reads as
   "this filter is engaged" from across the sidebar. */
.sev-row .st.on{box-shadow:0 0 0 1px var(--sc,rgba(136,192,208,0.5)) inset,0 0 0 1px var(--sc,rgba(136,192,208,0.5))}
.sev-row .st[data-sev="4"].on{--sc:rgba(248,81,73,0.7);background:rgba(248,81,73,0.16)}
.sev-row .st[data-sev="3"].on{--sc:rgba(219,109,40,0.7);background:rgba(219,109,40,0.16)}
.sev-row .st[data-sev="2"].on{--sc:rgba(227,179,65,0.7);background:rgba(227,179,65,0.16)}
.sev-row .st[data-sev="1"].on{--sc:rgba(86,211,100,0.7);background:rgba(86,211,100,0.16)}
/* Number — large enough to scan, tabular for visual alignment
   across the four tiles. */
.sev-row .st .n{
  font-size:15px;font-weight:700;line-height:1.05;
  letter-spacing:-.02em;font-variant-numeric:tabular-nums;
}
.sev-row .st[data-sev="4"] .n{color:var(--sev-4)}
.sev-row .st[data-sev="3"] .n{color:var(--sev-3)}
.sev-row .st[data-sev="2"] .n{color:var(--sev-2)}
.sev-row .st[data-sev="1"] .n{color:var(--sev-1)}
/* When the count is zero the severity color stops being
   meaningful (there's nothing critical to attend to) — fade
   the digit AND background so the eye isn't drawn to an
   empty pile. */
.sev-row .st .n.is-zero{color:var(--txt-4)!important}
.sev-row .st:has(.n.is-zero){background:rgba(255,255,255,0.015);border-color:var(--line)}
.sev-row .st .l{
  font-size:9.5px;color:var(--txt-3);margin-top:3px;
  text-transform:uppercase;letter-spacing:.08em;font-weight:600;
}
.sev-row .st .n.is-zero + .l,
.sev-row .st .n.is-zero ~ .l{color:var(--txt-4)}

/* =========================================================
   Filters card (Card 3) — all filter chips grouped under one
   bordered frame at narrow widths. Wide-screen layout (>=1400px)
   overrides the outer border + margin so it integrates into the
   side-by-side grid with the results column.
   ========================================================= */
.FS{
  padding:12px 14px 14px;
  margin:0 16px 12px;
  background:var(--surf-2);
  border:3px solid rgba(255,255,255,0.13);
  border-radius:var(--r-md);
  overflow-y:auto;flex:1;min-height:0;
}
.FS::-webkit-scrollbar{width:5px}
.FS::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.08);border-radius:3px}
.FS::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,0.16)}

/* Panel title above the whole filter stack — a different tier from
   the .sec headings (teal mono caps vs. white sans bold), so it
   reads as the panel's name rather than another section. The
   trailing gradient rule fills the row like the metabar kickers. */
.fs-title{
  margin:2px 0 6px;text-align:center;
  font-family:var(--mono);font-size:11px;font-weight:700;
  letter-spacing:.26em;text-transform:uppercase;color:var(--accent);
  /* letter-spacing trails the last glyph — offset it so the label
     sits optically centered */
  text-indent:.26em;
}

/* Section headers — visibly larger than .fb chip buttons (chips
   are 12px, so .sec at 16px). Each section also gets a hairline
   top divider + generous top margin so the eye actually registers
   "here's a new region" instead of scanning past another label.
   First section in the panel has no top divider since the
   featured filter above already breaks the visual rhythm. */
.sec{
  font-size:16px;color:var(--txt-1);font-weight:700;
  letter-spacing:-.015em;
  margin:22px 0 10px;padding-top:14px;
  border-top:1px solid var(--line);
  display:flex;align-items:center;justify-content:center;gap:6px;
}
.FS > .sec:first-of-type{
  margin-top:0;padding-top:0;border-top:none;
}
.sec-help{
  display:inline-flex;width:16px;height:16px;border-radius:50%;
  background:rgba(255,255,255,0.06);border:1px solid var(--line-strong);
  font-size:10px;align-items:center;justify-content:center;
  cursor:help;color:var(--txt-3);text-transform:none;font-weight:600;
}
.sec-help:hover{background:rgba(136,192,208,0.18);color:var(--accent);border-color:rgba(136,192,208,0.35)}
/* Sub-group label — much quieter than the section header so the
   three-tier hierarchy (section > sub-group > chip) actually
   reads. 9.5px uppercase letter-spaced caps in --txt-4 (most-
   muted text color) makes these feel like dividers, not titles. */
.grp-label{
  font-size:9.5px;color:var(--txt-4);font-weight:600;
  text-transform:uppercase;letter-spacing:.1em;
  margin:12px 0 5px;padding-left:2px;
}
/* 2-column grid for filter chips. Every chip is either half-row
   (one column) or full-row (both columns). An odd-count container
   spans its last child across both columns so a chip never sits
   alone with empty space next to it. Single-chip containers
   (HVAC, Mold, Pest, Vacant in their own .FB row) span full-width
   via the same rule — the only child is also the last child of
   an odd-count set. */
.FB{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:5px;margin-bottom:5px;
}
.FB > .fb:last-child:nth-child(odd){
  grid-column:span 2;
}
/* FB-stack: every chip in this container spans both columns.
   Used when half-width would compromise readability for the
   labels in that section (Landlord & Building, Refinements). */
.FB.FB-stack > .fb{
  grid-column:span 2;
}

/* Filter chip — centered label with symmetric padding. Chips
   that carry the ▾ sub-filter indicator get symmetric 26px L/R
   so the label still reads as centered despite the indicator
   sitting in the right padding zone. */
.fb{
  position:relative;
  padding:7px 13px;
  font-family:inherit;font-size:12.5px;font-weight:500;
  border-radius:var(--r-sm);
  border:1px solid var(--line-strong);
  background:transparent;color:var(--txt-2);
  cursor:pointer;outline:none;
  text-align:center;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  transition:border-color .15s,background .15s,color .15s,box-shadow .2s,transform .12s;
}
/* Filter-chip leading category symbol — mobile-only (revealed in the
   max-width:768px block). Hidden here so desktop chips are unchanged. */
.fb-ic{display:none}
/* Mobile-only map chrome: the on-map expanding search, the in-sheet
   Map Style row, the PEEK filter pills, and the Filters-pill sliders
   icon. Desktop keeps its own mode-bar + rail, so these are hidden
   ≥769px. */
.map-search,.map-style-sec,.map-style-row,.peek-pills,.rail-fic{display:none}
.fb[data-has-subs]{padding-left:26px;padding-right:26px}
.fb:hover{
  border-color:rgba(255,255,255,0.22);
  background:rgba(255,255,255,0.04);
  color:var(--txt-1);
  transform:translateY(-1px);
}
.fb:focus-visible{box-shadow:0 0 0 2px rgba(136,192,208,0.5)}
.fb.on{
  color:#fff;border-color:var(--c,#88c0d0);
  background:color-mix(in srgb,var(--c,#88c0d0) 22%,transparent);
  box-shadow:inset 0 0 0 1px var(--c,#88c0d0),0 0 6px color-mix(in srgb,var(--c,#88c0d0) 20%,transparent);
}
/* Sub-filter indicator — small ▾ glyph anchored to the right
   edge of the chip (not inline next to the label) so the label
   stays left-aligned and the indicator reads as a separate
   affordance. Flips to ▴ when the parent is active (subs shown). */
.fb[data-has-subs]::after{
  content:'▾';
  position:absolute;
  right:10px;top:50%;
  transform:translateY(-50%);
  font-size:9px;
  opacity:0.5;
  transition:transform .2s,opacity .15s;
}
.fb[data-has-subs]:hover::after{opacity:0.85}
.fb[data-has-subs].on::after{
  transform:translateY(-50%) rotate(180deg);
  opacity:0.95;
}

/* Sub-filter chip containers (HVAC sub-types, pest sub-types,
   mold sub-types, vacant sub-types). Same half-row / full-row
   grid pattern as the main .FB containers so the layout reads
   consistently. Visual subcategory cues:
   - 14px left margin + 10px padding-left + 2px colored left rail
     anchors them visually as "children of the parent chip above"
   - Smaller font + tighter padding distinguishes them from main
     chips without losing legibility */
.sub-btns{
  display:none;
  margin:0 0 6px 14px;
  padding-left:10px;
  border-left:2px solid var(--sub-tint,rgba(240,136,62,0.3));
}
.sub-btns.show{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:5px;
}
.sub-btns > .fb:last-child:nth-child(odd){grid-column:span 2}
.sub-btns .fb{
  font-size:11px;padding:5px 10px;
  /* Match the main chip text-overflow behavior but at sub scale */
}
/* Back-compat: legacy class still works for the pest container */
.pest-sub{
  display:none;
  margin:0 0 6px 14px;
  padding-left:10px;
  border-left:2px solid rgba(240,136,62,0.3);
}
.pest-sub.show{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:5px;
}
.pest-sub > .fb:last-child:nth-child(odd){grid-column:span 2}
.pest-sub .fb{font-size:11px;padding:5px 10px}

/* Date section */
.sec-toggle{cursor:pointer;user-select:none;justify-content:flex-start}
.sec-toggle:hover{color:var(--txt-2)}
.sec-toggle .sec-state{margin-left:auto;font-size:10px;color:var(--accent);text-transform:none;letter-spacing:0;font-weight:500}
.sec-toggle .sec-arrow{font-size:10px;color:var(--txt-3);transition:transform .2s;margin-left:6px}
.sec-toggle.open .sec-arrow{transform:rotate(180deg)}
.date-body{display:none;padding-left:2px}
.date-body.open{display:block}
.date-presets{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:8px}
.date-presets .pre{
  padding:5px 11px;font-family:inherit;font-size:11px;font-weight:500;
  border-radius:var(--r-sm);border:1px solid var(--line);
  background:transparent;color:var(--txt-3);cursor:pointer;
}
.date-presets .pre:hover{border-color:var(--line-strong);color:var(--txt-2)}
.date-presets .pre.on{background:rgba(136,192,208,0.13);border-color:rgba(136,192,208,0.4);color:var(--accent)}

.yr-row{display:flex;flex-wrap:wrap;gap:4px;margin-bottom:6px}
.mo-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:4px;margin-bottom:6px}
.mo{
  padding:6px 0;font-family:inherit;font-size:11px;font-weight:600;
  text-align:center;border-radius:var(--r-sm);
  border:1px solid var(--line);background:transparent;color:var(--txt-3);
  cursor:pointer;outline:none;letter-spacing:.02em;
  transition:all .15s;
}
.mo:hover{background:var(--surf-2);color:var(--txt-2)}
.mo:focus-visible{box-shadow:0 0 0 2px rgba(136,192,208,0.5)}
.mo.on{background:rgba(136,192,208,0.13);border-color:rgba(136,192,208,0.35);color:var(--accent)}

/* =========================================================
   Results header + list
   ========================================================= */
.r-hdr-btn{
  padding:5px 11px;font-family:inherit;font-size:11px;font-weight:500;
  border-radius:var(--r-sm);border:1px solid var(--line);
  background:transparent;color:var(--txt-3);cursor:pointer;
}
.r-hdr-btn:hover{background:var(--surf-2);color:var(--txt-2)}
.r-hdr-btn.on{background:rgba(136,192,208,0.13);border-color:rgba(136,192,208,0.4);color:var(--accent)}

.R{
  flex:1;overflow-y:auto;padding:12px 16px 18px;min-height:140px;
  /* scrollbar-gutter:stable keeps the visible content width
     constant whether or not the scrollbar is actually showing,
     so the cards' right edge stays at the same X position the
     stats row's tiles and BlockReport card align to. */
  scrollbar-gutter:stable;
}
.R::-webkit-scrollbar{width:5px}
.R::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.08);border-radius:3px}
.R::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,0.16)}
.R-hdr{font-size:11px;color:var(--txt-3);margin:6px 0 8px;font-weight:500;display:flex;align-items:center;gap:8px}

/* Empty state */
.empty{padding:28px 12px;text-align:center;color:var(--txt-3);font-size:13px;line-height:1.6}
.empty .e-icon{font-size:28px;opacity:.45;margin-bottom:8px}
.empty .e-hint{font-size:11px;color:var(--txt-4);margin-top:6px}
.empty button{
  margin-top:12px;padding:7px 14px;font-family:inherit;font-size:11px;font-weight:500;
  background:rgba(136,192,208,0.13);border:1px solid rgba(136,192,208,0.35);
  color:var(--accent);border-radius:var(--r-sm);cursor:pointer;
}
.empty button:hover{background:rgba(136,192,208,0.22)}

/* Hotspot card.
   Replaced the leading fire emoji with a 3px orange accent rail on
   the left edge — same "hot/important" semantic, native to the dark
   theme, no character clutter in the title. Card content sits flush
   with the rail. */
.cl-card{
  background:linear-gradient(135deg,rgba(219,109,40,0.06),rgba(248,81,73,0.03));
  border:1px solid rgba(219,109,40,0.18);
  border-left:3px solid var(--accent-warm);
  border-radius:var(--r-md);
  padding:11px 13px 11px 13px;margin-bottom:7px;cursor:pointer;
  position:relative;transition:border-color .15s,background .15s,box-shadow .2s,transform .15s;
}
.cl-card::before{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:var(--specular);border-radius:inherit;opacity:.4;
}
.cl-card:hover{
  border-color:rgba(219,109,40,0.4);
  border-left-color:var(--accent-warm);
  background:linear-gradient(135deg,rgba(219,109,40,0.1),rgba(248,81,73,0.05));
  box-shadow:0 0 0 1px rgba(248,81,73,0.15),var(--shadow-md);
}
.cl-card .cn{font-size:14px;font-weight:600;color:var(--accent-warm);padding-right:24px;letter-spacing:-.005em;line-height:1.25}
/* Scatter zone variant — purple/magenta to mirror the scatter map palette */
.cl-card-scatter{background:linear-gradient(135deg,rgba(168,85,247,0.07),rgba(236,72,153,0.03));border-color:rgba(168,85,247,0.22);border-left-color:#c4b5fd}
.cl-card-scatter:hover{background:linear-gradient(135deg,rgba(168,85,247,0.12),rgba(236,72,153,0.05));border-color:rgba(168,85,247,0.45);border-left-color:#c4b5fd;box-shadow:0 0 0 1px rgba(236,72,153,0.18),var(--shadow-md)}
.cl-card-scatter .cn{color:#c4b5fd}
.cl-card .ca{font-size:12px;color:var(--txt-2);margin-top:2px}
.cl-card .cs{font-size:11.5px;color:var(--txt-3);margin-top:7px;font-variant-numeric:tabular-nums;letter-spacing:.005em}
/* Empty-state icon — colored disc instead of the fire emoji */
.empty .e-icon.e-icon-hs{width:14px;height:14px;border-radius:50%;background:var(--accent-warm);box-shadow:0 0 8px rgba(240,136,62,0.45);margin:0 auto 8px;font-size:0;opacity:1}
.cl-card .copy{
  position:absolute;top:8px;right:8px;width:24px;height:24px;
  border-radius:var(--r-sm);border:none;
  background:rgba(255,255,255,0.05);color:var(--txt-3);
  cursor:pointer;font-size:12px;opacity:0;
  transition:opacity .15s,background .15s;
  display:flex;align-items:center;justify-content:center;
}
.cl-card:hover .copy{opacity:1}
.cl-card .copy:hover{background:rgba(136,192,208,0.18);color:var(--accent)}

/* Address group */
.addr-group{margin-bottom:8px;border-radius:var(--r-md);overflow:hidden}
/* Address-group wrapper. Neutral gray outline — severity is now
   communicated by the colored dot at the start of the header
   row, not by the box outline. Keeps a row of groups visually
   calm while still surfacing severity at a glance via the dots. */
.addr-group{
  margin-bottom:8px;
  background:var(--surf-2);
  border:1px solid var(--line);
  border-radius:var(--r-md);
  overflow:hidden;
  transition:border-color .15s;
}
.addr-group:not(.open):hover,.addr-group:hover{
  border-color:var(--line-strong);
}

.ag-header{
  display:flex;align-items:center;gap:10px;
  padding:10px 13px;cursor:pointer;
  transition:background .15s;
}
.ag-header:hover{background:rgba(255,255,255,0.03)}
.addr-group.open .ag-header{
  border-bottom:1px solid var(--line);
}
/* Severity dot — fills the role the box outline used to carry.
   Color comes from --ag-sc (max severity at the address). */
.ag-dot{
  width:8px;height:8px;border-radius:50%;
  background:var(--ag-sc,var(--txt-4));
  flex-shrink:0;
  box-shadow:0 0 6px color-mix(in srgb,var(--ag-sc,transparent) 35%,transparent);
}
.ag-addr{
  font-size:13px;font-weight:600;color:var(--txt-1);letter-spacing:-.005em;
  flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.ag-count{
  font-size:11px;color:var(--txt-3);font-variant-numeric:tabular-nums;flex-shrink:0;
}
/* Chevron always present — single-report groups start .open so
   the glyph displays rotated (▴). Users can still toggle a
   single-report group closed if they want a cleaner scan. */
.ag-arrow{
  font-size:11px;color:var(--txt-3);flex-shrink:0;
  width:11px;text-align:center;
  transition:transform .2s;
}
.addr-group.open .ag-arrow{transform:rotate(180deg)}
.ag-body{display:none}
.addr-group.open .ag-body{display:block}

/* Cards inside the group — no individual outline (the parent
   carries it). Internal bottom borders divide stacked cards.
   Layout flipped: description (.ds) at the top as the main
   content, metadata row (.rc-meta) at the bottom with tags +
   date as small neutral metadata. */
.rc{
  background:transparent;
  border:none;border-bottom:1px solid var(--line);
  border-radius:0;padding:11px 13px;
  cursor:pointer;transition:background .15s;
}
.rc:last-child{border-bottom:none}
.rc:hover{background:rgba(40,48,72,0.30)}
.rc.zoomed{background:rgba(136,192,208,0.10);box-shadow:inset 0 0 0 1px rgba(136,192,208,0.25)}
.rc .ds{
  font-size:13px;color:var(--txt-2);line-height:1.6;
  max-height:0;overflow:hidden;transition:max-height .25s;
}
.rc.open .ds{max-height:1400px;margin-bottom:8px}
/* Metadata row at the bottom of each card: 'Tags: X Y' on the
   left, date on the right. Divider line above only when the
   description is shown — keeps collapsed cards compact. */
.rc-meta{
  display:flex;justify-content:space-between;align-items:center;gap:10px;
}
.rc.open .rc-meta{padding-top:8px;border-top:1px solid var(--line)}
/* Tag strip — horizontally scrolls instead of wrapping so the
   date stays pinned to the right on one line. */
.rc .tg-row{
  display:flex;flex-wrap:nowrap;align-items:center;gap:5px;
  flex:1 1 auto;min-width:0;
  overflow-x:auto;overflow-y:hidden;
  scrollbar-width:thin;scrollbar-color:var(--line-strong) transparent;
}
.rc .tg-row::-webkit-scrollbar{height:4px}
.rc .tg-row::-webkit-scrollbar-track{background:transparent}
.rc .tg-row::-webkit-scrollbar-thumb{background:var(--line-strong);border-radius:2px}
.rc .tg-row > *{flex-shrink:0}
.rc .tg-label{
  font-size:10px;color:var(--txt-4);font-weight:500;letter-spacing:.02em;
  margin-right:2px;
}
.rc .dt{
  font-size:10.5px;color:var(--txt-4);flex-shrink:0;
  font-variant-numeric:tabular-nums;letter-spacing:.02em;
  white-space:nowrap;
}
/* Tag chips — outlined, neutral. Same treatment for severity
   and category tags so the row reads as one consistent strip.
   Severity color isn't repeated here because the address-group
   dot already carries it; doubling up in the Tags row added
   visual noise without adding signal. */
.rc .tg{
  display:inline-flex;font-size:10px;padding:1.5px 7px;border-radius:var(--r-sm);
  border:1px solid var(--line-strong);background:transparent;
  color:var(--txt-3);font-weight:500;letter-spacing:.005em;
}

.load-more{
  display:block;width:100%;padding:9px 12px;margin:8px 0;
  font-family:inherit;font-size:12px;font-weight:500;
  background:var(--surf-2);border:1px solid var(--line);
  color:var(--txt-2);border-radius:var(--r-sm);cursor:pointer;text-align:center;
  transition:background .15s,border-color .15s;
}
.load-more:hover{background:var(--surf-hi);border-color:var(--line-strong)}

/* =========================================================
   Map controls
   ========================================================= */
/* Top-right control stack: "What you're seeing" leads (open by
   default), the Map view bar sits beneath it. Right-aligned flex
   column, so the collapsed bar grows LEFTWARD when it expands.
   pointer-events:none on the wrapper (children restore auto) so
   on mobile — where both children are display:none — the empty
   wrapper never intercepts map gestures. */
.map-tr{
  position:absolute;top:14px;right:14px;z-index:1000;
  display:flex;flex-direction:column;align-items:flex-end;gap:8px;
  pointer-events:none;
}
.map-tr > *{pointer-events:auto}
.mode-bar{
  display:flex;align-items:center;gap:3px;
  background:var(--surf-1);
  backdrop-filter:blur(20px) saturate(140%);
  -webkit-backdrop-filter:blur(20px) saturate(140%);
  border:1px solid var(--line-strong);
  border-radius:var(--r-md);padding:3px 4px;
  box-shadow:var(--shadow-lg),0 0 0 1px rgba(255,255,255,0.04);
}
/* Header bar — the always-visible slice. Collapsed it IS the whole
   control (label + current mode + chevron); expanded it becomes the
   leading label for the buttons that follow. */
.mode-bar-hdr{
  display:flex;align-items:center;gap:7px;
  padding:7px 9px;border:none;background:transparent;
  font-family:inherit;cursor:pointer;border-radius:var(--r-sm);
  color:var(--txt-3);outline:none;flex-shrink:0;
}
.mode-bar-hdr:hover{background:var(--surf-hi);color:var(--txt-1)}
.mode-bar-hdr:focus-visible{box-shadow:0 0 0 2px rgba(136,192,208,0.5)}
.mode-bar-label{
  font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--txt-4);flex-shrink:0;
  user-select:none;
}
/* Active-mode readout — only meaningful while collapsed (the .on
   button shows it when expanded). */
.mode-bar-cur{
  font-size:11px;font-weight:600;color:var(--accent);
  user-select:none;
}
.mode-bar-arrow{
  font-size:9px;color:var(--txt-4);user-select:none;
  transition:transform .2s;
}
.mode-bar.collapsed .mb,
.mode-bar.collapsed .mode-bar-divider{display:none}
.mode-bar:not(.collapsed) .mode-bar-cur{display:none}
.mode-bar:not(.collapsed) .mode-bar-arrow{transform:rotate(180deg)}
/* Vertical line separating the mode group (Heatmap/Scatter/
   Markers) from the viewport-filter toggle so the eye reads
   them as two unrelated controls that just happen to share a
   row. */
.mode-bar-divider{
  display:inline-block;width:1px;height:18px;
  background:var(--line-strong);
  margin:0 5px;
  flex-shrink:0;
}
.mb{
  padding:7px 13px;font-family:inherit;font-size:12px;font-weight:500;
  background:transparent;border:1px solid transparent;
  color:var(--txt-3);cursor:pointer;border-radius:var(--r-sm);outline:none;
  transition:color .15s,background .15s,border-color .15s;
}
.mb:hover{color:var(--txt-1);background:var(--surf-hi)}
.mb:focus-visible{box-shadow:0 0 0 2px rgba(136,192,208,0.5)}
.mb.on{background:rgba(136,192,208,0.18);border-color:rgba(136,192,208,0.35);color:var(--accent)}
.mb-vp{margin-left:6px}

.legend{
  position:absolute;bottom:16px;right:14px;z-index:1000;
  background:var(--surf-1);
  backdrop-filter:blur(20px) saturate(140%);
  -webkit-backdrop-filter:blur(20px) saturate(140%);
  border:1px solid var(--line);border-radius:var(--r-md);
  font-size:11px;color:var(--txt-2);
  overflow:hidden;box-shadow:var(--shadow-md);
}

/* Context box — leads the top-right stack (.map-tr); the collapsed
   Map view bar sits beneath it. */
.ctx-box{
  width:340px;max-width:calc(100vw - 28px);
  background:var(--surf-1);
  backdrop-filter:blur(20px) saturate(140%);
  -webkit-backdrop-filter:blur(20px) saturate(140%);
  border:1px solid var(--line-strong);border-radius:var(--r-md);
  box-shadow:var(--shadow-lg),0 0 0 1px rgba(255,255,255,0.04);
  overflow:hidden;
  transition:width .2s;
}
.cb-hdr{
  padding:9px 13px;font-weight:600;color:var(--txt-3);
  cursor:pointer;display:flex;align-items:center;gap:6px;
  font-size:10px;text-transform:uppercase;letter-spacing:.08em;
  user-select:none;
}
.cb-hdr:hover{color:var(--txt-2)}
.cb-hdr .cb-arrow{margin-left:auto;font-size:9px;transition:transform .2s}
.ctx-box.open .cb-arrow{transform:rotate(180deg)}
.cb-body{
  display:none;padding:0 14px 13px;
  font-size:13px;color:var(--txt-2);line-height:1.5;
}
.ctx-box.open .cb-body{display:block}
.cb-body strong{color:var(--txt-1);font-weight:600}
.cb-body .cb-note{
  margin-top:8px;padding-top:8px;
  border-top:1px solid var(--line);
  font-size:11px;color:var(--txt-3);line-height:1.5;
}
.cb-body .cb-tip{
  margin-top:8px;font-size:11px;color:var(--txt-4);
}
.ctx-box:not(.open){width:auto}
.legend .lg-hdr{
  padding:8px 13px;font-weight:600;color:var(--txt-3);
  cursor:pointer;display:flex;align-items:center;gap:6px;
  font-size:10px;text-transform:uppercase;letter-spacing:.08em;
}
.legend .lg-hdr:hover{color:var(--txt-2)}
.legend .lg-arrow{transition:transform .2s;font-size:9px}
.legend.open .lg-arrow{transform:rotate(180deg)}
.legend .lg-body{display:none;padding:4px 13px 11px}
.legend.open .lg-body{display:block}
.legend .lg-row{display:flex;align-items:center;gap:8px;padding:3px 0;font-size:12px}
.legend .lg-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0;box-shadow:0 0 0 1px rgba(0,0,0,0.4) inset}
.legend .lg-sub{margin-top:7px;font-size:10px;color:var(--txt-4);line-height:1.4;max-width:200px}

.nearby-label{font-size:11px;color:var(--txt-4);padding:9px 0 5px;border-top:1px solid var(--line);margin-top:8px;font-style:italic}

/* =========================================================
   Right-click context menu + tooltip
   ========================================================= */
.ctx{
  position:absolute;z-index:2000;
  background:var(--surf-3);
  backdrop-filter:blur(20px) saturate(140%);
  -webkit-backdrop-filter:blur(20px) saturate(140%);
  border:1px solid var(--line-strong);border-radius:var(--r-md);
  padding:5px;display:none;min-width:200px;
  box-shadow:var(--shadow-lg);
}
.ctx.show{display:block}
.ctx button{
  display:block;width:100%;padding:8px 12px;
  border:none;background:none;color:var(--txt-2);
  font-family:inherit;font-size:12px;text-align:left;cursor:pointer;
  border-radius:var(--r-sm);
}
.ctx button:hover{background:var(--surf-hi);color:var(--txt-1)}

.tip{
  position:absolute;z-index:2000;
  background:var(--surf-3);
  backdrop-filter:blur(20px) saturate(140%);
  -webkit-backdrop-filter:blur(20px) saturate(140%);
  border:1px solid var(--line-strong);border-radius:var(--r-sm);
  padding:9px 11px;display:none;
  font-size:11px;color:var(--txt-2);line-height:1.5;
  max-width:280px;box-shadow:var(--shadow-md);pointer-events:none;
}
.tip.show{display:block}

/* =========================================================
   Leaflet overrides
   ========================================================= */
.leaflet-container{background:var(--bg-deep)!important;font-family:inherit!important}
/* Lighten Carto dark street/label tones for legibility. brightness() alone
   lifts the dark grays without crushing blacks darker the way contrast() did.
   Applied to the tile pane (not per-tile) to avoid seams between tiles. */
.leaflet-tile-pane{filter:brightness(1.5) saturate(1.12)}
.marker-cluster-small{background:rgba(136,192,208,0.2)!important}
.marker-cluster-small div{background:rgba(136,192,208,0.55)!important;color:#fff!important;font-size:11px!important;font-weight:600!important;font-family:inherit!important}
.marker-cluster-medium{background:rgba(219,109,40,0.2)!important}
.marker-cluster-medium div{background:rgba(219,109,40,0.55)!important;color:#fff!important;font-size:11px!important;font-weight:600!important;font-family:inherit!important}
.marker-cluster-large{background:rgba(248,81,73,0.2)!important}
.marker-cluster-large div{background:rgba(248,81,73,0.55)!important;color:#fff!important;font-size:11px!important;font-weight:600!important;font-family:inherit!important}
.leaflet-popup-content-wrapper{
  background:var(--surf-3)!important;
  backdrop-filter:blur(20px) saturate(140%);
  -webkit-backdrop-filter:blur(20px) saturate(140%);
  color:var(--txt-2)!important;
  border-radius:var(--r-md)!important;
  border:1px solid var(--line-strong)!important;
  box-shadow:var(--shadow-lg)!important;
}
.leaflet-popup-tip{background:var(--surf-3)!important}
.leaflet-popup-content{font-size:13px!important;line-height:1.6!important;margin:14px 16px!important;max-width:380px!important;font-family:inherit!important}
.lp-addr{font-size:15px;font-weight:700;color:var(--txt-1);margin-bottom:6px;letter-spacing:-.01em}
.lp-meta{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:6px}
.lp-sev{font-size:11px;font-weight:600;padding:3px 9px;border-radius:var(--r-sm)}
.lp-date{font-size:11px;color:var(--txt-3);font-variant-numeric:tabular-nums}
.lp-cats{font-size:11px;color:var(--txt-3);margin-bottom:6px}
.lp-desc{font-size:13px;color:var(--txt-2);line-height:1.6}

.leaflet-control-zoom a{
  background:var(--surf-1)!important;
  backdrop-filter:blur(16px) saturate(140%);
  border:1px solid var(--line)!important;
  color:var(--txt-2)!important;
}
.leaflet-control-zoom a:hover{background:var(--surf-hi)!important;color:var(--txt-1)!important}

/* =========================================================
   Desktop: no backdrop blur over the live map, ever.
   The glass chrome (sidebar, rail, mode bar, legend, context box,
   popups, zoom buttons, popovers) carried blur(16-24px)
   backdrop-filters. Over a live map that is a standing per-frame
   tax on a real GPU: every pan/zoom animation, tile fade-in,
   cluster animation, and inertia frame re-samples every blur —
   multiplied 4x on hiDPI displays — and the mid-gesture suspension
   (html.map-moving) papered over only the drag itself while adding
   compositor layer teardown/rebuild at every gesture boundary.
   Same call as the docked Block Report panel: drop the filters and
   compensate with near-opaque surfaces. Plain alpha blending is
   effectively free, and at these alphas the frost was barely
   visible over a dark basemap anyway. Mobile (<=768px) keeps its
   glass — panels there are transient and the user reports it fine.
   ========================================================= */
@media (min-width:769px){
  .P,.P-tog,.rail,.DD,.mode-bar,.legend,.ctx-box,.ctx,.tip,
  .leaflet-popup-content-wrapper,.leaflet-control-zoom a{
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
  }
  .P,.P-tog,.rail,.mode-bar,.legend,.ctx-box{background:var(--surf-op-1)}
  .DD,.ctx,.tip{background:var(--surf-op-3)}
  .leaflet-popup-content-wrapper{background:var(--surf-op-3)!important}
  .leaflet-popup-tip{background:var(--surf-op-3)!important}
  .leaflet-control-zoom a{background:var(--surf-op-1)!important}
}

/* =========================================================
   Two-column wide-screen layout (>=1400px)
   ========================================================= */
@media (min-width:1400px){
  .P{width:var(--panel-w,500px)}
  .P.hide{transform:translateX(calc(-1 * var(--panel-w,500px)))}
  .panel-cols{display:grid;grid-template-columns:var(--col-left,250px) 6px 1fr;grid-template-rows:1fr;border-top:3px solid rgba(255,255,255,0.13)}
  /* At wide screens the FS sits in a grid cell next to the
     results column — drop the card border + margin so it
     integrates with the grid frame instead of floating. The
     right edge is replaced by a thicker divider that visually
     separates the FS zone from the col-resize gutter and the
     results zone beyond it. */
  .FS{
    grid-column:1;min-height:0;
    margin:0;
    border:none;border-right:3px solid rgba(255,255,255,0.13);
    border-radius:0;
    background:transparent;
    padding:12px 16px 14px;
  }
  .col-resize{
    grid-column:2;display:block;cursor:col-resize;
    background:transparent;
    border-right:3px solid rgba(255,255,255,0.13);
    transition:background .15s;position:relative;
  }
  .col-resize::after{
    content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
    width:2px;height:32px;background:var(--line-strong);border-radius:1px;
  }
  .col-resize:hover,.col-resize.dragging{background:rgba(136,192,208,0.15)}
  .panel-results{grid-column:3;min-height:0}
  .panel-resize{
    display:block;position:absolute;top:0;right:-3px;width:6px;height:100%;
    cursor:col-resize;z-index:1001;background:transparent;
  }
  .panel-resize:hover,.panel-resize.dragging{background:rgba(136,192,208,0.15)}
  body.col-dragging{cursor:col-resize!important;user-select:none}
  body.col-dragging *{cursor:col-resize!important;user-select:none!important}
}

/* =========================================================
   Block Report — address-anchored neighborhood readout
   ========================================================= */

/* CTA — appears at the top of the search-result panel when an
   address is selected. Calm by design (the old attention pulse was
   distracting once you'd seen it) — a steady teal border + soft
   halo reads as "primary action" without demanding the eye. */
.br-cta{
  display:flex;align-items:center;gap:10px;
  width:100%;padding:11px 14px;margin:4px 0 10px;
  background:linear-gradient(135deg,rgba(136,192,208,0.16),rgba(136,192,208,0.06));
  border:1px solid rgba(136,192,208,0.6);
  border-radius:var(--r-md);
  color:var(--txt-1);
  font:inherit;text-align:left;cursor:pointer;
  box-shadow:0 0 14px rgba(136,192,208,0.16);
  transition:background .15s,border-color .15s,transform .08s,box-shadow .15s;
}
.br-cta:hover{
  background:linear-gradient(135deg,rgba(136,192,208,0.28),rgba(136,192,208,0.10));
  border-color:rgba(136,192,208,0.85);
  box-shadow:
    0 0 0 4px rgba(136,192,208,0.28),
    0 0 24px 4px rgba(136,192,208,0.35);
}
.br-cta:active{transform:translateY(1px)}
.br-cta-icon{font-size:18px;line-height:1;flex-shrink:0}
.br-cta-text{flex:1;min-width:0}
.br-cta-text strong{
  font-size:13.5px;font-weight:600;color:var(--txt-1);
  white-space:nowrap;
}
.br-cta-chevron{color:var(--accent);font-size:16px;flex-shrink:0;opacity:.85}
.br-cta:hover .br-cta-chevron{opacity:1}

/* "See the whole area" — the onward link at the foot of a Block
   Report. Same teal family as .br-cta but calm (no pulse): it's a
   next step, not the primary action. */
.br-area{
  display:flex;align-items:center;gap:14px;
  margin:18px 0 4px;padding:14px 16px;
  background:linear-gradient(135deg,rgba(136,192,208,0.10),rgba(136,192,208,0.03));
  border:1px solid rgba(136,192,208,0.32);
  border-radius:var(--r-md);
  color:var(--txt-1);text-decoration:none;
  transition:background .15s,border-color .15s,transform .08s;
}
.br-area:hover{
  background:linear-gradient(135deg,rgba(136,192,208,0.20),rgba(136,192,208,0.06));
  border-color:rgba(136,192,208,0.7);
}
.br-area:active{transform:translateY(1px)}
.br-area-txt{flex:1;min-width:0}
.br-area-kicker{
  font-family:var(--mono);font-size:9.5px;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;
  color:var(--accent);margin-bottom:4px;
}
.br-area-title{font-size:15px;font-weight:600;color:var(--txt-1);line-height:1.3}
.br-area-title strong{color:var(--accent);font-weight:700}
.br-area-sub{
  font-size:12px;color:var(--txt-3);margin-top:4px;line-height:1.45;
  font-variant-numeric:tabular-nums;
}
.br-area-arrow{flex-shrink:0;font-size:18px;color:var(--accent);transition:transform .2s}
.br-area:hover .br-area-arrow{transform:translateX(3px)}

/* "What to do with this" — the report's computed conclusions.
   Quiet bordered rows (not cards): these read as guidance, not data. */
.br-do{display:flex;flex-direction:column}
.br-do-item{
  font-size:13.5px;line-height:1.6;color:var(--txt-3);
  padding:11px 0 11px 14px;
  border-left:2px solid rgba(136,192,208,0.35);
  margin-bottom:8px;
}
.br-do-item strong{color:var(--txt-1);font-weight:600}
.br-do-item a{color:var(--accent);text-decoration:none}
.br-do-item a:hover{text-decoration:underline}

/* "Keep digging" — issue-guide chips ranked by this block's mix. */
.br-guides{margin:16px 0 4px}
.br-guides-kicker{
  font-family:var(--mono);font-size:9.5px;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;
  color:var(--txt-4);margin-bottom:9px;
}
.br-guides-row{display:flex;flex-wrap:wrap;gap:8px}
.br-guide{
  display:inline-flex;flex-direction:column;gap:2px;
  padding:9px 14px;border-radius:var(--r-md);
  background:var(--surf-2);border:1px solid var(--line-strong);
  text-decoration:none;
  transition:background .15s,border-color .15s;
}
.br-guide:hover{background:var(--surf-hi);border-color:rgba(136,192,208,0.55)}
.br-guide-l{font-size:13px;font-weight:600;color:var(--txt-1)}
.br-guide:hover .br-guide-l{color:var(--accent)}
.br-guide-n{
  font-family:var(--mono);font-size:9px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--txt-4);
}
.br-guide-all .br-guide-l{color:var(--txt-3)}
.br-guide-all:hover .br-guide-l{color:var(--accent)}

/* Onward guide link inside a noteworthy tile. */
.br-nw-link{
  display:inline-block;margin-top:8px;
  font-size:11.5px;font-weight:600;color:var(--accent);
  text-decoration:none;
}
.br-nw-link:hover{text-decoration:underline}

/* Block Report container.
   Desktop: a left-docked, full-height, OPAQUE panel over the sidebar
   zone — the live map stays visible (and interactive) to its right
   and takes over the old mini-map's job (search pin + ⅓-mile ring +
   report dots, drawn by brMapFocus in app.js). Opaque matters: the
   old centered modal stacked two backdrop-filter blurs (full-viewport
   scrim + 880px panel) that re-ran on every scroll frame — measured
   at ~10fps. No blur + no scrim = plain GPU scroll.
   Mobile: unchanged full-screen slide-over (rules in the max-width
   block below). */
.br-modal{
  position:fixed;inset:0;z-index:5000;
  display:none;
  pointer-events:none;      /* desktop: let the map beside the panel stay interactive */
}
.br-modal.show{display:block}
/* While the report is open, freeze the page behind it so a phone
   can't "scroll through" the modal into the map/splash underneath. */
html.br-lock,html.br-lock body{overflow:hidden}
.br-backdrop{
  position:absolute;inset:0;
  pointer-events:auto;
  background:rgba(4,6,12,0.78);
  backdrop-filter:blur(6px) saturate(120%);
  -webkit-backdrop-filter:blur(6px) saturate(120%);
}
.br-panel{
  position:absolute;
  pointer-events:auto;
  overflow:hidden;
  display:flex;flex-direction:column;
}
@media (min-width:769px){
  .br-backdrop{display:none}
  .br-panel{
    top:var(--mast-h);left:0;bottom:0;
    /* At least as wide as the sidebar it covers (incl. a user-resized
       one at ≥1400px), comfortable for the report column, never more
       than ~half the screen — the map keeps the rest. */
    width:min(56vw,max(620px,var(--panel-w,460px)));
    background:#0d111d;                 /* opaque — no backdrop-filter */
    border-right:1px solid var(--line-strong);
    box-shadow:14px 0 44px rgba(0,0,0,0.5);
    animation:br-dock-in .18s ease;
  }
  @keyframes br-dock-in{
    from{transform:translateX(-16px);opacity:0}
    to{transform:none;opacity:1}
  }
  /* The report owns the map while open: the mode pills / context box
     would fight the focused ⅓-mile view, and the standing legend
     describes the normal markers (size = severity) while the report
     dots use size = recency — rest all three. brMapFocus puts up its
     own legend (.br-flg). */
  html.br-focus .mode-bar,
  html.br-focus .ctx-box,
  html.br-focus .legend{display:none}
}
.br-panel::before{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:var(--specular);
}
.br-close{
  position:absolute;top:12px;right:12px;z-index:2;
  width:32px;height:32px;border-radius:50%;
  background:rgba(0,0,0,0.4);
  border:1px solid var(--line);
  color:var(--txt-2);font-size:20px;line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  transition:background .15s,color .15s;
}
.br-close:hover{background:rgba(248,81,73,0.18);color:var(--txt-1);border-color:rgba(248,81,73,0.3)}
/* Desktop: no backdrop to click and no obvious "modal" framing, so
   the close affordance carries a label — a pill, not a lone glyph. */
@media (min-width:769px){
  .br-close{
    width:auto;height:32px;gap:7px;
    padding:0 13px 0 11px;border-radius:16px;
    background:rgba(0,0,0,0.55);
    border-color:var(--line-strong);
  }
  .br-close::after{
    content:'Close';
    font-size:11.5px;font-weight:600;letter-spacing:.04em;
  }
}
.br-body{
  flex:1;min-height:0;overflow-y:auto;
  padding:28px 32px 32px;
}
/* Frame rate: while the report body is scrolling (html.br-scrolling,
   set by app.js) suspend the panel's backdrop blur — re-blurring the
   full panel every scroll frame roughly halved the frame rate, and
   the frost is imperceptible mid-scroll. Restored ~150ms after scroll
   stops, so the resting look is unchanged. (Only mobile still blurs;
   the desktop dock is opaque, so this is inert there.) */
html.br-scrolling .br-backdrop,
html.br-scrolling .br-panel{
  backdrop-filter:none !important;
  -webkit-backdrop-filter:none !important;
}
/* Desktop pointer to the live map (rendered in place of the old
   mini-map section — brRender only emits it on desktop). */
.br-map-note{
  display:flex;align-items:center;gap:10px;
  margin:0 0 22px;padding:10px 14px;
  background:rgba(255,62,165,0.05);
  border:1px solid rgba(255,62,165,0.22);
  border-radius:var(--r-sm);
  font-size:12.5px;color:var(--txt-3);line-height:1.5;
}
.br-map-note-dot{
  flex-shrink:0;width:10px;height:10px;border-radius:50%;
  border:2px solid #ff3ea5;background:rgba(255,62,165,0.2);
  box-shadow:0 0 6px rgba(255,62,165,0.5);
}
/* Floating severity legend on the main map while a report is open
   (Leaflet control, added by brMapFocus). Reuses .br-mlg-dot. */
.br-flg{
  /* Desktop-only (brMapFocus) and floats over the live map — solid,
     like the rest of the desktop over-map chrome, so nothing ghosts
     through while panning. */
  background:var(--surf-op-3);
  border:1px solid var(--line-strong);
  border-radius:var(--r-sm);
  box-shadow:var(--shadow-lg);
  padding:7px 12px;
  font-size:11px;color:var(--txt-3);
  display:flex;align-items:center;gap:9px;flex-wrap:wrap;
}
.br-flg-sep{color:var(--txt-4)}

/* Header */
.br-hdr{margin-bottom:18px;padding-right:32px}
.br-eyebrow{
  font-family:var(--mono);font-size:10.5px;font-weight:500;
  letter-spacing:.30em;text-transform:uppercase;
  color:var(--accent);margin-bottom:8px;
}
.br-addr{
  font-family:var(--display);font-optical-sizing:auto;
  font-size:30px;font-weight:900;color:var(--txt-1);
  letter-spacing:-0.02em;line-height:1.12;
}
.br-sub{font-size:13.5px;color:var(--txt-3);margin-top:6px}

/* Owner note — a personal message the site owner pins to the top of the
   Block Report for a small hand-picked set of addresses (brOwnerNote).
   Teal accent so it reads as an intentional note, not a data warning. */
.br-owner-note{
  margin:0 0 20px;padding:12px 15px;
  background:rgba(136,192,208,0.07);
  border:1px solid rgba(136,192,208,0.28);
  border-left:3px solid var(--accent);
  border-radius:var(--r-sm);
}
.br-owner-note-h{
  font-family:var(--mono);font-size:10.5px;font-weight:500;
  letter-spacing:.22em;text-transform:uppercase;
  color:var(--accent);margin-bottom:6px;
}
.br-owner-note p{
  margin:0;font-size:13px;color:var(--txt-2);line-height:1.55;
}
.br-owner-note a{color:var(--accent);font-weight:600}

/* Caveat (sparse data) */
.br-caveat{
  font-size:12px;color:var(--sev-2);
  background:rgba(227,179,65,0.08);
  border:1px solid rgba(227,179,65,0.2);
  border-radius:var(--r-sm);
  padding:8px 12px;margin-bottom:14px;
}

/* Hero block — sentence-as-headline. Paragraphs separate counts,
   severity rollup, and any narrative adds into distinct beats.
   Only the raw numbers carry color; descriptor words stay in
   body text so the eye lands on magnitude without the prose
   reading over-painted. */
.br-hero{
  font-size:16.5px;color:var(--txt-1);line-height:1.6;
  background:var(--surf-2);
  border:1px solid var(--line-strong);
  border-radius:var(--r-sm);
  padding:16px 18px;
  margin-bottom:22px;
}
.br-hero p{margin:0}
.br-hero p+p{margin-top:10px}
.br-hero-n{
  color:var(--accent);font-weight:700;
  font-variant-numeric:tabular-nums;
}

/* Quiet inline line for the "no reports at this exact address"
   state — replaces the old empty bordered card so the page
   doesn't waste vertical space on a null result. */
.br-noadd{
  font-size:13px;color:var(--txt-3);line-height:1.55;
  padding:10px 0;
  margin-bottom:8px;
  border-bottom:1px solid var(--line);
}

/* Section headings — each major section gets a solid top divider
   so the visual rhythm clearly separates ideas (noteworthy ≠ chronic
   neighbors ≠ map ≠ closest-most-relevant). */
.br-h{
  font-family:var(--mono);font-size:11.5px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;
  color:var(--txt-1);
  margin:24px 0 6px;
  padding-top:22px;
  border-top:2px solid rgba(255,255,255,0.18);
}
.br-h-sub{
  font-size:13px;color:var(--txt-3);margin-bottom:14px;line-height:1.55;
}

/* Shared dot used in at-address chips and elsewhere */
.br-dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}

/* At-this-address card — click to expand and reveal every report
   at the searched address. Closed: header + category chips.
   Open: header + chips + a divider line + the full report list. */
.br-atad{
  background:rgba(136,192,208,0.06);
  border:1px solid rgba(136,192,208,0.22);
  border-radius:var(--r-sm);
  margin-bottom:8px;
  overflow:hidden;
  transition:background .15s,border-color .15s;
}
.br-atad-summary{padding:12px 14px;cursor:pointer}
.br-atad-summary:hover{background:rgba(136,192,208,0.06)}
.br-atad.open{border-color:rgba(136,192,208,0.35)}
.br-atad-hdr{
  display:flex;align-items:center;gap:8px;
  font-size:11px;font-weight:600;color:var(--accent);
  text-transform:uppercase;letter-spacing:.08em;margin-bottom:6px;
}
.br-atad-caret{
  margin-left:auto;font-size:13px;color:var(--accent);
  transition:transform .2s;flex-shrink:0;
}
.br-atad.open .br-atad-caret{transform:rotate(180deg)}
.br-atad-row{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:13px;color:var(--txt-1)}
.br-atad-chip{display:inline-flex;align-items:center;gap:6px}
.br-atad-chip strong{color:var(--txt-1);font-variant-numeric:tabular-nums}
.br-atad-body{
  display:none;
  border-top:1px solid rgba(136,192,208,0.22);
  background:rgba(0,0,0,0.18);
  padding:12px 14px;
  cursor:default;
}
.br-atad.open .br-atad-body{display:flex;flex-direction:column;gap:10px}
.br-atad-body .br-row{background:var(--surf-2);border:1px solid var(--line)}

/* Potentially noteworthy — dashboard tile grid. Each tile leads
   with a giant count (the visual hit) and uses the category color
   as a top accent bar so the four tiles read as a coordinated
   panel. 2 columns by default; stacks to 1 on narrow screens. */
.br-nw-grid{
  display:grid;
  grid-template-columns:repeat(2,1fr);
  gap:10px;
}
/* Two-tier tinting. Both tiers use the same recipe: solid colored
   border + very-light wash of the same hue + number rendered in
   the border color. The two hues are tuned to sit next to each
   other without clashing (dusty pink + muted teal). */
.br-nw-card{
  background:rgba(142,195,211,0.05);
  border:1px solid #8EC3D3;
  border-radius:var(--r-sm);
  padding:16px 16px 14px;
  transition:background .15s,border-color .15s;
}
.br-nw-card:hover{
  background:rgba(142,195,211,0.10);
}
.br-nw-card .br-nw-num{color:#8EC3D3}

.br-nw-card:has(.br-nw-tag){
  background:rgba(143,71,101,0.06);
  border-color:#8f4765;
}
.br-nw-card:has(.br-nw-tag):hover{
  background:rgba(143,71,101,0.12);
}
.br-nw-card:has(.br-nw-tag) .br-nw-num{color:#b56b85}
.br-nw-card-top{
  display:flex;align-items:center;justify-content:space-between;
  gap:10px;margin-bottom:4px;
}
.br-nw-num{
  font-size:38px;font-weight:700;line-height:1;
  font-variant-numeric:tabular-nums;
  letter-spacing:-0.025em;
}
.br-nw-cat{
  font-size:14px;font-weight:600;color:var(--txt-1);
  margin-top:8px;line-height:1.3;
}
.br-nw-sev{
  font-size:12.5px;color:var(--txt-3);margin-top:4px;line-height:1.4;
}
.br-nw-tag{
  font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:#b56b85;
  background:rgba(143,71,101,0.12);
  border:1px solid #8f4765;
  border-radius:var(--r-pill);
  padding:3px 8px;
  flex-shrink:0;align-self:center;
}
@media (max-width:768px){
  /* Keep the dashboard read on phones — two compact tiles per row
     beats one tall full-width card with a giant number floating in
     empty space. Tighten padding and dial the count down so the
     pair fits without the category label wrapping awkwardly. */
  .br-nw-card{padding:13px 13px 12px}
  .br-nw-num{font-size:30px}
  .br-nw-card-top{flex-wrap:wrap;gap:6px}
  .br-nw-tag{align-self:flex-start}
}

/* Chronic-neighbor callout — same expandable pattern as at-address */
.br-cn-list{display:flex;flex-direction:column;gap:8px}
.br-cn{
  background:var(--surf-2);
  border:1px solid var(--line-strong);
  border-radius:var(--r-sm);
  overflow:hidden;
  transition:background .15s;
}
.br-cn-summary{padding:11px 14px 11px 16px;cursor:pointer}
.br-cn-summary:hover{background:var(--surf-hi)}
.br-cn-head{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
.br-cn-addr{font-size:14px;font-weight:600;color:var(--txt-1)}
.br-cn-dist{font-size:11.5px;color:var(--txt-3);font-variant-numeric:tabular-nums}
.br-cn-caret{
  margin-left:auto;font-size:14px;color:var(--accent);
  width:22px;height:22px;line-height:22px;text-align:center;
  border:1px solid var(--line-strong);border-radius:50%;
  transition:transform .2s,background .15s;
}
.br-cn-summary:hover .br-cn-caret{background:rgba(136,192,208,0.12)}
.br-cn.open .br-cn-caret{transform:rotate(180deg);color:var(--txt-1)}
.br-cn-meta{font-size:12.5px;color:var(--txt-2);margin-top:4px;line-height:1.5}
.br-cn-body{
  display:none;
  border-top:1px solid var(--line);
  background:rgba(0,0,0,0.18);
  padding:12px 14px;
  cursor:default;
}
.br-cn.open .br-cn-body{display:flex;flex-direction:column;gap:10px}
.br-cn-body .br-row{background:var(--surf-1);border:1px solid var(--line)}

/* Likely-same-building cards. Same structure as chronic-neighbor
   cards, with a different accent (cyan, matching the "this is
   about your specific location" affordance) and slightly looser
   tone since the relationship is heuristic. */
.br-sb-list{display:flex;flex-direction:column;gap:8px}
.br-sb{
  background:var(--surf-2);
  border:1px solid var(--line-strong);
  border-radius:var(--r-sm);
  overflow:hidden;
  transition:background .15s;
}
.br-sb-summary{padding:11px 14px 11px 16px;cursor:pointer}
.br-sb-summary:hover{background:var(--surf-hi)}
.br-sb-head{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
.br-sb-addr{font-size:14px;font-weight:600;color:var(--txt-1)}
.br-sb-dist{font-size:11.5px;color:var(--txt-3);font-variant-numeric:tabular-nums}
.br-sb-caret{
  margin-left:auto;font-size:14px;color:var(--accent);
  width:22px;height:22px;line-height:22px;text-align:center;
  border:1px solid var(--line-strong);border-radius:50%;
  transition:transform .2s,background .15s;
}
.br-sb-summary:hover .br-sb-caret{background:rgba(136,192,208,0.12)}
.br-sb.open .br-sb-caret{transform:rotate(180deg);color:var(--txt-1)}
.br-sb-meta{font-size:12.5px;color:var(--txt-2);margin-top:4px;line-height:1.5}
.br-sb-body{
  display:none;
  border-top:1px solid var(--line);
  background:rgba(0,0,0,0.18);
  padding:12px 14px;
  cursor:default;
}
.br-sb.open .br-sb-body{display:flex;flex-direction:column;gap:10px}
.br-sb-body .br-row{background:var(--surf-1);border:1px solid var(--line)}

/* Mini-map */
.br-map-wrap{
  background:var(--surf-2);
  border:1px solid var(--line);
  border-radius:var(--r-sm);
  overflow:hidden;
}
.br-map{
  width:100%;height:320px;
  background:#0a0d18;
}
.br-map-legend{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  font-size:11px;color:var(--txt-3);
  padding:8px 14px;
  border-top:1px solid var(--line);
}
.br-mlg-dot{
  display:inline-block;width:9px;height:9px;border-radius:50%;
  margin-right:3px;vertical-align:middle;
}
.br-map-detail{display:none;padding:10px 12px 12px;border-top:1px solid var(--line)}
.br-map-detail.show{display:block}
.br-map-detail .br-row{background:transparent;border:none;padding:0}
.br-map-detail .br-row-head{margin-bottom:5px}
/* Leaflet inside our dark modal — make controls readable */
.br-map .leaflet-control-zoom a{
  background:var(--surf-1)!important;color:var(--txt-2)!important;
  border:1px solid var(--line)!important;
}
.br-map .leaflet-control-zoom a:hover{background:var(--surf-hi)!important;color:var(--txt-1)!important}

/* Drill-down list — every row is click-to-expand. Closed: truncated
   description (clamped to ~3 lines). Open: full description. The
   click target is the whole row so the affordance is generous. */
.br-list{display:flex;flex-direction:column;gap:10px}
.br-row{
  background:var(--surf-2);
  border:1px solid var(--line);
  border-radius:var(--r-sm);
  padding:12px 14px;
  transition:background .12s,border-color .12s;
  position:relative;
}
.br-row[data-expand-toggle]{cursor:pointer}
.br-row[data-expand-toggle]:hover{background:var(--surf-hi);border-color:var(--line-strong)}
.br-row.open{background:var(--surf-hi);cursor:default}
.br-row-head{
  display:flex;flex-wrap:nowrap;align-items:center;gap:8px;
  margin-bottom:6px;
}
/* Chips strip — scrolls horizontally instead of wrapping so the
   meta + caret stay pinned to the right on one line, no matter
   how many chips a row has. */
.br-row-chips{
  display:flex;flex-wrap:nowrap;align-items:center;gap:6px 8px;
  flex:1 1 auto;min-width:0;
  overflow-x:auto;overflow-y:hidden;
  scrollbar-width:thin;scrollbar-color:var(--line-strong) transparent;
}
.br-row-chips::-webkit-scrollbar{height:4px}
.br-row-chips::-webkit-scrollbar-track{background:transparent}
.br-row-chips::-webkit-scrollbar-thumb{background:var(--line-strong);border-radius:2px}
.br-row-chips > *{flex-shrink:0}
.br-row-caret{
  font-size:11px;color:var(--txt-4);
  transition:transform .2s,color .2s;flex-shrink:0;
}
.br-row:hover .br-row-caret{color:var(--txt-2)}
.br-row.open .br-row-caret{transform:rotate(180deg);color:var(--txt-1)}
/* Closed: clamp description; Open: show full. Using a max-height
   approach (rather than -webkit-line-clamp alone) so the fade-out
   gradient overlays only when truncated. */
.br-row-desc{font-size:13.5px;color:var(--txt-2);line-height:1.6;position:relative}
.br-row:not(.open) .br-row-desc.clamped{
  max-height:4.8em;overflow:hidden;
}
.br-row:not(.open) .br-row-desc.clamped::after{
  content:'';position:absolute;left:0;right:0;bottom:0;height:1.6em;
  background:linear-gradient(180deg,rgba(22,28,44,0) 0%,rgba(22,28,44,0.95) 100%);
  pointer-events:none;
}
.br-row:hover:not(.open) .br-row-desc.clamped::after{
  background:linear-gradient(180deg,rgba(40,48,72,0) 0%,rgba(40,48,72,0.95) 100%);
}
.br-sev{
  font-size:10px;font-weight:600;letter-spacing:.04em;
  padding:2px 7px;border-radius:var(--r-sm);
}
.br-sev-1{background:rgba(86,211,100,0.14);color:var(--sev-1)}
.br-sev-2{background:rgba(227,179,65,0.14);color:var(--sev-2)}
.br-sev-3{background:rgba(219,109,40,0.14);color:var(--sev-3)}
.br-sev-4{background:rgba(248,81,73,0.18);color:var(--sev-4)}
.br-chip{
  font-size:11px;font-weight:500;
}
.br-chip-sub{
  color:var(--accent-warm);
  padding:1px 6px;
  background:rgba(240,136,62,0.08);
  border-radius:var(--r-sm);
  text-transform:capitalize;
}
.br-row-meta{
  font-size:11px;color:var(--txt-3);
  font-variant-numeric:tabular-nums;
  flex-shrink:0;white-space:nowrap;
}
.br-row-addr{font-size:12px;color:var(--txt-2);margin-bottom:4px;font-weight:500}
.br-row-desc{font-size:13px;color:var(--txt-2);line-height:1.55}
/* Placeholder shown while js/desc.json is still loading — a gently
   pulsing "Loading complaint details…" so an early-opened report reads
   as loading, not broken. Replaced by the real text on the re-render;
   never rendered once loading has terminally failed (DESC_FAILED). */
.br-row-desc-loading{color:var(--txt-4);font-style:italic;animation:br-desc-pulse 1.2s ease-in-out infinite}
@keyframes br-desc-pulse{0%,100%{opacity:.45}50%{opacity:.85}}
@media (prefers-reduced-motion:reduce){.br-row-desc-loading{animation:none}}

/* Empty state */
.br-empty{
  text-align:center;padding:32px 20px;color:var(--txt-3);
}
.br-empty-icon{font-size:32px;color:var(--txt-4);margin-bottom:8px}
/* Data-coverage caveat under the zero-report line: this dataset is
   Columbus-only, so an empty ⅓ mile often means "different city",
   not "clean address". */
.br-empty-note{
  margin:0 4px 18px;padding:12px 14px;
  border-left:3px solid var(--accent);border-radius:0 10px 10px 0;
  background:rgba(136,192,208,0.07);
  font-size:13px;line-height:1.55;color:var(--txt-2);
}

/* Footer methodology */
.br-foot{
  margin-top:22px;padding-top:14px;
  border-top:1px solid var(--line);
  font-size:11.5px;color:var(--txt-4);line-height:1.6;
}
.br-foot strong{color:var(--txt-3);font-weight:600}
.br-foot em{font-style:italic;color:var(--txt-3)}

/* Small viewports — matches the app-shell mobile breakpoint so a
   ~600-768px device doesn't get the phone layout with a desktop
   centered modal. */
@media (max-width:768px){
  /* dvh tracks the visual viewport so the report isn't taller than
     the screen (the bottom hiding behind the mobile URL bar). The
     glass look (translucent surface + backdrop blur) lives here now —
     the desktop dock is opaque for scroll performance. */
  .br-panel{
    top:0;left:0;right:0;width:100%;
    max-height:100vh;max-height:100dvh;
    height:100vh;height:100dvh;
    background:var(--surf-1);
    backdrop-filter:blur(24px) saturate(140%);
    -webkit-backdrop-filter:blur(24px) saturate(140%);
  }
  /* Tighter gutters reclaim ~8px of content width per side on a
     phone. Vertical padding moves onto the sticky header / first
     element so the header can sit flush at the top edge.
     Keep end-of-scroll from chaining into the frozen page behind. */
  .br-body{padding:0 14px 22px;overscroll-behavior:contain}
  .br-map{height:260px}
  /* Full-screen here means the backdrop is covered and there's no
     Esc key — the × is the only way out, so make it a real 44px
     touch target and drop it clear of the notch. */
  .br-close{
    top:calc(10px + env(safe-area-inset-top));
    right:calc(10px + env(safe-area-inset-right));
    width:44px;height:44px;font-size:24px;
  }
  /* Sticky mini-header — on a long report the address is the one
     piece of context you don't want to lose while scrolling, and
     the × is the only way out. Pin both to the top. Negative
     gutters let the bar span edge-to-edge over a padded body;
     z-index stays below .br-close (2) so the button sits on top. */
  .br-hdr{
    position:sticky;top:0;z-index:1;
    margin:0 -14px 16px;
    padding:calc(11px + env(safe-area-inset-top)) 58px 11px 14px;
    /* Opaque (not the .78-alpha surface var) so report content
       can't ghost through as it scrolls underneath. */
    background:var(--bg-base);
    border-bottom:1px solid var(--line-strong);
  }
  .br-eyebrow{margin-bottom:3px}
  .br-addr{font-size:20px;line-height:1.22}
  .br-sub{margin-top:3px}
  /* On touch, a horizontally-scrolling chip strip inside a
     vertically-scrolling modal is a trap — the 4px scrollbar is
     invisible and chips clip with no affordance. Let them wrap;
     meta + caret stay pinned right since the head itself doesn't. */
  .br-row-chips{flex-wrap:wrap;overflow:visible}
  .br-row-chips::-webkit-scrollbar{display:none}
  /* Methodology shouldn't be sub-legible on the device most people
     read this on. */
  .br-foot{font-size:12.5px}
}

/* Mobile segmented tabs — desktop keeps its single scrolling
   column, so the bar is hidden until the mobile breakpoint. */
.m-tabs{display:none}

/* =========================================================
   Mobile / small-tablet — full-screen slide-over panel
   The map is the primary surface; the filter/results panel
   slides in over it from a floating launcher and slides back
   out via an in-panel close button. Desktop is untouched.
   ========================================================= */
@media (max-width:768px){
  /* No desktop masthead on phones — the in-app top bar carries the
     brand there — so the app sizes against the full viewport. */
  /* --gutter: the one horizontal inset shared by the header padding,
     the on-map search, and the bottom sheet, so their contents ride
     the same left/right rails instead of three near-miss values. */
  :root{--mast-h:0px;--gutter:16px}
  /* dvh tracks the visual viewport so the mobile URL bar
     doesn't leave a dead strip under the map; the fixed mobile
     header (#mhead) owns the strip above. */
  #map{height:calc(100vh - var(--mhead-h));height:calc(100dvh - var(--mhead-h))}

  /* ---- Marker popups on touch ----
     The default Leaflet close affordance is a ~12px glyph in the
     corner — unhittable with a thumb. Enlarge it to a proper
     44px tap target, give the popup body breathing room around
     it, and cap the width so the card never crowds the screen.
     Tapping anywhere else on the map also closes it (Leaflet
     closeOnClick), so there are two clear ways out. */
  .cci-popup .leaflet-popup-content-wrapper{
    border-radius:14px!important;
  }
  .cci-popup .leaflet-popup-content{
    margin:16px 18px 18px!important;
    max-width:80vw!important;
  }
  .cci-popup .leaflet-popup-close-button{
    width:34px!important;
    height:34px!important;
    font:600 22px/34px Tahoma,Verdana,sans-serif!important;
    padding:0!important;
    top:4px!important;
    right:4px!important;
    color:var(--txt-2)!important;
    border-radius:50%!important;
  }
  .cci-popup .leaflet-popup-close-button:hover,
  .cci-popup .leaflet-popup-close-button:active{
    background:var(--surf-hi)!important;
    color:var(--txt-1)!important;
  }
  /* Keep the address clear of the enlarged close button. */
  .cci-popup .lp-addr{padding-right:38px}

  /* ---- Splash + scroll ----
     The app screen is FIXED to the viewport from boot; the splash is
     a scrollable layer that slides away OVER it. The document still
     scrolls (splash then the SEO footer, painted behind the fixed
     map), so scroll-to-dismiss keeps working — but removing the
     splash moves nothing: the map was already exactly where it will
     remain. Keeping the map out of the scroll flow is what holds
     CLS at ~0 (the old in-flow layout scored ~0.63 per session when
     the splash left the flow and the map jumped up a full screen —
     past the 500ms input exemption, and scroll dismissals were never
     exempt at all). The app screen is the positioning context for
     every overlay control, so they stay glued to the map screen. */
  html{scroll-behavior:smooth}
  html,body{height:auto}
  body{overflow:visible;overflow-x:hidden}
  /* Once the splash is swept, the app owns the viewport. Lock the
     document so a stray scroll can't drag the SEO footer (kept in
     the DOM for crawlers, but not a mobile scroll destination) up
     under the map. JS adds .app-locked the moment the splash
     dismisses; the panel / rail / mode-bar are absolutely
     positioned, so they're unaffected. */
  html.app-locked,html.app-locked body{height:100dvh;overflow:hidden}
  /* z-index:2 lifts the splash over the fixed app screen (z-index:1
     below); both are positioned, so DOM order would otherwise paint
     the later app screen on top. #mhead (z-index:1300) stays above
     both. */
  #splash{display:flex;z-index:2}
  /* One-shot dismissal: glide up + fade. JS then sets
     visibility:hidden — NOT display:none — so the splash keeps its
     box and nothing in the document reflows (a reflow here is the
     layout shift this layering exists to prevent). */
  #splash.swept{
    transform:translateY(-100%);opacity:0;pointer-events:none;
    transition:transform 520ms cubic-bezier(.65,.05,.78,.1),opacity 440ms ease;
    will-change:transform,opacity;
  }
  /* Fixed, not in flow — see the Splash + scroll comment above.
     top/bottom give the same box as the old
     min-height:calc(100dvh - var(--mhead-h)) in-flow block. */
  #appScreen{
    position:fixed;top:var(--mhead-h);left:0;right:0;bottom:0;
    z-index:1;
    --peek-h:88px;        /* visible height of the PEEK summary sheet */
  }
  /* Confine the vignette to the map screen (it's fixed by default,
     which would tint the splash too). */
  .map-vignette{position:absolute}

  /* ---- Map-first model ----
     The map is the home screen. The panel has two mobile states:

       PEEK (.P.hide)  — transparent passthrough. Only the search
                          bar shows, floating at the top over the
                          live map. The bottom browse bar (rail)
                          opens the list/filters. This is the home
                          screen: heatmap visible, search on top.
       FULL (no .hide)  — opaque full-screen list + filters, reached
                          by tapping the browse bar. Closes back to
                          PEEK (not off-screen).

     So .hide no longer means "gone" — it means "collapsed to just
     the on-map search bar." setPanelHidden(true) == go to PEEK. */
  .P{
    width:100vw;height:calc(100dvh - var(--mhead-h));
    border-right:none;
  }
  /* Map-first bottom sheet. PEEK shows only the summary slice (grab +
     severity tiles + a one-line context); the results body sits below
     the fold and is revealed by lifting to FULL. Rounded top + grab in
     both states so it always reads as a sheet over the live map. */
  .P,#panel:not(.hide){border-radius:18px 18px 0 0;overflow:hidden}
  .P.hide{transform:translateY(calc(100% - var(--peek-h)))}
  .P.hide::before{display:none}

  /* FULL: the close affordance is a full-width top grab handle —
     a grabber pill + a down chevron — so it clearly reads as
     "tap to send the sheet back down" rather than a dead-end ×.
     In PEEK it's hidden by the `> *` rule above. */
  .P-tog{
    position:relative;order:0;flex:0 0 auto;
    right:auto;top:auto;left:auto;
    width:100%;height:auto;
    display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:7px;
    padding:14px 0 14px;
    border:none;border-radius:18px 18px 0 0;
    border-bottom:1px solid var(--line);
    background:linear-gradient(180deg,var(--surf-2) 0%,transparent 100%);
    font-size:0;z-index:6;
    touch-action:none;  /* let the drag gesture drive the sheet, not the page */
  }
  .P-tog::before{
    content:'';width:44px;height:5px;border-radius:var(--r-pill);
    background:var(--line-strong);
  }
  .P-tog::after{
    content:'\2304';font-size:20px;line-height:.6;
    color:var(--txt-3);font-weight:700;
  }
  .P-tog:active::before{background:var(--txt-4)}
  /* In PEEK the grab is just a compact pill at the sheet top (no
     status-bar padding, no chevron — the pill alone reads as draggable). */
  .P.hide .P-tog{padding-top:11px;padding-bottom:7px}
  .P.hide .P-tog::after{display:none}

  /* Desktop-only resize affordances. */
  .panel-resize,.col-resize{display:none!important}

  /* The desktop collapsed-rail has no mobile role anymore: the PEEK
     sheet shows the count and is the way into the list, and the
     Filters entry lives inside the sheet header (.peek-filters) —
     not as a separate pill floating over the map. */
  .rail{display:none!important}

  /* PEEK sheet header — one row: count on the left, Filters chip on
     the right. The severity tiles/tabs belong to the lifted (FULL)
     state and are hidden here by the rules below. */
  #panel.hide .sev-row{
    display:flex;align-items:center;gap:10px;
  }
  #panel.hide .sev-row .ss-total{
    flex:1 1 auto;min-width:0;
    background:none;border:none;padding:2px 0 0;
  }
  #panel.hide .peek-filters{
    display:inline-flex;align-items:center;gap:8px;
    flex:0 0 auto;height:42px;padding:0 16px;
    border-radius:var(--r-pill);cursor:pointer;
    font:600 13.5px/1 inherit;font-family:inherit;letter-spacing:-0.1px;
    color:var(--txt-1);
    background:rgba(255,255,255,0.05);
    border:1px solid rgba(243,245,249,0.18);
  }
  #panel.hide .peek-filters:active{background:var(--surf-hi)}
  .peek-filters .pf-ic{display:inline-flex;width:17px;height:17px;color:var(--txt-2)}
  .peek-filters .pf-ic svg{width:100%;height:100%}
  /* Count badge — only rendered when filters are active (JS leaves
     it empty otherwise; no "0" noise). */
  .peek-filters .pf-count{
    display:inline-flex;align-items:center;justify-content:center;
    min-width:20px;height:20px;padding:0 6px;border-radius:999px;
    font-size:11.5px;font-weight:700;line-height:1;
    color:#06222b;background:var(--accent);
  }
  .peek-filters .pf-count:empty{display:none}
  .peek-filters.has-filters{
    border-color:rgba(136,192,208,0.55);
  }
  .peek-filters.has-filters .pf-ic,
  .peek-filters.has-filters .pf-label{color:var(--accent)}

  /* Severity row: the desktop 7-column grid (total · = · 4 tiles)
     is far too tight at phone width. Stack the total full-width
     above a 4-up tile row. */
  .sev-row{
    grid-template-columns:repeat(4,1fr);
    padding:12px var(--gutter) 12px;gap:7px;
  }
  .sev-row .ss-total{
    grid-column:1/-1;
    flex-direction:row;align-items:baseline;gap:8px;
  }
  .sev-row .ss-total .ss-l{margin-top:0}
  .sev-row .sev-eq{display:none}

  /* ---- Severity tiles — refined, low-chroma ----
     Neutral surface; a small dot carries the severity hue; the number
     sits in neutral ink and the label is muted. Reads calmer and more
     compact than the per-tile colour wash. */
  .sev-row .ss-n{font-size:20px}
  .sev-row .st[data-sev]{
    background:rgba(255,255,255,0.035);border-color:var(--line);
    padding:7px 10px 8px;border-radius:12px;
  }
  .sev-row .st[data-sev]:hover,
  .sev-row .st[data-sev]:focus-visible{
    background:rgba(255,255,255,0.06);border-color:var(--line-strong);
  }
  .sev-row .st[data-sev="4"]{--sc-dot:var(--sev-4)}
  .sev-row .st[data-sev="3"]{--sc-dot:var(--sev-3)}
  .sev-row .st[data-sev="2"]{--sc-dot:var(--sev-2)}
  .sev-row .st[data-sev="1"]{--sc-dot:var(--sev-1)}
  .sev-row .st[data-sev] .n{font-size:17px;color:var(--txt-1)}
  .sev-row .st[data-sev] .l{
    display:flex;align-items:center;gap:5px;
    margin-top:5px;font-size:9px;letter-spacing:.04em;color:var(--txt-3);
  }
  .sev-row .st[data-sev] .l::before{
    content:'';flex:0 0 auto;width:6px;height:6px;border-radius:50%;
    background:var(--sc-dot,var(--txt-3));
  }
  /* Active filter — neutral fill with a colored edge in the sev hue. */
  .sev-row .st[data-sev].on{background:rgba(255,255,255,0.06);border-color:var(--sc-dot);box-shadow:none}

  /* ---- Hero card on phones ----
     The hero's pitch/samples are desktop-only on phones; search
     lives in the persistent header's search state instead. */
  .hero-title,.hero-desc,.hero-example,.hero .SB{display:none!important}
  .hero{
    padding:0!important;background:transparent!important;
    border:none!important;box-shadow:none!important;margin:0!important;
  }

  /* While the splash is on screen the map peek stays calm — the
     hero search IS the search and the busy map chrome (top bar,
     view controls, browse bar) is hidden. Everything returns once
     the user engages and the hero sweeps. */
  html.hero-visible .mode-bar,
  html.hero-visible .map-search,
  html.hero-visible #ctxBox,
  html.hero-visible #rail,
  /* The PEEK sheet too: with the app screen viewport-fixed it would
     otherwise poke out at the bottom under the splash (in the old
     in-flow layout it sat below the fold, so it never needed
     hiding). */
  html.hero-visible .P{display:none!important}

  /* "What you're seeing" explainer is desktop-only now. On phones it
     hung behind a "?" in the map-view pill and dropped a popover over
     the map — an extra floating island that fought map gestures. The
     bottom Reports bar already states what's in view (citywide count /
     active filter / searched address), so the explainer and its "?"
     trigger are dropped on phones. */
  .ctx-box{display:none!important}

  /* Legend is desktop-only — on phones the severity colours read
     directly off the map and the report cards' severity tags. */
  #legend{display:none!important}

  /* Map-view toggle, restyled as one compact centered segmented
     pill (not a full-width 4-button toolbar). "View this area only"
     and its divider are gone — viewport filtering is automatic on
     phones, so the manual toggle would be redundant. */
  /* Pill, and the same translucent "liquid glass" as the bottom
     bar (not the opaque desktop surface). */
  .mode-bar{
    top:calc(58px + env(safe-area-inset-top));
    right:12px;left:auto;bottom:auto;transform:none;
    gap:2px;padding:4px 9px;border-radius:var(--r-pill);
    background:rgba(255,255,255,0.04);
    border:1px solid rgba(243,245,249,0.16);
    backdrop-filter:blur(24px) saturate(140%);
    -webkit-backdrop-filter:blur(24px) saturate(140%);
    box-shadow:var(--shadow-lg);
  }
  /* The "?" explainer sub-button (.mb-help) and its divider
     (.mbd-help) are gone on phones — the explainer is desktop-only
     now. Hiding them leaves a clean [Heatmap][Markers] pill. */
  .mode-bar-label,.mode-bar-divider,.mbd-help,.mb-help,.mb-vp,#mScat{display:none}
  .mb{padding:12px 15px;font-size:12px;border-radius:var(--r-pill)}

  /* Pinch-to-zoom covers this on touch, and at bottom-left the
     +/- control collided with the slide-over launcher. */
  #map .leaflet-control-zoom{display:none!important}
  /* Drop the Leaflet attribution tag on phones — no room for it. */
  #map .leaflet-control-attribution{display:none!important}

  /* FULL owns the screen — hide the floating map-view pill until it
     slides back to PEEK (.hide). */
  #panel:not(.hide) ~ .mode-bar{display:none}

  /* ---- On-map search (the one remaining map control) ----
     Replaces the old three-button stack (search / locate / layers):
     locate is redundant with the dropdown's "use my current location"
     row and map styles moved into the Filters sheet. The mode-bar
     buttons stay in the DOM (hidden) so setMode() still has them to
     flag active. */
  .mode-bar{display:none!important}
  /* Collapsed: a 56px glass circle at the top right — the map's
     single, primary call to action. Open (.open): a full-width
     opaque pill (same surface as its result sheet, so the input
     and the rows below read as one object at one contrast), with
     a round × sitting where the circle was. No animation — the
     swap is instant. */
  .map-search{
    display:flex;align-items:center;
    position:absolute;z-index:1000;
    top:12px;
    right:calc(var(--gutter) + env(safe-area-inset-right));
    left:calc(100% - var(--gutter) - env(safe-area-inset-right) - 56px);
    height:56px;border-radius:28px;
    color:var(--txt-1);
    background:rgba(12,15,26,0.62);border:1px solid rgba(243,245,249,0.22);
    box-shadow:var(--shadow-md);
    -webkit-backdrop-filter:blur(24px) saturate(140%);backdrop-filter:blur(24px) saturate(140%);
  }
  .map-search.open{
    left:calc(var(--gutter) + env(safe-area-inset-left));
    z-index:1100;
    /* Near-opaque (a touch past --surf-3): the cluster bubbles are
       high-chroma and ghost through 0.92 where backdrop blur isn't
       rendered, making the rows read as low-contrast. */
    background:rgba(28,34,52,0.97);border-color:var(--line-strong);
  }
  .map-search.open .DD{background:rgba(28,34,52,0.97)}
  /* While the result sheet is showing, square the pill's bottom
     corners so bar + sheet weld into one shape. (:has() is
     progressive enhancement — without it the sheet still sits
     flush underneath.) */
  .map-search.open:has(.DD.show){
    border-bottom-left-radius:0;border-bottom-right-radius:0;
  }
  .ms-toggle{
    flex:0 0 56px;height:100%;border:none;background:none;border-radius:inherit;
    display:flex;align-items:center;justify-content:center;
    color:inherit;cursor:pointer;
  }
  .ms-toggle svg{width:25px;height:25px}
  .map-search:not(.open) .ms-toggle:active{transform:scale(.95)}
  /* Open: the toggle demotes to a passive leading icon (tapping it
     just re-focuses the input via openTopSearch). */
  .map-search.open .ms-toggle{color:var(--txt-3)}
  .map-search.open .ms-toggle svg{width:19px;height:19px}
  .map-search input{
    flex:1 1 auto;min-width:0;background:none;border:none;outline:none;
    color:var(--txt-1);font-family:inherit;font-size:16px;
  }
  .map-search input::placeholder{color:var(--txt-3)}
  .ms-close{
    flex:0 0 auto;width:40px;height:40px;margin-right:8px;border-radius:50%;
    display:flex;align-items:center;justify-content:center;cursor:pointer;
    background:rgba(255,255,255,0.06);border:1px solid rgba(243,245,249,0.2);
    color:var(--txt-2);
  }
  .ms-close:active{background:var(--surf-hi);color:var(--txt-1)}
  /* display:none (not opacity) while collapsed so the 56px circle has
     no hidden flex content. */
  .map-search:not(.open) input,
  .map-search:not(.open) .ms-close{display:none}
  /* Result sheet — welded to the pill: no gap, borders overlapped
     1px so they read as one outline, a hairline (not the strong
     rule) dividing input from rows, bottom corners finishing the
     combined shape. Base .DD already carries the same surface. */
  .map-search .DD{
    top:calc(100% - 1px);left:-1px;right:-1px;z-index:1002;
    border-radius:0 0 18px 18px;
    border-top:1px solid var(--line);
    max-height:min(340px,60dvh);
  }
  /* Rows inside the sheet belong to it — no raised-card background,
     hairline separators, roomier touch padding. Trailing marks
     (chevron, count badge) sit in a fixed 40px column whose center
     matches the × button's center (content-right − 9px padding −
     20px half-column = the × circle's midpoint), so the right rail
     is one straight line down the sheet. */
  .map-search .DD > button{padding:13px 9px 13px 16px;font-size:14px}
  .map-search .DD .dd-geo-chev{
    width:40px;flex:0 0 40px;
    display:flex;align-items:center;justify-content:center;
  }
  /* Count badge: absolutely centered in the same 40px column — a
     float would drop below the text line on long addresses. */
  .map-search .DD > button[data-a]{position:relative;padding-right:49px}
  .map-search .DD > button[data-a] .ct{
    float:none;position:absolute;right:9px;top:50%;
    transform:translateY(-50%);
    width:40px;text-align:center;
  }
  /* Leading icon column mirrors the 56px search-icon column: 26px
     box centered at the search glyph's midpoint, 15px gap landing
     the label text where the input text starts. */
  .map-search .DD .dd-geo{background:none;border-top:1px solid var(--line);gap:15px}
  .map-search .DD .dd-geo-icon{
    width:26px;flex:0 0 26px;
    display:flex;align-items:center;justify-content:center;
  }
  .map-search .DD .dd-geo strong{font-size:14px}
  /* "Use my current location": one compact line — the label carries
     the meaning, the two-line pitch card was tall and redundant next
     to the input. */
  .map-search .DD .dd-loc{border-top:none;border-bottom:1px solid var(--line)}
  .map-search .DD .dd-loc .dd-geo-sub{display:none}
  /* Keep the accent wash when the geocode row is the only option. */
  .map-search .DD .dd-geo-primary{background:rgba(136,192,208,0.14)}
  /* Hide the on-map search when the panel is FULL. */
  #panel:not(.hide) ~ .map-search{display:none}

  /* ---- Filter chips ----
     At half-width on a phone the desktop chips truncated their
     labels ("Mgmt Negl…") and were too short to tap comfortably.
     Let labels wrap to two lines, center them, and grow every
     chip to a ~44px touch target. */
  .fb{
    white-space:normal;overflow:visible;text-overflow:clip;
    display:flex;align-items:center;justify-content:center;gap:7px;
    min-height:44px;padding:8px 14px;
    font-size:13px;line-height:1.25;
  }
  .fb[data-has-subs]{padding-left:28px;padding-right:28px}
  /* Reveal the leading category symbol on phones (hidden on desktop). */
  .fb-ic{
    display:inline-flex;align-items:center;justify-content:center;
    width:17px;height:17px;flex:0 0 auto;
  }
  .fb-ic svg{width:100%;height:100%;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;opacity:.85}
  .fb-l{flex:0 1 auto;min-width:0}
  .FB,.sub-btns.show,.pest-sub.show{gap:7px}
  /* Sub-filter chips: same treatment at a slightly smaller scale
     so the parent/child hierarchy still reads. */
  .sub-btns .fb,.pest-sub .fb{
    font-size:12px;min-height:38px;padding:7px 10px;
  }

  /* ---- Map style (mobile-only, top of the Filters body) ----
     Home of the Heatmap/Scatter/Markers switch since the on-map
     layers popover was removed; hidden on desktop (the mode-bar
     covers it there). Three equal chips, single-select. */
  /* display:flex, not block — .sec centers its label via
     justify-content, and block override left-aligned this one. */
  .map-style-sec{display:flex}
  .map-style-row{display:grid;grid-template-columns:repeat(3,1fr)}
  .map-style-row .fb{min-height:40px;padding:7px 8px}
  /* Neutralize the generic FB odd-last-child span — three chips,
     three columns, no full-width Markers. */
  .map-style-row > .fb:last-child:nth-child(odd){grid-column:auto}

  /* ---- Date controls ----
     Six month columns were tiny; drop to four and enlarge the
     preset/month hit targets. */
  .mo-grid{grid-template-columns:repeat(4,1fr);gap:6px}
  .mo{padding:10px 0;font-size:12.5px}
  .date-presets{gap:6px}
  .date-presets .pre{padding:8px 13px;font-size:12px}

  /* ---- Results / Filters toggle ----
     In FULL the body can't stack the list and the filter panel at
     once on a phone, so this 2-segment control switches which one
     fills the body beneath the persistent search + status. */
  .m-tabs{
    display:flex;gap:6px;
    flex:0 0 auto;
    padding:4px 14px 12px;
  }
  /* Single toggle: only the view you're NOT on is shown, full
     width — tapping it flips there. The current view's tab is
     hidden (it'd just be a no-op). */
  .m-tab{
    flex:1;padding:13px 8px;
    font:inherit;font-size:14px;font-weight:600;
    color:var(--txt-2);
    background:var(--surf-2);
    border:1px solid var(--line-strong);border-radius:var(--r-pill);
    cursor:pointer;outline:none;
  }
  .m-tab:active{background:var(--surf-hi)}
  .m-tab.on{display:none}

  /* ---- FULL panel layout ----
     Search is the on-map button, not a panel header, so the hero is
     hidden in FULL. flex `order` lays the column out as:
       grab handle · status · active filters · toggle · body
     (the desktop masthead is display:none on mobile already). */
  #panel:not(.hide) .hero{display:none}
  /* flex:none on the fixed rows: the panel is a flex column and
     .panel-cols' natural content height (the full filter list or
     results list) exceeds the screen, so without it the flex
     algorithm crushes these rows to their min-height and their
     content (e.g. the active-filter chips) paints outside the box. */
  .sev-row{order:2;flex:0 0 auto}
  /* Same order as .sev-row: ties resolve by DOM order, which puts
     the pills directly beneath the count row in PEEK. */
  .peek-pills{order:2;flex:0 0 auto}
  .active-bar{order:3;flex:0 0 auto}
  #mTabs{order:4}
  .panel-cols{order:5;flex:1 1 auto;min-height:0}

  /* Body toggle: Results by default, Filters slides over it.
     Both panes carry flex:1 from the desktop rules, so whichever
     is shown fills the leftover height and scrolls internally. */
  #panel .FS{display:none}
  #panel.m-filters .FS{display:block}
  #panel.m-filters .panel-results{display:none}
  #panel:not(.m-filters) .panel-results{display:flex}
  /* PEEK shows only the headline count ("N reports showing"). The
     severity tiles, Results/Filters tabs and active-filter strip all
     belong to the swiped-up (FULL) state; the floating Filters pill is
     the peek's single filter entry. */
  #panel.hide #mTabs,#panel.hide .active-bar,#panel.hide .sev-row .st{display:none}
  /* Tighter count row at peek (no tiles to balance against). */
  #panel.hide .sev-row{padding:6px var(--gutter) 12px}

  /* ---- PEEK filter pills ----
     When filters are active, the peek bar grows a second row: the
     active-filter pills (each tap removes that one filter) plus a
     trailing Clear all — so filters clear right from the map view
     without lifting the sheet. Rendered by renderRailChips();
     #appScreen.has-peek-pills raises --peek-h to make room, which
     the sheet transform + drag code pick up automatically. */
  #appScreen.has-peek-pills{--peek-h:134px}
  #panel.hide .peek-pills{
    display:flex;align-items:center;gap:8px;
    padding:0 calc(var(--gutter) + 24px) 12px var(--gutter);
    overflow-x:auto;-webkit-overflow-scrolling:touch;
    scrollbar-width:none;
    /* Right-edge fade hints that a long set scrolls; the extra
       right padding lets the last pill clear the fade. */
    -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 24px),transparent);
    mask-image:linear-gradient(90deg,#000 calc(100% - 24px),transparent);
  }
  #panel.hide .peek-pills::-webkit-scrollbar{display:none}
  /* FULL state keeps the classic active-bar; this row is peek-only. */
  #panel:not(.hide) .peek-pills{display:none}
  .ppill{
    flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;
    height:34px;padding:0 12px 0 14px;border-radius:var(--r-pill);
    border:1px solid rgba(136,192,208,0.45);background:rgba(136,192,208,0.10);
    color:var(--txt-1);font:600 12.5px/1 inherit;font-family:inherit;
    cursor:pointer;white-space:nowrap;
  }
  .ppill:active{background:rgba(136,192,208,0.2)}
  .ppill .ppl{max-width:180px;overflow:hidden;text-overflow:ellipsis}
  .ppill .ppx{
    display:inline-flex;align-items:center;justify-content:center;
    width:16px;height:16px;border-radius:50%;
    background:rgba(255,255,255,0.12);color:var(--txt-2);
    font-size:12px;line-height:1;
  }
  .ppill.pp-clear{
    border-color:rgba(243,245,249,0.25);background:rgba(255,255,255,0.05);
    color:var(--txt-2);padding:0 14px;
  }
  .ppill.pp-clear:active{background:var(--surf-hi)}

  .panel-resize{display:none}

  /* ---- Consistent rounding inside FULL ----
     Carry the round shape language of the top/bottom controls into
     the panel: pill-shaped buttons, generously rounded cards/tiles.
     No more mixed sharp/round treatments. */
  .sev-row .st{border-radius:16px}
  .fb,.hs-btn,.r-hdr-btn,.load-more,.date-presets .pre{
    border-radius:var(--r-pill)!important;
  }
  .addr-group,.cl-card,.mo{border-radius:16px!important}

  /* ---- Active-filter bar ----
     Stack it cleanly so multi-line pills + "Clear All" never
     overlap: label on its own line, pills wrap full-width below,
     Clear All on its own trailing line. */
  /* Label on its own line; below it the chips and the circular
     clear X share one row. Chips scroll horizontally (and cut off)
     when there are more than fit — the X stays pinned inline. */
  .active-bar.has-filters{
    align-items:center;flex-wrap:wrap;
    margin:0 16px 12px;padding:11px 13px;gap:9px;
    border-radius:var(--r-lg);
  }
  .active-bar .ab-label{flex:0 0 100%}
  .active-bar .pills{
    flex:1 1 0;min-width:0;gap:7px;
    flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;
    -webkit-overflow-scrolling:touch;scrollbar-width:none;
  }
  .active-bar .pills::-webkit-scrollbar{display:none}
  .active-bar .pill{font-size:11.5px;flex:0 0 auto}
  .active-bar .pill .pl{max-width:56vw}
  .active-bar .clear-btn{flex:0 0 auto;margin:0}

  /* "All Time" / section state value — was a tiny 10px mismatch
     next to the 16px section title. */
  .sec-toggle .sec-state{
    font-size:13px;font-weight:600;color:var(--txt-2);
  }
}

/* =========================================================
   SEO / informational footer.
   Real, accurate content for crawlers and for anyone who
   scrolls to the very bottom. Deliberately low-prominence:
   it sits below the full-height app, muted and quiet, so it
   never competes with the live map.
   ========================================================= */
.seo{
  border-top:1px solid var(--line);
  background:var(--bg-deep);
  color:var(--txt-3);
  font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  padding:56px 24px 72px;
  /* Skip layout/paint of this ~3000px below-the-fold block during
     boot; the browser renders it when it approaches the viewport. */
  content-visibility:auto;
  contain-intrinsic-size:auto 2800px;
}
.seo-wrap{max-width:760px;margin:0 auto}
.seo-block{margin:0 0 40px}
.seo-block:last-child{margin-bottom:0}
.seo h2{
  font-family:'Fraunces',Georgia,serif;font-optical-sizing:auto;
  font-size:26px;font-weight:600;color:var(--txt-1);
  margin:0 0 14px;letter-spacing:-0.02em;line-height:1.05;
}
.seo h3{
  font-size:15px;font-weight:600;color:var(--txt-2);
  margin:22px 0 6px;
}
.seo p{
  font-size:14px;line-height:1.65;color:var(--txt-3);
  margin:0 0 10px;
}
.seo-steps{
  margin:6px 0 0;padding-left:20px;
  font-size:14px;line-height:1.65;color:var(--txt-3);
}
.seo-steps li{margin:0 0 8px}
.seo strong{color:var(--txt-2);font-weight:600}
.seo-disclaimer{
  margin-top:22px;font-size:12px;color:var(--txt-4);
  border-top:1px solid var(--line);padding-top:16px;
}

/* Rich site-navigation footer — mirrors the neighborhoods page footer
   so the map page links out to the area/neighborhood directory. */
.seo-foot{
  display:grid;grid-template-columns:1.4fr repeat(5,1fr);gap:30px;
  margin-top:48px;padding-top:36px;border-top:1px solid var(--line);
}
.seo-foot-fw{
  font-family:'Fraunces',Georgia,serif;font-optical-sizing:auto;
  font-weight:900;font-size:24px;letter-spacing:-0.02em;line-height:1;color:var(--txt-1);
}
.seo-foot-ft{margin-top:12px;font-size:13px;color:var(--txt-3);max-width:32ch;line-height:1.55}
.seo-foot-col h4{
  font-size:11px;letter-spacing:0.16em;text-transform:uppercase;
  color:var(--txt-4);margin:0 0 14px;font-weight:700;
}
.seo-foot-col a{
  display:block;font-size:14px;padding:5px 0;color:var(--txt-2);text-decoration:none;
  transition:color .15s,padding-left .15s;
}
.seo-foot-col a:hover{color:var(--accent);padding-left:4px}
.seo-foot-col a.muted{color:var(--txt-4)}
.seo-foot-bar{
  display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;
  margin-top:36px;padding-top:18px;border-top:1px solid var(--line);
  font-size:11px;letter-spacing:0.04em;color:var(--txt-4);text-transform:uppercase;
}
.seo-foot-bar a{color:inherit;text-decoration:none;border-bottom:1px solid var(--line)}
.seo-foot-bar a:hover{color:var(--txt-2)}
@media (max-width:760px){.seo-foot{grid-template-columns:1fr 1fr;gap:28px}}
@media (max-width:480px){.seo-foot{grid-template-columns:1fr}}

/* =========================================================
   /about.html + /contact.html — standalone pages. Reuse .seo for
   body type; the header is the standard full-density site masthead
   (.site-chrome.sc-full, defined with the chrome block above).
   ========================================================= */
.about-body{
  /* The map app locks the document (body{overflow:hidden;height:100%}).
     The About page is a normal scrolling document, so undo both. */
  margin:0;height:auto;min-height:100dvh;overflow:visible;
  background:var(--paper);color:var(--txt-2);
  font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
}
.about-main{
  border-top:0;
  padding-bottom:calc(72px + env(safe-area-inset-bottom));
  padding-left:calc(24px + env(safe-area-inset-left));
  padding-right:calc(24px + env(safe-area-inset-right));
}
/* Footer on the About page: .seo already draws the top rule, so the
   inner grid drops its own leading rule/margins. */
.about-foot .seo-foot{margin-top:0;padding-top:0;border-top:none}
.about-main h1{
  font-family:'Fraunces',Georgia,serif;font-optical-sizing:auto;
  font-size:38px;font-weight:600;color:var(--txt-1);
  margin:0 0 14px;letter-spacing:-0.025em;line-height:1.02;
}
/* In-text links in the body (not the contact list, which is styled
   separately and more specifically). Default UA blue reads badly on
   the dark surface. */
.about-main p a,.about-main .about-list a{
  color:var(--accent);text-decoration:none;
}
.about-main p a:hover,.about-main .about-list a:hover{text-decoration:underline}
/* Contact page's per-route mailto links: a weighted line, not a button,
   so the page stays a reading page. */
.about-main .about-cta{font-weight:600}
/* /privacy — effective-date line + the analytics switch */
.about-main .pv-date{font-family:'JetBrains Mono',ui-monospace,monospace;font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:var(--txt-4);margin:-6px 0 18px}
.pv-switch{border:1px solid var(--line);padding:18px 20px;margin:0 0 18px;background:rgba(255,255,255,.03)}
.pv-status{margin:0 0 12px;font-weight:600}
.pv-btn{font:inherit;font-weight:600;font-size:14px;padding:10px 16px;border:1px solid var(--accent);border-radius:6px;background:transparent;color:var(--accent);cursor:pointer}
.pv-btn:hover,.pv-btn:focus-visible{background:var(--accent);color:#101218;outline:none}
.about-main code{
  font-family:var(--mono);font-size:.9em;color:var(--txt-1);
  background:var(--surf-2,rgba(255,255,255,.06));padding:1px 5px;border-radius:4px;
}
.about-list{
  margin:8px 0 12px;padding-left:20px;
  font-size:14px;line-height:1.6;color:var(--txt-3);
}
.about-list li{margin:0 0 9px}
.about-contact{list-style:none;margin:0 0 14px;padding:0}
.about-contact li{
  display:flex;align-items:baseline;gap:12px;
  padding:8px 0;border-bottom:1px solid var(--line);font-size:14px;
}
.about-contact li:first-child{border-top:1px solid var(--line)}
.about-contact-k{
  flex:0 0 72px;font-size:11px;font-weight:700;letter-spacing:0.08em;
  text-transform:uppercase;color:var(--txt-4);
}
.about-contact a{color:var(--accent);text-decoration:none;word-break:break-word}
.about-contact a:hover{text-decoration:underline}
/* Fast-facts variant of the contact list: wider key column for longer
   labels ("Address lookup", "Data window"), muted running values. */
.about-facts .about-contact-k{flex:0 0 118px}
.about-facts li span+span{color:var(--txt-3);line-height:1.5}
@media (max-width:640px){
  .about-main{
    padding-top:38px;
    padding-bottom:calc(56px + env(safe-area-inset-bottom));
    padding-left:calc(18px + env(safe-area-inset-left));
    padding-right:calc(18px + env(safe-area-inset-right));
  }
  .about-main h1{font-size:21px}
  .seo-block{margin-bottom:32px}
  /* Stack the contact label above its value so long emails never
     get crushed into a 72px column on narrow phones. */
  .about-contact li{flex-direction:column;align-items:flex-start;gap:3px}
  .about-contact .about-contact-k{flex:none} /* outranks .about-facts 118px basis */
}

/* ============================================================
   Tap-target hit-area expansion
   Several small controls (the splash eyebrow links and the
   map-mode toggles) render shorter than the 44px touch-target
   guideline. An invisible ::after grows each one's *touchable*
   area to ≥44px tall without changing the painted control.
   Growth is vertical only — these sit in horizontal rows, so a
   wider hit area would steal taps from neighbours.
   ============================================================ */
.mb{position:relative}
.mb::after{
  content:"";position:absolute;left:0;right:0;top:50%;
  transform:translateY(-50%);height:100%;min-height:44px;
}

/* ============================================================
   Persistent mobile header (#mhead)
   The phone-density masthead — fixed across the splash AND the
   live map so the brand never disappears. One brand state on both
   screens: the Know Your Block wordmark on a single line with the
   nav. On the map screen a search button expands the row into a
   full-width input (#mhead.search).
   Desktop uses the condensed masthead instead (display:none here).
   ============================================================ */
#mhead{display:none}

@media (max-width:768px){
  /* Header height: 3px topband + 44px row (+ the notch inset). The
     splash, map and panel all size against this. */
  :root{--mhead-h:calc(47px + env(safe-area-inset-top))}
  body{padding-top:var(--mhead-h)}
  /* about.html / contact.html / check-before-you-rent.html load this
     stylesheet too, but have no fixed #mhead — their masthead is
     in-flow. Without this reset they render a blank --mhead-h strip
     above the topband. */
  body.about-body{padding-top:0}
  #mhead{
    position:fixed;top:0;left:0;right:0;z-index:1300;
    display:flex;align-items:center;gap:12px;
    height:var(--mhead-h);
    /* No +3px here: the 3px topband is the border, which the
       border-box height already accounts for — adding it again as
       padding pushed the whole row ~1.5px below optical center. */
    padding:env(safe-area-inset-top) calc(var(--gutter) + env(safe-area-inset-right)) 0 calc(var(--gutter) + env(safe-area-inset-left));
    background:var(--paper);
    border-top:3px solid var(--accent);
    border-bottom:1px solid var(--chrome-rule);
  }
  .mh-brand{
    flex:1 1 auto;min-width:0;
    display:flex;flex-direction:column;justify-content:center;
    text-decoration:none;overflow:hidden;
  }
  .mh-word{
    display:block;
    font-family:var(--display);font-optical-sizing:auto;font-weight:900;
    font-size:clamp(14px,4vw,15.5px);letter-spacing:-.01em;line-height:1;
    color:var(--chrome-ink);white-space:nowrap;
  }
  /* Nav face matches the masthead nav (sans, not mono — mono is
     reserved for kickers/metabar) and stays narrow enough that the
     wordmark + nav + search button fit a 360px row. */
  /* Baseline nudge: align-items:center centers the two text BOXES,
     but side-by-side type at different sizes only reads as aligned
     when the BASELINES match — and the links' underline slot
     (2px padding + 1.5px border, below the text) hoists their text
     off-center. translateY re-seats the nav text on the wordmark's
     baseline (verified against the rendered header at 320–430px). */
  .mh-nav{display:flex;align-items:center;gap:10px;flex:0 0 auto;transform:translateY(3.5px)}
  .mh-nav a{
    position:relative;
    font-family:'Hanken Grotesk',system-ui,sans-serif;
    font-size:10px;font-weight:600;
    letter-spacing:.04em;text-transform:uppercase;
    color:rgba(243,245,249,0.72);text-decoration:none;
    white-space:nowrap;padding-bottom:2px;
    border-bottom:1.5px solid transparent;
  }
  .mh-nav a:active{opacity:0.7;border-bottom-color:var(--accent)}
  /* ≥44px tap target without changing the painted link */
  .mh-nav a::after{
    content:"";position:absolute;left:-5px;right:-5px;top:50%;
    transform:translateY(-50%);height:100%;min-height:44px;
  }
}
/* Small phones: tighten the shared gutter + the header row so
   wordmark + nav never crowd each other. The gutter override also
   pulls the on-map search and sheet padding in with it. */
@media (max-width:374px){
  :root{--gutter:12px}
  #mhead{gap:10px}
  .mh-nav{gap:9px}
  .mh-nav a{font-size:9px;letter-spacing:.02em}
}
