/* Interface and geography themes; forecast colours remain physically fixed. */
:root {
  /* The sticky section headers need something opaque behind them: a translucent
     panel colour lets the scrolling rows show through the header. */
  --panel-solid: #15232d;
  --selected: rgba(184,210,227,.12);
  --hover: rgba(184,210,227,.07);
  --map-halo: #172c3d;
  --map-ink: #e0e9ed;
  --ink: #f2f2f0;
  --ink-2: #a9a8a2;
  --muted: #7d7c77;
  --panel: rgba(19, 32, 43, 0.9);
  --edge: rgba(255, 255, 255, 0.12);
  --accent: #3987e5;
  --preview: #f2cd66;
  --preview-ink: #382c0a;
  color-scheme: dark;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0; background: #0b0f16; color: var(--ink); overflow: hidden;
  font: 13px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
}
/* Crosshair at rest: the globe is something you read a value off, and the
   cursor should say "point at a place". It becomes a closed hand only while you
   are actually turning it. */
#stage { display: block; width: 100vw; height: 100vh; cursor: crosshair; }
#stage:active { cursor: grabbing; }

/* Panel captions track the WebGL viewports. DOM text stays crisp and selectable
   where text drawn into the canvas would not. */
.panel-labels { position: fixed; inset: 0; pointer-events: none; }
.plabel {
  position: absolute; padding: 0 12px;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  font-size: 14px; font-weight: 600; letter-spacing: -0.01em; text-align: center;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.85);
}
/* Own line, so a wrapped model name cannot strand it in the margin. */
.ptag { font-size: 10px; font-weight: 500; letter-spacing: 0.05em; color: var(--muted); }
/* In compare the per-panel captions name the models, so the single title would
   just repeat the leftmost one. */
body.comparing .tl .model { display: none; }
/* Three columns of chrome plus a readout is too much at narrow widths; the
   readout is the least useful thing when the panels are already labelled. */
@media (max-width: 900px) { body.comparing .readout { display: none; } }

/* Chrome floats; nothing takes a slice out of the viewport. */
/* Above the wind and track canvases. Without a z-index the chrome sits at
   auto (0) while .tracks sits at 2, so track lines painted straight over the
   storm labels and the readout. */
.hud { position: fixed; inset: 0; pointer-events: none; z-index: 3; }
.hud > * { pointer-events: auto; }
.corner { position: absolute; display: flex; gap: 8px; }
.tl { top: 18px; left: 20px; flex-direction: column; gap: 2px; max-width: calc(100vw - 92px); }
.tr { top: 18px; right: 20px; flex-wrap: wrap; justify-content: flex-end; max-width: 62vw; }
.br { bottom: 78px; right: 20px; flex-direction: column; }

/* Both controls keep a compact, stable bottom row. Extra playback controls
   open above it without stretching the colour legend. */
/* The end labels are the first thing to go when there is no room for them. */
@media (max-width: 470px) { .ticks span:nth-child(2) { display: none; } }

.round {
  width: 36px; height: 36px; border-radius: 50%; cursor: pointer;
  background: var(--panel); border: 1px solid var(--edge); color: var(--ink);
  font-size: 15px; line-height: 1; backdrop-filter: blur(8px);
  display: grid; place-items: center;
}
.round:hover { border-color: var(--accent); color: var(--accent); }
.round.off { opacity: 0.35; }

.site-heading { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 10px; }
.site-heading h1 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -0.02em; }
.site-title { border: 0; padding: 0; background: none; color: inherit; font: inherit; letter-spacing: inherit; cursor: pointer; }
.site-title:hover { text-decoration: underline; text-decoration-color: var(--preview); text-underline-offset: 4px; }
.site-title:focus-visible { outline: 2px solid var(--preview); outline-offset: 3px; border-radius: 2px; }
.research-preview { display: inline-flex; align-items: center; gap: 5px; padding: 2px 6px; border: 1px solid var(--preview); border-radius: 12px; background: var(--preview); font: inherit; font-size: 10px; line-height: 1.5; color: var(--preview-ink); white-space: nowrap; cursor: pointer; }
.research-preview svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.5; }
.research-preview:hover, .research-preview[aria-expanded="true"] { background: #ffe095; }
.research-preview:focus-visible { outline: 2px solid var(--preview); outline-offset: 3px; }
.model { font-size: 12px; font-weight: 600; color: var(--ink); }
.cycle { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 8px; font-size: 12px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.cycle > span { white-space: nowrap; }
.cycle .badge, .cycle .wind-note { margin-left: 0; }
.badge {
  display: inline-block; margin-left: 4px; padding: 1px 7px; border-radius: 18px;
  font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase;
  border: 1px solid var(--edge); color: var(--muted);
}
.badge.fresh { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }

#diff-toggle[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); }
.segmented {
  display: flex; border: 1px solid var(--edge); border-radius: 18px; overflow: hidden;
  background: var(--panel); backdrop-filter: blur(8px);
}
.segmented button {
  appearance: none; border: 0; background: none; color: var(--muted);
  padding: 6px 13px; font: inherit; font-size: 12px; cursor: pointer;
}
.segmented button[aria-pressed="true"] { background: var(--accent); color: #fff; }

.pill {
  appearance: none; background: var(--panel); color: var(--ink);
  border: 1px solid var(--edge); border-radius: 18px; padding: 6px 13px;
  font: inherit; font-size: 12px; cursor: pointer; backdrop-filter: blur(8px);
}

/* The legend sits over the globe, so it needs its own ground to stay readable.
   Absolute, not relative: it places itself against the timeline, and it is also
   the containing block the ramp menu opens from. */
/* min-width: 0 is load-bearing: a grid item defaults to min-width: auto, so
   without it the colour bar refuses to go below its content width and spills
   out of its track straight into the timeline. */
.legend-wrap {
  position: relative; grid-column: -2; justify-self: end;
  min-width: 0; max-width: 100%;
}
/* The bar is the control: clicking it opens the ramps, previewed on the field
   currently on screen rather than as abstract gradients. */
.legend {
  display: flex; flex-direction: column; justify-content: center;
  width: clamp(108px, 26vw, 214px); max-width: 100%; height: 48px;
  padding: 6px 12px; text-align: left;
  background: var(--panel); border: 1px solid var(--edge); border-radius: 12px;
  backdrop-filter: blur(8px); cursor: pointer; font: inherit; color: inherit;
}
.legend:hover, .legend[aria-expanded="true"] { border-color: var(--accent); }

.ramp-menu {
  position: absolute; bottom: calc(100% + 8px); left: 0; width: 214px;
  padding: 6px; border-radius: 10px;
  background: var(--panel); border: 1px solid var(--edge); backdrop-filter: blur(10px);
}
.ramp-group {
  font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--muted); padding: 6px 8px 4px;
}
.ramp-item {
  display: block; width: 100%; padding: 5px 8px 6px; border: 0; border-radius: 7px;
  background: none; color: var(--ink-2); font: inherit; font-size: 12px;
  text-align: left; cursor: pointer;
}
.ramp-item:hover { background: rgba(255, 255, 255, 0.07); color: var(--ink); }
.ramp-item[aria-checked="true"] { color: var(--ink); }
.ramp-item canvas { display: block; width: 100%; height: 6px; border-radius: 3px; margin-top: 4px; }
.ramp-item .name { display: flex; justify-content: space-between; }
.ramp-item .tick { color: var(--accent); }
#legend { display: block; width: 100%; border-radius: 3px; }
.ticks {
  display: flex; justify-content: space-between; margin-top: 4px;
  font-size: 10px; color: var(--muted); font-variant-numeric: tabular-nums;
}

.readout {
  /* Wider than it was: model names and place names were wrapping to two lines
     in 232px, which made a fixed-shape panel jump about as the values changed. */
  position: absolute; top: 104px; left: 20px; width: 304px; padding: 10px 0 10px 12px;
  z-index: 2;
  background: var(--panel); border: 1px solid var(--edge); border-radius: 10px;
  backdrop-filter: blur(8px); font-variant-numeric: tabular-nums;
}
.readout .where { color: var(--ink); margin-bottom: 7px; }
/* The value table doubles as the chart's legend: identity is never colour alone,
   every series is directly labelled with its own reading. */
.readout .key {
  display: inline-block; width: 7px; height: 7px; border-radius: 2px; margin-right: 6px;
}
#plume { display: block; width: 100%; height: 92px; margin-top: 8px; }
.plume-axis {
  display: flex; justify-content: space-between; margin-top: 2px;
  font-size: 9.5px; color: var(--muted); font-variant-numeric: tabular-nums;
}
.readout table { width: 100%; border-collapse: collapse; font-size: 12px; }
.readout td { padding: 2px 0; color: var(--ink-2); }
.readout td:last-child { text-align: right; color: var(--ink); }
[hidden] { display: none !important; }

/* Bottom row. Three columns so the timeline is centred on the *frame*, not
   among its siblings -- the colour bar sits in the right column and the two
   1fr columns stay equal. Below the width where that stops fitting the grid
   drops to two columns and the timeline simply takes what is left. */
.dock {
  position: absolute; left: 20px; right: 20px; bottom: 20px; z-index: 4;
  /* minmax(0,1fr), not 1fr: a plain 1fr track refuses to go below its content,
     so the colour bar's column grew past its share and shoved the timeline off
     centre by 40px on a landscape phone. The bar shrinks to its track instead. */
  /* The middle track carries the timeline's width, not the timeline: inside an
     `auto` track a percentage width resolves against the track, which sizes to
     content, so the bar stayed at its content width on any screen. */
  display: grid;
  grid-template-columns:
    /* 880 rather than 1180: past that the scrub is wider than any forecast needs
       and the row stops reading as a control. */
    minmax(0, 1fr) minmax(0, clamp(320px, 52vw, 880px)) minmax(0, 1fr);
  align-items: end; gap: 12px;
  pointer-events: none;
}
.dock > * { pointer-events: auto; }

/* Fixed-height playback shares the colour key's baseline. */
.bar {
  position: relative; grid-column: 2; min-width: 0;
  display: flex; align-items: center; gap: 12px;
  width: 100%; height: 48px; padding: 6px 8px;
  background: var(--panel); border: 1px solid var(--edge); border-radius: 12px;
  backdrop-filter: blur(10px);
  transition: border-color .15s ease;
}
/* The same lift the colour key, the search bar and the rail buttons take. The
   dock sits over the map the whole time, so at rest the bar states the time and
   otherwise keeps quiet; pointing at it brings the controls up. The timestamp
   stays at --ink-2 rather than --muted -- if you have to hover to read the time,
   the bar has failed at its one job. */
.bar:hover, .bar:focus-within { border-color: var(--accent); }
.play {
  width: 32px; height: 32px; flex: none; border-radius: 50%; cursor: pointer;
  border: 0; color: #fff; font-size: 11px; display: grid; place-items: center;
  background: color-mix(in srgb, var(--accent) 62%, transparent);
  transition: background .15s ease;
}
.bar:hover .play, .bar:focus-within .play { background: var(--accent); }
.play:hover { filter: brightness(1.12); }

/* Time and track share the row: the timestamp names what you are looking at,
   the track changes it. */
.timeline {
  flex: 1; min-width: 0; font-variant-numeric: tabular-nums;
  display: flex; align-items: center; gap: 14px;
}
.clocks { display: flex; align-items: baseline; gap: 8px; white-space: nowrap; flex: none; }
#valid-label {
  font-size: 15px; font-weight: 600; line-height: 20px; color: var(--ink-2);
  letter-spacing: -0.01em; transition: color .15s ease;
}
.bar:hover #valid-label, .bar:focus-within #valid-label { color: var(--ink); }
#lead-label { font-size: 10px; color: var(--muted); white-space: nowrap; }
/* Track and its dates are one block: the scale sits directly under the thing it
   measures, inside the bar, and both are inset by the thumb's radius so a tick,
   a date and the playhead all mean the same position. */
.track-group { flex: 1 1 240px; min-width: 60px; display: flex; flex-direction: column; justify-content: center; }
.timeline-rail { position: relative; display: flex; align-items: center; height: 18px; }

/* A drawn timeline, not a browser widget. Everything else here is custom -- the
   panels, the pills, the icons -- so a stock range input was the one control
   that looked unfinished. The input keeps the behaviour (keyboard, drag, aria);
   the track, fill and day marks are ours, and the dates below share the same
   7px inset so a tick, a label and the playhead all mean one position. */
#scrub {
  position: relative; z-index: 1; flex: 1; min-width: 0; height: 18px; margin: 0;
  cursor: pointer; -webkit-appearance: none; appearance: none; background: transparent;
}
#scrub::-webkit-slider-runnable-track { height: 18px; background: transparent; border: 0; }
#scrub::-webkit-slider-thumb {
  -webkit-appearance: none; width: 14px; height: 14px; margin-top: 2px;
  border: 0; border-radius: 50%; background: #fff;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent),
              0 1px 3px rgba(0, 0, 0, .65);
  transition: transform .12s ease, box-shadow .15s ease;
}
.bar:hover #scrub::-webkit-slider-thumb {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 55%, transparent),
              0 1px 3px rgba(0, 0, 0, .65);
}
#scrub:hover::-webkit-slider-thumb, #scrub:active::-webkit-slider-thumb { transform: scale(1.12); }
#scrub:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px var(--accent); }
#scrub::-moz-range-track { height: 18px; background: transparent; border: 0; }
#scrub::-moz-range-thumb {
  width: 14px; height: 14px; border: 0; border-radius: 50%; background: #fff;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 55%, transparent);
}

.rail-track {
  position: absolute; left: 7px; right: 7px; top: 50%; height: 6px;
  margin-top: -3px; border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--ink) 15%, transparent); pointer-events: none;
}
.rail-fill {
  position: absolute; inset: 0 auto 0 0; width: var(--fill, 0%);
  background: color-mix(in srgb, var(--accent) 62%, transparent);
  border-radius: 999px; transition: background .15s ease;
}
.bar:hover .rail-fill, .bar:focus-within .rail-fill { background: var(--accent); }
.rail-ticks { position: absolute; inset: 0; }
.rail-ticks i {
  position: absolute; top: 0; width: 1px; height: 100%;
  background: color-mix(in srgb, var(--ink) 35%, transparent);
}

/* Floated out of flow. Without this it rendered as an inline block inside the
   row, so hovering the track injected a lump of text into the bar and shoved
   every control sideways -- which is what "the hover behaves weirdly" was. */
#time-preview {
  position: absolute; z-index: 3; transform: translateX(-50%);
  padding: 6px 9px; border-radius: 6px;
  background: var(--panel-solid); border: 1px solid var(--edge);
  color: var(--ink); font-size: 11px; white-space: nowrap; pointer-events: none;
}

.timeline-more { display: flex; align-items: center; gap: 8px; flex: none; }
/* Step, play, step -- the arrangement every player uses, because the thing you
   press most belongs between the two things that nudge it. */
.transport { display: flex; align-items: center; gap: 4px; flex: none; }
.tl-step {
  width: 26px; height: 26px; background: none; border: 0; color: var(--ink-2);
  font: 20px/20px system-ui; cursor: pointer; border-radius: 6px;
}
.tl-step:hover { background: var(--selected); color: var(--ink); }
.tl-step:disabled { opacity: .3; cursor: default; }
.timeline-speed { display: flex; align-items: center; font-size: 10px; color: var(--muted); }
.timeline-speed > span { display: none; }
.timeline-speed .pill { padding: 3px 8px; font-size: 11px; }

.tl-step:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

#timeline-days { position: relative; height: 12px; margin: 0 7px; }

#timeline-days button {
  position: absolute; top: 0; height: 12px; padding: 0; border: 0;
  background: transparent; color: var(--muted); font: inherit; font-size: 9px;
  line-height: 12px; white-space: nowrap; cursor: pointer;
  transform: translateX(-50%);
}
#timeline-days button:first-child { transform: none; }
#timeline-days button:last-child { transform: translateX(-100%); }
#timeline-days button:hover, #timeline-days button[aria-current="true"] { color: var(--ink); }
@media (max-width: 720px) {
  .dock { grid-template-columns: 1fr auto; }
  .bar { grid-column: 1; width: 100%; column-gap: 6px; padding: 6px 8px; }
}
@media (max-width: 470px) {
  .legend { width: 92px; padding: 6px 8px; }
  #valid-label { font-size: 12px; line-height: 16px; }
  .bar .play { width: 28px; height: 28px; }
  .timeline-speed .pill { padding-inline: 6px; }
  #lead-label { display: none; }
  .tl-step { width: 22px; }
}
@media (max-width: 360px) {
  .dock { left: 12px; right: 12px; }
}

/* Popovers: the map keeps the screen, these sit over a corner of it. */
.sheet {
  position: fixed; z-index: 5; width: 300px; max-height: 70vh; overflow: auto;
  padding: 12px; border-radius: 12px;
  background: var(--panel); border: 1px solid var(--edge); backdrop-filter: blur(12px);
}
/* #goto-panel is placed by .suggest, beside the rail rather than over it. */
#info-panel { bottom: 108px; right: 72px; width: 380px; max-width: calc(100vw - 92px); padding: 16px; overscroll-behavior: contain; }
#goto-input {
  width: 100%; padding: 7px 10px; margin-bottom: 6px; font: inherit; font-size: 13px;
  color: var(--ink); background: rgba(255,255,255,0.06);
  border: 1px solid var(--edge); border-radius: 8px;
}
.sheet-list { max-height: 46vh; overflow: auto; }
.sheet-group {
  font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--muted); padding: 8px 8px 4px;
}
.sheet-item {
  display: block; width: 100%; padding: 6px 8px; border: 0; border-radius: 7px;
  background: none; color: var(--ink-2); font: inherit; font-size: 13px;
  text-align: left; cursor: pointer;
}
.sheet-item:hover { background: rgba(255,255,255,0.08); color: var(--ink); }
.about-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.sheet.info h2 { font-size: 16px; font-weight: 600; color: var(--ink); margin: 0; }
.sheet.info .preview-label { display: inline-block; font-size: 11px; color: var(--preview-ink); background: var(--preview); padding: 2px 8px; border-radius: 12px; margin: 5px 0 16px; }
.about-copy { font-size: 13px; line-height: 1.6; color: var(--ink-2); }
.about-copy p { margin: 0 0 12px; }
.about-copy strong { font-weight: 600; color: var(--ink); }
.sheet.info h3 { font-size: 11px; font-weight: 600; color: var(--ink); margin: 16px 0 8px; }
.about-sources { font-size: 12px; line-height: 1.6; color: var(--ink-2); }
.about-sources p { margin: 3px 0; }
.sheet.info a { color: inherit; text-underline-offset: 3px; }
.sheet.info a:hover { color: var(--ink); }
.source-details { border-top: 1px solid var(--edge); margin-top: 16px; padding-top: 12px; }
.source-details summary { cursor: pointer; font-size: 12px; color: var(--ink); }
.source-details summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.source-details #field-grid { font-size: 11px; line-height: 1.6; color: var(--muted); }
.owb table { width: 100%; border-collapse: collapse; font-size: 12px; font-variant-numeric: tabular-nums; }
.owb td { padding: 3px 0; border-bottom: 1px solid rgba(255,255,255,0.07); color: var(--ink-2); }
.owb td:last-child { text-align: right; }
.owb tr.here td { color: var(--ink); font-weight: 600; }
.owb .rank { color: var(--muted); width: 20px; }
.owb-note { font-size: 11px; line-height: 1.5; color: var(--muted); margin: 8px 0 0; }
.attrib p { margin: 0 0 8px; font-size: 10.5px; line-height: 1.6; color: var(--muted); }
.attrib a { color: var(--muted); }
.readout tr.spread td { color: var(--muted); }

.loading {
  position: absolute; inset: 0; display: grid; place-content: center; gap: 10px;
  background: #05070c; text-align: center; color: var(--ink-2);
}
/* Background fill reports along the bottom bar rather than blocking the view. */
.progress {
  position: absolute; left: 14px; right: 14px; bottom: 4px; height: 2px;
  border-radius: 2px; background: rgba(255, 255, 255, 0.10); overflow: hidden;
}
.progress::after {
  content: ""; display: block; height: 100%; width: var(--p, 0%);
  background: var(--accent); transition: width 120ms linear;
}
/* The positioned bar contains its loading indicator; the dock anchors the row. */

/* Covers the swap between two different views. */
.fade {
  position: fixed; inset: 0; z-index: 3; pointer-events: none;
  background: #05070c; opacity: 0; transition: opacity 200ms ease;
}
.fade.on { opacity: 1; }

/* Anchored to a coordinate but drawn as DOM, so it keeps one size at every
   zoom -- an in-scene marker grows with the surface it is pinned to. */
.pin {
  position: fixed; left: 0; top: 0; z-index: 4; pointer-events: none;
  margin-left: -9px; margin-top: -24px;   /* tip of the pin sits on the point */
}
.pin svg { display: block; fill: var(--accent); filter: drop-shadow(0 1px 3px rgba(0,0,0,0.7)); }
.pin .pin-hole { fill: #05070c; }
.pin-pulse {
  position: absolute; left: 9px; top: 24px; width: 10px; height: 10px;
  margin: -5px 0 0 -5px; border-radius: 50%;
  background: var(--accent); animation: pin-pulse 2.4s ease-out infinite;
}
@keyframes pin-pulse {
  0%   { transform: scale(0.6); opacity: 0.55; }
  70%  { transform: scale(3.6); opacity: 0; }
  100% { transform: scale(3.6); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .pin-pulse { animation: none; opacity: 0.4; } }

/* Our own ask, before the browser's. The permission prompt only fires if the
   answer here is yes, so it arrives with a reason attached. */
.locate-ask {
  position: fixed; right: 20px; bottom: 200px; width: 244px; z-index: 6;
  padding: 12px 14px; border-radius: 12px;
  background: var(--panel); border: 1px solid var(--edge); backdrop-filter: blur(12px);
}
.locate-ask p { margin: 0 0 10px; font-size: 13px; color: var(--ink); }
.locate-ask .row { display: flex; gap: 8px; }
.locate-ask small { display: block; margin-top: 9px; font-size: 10.5px; color: var(--muted); }
.mini {
  flex: 1; padding: 6px 10px; border-radius: 8px; cursor: pointer; font: inherit; font-size: 12px;
  background: transparent; border: 1px solid var(--edge); color: var(--ink-2);
}
.mini:hover { color: var(--ink); border-color: var(--accent); }
.mini.primary { background: var(--accent); border-color: var(--accent); color: #fff; }

/* The pin names where it landed. Constant size like the pin itself, and pushed
   clear of the head so it never sits on the marker. */
.pin-label {
  pointer-events: auto; cursor: default;
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translate(-50%, -6px);
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(12, 16, 24, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: #e8edf6;
  font-size: 12px;
  line-height: 1.35;
  white-space: nowrap;
  pointer-events: none;
  backdrop-filter: blur(6px);
}

/* Second line in a search hit: which Springfield this is. */
.sheet-item { display: flex; align-items: baseline; gap: 8px; }
.sheet-sub {
  margin-left: auto;
  color: rgba(232, 237, 246, 0.5);
  font-size: 11px;
  white-space: nowrap;
}

/* Storm markers. Anchored to the map but drawn as DOM, like the location pin,
   so they keep their size at every zoom. */
/* No z-index: the markers are map decoration, so the timeline, legend and
   readout all paint over them rather than a storm dot floating on the chrome.
   pointer-events must lose to nothing -- this covers the whole frame, and .hud
   > * would otherwise make it swallow every drag on the globe. */
.storms { position: fixed; inset: 0; pointer-events: none !important; }
/* A zero-size anchor sitting exactly on the storm, with the name floated above
   it. Tracks run broadly horizontally, so a label beside the marker has a track
   line through it wherever it sits in the stack -- above, the line is not there.
   A flex column will not do this: the box still grows downward from the anchor,
   which put the label level with the marker and the dot below it. */
.storm { position: absolute; top: 0; left: 0; width: 0; height: 0; }

/* The centre pressure is the one number that says whether this is a storm worth
   looking at, so it rides along with the dot rather than needing a hover. */
.storm-p {
  position: absolute; left: 0; bottom: 13px; transform: translateX(-50%);
  font-size: 10px; font-variant-numeric: tabular-nums; color: var(--ink);
  padding: 2px 7px; border-radius: 999px;
  background: rgba(12, 16, 24, 0.88); border: 1px solid var(--edge);
  backdrop-filter: blur(4px);
  white-space: nowrap;
}
.storm.advisory .storm-p { top: 0; bottom: auto; left: 18px; transform: translateY(-50%); border-radius: 6px; }
.storm.advisory.flip-label .storm-p { left: -18px; transform: translate(-100%, -50%); }
.storm.advisory small { display: block; padding-top: 2px; color: var(--ink-2); font-size: 9px; font-weight: 400; }
#storm-toggle[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }

/* Storm detail on hover. The map stays quiet at rest -- a name or a pressure
   per storm -- and everything else lives here. */
.storm-tip {
  position: fixed; z-index: 6; width: 214px; padding: 9px 11px;
  pointer-events: none;
  background: var(--panel); border: 1px solid var(--edge); border-radius: 10px;
  backdrop-filter: blur(10px); font-variant-numeric: tabular-nums;
}
.tip-head { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; }
.tip-sub { font-size: 11px; color: var(--ink-2); margin-bottom: 6px; }
.storm-tip table { width: 100%; border-collapse: collapse; font-size: 12px; }
.storm-tip td { padding: 1px 0; color: var(--ink-2); }
.storm-tip td:last-child { text-align: right; color: var(--ink); }
/* Says whose forecast this is. A borrowed name must not read as a borrowed
   track -- NHC names the storm, the model draws the path. */
.tip-note {
  margin-top: 7px; padding-top: 6px; border-top: 1px solid var(--edge);
  font-size: 10px; line-height: 1.4; color: var(--muted);
}
.storm-p.named { font-weight: 600; letter-spacing: -0.01em; font-size: 11px; }

/* The controls that hold the state. Off-screen rather than display:none, so
   they stay focusable and a screen reader still gets real form elements while
   the rail renders the same options as icons. */
.a11y-controls {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* --- the search bar ------------------------------------------------------ */
/* Opens showing where you are looking, the way a map app does, rather than an
   empty box you have to know to press. */
.locbar {
  /* Flush with the rail's own right edge. They never share a horizontal band --
     the rail starts below this bar and ends above the dock -- so lining the
     edges up costs nothing and reads as one column of chrome. */
  position: absolute; top: 18px; right: 20px; z-index: 4;
  display: flex; align-items: center; gap: 8px;
  min-width: 232px; max-width: min(320px, 46vw); padding: 8px 14px;
  background: var(--panel); border: 1px solid var(--edge); border-radius: 999px;
  color: var(--ink-2); font: inherit; font-size: 13px; text-align: left;
  cursor: pointer; backdrop-filter: blur(10px);
}
.locbar:hover, .locbar[aria-expanded="true"] { border-color: var(--accent); color: var(--ink); }
.locbar span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.locbar .ico { flex: none; }
/* Pinned to the far edge rather than trailing the text, so the control has a
   fixed shape whatever place name is in it. */
.locbar .find-ico { margin-left: auto; }
.locbar { overflow: hidden; }
.locbar .pin-ico { color: var(--accent); }
/* Collapsed, the bar behaves like a rail button: a circle that grows its own
   pill leftward. It is anchored by its right edge, so it grows away from the
   frame and nothing else moves. The detached chip was the odd one out -- that
   treatment belongs to the view capsule alone, where a growing box would deform
   its container. What survives the collapse is the magnifier: a lone pin would
   read as a status light rather than something to press. */
@media (max-width: 620px) {
  /* gap: 0 as well as padding: 0 -- the hidden label still sits in the flex row,
     so its gap survived the collapse and pushed the glyph 4px off centre in a
     circle that was 48px instead of 38. */
  .locbar { min-width: 0; width: auto; height: 38px; padding: 0; gap: 0; }
  /* Hidden while it is just a circle; back as soon as the pill opens *and*
     there is a place to point at, so an expanded bar showing a place still
     shows what the place is. */
  .locbar .pin-ico { display: none; }
  .locbar[data-has-place="1"]:hover .pin-ico,
  .locbar[data-has-place="1"]:focus-visible .pin-ico,
  .locbar[data-has-place="1"][aria-expanded="true"] .pin-ico {
    display: block; margin-left: 12px;
  }
  /* An 18px glyph in a 38px box, by margin rather than button padding, so the
     icon stays centred in the collapsed circle. */
  .locbar .find-ico { margin: 0 10px; }
  .locbar #locbar-label {
    flex: none; max-width: 0; opacity: 0; padding-left: 0;
    transition: max-width 0.22s cubic-bezier(0.2, 0, 0, 1), opacity 0.16s ease,
                padding-left 0.22s cubic-bezier(0.2, 0, 0, 1);
  }
  .locbar:hover #locbar-label, .locbar:focus-visible #locbar-label,
  .locbar[aria-expanded="true"] #locbar-label {
    max-width: min(58vw, 240px); opacity: 1; padding-left: 14px;
  }
}

/* --- the icon rail ------------------------------------------------------- */
.rail {
  /* Centred in the space between the header and the timeline rather than in the
     window, so a short window pushes it down instead of under the search bar. */
  position: absolute; top: 74px; bottom: 108px; right: 20px;
  display: flex; flex-direction: column; align-items: flex-end;
  justify-content: center; gap: 8px;
  z-index: 4; pointer-events: none;
  /* Visible, not scrolling: the hover labels live outside the buttons now and
     any clipping would cut them off. It does not need to scroll -- a short
     window gets compact buttons, and a very short one wraps to two columns. */
  overflow: visible;
}
.rail > * { pointer-events: auto; flex: none; }

.rail-gap { height: 14px; }
/* A plain rail button grows its own pill leftward on hover -- label first, icon
   last, so the icon never moves. The button widens, which is fine because it is
   the only thing in its row and the column is right-aligned. */
.rail-btn {
  position: relative;
  display: flex; align-items: center; justify-content: flex-end;
  height: 38px; padding: 0; border-radius: 999px; flex: none;
  background: var(--panel); border: 1px solid var(--edge); color: var(--ink-2);
  font: inherit; font-size: 12px; cursor: pointer; backdrop-filter: blur(8px);
  overflow: hidden;
}
.rail-ico { flex: none; width: 36px; display: grid; place-items: center; }
.rail-label {
  max-width: 0; opacity: 0; white-space: nowrap; padding-left: 0;
  transition: max-width 0.22s cubic-bezier(0.2, 0, 0, 1), opacity 0.16s ease,
              padding-left 0.22s cubic-bezier(0.2, 0, 0, 1);
}
.rail-btn:hover, .rail-btn:focus-visible, .rail-btn[aria-expanded="true"] {
  border-color: var(--accent); color: var(--ink);
}
/* Above its neighbours while open: in the wrapped two-column rail the growing
   pill was passing behind the icons in the next column. */
.rail-btn:hover, .rail-btn:focus-visible, .rail-btn[aria-expanded="true"] { z-index: 2; }
.rail-seg:has(.rail-btn:hover) { z-index: 2; }
.rail-btn:hover .rail-label,
.rail-btn:focus-visible .rail-label,
.rail-btn[aria-expanded="true"] .rail-label { max-width: 160px; opacity: 1; padding-left: 14px; }

/* The view capsule is the exception. Growing a half inside a shared container
   deforms the container into an L, so its halves keep a fixed circle and the
   label floats free of the box instead. */
.rail-seg .rail-btn { overflow: visible; justify-content: center; }
.rail-seg .rail-label {
  position: absolute; right: calc(100% + 12px); top: 50%;
  max-width: none; padding: 6px 12px; border-radius: 999px;
  background: var(--panel); border: 1px solid var(--edge); backdrop-filter: blur(8px);
  color: var(--ink); pointer-events: none;
  transform: translateY(-50%) translateX(6px);
  transition: opacity 0.14s ease, transform 0.18s cubic-bezier(0.2, 0, 0, 1);
}
.rail-seg .rail-btn:hover .rail-label,
.rail-seg .rail-btn:focus-visible .rail-label {
  opacity: 1; padding-left: 12px; transform: translateY(-50%) translateX(0);
}
.rail-btn[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); }
.rail-btn.off { opacity: 0.4; }
/* Motion is decoration here; the label still appears, it just does not slide. */
@media (prefers-reduced-motion: reduce) { .rail-label { transition: none; } }

.ico { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.5;
       stroke-linecap: round; stroke-linejoin: round; }
.ico .fill { fill: currentColor; stroke: none; }

/* Panels open beside the rail rather than over the middle of the map. */
.rail-sheet { top: 50%; right: 72px; transform: translateY(-50%); width: 254px; }
.rail-sheet .sheet-item[aria-checked="true"] { color: var(--ink); }
.rail-sheet .tick { color: var(--accent); margin-left: auto; }

/* --- the view segment ---------------------------------------------------- */
/* Both modes in one pill. The selected half glows rather than merely darkening,
   because at 38px the difference between two greys is not a difference. */
.rail-seg {
  display: flex; flex-direction: column; align-items: flex-end;
  border: 1px solid var(--edge); border-radius: 999px;
  background: var(--panel); backdrop-filter: blur(8px);
}
/* Concentric, not coincident: the container is padded and each half is a full
   pill inside it, so the selected shape sits at a constant inset from the outer
   shape all the way round. Two pills sharing an edge never line up -- their
   curves meet at different radii and the seam shows on hover. */
.rail-seg { padding: 3px; }
.rail-seg .rail-btn {
  border: 0; background: none; backdrop-filter: none;
  width: 32px; height: 32px;
}
.rail-seg .rail-ico { width: 32px; }
.rail-seg .rail-btn[aria-pressed="true"] {
  color: #fff;
  background: var(--accent);
  box-shadow: 0 0 12px -2px var(--accent);
}
.rail-seg .rail-btn[aria-pressed="true"] .rail-label { color: #fff; }
.rail-seg:has(.rail-btn:hover) { border-color: var(--accent); }

/* --- the suggestion list ------------------------------------------------- */
/* Anchored under the search bar rather than floating in a corner, and empty
   until you type: with 7,342 cities in the list, a fixed menu of everything is
   not a menu. */
.suggest {
  position: absolute; top: 62px; right: 20px; left: auto;
  width: min(320px, calc(100vw - 40px)); max-height: 56vh;
}
.suggest input { width: 100%; }
.suggest .sheet-list:empty { display: none; }
.suggest-hint { padding: 10px 8px 4px; font-size: 12px; color: var(--muted); }

/* A short window gets a compact rail before it gets a scrolling one. Last in
   the file on purpose: a media query adds no specificity, so anywhere above the
   base .rail-btn rule it would simply lose -- which is how it was first
   written, and why the rail stayed full size on a landscape phone. */
/* The full-size rail needs 508px of buttons plus 158px of inset -- 666px of
   window. This was 620px, which left a 46px band where the rail simply
   overflowed its own bounds, and it drifted there because controls were added
   after the number was set. Measured, with margin, rather than guessed. */
@media (max-height: 740px) {
  .rail { gap: 5px; top: 66px; bottom: 108px; }
  /* Height only. Fixing the width here pinned a plain button to a circle, so
     its expanding label had nowhere to go and printed over its own icon. */
  .rail-btn { height: 32px; }
  .rail-ico { width: 32px; }
  .rail-seg .rail-btn { width: 28px; height: 28px; }
  .rail-seg .rail-ico { width: 28px; }
  .rail-gap { height: 8px; }
}

/* Shorter still -- a phone on its side -- and one column cannot hold nine
   controls at any sane size. Wrap into a second column rather than scroll:
   a control you have to discover by scrolling a rail is a control nobody finds.
   wrap-reverse so the new column grows inward, away from the screen edge. */
/* And the compact rail has its own floor, for the same reason. */
@media (max-height: 620px) {
  .rail { flex-wrap: wrap-reverse; align-content: center; column-gap: 8px; overflow: visible; }
  .rail-gap { display: none; }
  /* Wrapped, a growing button re-flows the columns and every icon in the rail
     jumps. So wrapped mode uses the capsule's mechanism for everything: fixed
     circles, labels floating free of the box. */
  .rail-btn { width: 32px; overflow: visible; justify-content: center; }
  .rail-label {
    position: absolute; right: calc(100% + 9px); top: 50%; max-width: none;
    padding: 6px 12px; border-radius: 999px; pointer-events: none;
    background: var(--panel); border: 1px solid var(--edge); backdrop-filter: blur(8px);
    color: var(--ink); transform: translateY(-50%) translateX(6px);
  }
  .rail-btn:hover .rail-label, .rail-btn:focus-visible .rail-label,
  .rail-btn[aria-expanded="true"] .rail-label {
    padding-left: 12px; transform: translateY(-50%) translateX(0);
  }
}

/* The colour bar is a button and does not look like one, so it says what it
   does on hover -- same chip as the rail, on the other side of the frame. */
.legend-tip {
  position: absolute; bottom: calc(100% + 9px); right: 0;
  padding: 6px 12px; border-radius: 999px; white-space: nowrap;
  background: var(--panel); border: 1px solid var(--edge); backdrop-filter: blur(8px);
  color: var(--ink); font-size: 12px; pointer-events: none;
  opacity: 0; transform: translateY(6px);
  transition: opacity 0.14s ease, transform 0.18s cubic-bezier(0.2, 0, 0, 1);
}
.legend-wrap:hover .legend-tip { opacity: 1; transform: translateY(0); }
.legend-wrap:has(.ramp-menu:not([hidden])) .legend-tip { opacity: 0; }

/* The timeline's own controls need to say they are pressable. */
#speed:hover { border-color: var(--accent); color: var(--accent); }


/* A second, smaller segmented control -- it sits inside a line of metadata, not
   in the chrome, so it has to be quieter than the ones in the rail. */
/* No left margin: it sits on its own row now, and 6px of inherited indent is
   exactly enough to look misaligned against the title above it. */
.segmented.tiny { display: inline-flex; }
.segmented.tiny button { padding: 2px 8px; font-size: 10px; letter-spacing: 0.03em; }

/* The time-zone control and what it will do. */
.clock-row { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.segmented.tiny { display: inline-flex; }
.clock-tip {
  font-size: 11px; color: var(--muted); white-space: nowrap;
  opacity: 0; transform: translateX(-4px);
  transition: opacity 0.14s ease, transform 0.18s cubic-bezier(0.2, 0, 0, 1);
}
.clock-row:hover .clock-tip, .clock-row:focus-within .clock-tip {
  opacity: 1; transform: translateX(0);
}
.clock-row.just-pressed .clock-tip { opacity: 0; }
.clock-row:hover .segmented { border-color: var(--accent); }

/* The chosen half of the view capsule keeps its accent when its label is out,
   so the label reads as belonging to the selected mode rather than floating
   free of it. */
.rail-seg .rail-btn[aria-pressed="true"] .rail-label {
  background: var(--accent); border-color: var(--accent); color: #fff;
}

/* The searched-place marker. Same shape as the location pin, a different colour
   and no pulse -- the pulse means "this is live", and a bookmark is not. */
.pin.mark svg { fill: #d9a441; }
.pin.mark .pin-hole { fill: #0c1018; }

/* The wind overlay sits between the globe and every piece of chrome. */
.wind { position: fixed; inset: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 1; }

/* A rail control that cannot run here stays visible and says why, rather than
   disappearing -- the reason ("HRES only") is the useful part. */
.rail-btn:disabled { cursor: default; }
.rail-btn:disabled:hover { border-color: var(--edge); color: var(--ink-2); }

/* Above the wind, below every piece of chrome. */
.tracks { position: fixed; inset: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 2; }

/* Says whose wind is on the screen, when it is not the model on the screen. */
.wind-note {
  margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  border: 1px solid var(--edge); color: var(--muted);
  font-size: 10px; letter-spacing: 0.03em; white-space: nowrap;
}

/* A second line inside a menu row, for what a choice actually means. */
.sheet-sub-b { display: block; font-size: 11px; color: var(--muted); margin-top: 1px; }

.tip-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 7px; }

/* A pinned readout belongs to a place, so it says the place first and the
   coordinates under it. */
.where-sub { display: block; font-size: 11px; color: var(--muted); margin-top: 1px; }
.readout.pinned { border-color: color-mix(in srgb, var(--accent) 40%, var(--edge)); }

/* The storm panel shares the readout's corner and its shape. */
.storm-panel { padding-right: 0; }
.sheet-close {
  position: absolute; top: 6px; right: 8px; width: 22px; height: 22px;
  border: 0; border-radius: 50%; background: none; color: var(--muted);
  font-size: 16px; line-height: 1; cursor: pointer;
}
.sheet-close:hover { color: var(--ink); background: rgba(255,255,255,0.08); }
.panel-head {
  margin: 9px 0 3px; font-size: 10px; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--muted);
}
.muted { color: var(--muted); }
.small { font-size: 11px; line-height: 1.45; }

/* Both detail panels stack in the same column: a place and a storm are
   different questions and you can be asking both. */
/* Both panels are capped against the dock by fitPanel and scroll what does not
   fit. overscroll-behavior so flicking to the end of a track list does not then
   start dragging the globe underneath it. */
.storm-panel { position: absolute; left: 20px; width: 304px; }
.storm-panel .panel-top .head-i { width: 24px; height: 24px; font-size: 12px; }
.storm-panel .steps { max-height: none; overflow: visible; }
.step-columns { color: var(--muted); font-size: 10px; padding: 5px 4px; }
.step-columns span:nth-last-child(-n+2) { text-align: right; }
.storm-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; padding: 4px 0; }
.storm-actions button { border: 1px solid var(--edge); border-radius: 6px; background: transparent; color: var(--ink-2); padding: 6px 9px; cursor: pointer; font: inherit; font-size: 11px; }
.storm-actions button:hover { color: var(--ink); border-color: var(--muted); }
.storm-summary { display: flex; align-items: baseline; gap: 7px; padding: 3px 0; font-size: 12px; font-variant-numeric: tabular-nums; }
.storm-summary strong { color: var(--ink); font-size: 16px; }
.storm-summary .muted { margin-left: auto; font-size: 10px; }
.forecast-area-toggle { display: flex; align-items: center; gap: 5px; padding: 4px 0; font-size: 11px; color: var(--ink-2); }
.forecast-area-toggle input { margin: 0; accent-color: var(--accent); }
.storm-info { position: fixed; z-index: 15; top: 104px; left: 336px; width: 304px; max-width: calc(100vw - 96px); max-height: calc(100dvh - 220px); display: flex; flex-direction: column; padding: 14px 0 14px 14px; border: 1px solid var(--edge); border-radius: 12px; background: var(--panel-solid); box-shadow: 0 12px 32px #0006; font-size: 12px; line-height: 1.55; }
.storm-info .panel-top strong { flex: 1; }
.storm-info h3 { font-size: 12px; margin: 10px 0 5px; }
.storm-info p { margin: 5px 0 12px; color: var(--ink-2); }
@media (max-width: 699px) {
  .readout, .storm-panel { width: min(304px, calc(100vw - 96px)); }
  .storm-info { top: 80px; max-width: calc(100vw - 86px); max-height: calc(100dvh - 190px); }
}
.readout, .storm-panel { display: flex; flex-direction: column; overflow: hidden; }

/* Identity stays put. The name of the place or the storm is what tells you
   which panel you are reading, so it is the one thing that must not scroll
   away -- and keeping it out of the scroll box means the scrollbar spans the
   content it actually scrolls rather than the full height of the panel. */
.panel-top {
  display: flex; align-items: center; gap: 6px;
  flex-shrink: 0;
  padding: 0 12px 8px 0; margin-bottom: 2px;
  border-bottom: 1px solid var(--edge);
}
.panel-top .where { margin-bottom: 0; min-width: 0; flex: 1; }
.where-name { display: block; }
.readout > .panel-top:has(.where-sub) { position: relative; padding-bottom: 18px; }
.readout > .panel-top .where-sub { position: absolute; left: 0; right: 12px; bottom: 0; margin: 0; line-height: 14px; }
.readout > .panel-top:has(.panel-ico) .where-sub { left: 22px; }
.panel-scroll {
  overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
  min-height: 0;   /* a flex child will not shrink past its content without it */
  padding: 7px 12px 0 0;
}
/* Sections stay put while their own content scrolls past, so you can always see
   which part of the panel you are inside and fold it without scrolling back. */
.panel-scroll .fold-row {
  position: sticky; top: 0; z-index: 2;
  background: var(--panel-solid); backdrop-filter: blur(8px);
}
.panel-top .sheet-close {
  position: static; flex: none; width: 24px; height: 24px; padding: 0;
  border: 0; border-radius: 50%; background: none; color: var(--muted);
  font-size: 15px; line-height: 1; cursor: pointer;
}
.panel-top .sheet-close:hover { color: var(--ink); background: rgba(255, 255, 255, 0.08); }
.panel-minimize { display: grid; place-items: center; flex: none; width: 24px; height: 24px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--muted); cursor: pointer; }
.panel-minimize svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.panel-minimize:hover { color: var(--ink); background: var(--hover); }
.panel-minimize:focus-visible, .panel-top .sheet-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.readout.minimized { width: 200px; max-width: calc(100vw - 96px); padding: 6px 8px; border-radius: 8px; }
.readout.minimized > .panel-scroll, .readout.minimized > .storm-overview { display: none; }
.readout.minimized > .panel-top { border: 0; padding: 0; margin: 0; gap: 4px; }
.readout.minimized .where-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
.readout.minimized .where-name.coordinates { font-size: 11px; }
.readout.minimized .where-sub { display: none; }
.readout.minimized .panel-ico { margin: 0; }
.readout.minimized .head-i { border-color: transparent; }
.readout.minimized .panel-minimize svg { transform: rotate(180deg); }
/* The icon says which panel this is at a glance: a pin for a place, a cyclone
   in its category colour for a storm. */
.panel-ico { flex: none; width: 16px; height: 16px; margin-top: 1px; }

/* A foldable section names itself. The chevron leads, because that is where the
   eye goes for a disclosure and because a caret alone in a corner told nobody
   what pressing it would hide. */
.fold-row {
  display: flex; align-items: center; gap: 6px; width: 100%;
  margin: 8px 0 2px; padding: 3px 2px;
  border: 0; background: none; cursor: pointer; text-align: left;
  font: inherit; font-size: 10px; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--muted);
}
.fold-row:hover { color: var(--ink-2); }
.fold-row .chev {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; font-size: 17px; line-height: 1; color: var(--ink-2);
}
.fold-note { margin-left: auto; text-transform: none; letter-spacing: 0; opacity: 0.75; }
/* A dismiss you can actually hit. At 11x12 the old one was smaller than the
   pointer's own aim, and a miss did not do nothing -- it landed on the globe
   and pinned a new place, which looks exactly like the button being broken. */
.pin-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; margin: -3px -3px -3px 5px;
  border: 0; border-radius: 50%; background: none; padding: 0;
  color: var(--ink-2); font-size: 14px; line-height: 1; cursor: pointer;
  pointer-events: auto;
}
.pin-x:hover { color: var(--ink); background: rgba(255, 255, 255, 0.14); }

#storm-plume { display: block; width: 100%; height: 74px; margin-top: 8px; }

/* The official track step by step. Scrolls rather than growing without limit --
   a storm a week old has 40 rows behind it. */
.steps {
  /* relative, so a row's offsetTop is measured against this scroll box rather
     than the panel -- without it the follow logic compares a row position in
     panel space against a scrollTop in box space and scrolls to nonsense. */
  position: relative;
  max-height: 152px; overflow-y: auto; scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
  overscroll-behavior: contain;
  font-variant-numeric: tabular-nums; font-size: 11px;
}
.step, .step-columns {
  display: grid; grid-template-columns: 1fr 26px 42px 48px; align-items: center; gap: 5px;
}
.step { width: 100%; min-height: 26px; padding: 3px 4px; border: 0; border-radius: 4px; background: transparent; color: var(--ink-2); text-align: left; font: inherit; cursor: pointer; }
.step:disabled { color: var(--muted); cursor: default; }
.step:disabled .category-chip { opacity: 0.6; }
.step:hover:not(:disabled) { background: rgba(255, 255, 255, 0.06); }
.step:focus-visible { outline: 1px solid var(--accent); outline-offset: -1px; }
.step.on { background: rgba(255, 255, 255, 0.09); color: var(--ink); }
.step .category-chip { min-width: 23px; height: 16px; font-size: 9px; }
.step-group { padding: 7px 4px 3px; font-size: 10px; color: var(--muted); }
.step-approx { padding-left: 4px; color: var(--muted); }
.track-history { border-bottom: 1px solid var(--edge); padding-bottom: 4px; margin-bottom: 2px; }
.track-history summary { padding: 6px 4px; color: var(--muted); font-size: 10px; cursor: pointer; }
@media (pointer: coarse) { .step { min-height: 32px; } }
.category-chip { display: inline-flex; align-items: center; justify-content: center; min-width: 26px; height: 18px; border-radius: 3px; color: #101820; font: 700 10px/1 ui-sans-serif, system-ui, sans-serif; letter-spacing: 0; }
.storm-p .category-chip { min-width: 23px; height: 16px; margin-right: 5px; font-size: 9px; vertical-align: middle; }
.step-w, .step-p { text-align: right; }
.step-p { color: var(--muted); }
.step.on .step-p { color: var(--ink-2); }
.panel-scroll.scrolling .steps { max-height: none; overflow-y: visible; }
.step-note { padding: 6px 4px; font-size: 11px; line-height: 1.4; color: var(--muted); }

.stormfix { font-size: 11px; line-height: 1.5; color: var(--ink-2); }

/* A section head that carries a control on its right. */
.panel-head.split { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.head-i {
  flex: none; width: 15px; height: 15px; padding: 0;
  border: 1px solid var(--edge); border-radius: 50%;
  background: none; color: var(--muted);
  font: italic 600 10px/1 Georgia, serif; cursor: pointer;
}
.head-i:hover, .head-i[aria-expanded="true"] { color: var(--ink); border-color: var(--ink-2); }

/* The category ramp and the notes that go with it: shown on request, because a
   key you have already read is four rows of nothing above a scrolling list. */
.step-key {
  position: relative; z-index: 1;
  margin-bottom: 5px; padding: 6px 7px; border-radius: 7px;
  background: rgba(255, 255, 255, 0.05); font-size: 10px; color: var(--muted);
}
.step-key p { margin: 5px 0 0; font-size: 10px; line-height: 1.5; }
.step-key em { font-style: normal; color: var(--ink-2); }
.key-ramp { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px 6px; }
.key-ramp .category-chip { height: 24px; }


/* Scrollbars inside the panels. The platform default is a wide grey slab with a
   track behind it, which on a translucent panel over a map reads as a seam down
   the side. Firefox takes scrollbar-color above; WebKit needs these. */
.panel-scroll::-webkit-scrollbar, .steps::-webkit-scrollbar { width: 8px; }
.panel-scroll::-webkit-scrollbar-track, .steps::-webkit-scrollbar-track { background: transparent; }
.panel-scroll::-webkit-scrollbar-thumb, .steps::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.14);
  border-radius: 999px;
  border: 2px solid transparent;      /* inset, so the thumb is 4px of ink */
  background-clip: content-box;
}
.panel-scroll:hover::-webkit-scrollbar-thumb, .steps:hover::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.28); background-clip: content-box;
}

/* A plume drawn before every lead has arrived. Dimmed rather than hidden: the
   shape so far is real, it is just not the whole forecast yet. */
#plume.filling, #storm-plume.filling { opacity: 0.45; }

/* One chronological storm table; phase changes only once. */
.storm-extent { display: flex; gap: 4px; }
.storm-extent button[aria-pressed="true"] { color: var(--ink); background: var(--selected, rgba(255,255,255,.1)); }
.step-divider { border-top: 1px solid var(--edge); margin-top: 5px; padding: 7px 4px 4px; font-size: 10px; color: var(--ink-2); }
.step-t small { font-size: 8px; margin-left: 4px; color: var(--accent); }
.step-t { white-space: nowrap; font-variant-numeric: tabular-nums; }
.step.advisory-row { box-shadow: inset 3px 0 0 var(--accent); }

:root[data-theme="light"] {
  --panel-solid: #f5f8fa; --panel: rgba(247,250,252,.94);
  --ink: #183143; --ink-2: #41596a; --muted: #607685;
  --edge: rgba(27,63,86,.18); --accent: #1469bb;
  --selected: rgba(27,93,136,.12); --hover: rgba(27,93,136,.07);
  --map-halo: #f1f5f5; --map-ink: #294a5b; color-scheme: light;
}
:root[data-theme="light"] .plabel { text-shadow: 0 1px 5px #fff; }
:root[data-theme="light"] .storm-label,
:root[data-theme="light"] .storm-tip { background: var(--panel-solid); color: var(--ink); }
:root[data-theme="light"] .fade,
:root[data-theme="light"] .loading { background: #e6edf1; }
:root[data-theme="light"] .storm-label .storm-sub { color: var(--ink-2); }
.step:hover:not(:disabled), .sheet-item:hover, .ramp-item:hover, .sheet-close:hover { background: var(--hover); }
.step.on { background: var(--selected); }
.map-labels { position: fixed; inset: 0; z-index: 2; pointer-events: none; overflow: hidden; }
.map-label { position: absolute; left: 0; top: 0; transition: none; height: 20px; padding: 0; border: 0; background: none; color: var(--map-ink);
  text-align: center; font: 500 11px/20px system-ui, sans-serif; white-space: nowrap;
  text-shadow: -1px -1px 2px var(--map-halo), 1px -1px 2px var(--map-halo), -1px 1px 2px var(--map-halo), 1px 1px 2px var(--map-halo); }
.map-label.country { font-size: 11px; font-weight: 650; letter-spacing: .04em; }
.map-label.ocean { font-size: 12px; font-style: italic; letter-spacing: .05em; opacity: .75; }
.map-label { pointer-events: none; user-select: none; }
.forecast-hint { margin-top: 8px; color: var(--muted); font-size: 10px; }

:root[data-theme="light"] .storm-p { background: var(--panel-solid); color: var(--ink); }
:root[data-theme="light"] .sheet-sub { color: var(--muted); }
/* Advisory summary stays outside the table's single scroll surface. */
.storm-overview { flex: none; padding: 3px 12px 3px 0; }
.storm-panel .panel-scroll { padding-top: 0; }
#storm-fold-track { position: sticky; top: 0; z-index: 3; height: 28px; margin: 0; padding: 3px 2px; }
.storm-panel .step-columns { position: sticky; top: 28px; height: 22px; background: var(--panel-solid); z-index: 2; }
.storm-panel .steps { max-height: none; overflow: visible; }
@media (max-width: 699px), (max-height: 600px) {
  .storm-coordinates { display: none; }
  #storm-live { padding-bottom: 3px; }
  .storm-actions button { font-size: 10px; padding: 5px 8px; }
}

/* One nearby, readable key rather than unexplained category initials. */
.storm-info .key-ramp { display: grid; grid-template-columns: 1fr; gap: 5px; }
.category-definition { display: grid; grid-template-columns: 28px 1fr auto; align-items: center; gap: 8px; font-size: 11px; }
.category-definition small { font-size: 10px; color: var(--muted); }

.pressure-key { display: flex; gap: 12px; padding: 6px 10px 10px; font-size: 10px; color: var(--muted); }
.pressure-key span { display: flex; flex-direction: column; gap: 4px; }
.pressure-key i { height: 2px; width: 28px; }


/* Keep the same 48px bar on narrow screens, with time above the track. */
@media (max-width: 1100px) {
  .timeline { flex-direction: column; align-items: stretch; gap: 0; }
  .clocks { justify-content: space-between; min-width: 0; }
  #valid-label { font-size: 12px; line-height: 14px; }
  .track-group { flex: none; }
  #timeline-days { display: none; }
}
@media (max-width: 700px) {
  .bar { gap: 8px; }
  #lead-label, .tl-step { display: none; }
}
@media (max-width: 520px) {
  #valid-label { font-size: 11px; line-height: 14px; overflow: hidden; text-overflow: ellipsis; }
  .timeline-more { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .bar, .play, #valid-label, .rail-fill { transition: none; }
  #scrub::-webkit-slider-thumb { transition: none; }
}
