.demetra-mapcanvas { width: 100%; height: 100%; }

/* Phase 8.1 — Map coordinate + scale HUD (top-left of map).
 *
 * Transparent overlay — just the three lines of text on top of the map
 * tiles, no chrome. Updates on every mousemove (throttled to 1 / paint
 * via rAF) and on zoomend (scale denominator).
 *
 * Position: top-left (inside the map container, after the 280px sidebar).
 * The Leaflet zoom control was moved to top-right in Phase 8.1, so there
 * is no conflict. The bottom-right area is occupied by MapLegend +
 * Leaflet attribution.
 *
 * Text is dropped over (potentially) satellite imagery, so we draw it
 * white with a black 1 px text-shadow halo + light text-stroke
 * fallback for legibility regardless of basemap brightness.
 */
.demetra-map-hud {
  /* UX sweep item 1 — the HUD was `position:absolute; top:12px; left:12px`,
   * but it mounts as a direct child of `#demetra-shell` (a `display:grid`
   * container with no `position`), so the absolute box resolved against the
   * viewport and the HUD painted 12px from the page's top-left, over the
   * topbar's OLYMPUS wordmark. Placing it in the `main` grid cell — the
   * technique `.demetra-basemap-switcher` and `.demetra-label-overlay`
   * already use — pins it to the map area's top-left, clear of the topbar. */
  grid-area: main;
  justify-self: start;
  align-self: start;
  margin: 12px;
  z-index: 1000;
  background: transparent;
  border: 0;
  box-shadow: none;
  padding: 0;
  font-family: var(--font-mono, ui-monospace, "SF Mono", Consolas, monospace);
  font-size: 11px;
  line-height: 1.45;
  color: #ffffff;
  pointer-events: none;
  user-select: none;
  text-shadow:
    0 0 2px rgba(0, 0, 0, 0.85),
    0 1px 2px rgba(0, 0, 0, 0.85),
    0 -1px 2px rgba(0, 0, 0, 0.85),
    1px 0 2px rgba(0, 0, 0, 0.85),
    -1px 0 2px rgba(0, 0, 0, 0.85);
}
.demetra-map-hud .map-hud__row,
.demetra-map-hud .map-hud__scale {
  display: flex;
  gap: 6px;
  align-items: baseline;
}
.demetra-map-hud .map-hud__label {
  font-weight: 700;
  letter-spacing: 0.02em;
  min-width: 36px;
}
.demetra-map-hud .map-hud__value {
  font-variant-numeric: tabular-nums;
}
.demetra-map-hud .map-hud__scale-v {
  font-weight: 700;
}

/* Phase 8.1 — Leaflet zoom control offset at top-right.
 *
 * The basemap switcher occupies the top-right corner at ~top: 8px
 * (grid margin) + the 56px topbar = ~64px from the page top. The
 * switcher icon is 32px tall, giving a bottom edge of ~96px.
 *
 * margin-top: 76px pushes the Leaflet zoom buttons below the
 * switcher: 32px icon height + 12px gap + 32px next icon = 76px
 * total, matching the plan spec.  The `.leaflet-top.leaflet-right`
 * ancestor stacks from the map container's top edge so `margin-top`
 * on the zoom control child shifts it within that pane.
 */
.leaflet-top.leaflet-right .leaflet-control-zoom {
  margin-top: 76px;
}

.dm-map-legend {
  position: absolute; right: var(--space-2); bottom: var(--space-3);
  background: rgba(255,255,255,0.96);
  border: var(--border-thin); border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-xs); color: var(--charcoal);
  /* Leaflet panes stack at 200–700 (tile=200, marker=600, tooltip=650, popup=700)
   * — pre-fix this was z-index: 10 and the legend rendered BEHIND every Leaflet
   * tile, marker, and overlay. Bumped above the popup pane so map legend +
   * basemap switcher sit on top of the map content where users can read +
   * click them. */
  z-index: 1000;
}
.dm-map-legend .row { display: flex; align-items: center; gap: var(--space-1); margin-bottom: 2px; }
.dm-map-legend .row .dot { width: 10px; height: 10px; border-radius: 50%; }
.dm-map-legend .row .swatch { width: 10px; height: 10px; border-radius: 2px; }
.dm-map-legend .row.muted { color: var(--muted); }
.dm-map-legend .row.sep hr { border: 0; border-top: 1px solid #e5e7eb; margin: 4px 0; width: 100%; }
.leaflet-container { background: #d8e3d6; }

/* a11y: Leaflet's default attribution link "Leaflet" uses #0078a8 on
 * #ffffff (3.6:1, fails AA contrast for non-large text) and has no
 * underline (fails WCAG 1.4.1 link-in-text-block). Override to a
 * darker blue + underline. Specificity bumped via .leaflet-container
 * ancestor so Leaflet's stylesheet (loaded after ours from CDN) doesn't
 * win the tie-break. */
.leaflet-container .leaflet-control-attribution a {
  color: #0a4a6c;             /* 7.2:1 contrast vs white */
  text-decoration: underline;
}
.leaflet-container .leaflet-control-attribution a:hover,
.leaflet-container .leaflet-control-attribution a:focus {
  color: #062d42;
  text-decoration: underline;
}

/* B2: distance label rendered as an L.divIcon at the last vertex of the
 * measure polyline. Fallback values (#fff / #15171c) handle the case where
 * Leaflet's overlay pane doesn't inherit our page-level CSS variables. */
.measure-label {
  background: var(--bg, #fff);
  color: var(--ink, #15171c);
  padding: 2px 6px;
  border-radius: 4px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
}

/* Stream H — host class for the @olympus/spatial-core/click-marker
 * inside Leaflet's divIcon. Leaflet's default `.leaflet-div-icon` adds
 * a white fill + grey border that fights the click-marker's own
 * background; the override below strips them so the marker renders
 * exactly as the storybook baseline does. */
.olympus-click-marker-host {
  background: transparent !important;
  border: 0 !important;
  /* Leaflet's iconAnchor positions the host; the inner button is
   * itself 24 × 24 so no extra layout is needed. */
}

/* Phase 3.3 (closes B4): visual halo on the picked result dot. Leaflet
 * renders circleMarkers as SVG <path> elements, so we target stroke + r
 * on .parcel-marker--selected. The class is applied imperatively in
 * ParcelMarkers.renderResultDots when state.inspect.parcelId matches the
 * dot's id. The Leaflet inline style wins for color + radius, so the
 * primary visual signal is set via marker options (radius: 8, gold stroke);
 * this rule is the belt-and-braces override + raises stacking so the picked
 * dot draws on top of any clustered neighbours. */
.parcel-marker--selected {
  stroke: var(--gold, #c9a84c);
  stroke-width: 3px;
  r: 8;
  z-index: 1000;
}

/* Parcel polygon popup — opens when the user clicks a polygon outline on
 * the map. Compact (matches the 280px sidebar style) so it doesn't crowd
 * the canvas; carries Inspect button that dispatches demetra:select-parcel,
 * the same channel used by the Find result dots. */
.dm-pp { font-family: var(--font-sans, system-ui, sans-serif); min-width: 180px; }
.dm-pp__id {
  font-weight: 600; font-size: var(--fs-sm, 13px);
  color: var(--ink, #15171c);
  margin-bottom: var(--space-1, 4px);
  font-variant-numeric: tabular-nums;
}
.dm-pp__hint {
  font-size: var(--fs-xs, 12px); color: var(--muted, #5b6473);
  margin-bottom: var(--space-2, 8px);
}
.dm-pp__rows {
  display: grid; grid-template-columns: auto 1fr;
  gap: 2px var(--space-2, 8px);
  margin: 0 0 var(--space-2, 8px) 0; padding: 0;
  font-size: var(--fs-xs, 12px);
}
.dm-pp__rows dt { color: var(--muted, #5b6473); }
.dm-pp__rows dd { margin: 0; color: var(--ink, #15171c); font-variant-numeric: tabular-nums; }
.dm-pp__grade {
  display: inline-block; padding: 0 6px; margin-left: 4px;
  background: var(--bg-soft, #f3f4f6); border-radius: 999px;
  font-size: 11px; color: var(--charcoal, #1f2937);
}
.dm-pp .dm-pp-inspect {
  display: inline-block;
  padding: 6px 12px; min-height: 32px;
  background: var(--teal, #2a9d8f); color: #fff;
  border: 1px solid var(--teal, #2a9d8f); border-radius: var(--radius-sm, 4px);
  font-size: var(--fs-xs, 12px); font-weight: 600;
  cursor: pointer; width: 100%;
}
.dm-pp .dm-pp-inspect:hover, .dm-pp .dm-pp-inspect:focus-visible {
  background: var(--teal-700, #21897d); border-color: var(--teal-700, #21897d);
  outline: 2px solid var(--gold, #c9a84c); outline-offset: 2px;
}

/* Phase 2 — persistent map popup
 * Phase 2.1 D3 — position:fixed so viewport coords from
 * map.latLngToContainerPoint + getBoundingClientRect translate directly.
 * Inline style (set by MapPopup.js) overrides left/top/transform when
 * coords are available; when coords are absent the popup renders at the
 * default fixed position (50% / 50%) as a graceful unanchored fallback.
 * z-index 500: above Leaflet overlay pane (400) + tile pane (200), below
 * Leaflet marker pane (600). Popup must be clickable hence pointer-events:auto.
 */
/*
 * Phase 1 Stream D — spec §4 popup transparency fix.
 *
 * The popup was inheriting whatever ambient background sat in its
 * cascade. Explicit `background: var(--olympus-color-bg-base, #fff)` from
 * `@olympus/design-tokens` makes the popup OPAQUE regardless of stacking
 * context. The `#ffffff` fallback guarantees opacity even when the
 * Olympus tokens haven't loaded (e.g. in test fixtures that bypass
 * tokens.css). Shadow + radius pulled from the workspace tokens too.
 *
 * Padding + border keep visual continuity with the historical popup —
 * the new `@olympus/spatial-core/popup/css` shipping rules apply to the
 * `.olympus-popup` class (new code paths); Demetra's existing
 * `.demetra-map-popup` keeps its layout but borrows the opacity fix.
 */
.demetra-map-popup {
  font-size: 11px; min-width: 220px;
  position: fixed;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: 500;
  pointer-events: auto;
  background: var(--olympus-color-bg-base, var(--bg, #ffffff));
  color: var(--olympus-color-fg-base, var(--ink, #15171c));
  border-radius: var(--olympus-radius-md, 6px);
  box-shadow: var(--olympus-shadow-md, var(--shadow-md, 0 4px 12px rgba(15,23,42,0.08)));
  padding: var(--olympus-spacing-3, 12px);
  border: 1px solid var(--olympus-color-border-thin, var(--border, #e3e3e8));
}
.demetra-map-popup .popup-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  margin-bottom: 8px;
}
.demetra-map-popup .popup-label {
  font-size: 8.5px; color: var(--text-muted, #6b7280);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.demetra-map-popup .popup-pid {
  font-weight: 600; font-family: var(--mono, monospace);
  font-size: 11px;
}
.demetra-map-popup .popup-close {
  width: 18px; height: 18px;
  background: transparent; border: 1px solid var(--border, #e3e3e8);
  border-radius: 3px; color: var(--text-muted, #5b6370);
  cursor: pointer; font-size: 10px; padding: 0; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.demetra-map-popup .popup-close:hover { background: #fee2e2; color: #b91c1c; border-color: #b91c1c; }
.demetra-map-popup .popup-close:focus-visible { outline: 2px solid var(--focus, #2563eb); outline-offset: 1px; }
.demetra-map-popup .popup-row {
  display: flex; justify-content: space-between;
  font-size: 10px; padding: 2px 0;
}
.demetra-map-popup .popup-row .l { color: var(--text-muted, #6b7280); }
.demetra-map-popup .popup-row .v { font-weight: 500; }
.demetra-map-popup .popup-row .v.alert { color: #b91c1c; }
.demetra-map-popup .popup-action {
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--border, #eef0f3);
  text-align: right;
}
.demetra-map-popup .popup-link {
  background: transparent; border: none;
  color: var(--accent, #4a6cf7);
  font-size: 10px; font-weight: 500; cursor: pointer; padding: 0;
}
.demetra-map-popup .popup-link:hover { text-decoration: underline; }
.demetra-map-popup .popup-link:focus-visible { outline: 2px solid var(--focus, #2563eb); outline-offset: 1px; border-radius: 3px; }

/* Phase 2 B2 — Top-right base-map switcher (≡ button + popover).
 *
 * Wired in app.js (Task B4) as a sibling of DemetraMapCanvas at the
 * `#demetra-shell` JSX level. Because `#demetra-shell` is `display: grid`
 * and NOT `position: relative`, an `position: absolute` switcher would
 * leak to the viewport edge (overlapping topbar). Instead we land it in
 * the `main` grid cell via `grid-area: main` + self-alignment so it
 * stacks on top of `.demetra-main` (which also occupies grid-area main).
 * The switcher itself is `position: relative` so the popover (declared
 * `position: absolute; top: 36px; right: 0;` below) anchors to it.
 *
 * Inputs carry their own focus outline (radio focus-visible is
 * design-system non-negotiable). */
.demetra-basemap-switcher {
  grid-area: main;
  justify-self: end;
  align-self: start;
  margin: var(--space-2, 8px);
  position: relative;
  /* Was 11 — below Leaflet's tile-pane (z-index 200) which made the
   * switcher button render behind any map tile or overlay. Bumped to
   * 1001 so it sits above the legend (1000) and above all Leaflet
   * panes (max 700 = popup-pane). The popover the button opens
   * inherits the stacking context. */
  z-index: 1001;
}
.demetra-basemap-switcher .basemap-btn {
  width: 32px; height: 32px;
  background: var(--bg, #fff);
  border: var(--border-thin, 1px solid #e5e7eb);
  border-radius: var(--radius-sm, 3px);
  color: var(--ink, #15171c);
  cursor: pointer;
  font-size: 18px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}
.demetra-basemap-switcher .basemap-btn:hover {
  background: var(--bg-soft, #f6f7f9);
}
.demetra-basemap-switcher .basemap-btn:focus-visible {
  outline: 2px solid var(--focus, #2563eb); outline-offset: 2px;
}
.demetra-basemap-switcher .basemap-popover {
  position: absolute; top: 36px; right: 0;
  min-width: 220px;
  background: var(--bg, #fff);
  border: var(--border-thin, 1px solid #e5e7eb);
  border-radius: var(--radius-sm, 3px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  padding: var(--space-2, 8px) 0;
  font-size: var(--fs-xs, 12px);
}
.demetra-basemap-switcher .section-label {
  padding: 4px var(--space-3, 12px) 2px;
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-muted, #6b7280);
}
.demetra-basemap-switcher .section-label.divider {
  border-top: 1px solid var(--border, #eef0f3);
  margin-top: 4px; padding-top: 6px;
}
.demetra-basemap-switcher .basemap-row {
  display: flex; align-items: center; gap: var(--space-2, 8px);
  padding: 4px var(--space-3, 12px);
  cursor: pointer;
  color: var(--ink, #15171c);
}
.demetra-basemap-switcher .basemap-row:hover {
  background: var(--bg-soft, #f3f5f9);
}
.demetra-basemap-switcher .basemap-row.disabled {
  color: var(--text-muted, #9ca3af);
  cursor: not-allowed;
  background: transparent;
}
.demetra-basemap-switcher .basemap-row input[type="radio"] {
  margin: 0;
}
.demetra-basemap-switcher .basemap-row input[type="radio"]:focus-visible {
  outline: 2px solid var(--focus, #2563eb); outline-offset: 2px;
}
.demetra-basemap-switcher .basemap-row:focus-within { background: #f3f5f9; }

/* Phase 1 Stream H — basemap switcher V2.
 *
 * Replaces the BaseMapSwitcher.js dropdown popover (Phase 2 era).
 * The new switcher is a top-right thumbnail grid that exposes all 9
 * basemap registry entries side-by-side; a year slider appears under
 * the grid whenever a `dls_ortho_*` thumb is active. All sizing,
 * colour, radius, shadow values come from `@olympus/design-tokens`
 * via the bridge-injected CSS variables. Fallbacks (the second arg
 * inside each `var(...)`) keep the switcher legible even when the
 * tokens haven't loaded — same pattern as the popup.
 *
 * z-index: 1001 to clear the legend (1000) and every Leaflet pane
 * (max 700 = popup-pane). The switcher anchors via grid-area: main
 * + justify-self: end / align-self: start so it sits in the
 * top-right corner of the map cell without overlapping the topbar.
 */
.olympus-basemap-switcher {
  grid-area: main;
  justify-self: end;
  align-self: start;
  margin: var(--olympus-space-2, 8px);
  position: relative;
  z-index: 1001;
  font-family: var(
    --olympus-font-family-sans,
    "Inter",
    system-ui,
    sans-serif
  );
  font-size: var(--olympus-font-size-sm, 12px);
  color: var(--olympus-color-fg-base, #15171c);
}

/* Collapsed trigger button: single corner button showing the active
 * basemap. Click opens the popover (.is-open). User-feedback hotfix
 * 2026-05-11 — replaces the always-on 312px panel. */
.olympus-basemap-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px 6px 6px;
  background: var(--olympus-color-bg-base, #ffffff);
  border: 1px solid var(--olympus-color-border-thin, #e5e7eb);
  border-radius: var(--olympus-radius-md, 6px);
  box-shadow: var(
    --olympus-shadow-sm,
    0 1px 3px rgba(15, 23, 42, 0.1)
  );
  cursor: pointer;
  font: inherit;
  color: inherit;
  transition: box-shadow 120ms ease, border-color 120ms ease;
}

.olympus-basemap-trigger:hover,
.olympus-basemap-switcher.is-open .olympus-basemap-trigger {
  border-color: var(--olympus-color-border-strong, #cbd5e1);
  box-shadow: var(
    --olympus-shadow-md,
    0 4px 12px rgba(15, 23, 42, 0.12)
  );
}

.olympus-basemap-trigger:focus-visible {
  outline: 2px solid var(--olympus-color-focus, #1a73e8);
  outline-offset: 2px;
}

.olympus-basemap-trigger-thumb {
  width: 28px;
  height: 28px;
  border-radius: var(--olympus-radius-sm, 4px);
  display: block;
}

.olympus-basemap-trigger-label {
  font-size: var(--olympus-font-size-sm, 12px);
  font-weight: 500;
  white-space: nowrap;
}

/* Popover panel — same look as the old always-on switcher, anchored
 * below the trigger button. */
.olympus-basemap-popover {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: 312px;
  background: var(--olympus-color-bg-base, #ffffff);
  border: 1px solid var(--olympus-color-border-thin, #e5e7eb);
  border-radius: var(--olympus-radius-md, 6px);
  box-shadow: var(
    --olympus-shadow-md,
    0 4px 12px rgba(15, 23, 42, 0.12)
  );
  padding: 12px;
}

.olympus-basemap-thumbgrid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.olympus-basemap-thumb {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 4px;
  background: transparent;
  border: 2px solid transparent;
  border-radius: 6px;
  cursor: pointer;
  color: var(--olympus-color-fg-base, #15171c);
  font: inherit;
  /* WCAG 2.5.5 — visible target ≥ 24 px on both axes. */
  min-height: 24px;
  transition: background var(--olympus-duration-fast, 160ms)
      var(--olympus-easing-out, cubic-bezier(0.2, 0.8, 0.2, 1)),
    border-color var(--olympus-duration-fast, 160ms)
      var(--olympus-easing-out, cubic-bezier(0.2, 0.8, 0.2, 1));
}

.olympus-basemap-thumb img {
  width: 60px;
  height: 60px;
  border-radius: 6px;
  background: var(--olympus-color-bg-soft, #f6f7f9);
  display: block;
}

.olympus-basemap-thumb span {
  font-size: 10px;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 84px;
}

.olympus-basemap-thumb:hover {
  background: var(
    --olympus-color-bg-soft,
    var(--olympus-color-surface-soft, #f6f7f9)
  );
}

.olympus-basemap-thumb.is-active {
  border-color: var(
    --olympus-color-accent-primary,
    var(--olympus-color-navy-base, #1a2744)
  );
  background: var(
    --olympus-color-bg-soft,
    var(--olympus-color-surface-soft, #f6f7f9)
  );
}

.olympus-basemap-thumb:focus-visible {
  outline: 2px solid
    var(
      --olympus-color-focus,
      var(--olympus-color-accent-secondary, #2a9d8f)
    );
  outline-offset: 2px;
}

.olympus-basemap-year {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--olympus-color-border-thin, #e5e7eb);
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 8px;
  align-items: center;
  font-size: var(--olympus-font-size-xs, 11px);
  color: var(--olympus-color-fg-muted, #6b7280);
}

.olympus-basemap-year input[type="range"] {
  width: 100%;
}

.olympus-basemap-year input[type="range"]:focus-visible {
  outline: 2px solid
    var(
      --olympus-color-focus,
      var(--olympus-color-accent-secondary, #2a9d8f)
    );
  outline-offset: 2px;
  border-radius: 4px;
}

.olympus-basemap-year output {
  font-family: var(
    --olympus-font-family-mono,
    ui-monospace,
    "Cascadia Code",
    monospace
  );
  color: var(--olympus-color-fg-base, #15171c);
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .olympus-basemap-thumb {
    transition: none;
  }
}

/* Phase 2 — score-gradient legend */
.score-legend {
  position: absolute;
  top: 12px; left: 50%; transform: translateX(-50%);
  background: rgba(255, 255, 255, 0.95);
  border: 1px solid var(--border, #c8c8d0); border-radius: 6px;
  padding: 6px 10px; font-size: 10px;
  display: flex; align-items: center; gap: 8px;
  z-index: 12;  /* above legend (10) and basemap-switcher (11) — but the switcher is top-right, no overlap */
  pointer-events: none;  /* legend is informational; clicks pass through to map */
}
.score-legend .label { color: var(--text-muted, #6b7280); font-size: 9px; text-transform: uppercase; letter-spacing: 0.06em; }
.score-legend .low  { color: #b91c1c; font-weight: 600; }
.score-legend .high { color: #047857; font-weight: 600; }
.score-legend .gradient {
  width: 120px; height: 8px;
  background: linear-gradient(to right, #b91c1c, #d97706, #ca8a04, #16a34a, #047857);
  border-radius: 4px;
}

/* Phase 2 Stream F (ADR 0025) — canvas label overlay.
 * Pinned to the same `grid-area: main` cell as the Leaflet map so the
 * canvas matches the map's pixel rect exactly. Z-index sits above the
 * Leaflet overlay pane (400 — polygon overlays) but below the popup
 * pane (700) so labels stack on top of polygons but never occlude
 * inspector popups. Pointer-events: none keeps the overlay completely
 * passive — clicks fall through to the Leaflet map. aria-hidden because
 * labels are decorative; the data they represent is keyboard-reachable
 * through LayerPanel + ParcelInspector. */
.demetra-label-overlay {
  grid-area: main;
  align-self: stretch;
  justify-self: stretch;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 450;
}
