:root {
  color-scheme: light;
  --surface-1: #fcfcfb;
  --page-plane: #f9f9f7;
  --text-primary: #0b0b0b;
  --text-secondary: #52514e;
  --text-muted: #898781;
  --gridline: #e1e0d9;
  --border: rgba(11, 11, 11, 0.10);

  --series-cleared: #2a78d6;
  --series-active: #eb6834;

  --seq-100: #cde2fb;
  --seq-200: #9ec5f4;
  --seq-350: #5598e7;
  --seq-450: #2a78d6;
  --seq-550: #1c5cab;
  --seq-700: #0d366b;
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --surface-1: #1a1a19;
    --page-plane: #0d0d0d;
    --text-primary: #ffffff;
    --text-secondary: #c3c2b7;
    --text-muted: #898781;
    --gridline: #2c2c2a;
    --border: rgba(255, 255, 255, 0.10);

    --series-cleared: #3987e5;
    --series-active: #d95926;
  }
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  height: 100%;
  background: var(--page-plane);
  color: var(--text-primary);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  overscroll-behavior: none;
}

/* ---- App shell: map + sidebar on desktop, map + bottom sheet on mobile ---- */

.app {
  display: flex;
  flex-direction: row;
  /* Leave room for the shared site header. The 0px fallback keeps the app
     full-height if /header.js fails to load, rather than breaking. */
  height: calc(100vh - var(--site-header-h, 0px));
}

#map-pane {
  flex: 1;
  min-width: 0;
  position: relative;
}

#map {
  position: absolute;
  inset: 0;
}

.panel {
  flex: 0 0 380px;
  /* 100% of .app, which already subtracts the shared header. Using 100vh here
     overflows the app by the header's height and gives the document a second
     scrollbar. */
  height: 100%;
  overflow-y: auto;
  background: var(--surface-1);
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
}

.panel-handle-row {
  display: none;
}

.panel-scroll {
  padding: 16px 20px 28px;
}

.panel-header h1 {
  font-size: 16px;
  font-weight: 600;
  margin: 0;
}

.stats {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 20px;
  margin-top: 10px;
}

.stat-tile {
  text-align: left;
}

.stat-tile .stat-value {
  font-size: 18px;
  font-weight: 600;
  line-height: 1.1;
}

.stat-tile .stat-label {
  font-size: 11px;
  color: var(--text-muted);
}

.filter-bar {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

/* display:flex would otherwise beat the UA sheet's [hidden] { display: none }
   on specificity, so a group hidden by JS would stay on screen. */
.filter-group[hidden] { display: none; }

.filter-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.filter-group-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin-right: 2px;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--page-plane);
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
}

.filter-chip .swatch {
  width: 8px;
  height: 8px;
  border-radius: 50%;
}

.filter-chip[data-active="true"] {
  color: var(--text-primary);
  border-color: var(--text-primary);
}

.filter-chip[data-active="false"] {
  opacity: 0.5;
}

.date-filter {
  align-self: stretch;
}

.histogram {
  display: flex;
  align-items: flex-end;
  gap: 1px;
  height: 44px;
  margin-top: 6px;
}

.histogram-bar {
  flex: 1 1 0;
  min-width: 1px;
  background: var(--series-cleared);
  opacity: 0.85;
  border-radius: 2px 2px 0 0;
  transition: opacity 120ms ease;
}

.histogram-bar.out-of-range {
  opacity: 0.2;
}

.range-slider {
  position: relative;
  height: 20px;
  margin-top: 6px;
}

.range-track {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 4px;
  transform: translateY(-50%);
  background: var(--gridline);
  border-radius: 2px;
  pointer-events: none;
}

.range-fill {
  position: absolute;
  top: 50%;
  height: 4px;
  transform: translateY(-50%);
  background: var(--series-cleared);
  border-radius: 2px;
  pointer-events: none;
}

.range-input {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 20px;
  margin: 0;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  pointer-events: none;
}

.range-input::-webkit-slider-runnable-track {
  -webkit-appearance: none;
  background: transparent;
  height: 20px;
}

.range-input::-moz-range-track {
  background: transparent;
  height: 20px;
}

.range-input::-webkit-slider-thumb {
  -webkit-appearance: none;
  pointer-events: auto;
  width: 14px;
  height: 14px;
  margin-top: 3px;
  border-radius: 50%;
  background: var(--surface-1);
  border: 2px solid var(--series-cleared);
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}

.range-input::-moz-range-thumb {
  pointer-events: auto;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--surface-1);
  border: 2px solid var(--series-cleared);
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}

.range-labels {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 4px;
}

.legend {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-secondary);
}

.legend h2 {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin: 0 0 6px;
}

.legend-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 3px 0;
}

.legend-row .swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
}

.legend-note {
  margin: 8px 0 0;
  font-size: 11px;
  line-height: 1.5;
  color: var(--text-muted);
}

.legend-note strong {
  color: var(--text-secondary);
}

.about {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  line-height: 1.55;
  color: var(--text-secondary);
}

.about h2 {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  margin: 0 0 8px;
}

.about h3 {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary);
  margin: 14px 0 4px;
}

.about p {
  margin: 0 0 8px;
}

.about a {
  color: var(--series-cleared);
}

.noscript-note {
  margin: 0;
  padding: 16px 20px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-secondary);
}

.ward-bin-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 3px 0;
  font-size: 11px;
}

.ward-bin-row .swatch {
  width: 12px;
  height: 12px;
  border-radius: 2px;
  flex: none;
}

.legend hr {
  border: none;
  border-top: 1px solid var(--gridline);
  margin: 8px 0;
}

.leaflet-popup-content-wrapper {
  background: var(--surface-1);
  color: var(--text-primary);
  border-radius: 6px;
}

.leaflet-popup-tip {
  background: var(--surface-1);
}

.leaflet-popup-content {
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 12px;
  margin: 10px 12px;
}

.popup-title {
  font-weight: 600;
  font-size: 13px;
  margin-bottom: 4px;
}

.popup-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 1px 0;
}

.popup-row .k {
  color: var(--text-muted);
}

.popup-row .v {
  font-weight: 500;
  text-align: right;
}

.popup-description {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--gridline);
  color: var(--text-secondary);
  font-style: italic;
  line-height: 1.4;
}

.load-error {
  position: absolute;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 13px;
  text-align: center;
  padding: 20px;
}

/* ---- Mobile: full-screen map, bottom sheet for stats/filters ---- */

@media (max-width: 860px) {
  .app {
    display: block;
    height: calc(100vh - var(--site-header-h, 0px));
  }

  #map-pane {
    position: fixed;
    inset: var(--site-header-h, 0px) 0 0 0;
  }

  .panel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1000;
    flex: none;
    height: auto;
    max-height: 42vh;
    overflow-y: auto;
    border-left: none;
    border-top: 1px solid var(--border);
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.18);
    transition: max-height 220ms ease;
  }

  /* Collapsed/expanded heights are set inline by JS from window.innerHeight, not from
     these vh rules -- on mobile browsers, CSS vh is pinned to the LARGEST viewport (as
     if the address bar were hidden), not the actually-visible one, so an "82vh" panel
     can visually overflow past the real viewport whenever the address bar is showing.
     window.innerHeight tracks the real, current viewport, so JS is the source of truth;
     these are just a pre-JS fallback to avoid a flash of the wrong size. */
  .panel[data-state="minimized"] {
    max-height: 56px;
  }

  .panel[data-state="expanded"] {
    max-height: 82vh;
  }

  .panel.dragging {
    transition: none;
  }

  .panel-handle-row {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    background: var(--surface-1);
  }

  .panel-drag-handle {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 10px 0 6px;
    background: none;
    border: none;
    cursor: grab;
    color: var(--text-secondary);
    font: inherit;
    touch-action: none;
    user-select: none;
  }

  .handle-bar {
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--gridline);
  }

  .handle-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
  }

  .panel[data-state="minimized"] .handle-label {
    display: none;
  }

  .panel[data-state="expanded"] .handle-label::after {
    content: " (tap to collapse)";
  }

  /* A dedicated, always-reliable escape hatch back to the map -- independent of the
     drag/tap handling on panel-drag-handle, so there's always a guaranteed way out. */
  .panel-hide-btn {
    flex: none;
    margin-right: 12px;
    padding: 5px 10px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--page-plane);
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 500;
    cursor: pointer;
  }

  .panel[data-state="minimized"] .panel-hide-btn {
    display: none;
  }
}

/* ---- Mobile point markers + clusters (desktop keeps plain canvas circleMarkers) ---- */

.point-badge {
  border-radius: 50%;
  border: 2px solid var(--surface-1);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

.cluster-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 2px solid var(--surface-1);
  color: #ffffff;
  font-weight: 600;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

/* ==========================================================================
   Tree-map additions.
   Everything above this line is the shared stylesheet from to-multiplex-map,
   copied verbatim so the two maps read as one site. Below are only the pieces
   this map needs that the permit map has no equivalent for: the origin/size
   palettes, the species picker, and the canvas overlay's furniture.
   ========================================================================== */

:root {
  /* Origin slots. A dot map puts every pair of colours side by side, so all pairs have
     to survive simulated colour-vision deficiency, not just neighbouring legend entries.
     None of the documented palette's own hues can be the fourth here -- violet collides
     with blue in dark mode (CVD dE 1.9), magenta with orange in light (12.9) -- so the
     fourth slot is a magenta stepped to clear every gate in both modes: worst all-pairs
     CVD dE 9.2 light / 9.4 dark, normal-vision 22.1 / 20.9.

     It would read better as a second shade of the native green, since the two native
     tiers nest. That isn't available: a same-hue step light enough to clear the
     normal-vision floor (dE 15) lands outside the lightness band, and no four-colour set
     exists with two hues closer than 56 degrees apart. Hue carries "different category"
     here; the legend ordering and labels carry "both are native". */
  --series-native: #1baf7a;
  --series-native-ena: #ad3aa2;
  --series-introduced: #2a78d6;
  --series-invasive: #eb6834;
  --series-unknown: #898781;
  --series-other: rgba(137, 135, 129, 0.55);

  /* Trunk-size ramp: one hue, light to dark, lightest step still >= 2:1 on the basemap. */
  --seq-1: #6da7ec;
  --seq-2: #3987e5;
  --seq-3: #256abf;
  --seq-4: #184f95;
  --seq-5: #0d366b;

  /* Full genus palette -- the documented eight, in the documented order. */
  --gen-1: #2a78d6; --gen-2: #eb6834; --gen-3: #1baf7a; --gen-4: #eda100;
  --gen-5: #e87ba4; --gen-6: #008300; --gen-7: #4a3aa7; --gen-8: #e34948;
}

@media (prefers-color-scheme: dark) {
  :root {
    --series-native: #199e70;
    --series-native-ena: #b03da5;
    --series-introduced: #3987e5;
    --series-invasive: #d95926;
    --series-unknown: #898781;
    --series-other: rgba(137, 135, 129, 0.5);

    --seq-1: #184f95;
    --seq-2: #256abf;
    --seq-3: #3987e5;
    --seq-4: #6da7ec;
    --seq-5: #b7d3f6;

    --gen-1: #3987e5; --gen-2: #d95926; --gen-3: #199e70; --gen-4: #c98500;
    --gen-5: #d55181; --gen-6: #008300; --gen-7: #9085e9; --gen-8: #e66767;
  }
}

/* ---- Ward checkboxes + link buttons (from the permit map's chart.css) ----- */

.ward-filter { align-self: stretch; width: 100%; }

.ward-filter-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 6px;
}

.ward-actions { display: inline-flex; gap: 10px; }

.link-btn {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 11px;
  color: var(--text-secondary);
  text-decoration: underline;
  cursor: pointer;
}

.link-btn:hover { color: var(--text-primary); }

.ward-list {
  max-height: 232px;
  overflow-y: auto;
  padding-right: 4px;
  border: 1px solid var(--border);
  border-radius: 8px;
}

.ward-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  font-size: 12.5px;
  color: var(--text-secondary);
  cursor: pointer;
}

.ward-row:hover { background: var(--page-plane); color: var(--text-primary); }
.ward-row input { accent-color: var(--series-introduced); margin: 0; flex: none; }
.ward-row .ward-name { flex: 1 1 auto; }

.ward-row .ward-count {
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--text-muted);
}

/* A standalone checkbox, distinct from the ward list's rows so the two don't get
   confused for one another. */
.check-row {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  width: 100%;
  margin-top: 6px;
  font-size: 12px;
  color: var(--text-secondary);
  cursor: pointer;
}
.check-row input { accent-color: var(--series-introduced); margin: 1px 0 0; flex: none; }

.filter-footnote {
  margin: 18px 0 0;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--text-muted);
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* The colour key renders nothing in origin mode. Without this it still counts as a flex
   item and contributes the filter bar's gap, leaving a hole under the Colour by chips. */
.filter-bar > .legend:empty { display: none; }

/* The shared .legend carries a top divider, which was right when the legend was its own
   block at the foot of the panel. Inline under the Colour by chips it reads as a section
   break in the middle of one control, so only this instance drops it. */
.filter-bar > .legend {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

/* ---- Species / genus picker ----------------------------------------------
   Same row rhythm as the ward list, plus a search box: 73 genera and 190
   species are too many to scan as chips. */

.taxon-search {
  width: 100%;
  font: inherit;
  font-size: 12.5px;
  padding: 6px 9px;
  margin-bottom: 6px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-1);
  color: var(--text-primary);
}

.taxon-search:focus-visible { outline: 2px solid var(--text-primary); outline-offset: 1px; }

.taxon-list {
  max-height: 232px;
  overflow-y: auto;
  padding-right: 4px;
  border: 1px solid var(--border);
  border-radius: 8px;
}

.taxon-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: 12.5px;
  padding: 5px 10px;
  background: none;
  border: 0;
  color: var(--text-secondary);
  cursor: pointer;
}

.taxon-row:hover { background: var(--page-plane); color: var(--text-primary); }
.taxon-row[aria-selected="true"] { color: var(--text-primary); font-weight: 600; }
.taxon-row input { accent-color: var(--series-introduced); margin: 0; flex: none; }
.taxon-row .taxon-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.taxon-row .taxon-sci { color: var(--text-muted); font-style: italic; }
.taxon-row .taxon-count {
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--text-muted);
}
.taxon-empty { padding: 10px; font-size: 12px; color: var(--text-muted); }

/* Compare mode: three slots the reader fills from the species list. */
.compare-slot {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-secondary);
  padding: 4px 8px;
  margin-bottom: 4px;
  border: 1px dashed var(--border);
  border-radius: 6px;
}
.compare-slot[data-filled="true"] { border-style: solid; color: var(--text-primary); }
.compare-slot .compare-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.compare-slot .compare-count { font-variant-numeric: tabular-nums; font-size: 11px; color: var(--text-muted); }
.compare-slot .compare-remove {
  font: inherit; font-size: 14px; line-height: 1;
  background: none; border: 0; padding: 0 2px;
  color: var(--text-muted); cursor: pointer;
}

/* The sequential legend is a continuous bar, not five swatches. */
.ramp-bar { display: flex; height: 10px; border-radius: 2px; overflow: hidden; margin-bottom: 4px; }
.ramp-bar span { flex: 1; }
.ramp-labels {
  display: flex; justify-content: space-between;
  font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums;
  margin-bottom: 6px;
}

/* ---- Canvas overlay furniture -------------------------------------------- */

#tree-canvas { position: absolute; pointer-events: none; }

.map-badge {
  position: absolute; z-index: 500;
  left: 50%; top: 14px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 8px;
  padding: 7px 13px;
  font-size: 12px; color: var(--text-primary);
  background: var(--surface-1);
  border: 1px solid var(--border); border-radius: 999px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12);
}
.map-badge[hidden] { display: none; }

.spinner {
  width: 11px; height: 11px; border-radius: 50%;
  border: 2px solid var(--gridline); border-top-color: var(--text-primary);
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2.4s; } }

/* Hover readout, so identity never rests on colour alone. */
.tree-tip {
  position: absolute; z-index: 550; pointer-events: none;
  padding: 5px 9px; max-width: 240px;
  font-size: 12px; line-height: 1.35;
  background: var(--surface-1); color: var(--text-primary);
  border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.14);
}
.tree-tip[hidden] { display: none; }
.tree-tip .tree-tip-sci { color: var(--text-muted); font-style: italic; }

/* ---- Popup extras --------------------------------------------------------
   .popup-title / .popup-row / .popup-description come from the shared sheet;
   these are the two pieces specific to a tree. */

.popup-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 600;
  margin-bottom: 6px;
  color: var(--text-secondary);
}
.popup-badge .swatch,
.taxa-row .swatch,
.compare-slot .swatch,
.taxon-row .swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
}

.popup-badge .swatch,
.taxon-row .swatch { box-shadow: 0 0 0 1px var(--border); }

.popup-figure { position: relative; margin: 6px 0 2px; }
/* Diverging from the centre, which is the median for this species: left of centre is
   smaller than typical, right is bigger. One hue in both directions -- the distance is
   magnitude, not polarity, and neither end is good or bad. */
.popup-figure-bar {
  position: relative;
  height: 5px;
  background: var(--gridline);
  border-radius: 3px;
  overflow: hidden;
}
.popup-figure-fill { position: absolute; top: 0; height: 100%; background: var(--seq-3); }
/* Sits outside .popup-figure-bar, which clips its children to hold the fill's rounded
   ends -- inside it, the tick was cropped to invisibility. */
.popup-figure-tick {
  position: absolute; top: -2px; left: 50%;
  width: 1px; height: 9px; margin-left: -0.5px;
  background: var(--text-secondary);
}
.popup-figure-label { font-size: 11px; color: var(--text-muted); margin-top: 4px; }
