* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { margin: 0; height: 100%; overflow: hidden; font-family: system-ui, sans-serif; }
/* Fixed to the visual viewport so it tracks the mobile browser's dynamic
   toolbar (avoids the height:100% dead-space gap at the bottom). */
#map { position: fixed; inset: 0; background: #0b0f14; }

.hidden { display: none !important; }

/* ---- Stale-data banner (top center) ---- */
#stale-banner {
  position: fixed; z-index: 550;
  top: calc(12px + env(safe-area-inset-top));
  left: 50%; transform: translateX(-50%);
  max-width: min(90vw, 440px);
  background: rgba(176, 108, 20, 0.96); color: #fff;
  padding: 8px 14px; border-radius: 10px;
  font-size: 12.5px; line-height: 1.35; text-align: center;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
}

/* ---- Auto-update toast (bottom center, above the controls) ---- */
#toast {
  position: fixed; z-index: 700; left: 50%; transform: translateX(-50%) translateY(10px);
  bottom: calc(96px + env(safe-area-inset-bottom));
  background: rgba(43, 108, 255, 0.96); color: #fff;
  padding: 9px 16px; border-radius: 999px; font-size: 12.5px; white-space: nowrap;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  opacity: 0; pointer-events: none; transition: opacity 250ms ease, transform 250ms ease;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---- Top-right overflow menu ---- */
#menu {
  position: fixed; z-index: 600;
  top: calc(12px + env(safe-area-inset-top));
  right: calc(12px + env(safe-area-inset-right));
  display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
}
#menu-btn, .menu-circle {
  width: 40px; height: 40px; border-radius: 50%; padding: 0;
  border: 1px solid #3a4657; background: rgba(17, 22, 30, 0.9); color: #e8edf3;
  cursor: pointer; backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center; line-height: 1;
}
#menu-btn { font-size: 24px; transition: transform 190ms ease; }
#menu.open #menu-btn { transform: rotate(90deg); }
.menu-circle { font-size: 18px; }
.menu-circle svg { width: 18px; height: 18px; fill: currentColor; display: block; }
#info-btn { font: italic 700 19px/1 Georgia, "Times New Roman", serif; }
#menu-btn:hover, .menu-circle:hover { background: rgba(30, 40, 55, 0.95); }
/* Disabled only while a geolocation fix is in flight. */
.menu-circle:disabled { opacity: 0.55; cursor: default; }
.menu-circle:disabled:hover { background: rgba(17, 22, 30, 0.9); }

/* The closed menu still occupies its full height — the rows are transparent and
   shifted, not removed — and a transparent container still swallows taps. That
   made a 256x281 box in the top corner permanently dead: fires under it could
   never be tapped, however many times you tried, and a popup's close button
   under it was equally stuck. Worse on a phone, where that box is a third of the
   screen. So nothing in the menu takes pointer events except the rows when it is
   open, and the button that opens it. */
#menu, #menu-items { pointer-events: none; }
#menu-btn { pointer-events: auto; }
#menu-items { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
/* Items expand out of the three-dots button, and collapse back into it. */
#menu-items > * {
  opacity: 0; transform: translateY(-14px) scale(0.8); transform-origin: top right;
  pointer-events: none;
  transition: opacity 150ms ease, transform 200ms cubic-bezier(.2, .8, .3, 1);
}
#menu.open #menu-items > * { opacity: 1; transform: none; pointer-events: auto; }
#menu.open #menu-items > *:nth-child(1) { transition-delay: 30ms; }
#menu.open #menu-items > *:nth-child(2) { transition-delay: 65ms; }
#menu.open #menu-items > *:nth-child(3) { transition-delay: 100ms; }
#menu.open #menu-items > *:nth-child(4) { transition-delay: 135ms; }
#menu.open #menu-items > *:nth-child(5) { transition-delay: 170ms; }
#menu.open #menu-items > *:nth-child(6) { transition-delay: 205ms; }
#menu.open #menu-items > *:nth-child(7) { transition-delay: 240ms; }
#menu.open #menu-items > *:nth-child(8) { transition-delay: 275ms; }
@media (prefers-reduced-motion: reduce) {
  #menu-items > *, #menu-btn { transition: none; }
}
/* Search pill and the locate button share the menu's top row: both answer
   "show me somewhere", one by name and one by GPS. */
#search-row { display: flex; align-items: center; gap: 10px; }

/* Place search: same floating pill as the other menu items, wrapping a text
   field and a submit button. */
#search-item {
  display: flex; align-items: center; gap: 6px;
  background: rgba(17, 22, 30, 0.9); border: 1px solid #3a4657;
  border-radius: 21px; padding: 4px 4px 4px 14px;
  backdrop-filter: blur(4px);
}
#search-item input {
  width: 150px; min-width: 0; background: transparent; border: 0; outline: none;
  color: #e8edf3; font-family: inherit; font-size: 12.5px;
}
#search-item input::placeholder { color: #7d8b9c; }
#search-item[aria-busy="true"] { opacity: 0.6; }
#search-btn {
  width: 30px; height: 30px; border-radius: 50%; border: 0; flex: none;
  background: #2b6cff; color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
#search-btn svg { width: 15px; height: 15px; fill: currentColor; display: block; }

#opacity-item {
  display: flex; align-items: center; gap: 9px;
  background: rgba(17, 22, 30, 0.9); border: 1px solid #3a4657;
  border-radius: 21px; padding: 9px 15px; color: #b7c2d0; font-size: 12px;
  backdrop-filter: blur(4px); white-space: nowrap;
}
#opacity-item input { width: 100px; accent-color: #2b6cff; height: 4px; }

/* Model selector: same floating pill as the opacity control, wrapping a
   segmented toggle whose buttons are generated from models.json. */
#model-item, #speed-item, #fires-item, #aq-item {
  display: flex; align-items: center; gap: 9px;
  background: rgba(17, 22, 30, 0.9); border: 1px solid #3a4657;
  border-radius: 21px; padding: 6px 7px 6px 15px; color: #b7c2d0; font-size: 12px;
  backdrop-filter: blur(4px); white-space: nowrap;
}
#model-item .seg, #speed-item .seg { background: rgba(0, 0, 0, 0.2); }
#model-item .seg button, #speed-item .seg button { padding: 5px 11px; }

/* ---- Info modal ---- */
#info-modal {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
}
.info-backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.5); }
.info-card {
  position: relative; z-index: 1;
  width: min(440px, calc(100vw - 32px)); max-height: 82vh; overflow: hidden;
  background: #141a22; color: #e8edf3; border: 1px solid #2b3644;
  border-radius: 14px; line-height: 1.5; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.info-body { max-height: 82vh; overflow-y: auto; padding: 22px 22px 18px; }
.info-card h2 { margin: 0 0 6px; font-size: 19px; padding-right: 30px; }
.info-card h3 {
  margin: 18px 0 6px; font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.05em; color: #8fa0b4;
}
.info-card p { margin: 6px 0; font-size: 14px; color: #cdd6e0; }
.info-card strong { color: #e8edf3; }
#info-run p:first-child { margin-top: 8px; }
.info-close {
  position: absolute; top: 10px; right: 12px; z-index: 2;
  width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
  background: #1b232e; border: 1px solid #2b3644; border-radius: 8px;
  color: #9aa8b8; font-size: 20px; line-height: 1; cursor: pointer;
}
.info-close:hover { color: #e8edf3; }
.info-foot { margin-top: 16px !important; font-size: 11px !important; color: #6b7888 !important; }

/* ---- Legend (inside the info card) ---- */
#legend { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; font-size: 11px; color: #b7c2d0; }
#legend .lg-label { width: 100%; margin-bottom: 5px; font-size: 13px; color: #cdd6e0; }
#legend .swatch { width: 28px; height: 12px; }
#legend .tick { min-width: 28px; text-align: center; }

/* ---- Bottom control bar ----
   Fixed-size widget: full screen width on mobile, capped and anchored to the
   bottom-left on larger screens. Same two-row layout at every size. */
#controls {
  position: fixed; z-index: 500;
  left: calc(12px + env(safe-area-inset-left));
  bottom: calc(12px + env(safe-area-inset-bottom));
  width: calc(100% - 24px - env(safe-area-inset-left) - env(safe-area-inset-right));
  max-width: 416px;
  background: rgba(17, 22, 30, 0.9); color: #e8edf3; backdrop-filter: blur(6px);
  border-radius: 14px;
  padding: 12px 16px;
  display: flex; flex-direction: column; gap: 10px;
}
.controls-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: nowrap; }
.controls-bottom { display: flex; align-items: center; gap: 12px; }

.seg { display: inline-flex; border: 1px solid #3a4657; border-radius: 8px; overflow: hidden; flex: none; }
.seg button {
  background: transparent; color: #b7c2d0; border: 0; padding: 6px 12px;
  font-size: 12.5px; cursor: pointer;
}
.seg button.active { background: #2b6cff; color: #fff; }

.playbtn {
  width: 40px; height: 40px; border-radius: 50%; border: 0; flex: none;
  background: #2b6cff; color: #fff; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.playbtn svg { width: 16px; height: 16px; fill: currentColor; display: block; }

#slider { flex: 1; accent-color: #2b6cff; height: 4px; min-width: 0; }

#time-label {
  font-variant-numeric: tabular-nums; font-size: 12px; white-space: nowrap;
  color: #cdd6e0; margin-left: auto; flex: 0 0 auto;
}
/* Only the time keeps fixed-width fields so it doesn't jitter each frame: the
   hour right-aligns so the colon stays put, and AM/PM is padded so the time
   zone after it stays put. The date is a plain string. */
#time-label .num { display: inline-block; min-width: 1.5em; text-align: right; }
#time-label .ap  { display: inline-block; min-width: 1.7em; text-align: center; }
/* Box the offset sign so the label does not shift when it crosses − to +. */
#time-label .sign { display: inline-block; min-width: 0.7em; text-align: right; }

/* On narrow phones, tighten the variable toggle (rather than shrinking the
   date/time text) so the date/time stays readable and on the same line. */
@media (max-width: 460px) {
  #var-toggle button { padding: 6px 8px; font-size: 12px; }
  #time-label { font-size: 12px; }
}

/* "You are here" dot dropped by the location button. Georeferenced like any
   marker, so it holds its spot while panning; it lives only in memory, so a
   reload clears it. */
.locate-dot {
  border-radius: 50%;
  background: #2b6cff;
  border: 3px solid #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35), 0 2px 6px rgba(0, 0, 0, 0.45);
}

/* Keep the required Esri/OSM attribution present but unobtrusive. */
.leaflet-control-attribution {
  background: rgba(11, 15, 20, 0.35) !important;
  color: #5a6472 !important;
  font-size: 9px !important;
  line-height: 1.4 !important;
  padding: 0 5px !important;
}
.leaflet-control-attribution a { color: #6f7d8e !important; text-decoration: none !important; }

/* Active-fires layer.

   The marker is a flame drawn as an SVG in a divIcon, not a canvas circle. Two
   reasons: it reads as a fire at a glance, and — the practical one — a DOM
   element gives a real hit area. The canvas circles were only tappable within
   their drawn radius, which at low zoom was a ~3 px target and missed constantly
   on touch. The box below is at least 30 px whatever the flame's size.

   Sizes are classes rather than inline styles: the CSP forbids style attributes,
   and Leaflet writes the box size through the CSSOM, which it does not. */
/* Purely visual: taps pass straight through to the map, which then picks the
   fire whose centre is nearest. A marker element that swallowed its own clicks
   would let the hit *box* decide, and a box is far bigger than the flame in it —
   so tapping one flame could open a neighbour's popup. */
.fire-icon { pointer-events: none; }
/* Centred by absolute positioning, not flex: Leaflet's stylesheet loads after
   this one (the map injects it at runtime) and its .leaflet-marker-icon rule
   resets display to block, so the flex centring silently did nothing. The flame
   then sat 4 px left of its box, and the popup arrow — which points at the
   marker's true position — appeared to the right of it every time. */
.fire-icon svg {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
}
/* One flame path with the inner cut-out, so the shape still reads as fire at
   13 px instead of as a red blob. The light outline keeps it legible on the dark
   basemap as well as the light one. */
/* No drop-shadow filter: a filtered SVG is repainted on every map frame, and
   200 of them made panning while zoomed out stutter. The light outline on the
   path does the same job for free. */
.fire-icon svg { overflow: visible; }
.fire-icon .flame-body { fill: #e8331a; stroke: #fff6ed; stroke-width: 1.1px; paint-order: stroke; }
.fire-1 svg { width: 13px; height: 13px; }
.fire-2 svg { width: 17px; height: 17px; }
.fire-3 svg { width: 22px; height: 22px; }
.fire-4 svg { width: 28px; height: 28px; }
.fire-5 svg { width: 35px; height: 35px; }

/* The legend dot is round and hot-coloured so it doesn't read as another band
   of the smoke ramp, which is a forecast. */
#legend .fire-swatch {
  border-radius: 50%; background: #ff3b1f; border: 1.5px solid #fff;
  width: 12px; height: 12px;
}
#legend .lg-sep { opacity: .45; margin: 0 6px 0 2px; }

/* Switch: a plain on/off control for a layer that is simply on or off. The
   button is the control (no hidden checkbox), so the CSP-safe styling is just
   two classes and aria-checked carries the state. */
/* The whole pill toggles, so it takes the pointer cursor and its label does not
   select on a double tap. */
#fires-item, #aq-item {
  padding: 6px 12px 6px 15px; cursor: pointer; user-select: none;
}
#fires-item:hover, #aq-item:hover { border-color: #4c5b70; }
.switch {
  width: 38px; height: 22px; border-radius: 11px; padding: 0; flex: none;
  border: 1px solid #3a4657; background: rgba(0, 0, 0, 0.28); cursor: pointer;
  position: relative; transition: background .15s ease, border-color .15s ease;
}
.switch .knob {
  position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
  border-radius: 50%; background: #8b97a6; transition: transform .15s ease, background .15s ease;
}
.switch[aria-checked="true"] { background: #2b6cff; border-color: #2b6cff; }
.switch[aria-checked="true"] .knob { transform: translateX(16px); background: #fff; }
.switch:focus-visible { outline: 2px solid #2b6cff; outline-offset: 2px; }

/* Air quality: the six EPA AQI categories as a compact ramp, matching the dots
   on the map. */
#legend .aq-swatch { width: 11px; height: 11px; border: 2px solid transparent; }
