/* ==========================================================================
   The full-screen map.

   Every other page on this site is a document with a map in it. This one is a
   map with the document folded into it, and that inversion drives every
   decision below.

   Three rules hold the layout together:

     1. The map is the page. It fills the viewport — 100dvh, not 100vh, so the
        bottom of the sheet isn't parked under a phone's address bar — and
        every control floats over it. Nothing scrolls except the panel.

     2. Controls sit on glass, not on the map. A translucent dark panel with a
        blur behind it stays legible over satellite imagery, dark tiles and
        white parcel fill alike, which no solid colour manages.

     3. The panel is the accessible copy. Everything the markers show is also
        listed as text, sorted by distance from the centre of the screen, and
        the skip link goes there first. A map you cannot see is not an excuse
        for data you cannot reach.

   Mobile is the default here and desktop is the media query, not the other way
   round: the sheet is a bottom sheet with three snap points, and only at 900px
   does it dock to the side.
   ========================================================================== */

/* ---- Page ---------------------------------------------------------------- */

body.explore-body {
  /* The map owns the scroll. A page-level scrollbar over a fixed map is the
     classic full-screen-map bug: you drag to pan and the document moves. */
  overflow: hidden;
  overscroll-behavior: none;
  background: var(--bg-ink);
  color: var(--text-on-ink);
}

.explore {
  position: fixed;
  inset: 0;
  overflow: hidden;

  /* Read by the chrome for its offsets, and by the sheet for its snap points.
     JS writes --sheet-h; everything else is static. */
  --chrome-top: calc(var(--space-3) + env(safe-area-inset-top, 0px));
  --chrome-side: calc(var(--space-3) + env(safe-area-inset-left, 0px));
  --glass: rgba(13, 19, 34, 0.82);
  --glass-strong: rgba(10, 15, 28, 0.94);
  --glass-line: rgba(255, 255, 255, 0.11);
  --glass-line-strong: rgba(255, 255, 255, 0.2);

  /* --sheet-h is the sheet's live height and moves with the drag.
     --peek-h is the collapsed height and does NOT move. Everything else
     floating at the bottom of the map — the rail, the scale bar, the
     attribution — is pinned to --peek-h rather than --sheet-h, so dragging the
     sheet up covers them instead of shoving them into the filter chips. That
     is what a native map app does, and it is the only arrangement where the
     rail cannot collide with the chrome at the top. */
  --sheet-h: 96px;
  --peek-h: 96px;
}

.explore__map {
  position: absolute;
  inset: 0;
  background: var(--bg-ink);
  z-index: 0;
}

/* Leaflet's own controls are replaced by the floating rail. The scale bar and
   attribution stay — one is useful, the other is a licence condition. */
.explore__map .leaflet-control-zoom {
  display: none;
}

.explore__map .leaflet-control-attribution {
  background: rgba(10, 15, 28, 0.72) !important;
  color: var(--text-on-ink-muted) !important;
  border-radius: var(--radius-sm) 0 0 0;
  padding: 2px 6px !important;
  backdrop-filter: blur(6px);
}

.explore__map .leaflet-control-attribution a {
  color: var(--teal-bright) !important;
}

.explore__map .leaflet-control-scale-line {
  background: rgba(10, 15, 28, 0.66);
  border-color: var(--glass-line-strong);
  color: var(--text-on-ink);
  backdrop-filter: blur(6px);
}

/* The scale bar and the attribution both live along the bottom, which on a
   phone is where the sheet is. Lift them to sit on top of the collapsed sheet.
   Attribution is a licence condition, so it has to be visible in the map view
   — which it is, in the default state. Dragging the sheet up over it is the
   same behaviour every native map app has. */
.explore__map .leaflet-bottom.leaflet-left,
.explore__map .leaflet-bottom.leaflet-right {
  bottom: calc(var(--peek-h) + 2px + env(safe-area-inset-bottom, 0px));
}

/* ---- Glass ---------------------------------------------------------------
   One recipe, reused. `backdrop-filter` is progressive: where it isn't
   supported the panel is simply more opaque, which is a fine outcome. */

.glass {
  background: var(--glass);
  border: 1px solid var(--glass-line);
  border-radius: var(--radius-lg);
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.45), 0 1px 0 rgba(255, 255, 255, 0.05) inset;
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass {
    background: var(--glass-strong);
  }
}

/* ---- Chrome column --------------------------------------------------------
   The top bar, the chips and the status line, stacked in flow. Laying them out
   rather than positioning each one means the phone's two-row top bar pushes
   the chips down by itself, with no magic offsets to keep in sync.

   The column does not take pointer events; its children do. Otherwise the
   transparent gaps between them would swallow drags on the map underneath. */

.explore__chrome {
  position: absolute;
  top: var(--chrome-top);
  left: var(--chrome-side);
  right: var(--chrome-side);
  z-index: 620;

  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2);
  pointer-events: none;
}

.explore__chrome > * {
  pointer-events: auto;
}

/* ---- Top bar -------------------------------------------------------------- */

.explore__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  /* Flex children default to min-width:auto and refuse to shrink below their
     content. Without this the search field pushes the bar wider than the
     phone and the whole page gains a horizontal scrollbar. */
  min-width: 0;
}

.explore__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
  padding: 4px var(--space-2) 4px 4px;
  border-radius: var(--radius-md);
  color: var(--text-on-ink);
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: var(--text-sm);
  font-stretch: 88%;
  letter-spacing: var(--tracking-tight);
}

.explore__brand:hover {
  background: rgba(255, 255, 255, 0.07);
}

.explore__brand svg {
  display: block;
  width: 26px;
  height: 26px;
  color: var(--teal-bright);
  filter: drop-shadow(0 0 6px rgba(0, 184, 189, 0.5));
}

.explore__brand-name {
  white-space: nowrap;
}

/* The full name is longer than the abbreviation it replaced, and on this bar
   that is not free: at 390px and below it pushed the Report button onto a
   second row, taking the header from 102px to 148px. This page's whole
   argument is that chrome eating a third of a phone screen is chrome stealing
   map, so buy the row back by setting the name a size down until there is
   room for it. It stays legible and it stays the full name. */
@media (max-width: 429px) {
  .explore__brand-name {
    font-size: 0.75rem;
    letter-spacing: -0.01em;
  }

  .explore__brand svg {
    width: 20px;
    height: 20px;
  }

  /* Two pixels decided this: with the brand's default padding the row came out
     just over and Report dropped to a second line, which is the difference
     between a 90px bar and a 132px one. Taken off padding, not off a touch
     target. */
  .explore__brand {
    padding: 4px 2px;
    gap: 4px;
  }

  /* And the spacer has to go, or none of that helps. It is flex: 1 1 auto,
     so it simply absorbs whatever the brand gives up — shrinking the name by
     15px grew the spacer by 15px and the Report button stayed on its second
     row. Its job is pushing the buttons to the right edge on a wide bar; on a
     phone there is nothing spare to push with. */
  .explore__top-spacer {
    display: none;
  }

  /* Last few pixels. Measured: with "LDC Watch" this bar was 102px at every
     width; the full name pushed it to 148px on phones, which is 46px of map
     gone on the page whose entire argument is that chrome must not steal map.
     Tightening the gaps buys the row back so the name costs nothing. */
  .explore__top {
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
  }
}


/* On a phone the search takes a row of its own. Sharing one row with the brand
   and three buttons left it about ten characters wide, which is not a search
   field, it is a suggestion of one. `order` puts it below without moving it in
   the DOM, so the tab order still runs brand → search → buttons. */
.explore__search {
  position: relative;
  display: flex;
  align-items: center;
  order: 3;
  flex: 1 0 100%;
  min-width: 0;
}

.explore__top-spacer {
  flex: 1 1 auto;
}

.explore__search input {
  width: 100%;
  min-width: 0;
  height: 38px;
  padding: 0 var(--space-3) 0 34px;
  border-radius: var(--radius-md);
  border: 1px solid var(--glass-line);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-on-ink);
  font: inherit;
  font-size: var(--text-sm);
}

.explore__search input::placeholder {
  color: var(--text-on-ink-muted);
}

.explore__search input:focus-visible {
  outline: 2px solid var(--teal-bright);
  outline-offset: 1px;
  background: rgba(255, 255, 255, 0.1);
}

.explore__search-icon {
  position: absolute;
  left: 10px;
  width: 16px;
  height: 16px;
  color: var(--text-on-ink-muted);
  pointer-events: none;
}

.explore__search-results {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 5;
  max-height: 46vh;
  overflow-y: auto;
  padding: var(--space-1);
}

.explore__search-results button {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-on-ink);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
}

.explore__search-results button:hover,
.explore__search-results button:focus-visible {
  background: rgba(0, 184, 189, 0.2);
  outline: none;
}

/* ---- Icon buttons --------------------------------------------------------- */

.explore-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--glass-line);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-on-ink);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  /* Some of these are links (Report), some are buttons. They must not look
     like two different things. */
  text-decoration: none;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out),
    transform var(--duration-fast) var(--ease-out);
}

.explore-btn svg {
  width: 18px;
  height: 18px;
}

.explore-btn:hover {
  background: rgba(255, 255, 255, 0.13);
  border-color: var(--glass-line-strong);
}

.explore-btn:active {
  transform: scale(0.94);
}

.explore-btn:focus-visible {
  outline: 2px solid var(--teal-bright);
  outline-offset: 2px;
}

.explore-btn[aria-pressed="true"],
.explore-btn[aria-expanded="true"] {
  background: var(--teal);
  border-color: var(--teal-bright);
  color: #fff;
  box-shadow: 0 0 16px rgba(0, 184, 189, 0.45);
}

.explore-btn--wide {
  width: auto;
  padding-inline: var(--space-3);
}

.explore-btn--report {
  background: var(--signal);
  border-color: var(--signal-bright);
  color: #fff;
  box-shadow: 0 0 18px rgba(217, 59, 15, 0.42);
}

.explore-btn--report:hover {
  background: var(--signal-bright);
  border-color: var(--signal-bright);
}

/* ---- Filter chips ---------------------------------------------------------
   A horizontal scroller on a phone, wrapping on a desktop. The scrollbar is
   hidden but the overflow is real, so it still swipes and still keyboards. */

.explore__chips {
  display: flex;
  gap: var(--space-2);
  /* A little vertical room so the focus ring on a chip isn't clipped by the
     scroller. */
  padding: 3px 0;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.explore__chips::-webkit-scrollbar {
  display: none;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex-shrink: 0;
  height: 34px;
  padding: 0 var(--space-3);
  border: 1px solid var(--glass-line);
  border-radius: var(--radius-pill);
  background: var(--glass);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  color: var(--text-on-ink);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.01em;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out),
    opacity var(--duration-fast) var(--ease-out),
    transform var(--duration-fast) var(--ease-out);
}

.chip:active {
  transform: scale(0.95);
}

.chip:focus-visible {
  outline: 2px solid var(--teal-bright);
  outline-offset: 2px;
}

.chip__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--chip-color, var(--text-on-ink-muted));
  box-shadow: 0 0 8px var(--chip-color, transparent);
  flex-shrink: 0;
}

.chip__count {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: var(--weight-medium);
  color: var(--text-on-ink-muted);
  font-variant-numeric: tabular-nums;
}

/* Off reads as off at a glance: the colour drains out and the label dims. It
   is deliberately not `display:none` on the dot — the reader needs to know
   which colour they are about to turn back on. */
.chip[aria-pressed="false"] {
  opacity: 0.5;
  border-style: dashed;
}

.chip[aria-pressed="false"] .chip__dot {
  background: transparent;
  border: 1px solid currentColor;
  box-shadow: none;
}

.chip[aria-pressed="true"] {
  border-color: var(--chip-color, var(--glass-line-strong));
}

.chip--operational { --chip-color: var(--teal-bright); }
.chip--under_construction { --chip-color: var(--amber-bright); }
.chip--proposed { --chip-color: var(--violet-bright); }
.chip--reports { --chip-color: var(--signal-bright); }

/* ---- The floating rail ---------------------------------------------------- */

.explore__rail {
  position: absolute;
  right: var(--chrome-side);
  /* Pinned above the COLLAPSED sheet and the attribution below it — not above
     the live height, or expanding the sheet would drive the rail up into the
     filter chips. */
  bottom: calc(var(--peek-h) + 26px + env(safe-area-inset-bottom, 0px));
  z-index: 615;

  display: flex;
  flex-direction: column;
  gap: 6px;
}

.explore__rail .explore-btn {
  width: 40px;
  height: 40px;
  background: var(--glass);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}

.explore__rail-group {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-md);
  overflow: hidden;
}

.explore__rail-group .explore-btn {
  border-radius: 0;
}

.explore__rail-group .explore-btn:first-child {
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}

.explore__rail-group .explore-btn:last-child {
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  border-top: 0;
}

/* ---- Drawers --------------------------------------------------------------
   Layers and site navigation. They slide from the right on a phone and hang
   under their button on a desktop; the scrim only exists on the phone, where
   the drawer covers most of the map. */

.explore__scrim {
  position: absolute;
  inset: 0;
  z-index: 640;
  background: rgba(4, 7, 14, 0.55);
  backdrop-filter: blur(2px);
  animation: explore-fade var(--duration-base) var(--ease-out);
}

@keyframes explore-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.drawer {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 650;
  width: min(340px, 88vw);
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-lg) 0 0 var(--radius-lg);
  padding-top: env(safe-area-inset-top, 0px);
  animation: drawer-in var(--duration-base) var(--ease-out);
}

@keyframes drawer-in {
  from { transform: translateX(100%); }
  to { transform: translateX(0); }
}

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--glass-line);
}

.drawer__head h2 {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-stretch: 88%;
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--text-on-ink);
}

.drawer__body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-4);
  padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
}

.drawer__group + .drawer__group {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--glass-line);
}

.drawer__title {
  margin: 0 0 var(--space-3);
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: var(--weight-bold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-kicker);
  color: var(--text-on-ink-muted);
}

/* Basemap picker: swatches, not a list of words. Which one you want is a
   visual question. */
.style-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
}

.style-swatch {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 0;
  border: 1px solid var(--glass-line);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-on-ink-muted);
  font: inherit;
  font-size: 11px;
  font-weight: var(--weight-semibold);
  cursor: pointer;
  overflow: hidden;
  transition: border-color var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out);
}

.style-swatch__chip {
  display: block;
  height: 40px;
  background: var(--swatch, #333);
  background-size: cover;
  background-position: center;
}

.style-swatch span:last-child {
  padding: 0 4px 6px;
}

.style-swatch:hover {
  border-color: var(--glass-line-strong);
  color: var(--text-on-ink);
}

.style-swatch:focus-visible {
  outline: 2px solid var(--teal-bright);
  outline-offset: 2px;
}

.style-swatch[aria-checked="true"] {
  border-color: var(--teal-bright);
  color: var(--text-on-ink);
  box-shadow: 0 0 0 1px var(--teal-bright), 0 0 18px rgba(0, 184, 189, 0.3);
}

/* Overlay switches */
.switch-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  cursor: pointer;
}

.switch-row + .switch-row {
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.switch-row input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.switch-row__track {
  position: relative;
  flex-shrink: 0;
  width: 38px;
  height: 22px;
  margin-top: 2px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid var(--glass-line);
  transition: background var(--duration-fast) var(--ease-out);
}

.switch-row__track::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  transition: transform var(--duration-base) var(--ease-spring);
}

.switch-row input:checked + .switch-row__track {
  background: var(--teal);
  border-color: var(--teal-bright);
}

.switch-row input:checked + .switch-row__track::after {
  transform: translateX(16px);
}

.switch-row input:focus-visible + .switch-row__track {
  outline: 2px solid var(--teal-bright);
  outline-offset: 2px;
}

.switch-row__text strong {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-on-ink);
}

.switch-row__text span {
  display: block;
  font-size: var(--text-xs);
  color: var(--text-on-ink-muted);
  line-height: 1.4;
}

/* .input is styled for paper. On the night ground it arrives as a white slab,
   so it is restated here rather than left to look like a hole in the drawer. */
.drawer select.input {
  width: 100%;
  background-color: rgba(255, 255, 255, 0.07);
  border-color: var(--glass-line);
  color: var(--text-on-ink);
  /* The default arrow is drawn in a dark colour for a light field. */
  background-image: linear-gradient(45deg, transparent 50%, var(--text-on-ink-muted) 50%),
    linear-gradient(135deg, var(--text-on-ink-muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) 52%, calc(100% - 13px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--space-7);
}

.drawer select.input option {
  /* Native option lists are painted by the OS, which does not inherit any of
     the above; give them a ground of their own so the text stays readable. */
  background: var(--bg-ink-raised);
  color: var(--text-on-ink);
}

.drawer select.input:focus-visible {
  outline: 2px solid var(--teal-bright);
  outline-offset: 1px;
}

.drawer__nav {
  list-style: none;
  margin: 0;
  padding: 0;
}

.drawer__nav a {
  display: block;
  padding: var(--space-3) var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--text-on-ink);
  text-decoration: none;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
}

.drawer__nav a:hover,
.drawer__nav a:focus-visible {
  background: rgba(255, 255, 255, 0.08);
  outline: none;
}

.drawer__nav a[aria-current="page"] {
  color: var(--teal-bright);
}

/* ---- The sheet ------------------------------------------------------------
   Bottom sheet on a phone, side panel on a desktop, one element either way.
   Height is driven by --sheet-h so the rail can sit above it and so the drag
   handler has a single number to move. */

.explore__sheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 630;

  display: flex;
  flex-direction: column;
  height: var(--sheet-h);
  max-height: 92dvh;
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  border-bottom: 0;
  padding-bottom: env(safe-area-inset-bottom, 0px);
  transition: height var(--duration-base) var(--ease-out);
}

/* While a finger is down the height must track the finger exactly. A
   transition here would lag the drag and feel broken. */
.explore__sheet.is-dragging {
  transition: none;
}

.explore__grip {
  display: block;
  width: 100%;
  padding: var(--space-2) 0 var(--space-1);
  border: 0;
  background: transparent;
  cursor: grab;
  touch-action: none;
}

.explore__grip::before {
  content: "";
  display: block;
  width: 42px;
  height: 4px;
  margin: 0 auto;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.32);
}

.explore__grip:focus-visible {
  outline: 2px solid var(--teal-bright);
  outline-offset: -4px;
  border-radius: var(--radius-md);
}

.explore__sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 0 var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--glass-line);
}

.explore__sheet-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-stretch: 88%;
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--text-on-ink);
}

.explore__sheet-sub {
  margin: 2px 0 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-on-ink-muted);
  font-variant-numeric: tabular-nums;
}

.explore__sheet-body {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: var(--space-2) var(--space-4) var(--space-5);
}

/* ---- Result rows ---------------------------------------------------------- */

.result {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-2);
  border: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-out);
}

.result:hover,
.result:focus-visible {
  background: rgba(255, 255, 255, 0.06);
  outline: none;
}

.result:focus-visible {
  box-shadow: inset 2px 0 0 var(--teal-bright);
}

.result__dot {
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  margin-top: 5px;
  border-radius: 50%;
  background: var(--dot, var(--text-on-ink-muted));
  box-shadow: 0 0 10px var(--dot, transparent);
}

.result__dot--operational { --dot: var(--teal-bright); }
.result__dot--under_construction { --dot: var(--amber-bright); border-radius: 1px; }
.result__dot--proposed { --dot: var(--violet-bright); border-radius: 1px; transform: rotate(45deg); }
.result__dot--report { --dot: var(--signal-bright); }

.result__main {
  min-width: 0;
  flex: 1 1 auto;
}

.result__name {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-on-ink);
  /* Owner names in the county records run long and have no spaces in them
     ("DIGITALREALTYLLC"); without this they push the panel wide. */
  overflow-wrap: anywhere;
}

.result__meta {
  display: block;
  margin-top: 2px;
  font-size: var(--text-xs);
  color: var(--text-on-ink-muted);
  overflow-wrap: anywhere;
}

.result__dist {
  flex-shrink: 0;
  align-self: center;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-on-ink-muted);
  font-variant-numeric: tabular-nums;
}

.explore__empty {
  padding: var(--space-6) var(--space-3);
  text-align: center;
  color: var(--text-on-ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

/* ---- Detail view inside the sheet ----------------------------------------- */

.explore__detail {
  padding-top: var(--space-2);
}

.explore__detail h3 {
  margin: 0 0 var(--space-2);
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-stretch: 86%;
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--text-on-ink);
  overflow-wrap: anywhere;
}

.explore__detail dl {
  display: grid;
  grid-template-columns: minmax(0, 8rem) 1fr;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-4) 0;
  font-size: var(--text-sm);
}

.explore__detail dt {
  color: var(--text-on-ink-muted);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding-top: 2px;
}

.explore__detail dd {
  margin: 0;
  color: var(--text-on-ink);
  overflow-wrap: anywhere;
}

.explore__detail p {
  color: var(--text-on-ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

/* Buttons and badges elsewhere on the site are drawn for paper — `--text` on
   `--bg`. Dropped straight onto the night ground they come out as dark grey on
   dark blue and effectively vanish. The three variants that reach the panel
   and the drawer are restated for this ground rather than left to disappear. */
.explore__sheet .btn--secondary,
.drawer .btn--secondary {
  border-color: var(--text-on-ink-muted);
  color: var(--text-on-ink);
}

.explore__sheet .btn--secondary:hover:not(:disabled),
.drawer .btn--secondary:hover:not(:disabled) {
  background: var(--text-on-ink);
  color: var(--bg-ink);
}

.explore__sheet .btn--ghost,
.drawer .btn--ghost {
  border-color: var(--glass-line);
  color: var(--text-on-ink-muted);
}

.explore__sheet .btn--ghost:hover:not(:disabled),
.drawer .btn--ghost:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.1);
  color: var(--text-on-ink);
}

.explore__sheet .badge--neutral {
  color: var(--text-on-ink-muted);
  border-color: var(--glass-line-strong);
}

.explore__detail .badge--operational,
.explore__detail .badge--under_construction,
.explore__detail .badge--proposed {
  /* These are soft light pills, which read correctly on the dark panel as-is;
     the border just needs to stop being a paper-grey hairline. */
  border-color: transparent;
}

/* These sit directly under the facility name now, above the parcel table, so
   that the action somebody opened the panel in order to take is inside the
   sheet's half snap on a phone rather than below the fold.

   Sitting that high, the block has to read as actions rather than as more of
   the heading — hence the rule beneath it, separating what you can do from
   what the county's records say. */
.explore__detail-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-4) 0 var(--space-5);
  padding-bottom: var(--space-4);
  border-bottom: var(--border-width) solid var(--border-on-ink);
}

/* The primary label carries the facility's entire name and is comfortably the
   longest string in the panel. Giving it the full width keeps it to one or two
   clean lines rather than letting it dictate the width of a wrapped row. */
.explore__detail-actions > .btn--primary {
  flex: 1 1 100%;
}

/* The two lesser actions share the line beneath it, so all three cost two rows
   of height rather than three full-width buttons. */
.explore__detail-actions > .btn--secondary,
.explore__detail-actions > .btn--ghost {
  flex: 1 1 9rem;
}

/* The report panel keeps its single action at the end, where it belongs: that
   panel is a couple of paragraphs, not a table, so nothing is buried. */
.explore__detail > p + .explore__detail-actions,
.explore__detail > .explore__detail-actions:last-child {
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: 0;
}

.explore__back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: var(--space-3);
  padding: 0;
  border: 0;
  background: none;
  color: var(--teal-bright);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  cursor: pointer;
}

.explore__back:hover {
  text-decoration: underline;
}

/* ---- Status line ---------------------------------------------------------- */

.explore__status {
  align-self: center;
  max-width: 100%;
  margin: 0;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  line-height: 1.45;
  color: var(--text-on-ink);
  text-align: center;
  animation: explore-fade var(--duration-fast) var(--ease-out);
}

.explore__status.is-error {
  border-color: var(--signal-bright);
  color: #ffd9cd;
}

/* ---- Markers on the night ground ------------------------------------------
   The bright tier of the ramp exists for dark grounds and FAILS on paper — see
   the three-tier rule at the top of tokens.css. So the glow and the bright
   colours are scoped to .map--dark-base, which map-layers.js sets on the
   container whenever the selected basemap declares itself dark. Switch to the
   Streets or Muted base and the markers fall back to the both-grounds tier
   the rest of the site uses.

   The glow is not decoration: on dark tiles an 11px dot with no halo
   disappears into tree cover and asphalt, and dark tiles are this page's
   default. */

.explore__map.map--dark-base .pin {
  border-color: rgba(255, 255, 255, 0.85);
}

.explore__map.map--dark-base .pin--operational {
  background: var(--teal-bright);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4), 0 0 12px rgba(0, 184, 189, 0.75);
}

.explore__map.map--dark-base .pin--under_construction {
  background: var(--amber-bright);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4), 0 0 12px rgba(232, 148, 12, 0.7);
}

.explore__map.map--dark-base .pin--proposed {
  background: var(--violet-bright);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4), 0 0 12px rgba(155, 132, 255, 0.7);
}

.explore__map.map--dark-base .pin--report {
  background: var(--signal-bright);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4), 0 0 16px rgba(255, 107, 61, 0.85);
}

/* The one marker the reader has selected, so it can be found again after the
   sheet covers half the screen. */
.explore__map .pin.is-selected {
  animation: pin-halo 1.6s var(--ease-in-out) infinite;
  z-index: 1000;
}

@keyframes pin-halo {
  0%, 100% { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), 0 0 12px rgba(255, 255, 255, 0.5); }
  50% { box-shadow: 0 0 0 8px rgba(255, 255, 255, 0), 0 0 26px rgba(255, 255, 255, 0.75); }
}

.explore__map.map--dark-base .marker-cluster-custom span {
  border-color: rgba(255, 255, 255, 0.7);
  color: #fff;
  box-shadow: 0 0 14px rgba(0, 0, 0, 0.55);
}

.explore__map.map--dark-base .marker-cluster-custom--operational span {
  background: var(--teal-deep);
}

.explore__map.map--dark-base .marker-cluster-custom--under_construction span {
  background: var(--amber-deep);
}

.explore__map.map--dark-base .marker-cluster-custom--proposed span {
  background: var(--violet-deep);
}

.explore__map.map--dark-base .marker-cluster-custom--reports span {
  background: var(--signal);
}

/* ---- Desktop --------------------------------------------------------------
   The sheet docks to the left as a panel and the map insets around it. Note
   the map itself is NOT resized — Leaflet keeps the full viewport and the
   panel floats over it, so panning under the panel still works and there is
   no invalidateSize() to chase on every breakpoint change. */

@media (min-width: 900px) {
  .explore {
    --panel-w: 380px;
  }

  .explore__top {
    gap: var(--space-3);
    flex-wrap: nowrap;
  }

  /* Back into the single row, in its natural place between the brand and the
     buttons. */
  .explore__search {
    order: 0;
    flex: 0 1 380px;
  }

  /* The chips clear the docked panel rather than hiding behind it. */
  .explore__chips {
    margin-left: calc(var(--panel-w) + var(--space-3));
    flex-wrap: wrap;
    overflow: visible;
  }

  .explore__status {
    margin-left: calc(var(--panel-w) + var(--space-3));
    align-self: flex-start;
  }

  .explore__sheet {
    top: calc(var(--chrome-top) + 58px);
    left: var(--chrome-side);
    right: auto;
    bottom: var(--space-4);
    width: var(--panel-w);
    height: auto;
    max-height: none;
    border-radius: var(--radius-lg);
    border-bottom: 1px solid var(--glass-line);
  }

  .explore__grip {
    display: none;
  }

  .explore__sheet-head {
    padding-top: var(--space-4);
  }

  .explore__rail {
    bottom: calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
  }

  /* The panel is on the left here, not the bottom, so the scale bar moves
     sideways to clear it and the attribution drops back to the corner. */
  .explore__map .leaflet-bottom.leaflet-left {
    left: calc(var(--panel-w) + var(--chrome-side) + var(--space-3));
    bottom: 4px;
  }

  .explore__map .leaflet-bottom.leaflet-right {
    bottom: 0;
  }

  .drawer {
    top: calc(var(--chrome-top) + 58px);
    right: var(--chrome-side);
    bottom: auto;
    max-height: calc(100dvh - var(--chrome-top) - 80px);
    border-radius: var(--radius-lg);
  }

  .explore__scrim {
    /* On a desktop the drawer covers a corner, not the map. A scrim there
       would darken the thing the reader is looking at for no reason. */
    background: transparent;
    backdrop-filter: none;
  }
}

/* ---- Reduced motion -------------------------------------------------------
   The glow pulse and the slide-ins go; the layout does not change. */

@media (prefers-reduced-motion: reduce) {
  .explore__sheet,
  .explore__rail,
  .drawer,
  .explore__scrim,
  .switch-row__track::after,
  .explore__map .pin.is-selected {
    animation: none !important;
    transition: none !important;
  }
}

/* ---- Choosing facilities to report about ----------------------------------
   Armed from the rail. While it is on, tapping a marker adds its campus to a
   selection rather than opening its record — so the crosshair cursor and the
   bar at the bottom are both load-bearing, not decoration: they are the only
   things telling the reader that a tap now means something different. */

.explore--selecting .explore__map {
  cursor: crosshair;
}

.explore__map .pin.is-picked {
  border-color: #fff;
  transform: scale(1.5);
  box-shadow: 0 0 0 2px var(--bg-ink), 0 0 0 4px #fff, 0 0 20px rgba(255, 255, 255, 0.65);
  z-index: 900;
}

/* The diamond and the teardrop already carry a rotation; overwriting transform
   wholesale would stand them back up and change what the shape means. */
.explore__map .pin--proposed.is-picked {
  transform: rotate(45deg) scale(1.5);
}

.explore__map .pin--report.is-picked {
  transform: rotate(45deg) scale(1.4);
}

.explore__selection {
  position: absolute;
  left: var(--chrome-side);
  right: var(--chrome-side);
  bottom: calc(var(--peek-h) + var(--space-3) + env(safe-area-inset-bottom, 0px));
  z-index: 632;

  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-color: var(--teal-bright);
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(0, 184, 189, 0.4);
  animation: selection-in var(--duration-base) var(--ease-out);
}

@keyframes selection-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
}

.explore__selection-text {
  margin: 0;
  min-width: 0;
  flex: 1 1 14ch;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: 1.4;
  color: var(--text-on-ink);
}

.explore__selection-sub {
  display: block;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-on-ink-muted);
  font-variant-numeric: tabular-nums;
}

.explore__selection-actions {
  display: flex;
  flex-shrink: 0;
  gap: var(--space-2);
}

.explore__selection .btn--ghost {
  border-color: var(--glass-line);
  color: var(--text-on-ink-muted);
}

.explore__selection .btn--ghost:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.1);
  color: var(--text-on-ink);
}

@media (min-width: 900px) {
  .explore__selection {
    left: calc(var(--panel-w) + var(--chrome-side) + var(--space-3));
    bottom: var(--space-5);
  }
}

@media (prefers-reduced-motion: reduce) {
  .explore__selection {
    animation: none;
  }
}
