/* ============================================================================
   CampingSorted — Explore (discover → park → site → alert)
   Extends the "field guide at dawn" token system from styles.css. Adds the
   discovery map, park/site cards, the site list, filter panel, and availability
   chips. Never redefines a :root token; only consumes them. No inline styles
   (strict CSP). No external assets.
   ========================================================================== */

/* availability state palette — derived from the existing forest/ember system,
   plus a calm slate for the honest "checking" (NEVER reuse the booked red). */
:root {
  --avail-open:    #2c5a43;   /* forest — bookable */
  --avail-open-bg: rgba(44, 90, 67, 0.12);
  --avail-shut:    #a8392a;   /* clay-red — booked/held (a real state) */
  --avail-shut-bg: rgba(168, 57, 42, 0.10);
  --avail-soon:    #b14f1c;   /* ember-deep — window-not-yet-open ("opens later") */
  --avail-soon-bg: rgba(177, 79, 28, 0.10);
  --avail-check:   #6b7280;   /* neutral slate — "checking", never a verdict */
  --avail-check-bg: rgba(107, 114, 128, 0.12);
  --avail-closed:  #55607a;   /* dusk slate-blue — closed for season (a definite
                                 verdict, but not the red "booked" or amber "soon") */
  --avail-closed-bg: rgba(85, 96, 122, 0.12);
  --avail-fcfs:    #8a6a1f;   /* trailhead gold — first-come-first-served: an
                                 action to take, not a reservation to make;
                                 deliberately distinct from every other hue so
                                 it can never be mistaken for "is-open" */
  --avail-fcfs-bg: rgba(138, 106, 31, 0.12);
  --avail-not-reservable:    #6a5c8a;   /* slate-violet — NOT_RESERVABLE: the source
                                 says this site can't be reserved without saying why
                                 (docs/design/non-reservable-state §3.6). Deliberately
                                 its own hue — distinct from fcfs gold (a known
                                 action), soon amber (a stated pending window), and
                                 closed slate-blue (a stated shutdown), since none of
                                 those claims is true here. */
  --avail-not-reservable-bg: rgba(106, 92, 138, 0.12);
}

/* The explore body uses the same paper/contour/grain background from styles.css;
   give it a touch more room than the marketing pages. */
.explore-body { padding-bottom: 1px; }

/* off-screen icon sprite host (no inline style — CSP-clean) */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* a chip + its "demo" qualifier sit together */
.avail-pair { display: inline-flex; align-items: center; gap: 0.4rem; }

/* The shared header (chrome.js) is styled entirely in styles.css, which every page
   including this one loads first. Nothing about `.site-header` / `.header-nav` /
   `.nav-toggle` belongs here: a second declaration in this file would load AFTER
   styles.css and silently win at equal specificity, so the SPA's header would stop
   matching the static pages' — and the mobile drawer's `display: none` is exactly
   the kind of rule such a copy would override. */

/* ---- breadcrumbs --------------------------------------------------------- */
.crumbs {
  position: relative; z-index: 2;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem;
  margin: 0.9rem auto 0;
  font-size: 0.82rem; color: var(--sage); letter-spacing: 0.02em;
}
.crumbs a { color: var(--forest-deep); text-decoration: none; font-weight: 600; }
.crumbs a:hover { color: var(--ember-deep); }
.crumbs .sep { opacity: 0.5; }
.crumbs .here { color: var(--ink); font-weight: 600; }

/* ---- generic loading / spinner ------------------------------------------- */
.view-loading {
  display: flex; flex-direction: column; align-items: center; gap: 0.9rem;
  padding: clamp(3rem, 12vw, 6rem) 0; color: var(--sage);
  font-family: var(--font-display); font-style: italic; font-size: 1.1rem;
}
.spinner {
  width: 2rem; height: 2rem; border-radius: 999px;
  border: 3px solid var(--line-strong); border-top-color: var(--forest);
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2s; } }

/* ---- a refusal / empty panel (honest "we don't know yet") ---------------- */
.notice {
  margin: 1.6rem 0 0; padding: clamp(1.2rem, 4vw, 1.8rem);
  background: var(--paper-card); border: 1.5px solid var(--line);
  border-left: 4px solid var(--avail-check); border-radius: var(--radius-lg);
  box-shadow: 0 18px 36px -30px rgba(28, 63, 47, 0.5);
}
.notice.is-err { border-left-color: var(--avail-shut); }
.notice.is-check { border-left-color: var(--avail-check); }
.notice h2 {
  font-family: var(--font-display); font-weight: 600;
  font-size: 1.3rem; margin: 0; color: var(--forest-deep);
}
.notice p { margin: 0.5rem 0 0; color: var(--sage); }
.notice .wz-btn-secondary { margin-top: 1rem; }

/* A1 planning banner (opening-alerts) — booking-window-not-open-yet surface. */
.planning-banner.notice.is-soon { border-left-color: var(--avail-soon); }
.planning-banner__head {
  margin: 0; font-family: var(--font-display); font-weight: 600;
  font-size: 1.15rem; color: var(--forest-deep);
}
.planning-banner__ic { font-style: normal; }
.planning-banner__when { margin: 0.5rem 0 0; color: var(--forest-deep); font-weight: 500; }
.planning-banner__note { margin: 0.4rem 0 0; color: var(--sage); font-size: 0.92rem; }
.planning-banner__cta { margin-top: 1rem; }

/* ============================================================================
   DISCOVER VIEW
   ========================================================================== */
.discover-head { padding: clamp(1.4rem, 4vw, 2.4rem) 0 0; max-width: 40rem; }
.discover-head h1 {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(2rem, 5.5vw, 3rem); line-height: 1.02;
  letter-spacing: -0.02em; margin: 0.5rem 0 0; color: var(--ink);
}
.discover-head .lede { margin-top: 0.8rem; }

/* ---- park-page date row (label + the shared wz-cal chip) ------------------ */
.park-date-row {
  display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
  margin: 0 0 1rem;
}
.park-date-label {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-weight: 600; color: var(--forest); font-size: 0.95rem;
}
.park-date-label .ic { width: 1.05rem; height: 1.05rem; }
/* the chip wrapper shouldn't add the discover top margin inside the row */
.park-date-row .dc-cal-wrap { margin: 0; flex: 1 1 auto; min-width: 14rem; }

/* ---- discover date picker (wz-cal shared widget, collapsible) ------------- */
/* Outer wrapper — collapses to just the toggle chip; opens to show the calendar. */
.dc-cal-wrap {
  margin: 1.2rem 0 0;
}
/* The toggle chip that shows the currently chosen date range (or "Any dates"). */
.dc-cal-toggle {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.62rem 1rem;
  font: inherit; font-size: 0.95rem; font-weight: 600; color: var(--forest-deep);
  background: var(--paper-card);
  border: 1.5px solid var(--line-strong); border-radius: calc(var(--radius) + 4px);
  cursor: pointer;
  box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;
  transition: border-color 0.15s var(--ease), color 0.15s var(--ease), background 0.15s var(--ease);
}
.dc-cal-toggle .ic { width: 1.1rem; height: 1.1rem; color: var(--forest); flex: none; }
.dc-cal-toggle .chev { font-size: 0.85rem; transition: transform 0.2s var(--ease); color: var(--sage); }
.dc-cal-toggle:hover { border-color: var(--forest); background: var(--paper-soft); }
.dc-cal-toggle:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
/* When dates are set, tint the toggle to signal an active filter. */
.dc-cal-wrap:not(.is-collapsed) .dc-cal-toggle,
.dc-cal-wrap .dc-cal-toggle[aria-expanded="true"] {
  border-color: var(--forest); color: var(--forest-deep); background: var(--paper-soft);
}
.dc-cal-wrap .dc-cal-toggle[aria-expanded="true"] .chev { transform: rotate(-180deg); }

/* Calendar panel (hidden when collapsed). */
.dc-cal-wrap.is-collapsed .dc-cal { display: none; }
.dc-cal {
  margin-top: 0.5rem;
  /* Inherits all .wz-cal styles from styles.css — we only add discover-context
     overrides here (max-width, drop shadow). */
  max-width: 22rem;
}

/* Footer row inside the calendar: "Clear dates" button. */
.dc-cal-footer {
  margin-top: 0.75rem;
  display: flex; justify-content: flex-end;
}
.dc-cal-clear {
  font: inherit; font-size: 0.84rem; font-weight: 600; color: var(--ember-deep);
  background: transparent; border: 0; cursor: pointer; padding: 0.2rem 0;
  text-decoration: underline; text-underline-offset: 2px;
}
.dc-cal-clear:hover { color: var(--ember); }
.dc-cal-clear:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }

/* search + near-me control bar */
.discover-controls {
  display: flex; flex-wrap: wrap; gap: 0.7rem; margin: 1.4rem 0 0;
  align-items: stretch;
}
.search-box {
  flex: 1 1 18rem; min-width: 0; display: flex; align-items: center; gap: 0.4rem;
  padding: 0.2rem 0.2rem 0.2rem 0.9rem;
  background: var(--paper-card); border: 1.5px solid var(--line-strong);
  border-radius: calc(var(--radius) + 4px);
  box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset, 0 10px 30px -24px rgba(28,63,47,0.5);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.search-box:focus-within {
  border-color: var(--forest);
  box-shadow: 0 0 0 3px rgba(44, 90, 67, 0.16);
}
/* Defensive base size for inline icons — every icon SVG carries class "ic".
   Per-context rules (.parent .ic) are more specific and keep their own sizes;
   this only catches an icon placed in a context that forgot to size it, so a
   missing rule degrades to a small icon instead of the SVG's huge intrinsic
   size (the "Hookups & type" oversized-logo bug). */
.ic { width: 1rem; height: 1rem; flex: none; }
.search-box .ic { width: 1.2rem; height: 1.2rem; color: var(--sage); flex: none; }
.search-box input {
  flex: 1 1 auto; min-width: 0; padding: 0.7rem 0.4rem;
  font: inherit; font-size: 1rem; color: var(--ink);
  background: transparent; border: 0;
}
.search-box input:focus { outline: none; }
.search-box input::placeholder { color: #9aa093; }

/* The location field is a `.search-box` with a slightly narrower basis and the
   pin in the brand green. Declared here, beside the base rule and BEFORE the
   mobile-polish block, because the stacked-bar override resets exactly this
   `flex` shorthand — declared after that block it would silently win and the
   field would keep rendering as a 16rem-TALL box on a phone. */
.loc-box { flex: 1 1 16rem; }
.loc-box .ic { color: var(--forest); }

.btn-near {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.7rem 1.1rem; font: inherit; font-size: 0.96rem; font-weight: 700;
  color: var(--forest-deep); background: var(--paper);
  border: 1.5px solid var(--line-strong); border-radius: calc(var(--radius) + 4px);
  cursor: pointer; transition: border-color 0.15s var(--ease), color 0.15s var(--ease), background 0.15s var(--ease);
}
.btn-near .ic { width: 1.2rem; height: 1.2rem; flex: none; }
.btn-near:hover { border-color: var(--forest); background: var(--paper-soft); }
.btn-near:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
.btn-near[disabled] { opacity: 0.6; cursor: progress; }
.btn-near.is-active { border-color: var(--ember); color: var(--ember-deep); }

/* ONE honesty line. `.discover-status`, `.scope-note` and `.resolved-loc` are three
   short phrases about the same thing — what this list is and how we scoped it — and
   as block <p>s with their own margins they stacked ~90px of near-empty rows above
   the results. Laid out as one wrapping row the gap IS the separator, and the row
   collapses to nothing when every phrase is empty (each child already hides itself
   via :empty / [hidden]). Every node keeps its own class, testid and ARIA. */
.discover-honesty {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 0.1rem 0.55rem; margin: 0.7rem 0 0;
}
.discover-honesty:empty { display: none; }
.discover-status {
  margin: 0; min-height: 1.2em;
  /* Raised from 0.9rem/0.82rem: this line carries "change it", and the fix for a
     wrong location guess must not be a link inside the smallest text on the page. */
  font-size: 0.82rem; color: var(--sage);
}
.discover-status:empty { min-height: 0; }
.discover-status.is-err { color: var(--avail-shut); }

/* the map-beside-list layout (desktop) */
.discover-layout {
  display: grid; gap: clamp(1.2rem, 3vw, 2rem);
  grid-template-columns: 1fr; /* mobile: stacked */
  margin: 1.4rem 0 0;
}
@media (min-width: 900px) {
  .discover-layout { grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); align-items: start; }
  .discover-layout .map-wrap { position: sticky; top: 1rem; }
}

/* ---- the self-hosted locator map ----------------------------------------- */
.map-wrap {
  background: var(--paper-card); border: 1.5px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: 0 22px 44px -32px rgba(28, 63, 47, 0.55);
}
.map-toggle {
  display: none; /* desktop: always visible */
  width: 100%; align-items: center; justify-content: space-between; gap: 0.5rem;
  padding: 0.8rem 1rem; font: inherit; font-size: 0.92rem; font-weight: 700;
  color: var(--forest-deep); background: var(--paper-soft);
  border: 0; border-bottom: 1.5px solid var(--line); cursor: pointer;
}
.map-toggle .chev { transition: transform 0.2s var(--ease); }
.map-wrap.is-collapsed .map-toggle .chev { transform: rotate(-90deg); }
/* MapLibre map container — needs an explicit height (the canvas fills it). */
.map-figure { margin: 0; position: relative; display: block; height: 460px; border-radius: var(--radius-lg); overflow: hidden; }
.map-figure .maplibregl-map { width: 100%; height: 100%; font: inherit; }
.map-wrap.is-collapsed .map-figure { display: none; }
@media (max-width: 640px) { .map-figure { height: 320px; } }

/* map-degraded: low-geo area → label "use the list", dim the half-empty map */
.map-degraded {
  display: flex; align-items: center; gap: 0.45rem;
  margin: 0 0 0.6rem; padding: 0.6rem 0.8rem;
  background: var(--paper-soft); border: 1.5px solid var(--line-strong);
  border-radius: var(--radius); font-size: 0.9rem; font-weight: 600; color: var(--forest-deep);
}
.map-degraded[hidden] { display: none; }
.map-degraded .ic { width: 1.05rem; height: 1.05rem; color: var(--sage); flex: none; }
.map-wrap.is-degraded .map-figure { opacity: 0.55; }

/* multi-action footer for the result-state notices (no-match / no-coverage) */
.notice-actions { margin-top: 1rem; display: flex; gap: 0.5rem; flex-wrap: wrap; }
.notice-actions .wz-btn-secondary { margin-top: 0; }

/* notice-inline: viewport-SELECT honesty cues (checking / more-regions) — a
   compact inline banner above the park list, distinct from the full notice
   panel (result-no-match / result-no-coverage) since it can render ALONGSIDE a
   populated list (a "partial" response still carries real parks[]). */
.notice-inline {
  margin: 0 0 0.6rem; padding: 0.5rem 0.75rem;
  background: var(--paper-soft); border: 1.5px solid var(--line-strong);
  border-radius: var(--radius); font-size: 0.85rem; color: var(--forest-deep);
}
.notice-inline.is-check { border-left: 3px solid var(--avail-check); }

/* HTML park-pin markers (coloured dots, availability-honest) */
.ml-pin {
  width: 16px; height: 16px; padding: 0; box-sizing: border-box;
  border: 2px solid var(--paper-card); border-radius: 50%;
  background: var(--avail-check); cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  transition: transform 0.12s var(--ease);
}
.ml-pin:hover, .ml-pin:focus { transform: scale(1.3); outline: none; z-index: 2; }
/* One hue per verdict, matching the site-grain `.avail` chips above. A pin is
   read as a bookability claim, so the states that are NOT bookable — first-come,
   not-reservable, closed, opens-later — each get their own colour rather than
   collapsing into the green/red pair (explore.js parkPinClass; the same rule
   STATE_CLASS states at site grain). */
.ml-pin.is-open  { background: var(--avail-open); }
.ml-pin.is-shut  { background: var(--avail-shut); }
.ml-pin.is-check { background: var(--avail-check); }
.ml-pin.is-soon  { background: var(--avail-soon); }
.ml-pin.is-closed { background: var(--avail-closed); }
.ml-pin.is-fcfs  { background: var(--avail-fcfs); }
.ml-pin.is-not-reservable { background: var(--avail-not-reservable); }
.ml-pin.is-selected {
  background: var(--ember); transform: scale(1.25); z-index: 3;
  box-shadow: 0 0 0 3px rgba(207, 99, 38, 0.35), 0 1px 4px rgba(0, 0, 0, 0.4);
}
.map-legend {
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.1rem;
  padding: 0.7rem 1rem 0.85rem; font-size: 0.78rem; color: var(--sage);
  border-top: 1.5px solid var(--line);
}
/* A legend explains the pins on the map. With the map collapsed there are no
   pins, so it explains nothing — and it is the one child of `.map-wrap` big
   enough to matter: seven wrapping rows is ~100px sitting between a phone
   visitor and the first park card, which is the exact space collapsing the map
   was meant to give back. */
.map-wrap.is-collapsed .map-legend { display: none; }
.map-legend span { display: inline-flex; align-items: center; gap: 0.4rem; }
.map-legend i { width: 0.7rem; height: 0.7rem; border-radius: 999px; flex: none; }
.map-legend .lg-open  { background: var(--avail-open); }
.map-legend .lg-shut  { background: var(--avail-shut); }
.map-legend .lg-check { background: var(--avail-check); }
.map-legend .lg-soon  { background: var(--avail-soon); }
.map-legend .lg-closed { background: var(--avail-closed); }
.map-legend .lg-fcfs  { background: var(--avail-fcfs); }
.map-legend .lg-not-reservable { background: var(--avail-not-reservable); }

/* ---- the park list (discover) -------------------------------------------- */
.park-list { display: grid; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
/* The ANSWER line — this surface's only heading. Not upper-cased and not a label:
   it is a sentence stating what we found, so it reads at sentence weight. */
.park-list .count {
  margin: 0; font-size: 1.05rem; font-family: var(--font-display); font-weight: 600;
  letter-spacing: 0; text-transform: none; color: var(--ink); line-height: 1.25;
}
/* What we do, what you do, where money changes hands — on every results state. */
.discover-model {
  margin: 0.25rem 0 0; font-size: 0.82rem; color: var(--sage); line-height: 1.4;
  max-width: 44rem;
}
/* At most one framing line above the list, never repeated per card. */
.list-framing {
  margin: 0.5rem 0 0; padding: 0.6rem 0.75rem;
  font-size: 0.85rem; line-height: 1.4; color: var(--ink);
  background: var(--paper-soft); border-left: 3px solid var(--moss);
  border-radius: 0 var(--radius) var(--radius) 0;
}
/* Ember means "an action you can take right now" on this surface, and nothing else. */
.list-framing.is-act { border-left-color: var(--ember); font-weight: 600; }
.park-card {
  display: grid; grid-template-columns: 3px 4.6rem minmax(0, 1fr) auto;
  gap: 0 0.7rem;
  align-items: start;
  padding: 0.75rem 0.9rem;
  background: var(--paper-card); border: 1.5px solid var(--line);
  border-radius: var(--radius-lg); text-decoration: none; color: inherit;
  box-shadow: 0 14px 30px -28px rgba(28, 63, 47, 0.5);
  transition: transform 0.15s var(--ease), border-color 0.15s var(--ease), box-shadow 0.2s var(--ease);
}
.park-card:hover {
  transform: translateY(-2px); border-color: var(--forest);
  box-shadow: 0 20px 38px -26px rgba(28, 63, 47, 0.55);
}
.park-card:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
.park-card.is-selected {
  border-color: var(--ember);
  background: rgba(207, 99, 38, 0.08);
  box-shadow: inset 4px 0 0 var(--ember), 0 2px 10px -6px rgba(207, 99, 38, 0.5);
}

/* MapLibre pin popups (hover tooltip + click card) */
.map-pop .maplibregl-popup-content {
  font: inherit; padding: 0.55rem 0.7rem; border-radius: 10px;
  box-shadow: 0 6px 20px -8px rgba(28, 63, 47, 0.5);
}
.map-pop--hover .maplibregl-popup-content { padding: 0.35rem 0.55rem; font-size: 0.88rem; font-weight: 600; color: var(--forest); }
.map-pop__name { margin: 0 0 0.3rem; font-weight: 700; color: var(--forest); }
.map-pop__link { display: inline-block; font-size: 0.9rem; font-weight: 600; color: var(--ember); text-decoration: none; }
.map-pop__link:hover { text-decoration: underline; }
/* ---- THE VERDICT RAIL ------------------------------------------------------
   The signature element. A 3px rule on the card's leading edge with the verdict set
   against it IN A FIXED-WIDTH COLUMN, so twelve cards stack into one column the eye
   can run down without reading a single park name.

   The fixed width is load-bearing, not decoration: let the column flex to the word
   and "OPEN" / "NONE OPEN" / "Checking now…" each start at a different x, the
   alignment goes, and with it the entire reason the rail exists. It is a TYPOGRAPHIC
   device that happens to be coloured — not a coloured border with a label.

   Sized to hold the longest single WORD in the vocabulary ("Checking") on one line.
   Wrapping happens at spaces only — "NONE / OPEN", "Checking / now…" — and never
   mid-word: `break-word` here rendered "Checkin / g now…", which reads as a
   rendering fault rather than a verdict. */
.park-card__rail {
  grid-column: 1; grid-row: 1; align-self: stretch;
  width: 3px; border-radius: 2px; min-height: 2.4rem;
}
.park-card__verdict {
  grid-column: 2; grid-row: 1; align-self: start;
  font-family: var(--font-display); font-size: 0.95rem; font-weight: 700;
  letter-spacing: 0.04em; line-height: 1.15; margin-top: 0.15rem;
  overflow-wrap: normal; word-break: normal; hyphens: none;
}
.park-card__body { grid-column: 3; grid-row: 1; min-width: 0; }
/* The rail and its word carry the SAME verdict class as the map pin, so the two
   surfaces can never disagree about WHICH state a park is in. What differs is the
   hue for three of them, deliberately, because the colour means something narrower
   here than it does on a map:
     --forest      something is open
     --sage        we looked, and nothing is open
     --line-strong we don't know yet
   `not_bookable` in particular is NOT `--avail-shut` red on this surface. A park
   with nothing open is not an error and not a warning — it is the majority row on a
   summer weekend, and it is the exact row carrying a free-watch offer in ember. A
   wall of alarm-red under a heading that says we watch these parks reads as "this
   region is broken"; sage reads as "we looked". The other four states keep their
   --avail-* hues, which already mean what they say. */
.park-card__rail.is-open  { background: var(--forest); }
.park-card__rail.is-shut  { background: var(--sage); }
.park-card__rail.is-check { background: var(--line-strong); }
.park-card__rail.is-soon  { background: var(--avail-soon); }
.park-card__rail.is-closed { background: var(--avail-closed); }
.park-card__rail.is-fcfs  { background: var(--avail-fcfs); }
.park-card__rail.is-not-reservable { background: var(--avail-not-reservable); }
.park-card__verdict.is-open  { color: var(--forest); }
.park-card__verdict.is-shut  { color: var(--sage); }
/* The word needs contrast the 3px rule does not: --line-strong on --paper-card is
   below the readable floor for text, so the checking WORD takes the darker
   --avail-check while its rule keeps the pale tone. */
.park-card__verdict.is-check { color: var(--avail-check); }
.park-card__verdict.is-soon  { color: var(--avail-soon); }
.park-card__verdict.is-closed { color: var(--avail-closed); }
.park-card__verdict.is-fcfs  { color: var(--avail-fcfs); }
.park-card__verdict.is-not-reservable { color: var(--avail-not-reservable); }
/* "Checking now…" is the one verdict that is a PROMISE rather than an answer, so it
   is the one row that moves — a shimmer says we are still working, where a static
   grey word said we were broken (P5). The only animation on this surface. */
@keyframes cs-verdict-shimmer { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
.park-card__verdict.is-check { animation: cs-verdict-shimmer 1.6s var(--ease) infinite; }
@media (prefers-reduced-motion: reduce) {
  .park-card__verdict.is-check { animation: none; opacity: 1; }
}
.park-card__name {
  font-family: var(--font-display); font-weight: 600; font-size: 1.1rem;
  line-height: 1.15; color: var(--ink); margin: 0;
}
.park-card__meta { font-size: 0.82rem; color: var(--sage); margin: 0.15rem 0 0; }
/* The count is never a bare figure under the verdict — vertical adjacency IS the
   claim, so it always carries the words that say what it counts. */
.park-card__count { font-size: 0.82rem; color: var(--sage); margin: 0.1rem 0 0; }
/* The conversion moment: ember, and ember on this surface means only "act here". */
.park-card__watch { margin: 0.4rem 0 0; }
.park-card__watch-cta {
  display: block; font-weight: 700; font-size: 0.9rem; color: var(--ember-deep);
}
.park-card__watch-sub { display: block; font-size: 0.78rem; color: var(--sage); margin-top: 0.1rem; }
.park-card__dist {
  grid-column: 4; grid-row: 1; align-self: start; margin-top: 0.2rem;
  font-size: 0.8rem; font-weight: 700; color: var(--forest-deep);
  white-space: nowrap; text-align: right; font-variant-numeric: tabular-nums;
}
.park-card__go { grid-column: 4; grid-row: 1; align-self: center; color: var(--moss); }
.park-card__go .ic { width: 1.2rem; height: 1.2rem; }

/* ============================================================================
   PARK VIEW
   ========================================================================== */
.park-hero {
  margin: 1.2rem 0 0; padding: clamp(1.4rem, 4vw, 2.2rem);
  background:
    radial-gradient(120% 120% at 88% -20%, rgba(201,138,75,0.18), transparent 55%),
    var(--paper-card);
  border: 1.5px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: 0 22px 44px -34px rgba(28, 63, 47, 0.5);
}
.park-hero h1 {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.9rem, 5vw, 2.9rem); line-height: 1.02;
  letter-spacing: -0.02em; margin: 0.5rem 0 0; color: var(--ink);
}
.park-hero .loc {
  display: inline-flex; align-items: center; gap: 0.4rem;
  margin: 0.5rem 0 0; font-size: 0.95rem; color: var(--forest); font-weight: 600;
}
.park-hero .loc .ic { width: 1rem; height: 1rem; }
.park-hero .desc { margin: 0.9rem 0 0; max-width: 44rem; color: var(--ink); line-height: 1.65; }

.facts { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 1.2rem 0 0; }
.fact {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.5rem 0.85rem; font-size: 0.86rem; font-weight: 600; color: var(--forest-deep);
  background: var(--paper); border: 1.5px solid var(--line-strong); border-radius: 999px;
}
.fact .ic { width: 1.05rem; height: 1.05rem; color: var(--moss); }
.fact.is-check { color: var(--avail-check); border-color: var(--avail-check); }
.fact .val { color: var(--ink); }

.park-cta { display: flex; flex-wrap: wrap; gap: 0.7rem; margin: 1.3rem 0 0; }

/* ---- Section-scope step on the "Watch this park" CTA (F3) ---- */
.watch-park-cta { display: flex; flex-direction: column; gap: 0.6rem; }
.section-scope {
  display: block; margin: 0.2rem 0 0; padding: 0.85rem 1rem;
  border: 1.5px solid var(--line-strong); border-radius: var(--radius);
  background: var(--paper-card); max-width: 34rem;
}
.section-scope.is-hidden, .section-scope[hidden] { display: none; }
.section-scope__head { margin: 0 0 0.3rem; font-family: var(--font-display); font-size: 1.02rem; font-weight: 600; color: var(--forest-deep); }
.section-scope__hint { margin: 0 0 0.7rem; font-size: 0.84rem; color: var(--sage); line-height: 1.35; }
.section-scope__list { display: flex; flex-direction: column; gap: 0.1rem; }
.section-scope__row { padding: 0.4rem 0; border-bottom: 1px solid var(--line); }
.section-scope__row:last-child { border-bottom: 0; }
.section-scope__row-label { display: flex; align-items: center; gap: 0.55rem; cursor: pointer; font-size: 0.95rem; color: var(--ink); font-weight: 600; }
.section-scope__cb { width: 1.05rem; height: 1.05rem; flex: none; accent-color: var(--forest); }
.section-scope__desc { margin: 0.15rem 0 0 1.6rem; font-size: 0.82rem; color: var(--sage); line-height: 1.35; }
.section-scope__diag { margin: 0.6rem 0 0; font-size: 0.86rem; line-height: 1.4; padding: 0.5rem 0.7rem; border-radius: var(--radius-sm, 8px); }
.section-scope__diag[hidden] { display: none; }
.section-scope__diag.is-conflict { color: var(--clay, #b4532f); background: color-mix(in srgb, var(--clay, #b4532f) 10%, transparent); }
.section-scope__diag.is-zero { color: var(--sage); background: var(--paper-soft); }
.section-scope__confirm { margin-top: 0.7rem; }
.section-scope__confirm.is-disabled { opacity: 0.5; pointer-events: none; cursor: not-allowed; }

/* sites layout: filters + list */
.sites-layout {
  display: grid; gap: clamp(1rem, 3vw, 1.6rem); margin: 1.6rem 0 0;
  grid-template-columns: 1fr;
}
/* One column: the filter panel now lives INSIDE the collapsed Advanced group in
   the list column (progressive disclosure), not in a permanent second column. */

/* ---- filter panel -------------------------------------------------------- */
.filter-panel {
  background: var(--paper-card); border: 1.5px solid var(--line);
  border-radius: var(--radius-lg); padding: clamp(1rem, 3vw, 1.3rem);
  box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;
}
.filter-panel h2 {
  font-family: var(--font-display); font-weight: 600; font-size: 1.2rem;
  margin: 0 0 0.4rem; color: var(--forest-deep);
}
.fp-group { margin: 1rem 0 0; border: 0; padding: 0; }
.fp-group:first-of-type { margin-top: 0.6rem; }
.fp-group legend, .fp-label {
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ember-deep); margin: 0 0 0.5rem; padding: 0;
}
/* equipment chips */
.equip-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.equip-chip {
  display: inline-flex; align-items: center; gap: 0.4rem; cursor: pointer;
  padding: 0.45rem 0.7rem; font-family: inherit; font-size: 0.88rem; font-weight: 600; color: var(--forest-deep);
  background: var(--paper); border: 1.5px solid var(--line-strong); border-radius: 999px;
  transition: border-color 0.12s var(--ease), background 0.12s var(--ease), color 0.12s var(--ease);
}
.equip-chip .ic { width: 1.05rem; height: 1.05rem; }
.equip-chip:hover { border-color: var(--forest); }
/* Equipment chips are now ONE component: <button>.equip-chip everywhere (section G /
   C2). Active state is driven by .is-active (single- and multi-select alike); the
   .equip-chip--multi variant is a hook for surface-specific multi-select styling. */

.fp-check { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; font-size: 0.94rem; color: var(--ink); margin: 0.5rem 0 0; }
.fp-check input { accent-color: var(--forest); width: 1.05rem; height: 1.05rem; flex: none; }
.fp-check .ic { width: 1.05rem; height: 1.05rem; color: var(--moss); flex: none; }

.fp-prox { display: grid; gap: 0.9rem; }
.fp-prox-item { display: flex; flex-direction: column; gap: 0.25rem; }
.fp-prox-item .row { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.fp-prox-item .row span { font-size: 0.9rem; font-weight: 600; color: var(--ink); }
.fp-prox-item .row .out { font-size: 0.82rem; color: var(--sage); font-variant-numeric: tabular-nums; }
.fp-prox-item input[type=range] { width: 100%; accent-color: var(--forest); }

.fp-num { display: flex; align-items: center; gap: 0.5rem; }
.fp-num input[type=number] {
  width: 5rem; padding: 0.45rem 0.55rem; font: inherit; color: var(--ink);
  background: var(--paper); border: 1.5px solid var(--line-strong); border-radius: var(--radius);
}
.fp-num input:focus { outline: none; border-color: var(--forest); box-shadow: 0 0 0 3px rgba(44,90,67,0.16); }
.fp-num label { font-size: 0.9rem; color: var(--ink); }

.fp-actions { margin: 1.2rem 0 0; display: flex; gap: 0.5rem; flex-wrap: wrap; }
.fp-reset {
  font: inherit; font-size: 0.86rem; font-weight: 600; color: var(--sage);
  background: none; border: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 3px;
}
.fp-reset:hover { color: var(--ember-deep); }

/* ---- Advanced group (progressive disclosure — COH-2) ----------------------
   A signed-out first load shows dates, the section list and one primary CTA.
   Everything that refines the view lives behind this one disclosure, whose open
   state is remembered per browser. Nothing is deleted — only folded away. */
.park-advanced {
  margin: 0 0 1rem; border: 1.5px solid var(--line); border-radius: var(--radius-lg);
  background: var(--paper-card);
}
.park-advanced__summary {
  list-style: none; cursor: pointer; padding: 0.75rem 1rem;
  font-weight: 700; font-size: 0.92rem; color: var(--forest-deep);
  display: flex; align-items: center; gap: 0.5rem;
}
.park-advanced__summary::-webkit-details-marker { display: none; }
.park-advanced__summary::marker { content: ""; }
.park-advanced__summary::before {
  content: "\25B8"; font-size: 0.8rem; transition: transform 0.15s var(--ease);
}
.park-advanced[open] > .park-advanced__summary::before { transform: rotate(90deg); }
.park-advanced__summary:hover { background: var(--paper-soft); border-radius: var(--radius-lg); }
.park-advanced__summary:focus-visible { outline: 3px solid var(--forest); outline-offset: -2px; }
.park-advanced__body { padding: 0 1rem 1rem; }
.park-advanced .filter-panel { border: 0; background: none; padding: 0; box-shadow: none; }
.park-advanced .filter-panel h2 { font-size: 1.05rem; }

/* ---- search-level filter bar (#/discover) -------------------------------- */
/* The region-wide filter bar — reuses the per-park panel's control idiom so the
   look matches, but lives on the discover search experience (the founder's fix). */
.search-filter-bar-wrap { margin: 0.4rem 0 0; }
.sf-bar-toggle {
  display: inline-flex; align-items: center; gap: 0.35rem; font: inherit;
  font-weight: 700; font-size: 0.92rem; color: var(--forest-deep);
  background: var(--paper-card); border: 1.5px solid var(--line-strong);
  border-radius: 999px; padding: 0.5rem 0.95rem; cursor: pointer;
}
.sf-bar-toggle:hover { border-color: var(--forest); }
.sf-bar-toggle.is-on { background: var(--forest); color: #fff; border-color: var(--forest-deep); }
.sf-bar-count { font-weight: 600; }
.search-filter-bar {
  margin: 0.7rem 0 0; padding: 1rem 1.1rem; background: var(--paper-card);
  border: 1.5px solid var(--line-strong); border-radius: var(--radius);
}
.search-filter-bar.is-collapsed { display: none; }
.search-filters {
  display: grid; gap: 1rem 1.6rem;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
.sf-group { min-width: 0; }
.sf-bar-actions { margin: 1rem 0 0; display: flex; gap: 0.5rem; flex-wrap: wrap; }
.sf-clear[hidden] { display: none; }

/* shared chip-buttons used by the search filter set (equipment single-select +
   toilet-type exclusive). These are <button>s toggled via .is-active (not the
   :has(input:checked) form the per-park equip-chips use). */
.cat-type-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.cat-type-chip {
  font: inherit; font-size: 0.86rem; font-weight: 600; color: var(--forest-deep);
  background: var(--paper); border: 1.5px solid var(--line-strong); border-radius: 999px;
  padding: 0.4rem 0.75rem; cursor: pointer;
  transition: border-color 0.12s var(--ease), background 0.12s var(--ease), color 0.12s var(--ease);
}
.cat-type-chip:hover { border-color: var(--forest); }
.cat-type-chip.is-active { background: var(--forest); color: #fff; border-color: var(--forest-deep); }
.cat-type-chip:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
/* the search-filter equipment chips are <button>s → .is-active drives the fill */
.equip-chip.is-active { background: var(--forest); color: #fff; border-color: var(--forest-deep); }
.equip-chip:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }

/* ---- tri-state amenity controls (Any / Must have / Exclude) --------------- */
/* The honest-unknown filter: each amenity is a segmented 3-way selector rather
   than a checkbox, so a user can include "maybes", require only confirmed, or
   exclude. Default "Any" keeps unconfirmed sites visible + watchable. */
/* Still used by the per-park panel's own Amenities group. The discover/Roam filter
   set's five hint paragraphs became the ⓘ disclosure below — see labelWithHint(). */
.sf-tri-hint { font-size: 0.78rem; color: var(--sage); margin: 0 0 0.6rem; line-height: 1.35; }

/* ---- the ⓘ axis hint (labelWithHint) --------------------------------------
   The hint is disclosed, not deleted: `[hidden]` keeps it out of the layout while
   leaving it resolvable as the label's `aria-describedby` target, so a screen
   reader still reads it every time and a sighted reader gets it on request. */
.sf-hint-btn {
  font: inherit; font-size: 0.95rem; line-height: 1; color: var(--moss);
  background: none; border: 0; cursor: pointer; padding: 0 0.3rem;
  /* A real target, not a glyph: the padding gives it width, the inline-flex box
     gives it the 44px height, and the negative block margin keeps that height from
     opening a gap in the label line it sits in. */
  display: inline-flex; align-items: center; min-height: 44px; margin: -14px 0;
  vertical-align: middle;
}
.sf-hint-btn:hover { color: var(--forest); }
.sf-hint-btn:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; border-radius: 4px; }
.sf-hint-btn[aria-expanded="true"] { color: var(--forest-deep); }
.sf-hint { font-size: 0.78rem; color: var(--sage); margin: 0.1rem 0 0.6rem; line-height: 1.35; }
.sf-hint[hidden] { display: none; }
.sf-tri {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
  flex-wrap: wrap; margin: 0.55rem 0 0;
}
.sf-tri__label { display: flex; align-items: center; gap: 0.45rem; font-size: 0.94rem; color: var(--ink); }
.sf-tri__label .ic { width: 1.05rem; height: 1.05rem; color: var(--moss); flex: none; }
.sf-tri__seg { display: inline-flex; border: 1.5px solid var(--line-strong); border-radius: 999px; overflow: hidden; flex: none; }
.sf-tri__opt {
  font: inherit; font-size: 0.8rem; font-weight: 600; color: var(--forest-deep);
  background: var(--paper); border: 0; border-left: 1.5px solid var(--line-strong);
  padding: 0.34rem 0.66rem; cursor: pointer;
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
.sf-tri__opt:first-child { border-left: 0; }
.sf-tri__opt:hover { background: var(--paper-soft); }
.sf-tri__opt.is-active { background: var(--forest); color: #fff; }
.sf-tri__opt[data-state="exclude"].is-active { background: var(--clay, #b4532f); }
.sf-tri__opt:focus-visible { outline: 3px solid var(--forest); outline-offset: -2px; }

/* ---- site list ----------------------------------------------------------- */
.sites-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.5rem; margin: 0 0 0.7rem; }
.sites-head h2 { font-family: var(--font-display); font-weight: 600; font-size: 1.4rem; margin: 0; color: var(--ink); }
.sites-head .as-of { font-size: 0.8rem; color: var(--sage); }
.truncation-note {
  margin: 0 0 0.8rem; padding: 0.6rem 0.85rem; font-size: 0.84rem; color: var(--avail-soon);
  background: var(--avail-soon-bg); border-radius: var(--radius);
}

.site-list { display: grid; gap: 0.7rem; margin: 0; padding: 0; list-style: none; }
.site-row {
  display: grid; grid-template-columns: 1fr auto; gap: 0.5rem 0.9rem; align-items: center;
  padding: 0.85rem 1rem; background: var(--paper-card);
  border: 1.5px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: 0 12px 26px -28px rgba(28, 63, 47, 0.5);
  transition: border-color 0.12s var(--ease), transform 0.12s var(--ease);
}
.site-row:hover { border-color: var(--forest); transform: translateY(-1px); }
.site-row__main { min-width: 0; }
.site-row__name {
  display: flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-display); font-weight: 600; font-size: 1.1rem; color: var(--ink); margin: 0;
}
.site-row__name a { color: inherit; text-decoration: none; }
.site-row__name a:hover { color: var(--ember-deep); }
.site-row__tags { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0.4rem 0 0; }
.tag {
  display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.2rem 0.55rem; font-size: 0.76rem; font-weight: 600; color: var(--forest-deep);
  background: var(--paper); border: 1px solid var(--line-strong); border-radius: 999px;
}
.tag .ic { width: 0.85rem; height: 0.85rem; color: var(--moss); }
.tag.is-linked { color: var(--ember-deep); border-color: var(--ember); }

.site-row__right { display: flex; flex-direction: column; align-items: flex-end; gap: 0.4rem; }

/* Stretched link: the whole site card is the tap target (a bare numeric site name
   like "19" was a tiny, hard-to-hit click area). The name link's ::after covers the
   card; the Watch/Book link in the right column is lifted above it so it stays
   independently clickable. Matches the card's existing hover-lift affordance. */
.site-row { position: relative; }
.site-row__link::after {
  content: ""; position: absolute; inset: 0; z-index: 1; border-radius: var(--radius-lg);
}
.site-row__right { position: relative; z-index: 2; }

/* ---- availability chip (the honest centerpiece) -------------------------- */
.avail {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.32rem 0.7rem; font-size: 0.82rem; font-weight: 700;
  border-radius: 999px; white-space: nowrap; border: 1.5px solid transparent;
}
.avail::before { content: ""; width: 0.55rem; height: 0.55rem; border-radius: 999px; background: currentColor; flex: none; }
.avail.is-open  { color: var(--avail-open);  background: var(--avail-open-bg);  border-color: rgba(44,90,67,0.3); }
.avail.is-shut  { color: var(--avail-shut);  background: var(--avail-shut-bg);  border-color: rgba(168,57,42,0.3); }
.avail.is-soon  { color: var(--avail-soon);  background: var(--avail-soon-bg);  border-color: rgba(177,79,28,0.3); }
.avail.is-closed { color: var(--avail-closed); background: var(--avail-closed-bg); border-color: rgba(85,96,122,0.3); }
.avail.is-check {
  color: var(--avail-check); background: var(--avail-check-bg); border-color: rgba(107,114,128,0.35);
}
/* first-come-first-served: never reuse is-open (see explore.js availIsOpen()'s
   own comment) — a distinct hue so "first come" can't be mistaken for "booked". */
.avail.is-fcfs  { color: var(--avail-fcfs);  background: var(--avail-fcfs-bg);  border-color: rgba(138,106,31,0.3); }
/* NOT_RESERVABLE / not_reservable: its OWN visual class — never reuse is-open,
   is-shut, is-soon, is-closed, or is-check, each of which would misrepresent it
   (docs/design/non-reservable-state §3.6; see explore.js STATE_CLASS's comment). */
.avail.is-not-reservable {
  color: var(--avail-not-reservable); background: var(--avail-not-reservable-bg);
  border-color: rgba(106,92,138,0.3);
}
.avail.is-check::before { animation: pulse-dot 1.6s var(--ease) infinite; }
@keyframes pulse-dot { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: reduce) { .avail.is-check::before { animation: none; } }
.demo-tag {
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--sage);
}

.watch-mini {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.4rem 0.75rem; font: inherit; font-size: 0.82rem; font-weight: 700;
  color: var(--forest-deep); background: var(--paper);
  border: 1.5px solid var(--line-strong); border-radius: 999px; cursor: pointer; text-decoration: none;
  transition: border-color 0.12s var(--ease), color 0.12s var(--ease), background 0.12s var(--ease);
}
.watch-mini .ic { width: 0.9rem; height: 0.9rem; }
.watch-mini:hover { border-color: var(--ember); color: var(--ember-deep); background: var(--paper-soft); }

/* ============================================================================
   SITE VIEW
   ========================================================================== */
.site-detail { margin: 1.2rem 0 0; }
.site-detail__head {
  display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 1rem;
}
.site-detail h1 {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.8rem, 5vw, 2.6rem); line-height: 1.04;
  letter-spacing: -0.02em; margin: 0.4rem 0 0; color: var(--ink);
}
.site-detail .avail { font-size: 0.92rem; padding: 0.5rem 0.95rem; }

.site-grid { display: grid; gap: clamp(1rem, 3vw, 1.6rem); margin: 1.4rem 0 0; grid-template-columns: 1fr; }
@media (min-width: 820px) { .site-grid { grid-template-columns: minmax(0,1.3fr) minmax(0,1fr); } }

.panel {
  background: var(--paper-card); border: 1.5px solid var(--line);
  border-radius: var(--radius-lg); padding: clamp(1.1rem, 3vw, 1.5rem);
  box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;
}
.panel h2 {
  font-family: var(--font-display); font-weight: 600; font-size: 1.25rem;
  margin: 0 0 0.8rem; color: var(--forest-deep);
}
.attr-list { display: grid; grid-template-columns: auto 1fr; gap: 0.55rem 1rem; margin: 0; }
.attr-list dt { font-weight: 600; color: var(--sage); font-size: 0.92rem; }
.attr-list dd { margin: 0; color: var(--ink); font-size: 0.95rem; }

.feature-tags { display: flex; flex-wrap: wrap; gap: 0.45rem; margin: 0; padding: 0; list-style: none; }
.feature-tags .tag { font-size: 0.82rem; padding: 0.3rem 0.7rem; }

.linked-note {
  margin: 1rem 0 0; padding: 0.85rem 1rem; font-size: 0.9rem; line-height: 1.5; color: var(--ink);
  background: var(--avail-soon-bg); border: 1.5px solid var(--ember); border-radius: var(--radius);
}
.linked-note strong { color: var(--ember-deep); }

/* photos */
.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
.photo-grid li { margin: 0; }
.photo-grid img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block;
  border-radius: var(--radius); border: 1.5px solid var(--line); background: var(--paper-soft);
}

.avail-strip { display: flex; flex-wrap: wrap; align-items: center; gap: 0.7rem; margin: 0.4rem 0 0; }
.avail-strip .as-of { font-size: 0.82rem; color: var(--sage); }

/* ---- big primary CTA reused across park + site -------------------------- */
.cta-watch {
  display: inline-flex; align-items: center; gap: 0.55rem;
  padding: 0.8rem 1.4rem; font: inherit; font-size: 1rem; font-weight: 700; color: #fff;
  background: linear-gradient(180deg, var(--ember), var(--ember-deep));
  border: 0; border-radius: var(--radius); cursor: pointer; text-decoration: none;
  box-shadow: 0 10px 22px -10px var(--ember-glow);
  transition: transform 0.15s var(--ease), box-shadow 0.2s var(--ease), filter 0.2s var(--ease);
}
.cta-watch .ic { width: 1.15rem; height: 1.15rem; }
.cta-watch:hover { transform: translateY(-1px); filter: brightness(1.04); box-shadow: 0 14px 28px -12px var(--ember-glow); }
.cta-watch:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }

/* ---- "Book on <operator> ↗" — the open-now → book fork (out to the operator) -- */
/* Primary CTA variant: forest (the "good news, it's open" action), distinct from
   the ember "Watch" CTA, so an open spot reads as a different, happier action. */
.cta-book {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.8rem 1.4rem; font: inherit; font-size: 1rem; font-weight: 700; color: #fff;
  background: linear-gradient(180deg, var(--forest), var(--forest-deep));
  border: 0; border-radius: var(--radius); cursor: pointer; text-decoration: none;
  box-shadow: 0 10px 22px -10px rgba(28, 63, 47, 0.5);
  transition: transform 0.15s var(--ease), box-shadow 0.2s var(--ease), filter 0.2s var(--ease);
}
.cta-book:hover { transform: translateY(-1px); filter: brightness(1.05); box-shadow: 0 14px 28px -12px rgba(28, 63, 47, 0.55); }
.cta-book:focus-visible { outline: 3px solid var(--ember); outline-offset: 2px; }
.cta-book .ext-arrow { font-weight: 700; }
/* List-row mini variant: mirrors .watch-mini but with the forest accent. */
.book-mini {
  display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.4rem 0.75rem; font: inherit; font-size: 0.82rem; font-weight: 700;
  color: #fff; background: var(--forest); border: 1.5px solid var(--forest-deep);
  border-radius: 999px; cursor: pointer; text-decoration: none;
  transition: filter 0.12s var(--ease), background 0.12s var(--ease);
}
.book-mini:hover { filter: brightness(1.07); }
.book-mini .ext-arrow { font-size: 0.9em; }
/* Model-reminder line near the fork — "we alert, we don't book". */
.book-model-note { margin: 1.1rem 0 0; font-size: 0.9rem; line-height: 1.5; color: var(--sage); }

/* ============================================================================
   MANAGE WATCHES VIEW  (#/watches)
   ========================================================================== */
.watches-view { margin: 1.2rem 0 0; }
.watch-list { list-style: none; margin: 1.4rem 0 0; padding: 0; display: grid; gap: 0.9rem; }
.watch-card {
  background: var(--paper-card); border: 1.5px solid var(--line);
  border-radius: var(--radius-lg); padding: clamp(1rem, 3vw, 1.35rem);
  box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;
}
.watch-card__head {
  display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 0.6rem;
}
.watch-card__summary { margin: 0; font-size: 0.98rem; line-height: 1.5; color: var(--ink); }
.watch-status {
  flex: none; font-size: 0.7rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  padding: 0.25rem 0.6rem; border-radius: 999px; border: 1.5px solid transparent;
}
.watch-status.is-active { color: var(--forest-deep); background: var(--avail-open-bg, #e7f0e9); border-color: var(--forest); }
.watch-status.is-off { color: var(--sage); background: var(--paper-soft); border-color: var(--line-strong); }
.watch-card__meta { margin: 0.5rem 0 0; font-size: 0.8rem; color: var(--sage); }
.watch-card__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 0.9rem; margin: 0.9rem 0 0; }
.watch-card__note { margin: 0; font-size: 0.85rem; line-height: 1.5; color: var(--sage); flex: 1 1 16rem; }
.watch-card__msg { font-size: 0.85rem; color: var(--forest-deep); }
.watch-forget {
  font: inherit; font-size: 0.82rem; font-weight: 600; color: var(--sage);
  background: none; border: 0; padding: 0; cursor: pointer; text-decoration: underline;
  text-underline-offset: 3px; margin-left: auto;
}
.watch-forget:hover { color: var(--ember-deep); }
.watch-forget:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }

/* ---- staggered reveal (reuses keyframes from styles.css) ----------------- */
@media (prefers-reduced-motion: no-preference) {
  .view-enter > * { opacity: 0; transform: translateY(10px); animation: rise 0.55s var(--ease) forwards; }
  .view-enter > *:nth-child(1) { animation-delay: 0.02s; }
  .view-enter > *:nth-child(2) { animation-delay: 0.08s; }
  .view-enter > *:nth-child(3) { animation-delay: 0.14s; }
  .view-enter > *:nth-child(4) { animation-delay: 0.20s; }
  .view-enter > *:nth-child(5) { animation-delay: 0.26s; }
}

/* ============================================================================
   SECTION GROUPING + NAVIGATION (§4.2 / §4.3)
   ========================================================================== */

/* ---- within-park section filter ------------------------------------------ */
.section-filter {
  margin: 0 0 0.9rem;
}
.section-filter-input {
  width: 100%; padding: 0.55rem 0.85rem;
  font: inherit; font-size: 0.92rem; color: var(--ink);
  background: var(--paper-card); border: 1.5px solid var(--line-strong);
  border-radius: calc(var(--radius) + 2px);
  box-shadow: 0 1px 0 rgba(255,255,255,0.6) inset;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.section-filter-input::placeholder { color: #9aa093; }
.section-filter-input:focus {
  outline: none;
  border-color: var(--forest);
  box-shadow: 0 0 0 3px rgba(44,90,67,0.16);
}

/* ---- collapsible section group (<details>/<summary>) --------------------- */
.sec-group {
  margin: 0 0 0.6rem;
  background: var(--paper-card); border: 1.5px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 28px -28px rgba(28,63,47,0.45);
  overflow: hidden;
}
.sec-group[open] > .sec-group__summary .chev-sec { transform: rotate(0deg); }

.sec-group__summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.8rem;
  padding: 0.75rem 1rem; cursor: pointer;
  list-style: none; /* hide the native disclosure triangle */
  border-radius: var(--radius-lg); /* matches parent when closed */
  transition: background 0.12s var(--ease);
}
/* Safari shows its own ::marker on summary; suppress it */
.sec-group__summary::-webkit-details-marker { display: none; }
.sec-group__summary::marker { display: none; }
.sec-group__summary:hover { background: var(--paper-soft); }
.sec-group__summary:focus-visible { outline: 3px solid var(--forest); outline-offset: -2px; }

/* When open, add a bottom border separating summary from the sites */
.sec-group[open] > .sec-group__summary {
  border-bottom: 1.5px solid var(--line);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

.sec-group__title {
  font-family: var(--font-display); font-weight: 600; font-size: 1.05rem;
  color: var(--forest-deep); flex: 0 0 auto;
}
/* Custom disclosure chevron — a pure CSS triangle so we control direction */
.sec-group__title::before {
  content: "▶";
  display: inline-block;
  font-size: 0.65em;
  margin-right: 0.45em;
  color: var(--moss);
  transition: transform 0.18s var(--ease);
  transform: rotate(0deg);
  vertical-align: 0.08em;
}
.sec-group[open] > .sec-group__summary .sec-group__title::before {
  transform: rotate(90deg);
}

.sec-group__count {
  font-size: 0.8rem; font-weight: 600; color: var(--sage);
  flex: 0 0 auto;
}

.sec-group__deeplink {
  font-size: 0.8rem; font-weight: 700; color: var(--forest-deep);
  text-decoration: none; margin-left: auto; /* push to the right of title + count */
  white-space: nowrap; padding: 0.25rem 0.6rem;
  border: 1.5px solid var(--line-strong); border-radius: 999px;
  background: var(--paper);
  transition: border-color 0.12s var(--ease), color 0.12s var(--ease);
}
.sec-group__deeplink:hover { border-color: var(--forest); color: var(--forest); }
.sec-group__deeplink:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }

.sec-group__watch {
  flex: 0 0 auto;
  /* inherits .watch-mini styles; add a small left margin on larger screens */
}

/* site list inside a group sits flush — the group card provides the container */
.sec-group > .site-list {
  margin: 0; padding: 0.7rem 0.85rem; list-style: none;
  display: grid; gap: 0.6rem;
}
/* tighten site rows inside a section group slightly */
.sec-group > .site-list .site-row {
  border-color: var(--line);
  box-shadow: none; /* the group card already provides depth */
}

/* ---- section view (click-into-section route) ------------------------------ */
.sec-view { margin: 1.2rem 0 0; }
.sec-view__hero {
  padding: clamp(1.2rem, 3.5vw, 2rem);
  background:
    radial-gradient(110% 120% at 90% -15%, rgba(44,90,67,0.12), transparent 55%),
    var(--paper-card);
  border: 1.5px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: 0 18px 38px -30px rgba(28,63,47,0.5);
  margin-bottom: 1.4rem;
}
.sec-view__hero h1 {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.7rem, 4.5vw, 2.6rem); line-height: 1.04;
  letter-spacing: -0.02em; margin: 0.4rem 0 0; color: var(--ink);
}
.sec-view__count {
  font-size: 0.9rem; color: var(--sage); margin: 0.4rem 0 0;
}
.sec-back { white-space: nowrap; }

/* ---- mobile polish ------------------------------------------------------- */
@media (max-width: 560px) {
  .discover-controls { flex-direction: column; }
  /* `flex-basis` is a MAIN-axis size, and stacking the bar makes the main axis
     vertical — so `.search-box`'s `flex: 1 1 18rem` and `.loc-box`'s `1 1 16rem`
     stop meaning "at least 18rem wide" and start meaning "288px TALL", which is
     what rendered both search fields as tall, near-empty rectangles with the input
     floating in the middle. Once stacked they are full-width by the container's
     `align-items: stretch`, so the basis has no width left to express: hand the
     main axis back to the content. */
  .search-box, .loc-box { flex: 0 0 auto; }
  .btn-near { justify-content: center; }
  .site-row { grid-template-columns: 1fr; }
  .site-row__right { flex-direction: row; align-items: center; justify-content: space-between; width: 100%; }
  /* The rail and its verdict column SURVIVE the narrow breakpoint — they are the
     whole point of the card here, where twelve results are one thumb-scroll and the
     eye needs a column to run down. What collapses instead is the distance, which
     moves under the body rather than competing with it for a fourth column. */
  .park-card { grid-template-columns: 3px 4.3rem minmax(0, 1fr); }
  .park-card__verdict { font-size: 0.9rem; }
  /* `span 2`, never `-1`: with no explicit grid-template-rows, line -1 resolves
     against the EXPLICIT grid (which has none here), so it would not reach the
     implicit second row the distance now occupies. */
  .park-card__rail { grid-row: 1 / span 2; }
  .park-card__dist, .park-card__go {
    grid-column: 3; grid-row: 2; text-align: left; align-self: start;
    margin-top: 0.25rem;
  }
  .map-toggle { display: flex; }
}

/* ---- wizard primitives (.wz-text / .wz-status / .wz-btn / .wz-btn-secondary)
   are now defined ONCE in styles.css (loaded by both pages) — section G / C2. The
   SPA-local diverging copies were removed so explore + alerts render identically.
   Add only page-context overrides here, never a re-declaration. ---------------- */

/* ---- results-fork wrapper (holds the free-vs-Roam fork once a location is active) */
.roam-cta-wrap:empty { display: none; }

/* ---- honest returning-user affordance ------------------------------------ */
.returning-note { margin: 1.6rem 0 0; border-top: 1px dashed var(--line-strong); padding-top: 1rem; }
.returning-note > summary {
  cursor: pointer; font-weight: 600; color: var(--forest-deep); font-size: 0.95rem;
  list-style: none; display: inline-flex; align-items: center; gap: 0.4rem;
}
.returning-note > summary::-webkit-details-marker { display: none; }
.returning-note > summary::before { content: "›"; transition: transform 0.2s var(--ease); color: var(--sage); }
.returning-note[open] > summary::before { transform: rotate(90deg); }
.returning-note > summary:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; border-radius: 4px; }
.returning-note__body { margin-top: 0.7rem; max-width: 38rem; }
.returning-note__body p { margin: 0 0 0.8rem; font-size: 0.92rem; color: var(--sage); }

/* ============================================================================
   STEP 0 — ENTRY (teach the model + how-it-works + the three doors)
   ========================================================================== */
/* The model-teaching sentence carries a touch more weight than a normal lede. */
.entry-model { max-width: 42rem; font-size: 1.08rem; }
.entry-model strong { color: var(--forest-deep); }

/* The accelerator door — a full-width tappable card alongside the search. ONE
   column at every width, because explore.js now renders ONE door: the "Scan a
   whole area" companion was the second place this page asked the
   one-park-vs-area question, and the results fork (which can answer it with a
   real count) is the surface that kept it. A two-column rule here would leave a
   half-width card floating beside a hole. If a second door is ever added back,
   restore the `@media (min-width: 720px) { repeat(2, 1fr) }` rule with it. */
.entry-doors {
  display: grid; gap: 0.7rem; margin: 1.5rem 0 0;
  grid-template-columns: 1fr;
}
/* MUST come after the `display: grid` above. The UA's `[hidden] { display: none }`
   carries the same specificity as any class rule and loses on source order, so a
   class that sets `display` silently defeats the attribute — which is exactly how
   the entry doors kept rendering above the results after being marked hidden.
   `.discover-head` sets no display and so does not need the help; it is listed
   anyway so the pair cannot drift apart when one of them gains a display later. */
.entry-doors[hidden], .discover-head[hidden] { display: none; }
.entry-door {
  display: flex; align-items: flex-start; gap: 0.7rem; text-align: left;
  padding: 0.95rem 1.05rem; font: inherit; cursor: pointer;
  background: var(--paper-card); border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-lg);
  /* Same drop-shadow treatment as .park-card below it (both are entry cards on
     the same screen) rather than an inset highlight — one card language, not two. */
  box-shadow: 0 14px 30px -28px rgba(28, 63, 47, 0.5);
  transition: border-color 0.15s var(--ease), transform 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.entry-door:hover { border-color: var(--forest); transform: translateY(-1px); box-shadow: 0 16px 30px -22px rgba(28,63,47,0.5); }
.entry-door:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
.entry-door.is-primary { border-color: var(--ember); background: linear-gradient(180deg, var(--paper-card), var(--avail-soon-bg)); }
.entry-door__ic {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 2.1rem; height: 2.1rem; border-radius: 999px;
  background: var(--avail-open-bg); color: var(--forest);
}
.entry-door.is-primary .entry-door__ic { background: var(--avail-soon-bg); color: var(--ember-deep); }
.entry-door__ic .ic { width: 1.2rem; height: 1.2rem; }
.entry-door__text { display: flex; flex-direction: column; gap: 0.2rem; }
.entry-door__title { font-family: var(--font-display); font-weight: 600; font-size: 0.98rem; color: var(--ink); line-height: 1.15; }
.entry-door__sub { font-size: 0.82rem; color: var(--sage); line-height: 1.4; }

/* ---- active-filter line ("Showing: …") on the park site list ------------- */
.active-filter-line {
  margin: 0 0 0.8rem; padding: 0.55rem 0.85rem; font-size: 0.86rem; line-height: 1.4;
  color: var(--forest-deep); background: var(--avail-open-bg);
  border-radius: var(--radius);
}
.active-filter-line .afl-label { font-weight: 700; color: var(--ember-deep); }


/* ============================================================================
   PROVENANCE LINE + HONEST-UNKNOWN + EV HELPER  (honest-data rendering)
   ========================================================================== */
/* "source: BC Parks · checked 4m ago" — the honesty-brand provenance line. */
.provenance {
  margin: 0.45rem 0 0; font-size: 0.82rem; color: var(--sage);
  letter-spacing: 0.01em;
}
/* An attributed-unknown value reads as a calm, distinct note — not an error, not a
   verdict. Italic + muted so the KNOWN rows around it stand out (raises trust). */
.attr-unknown { color: var(--sage); font-style: italic; }

/* EV/electric honest helper — sits under the About-this-site list. */
.ev-helper {
  margin: 0.9rem 0 0; padding: 0.8rem 0.95rem;
  background: var(--avail-soon-bg); border: 1px solid var(--line);
  border-left: 3px solid var(--ember); border-radius: var(--radius);
}
.ev-helper__head {
  display: inline-flex; align-items: center; gap: 0.4rem; margin: 0;
  font-weight: 600; color: var(--forest-deep);
}
.ev-helper__head .ic { width: 1.05rem; height: 1.05rem; color: var(--ember-deep); }
.ev-helper__body { margin: 0.35rem 0 0; font-size: 0.85rem; color: var(--ink); line-height: 1.5; }

/* ============================================================================
   MULTI-SITE WATCH — progressive-disclosure multi-select (STORYBOARD)
   ========================================================================== */
.sites-head__actions { display: flex; align-items: center; gap: 0.7rem; }

/* the ONE control that toggles the park's site list into multi-select mode */
.select-several-toggle {
  font: inherit; font-size: 0.86rem; font-weight: 700; color: var(--forest-deep);
  background: var(--paper); border: 1.5px solid var(--line-strong); border-radius: 999px;
  padding: 0.4rem 0.85rem; cursor: pointer;
  transition: border-color 0.12s var(--ease), background 0.12s var(--ease), color 0.12s var(--ease);
}
.select-several-toggle:hover { border-color: var(--forest); }
.select-several-toggle.is-active { background: var(--forest); color: #fff; border-color: var(--forest-deep); }
.select-several-toggle:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }

/* the checkbox column only exists in the DOM while multi-select mode is on */
.site-row--selectable { grid-template-columns: auto 1fr auto; }
.site-row__select-wrap { position: relative; z-index: 2; display: flex; align-items: center; }
.site-row__select { width: 1.2rem; height: 1.2rem; accent-color: var(--forest); cursor: pointer; }

/* "Group site" chip — is_group_site (list projection, B1 backend fix) */
.tag--group-site { color: var(--forest-deep); border-color: var(--forest); }

/* the one-line "?" tooltip on the "Watch this group of sites" action — a plain
   focusable span (native title + aria-label), never a <button>, so it can't
   fight the parent <summary>'s native open/close toggle behaviour. */
.info-tip {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 1.15rem; height: 1.15rem; margin-left: 0.15rem;
  font-size: 0.72rem; font-weight: 700; color: var(--sage);
  border: 1.5px solid var(--line-strong); border-radius: 999px; cursor: help;
  background: var(--paper);
}
.info-tip:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }

/* sticky selection bar (Step 2 hand-off) — fixed to the viewport so it's
   reachable no matter how long the site list is. */
.multi-select-bar {
  position: fixed; left: 50%; bottom: 1rem; transform: translateX(-50%);
  z-index: 30; max-width: calc(100vw - 2rem);
}
.multi-select-bar.is-hidden { display: none; }
.msb-inner {
  display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap;
  padding: 0.75rem 1.1rem; background: var(--forest-deep); color: #fff;
  border-radius: 999px; box-shadow: 0 16px 34px -14px rgba(28,63,47,0.55);
}
.msb-count { font-weight: 700; font-size: 0.92rem; white-space: nowrap; }
.msb-count.is-over { color: #f6c9a0; }
.msb-actions { display: flex; align-items: center; gap: 0.6rem; }
.msb-clear {
  font: inherit; font-size: 0.82rem; font-weight: 600; color: rgba(255,255,255,0.82);
  background: none; border: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 3px;
}
.msb-clear:hover { color: #fff; }
.msb-cta {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font: inherit; font-size: 0.9rem; font-weight: 700; color: var(--forest-deep);
  background: #fff; border: 0; border-radius: 999px; padding: 0.5rem 1rem;
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: transform 0.12s var(--ease), box-shadow 0.12s var(--ease);
}
.msb-cta:hover { transform: translateY(-1px); box-shadow: 0 10px 20px -10px rgba(0,0,0,0.35); }
.msb-cta:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
.msb-cta.is-disabled, .msb-cta[disabled] {
  opacity: 0.55; cursor: not-allowed; transform: none; box-shadow: none;
}
/* Both selection bars are lifted out of the flow, so they paint over whatever sits
   at the end of the list — on every viewport, not only phones (measured: the bar
   covering the last site row by 91 of its 101px). Reserve the bar's height inside
   whichever element is its containing block, so the last rows can always be
   scrolled clear of it instead of sitting permanently underneath.
   The subject is the bar's DIRECT PARENT (`*:has(> …)`) rather than a named
   ancestor: the bars are `position: fixed`, and whether that resolves against the
   viewport or against an ancestor depends on whether anything above them is
   carrying a transform — the parent is the one element that is the right place to
   reserve in either case. `:not(.is-hidden)` is the bars' own visibility state,
   the only signal explore.js publishes for this. 8rem covers the tallest observed
   bar (a wrapped two-line `.msb-inner`, ~101px) plus its 0.75rem offset. */
*:has(> .multi-select-bar:not(.is-hidden)),
*:has(> .section-select-bar:not(.is-hidden)) { padding-bottom: 8rem; }

@media (max-width: 560px) {
  .multi-select-bar { left: 0.75rem; right: 0.75rem; bottom: 0.75rem; transform: none; max-width: none; }
  .msb-inner { border-radius: var(--radius-lg); justify-content: space-between; }

  /* `.site-row--selectable`'s three-column grid is declared after the stacking rule
     in the mobile-polish block above and wins at equal specificity, so on a phone
     the middle track is squeezed to a width its own tags overflow — the section and
     equipment chips then paint straight over the availability chip in the right
     column (measured: an 88×19px overlap of "Amphitheatre" on "Closed for the
     season"). Restack here, AFTER that declaration: checkbox + details on the first
     row, status + action across the full width on the second. `minmax(0, 1fr)`
     rather than `1fr` because a bare `1fr` floors at min-content and so cannot
     shrink enough to let the tags wrap. */
  .site-row--selectable { grid-template-columns: auto minmax(0, 1fr); }
  .site-row--selectable .site-row__right { grid-column: 1 / -1; }
  .site-row__tags .tag { max-width: 100%; }
}

/* ============================================================================
   UNIFIED DISCOVERY → ROAM FUNNEL  (reach control · results fork · Roam layer ·
   filter bands) — sections B/C/E/F. Client-side reshape; tokens only.
   ========================================================================== */

/* ---- Filter LEVEL bands (Park · Section · Site) — section F ---------------- */
.filter-band { margin: 0 0 1.1rem; }
.filter-band__label {
  margin: 0.4rem 0 0.1rem; font-size: 0.78rem; font-weight: 700;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--forest-deep);
}
.filter-band__hint { margin: 0 0 0.5rem; font-size: 0.84rem; color: var(--sage); }

/* ---- Reach control ("How far are you willing to drive?") — section B ------- */
.reach-wrap { margin: 0.4rem 0 0.2rem; }
.reach-toggle {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.5rem 0.85rem; font: inherit; font-size: 0.92rem; font-weight: 700;
  color: var(--forest-deep); background: var(--paper);
  border: 1.5px solid var(--line-strong); border-radius: 999px; cursor: pointer;
}
.reach-toggle .ic { width: 1rem; height: 1rem; color: var(--forest); }
.reach-toggle:hover { border-color: var(--forest); }
.reach-toggle:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
.reach-body { margin: 0.7rem 0 0; }
.reach-body.is-collapsed { display: none; }
.reach-control__head { margin: 0 0 0.5rem; font-weight: 700; color: var(--forest-deep); }
.reach-control__sub { margin: 0.35rem 0 0.7rem; font-size: 0.84rem; color: var(--sage); }
.reach-band-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.reach-band-chip {
  padding: 0.45rem 0.85rem; font: inherit; font-size: 0.9rem; font-weight: 600;
  color: var(--forest-deep); background: var(--paper);
  border: 1.5px solid var(--line-strong); border-radius: 999px; cursor: pointer;
  transition: border-color 0.12s var(--ease), background 0.12s var(--ease), color 0.12s var(--ease);
}
.reach-band-chip:hover { border-color: var(--forest); }
.reach-band-chip.is-active { background: var(--forest); color: #fff; border-color: var(--forest-deep); }
.reach-band-chip:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
.reach-access-filter { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; margin: 0.7rem 0 0; }
.reach-access-filter__label { font-size: 0.9rem; font-weight: 600; color: var(--forest-deep); }
.reach-access-btn {
  padding: 0.35rem 0.75rem; font: inherit; font-size: 0.88rem; font-weight: 600;
  color: var(--forest-deep); background: var(--paper);
  border: 1.5px solid var(--line-strong); border-radius: 999px; cursor: pointer;
}
.reach-access-btn:hover { border-color: var(--forest); }
.reach-access-btn.is-active { background: var(--forest); color: #fff; border-color: var(--forest-deep); }
.reach-access-btn:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }

/* ---- Reach MODE toggle (drive-time vs distance-by-road) — item 4 ---- */
.reach-mode-toggle { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 0.7rem; }
.reach-mode-panel { margin: 0.2rem 0 0; }
.reach-mode-panel[hidden] { display: none; }

/* ---- Results FORK (free vs Roam, symmetric, equal weight R4) — section C ---- */
.results-fork {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0.9rem;
  margin: 1rem 0; padding: 1rem; border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-lg); background: var(--paper);
}
.results-fork.is-solo { grid-template-columns: 1fr; max-width: 34rem; }
.fork-option {
  display: flex; flex-direction: column; gap: 0.35rem;
  padding: 0.9rem; border-radius: var(--radius); background: var(--paper-card);
  border: 1.5px solid var(--line-strong);
}
.fork-option__q { margin: 0; font-size: 0.86rem; font-weight: 600; color: var(--sage); }
.fork-option__promise { margin: 0; font-family: var(--font-display); font-size: 1.05rem; color: var(--forest-deep); }
.fork-option__promise strong { color: var(--ember-deep); }
.fork-option__price { margin: 0.2rem 0 0; font-size: 0.86rem; color: var(--sage); }
.fork-option--free .fork-option__price { font-weight: 700; color: var(--forest-deep); }
.fork-option__founding { margin: 0.15rem 0 0; font-size: 0.78rem; color: var(--sage); font-style: italic; }
/* Fork CTA buttons — equal footprint on both sides so the cards mirror (R4).
   The right-sized watch gets the FILLED (primary) variant; the other side is a
   bordered (secondary) button of the SAME height/padding/radius. Free-primary
   uses forest (green) so it's prominent yet distinct from the paid ember. */
.fork-cta {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.55rem;
  width: 100%; margin: 0.45rem 0 0.1rem;
  padding: 0.75rem 1.2rem; font: inherit; font-size: 0.98rem; font-weight: 700;
  border-radius: var(--radius); border: 1.5px solid transparent; cursor: pointer;
  text-decoration: none; text-align: center;
  transition: transform 0.15s var(--ease), box-shadow 0.2s var(--ease), filter 0.2s var(--ease), border-color 0.15s var(--ease);
}
.fork-cta .ic { width: 1.15rem; height: 1.15rem; }
.fork-cta:hover { transform: translateY(-1px); filter: brightness(1.04); }
.fork-cta:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
.fork-cta--primary-free {
  color: #fff; background: linear-gradient(180deg, var(--forest), var(--forest-deep));
  box-shadow: 0 10px 22px -10px rgba(28, 63, 47, 0.45);
}
.fork-cta--primary-roam {
  color: #fff; background: linear-gradient(180deg, var(--ember), var(--ember-deep));
  box-shadow: 0 10px 22px -10px var(--ember-glow);
}
.fork-cta--secondary {
  color: var(--forest-deep); background: transparent; border-color: var(--line-strong);
}
.fork-cta--secondary:hover { border-color: var(--forest); }
.fork-roam-btn { margin: 0.45rem 0 0.1rem; }
.results-fork__note { margin: -0.4rem 0 1rem; font-size: 0.88rem; color: var(--sage); }
@media (max-width: 560px) {
  .results-fork { grid-template-columns: 1fr; }
}

/* ---- In-place Roam LAYER (the selectable set) — section E ------------------ */
.roam-layer-wrap {
  margin: 1rem 0; padding: 1.1rem; border: 1.5px solid var(--forest);
  border-radius: var(--radius-lg); background: var(--paper);
}
.roam-layer-wrap[hidden] { display: none; }
.roam-layer-head { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; }
.roam-layer-head h2 { margin: 0; font-family: var(--font-display); font-size: 1.25rem; color: var(--forest-deep); }
.roam-layer-close {
  flex: none; width: 2rem; height: 2rem; font-size: 1rem; line-height: 1;
  color: var(--forest-deep); background: var(--paper-card);
  border: 1.5px solid var(--line-strong); border-radius: 999px; cursor: pointer;
}
.roam-layer-close:hover { border-color: var(--ember); color: var(--ember-deep); }
.roam-layer-close:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
.roam-layer-status { margin: 0.7rem 0 0; min-height: 1.2em; font-size: 0.92rem; color: var(--sage); }
.roam-layer-status.is-err { color: #a8392a; }
.roam-layer-status.is-info { color: var(--ink); }
.roam-layer-coverage { margin: 0.8rem 0 0.1rem; font-family: var(--font-display); font-size: 1.1rem; font-weight: 600; color: var(--forest-deep); }
.roam-layer-sites { margin: 0 0 0.15rem; font-size: 0.9rem; font-weight: 600; color: var(--ember-deep); }
.roam-layer-sites[hidden] { display: none; }
.roam-layer-why { margin: 0 0 0.5rem; font-size: 0.86rem; color: var(--sage); }
.roam-layer-why[hidden] { display: none; }
.roam-layer-border { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; margin: 0.5rem 0; }
.roam-layer-hint { margin: 0.4rem 0; font-size: 0.86rem; color: var(--sage); }
.roam-layer-list { list-style: none; margin: 0.4rem 0; padding: 0; display: flex; flex-direction: column; gap: 0.15rem; }
.roam-layer-row {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
  padding: 0.45rem 0.2rem; border-bottom: 1px solid var(--line);
}
.roam-layer-label { display: flex; align-items: center; gap: 0.55rem; cursor: pointer; font-size: 0.95rem; color: var(--ink); }
.roam-layer-label input[type="checkbox"] { accent-color: var(--forest); width: 1.05rem; height: 1.05rem; }
.roam-layer-count { flex: none; font-size: 0.84rem; color: var(--sage); }
/* The routed drive estimate sits between the park name and the site count. `margin-left:
   auto` pushes that pair to the right edge while the name keeps the space it needs;
   `white-space: nowrap` stops a value breaking across "2 hr" / "15 min". */
.roam-layer-drive { flex: none; margin-left: auto; font-size: 0.84rem; color: var(--sage); white-space: nowrap; }
.roam-layer-drive-note { font-size: 0.8rem; }
.roam-layer-founding { margin: 0.7rem 0 0.5rem; font-size: 0.82rem; color: var(--sage); }
.roam-layer-identity { margin: 0 0 0.7rem; }
.roam-layer-identity[hidden] { display: none; }
.roam-layer-identity-label { display: block; font-size: 0.86rem; font-weight: 600; color: var(--ink); margin: 0 0 0.35rem; }
.roam-layer-identity-input {
  width: 100%; padding: 0.65rem 0.85rem; font: inherit; font-size: 0.95rem;
  border: 1.5px solid var(--line-strong); border-radius: var(--radius);
  background: var(--paper); color: var(--ink);
}
.roam-layer-identity-input:focus-visible { outline: 3px solid var(--forest); outline-offset: 1px; }
.roam-layer-identity-signin { margin: 0.35rem 0 0; font-size: 0.82rem; color: var(--sage); }
.roam-layer-identity-signin a { color: var(--ember-deep); font-weight: 700; }
.roam-layer-commit { margin: 0.3rem 0 0; }
.roam-layer-calm { padding: 0.8rem 0; }
.roam-layer-calm h3 { margin: 0 0 0.3rem; font-family: var(--font-display); font-size: 1.1rem; color: var(--forest-deep); }
.roam-layer-calm p { margin: 0; font-size: 0.92rem; color: var(--sage); }
.roam-price-clause-secondary { color: var(--sage); }

/* ---- flexible-dates (Scope A) + min-stay hints (Scope B) ------------------ */
/* The mode toggle / nights stepper / nudge / validation elements are styled by
   flex-dates.css; these are the explore-only wrappers + the min-stay note/alert. */
.fd-date-mode-wrap { margin: 0 0 0.6rem; }
.fd-flex-body { margin: 0.2rem 0 0; }

/* Park-view demo-source note — rendered only when the hero was built from the dev
   fixtures rather than a live read, so a preview never passes demo data off as live.
   Dev hosts only (DEV_FIXTURES_ENABLED in explore.js). Same quiet treatment as the
   min-stay note below: a provenance advisory, not an error. */
.demo-note {
  margin: 0.5rem 0 0;
  font-size: 0.9rem;
  color: var(--sage);
}

/* Park-view min-stay note (Scope B) — quiet, honest, only rendered when known. */
.fd-minstay-note {
  margin: 0.5rem 0 0;
  font-size: 0.9rem;
  color: var(--sage);
}

/* Actionable min-stay nudge (Scope B tie-in) — the desired-nights-below-minimum
   flag on a park view or a site row. Ember-toned to read as an advisory, not an
   error. On a site row it sits inline under the name as a small block. */
.fd-minstay-alert {
  display: block;
  margin: 0.25rem 0 0;
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--ember-deep, #8a3d15);
}
.site-row__name .fd-minstay-alert { font-weight: 500; }

/* Bridge from the device-local watches list to the account manager (/watch). */
.watches-account-link { margin: 0.7rem 0 0; }

/* ============================================================================
   SOFT-LAUNCH QA — park-page information architecture (EXP-1..EXP-7, EXP-11)
   ========================================================================== */

/* Facet chips carry their matching count; a chip that matches everything or
   nothing is never rendered at all (explore.js applyFacets). */
.cat-type-chip__n { font-variant-numeric: tabular-nums; opacity: 0.72; }
.cat-type-chip.is-active .cat-type-chip__n { opacity: 0.85; }

/* Section-grain facets. Deliberately their OWN classes rather than reusing the
   site-type chip's — `.cat-type-chips` is the site-type control's identity on this
   page and a guard counts it. Same visual language, separate identity. */
.sec-facet-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.2rem; }
.sec-facet-chip {
  font: inherit; font-size: 0.85rem; font-weight: 600; color: var(--forest-deep);
  background: var(--paper-card); border: 1.5px solid var(--line); border-radius: 999px;
  padding: 0.35rem 0.8rem; cursor: pointer;
}
.sec-facet-chip:hover { border-color: var(--forest); }
.sec-facet-chip.is-active { background: var(--forest); color: #fff; border-color: var(--forest-deep); }
.sec-facet-chip:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
.sec-facet-chip__n { font-variant-numeric: tabular-nums; opacity: 0.72; }

/* Reconciliation note: the operator's catalogue count and the live site list are
   two different reads and they disagree in production — say which is which
   rather than render one beside a list of the other length. */
.site-count-note {
  margin: 0 0 0.7rem; font-size: 0.82rem; color: var(--sage);
}

/* The ONE availability statement on the park page, counted from the rows the
   list actually renders (EXP-11 — it replaced a hero banner that asserted
   "Spots are open on your dates" off a park-grain rollup). */
.park-avail-line {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem;
  margin: 0 0 0.8rem; font-size: 0.9rem; color: var(--ink);
}
.park-avail-line.is-open { color: var(--avail-open, var(--forest-deep)); font-weight: 600; }

/* Section sort + expand-all (EXP-6). Labelled and switchable; nothing here is
   ever labelled "Most in demand" — there is no section-grain demand signal. */
.sec-sort {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin: 0 0 0.8rem;
}
.sec-sort__label { font-size: 0.8rem; color: var(--sage); margin-right: 0.2rem; }
.sec-sort__opt {
  font: inherit; font-size: 0.82rem; font-weight: 600; color: var(--forest-deep);
  background: var(--paper-card); border: 1.5px solid var(--line); border-radius: 999px;
  padding: 0.3rem 0.75rem; cursor: pointer;
}
.sec-sort__opt.is-active { background: var(--forest); color: #fff; border-color: var(--forest-deep); }
.sec-sort__opt:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
.sec-expand { margin-left: auto; }

/* Per-section select (EXP-5) — the checkbox lives in the section header, and the
   action beside it NAMES the section so it can never read as acting on the
   cross-park site selection docked at the bottom of the page. */
.sec-group__pick { display: inline-flex; align-items: center; margin-right: 0.15rem; }
.sec-group__cb { width: 1.05rem; height: 1.05rem; accent-color: var(--forest); cursor: pointer; }

/* The section multi-selection bar sits INLINE above the list — deliberately a
   different surface from the fixed site-selection bar, because they act on
   different things. */
.section-select-bar { margin: 0 0 0.9rem; }
.section-select-bar.is-hidden { display: none; }
.section-select-bar .msb-inner {
  background: var(--paper-soft); color: var(--forest-deep);
  border: 1.5px solid var(--line-strong); border-radius: var(--radius-lg);
  box-shadow: none;
}
.section-select-bar .msb-clear { color: var(--sage); }
.section-select-bar .msb-clear:hover { color: var(--forest-deep); }
.section-select-bar .msb-cta { background: var(--forest); color: #fff; }
.msb-scope {
  font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.06em;
  opacity: 0.75; margin-right: 0.5rem;
}
.msb-text { display: flex; align-items: baseline; flex-wrap: wrap; }

/* Geographic scope (ENT-3) — applied at the query, visible, one click to clear. */
.discover-scope {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.45rem;
}
.discover-scope__label { font-size: 0.8rem; color: var(--sage); }
.scope-select {
  font: inherit; font-size: 0.85rem; color: var(--ink);
  background: var(--paper-card); border: 1.5px solid var(--line-strong);
  border-radius: var(--radius); padding: 0.45rem 0.6rem;
}
.scope-select:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }
.scope-clear {
  font: inherit; font-size: 0.8rem; font-weight: 600; color: var(--forest-deep);
  background: none; border: 0; cursor: pointer; text-decoration: underline;
  text-underline-offset: 3px;
}
.scope-note { margin: 0; font-size: 0.82rem; color: var(--sage); }

/* A failed park query terminates in a stated error with a retry — never a
   permanent "Loading parks…" (docs/standards/refusal-to-operate.md). */
.discover-retry:empty { display: none; }
.discover-retry { margin: 0.4rem 0 0.8rem; }

/* The link-out panel that replaces a permanently-empty Photos panel (EXP-7). */
.panel--source-link p { font-size: 0.9rem; color: var(--sage); margin: 0 0 0.7rem; }

/* The two-month picker's layout (.wz-cal-months / .wz-cal-title2-row) and its range
   legend (.wz-cal-legend*) live in styles.css beside the WzCal grid primitives,
   shared with the alerts wizard's Step 3 — one definition is what keeps the two
   calendars looking alike. Only explore-specific chrome lives here. */

/* The resolved-location chip (ENT-2) — names the place we picked when the user
   didn't, and offers the one click that changes it. Empty whenever the user set
   the location themselves. */
.resolved-loc:empty { display: none; }
/* --moss is the provenance colour on this surface: freshness, coverage notes, and
   "we guessed". This line is the canonical one. */
.resolved-loc { margin: 0; font-size: 0.82rem; color: var(--moss); }
/* The fix for a wrong guess must be a real target, not a link inside the smallest
   text on the page — 44px is the floor, and the negative margins keep the padded
   hit area from opening a gap in the line it sits in. */
.resolved-loc__change {
  font: inherit; font-size: 0.82rem; font-weight: 700; color: var(--forest-deep);
  background: none; border: 0; cursor: pointer; text-decoration: underline;
  text-underline-offset: 3px;
  display: inline-flex; align-items: center; min-height: 44px;
  padding: 0 0.35rem; margin: -14px -0.35rem;
}
.resolved-loc__change:focus-visible { outline: 3px solid var(--forest); outline-offset: 2px; }

/* The ambiguous-place chooser. A typed name that names more than one real place
   — "Vancouver" is a city in British Columbia AND in Washington — is handed back
   to the visitor instead of resolved to whichever row the gazetteer lists first.
   Empty, and so hidden, whenever there is nothing to choose. */
.loc-choice:empty { display: none; }
.loc-choice { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.45rem 0 0.2rem; }
/* The question sits on its own row above the options — it is part of this node, not
   of the status line, so that it and the buttons it labels share one lifetime. */
.loc-choice__q { flex-basis: 100%; margin: 0; font-size: 0.9rem; color: var(--ink); }
.loc-choice__opt { font-size: 0.85rem; padding: 0.35rem 0.8rem; }

/* While a site selection is live, the section-scoped action visually steps BACK from
   the selection bar's CTA — it acts on the whole section, not on the ticks, and it
   used to be the nearest button to a live selection while saying nothing about that
   (the "3 sites became 18" defect). Its label and title now say so; this makes the
   difference visible too. */
.sec-group__watch.is-aside {
  opacity: 0.72;
  border-style: dashed;
}
.sec-group__watch.is-aside:hover { opacity: 1; }
