/* ============================================================
   yeip — Earthy Field Operations Design System
   Yemen Environmental Intelligence Platform
   ============================================================ */

/* ── Reset ─────────────────────────────────────────────────── */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* ── Design tokens ──────────────────────────────────────────── */
:root {
  /* Fixed brand palette — same hex values in both themes. What changes
     between light/dark is which of these each semantic role below maps
     to, not the palette itself. */
  --forest:       #3e6675;
  --forest-dk:    #26364b;
  --forest-md:    #3e6675;
  --forest-lt:    #4a6150;

  --sage:         #f2fff5;
  --sage-lt:      #b0c9b4;
  --sage-dim:     #99ae9e;

  --cream:        #faf9f6;
  --parchment:    #34625d;
  --parchment-lt: #659791;
  --tan:          #d6cfc0;
  --tan-lt:       #e8e3d8;

  /* Status colors — the saturated hue stays constant across themes;
     only the pale/tinted "-bg" backing changes (pale tint on light,
     translucent tint on dark) so it's always readable against the
     current card surface. */
  --critical:     #c0392b;
  --critical-border: #e8a09a;
  --severe:       #cd853f;
  --severe-border:#e8c49a;
  --moderate:     #b8a832;
  --moderate-border: #ddd098;
  --normal:       #4a7c59;
  --normal-border:#a0c8aa;

  --text-dark:    #202220;
  --text-on-dark: #ffffff;

  /* Typography — same family as the landing pages (static/css/landing.css)
     so the app shell reads as a continuation of the marketing site rather
     than a different product. */
  --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;

  /* Layout */
  --topbar-h:   64px;
  --sidebar-w:  200px;
  --breadcrumb-h: 32px;
  --statsbar-h:  62px;
  --right-panel-w: 280px;

  /* Same radius scale as landing.css */
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;

  /* ============================================================
     Theme-reactive semantic tokens — LIGHT (default). Everything
     above this point is a fixed brand color; everything below is a
     *role* ("chrome surface", "card text") that light/dark map onto
     different brand colors. Toggled via [data-theme] on <html>
     (see static/js/theme.js); light is the default so a first-time
     visitor's dashboard matches the always-light landing pages.
     ============================================================ */
  --canvas-bg:          var(--cream);   /* map/main-panel background */

  /* Sidebar, topbar, stats bar, right panel — the app's "chrome".
     Was #f2efe6 with a --tan (#d6cfc0) border and a topbar-bg that
     resolved to within 1.0 contrast of --canvas-bg (i.e. visually
     identical) — the whole shell read as one flat off-white surface
     with no visible seams. Darkened chrome-bg a step and gave it a
     genuinely darker border so panel edges are actually visible;
     topbar-bg now tracks chrome-bg (same tan family) instead of
     nearly matching the cream canvas behind it. */
  --chrome-bg:          #faf9f6;
  --topbar-bg:          #faf9f6;
  --topbar-shadow:      0 2px 16px rgba(0, 0, 0, 0.08);
  --chrome-border:      #9c8d6e;
  --chrome-text:        var(--forest-dk);
  --chrome-text-muted:  #5f6963;
  --chrome-text-faint:  #6d7b71;
  --chrome-icon:        var(--forest);
  --chrome-hover:       rgba(38, 54, 75, 0.09);
  --chrome-active-bg:   var(--forest-dk);
  --chrome-active-text: var(--text-on-dark);
  --chrome-active-icon: var(--text-on-dark);
  --chrome-scrollbar:   var(--tan);
  --chrome-input-bg:    #ffffff;
  --chrome-input-border:#9c8d6e;

  /* Floating cards over the map canvas: legend, trend panel, basemap
     toggle, flash messages. */
  --card-bg:            rgba(255, 255, 255, 0.93);
  --card-border:        var(--tan);
  --card-text:          var(--text-dark);
  --card-text-muted:    #5f6963;
  --card-text-faint:    #66756b;

  --badge-neutral-bg:   var(--tan-lt);
  --badge-neutral-text: #4a4a3d;

  --nav-badge-bg:       #fbe0e0;
  --nav-badge-text:     #b23b3b;

  --critical-bg:  #fdf1f0;
  --severe-bg:    #fdf6ee;
  --moderate-bg:  #e6db92;
  --normal-bg:    #f0f7f1;
}

/* ── Dark theme ─────────────────────────────────────────────── */
:root[data-theme="dark"] {
  --canvas-bg:          #161c24;

  --chrome-bg:          var(--forest-dk);
  /* Was rgba(19,26,35,...) — a near-black navy that didn't match
     chrome-bg's forest-dk at all, so the header read as flat black
     instead of the same "chrome" tone as the sidebar/right panel. */
  --topbar-bg:          rgba(38, 54, 75, 0.88);
  --topbar-shadow:      0 2px 16px rgba(0, 0, 0, 0.35);
  --chrome-border:      rgba(255, 255, 255, 0.08);
  --chrome-text:        var(--sage);
  --chrome-text-muted:  var(--sage-dim);
  --chrome-text-faint:  var(--sage-dim);
  --chrome-icon:        var(--sage);
  --chrome-hover:       var(--forest-md);
  --chrome-active-bg:   var(--forest-md);
  --chrome-active-text: #ffffff;
  --chrome-active-icon: var(--sage-lt);
  --chrome-scrollbar:   var(--forest-lt);
  --chrome-input-bg:    var(--forest-dk);
  --chrome-input-border:var(--forest-lt);

  --card-bg:            rgba(28, 36, 46, 0.94);
  --card-border:        rgba(255, 255, 255, 0.1);
  --card-text:          #e8ece9;
  --card-text-muted:    #9bb0a8;
  --card-text-faint:    #7f9690;

  --badge-neutral-bg:   rgba(255, 255, 255, 0.08);
  --badge-neutral-text: var(--sage-lt);

  --nav-badge-bg:       rgba(192, 57, 43, 0.22);
  --nav-badge-text:     #ff9d90;

  --critical-bg:  rgba(192, 57, 43, 0.18);
  --severe-bg:    rgba(205, 133, 63, 0.18);
  --moderate-bg:  rgba(184, 168, 50, 0.18);
  --normal-bg:    rgba(74, 124, 89, 0.2);
}

/* ── Base ───────────────────────────────────────────────────── */
html, body {
  height: 100%;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--card-text);
  background: var(--canvas-bg);
  -webkit-font-smoothing: antialiased;
}

body.lang-ar { font-family: 'Tajawal', var(--font-sans); }

a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; border: none; background: none; }
select { font-family: inherit; }
input, select { outline: none; }

/* ── App shell ──────────────────────────────────────────────── */
.yeip-shell {
  display: flex;
  flex-direction: column;
  /* 100vh on mobile browsers is sized as if the address bar were
     already hidden — the *largest* the viewport ever gets, not what's
     actually visible right now. With the address bar on screen (the
     normal state, not just a transient scroll-away collapse), a
     100vh-tall shell is taller than the real visible area, and since
     overflow is clipped below, the bottom of the page (map controls,
     a legend, whatever's lowest) ends up hidden instead of reachable.
     100dvh ("dynamic viewport height") tracks the actual visible area
     as browser chrome shows/hides; 100vh stays as the fallback for the
     handful of older/non-mobile browsers that don't support dvh yet. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

/* ── Page transition overlay ───────────────────────────────────
   Same visual language as .l-transition-overlay on the landing pages
   (static/css/landing.css) — the brand mark fading/scaling over the
   same forest-dk wash — so leaving the landing page and arriving here
   reads as one continuous motion instead of two unrelated page loads.
   Deliberately NOT theme-reactive — it's a one-shot brand moment, not
   part of the app chrome the user lives in. */
.page-transition-overlay {
  position: fixed;
  inset: 0;
  z-index: 10000;
  background: var(--forest-dk);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.45s ease, visibility 0.45s ease;
}

.page-transition-overlay.is-active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.page-transition-mark {
  width: 60px;
  height: 60px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
  transform: scale(0.7);
  transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.page-transition-overlay.is-active .page-transition-mark { transform: scale(1); }

.page-transition-mark img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ── Topbar ─────────────────────────────────────────────────────
   Matches the landing pages' .l-nav (static/css/landing.css) in light
   mode — same frosted-glass background, brand lockup, and button
   shapes — so arriving here from the marketing site doesn't feel like
   a jump to a different product. In dark mode it becomes a dark
   frosted variant of the same shape so the whole shell (topbar +
   sidebar + stats bar + right panel) reads as one coherent theme
   rather than a light header stuck above a dark app. */
.topbar {
  height: var(--topbar-h);
  background: var(--topbar-bg);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  display: flex;
  align-items: center;
  padding: 0 20px;
  gap: 16px;
  flex-shrink: 0;
  border-bottom: 1px solid var(--chrome-border);
  box-shadow: var(--topbar-shadow);
  transition: background 0.2s ease, border-color 0.2s ease;
}

.topbar-logo {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Two <img> tags, one per theme (see templates/base.html) — pure CSS
   display toggle so the correct logo shows from first paint with no
   JS needed and no flash of the wrong mark. */
.topbar-logo-img-dark { display: none; }
:root[data-theme="dark"] .topbar-logo-img-light { display: none; }
:root[data-theme="dark"] .topbar-logo-img-dark { display: block; }

.topbar-name {
  font-size: 15px;
  font-weight: 800;
  color: var(--chrome-text);
  letter-spacing: 0.2px;
  white-space: nowrap;
}

/* Purely decorative in the old dark topbar; the light nav doesn't need
   it (matches the landing nav, which has no such divider either). */
.topbar-divider { display: none; }

.topbar-spacer { flex: 1; }

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.topbar-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 14px;
  border-radius: var(--radius-md);
  background: transparent;
  border: 1.5px solid var(--chrome-border);
  color: var(--chrome-text);
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.topbar-btn i { font-size: 15px; }
.topbar-btn:hover { border-color: var(--chrome-icon); background: var(--chrome-hover); }

.topbar-theme-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-md);
  background: transparent;
  border: 1.5px solid var(--chrome-border);
  color: var(--chrome-text);
  font-size: 16px;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.topbar-theme-toggle:hover { border-color: var(--chrome-icon); background: var(--chrome-hover); }

/* Only one of these is ever visible at a time (see static/js/theme.js) */
.topbar-theme-toggle .ti-sun { display: none; }
:root[data-theme="dark"] .topbar-theme-toggle .ti-moon { display: none; }
:root[data-theme="dark"] .topbar-theme-toggle .ti-sun { display: block; }

.topbar-lang-switch {
  display: flex;
  border: 1px solid var(--chrome-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.topbar-lang-switch a {
  padding: 7px 11px;
  font-size: 11px;
  font-weight: 600;
  color: var(--chrome-text-muted);
  transition: background 0.15s ease, color 0.15s ease;
}

.topbar-lang-switch a.active { background: var(--chrome-active-bg); color: var(--chrome-active-text); }
.topbar-lang-switch a:not(.active):hover { background: var(--chrome-hover); color: var(--chrome-text); }

/* ── Body row ───────────────────────────────────────────────── */
.body-row {
  display: flex;
  flex: 1;
  overflow: hidden;
}

/* ── Sidebar ────────────────────────────────────────────────── */
.sidebar {
  width: var(--sidebar-w);
  background: var(--chrome-bg);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  border-inline-end: 1px solid var(--chrome-border);
  /* The module list (first .sidebar-section) stays fixed; only the
     description section below it scrolls — see .sidebar-description-section.
     Was overflow-y:auto here, scrolling the whole sidebar (nav included)
     as one unit. */
  overflow: hidden;
  transition: background 0.2s ease, border-color 0.2s ease;
}

.sidebar-section { padding: 14px 10px 8px; flex-shrink: 0; }

/* Module Description — the only part of the sidebar that scrolls.
   min-height: 0 is required for a flex child to actually shrink and
   scroll instead of overflowing its flex parent. */
.sidebar-description-section {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.sidebar-description-section::-webkit-scrollbar { width: 4px; }
.sidebar-description-section::-webkit-scrollbar-track { background: transparent; }
.sidebar-description-section::-webkit-scrollbar-thumb { background: var(--chrome-scrollbar); border-radius: 4px; }

.sidebar-label {
  font-size: 9px;
  color: var(--chrome-text-faint);
  letter-spacing: 1.2px;
  text-transform: uppercase;
  padding: 0 6px;
  margin-bottom: 6px;
}

.sidebar_subtitle {
  font-size: 8px;
  color: var(--chrome-text);
  text-transform: uppercase;
  letter-spacing: 0.8px;
  padding: 0 6px;
  margin-bottom: 4px;
  margin-top: 10px;
}

/* Nav items */
.nav-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 10px;
  border-radius: var(--radius-md);
  margin-bottom: 2px;
  cursor: pointer;
  transition: background 0.12s;
  text-decoration: none;
  border-inline-start: 2px solid transparent;
}

.nav-item:hover { background: var(--chrome-hover); }

.nav-item.active {
  background: var(--chrome-active-bg);
  border-inline-start-color: var(--chrome-active-icon);
  padding-inline-start: 8px;
  border-radius: var(--radius-md);
}

.nav-item i {
  font-size: 16px;
  color: var(--chrome-icon);
  flex-shrink: 0;
}

.nav-item.active i { color: var(--chrome-active-icon); }


.nav-item.disabled {
  pointer-events: none;
  opacity: 0.5;
  cursor: not-allowed;
}

.nav-text {
  font-size: 11px;
  color: var(--chrome-text);
  flex: 1;
}

.nav-item.active .nav-text { color: var(--chrome-active-text); }

.nav-badge {
  font-size: 9px;
  font-weight: 500;
  background: var(--nav-badge-bg);
  color: var(--nav-badge-text);
  padding: 1px 5px;
  border-radius: 8px;
}

.sidebar-divider {
    height: 1px;
    background: var(--chrome-border);
    margin-top: 12px;
}

/* Layer toggles */
.layer-item {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px;
  font-size: 10px;
  color: var(--chrome-text);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: background 0.1s;
}

.layer-item:hover { background: var(--chrome-hover); }
.layer-item input[type=checkbox] { accent-color: var(--chrome-icon); width: 11px; height: 11px; }
.layer-item.disabled { color: var(--chrome-text-faint); opacity: 0.5; }

/* Sidebar filters */
.sidebar-filter { padding: 0 10px 10px; }

.filter-label {
  font-size: 9px;
  color: var(--chrome-text-faint);
  letter-spacing: 0.8px;
  text-transform: uppercase;
  margin-bottom: 5px;
  display: block;
}

.filter-select {
  width: 100%;
  background: var(--chrome-input-bg);
  border: 0.5px solid var(--chrome-input-border);
  border-radius: var(--radius-sm);
  color: var(--chrome-text);
  font-size: 10px;
  padding: 5px 8px;
  appearance: none;
  cursor: pointer;
  transition: border-color 0.1s;
}

.filter-select:hover { border-color: var(--chrome-icon); }
.filter-select:focus { border-color: var(--chrome-icon); }

/* Map layer toggle (e.g. Groundwater's GW Anomaly / GW Trend switcher) */
.layer-toggle {
  display: flex;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 0.5px solid var(--chrome-input-border);
}

.layer-toggle-btn {
  flex: 1;
  padding: 7px 6px;
  font-size: 9.5px;
  line-height: 1.3;
  color: var(--chrome-text);
  background: var(--chrome-input-bg);
  text-align: center;
  border-inline-end: 0.5px solid var(--chrome-input-border);
  transition: background 0.12s, color 0.12s;
}

.layer-toggle-btn:last-child { border-inline-end: none; }
.layer-toggle-btn:hover { background: var(--chrome-hover); }

.layer-toggle-btn.active {
  background: var(--chrome-active-bg);
  color: var(--text-on-dark);
  font-weight: 500;
}

/* Download Data panel's two entry-point buttons (Districts Level /
   Study Area) — each just opens .download-popup-overlay below, so
   these read as cards/options rather than a pressed toggle state like
   .layer-toggle-btn. */
.download-option-buttons {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.btn-download-option {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 12px;
  background: var(--chrome-input-bg);
  border: 0.5px solid var(--chrome-input-border);
  border-radius: var(--radius-md);
  color: var(--chrome-text);
  font-size: 11px;
  font-weight: 500;
  text-align: start;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}

.btn-download-option i { font-size: 16px; color: var(--chrome-icon); flex-shrink: 0; }
.btn-download-option:hover { background: var(--chrome-hover); border-color: var(--chrome-icon); }

/* Download Data popup — same full-screen-scrim + centered-card language
   as .loading-overlay/.loading-card above, reused here (separate class
   names since this is toggled by its own class, not an inline
   display:flex) so opening it doesn't fight the loading indicator for
   the same element. activateStudyAreaDraw() (functions.js) hides this
   mid-draw so the modal doesn't sit on top of the map the user is
   drawing on, then reopens it once the polygon is finished/cancelled. */
.download-popup-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 23, 32, 0.4);
  backdrop-filter: blur(6px);
  z-index: 2000;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.2s ease, visibility 0.2s ease;
}

.download-popup-overlay.is-visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.download-popup-card {
  width: 300px;
  max-width: calc(100vw - 32px);
  max-height: calc(100vh - 64px);
  overflow-y: auto;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
  padding: 16px;
  transform: scale(0.96);
  transition: transform 0.2s ease;
}

.download-popup-overlay.is-visible .download-popup-card {
  transform: scale(1);
}

.download-popup-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--card-border);
}

.download-popup-title {
  font-size: 11px;
  font-weight: 600;
  color: var(--card-text);
  text-transform: uppercase;
  letter-spacing: 0.6px;
}

.download-popup-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  background: transparent;
  color: var(--card-text-muted);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}

.download-popup-close:hover { background: var(--chrome-hover); color: var(--card-text); }

/* Groups related fields inside the download popup (e.g. Study Area's
   "Define Area" / "Date Range" / "Dataset") — was previously one flat
   stack of controls with no visual grouping at all. */
.popup-subsection { margin-bottom: 14px; }
.popup-subsection:last-of-type { margin-bottom: 0; }

.popup-subsection-label {
  font-size: 9px;
  font-weight: 600;
  color: var(--card-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.7px;
  margin-bottom: 7px;
}

/* From/To month side by side instead of two full-width stacked rows. */
.popup-two-col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.popup-two-col .sidebar-filter { padding: 0; }

/* Draw-on-map / upload-shapefile as two equal-width compact options
   side by side (the "OR" between them), rather than a full-width
   .btn-primary stacked directly above a bare native file input. */
.study-area-input-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.btn-secondary-option {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 12px 8px;
  background: var(--chrome-input-bg);
  border: 0.5px dashed var(--chrome-input-border);
  border-radius: var(--radius-md);
  color: var(--chrome-text);
  font-size: 10px;
  font-weight: 500;
  text-align: center;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}

.btn-secondary-option i { font-size: 17px; color: var(--chrome-icon); }
.btn-secondary-option:hover { background: var(--chrome-hover); border-color: var(--chrome-icon); }
.btn-secondary-option.active { border-style: solid; border-color: var(--chrome-icon); background: var(--chrome-hover); }

/* "All Yemen Land" spans the full width of the Study Area grid (5
   area-definition options total: this one, plus Draw on map/Upload
   shapefile/Governorate/District as a 2x2 below it) — it's the "no
   clipping" option, conceptually distinct from the other 4 boundary-
   based ones, so it reads as its own row rather than sharing a cell.
   Flows icon+label side by side rather than stacked, since a full-width
   button has the room and a stacked layout would look sparse. */
.study-area-full-row {
  grid-column: 1 / -1;
  flex-direction: row;
  padding: 10px 12px;
}

/* Compact status chip under the draw/upload row — only shown once a
   polygon is drawn or a file picked (JS toggles .has-value; empty by
   default so nothing renders here otherwise). */
.study-area-status {
  margin-top: 8px;
  font-size: 10px;
  color: var(--card-text-muted);
}

.study-area-status.has-value {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 9px;
  background: var(--normal-bg);
  color: var(--normal);
  border-radius: var(--radius-sm);
  font-weight: 500;
}

/* Data Availability popup (explorer.html/_ar.html's #availability-popup-
   overlay) — a 3-column table (dataset / start / end), so it gets a wider
   card than the form-style .download-popup-card default (300px, sized for
   stacked filters) rather than wrapping every date onto its own line. */
.availability-popup-card {
  width: 380px;
}

/* Module Details popup (dashboard.html's #module-info-overlay) — real
   prose (Overview/Methodology/Data Sources/How to Use/Limitations), so
   it gets the widest card of this family; still capped and scrollable
   via .download-popup-card's own max-height for a long module writeup
   on a short viewport. */
.module-info-card {
  width: 640px;
}

/* Raised above .loading-overlay (z-index 9999 — the full-dashboard
   scrim shown while a tab switch's map/stats/actions are loading, see
   showLoadingIndicator() in functions.js) rather than sharing
   .download-popup-overlay's default 2000, so a user landing on a module
   for the first time can read its auto-shown popup while the map behind
   it is still loading instead of it being hidden behind the scrim until
   loading finishes. An ID selector so this only affects this one popup,
   not Explorer's download/availability/report popups, which are never
   opened during that full-page loading sequence anyway. */
#module-info-overlay {
  z-index: 10000;
}

/* Contact Us popup form (dashboard.html's #contact-popup-overlay) — the
   first real text-input form inside a .download-popup-card, so these
   are new classes rather than reusing .filter-select (that one's sized
   for a tiny sidebar dropdown at 10px, not a real name/email/message
   form); built on the same --chrome-input-* tokens for visual
   consistency with the rest of the dashboard chrome. */
.contact-form-group { margin-bottom: 14px; }
.contact-form-group:last-of-type { margin-bottom: 0; }

.contact-form-label {
  display: block;
  font-size: 10px;
  font-weight: 600;
  color: var(--card-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.6px;
  margin-bottom: 6px;
}

.contact-form-input,
.contact-form-textarea {
  width: 100%;
  background: var(--chrome-input-bg);
  border: 0.5px solid var(--chrome-input-border);
  border-radius: var(--radius-sm);
  color: var(--card-text);
  font-family: inherit;
  font-size: 12.5px;
  padding: 8px 10px;
  transition: border-color 0.1s;
}

.contact-form-textarea { resize: vertical; min-height: 80px; }

.contact-form-input:hover, .contact-form-textarea:hover,
.contact-form-input:focus, .contact-form-textarea:focus { border-color: var(--chrome-icon); }

.contact-form-input.has-error, .contact-form-textarea.has-error { border-color: var(--critical); }

.contact-form-error {
  display: block;
  font-size: 10.5px;
  color: var(--critical);
  margin-top: 4px;
}

.module-info-body h4 {
  font-size: 10px;
  font-weight: 700;
  color: var(--card-text);
  text-transform: uppercase;
  letter-spacing: 0.6px;
  margin: 16px 0 6px;
}
.module-info-body h4:first-child { margin-top: 0; }

.module-info-body p {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--card-text-muted);
  margin: 0 0 10px;
}
.module-info-body p:last-child { margin-bottom: 0; }

.module-info-body strong { color: var(--card-text); font-weight: 600; }

.module-info-body ul {
  margin: 0 0 10px;
  padding-inline-start: 18px;
}
.module-info-body li {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--card-text-muted);
  margin-bottom: 4px;
}

/* "Don't show this again" footer — a persistent (localStorage) opt-out
   of the auto-shown-on-tab-open popup only; reopening it manually via
   the topbar's "Module Details" button always works regardless (see
   setModuleInfoDontShowAgain() in functions.js). */
.module-info-footer {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 0.5px solid var(--chrome-input-border);
}

.module-info-dont-show {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--card-text-muted);
  cursor: pointer;
  user-select: none;
}

.module-info-dont-show input[type="checkbox"] {
  accent-color: #4c9ffe;
  width: 14px;
  height: 14px;
  cursor: pointer;
}

.availability-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
}

.availability-table th {
  text-align: left;
  font-size: 9px;
  font-weight: 600;
  color: var(--card-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.6px;
  padding: 0 8px 8px;
  border-bottom: 1px solid var(--card-border);
}

.lang-ar .availability-table th {
  text-align: right;
}

.availability-table td {
  padding: 8px;
  color: var(--card-text);
  border-bottom: 1px solid var(--card-border);
  white-space: nowrap;
}

.availability-table tr:last-child td { border-bottom: none; }

.availability-table td:first-child {
  font-weight: 500;
}

.availability-table td.availability-no-data {
  color: var(--card-text-muted);
  font-style: italic;
}

/* Download status toast (bottom-right, dashboard.html — survives module
   switches, see showDownloadToast()/hideDownloadToast() in functions.js).
   data-kind picks the icon color: loading (default, no rule needed —
   .map-spinner-sm already carries its own color), success/error/warning
   reuse the same semantic tokens as the alert badges (.badge-critical
   etc.) elsewhere on the dashboard. */
.download-toast {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 3000;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 320px;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.22);
  padding: 11px 14px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
  pointer-events: none;
}

.download-toast.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
}

.download-toast-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  font-size: 15px;
}

.download-toast[data-kind="success"] .download-toast-icon { color: var(--normal); }
.download-toast[data-kind="error"] .download-toast-icon { color: var(--critical); }
.download-toast[data-kind="warning"] .download-toast-icon { color: var(--severe); }

.download-toast-text {
  flex: 1;
  min-width: 0;
  font-size: 11px;
  font-weight: 500;
  color: var(--card-text);
  line-height: 1.4;
}

.download-toast-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  background: transparent;
  color: var(--card-text-muted);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}

.download-toast-close:hover { background: var(--chrome-hover); color: var(--card-text); }

/* ── Main panel ─────────────────────────────────────────────── */
.main-panel {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--canvas-bg);
}

/* Stats bar */
.statsbar {
  height: var(--statsbar-h);
  background: var(--chrome-bg);
  border-bottom: 1px solid var(--chrome-border);
  display: flex;
  align-items: center;
  padding: 0 16px;
  flex-shrink: 0;
  transition: background 0.2s ease, border-color 0.2s ease;
}

.stat-cell {
  flex: 1;
  padding: 0 16px;
  border-inline-end: 1px solid var(--chrome-border);
}

.stat-cell:first-child { padding-inline-start: 0; }
.stat-cell:last-child  { border-inline-end: none; }

.stat-label {
  font-size: 9px;
  color: var(--chrome-text-faint);
  text-transform: uppercase;
  letter-spacing: 0.7px;
  margin-bottom: 2px;
}

.stat-value {
  font-size: 22px;
  font-weight: 500;
  color: var(--chrome-text);
  line-height: 1.1;
}

.stat-value .stat-unit {
  font-size: 13px;
  font-weight: 400;
  color: var(--chrome-text-muted);
}

.stat-value.is-critical { color: var(--critical); }
.stat-value.is-severe   { color: var(--severe); }
.stat-value.is-moderate { color: var(--moderate); }
.stat-value.is-normal   { color: var(--normal); }

.stat-badge {
  display: inline-block;
  font-size: 8px;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  margin-top: 3px;
}

.badge-critical  { background: var(--critical-bg);  color: var(--critical); }
.badge-severe    { background: var(--severe-bg);    color: var(--severe); }
.badge-moderate  { background: var(--moderate-bg);  color: var(--moderate); }
.badge-normal    { background: var(--normal-bg);    color: var(--normal); }
.badge-neutral   { background: var(--badge-neutral-bg); color: var(--badge-neutral-text); }

/* Content area */
.content-area {
  flex: 1;
  display: flex;
  overflow: hidden;
}

/* ── Map panel ──────────────────────────────────────────────── */
.map-panel {
  flex: 1;
  position: relative;
  background: var(--canvas-bg);
  overflow: hidden;
}

#main-map {
  width: 100%;
  height: 100%;
}

.map-overlay-title {
  display: none !important;
}

/* Basemap toggle (top-right) and the Explorer Districts/Pixels view
   toggle (top-center, see switchDataView() in functions.js) — same
   floating-card language as .map-legend/.map-trend-panel, sharing
   every rule except position: .map-basemap-toggle is kept at a fixed
   corner, .map-view-toggle centered, regardless of language (map
   chrome doesn't mirror for RTL, unlike the sidebar it replaced the
   Districts/Pixels toggle used to live in). */
.map-basemap-toggle,
.map-view-toggle,
.map-gw-reference-toggle,
.map-explorer-toggle {
  position: absolute;
  top: 10px;
  z-index: 400;
  display: flex;
  background: var(--card-bg);
  border: 0.5px solid var(--card-border);
  border-radius: var(--radius-md);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
  overflow: hidden;
}

.map-basemap-toggle { right: 10px; }
.map-view-toggle,
.map-gw-reference-toggle { left: 50%; transform: translateX(-50%); }
.map-explorer-toggle { left: 10px; }

.map-basemap-btn,
.map-view-btn,
.map-gw-ref-btn,
.map-explorer-toggle-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  font-size: 11px;
  font-weight: 500;
  color: var(--card-text);
  background: transparent;
  border-inline-end: 0.5px solid var(--card-border);
  transition: background 0.12s ease, color 0.12s ease;
  white-space: nowrap;
}

.map-basemap-btn:last-child,
.map-view-btn:last-child,
.map-gw-ref-btn:last-child,
.map-explorer-toggle-btn:last-child { border-inline-end: none; }
.map-basemap-btn i,
.map-view-btn i,
.map-gw-ref-btn i,
.map-explorer-toggle-btn i { font-size: 14px; }
.map-basemap-btn:hover,
.map-view-btn:hover,
.map-gw-ref-btn:hover,
.map-explorer-toggle-btn:hover { background: var(--chrome-hover); }

.map-basemap-btn.active,
.map-view-btn.active,
.map-gw-ref-btn.active,
.map-explorer-toggle-btn.active {
  background: var(--forest-dk);
  color: var(--text-on-dark);
}

:root[data-theme="dark"] .map-basemap-btn,
:root[data-theme="dark"] .map-view-btn,
:root[data-theme="dark"] .map-gw-ref-btn,
:root[data-theme="dark"] .map-explorer-toggle-btn { background: var(--forest-dk); }
:root[data-theme="dark"] .map-basemap-btn.active,
:root[data-theme="dark"] .map-view-btn.active,
:root[data-theme="dark"] .map-gw-ref-btn.active,
:root[data-theme="dark"] .map-explorer-toggle-btn.active { background: var(--chrome-active-bg); }

@media (max-width: 900px) {
  .map-basemap-btn span,
  .map-view-btn span,
  .map-gw-ref-btn span,
  .map-explorer-toggle-btn span { display: none; }
}

/* Groundwater Monitor's timeline (top-left, replaces the old Water-
   Stress/GW-Trend layer-switch buttons — see #map-gw-timeline in
   dashboard.html, initGroundwaterTimeline()/switchGroundwaterMonth() in
   functions.js). Same floating-card language as the toggles above, but
   its own block since it mixes buttons with a range input + text label
   rather than being a uniform button group. */
.map-gw-timeline {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 400;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  background: var(--card-bg);
  border: 0.5px solid var(--card-border);
  border-radius: var(--radius-md);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}

.map-gw-timeline-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  color: var(--card-text);
  background: transparent;
  border-radius: 4px;
  transition: background 0.12s ease;
}
.map-gw-timeline-btn:hover { background: var(--chrome-hover); }
.map-gw-timeline-btn i { font-size: 14px; }

.map-gw-timeline-slider {
  width: 140px;
  accent-color: var(--forest-dk);
}

.map-gw-timeline-label {
  min-width: 62px;
  font-size: 11px;
  font-weight: 500;
  color: var(--card-text);
  text-align: center;
  white-space: nowrap;
}

@media (max-width: 900px) {
  .map-gw-timeline-slider { width: 90px; }
}

/* Leaflet overrides */

/* Pushes the default top-left zoom control down below the floating
   top-left layer toggle (.map-gw-timeline in Groundwater Monitor,
   .map-explorer-toggle in Data Explorer) so the two don't visually
   collide — those toggles aren't Leaflet controls, so Leaflet's own
   control-stacking logic doesn't know to make room for them. Applied
   unconditionally (rather than only when one of those toggles is
   visible) since the extra top offset is small and harmless on the
   tabs (drought/agriculture) that have no top-left toggle at all. */
.leaflet-top.leaflet-left {
  top: 56px;
}

.leaflet-control-zoom {
  border: 0.5px solid var(--card-border) !important;
  border-radius: var(--radius-md) !important;
}

.leaflet-control-zoom a {
  background: var(--forest-dk) !important;
  color: var(--sage) !important;
  border-bottom-color: var(--card-border) !important;
  font-size: 16px !important;
  line-height: 26px !important;
  width: 26px !important;
  height: 26px !important;
}

.leaflet-control-zoom a:hover {
  background: var(--forest) !important;
  color: var(--text-on-dark) !important;
  cursor: pointer !important;
}

/* Governorate/district boundary place-name labels (permanent Leaflet
   tooltips bound in loadGeoJSON(), static/js/functions.js). Hidden by
   default and only shown once the map container carries the matching
   .show-*-labels class (updateBoundaryLabelVisibility() in map.js,
   driven off zoom level) — governorates need less zoom to have room for
   their names than the much more numerous, smaller districts do. Strips
   Leaflet's default tooltip chrome (background/border/arrow) down to
   plain text with a white halo, so it reads over any basemap without
   looking like a popup. pointer-events:none so labels never intercept
   clicks meant for the polygon/basemap beneath them. */
.boundary-label {
  display: none;
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  padding: 0 !important;
  font-weight: 600;
  letter-spacing: 0.2px;
  color: #1f2d2a;
  text-shadow:
    -1px -1px 0 #fff, 1px -1px 0 #fff,
    -1px 1px 0 #fff, 1px 1px 0 #fff,
    0 0 4px rgba(255, 255, 255, 0.9);
  pointer-events: none;
  white-space: nowrap;
}

.boundary-label::before { display: none !important; }

.boundary-label-governorate { font-size: 12px; }
.boundary-label-district { font-size: 9.5px; }

#main-map.show-governorate-labels .boundary-label-governorate { display: block; }
#main-map.show-district-labels .boundary-label-district { display: block; }

/* Basin labels (bound in loadDistrictStatusLayer(), functions.js, when
   called with permanentTooltip=true) — always on, no zoom gate, unlike
   governorate/district above: only 14 basins vs. up to 337 districts, so
   there's no readability problem at any zoom level the map allows.

   Deliberately a distinct branded pill/chip, not a shrunk copy of the
   plain governorate/district halo-text labels — basins are the primary
   analysis unit of this module now, so their labels should read as a
   different, more prominent category on the map rather than one more
   boundary label. Doesn't extend .boundary-label (that class's base
   rules are tuned for plain text over the basemap); every property
   Leaflet's own .leaflet-tooltip default styling would otherwise supply
   is overridden explicitly here instead. */
.boundary-label-basin {
  display: block !important;
  background: linear-gradient(135deg, var(--forest-dk), var(--forest)) !important;
  color: #fff !important;
  font-family: var(--font-sans);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  padding: 2.5px 8px !important;
  border-radius: 999px !important;
  border: 1px solid rgba(255, 255, 255, 0.4) !important;
  box-shadow: 0 2px 7px rgba(15, 23, 32, 0.4) !important;
  text-shadow: none !important;
  pointer-events: none;
  white-space: nowrap;
  opacity: 0.96;
}

.boundary-label-basin::before { display: none !important; }

body.lang-ar .boundary-label-basin { font-family: 'Tajawal', var(--font-sans); text-transform: none; }

/* Selected-basin "lifted off the map" effect (selectBasinLayer(),
   functions.js) — a white glow ring plus an offset drop shadow on the
   basin polygon's own SVG path, on top of the thicker/brighter stroke
   already applied via layer.setStyle(). Reads as the polygon popping up
   off the flat map rather than just a color change. Transition is on
   filter specifically (not a blanket `all`) since weight/color are
   handled by Leaflet's own style diffing, not CSS. */
.basin-layer-selected {
  filter:
    drop-shadow(0 0 7px rgba(255, 255, 255, 0.85))
    drop-shadow(0 10px 20px rgba(15, 23, 32, 0.55));
  transition: filter 0.18s ease;
}

/* Map legend */
.map-legend {
  position: absolute;
  bottom: 14px;
  left: 14px;
  background: var(--card-bg);
  border-radius: var(--radius-md);
  border: 0.5px solid var(--card-border);
  padding: 9px 12px;
  z-index: 400;
  min-width: 140px;
}

.legend-title {
  font-size: 9px;
  font-weight: 500;
  color: var(--card-text);
  text-transform: uppercase;
  letter-spacing: 0.7px;
  margin-bottom: 6px;
}

.legend-row {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 4px;
  font-size: 10px;
  color: var(--card-text);
}

.legend-swatch {
  width: 11px;
  height: 11px;
  border-radius: 2px;
  flex-shrink: 0;
}

.legend-count {
  margin-inline-start: auto;
  font-size: 9px;
  color: var(--card-text-muted);
  padding-inline-start: 10px;
}

/* Map trend panel (Groundwater's "Fastest Depleting Districts") — same
   light-card visual language as .map-legend, opposite corner. Each row
   pairs a current-status dot with a trend figure on purpose: they're
   different measurements (see functions.js:populateMapTrendPanel), and
   showing both side by side is what makes that legible at a glance. */
.map-trend-panel {
  position: absolute;
  bottom: 22px;
  right: 14px;
  background: var(--card-bg);
  border-radius: var(--radius-md);
  border: 0.5px solid var(--card-border);
  padding: 10px 12px;
  z-index: 400;
  min-width: 220px;
  max-width: 270px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}

.map-trend-title {
  font-size: 9px;
  font-weight: 500;
  color: var(--card-text);
  text-transform: uppercase;
  letter-spacing: 0.7px;
}

.map-trend-subtitle {
  font-size: 8.5px;
  color: var(--card-text-faint);
  margin-bottom: 8px;
  line-height: 1.3;
}

.map-trend-row {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 3px 0;
  font-size: 10.5px;
  border-top: 1px solid var(--card-border);
  cursor: default;
}

.map-trend-row:first-child { border-top: none; padding-top: 0; }
.map-trend-row:last-child { padding-bottom: 0; }

.map-trend-name {
  flex: 1;
  color: var(--card-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.map-trend-val {
  color: var(--critical);
  font-weight: 500;
  flex-shrink: 0;
}

/* Basin groundwater/rainfall anomaly chart — a bottom drawer rather than
   a small corner card like .map-trend-panel above, since a legible
   ~90-month two-line chart needs real width. Shown/hidden by
   showBasinChartPanel()/hideBasinChartPanel() (static/js/functions.js),
   which also nudges .map-trend-panel's `bottom` up while this is open so
   the two floating cards don't overlap.
   Chart-local color roles are scoped as custom properties on this class
   (not the shared --card-* tokens) since they're specific to this one
   chart's two series — see static/js/functions.js:renderBasinAnomalyChart
   and the dataviz palette's categorical slots 1 (blue) / 3 (aqua). */
.basin-chart-panel {
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 14px;
  z-index: 450;
  background: var(--card-bg);
  border: 0.5px solid var(--card-border);
  border-radius: var(--radius-md);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
  padding: 10px 14px 12px;

  --gwchart-surface:  #fcfcfb;
  --gwchart-grid:     #e1e0d9;
  --gwchart-baseline: #c3c2b7;
  --gwchart-gw:       #2a78d6;
  --gwchart-rain:     #1baf7a;
}
:root[data-theme="dark"] .basin-chart-panel {
  --gwchart-surface:  #1c242e;
  --gwchart-grid:     #2c2c2a;
  --gwchart-baseline: #383835;
  --gwchart-gw:       #3987e5;
  --gwchart-rain:     #199e70;
}

.basin-chart-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
}

.basin-chart-title {
  font-size: 11px;
  font-weight: 600;
  color: var(--card-text);
}

.basin-chart-subtitle {
  font-size: 9px;
  color: var(--card-text-faint);
  margin-top: 1px;
}

.basin-chart-close {
  color: var(--card-text-faint);
  font-size: 15px;
  padding: 2px;
  line-height: 1;
  flex-shrink: 0;
}
.basin-chart-close:hover { color: var(--card-text); }

.basin-chart-legend {
  display: flex;
  gap: 16px;
  margin-bottom: 4px;
}
.basin-chart-legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  color: var(--card-text-muted);
}
.basin-chart-legend-swatch {
  width: 14px;
  height: 2px;
  border-radius: 1px;
  flex-shrink: 0;
}

.basin-chart-body {
  position: relative;
  height: 190px;
}
.basin-chart-body svg { display: block; width: 100%; height: 100%; }

.basin-chart-tooltip {
  position: absolute;
  pointer-events: none;
  background: var(--card-bg);
  border: 0.5px solid var(--card-border);
  border-radius: var(--radius-sm);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
  padding: 6px 8px;
  font-size: 10px;
  white-space: nowrap;
  display: none;
  z-index: 1;
}
.basin-chart-tooltip-date {
  color: var(--card-text-faint);
  margin-bottom: 3px;
  font-weight: 500;
}
.basin-chart-tooltip-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.basin-chart-tooltip-row + .basin-chart-tooltip-row { margin-top: 2px; }
.basin-chart-tooltip-key {
  width: 10px;
  height: 2px;
  border-radius: 1px;
  flex-shrink: 0;
}
.basin-chart-tooltip-label { color: var(--card-text-muted); }
.basin-chart-tooltip-value {
  color: var(--card-text);
  font-weight: 600;
  margin-inline-start: auto;
  padding-inline-start: 10px;
}

.basin-chart-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--card-text-faint);
  font-size: 11px;
  text-align: center;
  padding: 0 20px;
}

/* ── Right panel ────────────────────────────────────────────── */
.right-panel {
  width: var(--right-panel-w);
  background: var(--chrome-bg);
  border-inline-start: 1px solid var(--chrome-border);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  flex-shrink: 0;
  transition: background 0.2s ease, border-color 0.2s ease;
}

.right-panel::-webkit-scrollbar { width: 4px; }
.right-panel::-webkit-scrollbar-thumb { background: var(--chrome-scrollbar); border-radius: 4px; }

.rp-section {
  padding: 12px 12px 10px;
  border-bottom: 1px solid var(--chrome-border);
}

.rp-section:last-child { border-bottom: none; }

/* Pins a section (e.g. groundwater's "Map Layer" toggle) to the top of
   .right-panel while the rest of the panel (e.g. "Active Alerts") scrolls
   past/under it. Needs its own background since sticky elements are
   otherwise transparent against whatever scrolls beneath them, and a
   shadow once stuck so it doesn't visually blend into the scrolled
   content. .right-panel is already the scrolling ancestor
   (overflow-y: auto), so no container changes are needed elsewhere —
   this only affects the section it's applied to. */
.rp-section-sticky {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--chrome-bg);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
}

.rp-heading {
  font-size: 9px;
  font-weight: 500;
  color: var(--chrome-text);
  text-transform: uppercase;
  letter-spacing: 0.8px;
  margin-bottom: 8px;
}

/* ── Groundwater Monitor's basin detail panel ──────────────────
   Replaces the old map popup: clicking a basin (see selectBasinLayer()/
   renderBasinDetailPanel() in functions.js) fills this instead of
   opening a Leaflet popup. .rp-basin-empty is the resting state (no
   basin selected); exactly one of the two is ever visible. */
.rp-basin-panel {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.rp-basin-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 32px 20px;
  text-align: center;
  color: var(--chrome-text-faint);
}

.rp-basin-empty i { font-size: 26px; opacity: 0.6; }
.rp-basin-empty p { font-size: 11px; line-height: 1.5; max-width: 180px; }

.rp-basin-detail { padding: 12px; }

.rp-basin-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-bottom: 10px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--chrome-border);
}

.rp-basin-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--chrome-text);
  line-height: 1.3;
}

.rp-basin-header .stat-badge { margin-top: 0; flex-shrink: 0; }

.rp-basin-group { margin-bottom: 14px; }
.rp-basin-group:last-of-type { margin-bottom: 10px; }

.rp-basin-group-title {
  font-size: 9px;
  font-weight: 600;
  color: var(--chrome-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.7px;
  margin-bottom: 6px;
}

.rp-basin-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 0;
  border-top: 1px solid var(--chrome-border);
}

.rp-basin-row:first-child { border-top: none; }

.rp-basin-row-label {
  font-size: 10.5px;
  color: var(--chrome-text-muted);
  flex-shrink: 0;
}

.rp-basin-row-value {
  font-size: 12px;
  font-weight: 600;
  color: var(--chrome-text);
  text-align: end;
}

.rp-basin-footer {
  font-size: 9px;
  color: var(--chrome-text-faint);
  padding-top: 10px;
  border-top: 1px solid var(--chrome-border);
}

/* Alert cards — these sit on the chrome (right panel), but use the
   card-* surface tokens for their fill/text since the severity tint
   backgrounds (pale on light, translucent on dark) are the same ones
   the map legend/badges use, and read best against a near-white/
   near-black card rather than the chrome's flat tan/navy fill. */
.alert-card {
  padding: 7px 9px;
  margin-bottom: 5px;
  border-inline-start: 2px solid transparent;
  border-radius: var(--radius-sm);
}

.alert-card:last-child { margin-bottom: 0; }
.alert-card.critical  { background: var(--critical-bg);  border-inline-start-color: var(--critical); }
.alert-card.severe    { background: var(--severe-bg);    border-inline-start-color: var(--severe); }
.alert-card.moderate  { background: var(--moderate-bg);  border-inline-start-color: var(--moderate); }

.alert-gov  { font-size: 10px; font-weight: 500; color: var(--card-text); }
.alert-desc { font-size: 9px; color: var(--card-text); opacity: 0.75; margin-top: 1px; }
.alert-time { font-size: 8px; color: var(--card-text-faint); margin-top: 2px; }

/* Coverage bars */
.coverage-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 7px;
}

.coverage-row:last-child { margin-bottom: 0; }

.coverage-name {
  font-size: 10px;
  color: var(--chrome-text);
  width: 52px;
  flex-shrink: 0;
}

.coverage-track {
  flex: 1;
  height: 4px;
  background: var(--chrome-border);
  border-radius: 2px;
  overflow: hidden;
}

.coverage-fill {
  height: 100%;
  border-radius: 2px;
  transition: width 0.4s ease;
}

.coverage-pct {
  font-size: 9px;
  color: var(--chrome-text-muted);
  width: 28px;
  text-align: end;
  flex-shrink: 0;
}

/* Trend rows */
.trend-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 5px;
  font-size: 10px;
}

.trend-month {
  color: var(--chrome-text-muted);
  width: 92px;
  flex-shrink: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.trend-val   { font-weight: 500; min-width: 40px; text-align: end; }

.trend-bar-track {
  flex: 1;
  height: 4px;
  background: var(--chrome-border);
  border-radius: 2px;
  overflow: hidden;
}

.trend-bar-fill {
  height: 100%;
  border-radius: 2px;
}

/* ── Utility classes ────────────────────────────────────────── */
.text-critical { color: var(--critical); }
.text-severe   { color: var(--severe); }
.text-moderate { color: var(--moderate); }
.text-normal   { color: var(--normal); }
.text-muted    { color: var(--chrome-text-muted); }
.text-faint    { color: var(--chrome-text-faint); }

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

/* ── Flash messages ─────────────────────────────────────────── */
.messages { position: fixed; top: 52px; right: 16px; z-index: 1000; display: flex; flex-direction: column; gap: 6px; }

.message {
  padding: 8px 14px;
  border-radius: var(--radius-md);
  font-size: 12px;
  border-inline-start: 3px solid transparent;
  background: var(--card-bg);
  border: 0.5px solid var(--card-border);
  color: var(--card-text);
  max-width: 280px;
}

.message.success { border-inline-start-color: var(--normal); background: var(--normal-bg); }
.message.warning { border-inline-start-color: var(--severe); background: var(--severe-bg); }
.message.error   { border-inline-start-color: var(--critical); background: var(--critical-bg); }

/* ── Loading spinner ────────────────────────────────────────── */
@keyframes spin { to { transform: rotate(360deg); } }

/* Hidden by default (desktop) — shown by the <=900px media block below
   once its target drawer only exists as an off-canvas panel. Declared
   here, before that block, so its display:none doesn't win a same-
   specificity, later-wins-ties fight against the media query's
   display:flex (a @media wrapper changes *whether* a rule block applies,
   not its specificity — source order still decides ties). */
.topbar-hamburger, .map-panel-toggle-btn { display: none; }

.topbar-hamburger {
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-md);
  background: transparent;
  border: 1.5px solid var(--chrome-border);
  color: var(--chrome-text);
  font-size: 17px;
  flex-shrink: 0;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.topbar-hamburger:hover { border-color: var(--chrome-icon); background: var(--chrome-hover); }

/* Floating button that opens the right-panel drawer (mobile only — see
   .map-panel-toggle-btn's base display:none above). Bottom-corner FAB
   rather than another item in the already-crowded top toggle row. */
.map-panel-toggle-btn {
  position: absolute;
  bottom: 14px;
  inset-inline-end: 14px;
  z-index: 500;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--forest-dk);
  color: var(--sage);
  border: none;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
  font-size: 19px;
}

/* Right-panel drawer's close button (mobile only) — hidden until
   #right-panel actually carries '.is-open' (added by
   toggleRightPanelDrawer()/openRightPanelDrawer() in functions.js), a
   pure-CSS sibling reaction so nothing needs to show/hide this
   separately in JS. position:fixed (not absolute-in-.map-panel like
   .map-panel-toggle-btn) since it needs to sit above the drawer
   regardless of which panel is even open. */
.right-panel-close-btn {
  display: none;
  position: fixed;
  z-index: 1300;
}
@media (max-width: 900px) {
  .right-panel.is-open ~ .right-panel-close-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    top: calc(var(--topbar-h) + 10px);
    inset-inline-end: calc(min(88vw, 340px) + 10px);
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--card-bg);
    border: 0.5px solid var(--card-border);
    color: var(--card-text);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
    font-size: 15px;
  }
}

/* ── Responsive (tablet/phone, <=900px) ────────────────────────
   Below this width there isn't room for the desktop 3-column shell
   (sidebar + map + right-panel) side by side — a squeezed sidebar and
   squeezed right-panel used to fight over what little space was left
   for the map, the actual content every tab is built around. Both
   become off-canvas drawers instead (same slide-in-over-the-content
   pattern from opposite edges, one shared backdrop — see
   .mobile-backdrop and toggleSidebarDrawer()/toggleRightPanelDrawer()/
   closeMobileDrawers() in functions.js), so the map/content gets the
   full viewport by default and either panel is one tap away. */
@media (max-width: 900px) {
  .topbar { padding: 0 12px; gap: 10px; }
  .topbar-name { display: none; }
  .topbar-btn span { display: none; }

  /* Hidden entirely above 900px (see .topbar-hamburger's base rule) —
     shown here as the way to reach the sidebar drawer now that the
     sidebar itself is off-canvas. */
  .topbar-hamburger { display: flex; }

  .sidebar {
    position: fixed;
    top: var(--topbar-h);
    bottom: 0;
    inset-inline-start: 0;
    width: min(84vw, 280px);
    z-index: 1200;
    box-shadow: 6px 0 24px rgba(0, 0, 0, 0.25);
    transform: translateX(-100%);
    transition: transform 0.25s ease;
  }
  html[dir="rtl"] .sidebar { box-shadow: -6px 0 24px rgba(0, 0, 0, 0.25); transform: translateX(100%); }
  .sidebar.is-open,
  html[dir="rtl"] .sidebar.is-open { transform: translateX(0); }

  /* Full labels/description restored — the old icon-only 46px rail
     this replaced had no room for either, and an off-canvas drawer has
     width to spare. */
  .nav-item { justify-content: flex-start; padding: 10px 12px; }
  .nav-item.active { border-bottom: none; padding-inline-start: 9px; border-inline-start: 3px solid var(--chrome-active-icon); }

  /* Statsbar: 3 equal-thirds cells wrap/overlap below their designed
     min width — a horizontal scroll strip of fixed-width cells reads
     as "swipe for more" instead of "broken layout" once cells no
     longer fit three-across. The scrollbar itself is hidden (mobile
     convention — a native touch-scroll indicator flashes briefly on
     its own), so without some other cue this just looks like the last
     cell got cut off rather than "there's more, swipe" — the mask
     fades the trailing 28px to transparent as a permanent hint that
     survives after any native scrollbar flash has faded. A mask (on
     the statsbar's own painted box) rather than a gradient overlay
     element, since a mask stays fixed to the viewport edge as content
     scrolls underneath it; an overlay positioned by content coordinates
     would scroll away with everything else. Physical to/right direction
     — mask-image doesn't follow dir="rtl" automatically, so the RTL
     rule right below mirrors it explicitly. */
  .statsbar {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
  }
  .statsbar::-webkit-scrollbar { display: none; }
  html[dir="rtl"] .statsbar {
    -webkit-mask-image: linear-gradient(to left, black calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to left, black calc(100% - 28px), transparent 100%);
  }
  .stat-cell {
    flex: 0 0 auto;
    min-width: 148px;
    padding-inline: 14px;
  }
  .stat-cell:first-child { padding-inline-start: 2px; }

  /* Right panel: same off-canvas treatment as the sidebar, from the
     opposite edge. The existing display:none/flex toggling
     (showRightPanel()/hideRightPanel()) still governs whether there's
     any content at all; .is-open only controls whether that content is
     slid into view or parked off-screen, so nothing about the desktop
     behavior above 900px changes. */
  .right-panel {
    position: fixed;
    top: var(--topbar-h);
    bottom: 0;
    inset-inline-end: 0;
    width: min(88vw, 340px);
    z-index: 1200;
    box-shadow: -6px 0 24px rgba(0, 0, 0, 0.25);
    transform: translateX(100%);
    transition: transform 0.25s ease;
  }
  html[dir="rtl"] .right-panel { box-shadow: 6px 0 24px rgba(0, 0, 0, 0.25); transform: translateX(-100%); }
  .right-panel.is-open,
  html[dir="rtl"] .right-panel.is-open { transform: translateX(0); }

  /* Floating "open the right panel" affordance (map-panel-toggle-btn in
     dashboard.html) — hidden above 900px, where the panel is always
     visible in-flow and needs no toggle. */
  .map-panel-toggle-btn { display: flex; }

  /* Shared dim/blur scrim behind whichever drawer is open — hidden
     (not just transparent) when inactive so it can't eat clicks meant
     for the map underneath. */
  .mobile-backdrop {
    position: fixed;
    inset: 0;
    top: var(--topbar-h);
    background: rgba(10, 14, 18, 0.45);
    backdrop-filter: blur(2px);
    z-index: 1100;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s ease, visibility 0.2s ease;
  }
  .mobile-backdrop.is-active { opacity: 1; visibility: visible; }

  /* Basin chart panel: less horizontal margin to claw back width on a
     narrow screen, and a shorter plot area so the SVG's rendered aspect
     ratio doesn't get too extreme (see renderBasinAnomalyChart() in
     functions.js — the viewBox now always matches real pixel width, so
     this is a design choice, not a distortion fix). */
  .basin-chart-panel { left: 8px; right: 8px; bottom: 8px; padding: 8px 10px 10px; }
  .basin-chart-body { height: 150px; }
}

/* Sidebar/right-panel close buttons — only meaningful once either is an
   off-canvas drawer, so shown only inside the drawer breakpoint despite
   living in the always-rendered markup (dashboard.html/base.html). */
.drawer-close-btn { display: none; }
@media (max-width: 900px) {
  .drawer-close-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-sm);
    color: var(--chrome-text-muted);
    flex-shrink: 0;
  }
  .drawer-close-btn:hover { background: var(--chrome-hover); color: var(--chrome-text); }
  .sidebar-drawer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 14px;
    border-bottom: 1px solid var(--chrome-border);
    flex-shrink: 0;
  }
  .sidebar-drawer-header-title { font-size: 12px; font-weight: 700; color: var(--chrome-text); }
}
@media (min-width: 901px) {
  .sidebar-drawer-header { display: none; }
}

/* Extra-narrow phones: a bit more breathing room clawed back from
   chrome that can afford to shrink further than the shared 900px tier
   above already gives it. */
@media (max-width: 480px) {
  .topbar { gap: 6px; padding: 0 8px; }
  .topbar-actions { gap: 5px; }
  .topbar-btn { padding: 0 10px; }
  .stat-cell { min-width: 132px; padding-inline: 10px; }
  .stat-value { font-size: 19px; }
}

/* Full-screen scrim — covers the entire dashboard (topbar, sidebar,
   map, right panel), not just the map panel it happens to sit inside;
   position:fixed escapes .map-panel's overflow:hidden since nothing
   between it and the viewport sets a transform/filter of its own.
   Blurs + dims everything behind it and captures all clicks so the
   user can't navigate away mid-load. Holds a centered .loading-card
   rather than floating the spinner/text directly on the scrim. */
.loading-overlay {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(15, 23, 32, 0.4);
    z-index: 9999;
    backdrop-filter: blur(6px);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.25s ease, visibility 0.25s ease;
}

/* Active state (when shown via JS display:flex). Was opacity: 0.3 here,
   which washed out the spinner/text along with the scrim instead of
   just dimming the dashboard behind it — the card below is what should
   read as translucent-backed, not the whole loading UI. */
.loading-overlay[style*="display: flex"] {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}

/* Centered card (spinner + message) rather than bare elements floating
   on the scrim — same card-surface tokens used by the map's floating
   legend/trend panels, so this reads as the same design language. */
.loading-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    padding: 26px 34px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: var(--radius-lg);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
    transform: scale(0.96);
    transition: transform 0.25s ease;
}

.loading-overlay[style*="display: flex"] .loading-card {
    transform: scale(1);
}

/* Spinner */
.spinner {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 3px solid var(--card-border);
    border-top-color: var(--parchment);
    animation: spin 0.8s linear infinite;
}

/* Loading text */
.loading-text {
    font-size: 13px;
    font-weight: 500;
    color: var(--card-text);
    letter-spacing: 0.2px;
    text-align: center;
}

/* Map-scoped loading overlay — used for in-module map layer switches
   (Groundwater's water-stress/anomaly/trend toggle, Explorer's "Show on
   Map"), as opposed to .loading-overlay's full-screen scrim used for
   switching modules entirely. Absolutely positioned (not fixed), so
   .map-panel's own position:relative + overflow:hidden contains it —
   the topbar/sidebar/right-panel stay interactive and untouched while
   only the map re-renders. */
.map-loading-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(15, 23, 32, 0.12);
    backdrop-filter: blur(2px);
    z-index: 500;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.2s ease, visibility 0.2s ease;
}

.map-loading-overlay[style*="display: flex"] {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}

/* Thin indeterminate sweep across the top edge of the map panel —
   reads as "this panel is refreshing" without a scrim-covering card. */
.map-loading-overlay::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: linear-gradient(90deg, transparent, var(--parchment), transparent);
    background-size: 200% 100%;
    animation: map-loading-sweep 1.1s ease-in-out infinite;
}

@keyframes map-loading-sweep {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

.map-loading-chip {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: var(--radius-md);
    padding: 9px 16px;
    box-shadow: 0 8px 26px rgba(0, 0, 0, 0.22);
    opacity: 0;
    transform: translateY(4px) scale(0.98);
    transition: opacity 0.25s ease, transform 0.25s ease;
}

.map-loading-overlay[style*="display: flex"] .map-loading-chip {
    opacity: 1;
    transform: translateY(0) scale(1);
}

.map-spinner-sm {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 2px solid var(--card-border);
    border-top-color: var(--parchment);
    animation: spin 0.7s linear infinite;
    flex-shrink: 0;
}

.map-loading-text {
    font-size: 11px;
    font-weight: 500;
    color: var(--card-text);
    letter-spacing: 0.2px;
}

#module-description{
  font-size: 11px;
  text-align: justify;
  color: var(--chrome-text-muted);
}

/* Reopens the Module Details popup (see showModuleInfoPopup() in
   functions.js) — inline at the end of the description text rather
   than a separate topbar button, so it reads as "there's more where
   this came from" right where the short version just left off. */
.module-description-readmore {
  display: inline-block;
  color: var(--chrome-icon);
  font-weight: 600;
  white-space: nowrap;
  margin-inline-start: 4px;
}
.module-description-readmore:hover { text-decoration: underline; }

.btn-primary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  background: var(--parchment);
  color: var(--text-on-dark);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.2px;
  padding: 9px 14px;
  margin-top: 12px;
  border-radius: var(--radius-md);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  transition: background 0.15s ease, box-shadow 0.15s ease, transform 0.1s ease;
}

.btn-primary i { font-size: 14px; }

.btn-primary:hover {
  background: var(--parchment-lt);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
}

.btn-primary:active {
  transform: translateY(1px);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}

.btn-primary:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* .month-field also covers the <select> of exact years that
   toggleMonthInput() in functions.js swaps in for annual-snapshot
   datasets (pop/landcover) in place of the plain month input — same
   id, same class, so it needs to look the same. */
input[type="month"],
select.month-field {
  width: 100%;
  padding: 6px 6px;
  font-size: 10px;
  border: 1px solid var(--chrome-input-border);
  border-radius: 8px;
  background-color: var(--chrome-input-bg);
  color: var(--chrome-text);
  outline: none;
  transition: 0.2s ease;
}


input[type="month"]:focus,
select.month-field:focus {
  border-color: #4c9ffe;
  box-shadow: 0 0 0 3px rgba(76, 159, 254, 0.2);
}

input[type="month"]:hover,
select.month-field:hover {
  border-color: var(--chrome-icon);
}

/* =============================================================
   LEAFLET POPUP — YEIP Professional Style
   Deliberately fixed-dark regardless of dashboard theme (same
   reasoning as the page-transition overlay above) — a dark popup
   reads as an intentional map-UI convention rather than something
   that needs to track the surrounding page.
   ============================================================= */

.leaflet-popup-tip { background: #0f1923; box-shadow: none; }
.leaflet-popup-content { margin: 10px 12px; }

/* Header: district name + governorate, built by popupCard() in functions.js */
.popup-header {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-bottom: 7px;
    padding-bottom: 7px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.popup-place { font-size: 14px; font-weight: 600; color: #f4f7f6; }
.popup-gov   { font-size: 11px; font-weight: 400; color: #9fb0aa; white-space: nowrap; }

/* Dataset tag: which Explorer "Show on Map" layer this popup's values
   belong to (passed as popupCard()'s `dataset` param) — sits directly
   under the header, above the stat rows, so a district clicked while
   two different datasets are shown never reads ambiguously. */
.popup-dataset {
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.4px;
    text-transform: uppercase;
    color: #6fa88f;
    margin-bottom: 6px;
}

/* Stat rows: one per label/value pair passed to popupCard() */
.popup-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    padding: 2px 0;
}

.popup-label { font-size: 11px; color: #9fb0aa; }
.popup-value { font-size: 13px; font-weight: 600; color: #e8edf2; text-align: end; }

/* Footer: muted metadata (date/source), visually separated from the stats */
.popup-footer {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    margin-top: 7px;
    padding-top: 6px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    font-size: 10px;
    color: #7b8c85;
}

/* Wrapper */
.leaflet-popup-content-wrapper {
    background: var(--forest-dk);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    box-shadow:
        0 4px 24px rgba(0, 0, 0, 0.5),
        0 1px 4px rgba(0, 0, 0, 0.3),
        inset 0 1px 0 rgba(255, 255, 255, 0.06);
    padding: 10px;
    overflow: hidden;
}

/* Content area */
.leaflet-popup-content {
    margin: 0;
    padding: 0;
    color: #e8edf2;
    font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
    font-size: 13px;
    line-height: 1.5;
    min-width: 220px;
}

/* Tip / arrow container */
.leaflet-popup-tip-container {
    margin-top: -1px;
}

/* Close button
   Selector matches Leaflet's own default rule (.leaflet-container
   a.leaflet-popup-close-button) — that exact selector is more specific
   than a bare .leaflet-popup-close-button, so leaflet's own `right: 0`
   was winning over our override regardless of !important. Matching its
   specificity here, not just the class, is what actually overrides it. */
.leaflet-container a.leaflet-popup-close-button {
    color: rgba(255, 255, 255, 0.3) !important;
    font-size: 18px !important;
    font-weight: 300 !important;
    top: 10px !important;
    right: auto !important;
    /* Logical property (like border-inline-start elsewhere in this file)
       so it sits at the popup's trailing edge — right in LTR (English),
       left in RTL (Arabic, <html dir="rtl">, see apps/core/views.py). */
    inset-inline-end: 10px !important;
    width: 20px !important;
    height: 20px !important;
    line-height: 20px !important;
    transition: color 0.2s ease;
}

.leaflet-popup-close-button:hover {
    color: rgba(255, 255, 255, 0.8) !important;
    background: none !important;
}
