/* =========================================================================
   map.css — the map page. Full-bleed Leaflet below a sticky nav.
   Loaded only by map.html, AFTER leaflet.css so Leaflet's own chrome can be
   restyled into the owner's palette.

   No raw colour literals: every value is a theme.css role token.
   The donor's `.panel-label` collided with a different `.panel-label` in the
   studio sheet; the map's variant is `.map-panel-label` here.
   ========================================================================= */

.map-page { overflow: hidden; }          /* the map scrolls, the page does not */
.map-page .night { display: none; }      /* the basemap IS the backdrop here */

/* ★★★ THE SHELL ENDS ABOVE THE LIVE BAR — one clearance, not four.
   The shell used to fill everything under the nav, which put its bottom strip
   UNDERNEATH the bar (fixed to the bottom of the viewport). The map itself,
   its southern labels and its markers were all covered, and every overlay
   that had to stay readable dodged the bar on its own: the credit, the zoom
   control, the tilt float and the coordinate readout each carried their own
   `+ var(--livebar-h)`. Four places to forget, and the credit HAD been
   forgotten once already.

   Reserving the height on the SHELL fixes the map as well as the overlays,
   and it fixes them by construction: an overlay anchored to the bottom of a
   shell that already stops above the bar cannot be covered by it, so none of
   them needs to know the bar exists. Adding the token again inside here would
   now push that overlay a bar's height too high - see the test in
   tests/frontend/run_all.py, which pins both halves.

   The arithmetic closes: nav 62 + shell (100dvh - 62 - bar) + the
   `body.has-livebar` bottom padding (bar) = exactly 100dvh, so the page still
   does not scroll and nothing is clipped. `--livebar-h` falls back to 0px on
   a site with no bar, which is the whole-viewport behaviour this had before. */
.map-shell {
  position: relative;
  /* nav is 62px tall and sticky; fill everything under it, less the bar. dvh
     so a phone's collapsing address bar does not leave a dead strip. */
  height: calc(100vh - 62px - var(--livebar-h, 0px));
  height: calc(100dvh - 62px - var(--livebar-h, 0px));
  width: 100%;
}

#leafmap {
  position: absolute; inset: 0;
  background: var(--bg-deep);
  outline: none;
}

/* Leaflet's own chrome, restyled */
.leaflet-container { background: var(--bg-deep); font-family: var(--font-ui); }
.leaflet-control-zoom a {
  background: var(--scrim-panel); color: var(--fg-dim);
  border: 1px solid var(--border-2); border-radius: var(--r-s) !important;
  width: 32px; height: 32px; line-height: 30px; font-size: 17px;
  margin-bottom: 5px; backdrop-filter: blur(8px);
}
.leaflet-control-zoom a:hover { background: var(--surface-3); color: var(--fg); }
/* Was a hand-typed 74px, which is the live bar's height plus a gap written out
   in a sheet that has no other reason to know it. Same token as the credit. */
.leaflet-control-zoom { border: 0 !important; box-shadow: none !important;
  margin: 0 14px 30px 0 !important; }
.leaflet-bar { box-shadow: none; }

.leaflet-tooltip {
  background: var(--scrim-panel); color: var(--fg-dim);
  border: 1px solid var(--border-2); border-radius: 6px;
  box-shadow: var(--shadow-panel);
  font: 500 12.5px/1.4 var(--font-ui); padding: 5px 9px;
}
.leaflet-tooltip::before { display: none; }
.leaflet-tooltip .dim { color: var(--fg-mute); }

/* area-name labels drawn straight onto the map */
.poi-label {
  font: 600 10.5px/1 var(--font-ui);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--fg);
  text-shadow: var(--shadow-text);
  white-space: nowrap; pointer-events: none;
  transform: translate(-50%, -50%);
}

/* ---------- side panel ---------- */
.map-panel {
  position: absolute; top: 14px; left: 14px; z-index: var(--z-map-panel);
  width: 290px; max-height: calc(100% - 28px);
  display: flex; flex-direction: column;
  background: var(--scrim-panel);
  border: 1px solid var(--border-2); border-radius: var(--r-lg);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
}
.panel-head {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 14px 15px; border-bottom: 1px solid var(--border);
}
.panel-head h1 { font-size: 20px; margin: 0; }
.panel-head p { margin: 4px 0 0; font-size: 12.4px; }
.panel-x {
  margin-left: auto; flex: none; width: 32px; height: 32px;
  display: none; place-items: center;
  background: transparent; border: 1px solid var(--border-2);
  border-radius: var(--r-s); color: var(--fg-dim); cursor: pointer;
}
.panel-body { padding: 13px 15px 16px; overflow-y: auto; scrollbar-width: thin; }

.map-panel-label {
  margin: 0 0 9px;
  font: 600 10px/1 var(--font-ui); letter-spacing: .16em;
  text-transform: uppercase; color: var(--fg-mute);
}
/* .map-panel-label sets the margin shorthand, which zeroes margin-top and so
   silently beats the .mt-m utility. Restore it explicitly, and give each block
   after the first a rule so the panel reads as sections. */
.map-panel-label.mt-m {
  margin-top: 20px; padding-top: 16px;
  border-top: 1px solid var(--border);
}
.panel-note { font-size: 12.4px; margin: 6px 0 0; line-height: 1.5; }

.legend { display: flex; flex-direction: column; gap: 1px; }
/* The line the legend shows when the POI file is not installed: the "Layers"
   heading must never sit over a silent empty box. Colour comes from the
   sitewide .dim class - this rule is spacing only. */
.legend-note { font-size: 12.4px; line-height: 1.5; margin: 2px 0 0; padding: 0 7px; }
.legend-row {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 7px; border-radius: 6px; cursor: pointer;
  font-size: 13.2px; color: var(--fg-dim);
  transition: background .14s;
}
.legend-row:hover { background: var(--surface-hover); color: var(--fg); }
.legend-row input { accent-color: var(--accent); width: 14px; height: 14px; flex: none; cursor: pointer; }
/* --c is the category colour, set inline from the assembled map config —
   per-category values are DATA, not theme, so they arrive with the layer. */
.legend-swatch {
  width: 9px; height: 9px; border-radius: var(--r-round); flex: none;
  background: var(--c); box-shadow: 0 0 0 3px color-mix(in srgb, var(--c) 22%, transparent);
}
.legend-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.legend-count {
  font: 500 11px/1 var(--font-mono); color: var(--fg-mute);
  font-variant-numeric: tabular-nums; flex: none;
}

/* ---------- ambient AI layer: per-kind filter ----------
   One row per species the live feed is currently reporting: colour dot, name,
   count, toggle. Rows are built by map.js and rebuilt only when the SET of
   species changes, so the 8 s poll repaints numbers and nothing else.

   --c is the kind's colour, set inline by map.js from the assembled map config
   — exactly like .legend-swatch above. Per-kind colours are map DATA (a
   carnivore stays red when the site is repainted), so they arrive with the
   layer rather than living in this token-only sheet. */
/* SAY IT OUT LOUD. `.ai-kinds { display: flex }` and `.legend-row
   { display: flex }` both out-specify the browser's own `[hidden]` rule, so
   without these two the panel and the AI toggle would ship VISIBLE on every
   map page and the layer would advertise itself on sites that never installed
   the feed. This sheet has no global [hidden] rule to lean on. */
.ai-kinds[hidden], .legend-row[hidden] { display: none; }

.ai-kinds { display: flex; flex-direction: column; gap: 1px; }
.aik-bulk { display: flex; align-items: center; gap: 6px; margin: 0 0 6px; }
.aik-hint {
  flex: 1; min-width: 0;
  font: 500 10px/1 var(--font-mono); letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg-mute);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.aik-bbtn {
  flex: none; padding: 5px 10px; border-radius: var(--r-xs);
  border: 1px solid var(--border-2); background: transparent;
  color: var(--fg-dim); cursor: pointer;
  font: 600 9.5px/1 var(--font-ui); letter-spacing: .1em;
  text-transform: uppercase; transition: background .14s, color .14s;
}
.aik-bbtn:hover { background: var(--surface-hover); color: var(--fg); }

.aik {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 6px 7px; border-radius: 6px;
  border: 1px solid transparent; background: transparent;
  cursor: pointer; text-align: left;
  font: inherit; font-size: 13.2px; color: var(--fg-mute);
  transition: background .14s, color .14s;
}
.aik:hover { background: var(--surface-hover); }
.aik.on { color: var(--fg-dim); }
.aik-dot {
  width: 9px; height: 9px; border-radius: var(--r-round); flex: none;
  background: var(--c); opacity: .35; transition: opacity .14s, box-shadow .14s;
}
.aik.on .aik-dot {
  opacity: 1;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c) 22%, transparent);
}
.aik-nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aik-ct {
  font: 500 11px/1 var(--font-mono); color: var(--fg-mute);
  font-variant-numeric: tabular-nums; flex: none; min-width: 16px; text-align: right;
}
/* The toggle is drawn from theme tokens only — the KIND's colour says which
   animal, the ACCENT says whether it is showing. Two jobs, two colours. */
.aik-tg {
  position: relative; flex: none; width: 26px; height: 15px;
  border-radius: 9px; background: var(--surface-3);
  border: 1px solid var(--border-2); transition: background .16s, border-color .16s;
}
.aik-tg::after {
  content: ""; position: absolute; top: 1.5px; left: 1.5px;
  width: 10px; height: 10px; border-radius: var(--r-round);
  background: var(--fg-mute); transition: left .16s, background .16s;
}
.aik.on .aik-tg { background: var(--accent); border-color: var(--accent); }
.aik.on .aik-tg::after { left: 13px; background: var(--fg); }
.aik:focus-visible { outline: none; border-color: var(--accent); }
/* A finger needs a bigger target than a mouse does. */
@media (pointer: coarse) {
  .aik { padding: 10px 7px; }
  .aik-bbtn { padding: 8px 12px; }
}

.coord {
  font: 500 12.5px/1.5 var(--font-mono); color: var(--fg);
  background: var(--surface-3); border: 1px solid var(--border);
  border-radius: var(--r-s); padding: 9px 11px; margin: 0;
  overflow-x: auto; white-space: nowrap; scrollbar-width: thin;
}

/* The credit sits at the bottom of the map shell, and the live bar is fixed to
   the bottom of the VIEWPORT on top of it - so the bar printed over the credit
   line, found by a rendered screenshot and by nothing else. `--livebar-h` is
   published by site.css on `body.has-livebar` and is absent when there is no
   bar, which is what the 0px fallback is for. */
.map-credit {
  position: absolute; right: 12px;
  bottom: 10px;
  z-index: var(--z-map-overlay);
  font: 500 10.5px/1.4 var(--font-ui); color: var(--fg-mute);
  text-shadow: var(--shadow-text); pointer-events: none;
  max-width: 46%; text-align: right;
}

/* MISSING BASEMAP — an honest panel, never a broken grey grid.
   map.js probes one tile at boot; if the pyramid is not installed on this box
   it says so here instead of leaving a viewport of dead squares that reads as
   "the site is broken". */
.map-missing {
  position: absolute; inset: 0; z-index: var(--z-map-overlay);
  display: grid; place-content: center; justify-items: center;
  gap: 8px; text-align: center; padding: 28px;
  background: var(--scrim-modal);
  /* The legend panel sits ABOVE this (--z-map-panel), and this layer takes no
     clicks: layers and the coordinate readout still work while the message is
     on screen. An explanation should not also be a lock. */
  pointer-events: none;
}
.map-missing h2 { font-size: 21px; }
.map-missing p { color: var(--fg-dim); max-width: 44ch; font-size: 14.4px; }
.map-missing code {
  font: 500 12px/1 var(--font-mono); color: var(--accent-hi);
  background: var(--surface-3); padding: 3px 6px; border-radius: var(--r-xs);
}

/* =========================================================================
   RELIEF VIEW — the same island, tilted.

   The flat map and the WebGL stage share one box. `.mode-3d` on the viewport
   swaps which one is painted; both stay in the layout so the canvas always has
   a measured size to build against and the swap costs no reflow.

   VISIBILITY, NOT DISPLAY. `#map3d` is faded and pointer-transparent rather
   than `display:none`: a display-none host has zero clientWidth, and a
   renderer sized 0x0 comes back as a black rectangle the first time it is
   shown. Leaflet has the same problem in reverse, so it keeps its box too.
   ========================================================================= */
.map-viewport { position: absolute; inset: 0; }

.map3d-stage {
  position: absolute; inset: 0;
  background: var(--bg-deep);
  opacity: 0; pointer-events: none;
  transition: opacity .22s ease;
}
.map-viewport.mode-3d .map3d-stage { opacity: 1; pointer-events: auto; }
/* The flat map keeps its size but stops taking clicks and stops painting, so
   two maps can never both answer the same drag. */
.map-viewport.mode-3d #leafmap { opacity: 0; pointer-events: none; }
#leafmap { transition: opacity .22s ease; }

@media (prefers-reduced-motion: reduce) {
  .map3d-stage, #leafmap { transition: none; }
}

.map3d-error {
  position: absolute; inset: 0;
  display: grid; place-items: center; text-align: center; padding: 20px;
  color: var(--fg-mute); font: 500 13px/1.5 var(--font-ui);
}

/* ---- follow me ---- */
.map-follow {
  display: inline-block; margin: 7px 0 0;
  font: 600 10.5px/1 var(--font-ui); letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg-mute);
  background: var(--surface-3); border: 1px solid var(--border);
  border-radius: var(--r-xs); padding: 7px 11px; cursor: pointer;
  transition: color .15s ease, border-color .15s ease;
}
.map-follow:hover { color: var(--fg); }
.map-follow.on { color: var(--accent-hi); border-color: var(--accent); }
.map-follow:focus-visible { outline: none; border-color: var(--accent); }

/* ---- the tilt control, in the legend panel ---- */
.tilt-wrap {
  display: flex; align-items: center; gap: 9px;
  margin: 0 0 6px;
}
.tilt-wrap.tilt-float {
  position: absolute; right: 14px; bottom: 84px;
  z-index: var(--z-map-overlay);
  background: var(--scrim-panel); border: 1px solid var(--border-2);
  border-radius: var(--r-s); padding: 7px 10px; backdrop-filter: blur(8px);
}
.tilt-lab {
  font: 600 10.5px/1 var(--font-ui); letter-spacing: .14em;
  text-transform: uppercase; color: var(--fg-mute);
  background: var(--surface-3); border: 1px solid var(--border);
  border-radius: var(--r-xs); padding: 6px 9px; cursor: pointer;
  transition: color .15s ease, border-color .15s ease;
}
.tilt-lab:hover { color: var(--fg); }
.tilt-lab.on { color: var(--accent-hi); border-color: var(--accent); }
.tilt-lab:focus-visible { outline: none; border-color: var(--accent); }
.tilt-wrap.loading .tilt-lab { opacity: .55; }
.tilt-wrap.failed .tilt-lab { border-color: var(--danger, var(--border-2)); }

.tilt-range {
  flex: 1 1 auto; min-width: 0; height: 20px;
  accent-color: var(--accent);
  cursor: pointer; background: transparent;
}
.tilt-val {
  font: 500 11.5px/1 var(--font-mono); color: var(--fg-mute);
  min-width: 3.2ch; text-align: right;
}

/* Hover card over a relief pin. Appended to <body>, so it is positioned in
   client coordinates and must sit above the map's own overlay layer. */
.map3d-tip {
  position: fixed; z-index: var(--z-toast, 90);
  background: var(--scrim-panel); color: var(--fg-dim);
  border: 1px solid var(--border-2); border-radius: 6px;
  box-shadow: var(--shadow-panel);
  font: 500 12.5px/1.4 var(--font-ui); padding: 5px 9px;
  pointer-events: none; opacity: 0; transition: opacity .12s ease;
  max-width: 46ch;
}
.map3d-tip.show { opacity: 1; }
.map3d-tip .h { color: var(--fg); font-weight: 600; margin-right: 6px; }

/* ---------- phone ---------- */
@media (max-width: 820px) {
  .map-shell { height: calc(100vh - 62px - var(--livebar-h, 0px));
                height: calc(100dvh - 62px - var(--livebar-h, 0px)); }
  .map-panel {
    top: 10px; left: 10px; right: 10px; width: auto;
    max-height: calc(100% - 20px);
  }
  .panel-x { display: grid; }
  .panel-body { display: none; }
  .map-panel[data-open="true"] .panel-body { display: block; }
  .map-credit { max-width: 60%; font-size: 9.5px;
                bottom: 8px; }
  .leaflet-control-zoom { margin: 0 10px 22px 0 !important; }
}
