/* T-PIE Digital Twin dashboard — command-center grid (mockup rebuild, Phase 0+1). */
:root {
  --bg: #070b12;
  --bg-2: #0b1119;
  --panel: #0e1622;
  --panel-2: #121c2b;
  --text: #e7edf3;
  --muted: #8ba0b6;
  --faint: #64748b;
  --border: #1c2735;
  --border-2: #263346;
  --accent: #35c5f0;
  --accent-dim: #1f6f8a;
  --good: #2ecc71;
  --danger: #e74c3c;
  --warn: #e67e22;
  /* PM2.5 display bands (matches mockup legend) */
  --b1: #3b82f6; --b2: #2ecc71; --b3: #f1c40f; --b4: #e67e22; --b5: #e74c3c; --b6: #9b2530;
  /* height of the shared top bar (topbar.js) — the same on every page, so every page's
     grid must take its first row from this token rather than hard-coding a number */
  --top-h: 56px;
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  /* the typeface is §09's (ตัวอักษรของเว็บ) — theme.js writes --ui-font, this is the fallback
     for a browser that has never opened the settings page */
  font-family: var(--ui-font, "Segoe UI", "Noto Sans Thai", system-ui, sans-serif);
  color: var(--text); background: var(--bg);
  overflow: hidden;
  font-size: 13px;
}

/* The UA rule for [hidden] is `display: none`, which ANY author `display` declaration
   outranks -- so a component styled `display: flex` renders wide open on load despite its
   `hidden` attribute. That is exactly what `.why-drawer` did: it is `position: fixed` and
   parked at `translateX(102%)`, so a closed drawer sat 560px off the right edge of every
   page that carries it (index, source), still laid out and still counted by
   scripts/check_layout_fit.py as an overflow. Make the attribute win for every page built on
   this stylesheet -- impact.css already does the same for the war-room shell (`.ia [hidden]`),
   and this is the other half of that rule.

   Safe with the slide-in drawers: openWhy() clears `hidden` and only adds `.show` on the NEXT
   frame (requestAnimationFrame), so the transition still has a frame to start from; closeWhy()
   sets `hidden` back only after the 380ms transition has run. */
#app [hidden] { display: none !important; }

/* ============ GRID SHELL ============ */
/* minmax(0, …) on the flexible column + min-width:0 on the children lets tracks
   shrink below their content's intrinsic width — without it a wide child (map,
   charts) forces the grid past the viewport and clips the right rail. The bottom
   strip spans the full width (as in the mockup) so its four charts stay roomy. */
#app {
  display: grid;
  width: 100%;
  height: 100vh;
  grid-template-columns: 300px minmax(0, 1fr) 300px;
  grid-template-rows: var(--top-h) minmax(0, 1fr) 190px;
  grid-template-areas:
    "top    top    top"
    "left   center right"
    "bottom bottom bottom";
  gap: 8px;
  padding: 8px;
  background:
    radial-gradient(1200px 600px at 50% -10%, #0d1826 0%, var(--bg) 60%);
}
#topbar { grid-area: top; }
#left-rail { grid-area: left; }
#center { grid-area: center; }
#right-rail { grid-area: right; }
#bottom-strip { grid-area: bottom; }
#left-rail, #center, #right-rail, #bottom-strip { min-width: 0; }

#left-rail, #right-rail {
  display: flex; flex-direction: column; gap: 8px;
  overflow-y: auto; overflow-x: hidden; min-height: 0;
}
/* Keep each rail panel at its natural (content) height. Without this the panels
   inherit flex-shrink:1 and, on short viewports, get squashed below their content
   height — the content then spills past the panel border and overlaps the next
   panel. Pinning flex to 0 0 auto makes the rail scroll instead of squashing. */
#left-rail > .panel, #right-rail > .panel { flex: 0 0 auto; }
#bottom-strip {
  display: grid; grid-template-columns: 1.3fr 1fr 1.4fr 1.2fr; gap: 8px; min-height: 0;
}
#bottom-strip > .panel { min-width: 0; overflow: hidden; }

/* ============ PANEL ============ */
.panel {
  background: linear-gradient(180deg, var(--panel), var(--bg-2));
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  min-height: 0; min-width: 0;
  display: flex; flex-direction: column;
}
.panel h2 {
  font-size: 11px; letter-spacing: .08em; font-weight: 700;
  color: var(--accent); margin: 0; text-transform: uppercase;
}
.sub-th { font-size: 11px; color: var(--muted); margin: 2px 0 10px; }
.prov-tag {
  font-size: 9px; color: var(--warn); border: 1px solid var(--warn);
  border-radius: 4px; padding: 0 4px; margin-left: 4px; vertical-align: middle;
  opacity: .8;
}
.unit-tag { float: right; font-size: 10px; color: var(--faint); border: 1px solid var(--border-2);
  border-radius: 4px; padding: 1px 6px; }

/* ============ TOP BAR ============ */
#topbar {
  display: flex; align-items: center; gap: 20px;
  background: linear-gradient(180deg, #0d1826, var(--bg-2));
  border: 1px solid var(--border); border-radius: 10px; padding: 0 18px;
}
.brand h1 { font-size: 18px; margin: 0; font-weight: 700; letter-spacing: .01em; }
.brand .accent, .accent { color: var(--accent); }
.brand .sub { font-size: 10px; color: var(--muted); letter-spacing: .02em; }
/* Top-bar menu — markup comes from topbar.js, so these rules cover <a>, <button> and the
   inert <span> of the current page alike. Labels are Thai: no letter-spacing (it breaks
   up the script) and the row must stay on one line, hence nowrap + a shrinkable box. */
#nav { display: flex; gap: 2px; margin-left: 4px; min-width: 0; }
#nav .tab {
  background: transparent; border: none; color: var(--muted); cursor: pointer;
  font-family: inherit; font-size: 12px; font-weight: 600; letter-spacing: 0;
  padding: 8px 9px; border-bottom: 2px solid transparent; text-decoration: none;
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
}
#nav .tab:hover { color: var(--text); }
#nav .tab.active { color: var(--accent); border-bottom-color: var(--accent); cursor: default; }
#nav .tab-icon { font-size: 11px; opacity: .85; }
/* pages not built yet: visible in the menu, but plainly not reachable */
#nav .tab.soon, #nav .tab[disabled] { opacity: .4; cursor: not-allowed; }
#nav .tab.soon:hover { color: var(--muted); }
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 18px; }
.clock { text-align: right; }
.clock-date { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
.clock-sub { font-size: 10px; color: var(--muted); }
.live { color: var(--danger); font-weight: 700; }
.live.off { color: var(--faint); }
/* Data-age states. A long-running --live process serves a boot-time snapshot, so the badge
   has to report the age of the data, not the health of the HTTP call — a server up for three
   days once showed "● LIVE" beside three-day-old fires with nothing on screen to contradict
   it. .stale = past its refresh window, .old = more than a day behind. */
.live.stale { color: #f5a524; }
.live.old { color: #ff5a3c; text-decoration: underline dotted; text-underline-offset: 3px; }
.weather { display: flex; align-items: center; gap: 8px; }
.wx-icon { font-size: 22px; }
.wx-temp { font-size: 16px; font-weight: 700; }
.wx-sub { font-size: 10px; color: var(--muted); }
/* whose weather: "ทั้งประเทศ" on Overview, the province name on a province page. Without
   it the two scopes read as two contradictory numbers. */
.wx-scope { font-size: 9px; color: var(--faint); letter-spacing: .02em; }
.wx-scope:empty { display: none; }
/* estimated weather (no per-point thermodynamics for this scope) — marked rather than
   presented as measured, the same honesty rule the .prov-tag chips follow */
.weather[data-provisional] .wx-temp { border-bottom: 1px dotted var(--warn); }

/* ============ REAL-TIME OVERVIEW ============ */
.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.stat {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px;
  padding: 8px 10px; text-align: center;
}
.stat-label { font-size: 9px; color: var(--muted); letter-spacing: .06em; }
.stat-val { font-size: 26px; font-weight: 800; color: var(--good); line-height: 1.1; }
.stat-val.danger { color: var(--danger); }
.stat-unit { font-size: 9px; color: var(--faint); }
.stat-band { font-size: 10px; color: var(--muted); margin-top: 2px; }

/* ============ PM2.5 BY REGION ============ */
.region-bars { display: flex; flex-direction: column; gap: 9px; }
.region-row { display: grid; grid-template-columns: minmax(0, 84px) 1fr auto; align-items: center; gap: 8px; font-size: 11px; }
.region-row .r-name { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.region-row .r-track { height: 7px; background: var(--panel-2); border-radius: 4px; overflow: hidden; }
.region-row .r-fill { height: 100%; border-radius: 4px; }
.region-row .r-val { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }
.region-row .r-unit { font-size: 9px; color: var(--faint); }

/* ============ SOURCES ============ */
.sources-row { display: flex; align-items: center; gap: 8px; flex: 1; }
.donut { width: 110px; height: 110px; flex: none; }
.sources-legend { display: flex; flex-direction: column; gap: 6px; font-size: 11px; }
.sources-legend .sl-row { display: flex; align-items: center; gap: 7px; }
.sources-legend .sl-dot { width: 10px; height: 10px; border-radius: 2px; flex: none; }
.sources-legend .sl-pct { font-weight: 700; min-width: 32px; }
.sources-legend .sl-name { color: var(--muted); }
.sources-legend .sl-en { color: var(--faint); font-size: 9px; }

/* ============ CENTER MAP ============ */
#center { position: relative; border-radius: 10px; overflow: hidden; border: 1px solid var(--border); }
#map { position: absolute; inset: 0; background: var(--bg); }
.panel.floating {
  position: absolute; z-index: 5; background: rgba(11,17,25,0.86); backdrop-filter: blur(8px);
}
#map-controls {
  position: absolute; top: 12px; left: 12px; right: 12px; z-index: 6;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
/* ==================== PROVINCE COMBOBOX (map control bar) ==================== */
/* The province selector every other screen carries, on the Overview map bar. Defined here
 * for the same reason the layer menu below is: this page does not load impact.css, whose
 * copy of these classes is density-scaled for the `.ia` pages. Every selector is scoped to
 * #map-controls, so this copy cannot reach the pages that bring their own. */
#map-controls .prov-combo { position: relative; flex: 0 0 auto; }
#map-controls .pc-button {
  display: inline-flex; align-items: center; gap: 7px;
  background: rgba(11, 17, 25, 0.86); border: 1px solid var(--border-2); border-radius: 8px;
  color: #e8f2fb; font-family: inherit; font-size: 11px; font-weight: 600;
  padding: 6px 10px; cursor: pointer; max-width: 240px; backdrop-filter: blur(8px);
}
#map-controls .pc-button:hover { border-color: var(--accent); }
#map-controls .pc-button[aria-expanded="true"] { border-color: var(--accent);
  box-shadow: 0 0 0 1px rgba(53, 197, 240, 0.35), 0 0 18px -6px rgba(53, 197, 240, 0.6); }
#map-controls .pc-pin { font-size: 10px; }
#map-controls .pc-current { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

#map-controls .pc-panel {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
  width: 320px; max-height: 60vh; display: flex; flex-direction: column;
  background: #0d1725; border: 1px solid var(--border-2); border-radius: 10px;
  box-shadow: 0 24px 60px -18px rgba(0, 0, 0, 0.92); overflow: hidden;
}
#map-controls .pc-search { display: flex; align-items: center; gap: 7px; padding: 9px 10px;
  border-bottom: 1px solid var(--border); flex: 0 0 auto; }
#map-controls .pc-search-ic { color: var(--muted); font-size: 13px; }
#map-controls .pc-input { flex: 1; min-width: 0; background: transparent; border: 0; outline: none;
  color: var(--text); font-family: inherit; font-size: 11px; }
#map-controls .pc-input::placeholder { color: #5c6f85; }
#map-controls .pc-kbd, #map-controls .pc-foot kbd { font-family: inherit; font-size: 9px; color: var(--faint);
  border: 1px solid var(--border-2); border-radius: 4px; padding: 1px 4px; background: #101b29; }
/* the one place on this screen that scrolls: a 78-row picker inside a popover. It is an
   overlay, not page content, so the no-scroll contract still holds. */
#map-controls .pc-list { overflow-y: auto; overscroll-behavior: contain; padding: 5px; flex: 1 1 auto; min-height: 0; }
#map-controls .pc-group { font-size: 9px; letter-spacing: 0.1em; color: var(--faint); padding: 8px 8px 4px;
  text-transform: uppercase; position: sticky; top: 0; background: #0d1725; }
#map-controls .pc-item { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px;
  cursor: pointer; font-size: 11px; }
#map-controls .pc-item:hover, #map-controls .pc-item.cursor { background: #17273d; }
#map-controls .pc-item.selected { background: rgba(53, 197, 240, 0.14); color: #fff; }
#map-controls .pc-item .pi-th { font-weight: 600; }
#map-controls .pc-item .pi-en { color: var(--faint); font-size: 9px; margin-left: auto; }
/* ทั้งประเทศ sits above the groups and reads as the reset, not as a 78th province */
#map-controls .pc-item.pc-all { border: 1px dashed var(--border-2); margin-bottom: 4px; }
#map-controls .pc-item.pc-all .pi-th { color: var(--accent); }
#map-controls .pc-star { background: transparent; border: 0; color: #3d4d61; cursor: pointer;
  font-size: 12px; padding: 0 2px; line-height: 1; }
#map-controls .pc-star.on, #map-controls .pc-star:hover { color: #f1c40f; }
#map-controls .pc-empty { padding: 18px 10px; text-align: center; color: var(--faint); font-size: 11px; }
#map-controls .pc-foot { display: flex; justify-content: space-between; gap: 8px; padding: 7px 10px;
  border-top: 1px solid var(--border); font-size: 9px; color: var(--faint); flex: 0 0 auto; }

/* which scope a panel is showing — stamped into every [data-scope] sub-heading */
.scope-tag {
  display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 999px;
  border: 1px solid var(--border-2); background: rgba(53, 197, 240, 0.10);
  color: var(--accent); font-size: 9px; font-weight: 600; vertical-align: 1px;
}
/* the region bars stay a nationwide comparison; the selected province's own region is lit */
.region-row.active .r-name { color: var(--accent); font-weight: 700; }
.region-row.active .r-val { color: var(--text); }

/* ============================ MAP LAYER MENU ============================ */
/* One ☰ menu for every map layer, as on the Forecast and Impact screens — it replaced a row
 * of loose toggles so this bar stays one line next to the time ladder. Defined here because
 * the Overview screen does not load impact.css, whose copy of these classes is density-scaled
 * and, loading later, keeps winning for the four `.ia` pages. */
.mb-left { position: relative; flex: 0 0 auto; }
.mb-btn {
  display: inline-flex; align-items: center; gap: 6px;
  background: rgba(20,32,51,0.86); border: 1px solid var(--border-2); color: #cfe0f0;
  border-radius: 7px; padding: 6px 11px; font-family: inherit; font-size: 12px;
  cursor: pointer; white-space: nowrap; backdrop-filter: blur(8px);
}
.mb-btn:hover { border-color: var(--accent-dim); }
.mb-btn[aria-expanded="true"] { border-color: var(--accent); }
.mb-ic { font-size: 13px; }
.pc-caret { color: var(--muted); font-size: 10px; }
/* (the shell-wide `[hidden]` rule at the top of the file hides this until it is opened) */
.layers-panel {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 20; width: 214px;
  background: rgba(13,23,37,0.97); border: 1px solid var(--border-2); border-radius: 9px;
  padding: 8px; box-shadow: 0 22px 50px -16px rgba(0,0,0,0.9); backdrop-filter: blur(8px);
}
.lp-title { font-size: 9px; letter-spacing: 0.1em; color: var(--faint); padding: 2px 4px 6px; }
.lp-row {
  display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 6px;
  cursor: pointer; font-size: 12px; color: #c6d8ea;
}
.lp-row:hover { background: #17273d; }
.lp-row input { accent-color: var(--accent); cursor: pointer; }

/* ============================ TIME ANIMATION ============================ */
/* The lead-hour ladder every screen shares (ตอนนี้ · +1 · +3 · +6 · +12 · +24 · +72 · 7 วัน).
 * Lives here rather than in one page's sheet because the Overview and Source screens do not
 * load impact.css — that sheet redeclares these same classes against its own `--fs` density
 * scale and, loading later, keeps winning for the four `.ia` pages. So the values below are
 * literal (they are what impact.css resolves to at its default --fs: 13px) and this block is
 * the definition for every page that has no density scale of its own.
 *
 * `margin-left: auto` is what puts the group at the top *right* of the map bar. */
.mb-time { display: flex; align-items: center; gap: 8px; margin-left: auto; min-width: 0; }
.mb-time-label {
  font-size: 9px; letter-spacing: 0.1em; color: var(--accent);
  white-space: nowrap; flex: 0 0 auto;
}
.time-steps { display: flex; gap: 3px; min-width: 0; overflow: hidden; }
.ts-btn {
  background: rgba(17,29,44,0.86); border: 1px solid var(--border); color: var(--muted);
  border-radius: 6px; padding: 4px 9px; font-family: inherit; font-size: 11px;
  font-weight: 600; cursor: pointer; white-space: nowrap; font-variant-numeric: tabular-nums;
}
.ts-btn:hover { color: #dbe9f7; border-color: var(--border-2); }
.ts-btn.active { background: var(--accent); border-color: var(--accent); color: #04121a; }
.ts-btn.loading { opacity: 0.55; cursor: progress; }
.mb-play {
  background: rgba(20,32,51,0.86); border: 1px solid var(--border-2); color: var(--accent);
  border-radius: 7px; width: 30px; height: 26px; cursor: pointer; font-size: 12px;
  flex: 0 0 auto; font-family: inherit;
}
.mb-play:hover { border-color: var(--accent); }
.mb-play.playing { background: var(--accent); color: #04121a; }

/* On the Overview screen the group floats over the map instead of sitting inside a solid map
 * bar, so it carries its own backing — the same translucent chrome the neighbouring controls
 * use. #map-controls wraps, so with the day pill, the layer select, four toggles and 3D VIEW
 * already on the first line this group takes a tidy right-aligned second line of its own. */
#map-controls .mb-time {
  background: rgba(11, 17, 25, 0.86);
  border: 1px solid var(--border-2);
  border-radius: 8px;
  padding: 5px 8px;
  backdrop-filter: blur(8px);
}

/* `#time-steps` is overflow:hidden — a step that does not fit vanishes silently rather than
 * wrapping. This bar shares its line with the layer menu, so it sheds its own chrome as the
 * viewport narrows, least informative first, and the steps give up last:
 *   1. the "TIME ANIMATION" caption   (the ladder is self-evident)
 *   2. the long step labels           (+12 ชม. -> +12h; the tooltip keeps the wording)
 *   3. button padding
 * scripts/check_layout_fit.py is what turns a silent loss into a failing check. */
#map-controls .ts-s { display: none; }
@media (max-width: 1600px) {
  #map-controls .mb-time-label { display: none; }
}
@media (max-width: 1500px) {
  #map-controls .ts-l { display: none; }
  #map-controls .ts-s { display: inline; }
}
@media (max-width: 1340px) {
  #map-controls .ts-btn { padding: 4px 6px; }
  #map-controls { gap: 6px; }
}
/* .ctrl-select survives for the Source screen's wind-height picker; the Overview screen's
   loose toggles and its dead 3D/display-mode controls moved into the layer menu above. */
.ctrl-select select {
  background: rgba(11,17,25,0.86); border: 1px solid var(--border-2); color: var(--text);
  border-radius: 8px; padding: 6px 10px; font-size: 12px; cursor: pointer;
}
.ctrl-toggle {
  display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text);
  background: rgba(11,17,25,0.86); border: 1px solid var(--border-2); border-radius: 8px; padding: 6px 10px;
}
.ctrl-toggle input { accent-color: var(--accent); }
#map-side-btns {
  position: absolute; top: 58px; right: 12px; z-index: 6;
  display: flex; flex-direction: column; gap: 6px;
}
#map-side-btns button {
  width: 34px; height: 34px; border-radius: 8px; border: 1px solid var(--border-2);
  background: rgba(11,17,25,0.86); color: var(--text); cursor: pointer; font-size: 14px;
}
#map-side-btns button:hover { border-color: var(--accent); }

/* Bottom-left stack: the layer menu above the colour key (index.html #map-bl). The menu
   used to sit on the top control row; it moved here because both it and the legend answer
   "what am I looking at", while the row above answers "when" — and that row was wrapping to
   two lines once TIME & DATA's mode selector joined it.

   The stack spans the map's full height but is bottom-justified, so the legend keeps the
   exact position it always had and the menu grows *upward* out of it. Spanning the height
   rather than hugging the bottom is what lets the open panel clamp itself: it is a flex
   item in this column, so it can never grow past the map's top edge — it scrolls instead.
   Anchoring it absolutely to the button (the obvious version) put its top 85px above the
   map at 1366x800, where `#center`'s overflow:hidden then ate the first three layers with
   no trace. */
.map-bl {
  position: absolute; left: 12px; top: 12px; bottom: 12px; z-index: 6;
  display: flex; flex-direction: column; justify-content: flex-end;
  align-items: flex-start; gap: 8px;
  /* the column is mostly empty space over the map — clicks and drags must pass through it */
  pointer-events: none;
}
.map-bl > * { pointer-events: auto; }
/* column-reverse puts the panel above its button while keeping the button's markup where
   app.js expects it; min-height:0 is what allows the shrink that makes the clamp work */
.map-bl .mb-left {
  position: static; display: flex; flex-direction: column-reverse;
  align-items: flex-start; gap: 6px; min-height: 0; flex: 0 1 auto;
}
/* in the stack the panel is laid out, not floated over the button */
.map-bl .layers-panel {
  position: static; width: 214px; max-height: 100%; overflow-y: auto;
  box-shadow: 0 22px 50px -16px rgba(0, 0, 0, 0.9);
}
/* the legend keeps `.panel.floating`'s chrome but gives up its own absolute placement to
   the stack — otherwise it would sit under the menu rather than below it */
.map-bl #legend { position: static; left: auto; bottom: auto; flex: 0 0 auto; }
#legend { left: 12px; bottom: 12px; padding: 10px 12px; }
#legend h2 { margin-bottom: 8px; color: var(--text); font-size: 10px; }
#legend .item { display: flex; align-items: center; gap: 8px; font-size: 11px; margin: 4px 0; }
#legend .swatch { width: 12px; height: 12px; border-radius: 50%; }
#legend .lg-range { font-variant-numeric: tabular-nums; min-width: 44px; }
#legend .lg-th { color: var(--muted); }
/* the continuous (viridis) scale from §09 — a ramp, not six swatches, because the fills on
   the map are interpolated rather than binned */
#legend .lg-ramp { width: 168px; height: 10px; border-radius: 3px; border: 1px solid var(--border-2); }
#legend .lg-ramp-scale {
  display: flex; justify-content: space-between; margin-top: 4px;
  font-size: 10px; color: var(--muted); font-variant-numeric: tabular-nums;
}

/* ============ EARLY WARNING ============ */
.ew-row { display: flex; align-items: center; gap: 6px; }
.gauge { width: 120px; height: 100px; flex: none; }
.ew-meta { font-size: 11px; }
.ew-label { color: var(--muted); font-size: 10px; margin-top: 6px; }
.ew-level { font-size: 22px; font-weight: 800; color: var(--warn); }
.ew-areas { color: var(--text); line-height: 1.4; }
.ew-status { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--muted);
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); flex-wrap: wrap; }
.ew-status .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--good); }
.ew-status.warn .dot { background: var(--warn); }
.ew-status.danger .dot { background: var(--danger); }
.ew-sys { margin-left: auto; color: var(--faint); }
.ew-sys.warn { color: var(--warn); }
.ew-sys.danger { color: var(--danger); }

/* the alert count is a control, not a caption: it opens the detail drawer. Disabled
   (no alert in scope) it drops back to looking like the caption it replaced. */
.ew-alerts-btn {
  display: inline-flex; align-items: center; gap: 4px;
  background: transparent; border: 1px solid transparent; border-radius: 999px;
  color: var(--text); font: inherit; font-size: 11px; padding: 2px 8px 2px 6px;
  cursor: pointer; transition: background .15s ease, border-color .15s ease;
}
.ew-alerts-btn:hover, .ew-alerts-btn:focus-visible {
  background: rgba(255,255,255,.06); border-color: var(--border-2); outline: none;
}
.ew-alerts-btn:disabled { color: var(--muted); cursor: default; padding-left: 0; }
.ew-alerts-btn:disabled .ew-chev { display: none; }
.ew-chev { color: var(--faint); font-size: 13px; line-height: 1; }

/* ---- early-warning detail drawer (shell classes come from .why-drawer) ---- */
.al-eyebrow { color: var(--warn); }
.al-summary { margin-bottom: 14px; }
.al-sum-line { font-size: 17px; font-weight: 800; color: var(--text); }
.al-sum-sub { font-size: 11px; color: var(--muted); margin-top: 4px; line-height: 1.5; }
.al-legend { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.al-tag {
  font-size: 10px; font-weight: 700; padding: 3px 8px; border-radius: 999px;
  border: 1px solid var(--border-2); background: rgba(255,255,255,.04); color: var(--muted);
}
.al-tag.danger { color: var(--danger); border-color: rgba(231,76,60,.5); }
.al-tag.warn { color: var(--warn); border-color: rgba(230,126,34,.5); }
.al-tag.spot { color: var(--accent); border-color: rgba(53,197,240,.45); }
.al-tag.far { color: var(--faint); }

.al-list { display: flex; flex-direction: column; gap: 10px; }
.al-empty { font-size: 12px; color: var(--muted); padding: 18px 0; }
.al-card {
  width: 100%; text-align: left; display: block;
  background: var(--panel-2); border: 1px solid var(--border); border-left-width: 3px;
  border-radius: 12px; padding: 12px 14px; color: var(--text); font: inherit;
  cursor: pointer; transition: border-color .15s ease, transform .12s ease;
}
.al-card:hover, .al-card:focus-visible {
  border-color: var(--faint); transform: translateY(-1px); outline: none;
}
.al-card.danger { border-left-color: var(--danger); }
.al-card.warn { border-left-color: var(--warn); }
.al-card.spot { border-left-color: var(--accent); }
.al-head { display: flex; align-items: baseline; gap: 8px; }
.al-name { font-size: 13px; font-weight: 700; flex: 1; min-width: 0; }
.al-prov { font-size: 10px; color: var(--muted); }
.al-peak { font-size: 18px; font-weight: 800; font-variant-numeric: tabular-nums; }
.al-peak-unit { font-size: 10px; color: var(--muted); margin-left: 2px; }
.al-msg { font-size: 11px; color: var(--muted); margin-top: 6px; line-height: 1.5; }
.al-leads { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
.al-lead {
  font-size: 10px; font-variant-numeric: tabular-nums; padding: 2px 7px; border-radius: 6px;
  background: rgba(255,255,255,.05); border: 1px solid var(--border); color: var(--muted);
}
.al-lead.far { border-style: dashed; color: var(--faint); }
.al-cta { font-size: 10px; color: var(--accent); margin-top: 8px; }
.al-foot {
  font-size: 10px; color: var(--faint); line-height: 1.6; margin-top: 16px;
  padding-top: 12px; border-top: 1px solid var(--border);
}

/* ============ SCENARIO ============ */
.sc-field { display: flex; flex-direction: column; gap: 4px; font-size: 11px; color: var(--muted); }
.sc-field select {
  background: var(--panel-2); border: 1px solid var(--border-2); color: var(--text);
  border-radius: 6px; padding: 7px 8px; font-size: 12px;
}
.sc-caption { font-size: 10px; color: var(--muted); margin: 10px 0 6px; }
.sc-results { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; }
.sc-metric { background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; padding: 8px 4px; text-align: center; }
.sc-m-label { font-size: 9px; color: var(--muted); }
.sc-m-val { font-size: 17px; font-weight: 800; margin-top: 2px; color: var(--good); }
.sc-m-val.up { color: var(--danger); }
.sc-m-val.pending { color: var(--muted); font-size: 13px; }
.sc-run {
  margin-top: 10px; width: 100%; background: linear-gradient(90deg, var(--accent-dim), var(--accent));
  color: #04121a; border: none; border-radius: 8px; padding: 9px; font-weight: 700; cursor: pointer; font-size: 12px;
}
.sc-run:hover { filter: brightness(1.1); }
.sc-run:disabled { opacity: .6; cursor: wait; }
.sc-note { font-size: 10px; color: var(--muted); margin-top: 8px; line-height: 1.4; }

/* ============ DATA SOURCE ============ */
.datasource { display: flex; flex-direction: column; gap: 2px; }
.ds-row { display: flex; align-items: center; gap: 8px; font-size: 11px; padding: 5px 0;
  border-bottom: 1px solid var(--border); }
.ds-row:last-child { border-bottom: none; }
.ds-icon { width: 16px; text-align: center; color: var(--muted); }
.ds-name { color: var(--text); }
.ds-sub { color: var(--faint); font-size: 9px; }
.ds-val { margin-left: auto; text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
.ds-val.stale { color: var(--warn); }

/* ============ BOTTOM: 7-DAY ============ */
.forecast7 { display: flex; gap: 6px; flex: 1; }
.fc-card { flex: 1; background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px;
  padding: 6px 2px; text-align: center; display: flex; flex-direction: column; gap: 2px; justify-content: center; }
.fc-card.prov { border-style: dashed; opacity: .82; }
.fc-day { font-size: 10px; color: var(--muted); }
.fc-icon { display: flex; align-items: center; justify-content: center; height: 20px; }
.fc-glyph { display: block; }
.fc-val { font-size: 20px; font-weight: 800; line-height: 1.1; font-variant-numeric: tabular-nums; }
.fc-band { font-size: 8px; color: var(--faint); line-height: 1.1; }
/* The day's high and low sit above and below the mean, small enough to stay secondary:
   one glance reads the average, a second reads how far the day swings around it. The caret
   carries the band colour of its own value, so a peak that crosses into an unhealthy band
   shows up without a second number having to compete with the mean. */
.fc-range { display: flex; align-items: center; justify-content: center; gap: 2px;
  font-size: 9px; line-height: 1.05; color: var(--muted); font-variant-numeric: tabular-nums; }
.fc-range .fc-caret { font-size: 7px; line-height: 1; }
/* fixed cell: both rows come out the same width, so the two carets sit in one column */
.fc-range .fc-num { min-width: 25px; text-align: left; }
.fc-range.na { color: #2b3746; font-size: 8px; letter-spacing: .08em; }
/* hairline separators keep the three figures from reading as one stack of numbers */
.fc-range.hi { padding-bottom: 1px; border-bottom: 1px solid var(--border); margin: 0 6px 1px; }
.fc-range.lo { padding-top: 1px; border-top: 1px solid var(--border); margin: 1px 6px 0; }

/* ============ BOTTOM: WIND ============ */
.wind-canvas { width: 100%; flex: 1; min-height: 0; background: #060a10; border-radius: 6px; }
.wind-scale { display: flex; align-items: center; gap: 6px; font-size: 9px; color: var(--faint); margin-top: 4px; }
.wind-bar { flex: 1; height: 6px; border-radius: 3px;
  background: linear-gradient(90deg, #1b3a6b, #2ecc71, #f1c40f, #e74c3c); }

/* ============ BOTTOM: TREND ============ */
.trend { flex: 1; min-height: 0; }

/* ============ BOTTOM: HOTSPOT ============ */
.hotspot-row { display: flex; gap: 10px; flex: 1; min-height: 0; }
.hotspot-row > div:first-child { display: flex; flex-direction: column; }
.hotspot-count { font-size: 30px; font-weight: 800; color: var(--danger); line-height: 1; }
.hotspot-unit { font-size: 11px; color: var(--muted); }
.hotspot-trend { color: var(--warn); font-weight: 700; }
.hotspot-spark { width: 130px; height: 42px; margin-top: 6px; }
.hotspot-map { flex: 1; border-radius: 6px; overflow: hidden; background: #0a0f16; position: relative; }

/* ============ scrollbars ============ */
#left-rail::-webkit-scrollbar, #right-rail::-webkit-scrollbar { width: 6px; }
#left-rail::-webkit-scrollbar-thumb, #right-rail::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 3px; }

/* ============ responsive ============ */
/* ~1200–1440px: keep the 3-column command-center but narrow the rails and tighten
   the nav so the map keeps room (retina laptops sit here). */
@media (max-width: 1440px) {
  #app { grid-template-columns: 260px minmax(0, 1fr) 260px; }
  #nav .tab { padding: 8px 6px; font-size: 11px; gap: 4px; }
  #nav .tab-icon { display: none; }  /* Thai labels need the width more than the glyphs do */
  .brand h1 { font-size: 16px; }
  .stat-val { font-size: 22px; }
}
/* ≤1366px: the eight Thai labels are wider than the old English caps, so the brand
   tagline goes before the menu does — a clipped label is worse than a missing subtitle. */
@media (max-width: 1366px) {
  .brand .sub { display: none; }
  #nav { margin-left: 0; }
  #nav .tab { padding: 8px 4px; }
}
/* ≤1200px: rails drop below the map (two side-by-side), bottom strip 2×2. */
@media (max-width: 1200px) {
  #app {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: var(--top-h) minmax(60vh, 1fr) auto auto;
    grid-template-areas:
      "top    top"
      "center center"
      "left   right"
      "bottom bottom";
    height: auto; min-height: 100vh; overflow: auto;
  }
  body { overflow: auto; }
  #left-rail, #right-rail { overflow: visible; }
  #bottom-strip { grid-template-columns: 1fr 1fr; grid-auto-rows: 190px; }
  /* last squeeze before the menu is dropped entirely at ≤720px */
  #nav .tab { padding: 8px 3px; font-size: 10.5px; }
}
/* ≤720px: single column top-to-bottom. */
@media (max-width: 720px) {
  #app {
    grid-template-columns: 1fr;
    grid-template-areas: "top" "center" "left" "right" "bottom";
  }
  #bottom-strip { grid-template-columns: 1fr; }
  #nav { display: none; }
}

/* ============================================================= */
/* WHY ENGINE (EP-19) — signature CTA + explainability drawer     */
/* ============================================================= */
:root { --why: #a855f7; --why-dim: #6d28d9; }

/* --- topbar CTA: a glowing accent pill that gently breathes --- */
.why-btn {
  display: inline-flex; align-items: center; gap: 6px;
  background: linear-gradient(135deg, var(--accent), #2b9fd4);
  color: #04141c; border: none; border-radius: 999px;
  font-size: 12px; font-weight: 800; letter-spacing: .03em;
  padding: 7px 14px; cursor: pointer;
  box-shadow: 0 0 0 0 rgba(53,197,240,.5);
  animation: whyPulse 3.2s ease-in-out infinite;
  transition: transform .15s ease, filter .15s ease;
}
.why-btn:hover { transform: translateY(-1px); filter: brightness(1.08); }
.why-btn:active { transform: translateY(0); }
.why-btn-mark { font-size: 13px; }
@keyframes whyPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(53,197,240,.45); }
  50% { box-shadow: 0 0 16px 3px rgba(53,197,240,.28); }
}

/* --- backdrop --- */
.why-backdrop {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(4, 8, 14, .58); backdrop-filter: blur(3px);
  opacity: 0; transition: opacity .3s ease;
}
.why-backdrop.show { opacity: 1; }

/* --- drawer shell: slides in from the right --- */
.why-drawer {
  position: fixed; top: 0; right: 0; z-index: 41;
  width: min(560px, 94vw); height: 100vh;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, #101a28, #0a0f16);
  border-left: 1px solid var(--border-2);
  box-shadow: -24px 0 60px rgba(0,0,0,.55);
  transform: translateX(102%); transition: transform .38s cubic-bezier(.22,.61,.36,1);
}
.why-drawer.show { transform: translateX(0); }

.why-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 18px; border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg, #14202f, transparent);
}
.why-eyebrow { font-size: 10px; font-weight: 800; letter-spacing: .12em; color: var(--why);
  text-transform: uppercase; }
.why-head-ctrl { display: flex; align-items: center; gap: 12px; }
.why-lang { display: flex; border: 1px solid var(--border-2); border-radius: 8px; overflow: hidden; }
.why-lang button {
  background: transparent; border: none; color: var(--muted); cursor: pointer;
  font-size: 11px; font-weight: 700; padding: 5px 11px;
}
.why-lang button.active { background: var(--accent); color: #04141c; }
.why-close {
  background: transparent; border: 1px solid var(--border-2); color: var(--muted);
  width: 28px; height: 28px; border-radius: 8px; cursor: pointer; font-size: 13px;
}
.why-close:hover { color: var(--text); border-color: var(--faint); }

.why-body { padding: 18px; overflow-y: auto; overflow-x: hidden; flex: 1; }

/* --- hero headline --- */
.why-hero { margin-bottom: 16px; }
.why-scope {
  font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .08em;
  margin-bottom: 6px;
}
.why-headline { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.why-prefix { font-size: 17px; font-weight: 700; color: var(--text); }
.why-value {
  font-size: 46px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums;
  color: var(--danger); text-shadow: 0 0 22px currentColor;
}
.why-hunit { font-size: 13px; color: var(--muted); }
.why-band {
  font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 999px;
  background: rgba(255,255,255,.06); border: 1px solid var(--border-2); color: var(--text);
}
.why-valid { font-size: 11px; color: var(--faint); margin-top: 6px; }

/* --- score arcs --- */
.why-scores { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 20px; }
.why-score {
  display: flex; align-items: center; gap: 12px;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; padding: 12px;
}
.why-arc { width: 74px; height: 74px; flex: none; position: relative; }
.why-arc svg { transform: rotate(-90deg); }
.why-arc .arc-num {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 17px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--text);
}
.why-arc .arc-fg { transition: stroke-dashoffset 1.1s cubic-bezier(.22,.61,.36,1); }
.why-score-label { font-size: 12px; font-weight: 700; color: var(--text); }
.why-score-hint { font-size: 10px; color: var(--muted); margin-top: 3px; line-height: 1.35; }

/* --- sections --- */
.why-sec { margin-bottom: 20px; }
.why-sec h3 {
  font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 2px;
}
.why-sub { font-size: 11px; color: var(--muted); margin-bottom: 12px; }

/* --- attribution: donut + factor bars --- */
.why-sources { display: grid; grid-template-columns: 140px 1fr; gap: 14px; align-items: center; }
.why-donut { width: 140px; height: 140px; }
.why-factors { display: flex; flex-direction: column; gap: 9px; }
.why-factor { display: grid; grid-template-columns: 1fr auto; gap: 2px 8px; align-items: center; }
.why-f-name { font-size: 12px; color: var(--text); display: flex; align-items: center; gap: 6px; }
.why-f-dot { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.why-f-en { font-size: 10px; color: var(--faint); }
.why-f-pct { font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums; }
.why-f-track {
  grid-column: 1 / -1; height: 6px; border-radius: 4px; background: var(--panel-2);
  overflow: hidden;
}
.why-f-fill {
  height: 100%; width: 0; border-radius: 4px;
  transition: width 1s cubic-bezier(.22,.61,.36,1);
}

/* --- drivers: evidence chips --- */
.why-drivers { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.why-chip {
  display: flex; align-items: center; gap: 9px;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: 10px; padding: 9px 11px;
}
.why-chip-ic { font-size: 17px; flex: none; }
.why-chip-k { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.why-chip-v { font-size: 13px; font-weight: 700; color: var(--text); }
.why-chip.bad .why-chip-v { color: var(--danger); }
.why-chip.warn .why-chip-v { color: var(--warn); }
.why-chip.good .why-chip-v { color: var(--good); }

/* --- explanation: staggered fade-in bullets --- */
.why-explain { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.why-explain li {
  position: relative; padding-left: 18px; font-size: 13px; line-height: 1.5; color: var(--text);
  opacity: 0; transform: translateY(6px); animation: whyLineIn .5s ease forwards;
}
.why-explain li::before {
  content: "▹"; position: absolute; left: 0; color: var(--accent); font-size: 12px;
}
@keyframes whyLineIn { to { opacity: 1; transform: translateY(0); } }

/* --- footer --- */
.why-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding-top: 12px; border-top: 1px solid var(--border);
  font-size: 10px; color: var(--faint);
}
.why-method { text-transform: uppercase; letter-spacing: .06em; }
.why-inputs {
  color: var(--good); border: 1px solid rgba(46,204,113,.5); border-radius: 4px;
  padding: 0 5px; opacity: .9; font-variant: small-caps;
}
.why-prov {
  color: var(--warn); border: 1px solid var(--warn); border-radius: 4px; padding: 0 5px; opacity: .85;
}
.why-updated { margin-left: auto; }

@media (max-width: 560px) {
  .why-scores, .why-drivers { grid-template-columns: 1fr; }
  .why-sources { grid-template-columns: 1fr; justify-items: center; }
}
