:root {
  --bg: #14161a;
  --panel: rgba(22, 25, 30, 0.88);
  --panel-border: rgba(255, 255, 255, 0.10);
  --text: #e9edf2;
  --muted: #9aa4b1;
  --accent: #7db3ff;
  /* Strike colours, fixed. They match the fills baked into the icon files and
     are used by the legend's age ramps. */
  --cg: #ffb020;      /* cloud-to-ground: orange-yellow */
  --ic: #4da6ff;      /* cloud-to-cloud: blue */

  /* Scales every duration in the arrival sequence. app.js sets it to 1/speed so
     the arrival stays the same fraction of a strike's life at fast playback. */
  --arrive-scale: 1;
}

* { box-sizing: border-box; }

html, body {
  margin: 0; height: 100%;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

#map { position: absolute; inset: 0; background: #2b2f36; }
.leaflet-container { background: #2b2f36; }
.leaflet-control-attribution {
  background: rgba(20, 22, 26, .7) !important;
  color: var(--muted) !important;
  font-size: 10px !important;
}
.leaflet-control-attribution a { color: var(--accent) !important; }

/* ---------- panels ---------- */
.panel {
  position: absolute;
  z-index: 1000;
  background: var(--panel);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--panel-border);
  border-radius: 14px;
  padding: 14px 16px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, .45);
}
.panel-tl { top: 16px; left: 16px; width: 262px; }

.panel-title {
  font-size: 14px; font-weight: 650; letter-spacing: .1px;
  display: flex; align-items: center; gap: 8px; margin-bottom: 12px;
}
.badge {
  font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .6px;
  color: var(--accent);
  border: 1px solid rgba(125, 179, 255, .35);
  background: rgba(125, 179, 255, .12);
  border-radius: 999px; padding: 2px 7px;
}

/* min-height keeps the switch row on the same rhythm as the taller segmented
   control above it, so the two right-hand controls line up. */
.control-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 10px; min-height: 28px;
}
.control-label { color: var(--muted); font-size: 12.5px; }
/* A setting that belongs to the row above it, indented to show that. */
.control-sub .control-label { padding-left: 12px; }
.control-row.is-disabled { opacity: .45; }
.control-row.is-disabled .switch { cursor: default; }

.segmented {
  display: inline-flex; background: rgba(255, 255, 255, .06);
  border-radius: 9px; padding: 2px;
}
.segmented button {
  border: 0; background: transparent; color: var(--muted);
  font: inherit; font-size: 12px; padding: 4px 10px;
  border-radius: 7px; cursor: pointer; transition: background .15s, color .15s;
}
.segmented button.is-active { background: rgba(255, 255, 255, .14); color: var(--text); }

.switch { position: relative; display: flex; align-items: center; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  display: block; flex: none; width: 38px; height: 21px; border-radius: 999px;
  background: rgba(255, 255, 255, .16); transition: background .18s;
}
.switch-thumb {
  display: block; width: 17px; height: 17px; border-radius: 50%;
  background: #fff; margin: 2px; transition: transform .18s;
}
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track .switch-thumb { transform: translateX(17px); }

.radar-status { font-size: 11px; color: var(--muted); display: block; line-height: 1.35; }
.radar-status.is-error { color: #ff9f9f; }

.legend { border-top: 1px solid var(--panel-border); margin-top: 12px; padding-top: 12px; }
.legend-title {
  font-size: 11px; text-transform: uppercase; letter-spacing: .7px;
  color: var(--muted); margin-bottom: 8px;
}
.legend-title:not(:first-child) { margin-top: 14px; }
.legend-title .muted { text-transform: none; letter-spacing: 0; opacity: .8; }
.legend-item { display: flex; align-items: center; gap: 9px; font-size: 12.5px; margin-bottom: 7px; }
.legend-item em { font-style: normal; color: var(--muted); font-variant-numeric: tabular-nums; }
.legend-icon { width: 16px; height: 16px; display: inline-flex; }
.legend-icon img { width: 100%; height: 100%; display: block; }

.age-ramp { display: flex; gap: 4px; }
.age-ramp + .age-ramp { margin-top: 4px; }
.age-ramp span { flex: 1; height: 7px; border-radius: 3px; }
.age-ramp-cg span { background: var(--cg); }
.age-ramp-ic span { background: var(--ic); }
.age-scale {
  display: flex; justify-content: space-between;
  font-size: 10px; color: var(--muted); margin-top: 4px;
}

/* ---------- timeline ---------- */
.timeline {
  position: absolute; z-index: 1000;
  left: 50%; transform: translateX(-50%);
  bottom: 22px; width: min(760px, calc(100% - 32px));
  display: flex; align-items: center; gap: 14px;
  background: var(--panel);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--panel-border);
  border-radius: 16px; padding: 12px 16px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, .45);
}

.play-btn {
  flex: none; width: 40px; height: 40px; border-radius: 50%;
  border: 0; cursor: pointer; background: var(--accent); color: #0d1420;
  display: grid; place-items: center; transition: transform .12s, filter .15s;
}
.play-btn:hover { filter: brightness(1.08); }
.play-btn:active { transform: scale(.94); }
.play-btn svg { width: 22px; height: 22px; fill: currentColor; grid-area: 1 / 1; }
.play-btn .icon-pause { display: none; }
.play-btn.is-playing .icon-play { display: none; }
.play-btn.is-playing .icon-pause { display: block; }

.timeline-main { flex: 1; min-width: 0; }

#slider {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 6px; border-radius: 999px; outline: none;
  background: rgba(255, 255, 255, .14);
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-size: 100% 100%;
  cursor: pointer;
}
#slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px; border-radius: 50%;
  background: #fff; border: 3px solid var(--accent);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .5); cursor: grab;
}
#slider::-moz-range-thumb {
  width: 16px; height: 16px; border-radius: 50%; border: 3px solid var(--accent);
  background: #fff; cursor: grab;
}

.timeline-meta {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 8px; font-size: 11px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.time-pill {
  background: rgba(255, 255, 255, .10);
  border: 1px solid var(--panel-border);
  border-radius: 999px; padding: 3px 12px;
  color: var(--text); font-size: 12px; font-weight: 600;
}

.speed { flex: none; display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted); }
#density-ramp-select, .speed select {
  background: rgba(255, 255, 255, .08); color: var(--text);
  border: 1px solid var(--panel-border); border-radius: 8px;
  padding: 4px 6px; font: inherit; font-size: 12px; cursor: pointer;
}

/* ---------- density surface ---------- */
/* Positioned by Leaflet via setPosition, so it must not carry its own offsets. */
.density-canvas { position: absolute; left: 0; top: 0; pointer-events: none; }

.density-ramp { height: 9px; border-radius: 3px; }
/* One flat block per BOM band: the scale is stepped, so no gradient. */
.radar-ramp { display: flex; height: 9px; border-radius: 3px; overflow: hidden; }
.radar-ramp span { flex: 1; }
.density-scale {
  display: flex; justify-content: space-between;
  font-size: 10px; color: var(--muted); margin-top: 4px;
}

/* ---------- strike markers ----------
   The glyph is an <img> pointing at a file in icons/, so CSS cannot
   reach the path inside it — fill and stroke are fixed by the file. So the
   white arrival flash is a separate white layer masked to the glyph
   silhouette. The dark edge is a drop-shadow filter, which does work on an
   <img> because it follows its alpha. ---- */
/* Leaflet sizes the outer .strike-icon from iconSize; this fills it, so the
   absolutely-positioned glyph below has a box to resolve inset: 0 against. */
.strike-marker { position: relative; width: 100%; height: 100%; will-change: opacity; }

/* The dark edge is not a halo — it is what separates a bolt from the density
   surface painted underneath it, and it matters more now that the markers are
   30% smaller. Kept deliberately. */
.strike-glyph {
  position: absolute; inset: 0;
  filter: drop-shadow(0 0 1.5px rgba(0, 0, 0, .85));
  will-change: transform, opacity;
}
.strike-glyph img { width: 100%; height: 100%; display: block; }

/* The white flash: a solid white layer clipped to the glyph's silhouette, since
   the img's own colours cannot be animated. Inside .strike-glyph so the drop
   animation carries it along with the icon. */
.strike-glyph::after {
  content: ""; position: absolute; inset: 0;
  background: #fff; opacity: 0; pointer-events: none;
  -webkit-mask: url(icons/strike-mask.svg) center / contain no-repeat;
          mask: url(icons/strike-mask.svg) center / contain no-repeat;
}

/* Scale from the point the strike actually hits, so it stays put while the
   glyph grows: the tip for cloud-to-ground, the centre for cloud-to-cloud. */
.strike-cloud-to-ground .strike-glyph { transform-origin: 50% 100%; }
.strike-cloud-to-cloud .strike-glyph { transform-origin: 50% 50%; }

/* ---- the arrival ----
   Fires once, on strikes landing at the newest timestamp. Two parts run
   together: the glyph falls into place, and a white layer over it fades away so
   the strike reads white first and then settles into its type colour. There is
   no ring or glow around it. Every duration is scaled by --arrive-scale
   so the whole sequence still fits inside the strike's life at 4x playback. ---- */
.strike-arrive .strike-glyph {
  animation: strike-drop calc(1s * var(--arrive-scale)) cubic-bezier(.22, 1, .36, 1) both;
}
/* The white-to-colour handover. The glyph's own colour lives inside the SVG
   file and cannot be animated, so the white is a separate masked layer on top
   that fades off to reveal the blue or the yellow underneath. Slower than the
   drop, so the colour arrives after the motion has settled. */
.strike-arrive .strike-glyph::after {
  animation: strike-flash calc(1.5s * var(--arrive-scale)) ease-out both;
}

/* Falls a few pixels into place as it lands — enough to read as downward
   motion, not so much that it looks like the marker is in the wrong spot.
   Scaled down with the marker itself. */
@keyframes strike-drop {
  0%   { transform: translateY(-3.5px) scale(1.35); opacity: 0; }
  30%  { opacity: 1; }
  55%  { transform: translateY(0) scale(1.06); }
  100% { transform: translateY(0) scale(1); opacity: 1; }
}
/* Holds solid white long enough to register as white, then crosses to the type
   colour rather than cutting. */
@keyframes strike-flash {
  0%, 30% { opacity: 1; }
  100%    { opacity: 0; }
}

/* The white arrival still resolves to the type colour — that is the strike's
   identity, not decoration — it just gets there without moving. */
@media (prefers-reduced-motion: reduce) {
  .strike-arrive .strike-glyph { animation: none; transform: none; opacity: 1; }
}

.strike-popup .leaflet-popup-content-wrapper {
  background: var(--panel); color: var(--text);
  border: 1px solid var(--panel-border); border-radius: 10px;
  box-shadow: 0 8px 26px rgba(0, 0, 0, .5);
}
.strike-popup .leaflet-popup-tip { background: var(--panel); }
.strike-popup .leaflet-popup-content { margin: 10px 12px; font-size: 12px; }
.strike-popup b { display: block; margin-bottom: 3px; }
.strike-popup .k { color: var(--muted); }

@media (max-width: 720px) {
  .panel-tl { width: calc(100% - 32px); }
  .legend { display: none; }
  .timeline { bottom: 14px; padding: 10px 12px; gap: 10px; }
  .speed label { display: none; }
}
