/* ============================================================
   nokken chrome — top bar (desktop) and bottom tabs (mobile).
   Mirrors the Round 3 prototype's `.topbar`, `.search-mini`,
   `.icon-btn`, and `.tabbar` rules. The one production change
   from the prototype: tabbar uses `position: fixed` against the
   viewport (the prototype used `position: absolute` inside a
   `.screen` frame — a canvas-mode artefact).
   ============================================================ */

/* ---- Top bar (desktop ≥ 960px) ------------------------------- */
.topbar {
  display: flex;
  align-items: center;
  gap: var(--space-7);
  padding: 14px var(--space-8);
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border-subtle);
  position: relative;
  /* Above Leaflet's `.leaflet-control-container` (default z-index 1000).
     Map containers like `.river-map` / `.sd-feature-map` use
     `position: relative` without `isolation`, so Leaflet's control
     z-index leaks to the root stacking context — without this the
     mobile search overlay (z=30 inside this stacking context) and
     the desktop search dropdown (z=20) paint behind Leaflet UI on
     any page that hosts a map. `.map-wrap` on /WhatsUpMap clamps
     this locally with `isolation: isolate`; the chrome-layer bump
     covers every present and future map surface in one rule. */
  z-index: 1100;
}

.topbar .logo {
  display: flex;
  align-items: center;
  text-decoration: none;
  /* Pin the anchor against flex-squeeze; without this the logo
     inherits the global reset's `max-width: 100%` on its SVG
     child and the 28px wordmark collapses progressively as the
     viewport narrows towards the desktop→mobile breakpoint. The
     breakpoint itself now lands above the topbar's natural
     minimum width (see the responsive swap at the bottom of this
     file), so the logo never needs to shrink. */
  flex-shrink: 0;
}
.topbar .logo svg {
  /* Override the base.css reset's `max-width: 100%` for the logo
     specifically — the reset is still valuable for content images
     but here it lets flex squeeze shrink the static 28px wordmark. */
  max-width: none;
}

.topbar nav {
  display: flex;
  gap: var(--space-2);
  margin-left: var(--space-3);
}

.topbar nav a {
  color: var(--text-secondary);
  text-decoration: none;
  padding: var(--space-3) 14px;
  font-size: 14px;
  font-weight: 500;
  border-radius: 8px;
  letter-spacing: -0.005em;
  /* "What's up" wraps to two lines if the flex container has to
     squeeze. The desktop→mobile breakpoint is now set above the
     topbar's natural minimum width so this is belt-and-braces,
     but leaving wrap enabled leaves the layout one override
     away from regressing. */
  white-space: nowrap;
}

.topbar nav a:hover {
  background: var(--surface-sunken);
  color: var(--text-primary);
  text-decoration: none;
}

.topbar nav a.current {
  color: var(--text-primary);
  background: var(--brand-050);
}

.topbar .grow { flex: 1; }

/* Search-mini base rules — usable in any surface (desktop topbar,
   mobile overlay, and wherever else a live-search entry lands
   later). Surface-specific tweaks live in their owning section. */
.search-mini {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 7px var(--space-4);
  background: var(--surface-sunken);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  font-size: 13px;
  color: var(--text-muted);
  font-family: var(--font-sans);
  position: relative;
}

.search-mini .search-mini-input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: var(--text-primary);
}

.search-mini .search-mini-input::placeholder {
  color: var(--text-muted);
}

/* iOS Safari zooms the page when an input with computed font-size
   < 16px receives focus. Override at mobile widths to suppress the
   zoom — the desktop 13px stays as designed (compact chrome). */
@media (max-width: 767.98px) {
  .search-mini .search-mini-input { font-size: 16px; }
}

/* Native clear button / decorations get in the way of our custom
   dropdown; suppress them and let the JS own the cleared-state UX. */
.search-mini .search-mini-input::-webkit-search-decoration,
.search-mini .search-mini-input::-webkit-search-cancel-button { display: none; }

.search-mini .kbd,
.search-mini .search-mini-kbd {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 11px;
  background: var(--surface-raised);
  padding: 2px 6px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--border-subtle);
}

/* Desktop topbar wants the search-mini to reserve width so the bar
   doesn't feel empty. The mobile overlay leaves it fluid. */
.topbar > .search-mini { min-width: 260px; }

.topbar .icon-btn {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle);
  background: var(--surface-raised);
  color: var(--text-secondary);
  cursor: pointer;
}

.topbar .icon-btn:hover { background: var(--surface-sunken); }
/* When the icon-btn is a link (e.g. the alerts bell) it defaults to
   brand-blue underlined text. Restore the icon-button typography so
   it reads as chrome, not a text link. */
.topbar a.icon-btn { text-decoration: none; color: var(--text-secondary); }
.topbar a.icon-btn:hover { text-decoration: none; }
/* Active-page highlight — shared shape with the nav entries. Kept
   restrained; the icon-btn already stands out from body chrome. */
.topbar a.icon-btn.current {
  background: var(--brand-050);
  color: var(--brand-700);
  border-color: var(--brand-100);
}

/* D4 follow-up — color-scheme toggle button. Hosts three SVG
 * glyphs; only the one matching the current data-state is shown
 * via CSS rules below. ``color_scheme.js`` flips data-state on
 * click and lets the same rules pick the next glyph. */
.topbar .topbar-color-scheme .cs-icon { display: none; }
.topbar .topbar-color-scheme[data-state="auto"]  .cs-icon-auto  { display: block; }
.topbar .topbar-color-scheme[data-state="light"] .cs-icon-light { display: block; }
.topbar .topbar-color-scheme[data-state="dark"]  .cs-icon-dark  { display: block; }

/* D4 follow-up #2 — visible text label next to the glyph so the
 * three states (Auto / Light / Dark) are discoverable without
 * hover. Override the .icon-btn fixed 36×36 box so the button
 * grows to fit "Auto/Light/Dark"; height stays at 36 to keep the
 * topbar baseline aligned with the auth indicator. Mobile keeps
 * the label visible — at 375px the topbar drops nav + search-mini
 * (logo + toggle + auth only), so there's room. */
.topbar .topbar-color-scheme {
  width: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
}
.topbar .topbar-color-scheme .cs-label {
  font-size: 13px;
  font-weight: 500;
  line-height: 1;
}

/* R10 slice 2 closing — avatar pill for the signed-in topbar
 * indicator. Same .icon-btn box (36×36) so the topbar geometry is
 * identical to the pre-R10 bell; difference is the inner content
 * is initials-text in a brand-tinted circle rather than an SVG. */
.topbar .topbar-avatar {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--brand-600);
  color: var(--text-on-brand);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1;
}

/* ---- Bottom tab bar (mobile < 960px) ------------------------- */
.tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--surface-raised);
  border-top: 1px solid var(--border-subtle);
  display: grid;
  /* Five tabs: Home / What's up / Map / Rivers / Search. At 375px
     each slot is ~75px — enough for icon + short label. */
  grid-template-columns: repeat(5, 1fr);
  /* Hard-lock the rendered height so `--tabbar-height` is authoritative
     for every reservation elsewhere. Without this the tabbar's height
     was an emergent sum of child min-height + padding + line-boxes
     (≈74px in practice), and the sheet's reserve token drifted from
     reality — which was the PR-#52 regression root cause. */
  height: calc(var(--tabbar-height) + env(safe-area-inset-bottom));
  padding: 0 var(--space-2) env(safe-area-inset-bottom);
  box-sizing: border-box;
  /* z-index above the map page's transformed bottom sheet (z-50).
     Same-z stacking with a transformed sibling proved unreliable on
     iOS Safari — the sheet's stacking context could occlude the
     tabbar on transformed-sibling pages. Bump explicitly. */
  z-index: 100;
  box-shadow: 0 -2px 12px -4px rgba(14, 26, 38, 0.06);
}

/* Tabs are usually `<a>` (navigation) but Search is a `<button>`
   because it opens an overlay rather than navigating. Style both
   identically so the vocabulary stays consistent. */
.tabbar a,
.tabbar button.tabbar-search {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  /* No vertical padding — height is controlled by the grid's row
     (= the tabbar's locked content area). Horizontal padding stays
     for thumb-comfortable tap targets. */
  padding: 0 var(--space-1);
  text-decoration: none;
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 500;
  font-family: var(--font-sans);
  border-radius: var(--radius-md);
  /* Row height comes from the grid; explicit 100% so the focus /
     hover ring fills the slot. `--tabbar-height` at 74px exceeds
     the 44px WCAG touch target on its own. */
  height: 100%;
  background: transparent;
  border: none;
  cursor: pointer;
}

.tabbar a:hover,
.tabbar button.tabbar-search:hover { text-decoration: none; }
/* Phase 6 PR 6.4 — brand-500 (#2285c6) on white is 4:1, below WCAG
   AA (4.5:1) for the tabbar's 11px labels. brand-600 (#1a6ea8)
   clears 5.4:1 while staying recognisably the brand hue. */
.tabbar a.current { color: var(--brand-600); }

.tabbar a .ico,
.tabbar button.tabbar-search .ico {
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
}

/* ---- Site footer --------------------------------------------- */
/* Muted pipe-separated link row. Placeholder links — some routes
   aren't built yet and 404 on click; intentional (it surfaces
   what's still missing rather than hiding the absence). */
.site-footer {
  padding: 24px 20px 28px;
  border-top: 1px solid var(--border-subtle);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 14px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
}
.site-footer a {
  color: var(--text-muted);
  text-decoration: none;
  padding: 4px 2px;
}
.site-footer a:hover {
  color: var(--text-secondary);
  text-decoration: underline;
}

/* ---- Mobile search overlay ---------------------------------- */
/* Rendered inside the topbar; opens as a full-width panel below
   the top edge when `data-search-overlay-open` is tapped. The
   trigger is the Search tab in the bottom tabbar on mobile; the
   tabbar itself stays visible — the overlay's bottom stops short
   of it so Home/What's-up/Map/Rivers remain reachable while
   searching. Desktop hides the overlay entirely (media query below). */

.mobile-search-overlay {
  position: fixed;
  inset: 0;
  bottom: calc(var(--tabbar-height) + env(safe-area-inset-bottom));
  background: rgba(14, 26, 38, 0.4);
  z-index: 30;
  display: flex;
  flex-direction: column;
}

.mobile-search-overlay[hidden] { display: none; }

.mobile-search-overlay-bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 10px 16px calc(10px + env(safe-area-inset-top));
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border-subtle);
  box-shadow: var(--elev-2);
  position: relative;
  z-index: 1;
}

/* The search-mini inside the overlay takes the full bar width so
   typing surface matches the thumb-friendly mobile target. */
.mobile-search-overlay-bar .search-mini { flex: 1; min-width: 0; }

.mobile-search-close {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle);
  background: var(--surface-sunken);
  color: var(--text-secondary);
  cursor: pointer;
}

.mobile-search-close:hover { background: var(--surface-raised); }

.mobile-search-overlay-backdrop {
  flex: 1;
  border: none;
  background: transparent;
  padding: 0;
  cursor: default;
}

/* ---- Responsive swap -----------------------------------------
   Mobile: slim topbar (logo only), bottom tabbar handles nav +
   search. Desktop: full topbar with nav + inline search-mini.

   Breakpoint: 960px. The topbar's natural minimum width at
   default sizes is ~940px — logo + nav at nowrap + min
   search-mini + bell + horizontal padding + child gaps.
   Anything below that was flex-squeezing the logo and wrapping
   "What's up" to two lines. Was 768px before this PR; the
   tabbar (and its body bottom-padding reservation) moves with
   it since the mobile nav IS the tabbar in this stack. Other
   per-page mobile media queries (WhatsUpList table collapse,
   WhatsUpMap sheet, Favorites grid, iOS input-zoom fix) stay
   at 767.98px — those have their own geometry reasons to
   switch at phone/tablet widths, independent of the chrome
   swap. */
@media (max-width: 959.98px) {
  .topbar {
    /* Slim padding + smaller gap so the mobile header is compact. */
    padding: 10px 16px;
    gap: var(--space-3);
  }
  .topbar nav,
  .topbar > .search-mini {
    /* Nav and search-trigger move to the tabbar on mobile. The
       `>` targets only the topbar's direct search-mini child so
       the overlay's nested search-mini (still rendered inside
       the topbar element) stays visible. The R10 slice 2
       auth-aware indicator (.topbar-account / .topbar-signin)
       stays visible on mobile — without it, mobile signed-out
       users have no chrome path to /login, and signed-in users
       have no chrome path to /account/profile. The pre-R10
       bell was desktop-only because alerts had a section-detail
       affordance; auth has no such alternative. */
    display: none;
  }
  .topbar .grow { display: none; }
  /* Reserve space under the fixed tabbar so page content doesn't
     hide under it. `--tabbar-height` is the tabbar's locked content
     height (see tokens.css); add the home-indicator inset so the
     reservation matches what the tabbar actually occupies. */
  body { padding-bottom: calc(var(--tabbar-height) + env(safe-area-inset-bottom)); }
  /* Keep the footer above the tabbar rather than hidden under it. */
  .site-footer { margin-bottom: 0; }
}

@media (min-width: 960px) {
  .tabbar { display: none; }
  .mobile-search-overlay { display: none; }
}

/* ============================================================
   Cookie consent banner — site-wide.

   Server-rendered when ``nk_cookie_ack`` is absent. Sticks to the
   bottom of the viewport, sits above the mobile tabbar via the
   safe-area + tabbar offset. Dismissed by clicking "Got it",
   which writes the cookie and removes the element.
   ============================================================ */
.cookie-banner {
  position: fixed;
  left: var(--space-4);
  right: var(--space-4);
  bottom: calc(var(--space-4) + env(safe-area-inset-bottom));
  z-index: 1000;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
  /* Tight padding: the banner is informational and should not
     compete with the page hero for LCP. See chrome.css comment
     near .cookie-banner__msg for the LCP-shrink rationale. */
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--elev-2, 0 6px 24px rgba(0, 0, 0, 0.18));
  color: var(--text-primary);
  font-size: 13px;
  line-height: 1.4;
}
.cookie-banner__msg {
  /* Small font + short copy keeps the painted area below the
     LCP threshold on routes like /WhatsUpList where the natural
     hero (filter-chip strip) is also small. Shrinking past this
     hits diminishing returns; .lighthouserc.json carries a small
     /WhatsUpList performance allowance to reflect the residual
     cost of shipping the banner at all (post-F2b GDPR). */
  margin: 0;
  flex: 1 1 320px;
  min-width: 0;
}
.cookie-banner__actions {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  flex-wrap: wrap;
}
.cookie-banner__more {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 2px;
  font-size: 13px;
}
.cookie-banner__more:hover {
  color: var(--link-hover);
}
.cookie-banner__ack {
  /* WCAG AA: white-on-brand-500 (#2285c6) is only 4:1; using
     brand-700 (#155a8a) clears the 4.5:1 bar. Mirrors btn-primary's
     shipped contrast contract from components.css. */
  background: var(--brand-700);
  color: #fff;
  border: 1px solid var(--brand-700);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.cookie-banner__ack:hover {
  filter: brightness(1.1);
}
.cookie-banner__ack:focus-visible {
  outline: 3px solid color-mix(in srgb, var(--brand-500) 50%, transparent);
  outline-offset: 2px;
}

@media (max-width: 559.98px) {
  .cookie-banner {
    /* Above the mobile tabbar; --tabbar-height is locked in tokens.css. */
    bottom: calc(var(--tabbar-height) + var(--space-3) + env(safe-area-inset-bottom));
    left: var(--space-3);
    right: var(--space-3);
    padding: var(--space-3) var(--space-4);
  }
  .cookie-banner__actions {
    width: 100%;
    justify-content: flex-end;
  }
}

/* Reserve space at the bottom of the page for the fixed cookie
   banner so it doesn't cover interactive content on /section/<id>,
   /contact, /signup, etc. The body class is set server-side in
   base.html (and removed by the dismiss handler in
   _cookie_banner.html) so the reservation only applies while the
   banner is actually rendered. ~96px covers the banner's actual
   rendered height (text + Got it button + padding) at both
   single-line desktop and two-line mobile layouts; tabbar
   reservation at <960px composes additively below. Closure S4
   R2 fix. */
body.has-cookie-banner { padding-bottom: 96px; }
@media (max-width: 959.98px) {
  body.has-cookie-banner {
    padding-bottom: calc(var(--tabbar-height) + 110px + env(safe-area-inset-bottom));
  }
}
