/* Radioguard stylesheet.
   Structure: one sticky top bar with the main navigation, then either the
   full-screen map page or a scrolling documentation page. The map owns its own
   stacking order (see "Map layer order" below); everything else is ordinary
   flow layout. */

:root {
  --ink: #eef6f3;
  --muted: #8ea4a7;
  --faint: #60777d;
  --line: rgba(141, 184, 183, .16);
  --panel: #0d222e;
  --bg: #07151e;
  --teal: #86d5bd;
  --teal-bright: #a8f0d8;
  --yellow: #ffd44b;
  --red: #ff4435;
  --red-glow: rgba(255, 68, 53, .5);
  /* The field and the disks. These two are painted on canvas in app.js
     (FAN_COLOR, QUIET_FILL, QUIET_RIM); the tokens below are what the DOM
     mirrors of them — the opacity band under the fan slider, the disk key —
     read, so the key cannot drift from the map it describes. Amber and teal
     rather than red and green: red against green is the pair that collapses
     under the common colour-vision deficiencies, on a map whose whole content
     is two coloured regions, and red-for-danger / green-for-safe is a verdict
     this site explicitly declines to give. */
  --field: rgb(224, 140, 30);
  --field-wash: rgba(224, 140, 30, .16);
  --disk: rgb(56, 178, 172);
  --disk-rim: rgb(12, 74, 84);
  /* Kept apart from --field on purpose. Reusing the field colour for interface
     chrome means a hover state and a modelled value are painted the same, and on
     this map the colour carries data. The handles on a range control are
     interface; the band they drag is the field, and keeps its own colour. */
  --accent: rgb(96, 165, 250);
  --topbar: 64px;
  /* Distance from the bottom of the map to the row of floating buttons. Set
     from JavaScript on narrow screens, where the note's height decides it. */
  --note-offset: 14px;
  /* The two faces the site is built on, each with a substitute of the same kind
     behind it. Both families are served from this site (see website/vendor/fonts),
     so the fallback only shows if a font file itself fails to arrive — but the
     `font` shorthand resets the family, and without one the small monospace
     labels would come out in the browser's default serif, at a different width,
     rather than in another monospace. */
  --font-sans: 'Manrope', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'DM Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

html { background: var(--bg); }
body {
  margin: 0;
  color: var(--ink);
  background: var(--bg);
  font-family: var(--font-sans);
  font-size: 13px;
  -webkit-font-smoothing: antialiased;
}
button, input, select { font: inherit; min-height: 30px; }
button { color: inherit; cursor: pointer; }
a { color: var(--teal); }

:focus-visible { outline: 3px solid #f5d85b; outline-offset: 3px; }
.skip-link { position: fixed; top: 8px; left: 8px; z-index: 100; padding: 9px 12px; color: #07151e; background: #f5d85b; transform: translateY(-150%); }
.skip-link:focus { transform: translateY(0); }
.hidden { display: none !important; }
button:disabled, select:disabled, input:disabled { cursor: not-allowed; opacity: .55; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; animation-duration: .01ms !important; }
}
/* Users who ask for more contrast get it without hunting for a setting. */
@media (prefers-contrast: more) {
  :root { --ink: #ffffff; --muted: #d8eeea; --faint: #b4d0cc; --line: rgba(230, 255, 249, .34); }
  .control-panel, .map-note, .search-box { border-color: rgba(245, 216, 91, .7); }
}

/* --- Top bar ------------------------------------------------------------ */
.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  height: var(--topbar);
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 0 22px;
  border-bottom: 1px solid var(--line);
  background: rgba(7, 21, 30, .95);
  backdrop-filter: blur(16px);
}
.brand { display: flex; flex: 0 0 auto; align-items: center; gap: 10px; color: var(--ink); text-decoration: none; }
.brand strong { display: block; font-size: 16px; font-weight: 800; letter-spacing: -.8px; }
.brand strong span { color: var(--teal); }
.brand small { display: block; margin-top: 2px; color: #6b888c; font: 7px var(--font-mono); letter-spacing: 1.8px; }
.brand-mark { display: flex; align-items: end; gap: 2px; width: 20px; height: 22px; }
.brand-mark i { display: block; width: 4px; border-radius: 4px; background: var(--teal); transform: skew(-17deg); }
.brand-mark i:nth-child(1) { height: 10px; opacity: .45; }
.brand-mark i:nth-child(2) { height: 16px; opacity: .72; }
.brand-mark i:nth-child(3) { height: 22px; }

/* `overflow: visible` because the Understand menu is an absolutely positioned
   child of this nav: any other overflow value here would clip the dropdown at
   the bar's own bottom edge. Nothing needs to scroll — the group replaced four
   top-level items with one, so the remaining four fit well inside the width at
   which this bar is shown at all (below 900px the whole nav becomes the
   toggle's dropdown and sets its own overflow). */
.topnav { display: flex; align-items: center; gap: 2px; overflow: visible; }
.nav-item {
  padding: 9px 11px;
  border-radius: 6px;
  color: #8ca4a7;
  font-size: 11px;
  font-weight: 500;
  white-space: nowrap;
  text-decoration: none;
}
.nav-item:hover { color: var(--ink); background: rgba(135, 211, 187, .08); }
.nav-item.active { color: var(--teal-bright); background: rgba(135, 211, 187, .12); }

.main-content { min-height: 100vh; }
.page { display: none; max-width: 1510px; margin: 0 auto; padding: 34px 26px 48px; }
.active-page { display: block; }

/* --- Map page: full-screen mode ----------------------------------------- */
body:has(#page-map.active-page) { overflow: hidden; }
body:has(#page-map.active-page) .topbar { position: fixed; inset: 0 0 auto; }
body:has(#page-map.active-page) #page-map { position: fixed; inset: var(--topbar) 0 0; z-index: 1; max-width: none; margin: 0; padding: 0; }
body:has(#page-map.active-page) .map-workspace { display: block; height: 100%; }
body:has(#page-map.active-page) .map-card { position: absolute; inset: 0; overflow: visible; }
body:has(#page-map.active-page) .map-viewport { height: 100%; }
body:has(#page-map.active-page) .map-toolbar {
  position: absolute;
  z-index: 12;
  top: 18px;
  left: 18px;
  right: 380px;
  display: flex;
  padding: 0;
  border: 0;
  pointer-events: none;
}
body:has(#page-map.active-page) .search-box, body:has(#page-map.active-page) .toolbar-right { pointer-events: auto; }
body:has(#page-map.active-page) .search-results { top: 60px; left: 18px; margin: 0; width: min(440px, calc(100vw - 400px)); }

.map-workspace { display: grid; gap: 17px; }
.map-card { position: relative; min-width: 0; background: #0b202c; }
.map-viewport { position: relative; overflow: hidden; background: #d9e4df; }

/* --- Search ------------------------------------------------------------- */
.map-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.search-box {
  width: min(440px, 100%);
  height: 42px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 6px 0 11px;
  border: 1px solid rgba(83, 128, 128, .55);
  border-radius: 6px;
  color: #70898d;
  background: rgba(12, 39, 51, .78);
  backdrop-filter: blur(14px);
  box-shadow: 0 10px 28px rgba(0, 0, 0, .2);
}
.search-box:focus-within { border-color: #598b82; box-shadow: 0 0 0 3px rgba(134, 213, 189, .1); }
.search-glyph { flex: 0 0 auto; font-size: 20px; transform: rotate(-20deg); }
.search-box input { width: 100%; min-width: 0; min-height: 0; padding: 0; border: 0; outline: 0; color: var(--ink); background: transparent; font-size: 12px; }
.search-box input::placeholder { color: #668187; }
.search-box input::-webkit-search-cancel-button, .search-box input::-webkit-search-decoration { display: none; }
.search-clear { flex: 0 0 auto; width: 26px; height: 26px; min-height: 0; display: grid; place-items: center; border: 1px solid #2f5058; border-radius: 4px; color: #9db6b4; background: #123642; font-size: 16px; line-height: 1; }
.search-clear:hover { color: #e6f6ef; border-color: #6ba294; background: #17454c; }
/* An author `display` beats the user-agent `[hidden]` rule, so the hidden state
   has to be spelled out or the clear button would never disappear. */
.search-clear[hidden] { display: none; }
.toolbar-right { display: flex; gap: 7px; }
.tool-button { height: 42px; border: 1px solid #29464f; border-radius: 6px; padding: 0 12px; color: #96adae; background: rgba(12, 39, 51, .78); backdrop-filter: blur(14px); font-size: 11px; }
.tool-button:hover { color: var(--teal); border-color: #547d78; }

.search-results {
  position: absolute;
  z-index: 1100;
  display: none;
  width: min(440px, calc(100vw - 40px));
  border: 1px solid #2b5056;
  border-radius: 6px;
  background: #102d37;
  box-shadow: 0 15px 30px rgba(0, 0, 0, .3);
  overflow: hidden;
}
.search-results.visible { display: block; }
.result-item { display: flex; justify-content: space-between; gap: 12px; width: 100%; padding: 12px; border: 0; border-bottom: 1px solid rgba(135, 190, 180, .1); color: #a9bfba; background: transparent; text-align: left; font-size: 12px; }
.result-item:hover { background: #173c43; }
.result-item small { color: #66858a; font: 9px var(--font-mono); }
.result-empty { padding: 13px; color: #7f9a9c; font: 10px var(--font-mono); }

/* --- Leaflet map -------------------------------------------------------- */
.map-viewport #real-map { position: absolute; inset: 0; z-index: auto; overflow: hidden; background: #d9e4df; }
.map-viewport .leaflet-container { height: 100% !important; width: 100% !important; min-height: 100%; font-family: var(--font-sans); background: #d9e4df; }
.map-viewport .leaflet-tile-container { will-change: transform; }
.map-viewport .leaflet-tile { max-width: none !important; }
.map-viewport .leaflet-control-zoom { display: none; }
.map-viewport .leaflet-control-attribution { color: #3f5557; background: rgba(241, 247, 242, .78); font-size: 9px; }
.map-viewport .leaflet-control-attribution a { color: #2b5a54; }
.map-viewport .leaflet-popup-content-wrapper, .map-viewport .leaflet-popup-tip { color: #b8cec7; background: #0c2732; }
/* A popup may be taller than the screen: a mast can carry six antennas, each
   with its own details. Leaflet pans a popup into view but cannot shrink one,
   so the content is capped and scrolls, and the antenna panel gets a tighter cap
   of its own so the tab strip stays on screen while a long panel scrolls. */
/* Capped so the autopan can always clear the toolbar above it: a popup taller
   than the map has nothing to pan to, and the overflow lands under the search
   field. 62vh of a 900 px window is 558 px against a 774 px field, and the rest
   scrolls inside the panel. */
.map-viewport .leaflet-popup-content { font-size: 12px; max-height: min(62vh, 470px); overflow-y: auto; overscroll-behavior: contain; }
.popup-note { margin: 9px 0 0; color: #90aaad; font-size: 10px; line-height: 1.45; }
/* A link inside a popup is a button, because the popup's own markup is set as
   innerHTML and a real anchor would navigate the page instead of the app. It is
   styled as a link so it still reads as one. */
.popup-link { min-height: 0; padding: 0; border: 0; color: var(--teal); background: transparent; font: inherit; text-decoration: underline; }
.site-popup { min-width: 0; }
.site-popup > strong, .site-popup > small { display: block; }
.site-popup > strong { color: #d9e9e0; font-size: 12px; margin-bottom: 4px; }
.site-popup > small { color: #7d999b; font: 8px var(--font-mono); margin-bottom: 10px; }
.popup-row { display: grid; grid-template-columns: 108px 1fr; gap: 8px; padding: 5px 0; border-top: 1px solid rgba(134, 190, 180, .12); }
.popup-row span { color: #8aa3a6; font: 9px var(--font-mono); }
.popup-row b { color: #bad2c7; font-size: 10px; font-weight: 500; overflow-wrap: anywhere; }
/* The estimate block: a small segmented control over the two readings. It sits
   above the figures it changes, and the whole strip is the target rather than a
   checkbox label, because the two options are a question being answered rather
   than a setting being enabled. */
.estimate-block { margin-top: 10px; }
.estimate-mode { display: flex; gap: 0; margin-bottom: 2px; border: 1px solid #2b5056; border-radius: 5px; overflow: hidden; }
.estimate-mode button { flex: 1 1 0; min-height: 0; padding: 5px 6px; border: 0; border-right: 1px solid #2b5056; color: #8aa3a6; background: #102d37; font: 9px var(--font-mono); }
.estimate-mode button:last-child { border-right: 0; }
.estimate-mode button:hover { color: #dff3ec; background: #173f4a; }
.estimate-mode button.active { color: #eafaf3; background: #17454c; }
.estimate-mode button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* One tab per antenna on the mast. */
.popup-tabs { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 10px; }
.popup-tabs button { min-height: 0; padding: 4px 7px; border: 1px solid #2b5056; border-radius: 4px; color: #8aa3a6; background: #102d37; font: 9px var(--font-mono); }
.popup-tabs button:hover { color: #dff3ec; border-color: #46707a; }
.popup-tabs button.active { color: #eafaf3; border-color: #6ba294; background: #17454c; }
.sector-dial-wrap { display: grid; justify-items: center; gap: 4px; margin: 10px auto 8px; }
.sector-dial { width: 128px; height: 128px; overflow: visible; }
.sector-dial-slice { cursor: pointer; opacity: .72; stroke: #0c2732; stroke-width: 1.5; outline: none; }
.sector-dial-slice:hover, .sector-dial-slice:focus-visible { opacity: .95; stroke: #f5d85b; stroke-width: 2.5; }
.sector-dial-slice.active { opacity: 1; stroke: #ffffff; stroke-width: 2.5; }
.sector-dial-label { fill: #07151e; font: 700 7px var(--font-mono); text-anchor: middle; dominant-baseline: middle; pointer-events: none; }
.sector-dial-centre { fill: #0c2732; stroke: #d9e9e0; stroke-width: 1.5; pointer-events: none; }
.sector-dial-north { fill: #d9e9e0; font: 700 8px var(--font-mono); text-anchor: middle; pointer-events: none; }
.sector-dial-hint { color: #8aa3a6; font: 8px var(--font-mono); }
.sector-panel-tabs { display: flex; flex-wrap: wrap; gap: 4px; margin: 4px 0 6px; }
.sector-panel-tabs button { min-height: 0; padding: 4px 6px; border: 1px solid #2b5056; border-radius: 4px; color: #8aa3a6; background: #102d37; font: 9px var(--font-mono); }
.sector-panel-tabs button:hover, .sector-panel-tabs button.active { color: #eafaf3; border-color: #6ba294; background: #17454c; }
.popup-panel { display: none; max-height: 42vh; overflow-y: auto; overscroll-behavior: contain; }
.popup-panel.active { display: block; }

/* Map layer order: basemap → country mask → modelled field → mast dots →
   Leaflet popups → UI overlays. `#real-map` must not create its own stacking
   context, so Leaflet's panes can interleave with the field canvases. */
.map-viewport .leaflet-pane { z-index: auto; }
.map-viewport .leaflet-popup-pane { z-index: 700; }
/* Every stroke — fan outlines, settlement rims — sits one step above the field
   and one below the mast dots: Leaflet's overlay pane carries the paths it
   draws itself, and the outline pane carries ours. */
.map-viewport .leaflet-outline-pane { z-index: 520; }
.map-viewport .leaflet-tooltip-pane { z-index: 690; }
.map-viewport .leaflet-marker-pane { z-index: 640; }
.map-viewport .leaflet-overlay-pane { z-index: 430; }
.map-viewport > .map-footer, .map-viewport > .zoom-controls { z-index: 900; }

/* --- Live readout following the pointer --------------------------------- */
/* It hangs off the cursor, so it must never take a pointer event of its own: a
   tooltip that swallows the click it exists to save would be worse than no
   tooltip. It is pinned under the settings panel in stacking order because it is
   transient, and given the same translucent ground as the other map overlays.
   `[hidden]` is restated because `display: flex` below would otherwise win
   against the user-agent rule and the element could never be hidden. */
.probe-readout[hidden] { display: none; }
.probe-readout {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 900;
  display: flex;
  gap: 14px;
  padding: 6px 10px;
  border: 1px solid rgba(119, 163, 163, .3);
  border-radius: 6px;
  background: rgba(8, 27, 37, .78);
  backdrop-filter: blur(10px);
  pointer-events: none;
  transition: opacity .1s ease-out;
}
.probe-readout span { display: grid; gap: 1px; }
.probe-readout i { color: #7d999b; font: 8px var(--font-mono); font-style: normal; text-transform: uppercase; letter-spacing: .06em; }
.probe-readout b { color: #d9e9e0; font-size: 12px; font-variant-numeric: tabular-nums; }
/* Reads as inert where nothing nearby is transmitting, so a blank-looking value
   is not mistaken for a missing one. */
.probe-readout.zero b { color: #7d999b; }
body:has(#page-map.active-page) .map-toolbar,
body:has(#page-map.active-page) .control-panel,
body:has(#page-map.active-page) .map-note { z-index: 1000; }

/* The field and mast-dot canvases live in their own Leaflet panes, so the map
   pane carries them along during a pan and Leaflet's zoom animation scales them
   together with the tiles. Positions and sizes are inline styles written in
   layer coordinates, so no width/height belongs here.
   The heat pane deliberately uses normal compositing: a fan at 100% opacity has
   to be able to cover the basemap completely. */
.map-viewport .leaflet-heat-pane { mix-blend-mode: normal; }
.map-viewport .leaflet-mask-pane { mix-blend-mode: normal; }
.map-viewport .leaflet-outline-pane { mix-blend-mode: normal; }
.map-viewport .leaflet-heat-pane,
.map-viewport .leaflet-outline-pane,
.map-viewport .leaflet-dot-pane,
.map-viewport .leaflet-mask-pane { pointer-events: none; }
.map-viewport .leaflet-heat-pane canvas,
.map-viewport .leaflet-outline-pane canvas,
.map-viewport .leaflet-dot-pane canvas { display: block; }
/* Leaflet gives its own animated layers this hint (`svg.leaflet-zoom-animated`
   in leaflet.css, and the tile containers on the next line), because a layer
   that is transformed without it gets re-rasterised on the main thread for
   every frame of a zoom. The field is cheap enough to survive that; the mast
   dots and the fan outlines are drawn at display density and were not, which is
   why they visibly trailed the map while the smooth red field did not. Keeping
   the two canvases on their own compositor layers makes the zoom a texture
   scale instead of a raster job, so all three move together. */
.map-viewport .leaflet-heat-pane canvas,
.map-viewport .leaflet-outline-pane canvas,
.map-viewport .leaflet-dot-pane canvas { will-change: transform; }

/* --- Map overlays ------------------------------------------------------- */
/* Attribution and the loading state, as a line of text rather than a box: no
   border, no ground, nothing that reads as another panel. It sits under the
   zoom stack so the two never meet, and takes no pointer events. */
.map-footer {
  position: absolute;
  left: 18px;
  /* The note owns the bottom edge, so the footer sits directly above it, on the
     same measured offset the zoom stack uses. */
  bottom: var(--note-offset);
  z-index: 900;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  max-width: calc(100% - 420px);
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: #a9c3c0;
  font: 9px var(--font-mono);
  text-shadow: 0 1px 3px rgba(0, 0, 0, .85);
  pointer-events: none;
}
.zoom-controls { position: absolute; right: 372px; bottom: 18px; display: grid; border: 1px solid rgba(119, 163, 163, .25); border-radius: 5px; overflow: hidden; }
.zoom-controls button { width: 32px; height: 30px; min-height: 0; border: 0; border-bottom: 1px solid rgba(119, 163, 163, .2); color: #b6cfca; background: rgba(8, 27, 37, .78); backdrop-filter: blur(10px); }
.zoom-controls button:last-child { border: 0; font-size: 13px; }
.zoom-controls button:hover { color: var(--teal); background: #163846; }

/* One dismissible note. The legend strip and the disclaimer used to be two
   stacked boxes in the same corner. */
.map-note {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  max-width: 620px;
  margin: 0;
  padding: 11px 38px 11px 12px;
  border: 1px solid rgba(91, 135, 132, .28);
  border-radius: 6px;
  color: #a2b8b6;
  background: rgba(7, 21, 30, .75);
  backdrop-filter: blur(10px);
  font-size: 11px;
  line-height: 1.55;
}
.map-note strong { color: #c2d6d1; font-weight: 600; }
.map-note button:not(.map-note-close) { min-height: 0; padding: 0; border: 0; color: var(--teal); background: transparent; font-size: 11px; }
.map-note-close { position: absolute; top: 6px; right: 6px; width: 22px; height: 22px; min-height: 0; display: grid; place-items: center; border: 1px solid #35525b; border-radius: 50%; color: #9db3b1; background: transparent; font-size: 13px; line-height: 1; }
.map-note-close:hover { color: #ffffff; border-color: var(--teal); }
.map-note[hidden] { display: none; }
.info-icon { width: 16px; height: 16px; flex: 0 0 auto; display: grid; place-items: center; min-height: 0; padding: 0; border: 1px solid #507276; border-radius: 50%; color: #82a29d; background: transparent; cursor: help; font: 10px serif; line-height: 1; }
button.info-icon:hover { color: #d6efe7; border-color: #6ba294; }
button.info-icon[aria-expanded="true"] { color: #07151e; border-color: var(--teal); background: var(--teal); }
body:has(#page-map.active-page) .map-note { position: absolute; left: 18px; bottom: 18px; }

/* --- Narrow-layout chrome ----------------------------------------------- */
/* The menu button, the settings button and the sheet's close button exist only
   where the wide layout cannot fit: the nav is a horizontal row above 900 px and
   the panel is a permanent column, so all three are switched off here and turned
   on inside the narrow media query below. */
.nav-toggle, .sheet-toggle, .sheet-close { display: none; }
.nav-toggle { height: 38px; align-items: center; gap: 8px; padding: 0 12px; border: 1px solid #2b4e56; border-radius: 6px; color: #b6cfca; background: #102c37; font-size: 11px; }
.nav-toggle[aria-expanded="true"] { color: var(--ink); border-color: #598b82; background: #16414b; }
.nav-toggle-bars { width: 16px; height: 9px; border-top: 2px solid currentColor; border-bottom: 2px solid currentColor; }
.sheet-toggle { height: 38px; align-items: center; gap: 7px; padding: 0 12px; border: 1px solid rgba(119, 163, 163, .3); border-radius: 6px; color: #b6cfca; background: rgba(8, 27, 37, .8); backdrop-filter: blur(10px); font-size: 11px; }
.sheet-toggle[aria-expanded="true"] { color: var(--ink); border-color: #6ba294; }
.sheet-header { display: none; }
.sheet-close { width: 28px; height: 28px; min-height: 0; place-items: center; border: 1px solid #35525b; border-radius: 6px; color: #9db3b1; background: transparent; font-size: 15px; line-height: 1; }
.sheet-close:hover { color: #ffffff; border-color: var(--teal); }
.sheet-title { color: #c2d6d1; font-size: 12px; font-weight: 600; }
.sheet-grip { width: 34px; height: 4px; border-radius: 3px; background: #34555c; }

/* --- Control panel ------------------------------------------------------ */
.control-panel {
  position: absolute;
  top: 18px;
  right: 20px;
  width: 344px;
  /* A height cap expressed as a percentage max-height is what keeps the bottom
     gap: against this containing block a `top`+`bottom` pair is ignored and the
     content height wins, which is what let the panel run off the screen.
     `border-box` is what makes the cap mean the whole panel. Without it
     `max-height` bounds the content box only, so the padding and border still
     pushed the panel's bottom edge 6 px past the viewport on a 768 px-tall
     window — a rounded corner sliced off by the screen edge. */
  box-sizing: border-box;
  max-height: calc(100% - 38px);
  overflow-y: auto;
  overscroll-behavior: contain;
  border: 1px solid #466a6b;
  border-radius: 9px;
  background: rgba(11, 37, 49, .78);
  backdrop-filter: blur(14px);
  box-shadow: 0 16px 50px rgba(0, 0, 0, .45);
}
/* One row per setting, and nothing else. The panel used to be five headed
   sections with a paragraph under each; the paragraphs moved behind the info
   buttons and the headings went away, which is what lets the whole thing fit a
   desktop without scrolling. Each row is `title + tooltip` on the left and the
   control on the right. */
.control-panel { padding: 10px 14px; }
.setting-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 5px 10px; margin: 0 0 4px; }
.setting-row:last-child { margin-bottom: 0; }
/* A row of chips under its title, a title with its control beside it, and a
   title with everything on one line. */
.setting-row.stacked { grid-template-columns: minmax(0, 1fr); }
.setting-row.inline { grid-template-columns: auto minmax(0, 1fr); }
/* A tip or a two-ended slider is as wide as the row, never a column in it. */
.setting-row .info-tip, .setting-row .dual-range { grid-column: 1 / -1; }
.row-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.eyebrow { color: #799b99; font-family: var(--font-mono); font-size: 9px; letter-spacing: 1.5px; }
.control-label { display: flex; align-items: center; gap: 6px; min-width: 0; }
.control-label strong { color: #c3d3cf; font-size: 11px; font-weight: 600; }

/* Setting explanations live behind the info button on each section title, so
   the panel only shows controls. The tip opens in place rather than floating:
   the panel scrolls, and a floating box would be clipped by it. */
.info-tip { margin: 0 0 12px; padding: 10px 11px; border: 1px solid #2f5b62; border-left: 3px solid var(--teal); border-radius: 5px; color: #a9c3c0; background: #0d2833; font-size: 10px; line-height: 1.6; }
.info-tip[hidden] { display: none; }
.info-tip p { margin: 0 0 8px; }
.info-tip strong, .info-tip em { color: #d3e8e1; }
.tip-link { min-height: 0; padding: 0; border: 0; color: var(--teal); background: transparent; font: 9px var(--font-mono); text-decoration: underline; }
.tip-link:hover { color: #9fe6cd; }
.model-mix, .scale-readout { grid-column: 1 / -1; margin: 6px 0 0; color: #7d9498; font: 9px var(--font-mono); line-height: 1.5; }
.text-button { min-height: 0; padding: 0; border: 0; color: var(--teal); background: transparent; font-size: 11px; }

.network-options { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.network-toggle { position: relative; display: flex; flex-direction: column; align-items: stretch; gap: 3px; min-height: 40px; padding: 5px 7px; border: 1px solid #24444d; border-radius: 6px; color: #8fa6a7; background: #102b37; text-align: left; }
/* The selected state has to be readable at a glance, not hunted for: a tile that
   is on lifts its border and background to full strength, and one that is off is
   visibly stood down — dimmed, with its colour bar greyed. The 8 px tick that
   used to be the only marker becomes a filled badge, and the tile's own border
   carries the same message for anyone who cannot pick out a glyph. */
.network-toggle:hover { border-color: #3f6b75; background: #123039; }
.network-toggle[aria-pressed="false"] { opacity: .5; }
.network-toggle[aria-pressed="false"] .network-color { filter: grayscale(1); }
.network-toggle.active { border-color: var(--teal); background: #143a44; box-shadow: 0 0 0 1px rgba(94, 214, 180, .45), 0 0 12px rgba(94, 214, 180, .18); }
.network-toggle.active strong { color: #effcf7; }
.network-toggle.active small { color: #8fbdb5; }
.network-toggle > span:nth-child(2) { display: grid; gap: 2px; width: 100%; min-width: 0; }
.network-toggle strong { color: #d1dfda; font-size: 10px; font-weight: 600; }
.network-toggle small { color: #6c898d; font: 8px var(--font-mono); line-height: 1.4; }
.network-toggle i { position: absolute; top: 4px; right: 4px; display: none; place-items: center; width: 15px; height: 15px; border-radius: 50%; color: #06202a; background: var(--teal); box-shadow: 0 0 0 2px #0b2531; font-style: normal; font-size: 10px; font-weight: 700; line-height: 1; }
.network-toggle.active i { display: grid; }
.network-color { width: 100%; height: 4px; border-radius: 3px; }
/* The three technology chips are a labelled categorical legend — which
   generation — not a magnitude ramp, so they keep their own hues and only stop
   duplicating the field's red as a literal. */
.fiveg { background: var(--red); box-shadow: 0 0 10px var(--red-glow); }
.fourg { background: #ff8b3d; }
.threeg { background: var(--yellow); }

.filter-group { margin-top: 13px; }
.filter-group > small { display: block; margin-bottom: 7px; color: #607c82; font: 8px var(--font-mono); }
.filter-options { display: flex; flex-wrap: wrap; gap: 5px; }
/* The operator and power-class chips announce their state the way the technology
   tiles do, and for the same reason: which filters are on is the first thing to
   read in this panel, and it must not depend on picking out a glyph or a hue. A
   chip that is on lifts its border, its background and its label to full strength
   and carries a filled badge; a chip that is off is dimmed, with its colour dot or
   swatch greyed. The border carries the message on its own, so it survives
   colour-vision deficiency, and the hue is left to mean magnitude rather than
   selection. */
.filter-toggle { position: relative; display: inline-flex; align-items: center; gap: 4px; min-height: 0; padding: 4px 7px; border: 1px solid #24444d; border-radius: 5px; color: #7a979c; background: #102b37; font: 9px var(--font-mono); }
.filter-toggle:hover { border-color: #3f6b75; color: #cfe6e0; background: #123039; }
.filter-toggle[aria-pressed="false"] { opacity: .55; }
.filter-toggle[aria-pressed="false"] .provider-dot,
.filter-toggle[aria-pressed="false"] .power-swatch { filter: grayscale(1); }
.filter-toggle.active { border-color: var(--teal); color: #eafaf3; background: #153c40; box-shadow: 0 0 0 1px rgba(94, 214, 180, .4), 0 0 10px rgba(94, 214, 180, .16); }
/* The tick badges are gone: a chip that is off is dimmed with its colour marker
   greyed, which reads at a glance and leaves the button label clean. */
.power-swatch { width: 8px; height: 8px; flex: 0 0 auto; display: inline-block; border: 1px solid #0a202a; border-radius: 2px; }
.power-swatch.high { background: #ff8870; }
.power-swatch.medium { background: #ffb15d; }
.power-swatch.low { background: #f0d05e; }
.power-swatch.very-low { background: #a4c6a7; }

.setting-row select { max-width: 190px; padding: 5px 6px; border: 1px solid #2a4b53; border-radius: 5px; color: #b7c9c4; background: #102c37; outline: 0; font-size: 10px; }
.setting-row output { color: #b6d8c8; font: 10px var(--font-mono); white-space: nowrap; }
/* Sliders get their own line: the value reads on the title line, the track runs
   the full width beneath it. */
/* A slider keeps its title on the same line, because the track only needs a
   couple of hundred pixels to be usable and a row saved is a row that fits. */
.setting-row.slider-row { grid-template-columns: auto minmax(0, 1fr) auto; }
.setting-row.slider-row > .control-label { grid-column: 1; }
.setting-row.slider-row > input[type="range"] { grid-column: 2; width: 100%; margin: 0; accent-color: var(--teal); }
.setting-row.slider-row > output { grid-column: 3; }
.setting-row.slider-row > .info-tip { grid-column: 1 / -1; }
.panel-help { margin: 8px 0 0; color: #83999c; font-size: 10px; line-height: 1.5; }

.dual-range { grid-column: 1 / -1; position: relative; height: 28px; }
.dual-range-track { position: absolute; left: 0; right: 0; top: 12px; height: 4px; border-radius: 3px; background: #1d4550; }
.dual-range-track i { position: absolute; top: 0; bottom: 0; border-radius: 3px; background: var(--field); }
.dual-range input[type="range"] { position: absolute; left: 0; top: 7px; width: 100%; height: 14px; min-height: 0; margin: 0; padding: 0; border: 0; background: transparent; -webkit-appearance: none; appearance: none; pointer-events: none; }
.dual-range input[type="range"]::-webkit-slider-runnable-track { height: 4px; border: 0; background: transparent; }
.dual-range input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 16px; height: 16px; margin-top: -6px; border: 2px solid #0b2531; border-radius: 50%; background: var(--accent); pointer-events: auto; cursor: grab; }
.dual-range input[type="range"]::-moz-range-track { height: 4px; border: 0; background: transparent; }
.dual-range input[type="range"]::-moz-range-thumb { width: 12px; height: 12px; border: 2px solid #0b2531; border-radius: 50%; background: var(--accent); pointer-events: auto; cursor: grab; }
.dual-range input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px rgba(245, 216, 91, .8); }
.dual-range input[type="range"]:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px rgba(245, 216, 91, .8); }
/* The gradient switch sits to the left of the opacity band and the band takes
   whatever is left of the row. */
.opacity-row { grid-column: 1 / -1; display: flex; align-items: center; gap: 9px; }
.opacity-row .dual-range { flex: 1 1 auto; min-width: 0; }
.opacity-switch { flex: 0 0 auto; gap: 6px; }
.switch-label { color: #8fa9a4; font: 10px var(--font-mono); white-space: nowrap; }
/* With the gradient off the row is a single-value control: the minimum handle
   is hidden outright, so what is on screen is one thumb on one track — the flat
   opacity — rather than a two-handle range with one end that does nothing. */
.dual-range.gradient-off #fan-opacity-min { display: none; }
.dual-range.gradient-off #fan-opacity-min::-webkit-slider-thumb { cursor: default; }
.dual-range.gradient-off #fan-opacity-min::-moz-range-thumb { cursor: default; }

/* A single lobe drawn with the real sprite, the real alpha ramp and the real
   clamp, over a light ground matching the basemap it sits on. */
.fan-preview { margin: 0 0 6px; }
.fan-preview canvas { display: block; width: 100%; height: 30px; border: 1px solid #2a4b53; border-radius: 5px; }
.fan-preview-scale { display: flex; justify-content: space-between; margin: 4px 0 0; color: #6d898d; font: 8px var(--font-mono); }

.switch-control { position: relative; display: inline-flex; align-items: center; }
.switch-control input { position: absolute; left: 0; top: 50%; width: 32px; height: 18px; min-height: 0; margin: 0; transform: translateY(-50%); opacity: 0; cursor: pointer; }
.switch { position: relative; width: 32px; height: 17px; flex: 0 0 auto; border-radius: 17px; background: #28454c; cursor: pointer; }
.switch:after { content: ''; position: absolute; width: 13px; height: 13px; left: 2px; top: 2px; border-radius: 50%; background: #789196; transition: .2s; }
.switch-control input:checked + .switch { background: #377b6c; }
.switch-control input:checked + .switch:after { transform: translateX(15px); background: #d5eee3; }
.switch-control input:focus-visible + .switch { outline: 3px solid #f5d85b; outline-offset: 3px; }

.unit-tabs { display: flex; gap: 5px; margin: 0; }
.unit-tab { flex: 1; min-height: 0; padding: 5px 3px; border: 1px solid #294b53; border-radius: 5px; color: #759196; background: #102c37; font: 9px var(--font-mono); }
.unit-tab:hover, .unit-tab.active { border-color: #629a87; color: #c0e2d4; background: #153c40; }

/* These are the marker colours the canvas paints (providerFor() in app.js), so
   the operator filters double as the map's legend instead of a second, separate
   key that could drift out of step with them. */
.provider-dot { width: 8px; height: 8px; flex: 0 0 auto; display: inline-block; border: 1px solid #0a202a; border-radius: 50%; }
.provider-dot.swisscom { background: #42a5ff; }
.provider-dot.sunrise { background: #ff745e; }
.provider-dot.salt { background: #b18bff; }
.provider-dot.other { background: #ffbf42; }
.national-stat { display: flex; align-items: baseline; gap: 9px; padding: 8px 0 4px; }
.national-stat strong { color: #d5e7dc; font-size: 24px; font-weight: 500; letter-spacing: -1px; }
.national-stat span { color: #8ba3a5; font-size: 10px; line-height: 1.4; }
.peak-stat { display: flex; justify-content: space-between; gap: 8px; padding: 7px 0 3px; border-top: 1px solid rgba(134, 190, 180, .12); color: #8ba3a5; font: 9px var(--font-mono); }
.peak-stat strong { color: #ff6950; font-weight: 500; }
.facts { display: grid; gap: 2px; margin: 4px 0 2px; }
.loaded-sites { display: block; margin: 0; padding: 0; border: 0; color: #9cd1bd; font: 9px var(--font-mono); }

/* Municipality ranking: one row per municipality, with a bar showing its mean
   against the highest one, so the order is readable without the numbers. */
.control-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 12px; }
.control-row select { max-width: 168px; padding: 6px 7px; border: 1px solid #2a4b53; border-radius: 5px; color: #b7c9c4; background: #102c37; outline: 0; font-size: 10px; }
/* Sits below the switch it belongs to rather than in the section heading, so the
   panel's own controls stay where a reader looks for them. */
.database-link { display: block; margin: 6px 0 0; }
.database-link[aria-expanded="true"] { color: #9fe6cd; }

/* Municipality database: the whole country as a filterable table, over the map
   rather than beside it, so a row can be clicked and seen in place. Opaque
   enough to stay readable over a red field. */
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
.database-panel { position: absolute; z-index: 1200; top: 14px; right: 14px; bottom: 14px; display: flex; flex-direction: column; width: min(880px, calc(100% - 28px)); padding: 14px 16px 12px; border: 1px solid #2f5a5f; border-radius: 8px; background: rgba(7, 22, 29, .97); box-shadow: 0 24px 60px rgba(0, 0, 0, .5); backdrop-filter: blur(6px); }
.database-panel[hidden] { display: none; }
.database-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding-bottom: 10px; border-bottom: 1px solid #23424a; }
.database-head h2 { margin: 2px 0 4px; font-size: 15px; }
.database-head p { margin: 0; color: #8ba3a5; font: 9px var(--font-mono); }
.database-close { display: grid; place-items: center; width: 30px; height: 30px; min-height: 0; padding: 0; border: 1px solid #2f5a5f; border-radius: 5px; color: #cfe1dc; background: #102b37; font-size: 15px; line-height: 1; }
.database-close:hover { border-color: #4d7f75; background: #14333c; }
.database-controls { display: grid; gap: 8px; padding: 10px 0; border-bottom: 1px solid #23424a; }
.database-search input { width: 100%; padding: 9px 10px; border: 1px solid #2a4b53; border-radius: 5px; color: #d6e6e1; background: #0e2731; outline: 0; font-size: 12px; }
.database-search input:focus { border-color: #4d9f8c; }
.database-filters { display: flex; flex-wrap: wrap; gap: 10px; }
.database-filters label { display: grid; gap: 4px; color: #8ba3a5; font: 9px var(--font-mono); text-transform: uppercase; letter-spacing: .06em; }
.database-filters select, .database-filters input { padding: 6px 7px; border: 1px solid #2a4b53; border-radius: 5px; color: #b7c9c4; background: #102c37; outline: 0; font: 11px 'DM Sans', sans-serif; }
.database-filters select#database-sort { min-width: 240px; }
.database-filters input#database-population { width: 110px; }
.database-checks { display: flex; flex-wrap: wrap; gap: 14px; }
.database-check { display: flex; align-items: center; gap: 7px; color: #b8cec9; font-size: 11px; }
.database-check input { width: 15px; height: 15px; accent-color: #4d9f8c; }
.database-table-wrap { flex: 1 1 auto; margin: 6px -4px 0; overflow: auto; }
.database-table { width: 100%; border-collapse: collapse; font-size: 11px; }
.database-table th, .database-table td { padding: 6px 8px; border-bottom: 1px solid rgba(134, 190, 180, .12); text-align: right; white-space: nowrap; }
.database-table thead th { position: sticky; top: 0; z-index: 1; color: #8ba3a5; background: #0b232c; font: 9px var(--font-mono); text-transform: uppercase; letter-spacing: .06em; text-align: right; }
.database-table thead th:first-child, .database-table thead th:nth-child(2), .database-table .cell-left { text-align: left; }
/* Settlement and centre source are text, so they are left-aligned like the two
   leading columns rather than right-aligned with the figures. */
.database-table .cell-left { text-align: left; }
.database-table td.cell-left { color: #93a9a6; }
.database-table th[scope="row"] { text-align: left; font-weight: 400; }
.database-table tbody tr:hover { background: rgba(134, 213, 189, .06); }
.database-table button { min-height: 0; padding: 2px 0; border: 0; color: #9cd1bd; background: none; text-align: left; font-size: 11px; }
.database-table button:hover { color: #d6e6e1; text-decoration: underline; }
.database-table td { color: #a9bdba; font-variant-numeric: tabular-nums; }
/* Sortable headers. The button fills its cell so the whole heading is the
   target, and it keeps the table's own mono uppercase so a sortable header and
   a plain one look identical until you hover. */
.database-table thead th { padding: 0; }
.database-table .database-sort { display: flex; align-items: center; gap: 4px; width: 100%; min-height: 26px; padding: 6px 8px; color: #8ba3a5; background: none; border: 0; font: inherit; text-transform: inherit; letter-spacing: inherit; cursor: pointer; }
.database-table th.cell-right .database-sort { justify-content: flex-end; }
.database-table .database-sort:hover { color: #d6e6e1; text-decoration: none; background: rgba(134, 213, 189, .07); }
.database-table .database-sort.active { color: #9cd1bd; }
.database-table .database-sort-arrow { font-size: 8px; line-height: 1; }
.database-table .database-sort:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.database-empty { color: #8ba3a5; text-align: center !important; font-style: italic; }
.database-note { margin: 8px 0 0; color: #7f9598; font-size: 10px; line-height: 1.5; }

@media (max-width: 900px) {
  .database-panel { top: 8px; right: 8px; bottom: 8px; width: calc(100% - 16px); padding: 12px; }
  .database-filters label { flex: 1 1 140px; }
  .database-table { font-size: 10px; }
  .database-table th, .database-table td { padding: 6px 4px; }
}

/* --- Documentation pages ------------------------------------------------ */
.legal-page { max-width: 1100px; }
.legal-hero { max-width: 720px; padding: 24px 0 44px; }
.legal-hero h1 { margin: 15px 0 18px; color: #d9e8e2; font-size: 46px; line-height: 1.05; letter-spacing: -2.4px; font-weight: 700; }
.legal-hero h1 em { color: var(--teal); font-style: normal; }
.legal-hero p { max-width: 560px; margin: 0; color: #93a9aa; font-size: 14px; line-height: 1.7; }
.long-copy { max-width: 780px; padding-top: 30px; border-top: 1px solid #23424a; }
.long-copy h2 { margin: 28px 0 10px; font-size: 17px; line-height: 1.3; }
.long-copy h2:first-child { margin-top: 0; }
.long-copy p { color: #93a9aa; font-size: 13px; line-height: 1.8; }
.long-copy strong { color: #c8ddd3; }
.long-copy em { color: #b6cbc2; }
.plain-list { margin: 0 0 6px; padding-left: 20px; color: #93a9aa; font-size: 13px; line-height: 1.8; }
.plain-list li { margin-bottom: 6px; }
.long-copy button[data-page] { min-height: 0; padding: 0; border: 0; color: var(--teal); background: transparent; font-size: 13px; }
/* The same link, on a page that has no app to switch sections with: there it is
   a real anchor, and has to look like the button it was. See tools/build-pages.mjs. */
.long-copy .page-link:not(.tip-link):not(.popup-link), .legal-page .page-link:not(.tip-link):not(.popup-link) { min-height: 0; padding: 0; border: 0; color: var(--teal); background: transparent; font-size: 13px; text-decoration: underline; }
.page-link:hover { color: #9fe6cd; }
.formula { margin: 6px 0 14px; padding: 12px 14px; border-left: 3px solid var(--teal); color: #cfe2d9; background: rgba(134, 213, 189, .06); font: 13px var(--font-mono); }
.provenance { margin: 0 0 26px; padding: 12px 14px; border: 1px solid #2c5559; border-radius: 6px; color: #b6d8c8; background: rgba(134, 213, 189, .05); font: 11px var(--font-mono); line-height: 1.7; }
.code { color: #cfe2d9; font: 12px var(--font-mono); }
.callout { margin: 22px 0; padding: 16px 18px; border: 1px solid #3d6a63; border-radius: 7px; background: rgba(134, 213, 189, .06); }
.callout p { margin: 0 0 10px; color: #a9c0b9; font-size: 13px; line-height: 1.75; }
.callout p:last-child { margin-bottom: 0; }
.callout strong { color: #d5e8df; }
.data-table { width: 100%; margin: 14px 0 20px; border-collapse: collapse; font-size: 12px; }
.data-table caption { margin-bottom: 8px; color: #8ba3a5; font: 9px var(--font-mono); text-align: left; letter-spacing: 1px; text-transform: uppercase; }
.data-table th, .data-table td { padding: 9px 10px; border: 1px solid #1f3f48; text-align: left; vertical-align: top; }
.data-table th { color: #c8ddd3; background: #0d2733; font-weight: 600; }
.data-table td { color: #93a9aa; line-height: 1.5; }
.data-table td:first-child, .data-table td:nth-child(2) { font-family: var(--font-mono); color: #cfe2d9; white-space: nowrap; }

/* --- Toast -------------------------------------------------------------- */
.toast { position: fixed; z-index: 40; left: 50%; bottom: 25px; transform: translate(-50%, 20px); padding: 11px 16px; border: 1px solid #38665d; border-radius: 6px; color: #c0ded2; background: #12372f; font-size: 11px; opacity: 0; pointer-events: none; transition: .25s; }
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* --- Narrow layouts ----------------------------------------------------- */
@media (max-width: 900px) {
  /* --- Header navigation becomes a dropdown ------------------------------ */
  .topbar { gap: 10px; padding: 0 14px; }
  .nav-toggle { display: inline-flex; margin-left: auto; }
  .topnav { display: none; }
  .topnav.open {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    overflow: visible;
    padding: 6px 12px 10px;
    border-bottom: 1px solid var(--line);
    background: rgba(7, 21, 30, .97);
    backdrop-filter: blur(16px);
    box-shadow: 0 18px 34px rgba(0, 0, 0, .45);
  }
  .topnav.open .nav-item { padding: 13px 10px; border-bottom: 1px solid rgba(135, 190, 180, .08); border-radius: 0; font-size: 13px; }

  /* --- Map overlays ----------------------------------------------------- */
  body:has(#page-map.active-page) .map-toolbar { left: 14px; right: 14px; top: 14px; }
  body:has(#page-map.active-page) .toolbar-right { display: none; }
  body:has(#page-map.active-page) .search-results { left: 14px; width: calc(100vw - 28px); }
  body:has(#page-map.active-page) .map-footer { left: 14px; max-width: calc(100% - 28px); }
  body:has(#page-map.active-page) .map-note { left: 14px; right: 14px; bottom: 14px; max-width: none; }
  /* The note owns the bottom edge, so the zoom stack and the settings button
     sit in a row above it rather than over it. `--note-offset` is the measured
     height of the note, so the row clears however many lines it wraps to. */
  body:has(#page-map.active-page) .zoom-controls { right: 14px; bottom: var(--note-offset); }
  body:has(#page-map.active-page) .sheet-toggle { position: absolute; left: 14px; bottom: var(--note-offset); z-index: 900; display: inline-flex; }
  .nav-toggle, .sheet-toggle { min-height: 44px; }
  .zoom-controls button { width: 42px; height: 42px; }
  .search-clear { width: 32px; height: 32px; }
  .popup-tabs button { padding: 7px 9px; }
  .map-viewport .leaflet-popup-content-wrapper { max-width: calc(100vw - 30px); }

  /* --- Settings become a bottom sheet ----------------------------------- */
  /* Closed by default: on a phone a sheet that opens with the page would cover
     the map it exists to configure. The button above the note opens it. */
  .control-panel { display: none; }
  body:has(#page-map.active-page) .control-panel.sheet-open {
    display: block;
    left: 0;
    right: 0;
    top: auto;
    bottom: 0;
    width: auto;
    max-height: 74vh;
    border-radius: 14px 14px 0 0;
    border-bottom: 0;
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .control-panel .sheet-header {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-bottom: 1px solid rgba(27, 57, 66, .8);
    background: rgba(11, 37, 49, .97);
    backdrop-filter: blur(10px);
  }
  .control-panel .sheet-close { display: grid; margin-left: auto; }
  body:has(#page-map.active-page) .panel-section { padding: 15px 16px; }

  /* --- Documentation pages --------------------------------------------- */
  .page { padding: 24px 16px 40px; }
  .legal-hero h1 { font-size: 34px; }
  .legal-hero p { font-size: 13px; }
  .data-table { display: block; overflow-x: auto; }
  /* A phone cannot show four numeric columns beside a label at the desktop
     density. The column with words in it wraps instead of forcing the table
     wider, and the cell padding comes in, so the numbers keep their columns and
     as little as possible is off screen — the two-column tables on the levels
     page stop needing to scroll at all. The columns that hold a single value
     keep `nowrap`, so a figure never breaks across two lines. */
  .data-table th, .data-table td { padding: 5px 5px; }
  .data-table td:first-child { white-space: normal; }
  .data-table caption { letter-spacing: .5px; }

  /* --- Popups ----------------------------------------------------------- */
  .map-viewport .leaflet-popup-content { max-height: 62vh; }
  .popup-panel { max-height: 34vh; }
}
