/* DitchBook /map/ — Leaflet map page. Builds on styles.css + directory.css
   tokens; only map-specific layout lives here. Rebuilt for v3
   (specs/ditch-map-v3.md, D7): the map frame is near-full-screen with
   floating overlay controls, capped at two persistent ones on phones
   (search + satellite) plus Leaflet's own attribution/zoom — everything
   else (legend, shape key, slider, coverage count) lives in one
   collapsible info sheet. */

/* PRODUCTION BUG FIX (2026-08-07): canal lines/reservoir pools rendered
   invisible despite correct `d` paths -- root cause was styles.css's
   generic `img, svg{max-width:100%}` collapsing DitchBook's own CUSTOM
   pane (ditchbook-geometry-pane) to 0×0, since Leaflet's OWN built-in
   `svg{max-width:none}` reset in leaflet.css only covers its own pane,
   never a consumer-created one. Pane class below is Leaflet's generated
   `leaflet-<name>-pane` for GEOMETRY_PANE_NAME ("ditchbook-geometry-pane")
   in map.js — keep both in sync by hand if that constant ever changes. */
.leaflet-container .leaflet-ditchbook-geometry-pane-pane svg,
.leaflet-container .leaflet-ditchbook-basins-pane-pane svg,
.leaflet-container .leaflet-ditchbook-waterbodies-pane-pane svg {
  max-width: none !important;
  max-height: none !important;
}

.map-page {
  /* styles.css's generic section top-padding pushed the map down/off the
     bottom of the viewport once it became the first thing on the page
     (.map-frame's own height calc already accounts for the header).
     Zeroed here, page-scoped only. */
  padding-top: 0;
  padding-bottom: 2rem;
}

/* Map-first layout (round-1 fix, DoD M20): the map is the first thing in
   the page; breadcrumb/H1/intro/oldest-ten live in .map-below, AFTER the
   map-frame (see the layout guard in www/generate-directory.mjs).
   --map-header-h tracks the header's own real rendered height per
   breakpoint, so .map-frame's height calc always fills what's left. */
body.map-body {
  /* Measured in the browser: the header's nav wraps to two rows at EVERY
     width above the 860px breakpoint below (.container caps at ~1024px
     available, less than the nav's own ~1068px). 134px measured at
     1280x720; rounded up for margin. */
  --map-header-h: 140px;
}

@media (max-width: 860px) {
  body.map-body {
    --map-header-h: 60px; /* the slimmed single-row header this breakpoint switches to below */
  }

  /* The global header (styles.css) stacks brand + nav into ~3 rows below
     860px -- fine everywhere else, but it ate most of the first viewport
     on this page. Scoped to body.map-body (this page's own <body> class)
     only; no other page's header is touched. */
  body.map-body .site-header {
    padding: 0.4rem 0;
  }

  body.map-body .site-header .container {
    flex-direction: row;
    flex-wrap: nowrap;
    gap: 0.6rem;
  }

  body.map-body .brand img {
    width: 128px;
  }

  body.map-body .site-nav {
    flex-wrap: nowrap;
    gap: 0.6rem;
  }

  /* Keep the phone CTA -- this audience calls more than it clicks -- and
     drop the six text links from view. They stay in the DOM (crawlable,
     unchanged for SEO) and are one tap away in the footer at the bottom of
     this same page; hiding them is what turns 3 rows into 1. */
  body.map-body .site-nav a:not(.phone) {
    display: none;
  }

  body.map-body .site-nav .phone {
    font-size: 0.85rem;
  }
}

.map-frame-outer {
  width: 100%;
  margin: 0;
}

.map-intro {
  max-width: var(--measure);
  color: var(--ink-mid);
  margin: 1rem 0 1.25rem;
}

.map-intro strong {
  color: var(--ink);
}

.map-frame {
  position: relative;
  width: 100%;
  /* 100dvh with a 100vh fallback (round-1 fix M20/M20-adjacent): browsers
     that don't understand dvh ignore the whole second declaration and keep
     the vh one, so this degrades safely. No max-height cap -- the map is
     meant to fill whatever viewport height is left after the header, full
     stop, on any screen size (specs/ditch-map-v3.md's "full-screen
     statewide map"). */
  height: calc(100vh - var(--map-header-h, 80px));
  height: calc(100dvh - var(--map-header-h, 80px));
  min-height: 360px;
  border-top: 1px solid var(--border-strong);
  border-bottom: 1px solid var(--border-strong);
  overflow: hidden;
  background: var(--paper-dim);
  margin-bottom: 1.5rem;
}

/* round-3 fix item 6: the 360px floor above overflowed a genuinely
   short-HEIGHT viewport (landscape phone, ~660x375), pushing the
   attribution control off-screen. Height-only query (not combined with
   the width condition above), so a narrow-but-tall viewport (768x1024)
   keeps the normal 360px floor. */
@media (max-height: 500px) {
  .map-frame {
    min-height: 240px;
  }
}

#ditch-map {
  width: 100%;
  height: 100%;
  background: var(--paper-dim);
}

.map-noscript {
  max-width: var(--measure);
  background: var(--water-tint);
  border: 1px solid var(--water-line);
  border-radius: 6px;
  padding: 1rem 1.25rem;
  margin: 1.5rem 0;
}

.map-loading {
  position: absolute;
  top: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 850;
  background: var(--paper);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: 0.4em 1em;
  font-size: var(--step--1);
  color: var(--ink-mid);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

/* ---------- floating search (one of exactly two persistent controls) ---------- */

.map-overlay-search {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  right: 4.75rem; /* clears hero mode's topright "Open the full map" button at narrow widths */
  max-width: 22rem;
  z-index: 900;
  margin: 0;
}

.map-search-input {
  display: block;
  width: 100%;
  box-sizing: border-box;
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.3;
  color: var(--ink);
  background: var(--paper);
  border: 2px solid var(--water-deep);
  border-radius: 8px;
  padding: 0.7em 0.85em;
  min-height: 44px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.map-search-input::placeholder {
  color: var(--ink-soft);
  opacity: 1;
}

.map-search-input:focus-visible {
  outline: 3px solid var(--water);
  outline-offset: 2px;
}

.map-search-results {
  max-width: none;
  margin: 0.4rem 0 0;
  background: var(--paper);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
  max-height: min(60vh, 26rem);
  overflow-y: auto;
}

.map-search-cta {
  padding: 0.9em 0.9em;
  font-size: var(--step--1);
  color: var(--ink-mid);
  line-height: 1.5;
}

/* These render as plain inline sentences (CTA copy, no-match message,
   address row) — override directory.css's .company-search-results a rule
   (display:flex; flex-direction:column; min-height:56px), meant for the
   full-row matched-company link, not a link/button sitting mid-sentence. */
.map-search-cta a,
.map-search-results .search-empty a {
  display: inline;
  flex-direction: initial;
  min-height: 0;
  padding: 0.3em 0.15em;
  color: var(--water-deep);
  font-weight: 600;
}

.map-search-results li {
  padding: 0 0.1em;
}

.map-search-address-row {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.75em 0.9em 0.2em;
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 600;
  color: var(--water-deep);
  min-height: 44px;
}

.map-search-address-row:hover,
.map-search-address-row:focus-visible {
  background: var(--water-tint);
  outline: none;
}

.map-search-address-note {
  margin: 0 0 0.75em;
  padding: 0 0.9em;
  font-size: 0.8rem;
  color: var(--ink-soft);
}

.map-layers-control {
  position: relative;
}

/* leaflet.css's `.leaflet-bottom .leaflet-control{margin-bottom:10px}`
   (2-class) beats a 1-class override. --map-layers-strip-extra is the
   legend strip's OWN live-measured height (0px empty/hidden, set by
   layers-panel.js's updateStrip()), added on top of the 6.5rem peek-row
   clearance so the button (Leaflet z-index 1000) can never paint over the
   sheet's peek (z-index 880) as the strip grows. Left UNCHANGED by the
   sheet retirement (specs/sheet-retirement.md move 7) despite the typical
   peek height shrinking (browser-measured, 375px: ~31px vs. ~54px with the
   old chevron) -- the worst realistic case (longest coverage string +
   satellite.js's two notes both showing) already measures ~168px at 375px,
   over budget before the chevron's removal; shrinking further would only
   tighten a pre-existing edge case. */
.leaflet-bottom .map-layers-control {
  margin-bottom: calc(6.5rem + var(--map-layers-strip-extra, 0px));
}

/* v2 mobile sheet (browser verification): Leaflet's own bottomleft/
   bottomright corner containers share z-index:1000 -- equal-z-index
   siblings paint in DOM order (bottomright emits after bottomleft), so
   the zoom +/- buttons painted OVER the open sheet regardless of
   .map-layers-panel's own z-index:1200 (only wins WITHIN this corner's
   stacking context, never against an equal sibling). Harmless bump --
   zoom has no reason to render above this corner. */
.leaflet-bottom.leaflet-left {
  z-index: 1001;
}

/* v2 (specs/layers-panel-v2.md D1): 56px Google-style photo chip,
   detached from .btn/.map-open-full-btn (a pill shape, wrong job here).
   Pure-CSS gradient face + glyph at first paint (no image fetch before
   `load`); layers-panel.js's warm-load hook swaps the face to the real
   thumbnail later. Visible "Layers" label (D13) is the only non-aria-
   hidden content -- also the accessible name. */
.map-layers-btn {
  position: relative;
  width: 56px;
  height: 56px;
  padding: 0;
  border: 2px solid #fff;
  border-radius: 14px;
  overflow: hidden;
  cursor: pointer;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.28);
  background: none;
  display: block;
}

.map-layers-btn-face {
  position: absolute;
  inset: 0;
  background-image: linear-gradient(135deg, #5c7a4a 0%, #8a6a45 50%, #3f6e8c 100%);
  background-size: cover;
  background-position: center;
}

.map-layers-btn-glyph {
  position: absolute;
  top: 38%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 14px;
  height: 14px;
}

.map-layers-btn-glyph::before,
.map-layers-btn-glyph::after {
  content: "";
  position: absolute;
  width: 9px;
  height: 9px;
  border: 1.5px solid #fff;
  border-radius: 1px;
}

.map-layers-btn-glyph::before {
  top: 0;
  left: 0;
  opacity: 0.7;
}

.map-layers-btn-glyph::after {
  top: 5px;
  left: 5px;
  background: rgba(255, 255, 255, 0.9);
  opacity: 0.95;
}

.map-layers-btn-label {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background: #fff;
  color: var(--ink);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
  padding: 2px 0;
}

/* specs/sheet-retirement.md move 6: desktop-only enlargement, additive on
   the 56px mobile-default rule above (still pinned by test-map-controls-v2
   DoD 9). No focus-visible override -- the sitewide rule (styles.css)
   applies untouched. */
@media (min-width: 769px) {
  .map-layers-btn {
    width: 76px;
    height: 76px;
    border-radius: 16px;
    transition: box-shadow 150ms ease, transform 150ms ease;
  }
  .map-layers-btn:hover {
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.32);
    transform: translateY(-1px);
  }
  .map-layers-btn-label {
    font-size: 13px;
    padding: 3px 0;
  }
  .map-layers-btn-glyph {
    width: 19px;
    height: 19px;
  }
  .map-layers-btn-glyph::before,
  .map-layers-btn-glyph::after {
    width: 12px;
    height: 12px;
  }
  .map-layers-btn-glyph::after {
    top: 7px;
    left: 7px;
  }
}

/* ---------- coverage bar (D7) ---------- */
/* specs/sheet-retirement.md: the sheet's old collapsible legend/shape-key/
   slider+Play moved into the Layers panel (layers-panel.js, incl. a new
   Timeline section) -- this is now a permanent bar: the coverage sentence +
   fetch-error Retry (#map-count), plus satellite.js's notes / the
   legend-chip strip as siblings. Docked panel wide, overlay narrow/short
   (max-width:768px / max-height:500px block below). */

.map-sheet {
  position: absolute;
  left: 0.75rem;
  bottom: 0.75rem;
  width: min(22rem, calc(100% - 1.5rem));
  z-index: 880;
  background: var(--paper);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.22);
  max-height: calc(100% - 1.5rem);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* specs/sheet-retirement.md: the chevron toggle + collapsible body are
   retired -- this plain, non-interactive <div> is the permanent bar now,
   nothing here is clickable. */
.map-sheet-peek {
  display: flex;
  align-items: center;
  gap: 0.6em;
  width: 100%;
  padding: 0.35em 0.5em 0.35em 0.9em;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--ink);
}

.map-sheet-count {
  /* v2 (specs/layers-panel-v2.md point 5): quieter, smaller peek-row
     typography, matching the rest of the redesigned control area --
     content/wording unchanged, visual weight only. round-2 fix item 10:
     at narrow widths the coverage sentence (now longer -- "715 verified
     structures published (366 of 367 matched companies) — zoom out for
     the company view") must wrap onto multiple lines and stay fully
     legible rather than being squeezed by its flex sibling (the chevron)
     or clipped. */
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-soft);
  min-width: 0;
  flex: 1 1 auto;
  white-space: normal;
}

/* round-2 fix item 8: the inline Retry control setCoverageError() builds
   inside #map-count. round-3 fix item 7 (release blocker): now a REAL
   <button> (map.js no longer builds a role="button" span) -- #map-count
   is a sibling of the sheet's toggle button now, not nested inside it, so
   there's no ancestor button left for a real one to invalidly nest inside.
   Reset to plain UA button chrome so it still reads as inline text, styled
   to look clearly interactive (underline). */
.map-coverage-retry {
  display: inline-block;
  margin-left: 0.35em;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: var(--water-deep);
  font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
}

.map-coverage-retry:focus-visible {
  outline: 3px solid var(--water);
  outline-offset: 2px;
}

/* satellite.js's D2 fallback note. */
.map-satellite-note,
.map-accuracy-note {
  margin: 0;
  padding: 0 0.9em 0.5em 0.9em;
  font-size: var(--step--1);
  color: var(--ink-mid);
  font-style: italic;
}

/* Retired #map-legend list is gone; reused by the panel's Map key rows
   (layers-panel.js). */
.map-legend-swatch {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  border-radius: 50%;
  border: 1px solid color-mix(in oklab, var(--ink) 25%, transparent);
  flex-shrink: 0;
}

.map-footnote {
  max-width: var(--measure);
  color: var(--ink-soft);
  font-size: var(--step--1);
  margin-top: 1rem;
}

.map-footnote a {
  color: var(--water-deep);
}

/* Leaflet popup card content — DitchBook-flavored, not Leaflet defaults. */

.leaflet-popup-content-wrapper {
  border-radius: 6px;
}

.leaflet-popup-content {
  font-family: var(--font-sans);
  margin: 0.9em 1em;
  min-width: 220px;
}

.map-card h2 {
  font-family: var(--font-serif);
  font-size: var(--step-0);
  margin: 0 0 0.35em;
  color: var(--ink);
}

.map-card dl {
  margin: 0 0 0.6em;
  font-size: var(--step--1);
  color: var(--ink-mid);
}

.map-card dt {
  font-weight: 600;
  color: var(--ink-soft);
  display: inline;
}

.map-card dd {
  display: inline;
  margin: 0 0 0 0.3em;
}

.map-card dd::after {
  content: "";
  display: block;
}

.map-card .map-card-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.8em;
  margin-top: 0.5em;
}

.map-card .map-card-links a {
  font-weight: 600;
  color: var(--water-deep);
  text-decoration: none;
}

.map-card .map-card-links a:hover,
.map-card .map-card-links a:focus {
  text-decoration: underline;
}

.map-waterbody-card {
  max-width: 390px;
}

.map-waterbody-eyebrow {
  display: inline-flex;
  align-items: center;
  margin: 0 0 0.45em;
  padding: 0.22em 0.55em;
  border-radius: 999px;
  background: #e2e8f0;
  color: #334155;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.map-waterbody-status-dwr_linked {
  background: #e0f2fe;
  color: #075985;
}

.map-waterbody-status-public_site_verified {
  background: #d1fae5;
  color: #065f46;
}

.map-waterbody-status-visual_only {
  background: #fef3c7;
  color: #92400e;
}

.map-waterbody-note {
  margin: 0.65em 0 0;
  padding: 0.65em 0.75em;
  border-left: 3px solid #94a3b8;
  background: #f8fafc;
  color: var(--ink-soft);
  font-size: var(--step--1);
  line-height: 1.45;
}

.map-card .map-waterbody-sources {
  gap: 0.45em 0.8em;
  padding-top: 0.15em;
  border-top: 1px solid var(--line);
}

.map-river-popup-subtitle,
.map-river-popup-intro,
.map-river-popup-footer {
  margin: 0 0 0.4em;
  font-size: var(--step--1);
  color: var(--ink-soft);
}

.map-river-popup-subtitle {
  font-style: italic;
}

.map-river-popup-list {
  list-style: none;
  margin: 0 0 0.6em;
  padding: 0;
  max-height: 10rem;
  overflow-y: auto;
}

.map-river-popup-list a,
.map-river-popup-footer a {
  color: var(--water-deep);
}

.map-river-popup-list a {
  font-weight: 600;
  text-decoration: none;
}

.map-river-popup-list a:hover,
.map-river-popup-list a:focus {
  text-decoration: underline;
}

/* Marker focus ring for keyboard users tabbing through pins (circleMarkers
   render as SVG <path class="leaflet-interactive">; reservoir markers are
   divIcons, focus ring applied to the wrapping element instead). */
path.leaflet-interactive:focus,
path.leaflet-interactive:focus-visible,
.ditch-marker-reservoir:focus,
.ditch-marker-reservoir:focus-visible {
  outline: 3px solid var(--water);
  outline-offset: 1px;
}

/* Reservoir pin — a triangle, same basin color as the ditch circle marker.
   Plain CSS clip-path, no image asset, so it stays vendor-free. */
.ditch-marker-reservoir {
  background: none;
  border: none;
}

.reservoir-shape {
  display: block;
  width: 16px;
  height: 16px;
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  border: 1.5px solid #fff;
  box-sizing: border-box;
}

/* round-2 fix item 17: a co-located company pin rendered as a pixel-offset
   divIcon (see markerFor's pixelOffset param in map.js) instead of a
   circleMarker -- the wrapping element is a self-contained >=24px touch
   target (iconSize) with this small visual dot centered inside it via
   flexbox, matching the circleMarker look it stands in for. */
.ditch-marker-offset {
  background: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

.offset-dot-shape {
  display: block;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 1.5px solid #fff;
  box-sizing: border-box;
}

/* Retired #map-legend-shapekey paragraph is gone; glyphs stay, reused by
   the panel's Water-record rows + Map key (layers-panel.js). */
.shapekey-circle,
.shapekey-triangle {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  background: var(--ink-soft);
  flex-shrink: 0;
}

.shapekey-circle {
  border-radius: 50%;
}

.shapekey-triangle {
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

/* round-2 fix item 18: two line-swatch rows added to the shape key.
   Colors match map.js's DITCH_LINE_STYLE (canal/river) -- build-guarded
   for parity in www/generate-directory.mjs, but that guard checks map.js
   against the generator's own JS constant, not this CSS; kept in sync by
   hand, same as the rest of this file's basin/line colors. */
.shapekey-line {
  display: inline-block;
  width: 1.1em;
  height: 3px;
  border-radius: 2px;
  flex-shrink: 0;
}

.shapekey-line-canal {
  background: #c25100;
}

.shapekey-line-river {
  background: #028697;
}

/* Item 4 / Leap A: a filled swatch, like the circle/triangle pin shapes
   above, not a line stroke -- a reservoir pool is an area, not an
   alignment. Colors match map.js's RESERVOIR_POOL_STYLE -- build-guarded
   for parity in www/generate-directory.mjs the same way DITCH_LINE_STYLE
   is; kept in sync by hand here, same as .shapekey-line-canal/-river
   above. This row (and its swatch) only ever appears in the DOM once
   www/map/map.js's buildLegend() sees a non-zero reservoirPoolCompanyCount
   -- see that function's own comment. */
.shapekey-reservoir-pool {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  background: #1a5780;
  border: 1px solid #003e64;
  border-radius: 2px;
  flex-shrink: 0;
}

/* Timeline UI (slider/Play/caption/tier-note) moved into layers-panel.js's
   Timeline section, CSS into its lazy PANEL_CSS; engine stays in map.js.
   The retired #map-legend list's click-to-highlight outline has no
   surviving target -- see map.js's _ditchbookHighlightBasin comment. */

/* ---------- oldest-ten list — normal page flow below the map, every
   screen size; never part of the floating chrome (D7). ---------- */

.map-oldest {
  margin: 2rem 0;
  max-width: var(--measure);
}

.map-oldest h2 {
  font-size: var(--step-1);
  margin-bottom: 0.75rem;
}

.map-oldest-list {
  list-style: decimal;
  padding-left: 1.4em;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5em;
  font-size: var(--step--1);
}

.map-oldest-year {
  font-weight: 600;
  color: var(--ink);
  margin-right: 0.4em;
}

.map-oldest-list a {
  color: var(--water-deep);
  font-weight: 600;
  text-decoration: none;
}

.map-oldest-list a:hover,
.map-oldest-list a:focus {
  text-decoration: underline;
}

.map-oldest-source {
  color: var(--ink-soft);
  margin-left: 0.4em;
}

.map-oldest-source::before {
  content: "— ";
}

/* ---------- address search results: side card (desktop) ---------- */

.map-address-card {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  bottom: 0.75rem;
  width: min(20rem, calc(100% - 6rem));
  z-index: 920;
  background: var(--paper);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.28);
  padding: 1rem 1.1rem;
  /* round-2 fix item 11: the card itself no longer scrolls as a whole —
     #map-address-card-body (below) is the scrolling region, so
     #map-address-card-footer (the Ditch Finder link) can stay a normal,
     always-visible flex item beneath it instead of scrolling out of
     view. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Bug found in browser verification: an author-stylesheet `display`
   declaration on a selector of equal-or-greater specificity overrides the
   UA stylesheet's default `[hidden] { display: none; }` — so the
   `display: flex` above, unconditional, was leaving this card visible
   (as an empty box) even while its `hidden` attribute was set. Higher
   specificity than the bare class selector above re-asserts the hide. */
.map-address-card[hidden] {
  display: none;
}

#map-address-card-body {
  flex: 1 1 auto;
  overflow-y: auto;
  min-height: 0; /* let this flex child actually shrink/scroll instead of growing to fit all its content */
}

.map-address-close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  /* round-2 fix item 14: was 36px. */
  min-width: 44px;
  min-height: 44px;
  border: none;
  background: none;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--ink-soft);
  cursor: pointer;
  border-radius: 6px;
}

.map-address-close:hover,
.map-address-close:focus-visible {
  background: var(--water-tint);
  color: var(--ink);
  outline: none;
}

#map-address-card-body h2 {
  font-family: var(--font-serif);
  font-size: var(--step-0);
  margin: 0 1.75rem 0.6em 0;
  color: var(--ink);
}

.map-address-results {
  list-style: none;
  margin: 0 0 0.9em;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6em;
  font-size: var(--step--1);
}

.map-address-results li {
  padding-bottom: 0.6em;
  border-bottom: 1px solid var(--border);
}

.map-address-results a {
  color: var(--water-deep);
}

.map-address-honest {
  font-size: 0.85rem;
  color: var(--ink-mid);
  margin: 0.75em 0;
}

/* round-2 fix item 11: #map-address-card-body scrolls internally
   (flex:1 1 auto + overflow-y:auto below, on .map-address-card /
   #map-address-card-body) while #map-address-card-footer stays a normal,
   non-scrolling flex item — "See full results at Ditch Finder" is
   reachable without scrolling even when the results list is long enough
   to need its own internal scroll. Hidden (via JS, footer.hidden) for the
   simple loading/error messages that don't set footer content, so no
   empty bordered bar shows for those. */
#map-address-card-footer {
  flex: 0 0 auto;
  padding-top: 0.75em;
  margin-top: 0.5em;
  border-top: 1px solid var(--border);
}

/* The searched-address marker — deliberately NOT a basin-colored circle or
   triangle (must never be mistaken for a verified structure), no radius
   circle (misreads as a service area). A dark teardrop pin instead. */
.map-address-marker-wrap {
  background: none;
  border: none;
}

.map-address-marker {
  display: block;
  width: 20px;
  height: 20px;
  position: relative;
  transform: rotate(-45deg);
  border-radius: 50% 50% 50% 0;
  background: var(--ink);
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}

/* ---------- "Follow the water" (decisions 1-5, approved by Nick
   2026-08-08) — specs/follow-the-water.md. Ships fully dormant: nothing
   below ever renders (no button, no card) until a structure carries a
   real s.journey — see map.js's journeyEligible() and
   www/generate-directory.mjs's journeysByWdid. ---------- */

.map-journey-follow-btn {
  min-height: 44px;
  font-size: 0.85rem;
}

/* Inside a Leaflet popup (structure-tier click) — the popup itself is
   narrow (min-width 220px, .leaflet-popup-content above), so the button
   reads better as its own full-width row than crowded onto the links
   line above it. */
.map-card .map-journey-follow-btn {
  display: block;
  width: 100%;
  margin-top: 0.6em;
  text-align: center;
}

/* Inside the address card's nearest-structure list — a narrower, inline
   companion to the WDID link already on that line, not a full row. */
.map-address-results .map-journey-follow-btn {
  display: inline-block;
  margin: 0.4em 0 0;
}

/* The quiet journey card — same shape/shadow/positioning as
   .map-address-card (deliberately reused, not reinvented: both are
   "quiet detail card" surfaces). z-index sits ABOVE the address card
   (920): entry point (c)'s own button lives INSIDE the address card, so
   activating a journey from there must not be hidden behind it — the two
   can occupy the same screen slot; closing the journey card (its own
   close button) reveals the address card again, unchanged underneath. */
.map-journey-card {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  bottom: 0.75rem;
  width: min(20rem, calc(100% - 6rem));
  z-index: 930;
  background: var(--paper);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.28);
  padding: 1rem 1.1rem;
  overflow-y: auto;
}

.map-journey-card[hidden] {
  display: none;
}

.map-journey-card h2 {
  font-family: var(--font-serif);
  font-size: var(--step-0);
  margin: 0 1.75rem 0.6em 0;
  color: var(--ink);
}

.map-journey-caption {
  font-size: var(--step--1);
  color: var(--ink-mid);
  margin: 0 0 0.75em;
}

/* The boundary line — approved verbatim, always visible alongside the
   caption above (never in place of it). */
.map-journey-boundary {
  font-size: 0.82rem;
  font-style: italic;
  color: var(--ink-soft);
  margin: 0;
  padding-top: 0.6em;
  border-top: 1px solid var(--border);
}

.map-journey-close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  min-width: 44px;
  min-height: 44px;
  border: none;
  background: none;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--ink-soft);
  cursor: pointer;
  border-radius: 6px;
}

.map-journey-close:hover,
.map-journey-close:focus-visible {
  background: var(--water-tint);
  color: var(--ink);
}

@media (max-width: 768px), (max-height: 500px) {
  .map-journey-card {
    top: auto;
    left: 0;
    right: 0;
    bottom: var(--map-mobile-attribution-gutter);
    width: 100%;
    max-height: 45vh;
    border-radius: 10px 10px 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
  }
}

/* The comet: a single declarative CSS animation (compositor-driven, no
   per-frame JS) traveling a short dash along the downsampled canal+river
   chain (map.js's drawJourney -- one L.polyline, className
   "journey-pulse-path"). Path length in SVG user-space (pixels, at the
   current Leaflet zoom) varies by company/zoom -- the travel distance
   below is sized generously so the effect reads as continuous flow
   regardless of the exact figure, not to complete an exact lap. */
@keyframes journey-pulse-travel {
  from {
    stroke-dashoffset: 0;
  }
  to {
    stroke-dashoffset: -600;
  }
}

.journey-pulse-path {
  stroke-dasharray: 14 34;
  animation: journey-pulse-travel 2.2s linear infinite;
}

/* Belt-and-suspenders: drawJourney() never creates .journey-pulse-path
   under reduced-motion (draws .journey-static-path instead) -- this rule
   is defense in depth only, should that JS branch ever regress. */
@media (prefers-reduced-motion: reduce) {
  .journey-pulse-path {
    animation: none;
  }
}

/* Reduced motion: a single static highlighted path with a real SVG
   direction arrowhead (marker-end, auto-oriented -- see
   ensureJourneyArrowMarker() in map.js) instead of any animation. */
.journey-static-path {
  marker-end: url(#journey-arrow-marker);
}

/* River-end-to-headgate bridge connector (red-team fix, 2026-08-08): a
   short, visually distinct dotted segment -- never solid watercourse
   styling. Drawn only when the gap is sane (JOURNEY_BRIDGE_MAX_GAP_METERS
   in map.js); static (never animated). */
.journey-bridge-path {
  stroke-dasharray: 2 6;
}

/* alignment-connectors.md D1: river connector tier-gate. */
#ditch-map.tier-company .alignment-connector {
  display: none;
}

/* Headgate arrival emphasis (Nick, 2026-08-08): small, subtle, looping
   ring at the structure's own point -- decorative only (map.js sets
   interactive:false/keyboard:false, so it never intercepts the real pin's
   click target). Duration comparable to the pulse's 2.2s loop, never
   frame-synced to it. */
.journey-headgate-emphasis {
  background: none;
  border: none;
  pointer-events: none;
}

.journey-headgate-ring {
  display: block;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 3px solid #e8a33d;
  box-sizing: border-box;
  animation: journey-headgate-arrival 2.2s ease-in-out infinite;
}

@keyframes journey-headgate-arrival {
  0%,
  100% {
    opacity: 0.25;
    transform: scale(0.7);
  }
  50% {
    opacity: 0.9;
    transform: scale(1.15);
  }
}

/* ---------- satellite mode: white-halo treatment so the basin palette
   stays legible over imagery (D7) — extends the ditch-line halo pattern
   already used elsewhere on the site. ---------- */

#ditch-map.satellite-on .reservoir-shape {
  border-width: 3px;
}

/* round-2 fix item 19: the pixel-offset dot (item 17) is a divIcon like
   reservoir markers, not a circleMarker, so it needs the same CSS-driven
   halo (JS's per-marker `weight` style only applies to circleMarker/path
   layers) -- and CSS is inherently always-current regardless of WHEN the
   marker was built relative to the satellite toggle, sidestepping the
   staleness problem entirely for this marker type. */
#ditch-map.satellite-on .offset-dot-shape {
  border-width: 3px;
}

#ditch-map.satellite-on .leaflet-control-attribution {
  background: rgba(255, 255, 255, 0.85);
}

/* Leaflet attribution — compact on small screens (D6: stays visible, just
   smaller), never removed. */

.leaflet-control-attribution {
  font-size: 0.72rem;
}

/* round-3 fix item 6: width-only `(max-width:640px)` missed the 641-768px
   band and wide-but-short landscape phones -- matches isSmallScreen() in
   map.js (width<=768 || height<=500) so JS and CSS never disagree on
   layout mode. */
@media (max-width: 768px), (max-height: 500px) {
  /* round-2 fix item 10: dedicated bottom gutter for Leaflet's attribution
     control, sized for the 4-line USGS string satellite mode wraps to at
     this width. .map-sheet/.map-address-card anchor ABOVE this gutter
     (not true bottom:0) so the sheet's chevron can't overlap the
     OpenStreetMap link under it. 4.5rem measured live at 375px satellite
     (~58px real box; the tighter 3.75rem first tried left too little
     slack). Declared on body.map-body (a real selector), same pattern as
     --map-header-h. */
  body.map-body {
    --map-mobile-attribution-gutter: 4.5rem;
  }

  /* mobile #map-sheet docks at the larger gutter above -- measured live.
     N1 fix: same --map-layers-strip-extra tracking as the desktop rule
     above, added on top of mobile's own larger base clearance. */
  .leaflet-bottom .map-layers-control {
    margin-bottom: calc(9rem + var(--map-layers-strip-extra, 0px));
  }

  /* right stays clear of hero mode's topright "Open the full map" button
     (D7-era sizing, kept as headroom now the layers control moved to
     bottom-left -- D1) rather than spanning edge-to-edge. */
  .map-overlay-search {
    right: 5.5rem;
    max-width: none;
  }

  /* Full-width permanent bar -- nothing slides up on tap anymore. */
  .map-sheet {
    left: 0;
    right: 0;
    bottom: var(--map-mobile-attribution-gutter);
    width: 100%;
    max-width: none;
    border-radius: 10px 10px 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
  }

  /* Address results become a bottom sheet too on small screens, matching
     D5's "side card (desktop) / bottom sheet (mobile)" split. round-2 fix
     item 11: capped at ~45% of the viewport (was 70%) so the card never
     dominates the screen the way the (much longer, now three-part)
     content could otherwise push it to — #map-address-card-body scrolls
     internally past that, #map-address-card-footer (the Ditch Finder
     link) stays visible without scrolling either way (see the flex rules
     on .map-address-card above). */
  .map-address-card {
    top: auto;
    left: 0;
    right: 0;
    bottom: var(--map-mobile-attribution-gutter);
    width: 100%;
    max-height: 45vh;
    border-radius: 10px 10px 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
  }

  .leaflet-control-attribution {
    font-size: 0.65rem;
    max-width: 40vw;
  }
}

/* round-3 fix item 6: the logical complement of the block above -- both
   conditions must fail (wide enough AND tall enough) before the desktop
   side-card sizing applies, matching isSmallScreen()'s OR predicate. */
@media (min-width: 769px) and (min-height: 501px) {
  .map-address-card {
    max-height: min(70vh, 32rem);
  }
}

/* ============================================================
   Homepage hero-mode map embed (specs/homepage-map-first.md D1/D2) — the
   SAME #ditch-map/.map-frame/.map-frame-outer markup as /map/, hero-gated
   by data-map-hero (see isHeroMode in map.js). .map-hero/[data-map-hero]
   are homepage-only; .map-open-full-btn is also reused by the layers
   panel's control on both pages (D1). ============================ */

.map-hero {
  /* Zeroes styles.css's generic `section { padding: var(--space-section) 0;
     border-bottom: ...; }` (~88px top+bottom) — left alone it ate H4's
     below-fold sliver budget. Same fix /map/'s .map-page already applies.
     .hero right below keeps its own border-bottom as the section divider. */
  position: relative;
  padding: 0;
  border-bottom: none;
}

.map-hero-heading {
  /* Longhand, not the `padding` shorthand: this class rides alongside
     .container on the same element (both single-class, same specificity;
     map.css loads after styles.css) -- a shorthand here would zero out
     .container's own left/right padding instead of just adding top/bottom. */
  padding-top: 0.75rem;
  padding-bottom: 0.6rem;
}

.map-hero-line {
  margin: 0;
  max-width: var(--measure);
  font-family: var(--font-serif);
  font-size: var(--step-0);
  color: var(--ink-mid);
}

/* D2: ~70vh desktop / ~60vh mobile — overrides .map-frame's /map/ height
   rule (calc(100vh - header)), scoped under .map-hero so /map/ (never
   inside that section) is unaffected. H4's own floor (>=60% desktop,
   >=55vh mobile) is looser than these targets on purpose, leaving margin
   for the header + heading above the frame and a sliver below the fold. */
.map-hero .map-frame {
  height: 70vh;
  height: 70dvh;
  min-height: 420px;
}

@media (max-width: 768px) {
  .map-hero .map-frame {
    height: 60vh;
    height: 60dvh;
    min-height: 320px;
  }
}

/* ---------- "Open the full map" control (D1, hero mode only) ---------- */

.map-open-full-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  background: #ffffff;
  color: var(--water-deep);
  border-color: var(--water-deep);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
  min-height: 44px;
  min-width: 44px;
  padding: 0.55em 1em;
  font-size: 0.95rem;
}

.map-open-full-btn:hover,
.map-open-full-btn:focus {
  background: var(--water-tint);
  color: var(--water-deep);
}

@media (max-width: 768px), (max-height: 500px) {
  .map-open-full-btn {
    padding: 0.5em 0.7em;
  }

  /* Icon-only at this width (clip-rect recipe, same as styles.css's
     .visually-hidden but scoped here since that class is unconditional).
     aria-label on the control (map.js) keeps the accessible name intact. */
  .map-open-full-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
}

/* ---------- search-results list: directory.css fallback ----------
   The homepage doesn't load directory.css. map.js still emits the SAME
   .company-search-results/.company-search-result-name classes for a
   "mapped" row; PER-ROW styling below has only ever lived in
   directory.css — reproduced here, scoped to .map-hero, kept byte-
   identical by hand. Skips list/margin/max-width/border (already set by
   .map-search-results, sitewide, above — redeclaring would outrank it by
   specificity). */
.map-hero .company-search-results {
  list-style: none;
  padding: 0;
}

.map-hero .company-search-results li {
  border-bottom: 1px solid var(--border);
}

/* fix-round item 4c: was `.map-hero .company-search-results a` -- matched
   EVERY anchor in the results list, including ones nested inside
   .map-search-cta/.search-empty (plain inline sentences, see
   .map-search-cta a above), outranking that rule by specificity and
   wrongly giving CTA/verified/empty-state links this 56px block
   treatment. map.js now stamps a dedicated .company-search-result-link
   class on exactly the matched-company ROW anchor, so scoping here
   reaches only real rows. */
.map-hero .company-search-result-link {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.2em;
  min-height: 56px;
  padding: 0.75em 0.6em;
  color: var(--ink);
  text-decoration: none;
  border-radius: 6px;
}

.map-hero .company-search-result-link:hover,
.map-hero .company-search-result-link:focus-visible {
  background: var(--water-tint);
  outline: none;
}

.map-hero .company-search-result-link:focus-visible {
  box-shadow: inset 0 0 0 2.5px var(--water-deep);
}

.map-hero .company-search-result-name {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--step-1);
  color: var(--water-deep);
}

.map-hero .company-search-results .search-empty {
  border-bottom: none;
  padding: 0.9em 0.6em;
  font-size: var(--step-0);
  color: var(--ink-soft);
}
