/* The homepage composition: 1400px span, 2.2:1 feature split, horizontal discovery.
 *
 * The headline is whichever of Severe and Tropics has the more serious thing happening
 * (decision 15), composed by landing.js. It used to be a slogan at
 * 94px — the largest element on the page and the only one with nothing to say, sitting
 * above a Category 4 rendered as 12px grey. The display role is smaller now because it
 * carries a sentence rather than four words, and the sentence changes with the data. */
body[data-page="landing"] { background: var(--paper); isolation: isolate; }
.home { max-width: var(--hub-width); width: calc(100% - 2 * var(--hub-gutter)); margin: auto; padding: 0; }

/* The sky band ends where the intro ends.
 *
 * It was `body::before` at a fixed `height: 340px` in an otherwise fluid layout, so it
 * landed wherever 340px happened to fall: through the satellite image at 1536, inside the
 * briefing card at 390, and in dark mode as a hard full-width edge across the middle of
 * the feature row, aligned to no content edge at any width. Anchoring the band to the box
 * it belongs to means it can never be out of register. The spread-only shadow paints the
 * bleed and the clip keeps it from growing the page vertically. */
.home-intro { position: relative; padding: 12px 12px 20px; margin-inline: -12px; background: var(--intro);
  box-shadow: 0 0 0 100vmax var(--intro); clip-path: inset(0 -100vmax); }
.home-intro h1 { margin: 0 0 10px; color: var(--link); text-wrap: balance;
  font: 800 var(--fs-display)/1.08 var(--font-body); letter-spacing: -.03em; }
/* The page changes key with what leads the Severe section, as /severe's head does (severe.css):
   an emergency or a tornado on the ground at the top of a current list (`severity()`, set on the
   section by the server and carried by every refresh) takes the headline out of ocean blue into
   ink, under a band of NWS's own colour for that hazard. Ocean blue is the calm the rest of the
   week reads in; the band is seen before a word is read, and the words still name the hazard. */
.home:has([data-home-severe] [data-sv-edge="tornado-warning"][data-sv-severity]) { --home-edge: var(--hz-tornado-warning); }
.home:has([data-home-severe] [data-sv-edge="flash-flood-warning"][data-sv-severity]) { --home-edge: var(--hz-flash-flood-warning); }
.home:has([data-home-severe] [data-sv-edge="severe-thunderstorm-warning"][data-sv-severity]) { --home-edge: var(--hz-severe-thunderstorm-warning); }
.home:has([data-home-severe] [data-sv-edge="tsunami-warning"][data-sv-severity]) { --home-edge: var(--hz-tsunami-warning); }
.home:has([data-home-severe] [data-sv-edge="extreme-wind-warning"][data-sv-severity]) { --home-edge: var(--hz-extreme-wind-warning); }
.home:has([data-home-severe] [data-sv-edge="storm-surge-warning"][data-sv-severity]) { --home-edge: var(--hz-storm-surge-warning); }
.home:has([data-home-severe] [data-sv-edge="hurricane-warning"][data-sv-severity]) { --home-edge: var(--hz-hurricane-warning); }
.home:has([data-home-severe] [data-sv-edge="typhoon-warning"][data-sv-severity]) { --home-edge: var(--hz-typhoon-warning); }
.home:has([data-home-severe] [data-sv-edge="blizzard-warning"][data-sv-severity]) { --home-edge: var(--hz-blizzard-warning); }
.home:has([data-home-severe] [data-sv-edge="snow-squall-warning"][data-sv-severity]) { --home-edge: var(--hz-snow-squall-warning); }
.home:has([data-home-severe] [data-sv-edge="ice-storm-warning"][data-sv-severity]) { --home-edge: var(--hz-ice-storm-warning); }
.home:has([data-home-severe] [data-sv-edge="winter-storm-warning"][data-sv-severity]) { --home-edge: var(--hz-winter-storm-warning); }
.home:has([data-home-severe] [data-sv-edge="lake-effect-snow-warning"][data-sv-severity]) { --home-edge: var(--hz-lake-effect-snow-warning); }
.home:has([data-home-severe] [data-sv-edge="dust-storm-warning"][data-sv-severity]) { --home-edge: var(--hz-dust-storm-warning); }
.home:has([data-home-severe] [data-sv-edge="blowing-dust-warning"][data-sv-severity]) { --home-edge: var(--hz-blowing-dust-warning); }
.home:has([data-home-severe] [data-sv-edge="high-wind-warning"][data-sv-severity]) { --home-edge: var(--hz-high-wind-warning); }
.home:has([data-home-severe] [data-sv-edge="tropical-storm-warning"][data-sv-severity]) { --home-edge: var(--hz-tropical-storm-warning); }
.home:has([data-home-severe] [data-sv-edge="ashfall-warning"][data-sv-severity]) { --home-edge: var(--hz-ashfall-warning); }
.home:has([data-home-severe] [data-sv-edge="flood-warning"][data-sv-severity]) { --home-edge: var(--hz-flood-warning); }
.home:has([data-home-severe] [data-sv-edge="coastal-flood-warning"][data-sv-severity]) { --home-edge: var(--hz-coastal-flood-warning); }
.home:has([data-home-severe] [data-sv-edge="lakeshore-flood-warning"][data-sv-severity]) { --home-edge: var(--hz-lakeshore-flood-warning); }
.home:has([data-home-severe] [data-sv-edge="high-surf-warning"][data-sv-severity]) { --home-edge: var(--hz-high-surf-warning); }
.home:has([data-home-severe] [data-sv-edge="extreme-heat-warning"][data-sv-severity]) { --home-edge: var(--hz-extreme-heat-warning); }
.home:has([data-home-severe] [data-sv-edge="extreme-cold-warning"][data-sv-severity]) { --home-edge: var(--hz-extreme-cold-warning); }
.home:has([data-home-severe] [data-sv-edge="freeze-warning"][data-sv-severity]) { --home-edge: var(--hz-freeze-warning); }
.home:has([data-home-severe] [data-sv-edge="red-flag-warning"][data-sv-severity]) { --home-edge: var(--hz-red-flag-warning); }
/* NWS's colours are never retuned, and flash-flood maroon is 1.3:1 on the dark theme's surfaces,
   so each hazard edge is paired with a 1px line of ink on its inner side: dark on the light theme,
   light on the dark one, a boundary at contrast in both. The band spans the content column, not
   the intro's 12px bleed either side of it (HB-20), so its ends line up with the headline's. */
.home:has([data-home-severe] [data-sv-severity]) .home-intro { padding-top: calc(12px + var(--sp-3)); }
.home:has([data-home-severe] [data-sv-severity]) .home-intro::before { content: ""; position: absolute; top: 0; left: 12px; right: 12px;
  height: var(--sp-3); background: var(--home-edge, var(--ink)); box-shadow: 0 1px 0 var(--ink); }
.home:has([data-home-severe] [data-sv-severity]) .home-intro h1 { color: var(--ink); }
.home-lede { font-size: var(--fs-lead); line-height: 1.45; color: var(--ink); margin: 0; max-width: 52ch; }
/* The clock for the claim above it: which sources, published when. Empty until painted. */
.home-provenance { font-size: var(--fs-small); line-height: 1.5; color: var(--ink-2); margin: 10px 0 0; }
/* Under the heading, when the Severe sentence is it: who issued what it names, until when, and
   where the official warnings are (HB-17), at body size since HB-21. The storm list's clock stays under the
   lede it speaks for. Its own class, since it is never reserved for as the storm list's is. */
.home-source { font-size: var(--fs-remarks); line-height: 1.5; color: var(--ink-2); margin: 0 0 12px; }
/* The heading's one step to the official warnings is a whole target (HB-21): at body size, not
   12px, and a line box of 24px. */
.home-source a { display: inline-block; min-height: 24px; }
:is(.home-provenance, .home-source):empty { display: none; }
/* The loud register's lede (an emergency or a tornado warning leading a current list): the lead
   warning's places and NWS's own instruction as sent, or every warning by kind and state when the
   heading names more than one, and weather.gov as the one action, directly under
   the heading and its source. The other section's sentence is not the lede then: it speaks in the
   panel, and the storm list's clock goes with it (`.now-provenance`). */
.home-urgent { display: grid; gap: var(--sp-3); max-width: 68ch; }
/* The lines by kind take the intro's width (2026-10-14): one line each in a 68ch column left most of
   a desktop's intro empty and ran an outbreak's seven kinds down the screen. The measure stays for
   one warning's lede, where NWS's instruction is prose to read. */
.home-urgent:has(> .home-urgent-groups) { max-width: none; }
.home-urgent:empty { display: none; }
.home-urgent-where { margin: 0; font-size: var(--fs-lead); font-weight: 800; line-height: 1.35; color: var(--ink); }
.home-urgent .sv-says { background: var(--surface); border-color: var(--edge); }
/* More than one warning named: every warning in force, one line for each kind, naming its states.
   Each line carries its hazard's exact NWS colour (decision 11, never retuned) as a rounded bar
   and a wash that fades out behind the words, and its states are underlined in it. The words stay
   in ink, which the NWS fills would fail as text (red is about 4:1 on this page, orange 2:1), and
   say what each line is, so the colour is never the only signal. An emergency's wash is stronger.
   The lines are cells of a grid, three across a desktop's intro and one on a phone, read across
   then down in NWS's priority order; an emergency takes a row of its own, first. */
.home-urgent-groups {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr));
  align-items: start;
  gap: var(--sp-2) var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.home-urgent-group[data-emergency] { grid-column: 1 / -1; }
.home-urgent-group {
  --g: var(--ink);
  --g-wash: 13%;
  position: relative;
  padding: 7px 14px 7px 18px;
  border-radius: var(--radius-control);
  background: linear-gradient(90deg, color-mix(in srgb, var(--g) var(--g-wash), transparent), transparent 85%);
  line-height: 1.5;
}
.home-urgent-group::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 5px; border-radius: 3px; background: var(--g); }
.home-urgent-group[data-emergency] { --g-wash: 24%; }
.home-urgent-group[data-hazard="tornado-warning"] { --g: var(--hz-tornado-warning); }
.home-urgent-group[data-hazard="flash-flood-warning"] { --g: var(--hz-flash-flood-warning); }
.home-urgent-group[data-hazard="severe-thunderstorm-warning"] { --g: var(--hz-severe-thunderstorm-warning); }
.home-urgent-group[data-hazard="tsunami-warning"] { --g: var(--hz-tsunami-warning); }
.home-urgent-group[data-hazard="extreme-wind-warning"] { --g: var(--hz-extreme-wind-warning); }
.home-urgent-group[data-hazard="storm-surge-warning"] { --g: var(--hz-storm-surge-warning); }
.home-urgent-group[data-hazard="hurricane-warning"] { --g: var(--hz-hurricane-warning); }
.home-urgent-group[data-hazard="typhoon-warning"] { --g: var(--hz-typhoon-warning); }
.home-urgent-group[data-hazard="blizzard-warning"] { --g: var(--hz-blizzard-warning); }
.home-urgent-group[data-hazard="snow-squall-warning"] { --g: var(--hz-snow-squall-warning); }
.home-urgent-group[data-hazard="ice-storm-warning"] { --g: var(--hz-ice-storm-warning); }
.home-urgent-group[data-hazard="winter-storm-warning"] { --g: var(--hz-winter-storm-warning); }
.home-urgent-group[data-hazard="lake-effect-snow-warning"] { --g: var(--hz-lake-effect-snow-warning); }
.home-urgent-group[data-hazard="dust-storm-warning"] { --g: var(--hz-dust-storm-warning); }
.home-urgent-group[data-hazard="blowing-dust-warning"] { --g: var(--hz-blowing-dust-warning); }
.home-urgent-group[data-hazard="high-wind-warning"] { --g: var(--hz-high-wind-warning); }
.home-urgent-group[data-hazard="tropical-storm-warning"] { --g: var(--hz-tropical-storm-warning); }
.home-urgent-group[data-hazard="ashfall-warning"] { --g: var(--hz-ashfall-warning); }
.home-urgent-group[data-hazard="flood-warning"] { --g: var(--hz-flood-warning); }
.home-urgent-group[data-hazard="coastal-flood-warning"] { --g: var(--hz-coastal-flood-warning); }
.home-urgent-group[data-hazard="lakeshore-flood-warning"] { --g: var(--hz-lakeshore-flood-warning); }
.home-urgent-group[data-hazard="high-surf-warning"] { --g: var(--hz-high-surf-warning); }
.home-urgent-group[data-hazard="extreme-heat-warning"] { --g: var(--hz-extreme-heat-warning); }
.home-urgent-group[data-hazard="extreme-cold-warning"] { --g: var(--hz-extreme-cold-warning); }
.home-urgent-group[data-hazard="freeze-warning"] { --g: var(--hz-freeze-warning); }
.home-urgent-group[data-hazard="red-flag-warning"] { --g: var(--hz-red-flag-warning); }
.home-urgent-kind { font-size: var(--fs-small); font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink); }
.home-urgent-states { font-size: var(--fs-remarks); font-weight: 700; color: var(--ink); }
.home-urgent-state { white-space: nowrap; }
/* `styles.css` underlines every link in an `li` (`li a:not(.btn):not(.panel-item)`, WCAG 1.4.1),
   which would reset the underline to ink: these keep its underline and colour it. */
.home-urgent-group .home-urgent-states a:any-link {
  display: inline-block;
  min-height: 24px;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--g);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}
.home-urgent-group .home-urgent-states a:any-link:hover { text-decoration-thickness: 3px; }
/* On the dark page the dark red flash flood fill barely shows as a wash: it takes more. */
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-urgent-group { --g-wash: 22%; }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-urgent-group[data-emergency] { --g-wash: 34%; }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) :is(.home-urgent-group[data-hazard="flash-flood-warning"], .home-urgent-group[data-hazard="snow-squall-warning"], .home-urgent-group[data-hazard="ice-storm-warning"], .home-urgent-group[data-hazard="tropical-storm-warning"], .home-urgent-group[data-hazard="extreme-heat-warning"], .home-urgent-group[data-hazard="extreme-cold-warning"], .home-urgent-group[data-hazard="freeze-warning"]) { --g-wash: 40%; }
/* The NWS fills under 1.5:1 on the light page (the dust warnings' bisque at 1.2:1, the flood
   warning's green at 1.3:1) all but vanish there: their bar takes an ink edge, as the watches'
   yellow does on the maps, and their states are underlined in ink. The fill is never changed. */
:root:not(:is([data-theme="dark"], [data-dark]:not([data-theme="light"]))) :is(.home-urgent-group[data-hazard="dust-storm-warning"], .home-urgent-group[data-hazard="blowing-dust-warning"], .home-urgent-group[data-hazard="flood-warning"])::before { box-shadow: 0 0 0 1px var(--ink); }
:root:not(:is([data-theme="dark"], [data-dark]:not([data-theme="light"]))) :is(.home-urgent-group[data-hazard="dust-storm-warning"], .home-urgent-group[data-hazard="blowing-dust-warning"], .home-urgent-group[data-hazard="flood-warning"]) .home-urgent-states a:any-link { text-decoration-color: currentColor; }
/* The NWS fills under 3:1 on the dark page (flash flood's dark red is 1.5:1; snow squall, ice storm,
   tropical storm, extreme heat, extreme cold and freeze are the others, measured against the dark
   paper) are too faint to mark a link: there their states are underlined in ink, and the bar and
   the wash keep the hazard's colour. */
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) :is(.home-urgent-group[data-hazard="flash-flood-warning"], .home-urgent-group[data-hazard="snow-squall-warning"], .home-urgent-group[data-hazard="ice-storm-warning"], .home-urgent-group[data-hazard="tropical-storm-warning"], .home-urgent-group[data-hazard="extreme-heat-warning"], .home-urgent-group[data-hazard="extreme-cold-warning"], .home-urgent-group[data-hazard="freeze-warning"]) .home-urgent-states a:any-link { text-decoration-color: currentColor; }
.home-urgent-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-5); margin: 0; }
.home-urgent-actions .btn { min-height: 44px; }
.home[data-urgent] .home-lede:empty { min-height: 0; }
.home[data-urgent] .home-provenance { display: none; }
/* "Warnings for your city", the last line of the intro: one row, its label beside the field, so
   the reader's own question ("is anything near me?") is asked where the page's answer starts. The
   suggestions are /severe's own list (severe.css `.svb-suggest`). */
.home-place { margin: 16px 0 0; max-width: 640px; }
.home-place-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: flex-start; }
.home-place-label { display: inline-flex; align-items: center; min-height: 44px; font-size: var(--fs-remarks); font-weight: 700; color: var(--ink); margin-right: var(--sp-1); }
.home-place-search { flex: 1 1 180px; max-width: none; min-width: 0; }
.home-place .btn { min-height: 44px; }
.home-place [data-home-locate][hidden] { display: none; }
/* With script the button is shown once landing.js knows the browser can locate; its place is kept
   from the first paint, so it arriving does not move the row, or on a phone the page, down. */
@media (scripting: enabled) { .home-place [data-home-locate][hidden] { display: inline-flex; visibility: hidden; } }
/* The intro's full-bleed background is clipped to its box (`clip-path`), which would cut a list
   floating over the page in half: here the suggestions open in the flow, under the field. */
.home-place .svb-suggest { position: static; margin-top: 4px; box-shadow: none; max-height: none; }
.home-place-near { margin: 6px 0 0; font-size: var(--fs-small); line-height: 1.45; color: var(--ink); }
.home-place-near[hidden] { display: none; }
.now-provenance { margin: 4px 0 0; font-size: var(--fs-small); line-height: 1.5; color: var(--ink-2); }
.home:not([data-urgent]) .now-provenance, .now-provenance:empty { display: none; }
/* The server can only print Severe's half of the heading, often one short line with no lede;
   landing.js composes the full heading, lede and storm-list clock once the storm list answers,
   usually after the first paint. Reserving the composed height keeps the window and panel from
   dropping by a hundred pixels when it does. The heading is reserved only when the Tropics can
   take it, Severe at rank 6 to 8 (the order guess below); with warnings in force the server's
   heading is the page's. Only with script: without it nothing is composed, and the page stays
   as compact as the server made it.
   Since HB-5 the server composes the page whole whenever it can read the storm list
   (body[data-home-composed]): then nothing arrives late, and nothing is reserved. These rules are
   the fallback for a page it could not compose. */
@media (scripting: enabled) {
  body:not([data-home-composed]):has([data-home-section]:is([data-rank="6"], [data-rank="7"], [data-rank="8"])) .home-intro h1 { min-height: 2lh; }
  body:not([data-home-composed]) .home-lede { min-height: 1lh; }
  body:not([data-home-composed]) :is(.home-provenance, .home-provenance:empty) { display: block; min-height: 1lh; }
}
/* A phone sets the heading at 30px, about twenty characters a line: decision 15's sentences run
   to four lines there, and the storm-list clock to two. */
@media (scripting: enabled) and (max-width: 719px) {
  body:not([data-home-composed]):has([data-home-section]:is([data-rank="6"], [data-rank="7"], [data-rank="8"])) .home-intro h1 { min-height: 4lh; }
  body:not([data-home-composed]) :is(.home-provenance, .home-provenance:empty) { min-height: 2lh; }
}

.home-feature { display: grid; grid-template-columns: minmax(0,2.2fr) minmax(0,1fr); gap: 16px; align-items: start; margin-top: 16px;
  --home-switch-row: 54px; }
/* A grid item keeps at least its widest unbreakable run: the satellite caption's source line held
   the window 13px past a 195px screen (200% zoom) and pushed the page sideways (HB-25). The items
   take the column, and the caption's runs wrap anywhere they must. */
.home-feature > * { min-width: 0; }
.satellite-caption { overflow-wrap: anywhere; }
/* The panel is first in the document at every width (HB-12): a keyboard or a screen reader reaches
   what is happening before the pictures of it. On a desktop the grid puts the window on the left. */
@media (min-width:1101px) {
  .home-feature > .home-window { grid-column: 1; grid-row: 1; }
  .home-feature > .now-briefing { grid-column: 2; grid-row: 1; }
}
.satellite { margin: 0; min-width: 0; }
.satellite-frame { position: relative; }
.satellite-window { position: relative; width: 100%; aspect-ratio: 2.25; background: var(--intro); border-radius: var(--radius-surface); overflow: hidden; }
.satellite-window picture, .satellite-window img { display: block; width: 100%; height: 100%; }
.satellite-window img { object-fit: contain; }
.satellite-empty { display: grid; align-content: center; justify-items: start; padding: 32px; height: 100%; color: var(--ink); }
.satellite-empty h2 { font-size: var(--fs-section); margin: 0 0 8px; }
.satellite-empty p { margin: 0 0 12px; }
/* The action colour, as a primary button: reversed ink on this page now means caution (an outage
   notice, a stale frame, a strong warning tag), so a way into the viewer no longer wears it. */
.satellite-full { position: absolute; bottom: 14px; right: 16px; background: var(--enamel); color: var(--on-enamel);
  border-radius: var(--radius-control); padding: 9px 14px; font-size: var(--fs-lead); min-height: 44px;
  display: inline-flex; align-items: center; gap: .3em; max-width: calc(100% - 24px); overflow-wrap: anywhere; }
.satellite-full:hover { color: var(--on-enamel); background: var(--enamel-strong); text-decoration: underline; }
.satellite figcaption { color: var(--ink-2); font-size: var(--fs-small); line-height: 1.4; padding: 6px 0 0; }
.satellite-caption-line { display: flex; flex-wrap: wrap; gap: 4px 12px; }
.satellite-caption-line strong { text-transform: uppercase; letter-spacing: .07em; }
.satellite figcaption p { margin: 2px 0; }
.satellite figcaption a { text-decoration: underline; }
[data-satellite-state]:empty { display: none; }
/* A late frame says so on the image, not only in the caption under it: a day-old picture of calm
   seas reads as calm seas. The caption's status line is what is announced, so the mark's text is
   given an empty alternative. The marker count is the freshness component's: one for delayed,
   two for stale. */
.satellite-window:is([data-state="stale"], [data-state="delayed"])::after {
  position: absolute; top: 12px; right: 12px; z-index: 1;
  padding: 4px 10px; border-radius: var(--radius-control); background: var(--ink); color: var(--paper);
  font: 700 var(--fs-small)/1.4 var(--font-body); pointer-events: none; }
.satellite-window[data-state="stale"]::after { content: "!! Stale frame" / ""; }
.satellite-window[data-state="delayed"]::after { content: "! Delayed frame" / ""; }

.now-briefing { background: var(--briefing); border-radius: var(--radius-surface); padding: 20px 24px 24px; display: flex; flex-direction: column; height: auto; }
.now-briefing h2 { font: 800 var(--fs-section)/1.2 var(--font-body); letter-spacing: -.03em; margin: 0 0 12px; }
.now-briefing .landing { min-height: 0; display: block; }
.briefing-list { list-style: none; padding: 0; margin: 0; }
.briefing-list li { border-top: var(--hairline); padding: 14px 0; }
.briefing-row { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,auto); gap: 8px; align-items: baseline; }
.briefing-row a { color: var(--teal); font-size: var(--fs-subhead); font-weight: 800; overflow-wrap: anywhere; }
.briefing-wind { font-size: var(--fs-subhead); font-variant-numeric: tabular-nums; }
/* A storm's Storm Mode page beside its name (SM-9): a quieter link than the name it follows. */
.briefing-name { min-width: 0; }
.briefing-row a.briefing-sm, .now-section .briefing-row a.briefing-sm { font-size: var(--fs-small); font-weight: 700; white-space: nowrap; }
.briefing-detail { color: var(--ink-2); font-size: var(--fs-small); line-height: 1.4; margin: 4px 0 0; }
.briefing-detail .chip { vertical-align: baseline; margin-right: 2px; }
/* The UTC clock and the state after it never break apart; the reader's local time before them may,
   where a 195px column (a phone at 200% zoom) has no room for it on one line. */
.briefing-when { white-space: nowrap; }
.briefing-when .sv-local { white-space: normal; }
/* A late advisory, marked (HB-29): the freshness component's "!" and the storms page's word. */
.briefing-late { font-weight: 700; color: var(--ink); }
.briefing-source { color: var(--ink-2); font-size: var(--fs-small); line-height: 1.5; padding: 10px 0 0; border-top: var(--hairline); margin: 0; }

/* The outage notice outranks the storm rows, because it is the sentence that says the
   rows may be wrong. It was 13px, unbordered and uncoloured on the same aqua as
   everything around it — the smallest live text in the panel, under a 94px slogan, on a
   product whose third principle is "say the limits out loud". DESIGN.md already licenses
   this treatment for attached degraded notices. The printed marker carries it where the
   reversed ink does not survive: forced-colors, and any reader who cannot use the tint. */
.briefing-notice { font-size: var(--fs-remarks); font-weight: 700; line-height: 1.45;
  background: var(--ink); color: var(--paper); border-radius: var(--radius-control);
  padding: 10px 12px; margin: 0 0 12px; display: flex; gap: 8px; }
.briefing-notice::before { content: "!" / ""; font-weight: 800; flex: none; }
.briefing-more { display: inline-flex; align-items: center; min-height: 44px; font-weight: 700; }
.now-briefing #app { margin-bottom: 4px; }

.home-explore { border-top: var(--hairline); margin-top: 24px; padding-top: 8px; }
.explore-heading { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 8px; flex-wrap: wrap; }
.explore-heading h2 { font: 800 var(--fs-section)/1.3 var(--font-body); margin: 0; letter-spacing: -.03em; }
.tool-previews { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 14px; }
.tool-preview { display: grid; grid-template-columns: 44% minmax(0,1fr); gap: 20px; background: var(--intro); padding: 12px; border-radius: var(--radius-surface); color: var(--ink); text-decoration: none; }
.tool-preview:hover { color: var(--ink); text-decoration: none; outline: 1px solid var(--edge); }
/* The photograph sits in a <picture> so it has a PNG under its WebP (shell.js § 6b); the
   picture is the grid item, and the image fills it as a block, as it did when it was one. */
.tool-preview picture { display: block; }
.tool-preview img { display: block; width: 100%; height: 146px; object-fit: cover; border-radius: var(--radius-preview); }
.tool-preview h3 { font-size: var(--fs-subhead); font-weight: 800; letter-spacing: -.035em; line-height: 1.3; margin: 4px 0 6px; }
.tool-preview p { font-size: var(--fs-remarks); line-height: 1.4; color: var(--ink-2); margin: 0; }
.tool-nws { background: var(--briefing); }
.tool-archive { background: var(--archive); }
.preview-arrow { display: block; font-size: var(--fs-section); color: var(--teal); line-height: 1; margin-top: 8px; }
/* The radar and archive cards carry a live reading rather than their own heading set in
   bigger letters, which is what the two typographic-art blocks were: 44% of each card
   repeating the words immediately beside it. */
.preview-text-art { border-radius: var(--radius-preview); padding: 12px; background: var(--surface); color: var(--teal); display: flex; flex-direction: column; justify-content: space-between; min-height: 146px; }
.preview-text-art > span { font-size: var(--fs-section); font-weight: 800; letter-spacing: -.04em; line-height: 1.15; }
/* In the loud register the page does not end on promotion: each Explore card is its words alone,
   as it is under 360px, and the row steps back to the small print's colour. */
.home[data-urgent] .tool-preview { grid-template-columns: minmax(0, 1fr); background: var(--paper); outline: 1px solid var(--line); }
.home[data-urgent] .tool-preview > :is(.preview-text-art, picture) { display: none; }
.home[data-urgent] .explore-heading h2 { font-size: var(--fs-subhead); }

body[data-page="landing"] footer { margin: 16px auto 0; max-width: var(--hub-width); width: calc(100% - 2 * var(--hub-gutter)); padding: 8px 0 20px; }
body[data-page="landing"] footer .shell-inner { padding: 0; width: 100%; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 24px; }
body[data-page="landing"] footer .brand { display: none; }
body[data-page="landing"] footer p, body[data-page="landing"] footer li, body[data-page="landing"] footer h2 { font-size: var(--fs-micro); }
body[data-page="landing"] footer h2 { margin: 0 0 4px; }
/* The one line of the footer a reader must not miss is body size (HB-29), not its small print. */
body[data-page="landing"] footer .footer-safety { font-size: var(--fs-remarks); color: var(--ink); }
/* One clock (HB-29): every time on the homepage in the body face with tabular figures, the
   reader's local time first and UTC after it. /severe keeps Plex Mono for its clocks; here UTC
   sat in mono inside Manrope sentences, and its colon read "23 : 30Z". */
body[data-page="landing"] :is(.sv-clock, .sv-local, .sv-left, .sv-ago) { font-family: var(--font-body); font-variant-numeric: tabular-nums; }
.home-noscript { padding: 16px 0; }

/* The briefing is held to the *image box*, with the switch's row above it (HB-27) — not the
   figure, which also carries the caption below it — so the two columns end level. That relationship cannot be expressed by grid
   alignment, because the grid item is the whole figure; the minimum restates the image's
   geometry deliberately, and `tests/web/browser/acceptance.test.ts` and `home_theme` both
   assert the result. The slack this used to leave is filled with storms rather than
   removed: the panel shows four, and a quiet panel ends where its content ends. */
@media (min-width:1101px) {
  .now-briefing { min-height: calc(var(--home-switch-row) + (100vw - 2 * var(--hub-gutter) - 16px) * .6875 / 2.25); }
  .home-feature[data-state="quiet"] .now-briefing { min-height: 0; }
  /* With the warnings map beside it, the panel does not print the map's two source clocks a
     second time: they sit under the map, level with the rows they date. The reader who switches
     to the satellite gets them back in the panel, as a phone always has them, the map being a
     screen further down there. Either way each clock is on screen once. */
  .home-feature:has(.home-window[data-home-view="map"]) [data-home-severe] .now-clocks { display: none; }
}
/* Below 1400px a third of the row left the panel about 340px wide, where every clock line and
   storm detail took two or three lines and the panel ran 150-350px past the window. It takes a
   larger share there; the floor restates the window's image box for this split (1.8 of 2.8). */
@media (min-width:1101px) and (max-width:1399px) {
  .home-feature { grid-template-columns: minmax(0,1.8fr) minmax(0,1fr); }
  .now-briefing { min-height: calc(var(--home-switch-row) + (100vw - 2 * var(--hub-gutter) - 16px) * 1.8 / 2.8 / 2.25); }
}
@media (min-width:1536px) {
  .now-briefing { min-height: calc(var(--home-switch-row) + 423px); }
  .home-feature[data-state="quiet"] .now-briefing { min-height: 0; }
}
@media (max-width:1100px) {
  /* Stacked, the panel and its clocks come first; the map, a screen further down, does not print
     the same two clocks again. Each clock is on screen once. */
  .home-mapview .home-map-clocks { display: none; }
  .home-feature { grid-template-columns: 1fr; gap: 24px; }
  .now-briefing { padding: 24px; }
  .now-briefing:has(#app:not([data-painted])) { min-height: 25rem; }
  .tool-previews { gap: 12px; }
  .tool-preview { grid-template-columns: 1fr; gap: 10px; }
  .preview-text-art, .tool-preview img { height: 160px; }
}
@media (max-width:719px) {
  .home-intro { padding-top: 12px; }
  .home-lede { font-size: var(--fs-remarks); }
  .home-feature { gap: 20px; }
  .satellite-empty { padding: 16px; }
  .satellite-empty h2 { font-size: var(--fs-subhead); }
  .satellite-empty p, .satellite-empty a { font-size: var(--fs-small); }
  /* The frame is 2.25:1 — one published extent, `home-mercator-v1`, and no taller crop
     exists — so at 390px it is only ~173px of imagery and every pixel of width counts.
     It runs to both edges of the device rather than sitting inside the page gutter, and
     loses its corner radius because it is now touching the screen. Nothing is cropped:
     the aspect ratio still matches the frame exactly.

     The viewer link comes off the image and sits under it. Floating over a 173px frame it
     covered about a quarter of the observation, and at 36px it was the one control on the
     page below the 44px the rest of the site holds to. */
  .satellite-window { width: calc(100% + 2 * var(--hub-gutter)); margin-inline: calc(-1 * var(--hub-gutter)); border-radius: 0; }
  .satellite-full { position: static; margin-top: 10px; width: 100%; max-width: none;
    justify-content: center; font-size: var(--fs-remarks); font-weight: 700; min-height: 44px;
    padding: 8px 12px; background: transparent; color: var(--link);
    border: 1px solid var(--edge); border-radius: var(--radius-control); }
  .satellite-full:hover { color: var(--link); background: var(--wash); }
  .now-briefing { padding: 20px; }
  .explore-heading a { font-size: var(--fs-small); }
  .tool-previews { grid-template-columns: 1fr; }
  .tool-preview { grid-template-columns: 38% minmax(0,1fr); gap: 14px; }
  .tool-preview > *, .tool-preview { min-width: 0; }
  .tool-preview h3 { font-size: var(--fs-subhead); }
  .tool-preview p { font-size: var(--fs-small); }
  .tool-preview img, .preview-text-art { height: 150px; min-height: 150px; }
  .preview-text-art > span { font-size: var(--fs-subhead); }
  .satellite-window:has(.satellite-empty:not([hidden])) { min-height: 180px; }
  body[data-page="landing"] footer .shell-inner { grid-template-columns: 1fr 1fr; }
  body[data-page="landing"] footer .footer-about { grid-column: 1/-1; }
}
@media (prefers-reduced-motion: reduce) { .home *, .masthead * { animation: none; transition: none; scroll-behavior: auto; } }
/* A short screen (a phone at 200% zoom is 422px tall, a phone on its side about 390) keeps the
   masthead at the top of the page rather than over every screen of it: sticky, its two rows of
   navigation covered 45% of the view (HB-25). */
@media (max-height: 480px) { body[data-page="landing"] .masthead { position: static; } }

/* Long storm names and identifiers may break anywhere; ordinary prose may not. */
.home h1, .home h2, .home h3, .home p { overflow-wrap: break-word; }
.briefing-row a, .satellite-full { overflow-wrap: anywhere; }

/* When the storm list could not be read, "Try again" is the Tropics section's recovery action,
   the one control that can change what it says: full width, above the section's links. */
.now-tropics .btn.primary { width: 100%; justify-content: center; min-height: 52px; font-size: var(--fs-lead); }

/* The quiet reading, in the panel's own words. Without it a calm tropics rendered as an
   empty card, which reads as a failure rather than as an all-clear. */
.briefing-quiet { font-size: var(--fs-remarks); line-height: 1.5; color: var(--ink); margin: 0; }

/* ---- "Right now": Severe and Tropics in one panel (decision 15, HB-3) --------------------
 *
 * The Severe section is the server's (worker/severe/home.ts), in the Briefing's own markup:
 * its hazard chips, tags, notices and clocks take severe.css's rules. The Tropics section is
 * landing.js's, in the briefing's existing row. The section that leads is listed first. */
.now-briefing h2 { margin-bottom: 4px; }
.now-section { border-top: var(--hairline); padding: 12px 0 4px; }
.now-sections { display: flex; flex-direction: column; gap: 8px; }
/* Which section leads is known only once the storm list answers, usually after the first paint,
   and moving a section above the other then shifts the whole panel. So until landing.js sets the
   order (data-arranged), the page lays out the likelier one: the Tropics can only lead when the
   server's Severe rank is 6 or below (unconfirmed, a watch or an outlook, or quiet), and on those
   days some basin is usually active. Warnings in force keep Severe first. Only a guess of layout:
   landing.js then puts the lead first in the document too, which is the order that is read. Not
   without script, where nothing is ranked and the section the server filled stays first. */
@media (scripting: enabled) {
  .now-sections:not([data-arranged]):has(> .now-slot > [data-home-section]:is([data-rank="6"], [data-rank="7"], [data-rank="8"])) > .now-tropics { order: -1; }
  /* And laid out first, it is given the height of the three storm rows it is guessed to lead
     with, so the Severe section under it stays put when they arrive. */
  .now-sections:not([data-arranged]):has(> .now-slot > [data-home-section]:is([data-rank="6"], [data-rank="7"], [data-rank="8"])) > .now-tropics:has(#app:not([data-painted])) { min-height: 18rem; }
}
.now-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; flex-wrap: wrap; margin: 0 0 4px; }
.now-head h3 { margin: 0; font: 800 var(--fs-micro)/1.5 var(--font-body); letter-spacing: .08em; text-transform: uppercase; color: var(--ink); }
.now-count { font-size: var(--fs-small); color: var(--ink-2); font-variant-numeric: tabular-nums; }
.now-count:empty { display: none; }
.now-list { list-style: none; margin: 0; padding: 0; }
.now-list > li { padding: 6px 0; }
.now-list > li + li { border-top: 1px dashed var(--line); }
/* An emergency row carries its hazard's colour down its edge: the tag says "Tornado emergency" in
   the same reversed ink as every strong tag, and the edge is what sets it apart from them. */
.now-list > li:has(> .now-top[data-sv-edge]) { border-left: 6px solid var(--row-edge); box-shadow: inset 1px 0 0 var(--ink); padding-left: 10px; margin-left: -16px; }
.now-list > li:has(> .now-top[data-sv-edge="tornado-warning"]) { --row-edge: var(--hz-tornado-warning); }
.now-list > li:has(> .now-top[data-sv-edge="flash-flood-warning"]) { --row-edge: var(--hz-flash-flood-warning); }
.now-list > li:has(> .now-top[data-sv-edge="severe-thunderstorm-warning"]) { --row-edge: var(--hz-severe-thunderstorm-warning); }
.now-list > li:has(> .now-top[data-sv-edge="tsunami-warning"]) { --row-edge: var(--hz-tsunami-warning); }
.now-list > li:has(> .now-top[data-sv-edge="extreme-wind-warning"]) { --row-edge: var(--hz-extreme-wind-warning); }
.now-list > li:has(> .now-top[data-sv-edge="storm-surge-warning"]) { --row-edge: var(--hz-storm-surge-warning); }
.now-list > li:has(> .now-top[data-sv-edge="hurricane-warning"]) { --row-edge: var(--hz-hurricane-warning); }
.now-list > li:has(> .now-top[data-sv-edge="typhoon-warning"]) { --row-edge: var(--hz-typhoon-warning); }
.now-list > li:has(> .now-top[data-sv-edge="blizzard-warning"]) { --row-edge: var(--hz-blizzard-warning); }
.now-list > li:has(> .now-top[data-sv-edge="snow-squall-warning"]) { --row-edge: var(--hz-snow-squall-warning); }
.now-list > li:has(> .now-top[data-sv-edge="ice-storm-warning"]) { --row-edge: var(--hz-ice-storm-warning); }
.now-list > li:has(> .now-top[data-sv-edge="winter-storm-warning"]) { --row-edge: var(--hz-winter-storm-warning); }
.now-list > li:has(> .now-top[data-sv-edge="lake-effect-snow-warning"]) { --row-edge: var(--hz-lake-effect-snow-warning); }
.now-list > li:has(> .now-top[data-sv-edge="dust-storm-warning"]) { --row-edge: var(--hz-dust-storm-warning); }
.now-list > li:has(> .now-top[data-sv-edge="blowing-dust-warning"]) { --row-edge: var(--hz-blowing-dust-warning); }
.now-list > li:has(> .now-top[data-sv-edge="high-wind-warning"]) { --row-edge: var(--hz-high-wind-warning); }
.now-list > li:has(> .now-top[data-sv-edge="tropical-storm-warning"]) { --row-edge: var(--hz-tropical-storm-warning); }
.now-list > li:has(> .now-top[data-sv-edge="ashfall-warning"]) { --row-edge: var(--hz-ashfall-warning); }
.now-list > li:has(> .now-top[data-sv-edge="flood-warning"]) { --row-edge: var(--hz-flood-warning); }
.now-list > li:has(> .now-top[data-sv-edge="coastal-flood-warning"]) { --row-edge: var(--hz-coastal-flood-warning); }
.now-list > li:has(> .now-top[data-sv-edge="lakeshore-flood-warning"]) { --row-edge: var(--hz-lakeshore-flood-warning); }
.now-list > li:has(> .now-top[data-sv-edge="high-surf-warning"]) { --row-edge: var(--hz-high-surf-warning); }
.now-list > li:has(> .now-top[data-sv-edge="extreme-heat-warning"]) { --row-edge: var(--hz-extreme-heat-warning); }
.now-list > li:has(> .now-top[data-sv-edge="extreme-cold-warning"]) { --row-edge: var(--hz-extreme-cold-warning); }
.now-list > li:has(> .now-top[data-sv-edge="freeze-warning"]) { --row-edge: var(--hz-freeze-warning); }
.now-list > li:has(> .now-top[data-sv-edge="red-flag-warning"]) { --row-edge: var(--hz-red-flag-warning); }
/* A row is read at a glance (HB-18): the hazard, then until when and the time left in the body
   face at body size, the reader's local time leading and the UTC clock stepping back; then the
   places in words ("Lincoln, Logan and Wayne counties, WV"), each line of the link padded to a
   24px target; then the strong tags. Nothing in a row is set below 13px. */
/* The link is first in the row's document (HB-30), so a screen reader names the hazard before its
   countdown; on the screen the chip and the time stay on top. A hidden row (the row budget) keeps
   its `hidden`: the grid is only a shown row's. */
.now-list > li:not([hidden]) { display: grid; }
.now-top { order: -1; }
.now-top { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; margin: 0; font-size: var(--fs-small); }
.now-top .sv-hz { font-size: var(--fs-small); }
.now-when { font-size: var(--fs-remarks); line-height: 1.5; color: var(--ink); }
/* A row's times are the reader's alone once the local time is there: "Expires 6:30 PM CDT", not
   "6:30 PM CDT · 23:30Z". UTC stays in the source lines, where the products' own clocks are read,
   and is the row's only time for a reader on UTC or without script. */
:is(.now-when, .briefing-when) :is(.sv-local-sep, .sv-clock--second) { display: none; }
.now-when :is(.sv-clock, .sv-left) { font-family: var(--font-body); }
.now-when .sv-left { font-weight: 700; }
.now-left { white-space: nowrap; }
.now-when .sv-clock--second { font-size: var(--fs-small); }
.now-where { margin: 2px 0 0; font-size: var(--fs-remarks); font-weight: 800; line-height: 1.5; }
.now-where a { color: var(--teal); overflow-wrap: anywhere; padding-block: 2px; }
.now-list .sv-tags { margin-top: 4px; }
.now-list .sv-tag { font-size: var(--fs-small); }
/* The watches in force, named on a quiet day (HB-24): the chip and number, then the states and
   the end, at the rows' sizes. */
.now-watches { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 8px; font-size: var(--fs-small); color: var(--ink); }
/* Each watch as a row reads (HB-26): the chip on its own line, then the states and the end, so the
   end time does not break across a line beside the chip. */
.now-watches > li { display: grid; justify-items: start; gap: 2px; }
.now-watches .sv-hz { font-size: var(--fs-small); }
.now-watches-more { color: var(--ink-2); }
/* Two rows for the same hazard and places (a warning and its successor) are told apart, by when
   each was issued or by NWS's number for each, said only while both are shown: the row budget
   can hide one, and three rows hold at most one such pair. */
.now-twin { display: none; }
.now-list:has(> li[data-twin]:not([hidden]) ~ li[data-twin]:not([hidden])) .now-twin { display: inline; }
.now-quiet { margin: 4px 0 8px; font-size: var(--fs-remarks); line-height: 1.5; color: var(--ink); }
.now-quiet small { display: block; margin-top: 2px; font-size: var(--fs-small); color: var(--ink-2); }
/* Both sections quiet, one merged heading: its lede is the Severe section's next-outlook line, so
   the section does not repeat it. Set by the server and by landing.js as the page is composed. */
[data-home-sections][data-merged="quiet"] [data-home-severe] .now-quiet:not(.now-next) { display: none; }
.now-caveat { margin: 4px 0 8px; font-size: var(--fs-small); line-height: 1.45; color: var(--ink); }
.now-clocks { display: flex; flex-direction: column; margin: 4px 0 0; }
/* A clock line in a narrow panel flows as one sentence, in the caption size, rather than as the
   component's columns, which a 390px panel broke into three. */
.now-clocks .freshness, .home-map-clocks .freshness { display: block; font-size: var(--fs-small); line-height: 1.5; }
/* As a block the line's marker is an inline span, which ignores the component's fixed slot, and
   "!!" ran into the sentence it ranks ("!!NWS alerts down"). The tracker's clocks do the same.
   The slot is widened to hold "!!", so the lines under one another start their words together. */
.now-clocks .freshness-dot, .home-map-clocks .freshness-dot { display: inline-block; min-width: 1.6em; }
/* The events this site's operators are following (SM-9): a section of its own, or nothing. */
.now-events:empty { display: none; }
.now-events-list { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 4px; }
.now-events-list a { font-weight: 700; }
.now-events-kind { color: var(--ink-2); font-size: var(--fs-small); }
.now-links { display: flex; flex-wrap: wrap; gap: 0 20px; margin: 4px 0 0; font-size: var(--fs-small); }
.now-links a { display: inline-flex; align-items: center; min-height: 44px; }
.now-section .sv-notice { margin: 4px 0 8px; font-size: var(--fs-small); }
/* An outage's notice says when the feed last answered, so its clock line does not say it again. */
.now-section:has(> .sv-notice) .now-clocks [data-sv-source="alerts"] { display: none; }
.now-lastknown { margin: 0 0 8px; font-size: var(--fs-remarks); font-weight: 700; line-height: 1.4; color: var(--ink); }
/* The Tropics notice takes the Severe one's size, weight and printed marker, so the panel's two
   outage sentences read as the same kind of thing, and neither outranks the other by type. Every
   Tropics notice (a failed refresh, a stale list, a source down) is a "!!" state in the
   freshness component's scale. */
.now-section .briefing-notice { margin: 4px 0 8px; padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-small); font-weight: 600; line-height: 1.5; }
/* The marker is printed, not spoken ("/ """): the sentence after it says what is wrong. */
.now-section .briefing-notice::before { content: "!!" / ""; font-family: var(--font-mono); font-weight: 700; }
.now-section .sv-actions { margin: 0 0 8px; }
/* A storm row in the panel is set like a warning row: its name at the reading size, and dashed
   rules between rows, so the two sections read as one list of what is happening. */
.now-section .briefing-list li { padding: 8px 0; border-top: 0; }
.now-section .briefing-list li + li { border-top: 1px dashed var(--line); }
.now-section .briefing-row a, .now-section .briefing-wind { font-size: var(--fs-remarks); }
.now-section .briefing-more { font-size: var(--fs-small); }
/* The panel's floor is the image box, as before; a panel with nothing to report ends where
   its content ends, and on a day with rows in both sections it may run past the image, since
   a row is never clipped to hold the columns level. */
.home-feature[data-state="quiet"] .now-briefing { min-height: 0; }

/* ---- The window: the warnings map or the satellite (decision 15, concept B) ------------- */
.home-window { position: relative; }
/* The options carry the pill's corners themselves rather than the pill clipping them: at 200%
   text an option wraps, and a clip would cut its words. */
/* The switch sits above the window at every width (HB-27), as it always has on a phone, not over
   the picture: in the top-left corner it covered western Washington, where a warning in Puget
   Sound would have been drawn under it, and no corner of the national map is empty at every size.
   Its row is --home-switch-row tall (on .home-feature, which the panel's floor reads too): 44px,
   its border and 8px under it. */
.home-window { container: home-window / inline-size; }
.home-switch { display: inline-flex; margin: 0 0 8px;
  border: 1px solid var(--edge); border-radius: var(--radius-control); background: var(--surface); }
.home-switch-option { display: inline-flex; align-items: center; min-height: 44px; padding: 4px 16px;
  font: 700 var(--fs-small)/1.25 var(--font-body); color: var(--ink); background: var(--surface); border: 0; text-decoration: none; cursor: pointer; }
.home-switch-option:first-child { border-radius: calc(var(--radius-control) - 1px) 0 0 calc(var(--radius-control) - 1px); }
.home-switch-option:last-child { border-radius: 0 calc(var(--radius-control) - 1px) calc(var(--radius-control) - 1px) 0; }
.home-switch-option + .home-switch-option { border-left: 1px solid var(--edge); }
.home-switch-option[aria-pressed="true"] { background: var(--enamel); color: var(--on-enamel); }
.home-switch-option:hover { text-decoration: underline; }
.home-switch-option[aria-pressed="true"]:hover { text-decoration: none; }
/* The satellite with no frame (HB-31): one line where the 890 by 395 frame stood empty, saying
   the outage three times (the frame, the caption's time, its status line). It says it once: the
   frame's line, which the server writes when it has read no frame (withSatelliteOutage()) and
   satellite.mjs writes when its own read fails, the same sentence, so nothing moves when the two
   agree. The caption goes with the frame it captions. A frame that arrives later expands it.
   Only a window that arrived so (data-arrived, the server's) collapses: one whose outage the
   script finds after paint keeps its frame, which would otherwise move the page under the reader. */
.satellite-window[data-arrived="unavailable"][data-state="unavailable"] { aspect-ratio: auto; }
/* Beats the phone's 180px floor for an empty frame. */
.satellite-window[data-arrived="unavailable"][data-state="unavailable"]:has(.satellite-empty) { min-height: 0; }
.satellite-window[data-arrived="unavailable"][data-state="unavailable"] .satellite-empty { display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 4px 12px; height: auto; padding: 12px 16px; }
.satellite-window[data-arrived="unavailable"][data-state="unavailable"] .satellite-empty h2 { font-size: var(--fs-remarks); margin: 0; }
.satellite-window[data-arrived="unavailable"][data-state="unavailable"] .satellite-empty :is(p, a) { margin: 0; font-size: var(--fs-small); }
.home-window:has(.satellite-window[data-arrived="unavailable"][data-state="unavailable"]) .satellite-caption { display: none; }
/* On a desktop the panel takes the width, its two sections side by side, and the window's one line
   follows it; the empty frame had held the left column open beside it. The reader who switches to
   the warnings map gets the two columns back.
   The panel comes before the window in the document, so a browser that paints before it has parsed
   the window cannot see the window's state yet: until it can, the server's mark on the feature
   (data-satellite-arrived, written only when the window arrives on the satellite) says the same,
   and the panel is wide from its first paint rather than widened under the reader. */
@media (min-width: 1101px) {
  .home-feature:is([data-satellite-arrived="unavailable"]:not(:has(.satellite-window)), :has(> .home-window[data-home-view="satellite"] .satellite-window[data-arrived="unavailable"][data-state="unavailable"])) { grid-template-columns: minmax(0, 1fr); }
  .home-feature:is([data-satellite-arrived="unavailable"]:not(:has(.satellite-window)), :has(> .home-window[data-home-view="satellite"] .satellite-window[data-arrived="unavailable"][data-state="unavailable"])) > .now-briefing { grid-column: 1; grid-row: 1; min-height: 0; }
  .home-feature:is([data-satellite-arrived="unavailable"]:not(:has(.satellite-window)), :has(> .home-window[data-home-view="satellite"] .satellite-window[data-arrived="unavailable"][data-state="unavailable"])) > .home-window { grid-column: 1; grid-row: 2; }
  .home-feature:is([data-satellite-arrived="unavailable"]:not(:has(.satellite-window)), :has(> .home-window[data-home-view="satellite"] .satellite-window[data-arrived="unavailable"][data-state="unavailable"])) .now-sections {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 48px; align-items: start; }
}
/* Which view shows is data-home-view's: the server's choice on arrival, the reader's after. */
.home-window[data-home-view="map"] .satellite-frame,
.home-window[data-home-view="map"] .satellite-caption { display: none; }
.home-window[data-home-view="satellite"] .home-mapview { display: none; }
.home-mapview .sv-mapframe { position: relative; aspect-ratio: 2.25; display: grid; place-items: center;
  background: var(--graphic-wash); border-radius: var(--radius-surface); overflow: hidden; }
/* The drawing and the close-ups' numbers over it share one box in the map's own shape, 1600 by 1021
   (map.ts WIDTH and HEIGHT), so a number placed in the map's units lands where it was placed. */
.home-map-canvas { position: relative; height: 100%; max-width: 100%; aspect-ratio: 1600 / 1021; }
.home-map-canvas > .sv-map { display: block; width: 100%; height: 100%; }
.home-mapview .sv-legend { display: flex; flex-wrap: wrap; gap: 4px 14px; list-style: none; margin: 0; padding: 0; font-size: var(--fs-small); color: var(--ink); }
/* Under the map, its key first (HB-22): the legend, the clocks and the caption directly below the
   drawing they explain. */
.home-mapview { position: relative; }
.home-map-under { display: grid; gap: 12px 24px; margin: 6px 0 0; }
/* The key: what is in force, then SPC's shading and the discussions behind "Full key". */
.home-map-fullkey { margin: 2px 0 0; font-size: var(--fs-small); }
.home-map-fullkey summary { display: inline-flex; align-items: center; min-height: 24px; cursor: pointer; font-weight: 700; color: var(--link); }
.home-map-fullkey .sv-legend { margin-top: 4px; }

/* The map answers a press and zooms (2026-10-14, landing.js setUpMap()): the briefing map's zoom
   controls in its corner, a drag that pans it once zoomed and a pinch on a touch screen, so one
   finger still scrolls the page past it. What the details box is about is outlined in ink. */
/* Zoomed in, the drawing and its numbers fill the window, past the country's own shape. */
.home-map-canvas.is-zoomed { width: 100%; aspect-ratio: auto; }
.home-map-canvas > .sv-map { touch-action: pan-y; }
/* A pointer that says a press does something only where the script has set the map up. */
.home-map-canvas[data-home-armed] > .sv-map { cursor: pointer; }
.home-map-canvas > .sv-map.is-zoomed { touch-action: none; cursor: grab; }
.home-map-canvas > .sv-map.is-dragging { cursor: grabbing; }
.home-map-canvas .sv-watch-edge { pointer-events: visibleFill; }
.home-map-canvas :is(.sv-md-line, .sv-map-land, .sv-map-borders, .sv-map-sea) { pointer-events: none; }
.home-map-canvas .sv-warning.is-picked { stroke: var(--graphic-ink); stroke-width: 3; }
.home-map-canvas .sv-wt.is-picked .sv-watch-edge { stroke: var(--graphic-ink); stroke-width: 6; }
.home-map-hint { margin: 0; font-size: var(--fs-small); color: var(--ink-2); }
@container home-window (max-width: 599px) {
  .home-mapctl { top: 6px; right: 6px; gap: 4px; }
  .home-mapctl .btn { min-width: 40px; min-height: 40px; padding: 0 8px; }
}
/* The details: beside the press on a desktop, a sheet along the bottom on a phone, over the page.
   Its words and links are the cards' and the rows'. */
.home-pop { position: absolute; z-index: 6; box-sizing: border-box; width: min(360px, calc(100% - 16px)); max-height: min(70vh, 540px); overflow: auto;
  padding: 8px 12px 12px; background: var(--surface); color: var(--ink); border: 1px solid var(--edge); border-radius: var(--radius-surface);
  box-shadow: 0 10px 28px color-mix(in srgb, var(--graphic-ink) 28%, transparent); }
.home-pop[hidden] { display: none; }
.home-pop--sheet { position: fixed; left: 8px; right: 8px; bottom: 8px; width: auto; max-height: 62vh; }
.home-pop-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 0 0 4px; }
.home-pop-title { margin: 0; font: 800 var(--fs-small)/1.3 var(--font-body); letter-spacing: .06em; text-transform: uppercase; color: var(--ink-2); }
.home-pop-title:focus { outline: none; }
.home-pop-title:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }
.home-pop-actions { display: inline-flex; align-items: center; gap: 4px; }
.home-pop-zoom { min-height: 32px; }
.home-pop-close { min-width: 36px; min-height: 36px; margin-right: -6px; border: 0; border-radius: var(--radius-control); background: none;
  color: var(--ink); font: 700 22px/1 var(--font-body); cursor: pointer; }
.home-pop-close:hover { background: var(--wash-strong); }
.home-pop-items { display: grid; gap: 10px; }
.home-pop-item { display: grid; gap: 4px; }
.home-pop-item + .home-pop-item { padding-top: 10px; border-top: 1px dashed var(--line); }
.home-pop-top { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; margin: 0; font-size: var(--fs-small); }
.home-pop-top .sv-hz { font-size: var(--fs-small); }
.home-pop-left { font-weight: 700; }
.home-pop-where { margin: 0; font: 800 var(--fs-remarks)/1.35 var(--font-body); overflow-wrap: anywhere; }
.home-pop .sv-tags { margin: 0; }
.home-pop .sv-tag { font-size: var(--fs-small); }
:is(.home-pop-near, .home-pop-includes, .home-pop-meta, .home-pop-loading, .home-pop-gone) { margin: 0; font-size: var(--fs-small); line-height: 1.45; }
.home-pop-near { font-weight: 700; }
.home-pop-meta { color: var(--ink-2); }
.home-pop-meta .sv-clock { font-family: var(--font-body); }
.home-pop-meta :is(.sv-local-sep, .sv-clock--second) { display: none; }
.home-pop-links { display: flex; flex-wrap: wrap; gap: 4px 18px; margin: 2px 0 0; font-size: var(--fs-small); }
.home-pop-labels { margin: 0 0 4px; padding-left: 18px; font-weight: 700; font-size: var(--fs-small); }

/* The close-ups' numbers on the national map (2026-10-14, closer.ts mapMarks()), where the closer
   view's ring was: a numbered badge beside each warning, joined to it by a leader, and the few
   cities nearest them. An SVG of the map's own shape laid over the drawing, so a number placed in
   the map's units lands where it was placed; in SVG because the site allows no inline style. Two
   placements are sent, one for the desktop window and one for a phone's; the window's own width
   picks between them. Printed, not spoken: the panel's numbered rows and the cards say the same in
   words. */
.home-marks { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.home-mark-leader { stroke: var(--graphic-ink); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.home-mark circle { fill: var(--graphic-paper); stroke: var(--graphic-ink); stroke-width: 2; vector-effect: non-scaling-stroke; }
.home-mark text { fill: var(--graphic-ink); font-family: var(--font-body); font-weight: 800; font-variant-numeric: tabular-nums; }
.home-city { fill: var(--graphic-ink); font-family: var(--font-body); font-weight: 700;
  stroke: var(--graphic-paper); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; }
.home-city-dot { fill: var(--graphic-ink); stroke: var(--graphic-paper); stroke-width: 1; vector-effect: non-scaling-stroke; }
.home-marks-narrow { display: none; }
@container home-window (max-width: 599px) {
  .home-marks-wide { display: none; }
  .home-marks-narrow { display: inline; }
}
/* The storms on the national map (2026-10-16, worker/home-tropics/national.ts): the tropics' layer
   in the map's own box, over the warnings and under the numbers, its strokes and its cone the
   tropics section's (.ht-*). Its points and labels are sent for the same two widths as the numbers.
   Only a cone answers a press, and only where the script has set the map up; the rest lets a press
   through to the warnings under it. Clipped to the map, as the drawing is: a cone runs past it. */
.home-storm-layer { position: absolute; inset: 0; width: 100%; height: 100%; overflow: hidden; pointer-events: none; touch-action: pan-y; }
.home-map-canvas.is-zoomed > .home-storm-layer { touch-action: none; }
.home-storm-layer text { font-family: var(--font-body); }
.home-map-canvas[data-home-armed] .home-storm-cone { pointer-events: visibleFill; cursor: pointer; }
.home-map-canvas .home-storm.is-picked .home-storm-cone { stroke: var(--graphic-ink); stroke-width: 3; }
.home-storm-layer-narrow { display: none; }
@container home-window (max-width: 599px) {
  .home-storm-layer-wide { display: none; }
  .home-storm-layer-narrow { display: inline; }
}
/* Under the key, whose cone and track the map draws, with the tropics section's swatch. */
.home-map-storms { display: flex; align-items: baseline; gap: 8px; margin: 4px 0 0; font-size: var(--fs-small); line-height: 1.45; color: var(--ink); }
.home-map-storms .ht-key-cone { flex: none; transform: translateY(1px); }
.home-map-storms a { color: var(--link); text-decoration: underline; }
.home-map-storms-slot:empty { display: none; }

/* ---- The close-ups (2026-10-14, closer.ts) ---------------------------------------------- */
/* In the loud register, a card for each warning the page leads with, under the feature and across
   the page: five at most, most serious first, numbered as the map and the panel's rows are. With
   one warning named in the heading its card is two columns wide. Each card is a map a reader can
   place (the towns, the counties, the state codes, a scale and the storm's heading as NWS wrote
   it), then the warning in the panel's words. On any other day the slot holds an empty template. */
.home-closer-slot:not(:has(.home-closer)), .home-coast-slot:not(:has(.home-coast)) { display: none; }
/* A storm's coastal warnings, closer (2026-10-16, worker/home-tropics/coast.ts): the close-ups' own
   cards, over them, with NHC's cone and track on each map. */
.home-coast-storm-name { margin: 0; font-size: var(--fs-small); font-weight: 700; color: var(--ink); }
.home-closer-head p a { color: var(--link); text-decoration: underline; }
.home-closer { margin-top: 24px; }
.home-closer-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; margin: 0 0 10px; }
.home-closer-head h2 { font: 800 var(--fs-section)/1.3 var(--font-body); letter-spacing: -.03em; margin: 0; }
.home-closer-head p { margin: 0; font-size: var(--fs-small); color: var(--ink-2); }
.home-closer-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
@media (min-width: 720px) { .home-closer-list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1101px) { .home-closer-list { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
/* The card does not clip: its map rounds its own top corners, so no word of the card is cut when
   text is enlarged. */
.home-closer-card { display: grid; grid-template-rows: auto 1fr; min-width: 0; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-surface); }
@media (min-width: 720px) { .home-closer-card--wide { grid-column: span 2; } }
/* The map at the shape closer.ts drew it for: 264 by 176, or 544 by 176 for the wide card, so two
   cards side by side stand level. Its SVG slices rather than letterboxes, so the wide card's map
   on a phone's single column keeps its warning in the middle and loses only the edges. */
.home-closer-map { position: relative; aspect-ratio: 264 / 176; background: var(--graphic-wash); border-bottom: 1px solid var(--line);
  border-radius: calc(var(--radius-surface) - 1px) calc(var(--radius-surface) - 1px) 0 0; overflow: hidden; }
@media (min-width: 720px) { .home-closer-card--wide .home-closer-map { aspect-ratio: 544 / 176; } }
/* The locator (2026-10-14): the warning's state, small, with the card's frame boxed in ink, or a
   dot when the box would be too small to see, beside the words that place the warning (how far from
   a city, its towns); closer.ts sizes it, 64px on its longer side. */
.home-closer-place { display: flex; align-items: center; gap: 10px; margin: 2px 0; }
.home-closer-place-words { display: grid; gap: 2px; min-width: 0; }
.home-closer-locator { flex: none; display: block; }
.home-closer-locator-land { fill: var(--graphic-wash); stroke: var(--graphic-ink); stroke-width: 1; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.home-closer-locator-frame { fill: color-mix(in srgb, var(--graphic-ink) 22%, transparent); stroke: var(--graphic-ink); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.home-closer-locator-dot { fill: var(--graphic-ink); stroke: var(--graphic-paper); stroke-width: 1; vector-effect: non-scaling-stroke; }
.home-closer-map > .sv-map { display: block; width: 100%; height: 100%; }
.home-closer-num { position: absolute; left: 8px; top: 8px; box-sizing: border-box; width: 26px; height: 26px; display: grid; place-items: center;
  border: 2px solid var(--graphic-ink); border-radius: 50%; background: var(--graphic-paper); color: var(--graphic-ink);
  font: 800 14px/1 var(--font-body); font-variant-numeric: tabular-nums; }
.home-closer-body { display: grid; align-content: start; gap: 4px; padding: 10px 12px 12px; }
.home-closer-top { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; margin: 0; font-size: var(--fs-small); }
.home-closer-top .sv-hz { font-size: var(--fs-small); }
.home-closer-left { font-weight: 700; }
.home-closer-where { margin: 0; font: 800 var(--fs-remarks)/1.4 var(--font-body); }
.home-closer-where a { color: var(--teal); overflow-wrap: anywhere; padding-block: 2px; }
.home-closer-card .sv-tags { margin: 0; }
.home-closer-card .sv-tag { font-size: var(--fs-small); }
.home-closer-near { margin: 0; font-size: var(--fs-small); font-weight: 700; line-height: 1.45; color: var(--ink); }
.home-closer-includes { margin: 0; font-size: var(--fs-small); line-height: 1.45; color: var(--ink); }
.home-closer-meta { margin: 0; font-size: var(--fs-small); line-height: 1.45; color: var(--ink-2); }
.home-closer-meta .sv-clock { font-family: var(--font-body); }
.home-closer-meta :is(.sv-local-sep, .sv-clock--second) { display: none; }
/* A card's map: land in paper over the wash, county lines faint and state lines firmer, NWS's
   polygons in their own colours, the one the card is for outlined in ink and the others faded
   under it, its counties dashed and named, then state codes and towns, each in ink on a paper
   halo so it reads over a fill. Strokes are a fixed width on screen. */
.sv-map--closer .svc-land { fill: var(--sv-land, var(--graphic-paper)); }
.sv-map--closer .svc-county { stroke: var(--graphic-line); stroke-width: .75; vector-effect: non-scaling-stroke; }
.sv-map--closer .svc-states { stroke: var(--sv-border, var(--graphic-line-strong)); stroke-width: 1.5; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.sv-map--closer .sv-warning { fill-opacity: .45; }
.sv-map--closer .svc-lead { fill-opacity: .8; stroke: var(--graphic-ink); stroke-width: 2; }
.sv-map--closer :is(.svc-county-label, .svc-state, .svc-town, .svc-scale text) { stroke: var(--graphic-paper); stroke-width: 3px; stroke-linejoin: round;
  paint-order: stroke; font-family: var(--font-body); }
.sv-map--closer .svc-county-label { fill: var(--graphic-ink); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.sv-map--closer .svc-state { fill: var(--graphic-muted); font-weight: 800; letter-spacing: .08em; }
.sv-map--closer .svc-town { fill: var(--graphic-ink); font-weight: 700; }
.sv-map--closer .svc-town-dot { fill: var(--graphic-ink); stroke: var(--graphic-paper); stroke-width: 1; vector-effect: non-scaling-stroke; }
.sv-map--closer .svc-motion :is(line, .svc-motion-line) { stroke: var(--graphic-ink); stroke-width: 2; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.sv-map--closer .svc-motion-head { fill: var(--graphic-ink); }
.sv-map--closer .svc-storm { fill: var(--graphic-paper); stroke: var(--graphic-ink); stroke-width: 2; vector-effect: non-scaling-stroke; }
.sv-map--closer .svc-scale path { stroke: var(--graphic-ink); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.sv-map--closer .svc-scale text { fill: var(--graphic-ink); font-weight: 700; }
@media (max-width: 719px) { .home-closer-card--wide .svc-scale { display: none; } }
/* ---- The tropics (2026-10-14, worker/home-tropics/render.ts) ---------------------------- */
/* NHC's coastal lines in the colours the storms page draws them in (tropics-product-layers.mjs
   WW_COLORS; worker/test/home-tropics-render.test.ts reads both), and NWS's typhoon warning in the
   hurricane warning's. The cone is the live storm map's (storm-official-overlay.mjs). */
.home {
  --ht-hwr: #d8344a; --ht-hwa: #f28ea0; --ht-twr: #1f6fd1; --ht-twa: #8dc2ff; --ht-ssw: #6a3fc9; --ht-ssa: #b79cf0;
  --ht-cone-fill: #65bfff; --ht-cone-edge: #8bd2ff;
}
/* "Right now" holds a storm only with U.S. watches or warnings; with none its section is hidden. */
.now-tropics[hidden] { display: none; }
.now-storms { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.now-storm { display: grid; gap: 3px; }
.now-storm-top { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin: 0; }
.now-storm-name { font-size: var(--fs-subhead); font-weight: 800; color: var(--teal); overflow-wrap: anywhere; }
.now-storm-facts, .now-storm-issued { margin: 0; font-size: var(--fs-small); line-height: 1.45; color: var(--ink-2); }
.now-storm-facts { color: var(--ink); }
.now-storm-area { margin: 6px 0 0; font: 800 var(--fs-micro)/1.5 var(--font-body); letter-spacing: .08em; text-transform: uppercase; color: var(--ink); }
.now-storm-ww, .ht-us-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; font-size: var(--fs-small); line-height: 1.4; }
.now-storm-ww > li, .ht-us-list > li { display: flex; gap: 8px; align-items: baseline; }
/* A state's name breaks rather than push the panel past a phone at 200% text (WCAG 1.4.4, 1.4.10). */
.now-storm-ww > li > *, .ht-us-list > li > * { min-width: 0; }
.now-storm-ww > li, .ht-us-list > li { overflow-wrap: anywhere; }
.ht-ww-key { flex: none; width: 12px; height: 12px; border-radius: 2px; border: 1px solid var(--ink); transform: translateY(1px); }
.ht-ww-key--hwr, .ht-ww-key--tyw { background: var(--ht-hwr); }
.ht-ww-key--hwa { background: var(--ht-hwa); }
.ht-ww-key--twr { background: var(--ht-twr); }
.ht-ww-key--twa { background: var(--ht-twa); }
.ht-ww-key--ssw { background: var(--ht-ssw); }
.ht-ww-key--ssa { background: var(--ht-ssa); }
.now-storm-also { margin: 4px 0 0; font-size: var(--fs-small); line-height: 1.45; }
.now-storm .now-links { margin: 6px 0 0; }
/* A Severe row in the storm's area says so: outlined, so it is not read as a caution tag. */
.now-area { display: inline-block; padding: 0 6px; border: 1px solid var(--ink-2); border-radius: var(--radius-control);
  font-size: var(--fs-small); font-weight: 700; line-height: 1.5; color: var(--ink); }
.now-area[hidden] { display: none; }

.home-tropics-slot { margin-top: 32px; border-top: var(--hairline); padding-top: 12px; }
.ht-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; margin: 0 0 4px; }
.ht-head h2 { font: 800 var(--fs-section)/1.3 var(--font-body); letter-spacing: -.03em; margin: 0; }
.ht-source { margin: 0; font-size: var(--fs-small); color: var(--ink-2); }
.ht-source a { text-decoration: underline; }
.ht-summary { margin: 0 0 12px; font-size: var(--fs-remarks); line-height: 1.5; color: var(--ink); max-width: 80ch; }
.home-tropics > .briefing-notice { margin: 0 0 12px; }
/* The band: one panel per basin with a storm, the Eastern Pacific and the Atlantic always, at one
   scale, so each panel's width is its span of longitude; a basin left out is a labelled gap. */
.ht-band { display: flex; gap: 8px; align-items: stretch; }
.ht-basin { position: relative; min-width: 0; border-radius: var(--radius-surface); overflow: hidden; background: var(--graphic-wash); }
.ht-basin > .ht-map { display: block; width: 100%; height: 100%; }
.ht-basin--wp { flex: 80 1 0; }
.ht-basin--io { flex: 60 1 0; }
.ht-basin--sh { flex: 100 1 0; }
/* The Eastern Pacific and the Atlantic from 140°W, or from further west for a storm out there:
   each span's share of the row and its shape, ten degrees at a time (render.ts `epalSpan()`). */
.ht-basin--epal-130 { flex: 130 1 0; aspect-ratio: 1187.6 / 450; }
.ht-basin--epal-140 { flex: 140 1 0; aspect-ratio: 1278.9 / 450; }
.ht-basin--epal-150 { flex: 150 1 0; aspect-ratio: 1370.3 / 450; }
.ht-basin--epal-160 { flex: 160 1 0; aspect-ratio: 1461.6 / 450; }
.ht-basin--epal-170 { flex: 170 1 0; aspect-ratio: 1553.0 / 450; }
.ht-basin--gap { flex: 0 0 104px; display: grid; place-items: center; padding: 10px; text-align: center; background: transparent;
  border: 1px dashed var(--edge); font-size: var(--fs-small); line-height: 1.45; color: var(--ink-2); }
.ht-basin--gap p { margin: 0; }
.ht-basin--gap strong { display: block; margin-bottom: 4px; color: var(--ink); font-size: var(--fs-micro); letter-spacing: .12em; text-transform: uppercase; }
.ht-band--phone, .ht-elsewhere { display: none; }
/* A phone's panel: seventy degrees by forty (render.ts `phonePanel()`). */
.ht-basin--phone { flex: 1 1 0; aspect-ratio: 639.5 / 400; }
.ht-key { display: flex; flex-wrap: wrap; gap: 6px 18px; list-style: none; margin: 8px 0 16px; padding: 0; font-size: var(--fs-small); color: var(--ink-2); }
.ht-key > li { display: inline-flex; align-items: center; gap: 6px; }
.ht-key-ramp { display: inline-flex; border: 1px solid var(--ink); }
.ht-key-ramp i { width: 10px; height: 10px; }
.ht-key-ramp .tier-td { background: var(--wx-tier-td); }
.ht-key-ramp .tier-ts { background: var(--wx-tier-ts); }
.ht-key-ramp .tier-c1 { background: var(--wx-tier-c1); }
.ht-key-ramp .tier-c2 { background: var(--wx-tier-c2); }
.ht-key-ramp .tier-c3 { background: var(--wx-tier-c3); }
.ht-key-ramp .tier-c4 { background: var(--wx-tier-c4); }
.ht-key-ramp .tier-c5 { background: var(--wx-tier-c5); }
.ht-key-cone { width: 20px; height: 10px; border-radius: 5px; background: color-mix(in srgb, var(--ht-cone-fill) 30%, transparent); border: 1.5px solid var(--ht-cone-edge); }
.ht-key-past { width: 22px; border-top: 2px dashed var(--ink); }
.ht-key-invest { width: 18px; height: 10px; border-radius: 50%; border: 1.5px dashed var(--ink); }
/* A card for each storm, the one with U.S. watches or warnings first and two wide, then the areas of
   possible formation. */
.ht-cards { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
@media (min-width: 720px) { .ht-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } .ht-card--wide { grid-column: span 2; } }
@media (min-width: 1101px) { .ht-cards { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.ht-card { display: grid; grid-template-rows: auto 1fr; min-width: 0; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-surface); }
.ht-card--formation { grid-template-rows: 1fr; }
.ht-card-map { aspect-ratio: 300 / 190; background: var(--graphic-wash); border-bottom: 1px solid var(--line);
  border-radius: calc(var(--radius-surface) - 1px) calc(var(--radius-surface) - 1px) 0 0; overflow: hidden; }
@media (min-width: 720px) { .ht-card--wide .ht-card-map { aspect-ratio: 620 / 260; } }
.ht-card-map > .ht-map { display: block; width: 100%; height: 100%; }
.ht-card-body { display: grid; align-content: start; gap: 4px; padding: 10px 12px 12px; }
.ht-card-top { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 4px 8px; margin: 0; }
.ht-card-wind { font: 800 var(--fs-subhead)/1.1 var(--font-body); font-variant-numeric: tabular-nums; color: var(--ink); }
.ht-card-wind small { font-size: var(--fs-small); font-weight: 700; }
.ht-card-name { margin: 0; font: 800 var(--fs-subhead)/1.3 var(--font-body); }
.ht-card-name a { color: var(--teal); }
.ht-card-detail { margin: 0; font-size: var(--fs-small); line-height: 1.45; color: var(--ink); }
.ht-card-issued { margin: 0; font-size: var(--fs-small); line-height: 1.45; color: var(--ink-2); }
/* The clock and a late mark wrap as one run, so "! late" is on the line of the time it qualifies. */
:is(.ht-card-issued, .now-storm-issued) .briefing-when { display: inline-block; }
.ht-note { margin: 4px 0 0; padding: 4px 8px; border-radius: var(--radius-control); background: var(--intro); font-size: var(--fs-small); line-height: 1.4; color: var(--ink); }
.ht-note a { text-decoration: underline; }
.ht-us { margin-top: 6px; }
.ht-us-head, .ht-formation-kicker { margin: 0 0 2px; font: 800 var(--fs-micro)/1.5 var(--font-body); letter-spacing: .08em; text-transform: uppercase; color: var(--ink); }
.ht-card-strip { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
.ht-strip-head { margin: 0; font-size: var(--fs-small); font-weight: 700; color: var(--ink-2); }
.ht-strip { display: block; width: 100%; max-width: 440px; height: auto; }
.ht-strip-none { margin: 2px 0 0; font-size: var(--fs-small); color: var(--ink-2); }
.ht-card-links { margin: 6px 0 0; font-size: var(--fs-small); line-height: 1.6; }
.ht-card-links a, .ht-card-name a { overflow-wrap: anywhere; }
.ht-card-links a { text-decoration: underline; }
.ht-chances { list-style: none; margin: 4px 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px; }
/* The maps: the homepage's own ground, the national map's roles (`--graphic-*`), with the storms on
   the site's wind ramp. Strokes are a fixed width on screen. */
:is(.ht-map, .ht-strip) text { font-family: var(--font-body); }
.ht-sea { fill: var(--graphic-wash); }
.ht-land { fill: var(--sv-land, var(--graphic-paper)); stroke: var(--sv-coast, var(--graphic-muted)); stroke-width: .75; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.ht-grid { stroke: var(--graphic-line); stroke-width: .5; stroke-opacity: .7; vector-effect: non-scaling-stroke; }
.ht-grid-label { fill: var(--graphic-muted); }
.ht-eq { stroke: var(--graphic-line-strong); stroke-width: 1; stroke-dasharray: 4 4; vector-effect: non-scaling-stroke; }
.ht-eq-label, .ht-basin-name, .ht-basin-note { fill: var(--graphic-muted); }
.ht-basin-name { font-weight: 800; letter-spacing: .14em; }
.ht-basin-note { font-weight: 700; }
.ht-city { fill: var(--graphic-ink); font-weight: 700; stroke: var(--graphic-paper); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; }
.ht-city-dot { fill: var(--graphic-ink); stroke: var(--graphic-paper); stroke-width: 1; vector-effect: non-scaling-stroke; }
.ht-cone { fill: var(--ht-cone-fill); fill-opacity: .25; stroke: var(--ht-cone-edge); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.ht-ww { fill: none; stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.ht-ww--HWR { stroke: var(--ht-hwr); }
.ht-ww--HWA { stroke: var(--ht-hwa); }
.ht-ww--TWR { stroke: var(--ht-twr); }
.ht-ww--TWA { stroke: var(--ht-twa); }
.ht-ww--SSW { stroke: var(--ht-ssw); }
.ht-ww--SSA { stroke: var(--ht-ssa); }
.ht-past { fill: none; stroke: var(--graphic-ink); stroke-opacity: .75; stroke-width: 1.5; stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; }
.ht-track-case { fill: none; stroke: var(--graphic-ink); stroke-width: 4.5; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.ht-leg { fill: none; stroke-width: 2.5; stroke-linecap: round; vector-effect: non-scaling-stroke; }
:is(.ht-pt, .ht-pos, .ht-pill-tag) { stroke: var(--graphic-ink); stroke-width: 1.2; vector-effect: non-scaling-stroke; }
.ht-pos { stroke-width: 2.5; }
.ht-leg.tier-unknown { stroke: var(--wx-tier-unknown); } :is(.ht-pt, .ht-pos, .ht-pill-tag, .ht-bar).tier-unknown { fill: var(--wx-tier-unknown); }
.ht-leg.tier-td { stroke: var(--wx-tier-td); } :is(.ht-pt, .ht-pos, .ht-pill-tag, .ht-bar).tier-td { fill: var(--wx-tier-td); }
.ht-leg.tier-ts { stroke: var(--wx-tier-ts); } :is(.ht-pt, .ht-pos, .ht-pill-tag, .ht-bar).tier-ts { fill: var(--wx-tier-ts); }
.ht-leg.tier-c1 { stroke: var(--wx-tier-c1); } :is(.ht-pt, .ht-pos, .ht-pill-tag, .ht-bar).tier-c1 { fill: var(--wx-tier-c1); }
.ht-leg.tier-c2 { stroke: var(--wx-tier-c2); } :is(.ht-pt, .ht-pos, .ht-pill-tag, .ht-bar).tier-c2 { fill: var(--wx-tier-c2); }
.ht-leg.tier-c3 { stroke: var(--wx-tier-c3); } :is(.ht-pt, .ht-pos, .ht-pill-tag, .ht-bar).tier-c3 { fill: var(--wx-tier-c3); }
.ht-leg.tier-c4 { stroke: var(--wx-tier-c4); } :is(.ht-pt, .ht-pos, .ht-pill-tag, .ht-bar).tier-c4 { fill: var(--wx-tier-c4); }
.ht-leg.tier-c5 { stroke: var(--wx-tier-c5); } :is(.ht-pt, .ht-pos, .ht-pill-tag, .ht-bar).tier-c5 { fill: var(--wx-tier-c5); }
.ht-pill-ground { fill: var(--graphic-paper); stroke: var(--graphic-ink); stroke-width: 1; vector-effect: non-scaling-stroke; }
.ht-pill-tagtext { fill: var(--wx-tier-ink); font-weight: 800; }
.ht-pill-tagtext:is(.tier-c4, .tier-c5, .tier-unknown) { fill: var(--wx-chip-ink); }
.ht-pill-text { fill: var(--graphic-ink); font-weight: 700; }
.ht-invest { fill-opacity: .25; stroke-width: 1.5; stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; }
.ht-invest.ht-invest--low { fill: #e6cf45; stroke: #8a7a12; }
.ht-invest.ht-invest--medium { fill: #f2a53b; stroke: #a35d00; }
.ht-invest.ht-invest--high { fill: #cc3323; stroke: #cc3323; }
.ht-invest.ht-invest--unknown { fill: #93a4b5; stroke: #5a6b7c; }
.ht-invest-text { fill: var(--graphic-ink); font-weight: 800; stroke: var(--graphic-paper); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; }
.ht-scale path { stroke: var(--graphic-ink); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.ht-scale text { fill: var(--graphic-ink); font-weight: 700; stroke: var(--graphic-paper); stroke-width: 3px; stroke-linejoin: round; paint-order: stroke; }
.ht-axis { stroke: var(--line); stroke-width: 1; }
.ht-h64 { stroke: var(--ink-2); stroke-width: 1; stroke-dasharray: 3 3; }
.ht-tick { fill: var(--ink-2); }
.ht-val { fill: var(--ink); font-weight: 800; }
.ht-bar { stroke: var(--ink); stroke-width: .75; }
@media (max-width: 719px) {
  .ht-band--wide { display: none; }
  .ht-band--phone { display: flex; margin-inline: calc(-1 * var(--hub-gutter)); }
  .ht-band--phone .ht-basin { border-radius: 0; }
  .ht-elsewhere { display: flex; flex-wrap: wrap; gap: 4px 14px; list-style: none; margin: 8px 0 0; padding: 0; font-size: var(--fs-small); color: var(--ink); }
}

/* The panel's numbered rows: the badge beside the row, as on the map and the card. */
.now-list > li:has(> .now-num) { grid-template-columns: 24px minmax(0, 1fr); column-gap: 10px; }
.now-list > li:has(> .now-num) > :not(.now-num) { grid-column: 2; }
.now-num { grid-column: 1; grid-row: 1 / span 5; align-self: start; box-sizing: border-box; width: 24px; height: 24px; margin-top: 2px;
  display: grid; place-items: center; border: 2px solid var(--graphic-ink); border-radius: 50%;
  background: var(--graphic-paper); color: var(--graphic-ink); font: 800 13px/1 var(--font-body); font-variant-numeric: tabular-nums; }
.now-includes { margin: 2px 0 0; font-size: var(--fs-small); line-height: 1.45; color: var(--ink); }
/* Under 360px (a phone at 200% zoom among them) the badge takes a line of its own over the row,
   so the row keeps the column's width and its unbreakable time run fits. */
@media (max-width: 359px) {
  .now-list > li:has(> .now-num) { grid-template-columns: minmax(0, 1fr); }
  .now-list > li:has(> .now-num) > :not(.now-num) { grid-column: 1; }
  .now-num { grid-row: auto; order: -2; margin-top: 0; }
  /* With every close-up's row shown, twins are numbered more often: the time left and the number
     each stay whole, and the number may take the next line. */
  .now-left { white-space: normal; }
  .now-left > * { white-space: nowrap; }
}
.home-map-clocks { display: flex; flex-wrap: wrap; gap: 0 16px; margin: 4px 0 0; }
.home-map-caption { margin: 4px 0; font-size: var(--fs-small); line-height: 1.4; color: var(--ink-2); }
.home-map-caption a { text-decoration: underline; }
.home-map-down { aspect-ratio: 2.25; display: grid; align-content: center; justify-items: start; gap: 8px;
  padding: 24px 32px; background: var(--surface); border-radius: var(--radius-surface); color: var(--ink); }
.home-map-down p { margin: 0; font-size: var(--fs-remarks); line-height: 1.5; max-width: 52ch; }
.home-map-down .home-map-title { font-size: var(--fs-subhead); font-weight: 800; line-height: 1.3; }

/* The window's map at night (HB-14). The plate's own roles stay the site's `--graphic-*` palette,
   which the parity tests hold and which is never retuned for a theme; what changes on the dark
   theme is which of them the homepage's map is drawn from. Sea and frame take the ocean base, and
   land, coast and state lines the mixes of it and paper that /severe's live tile uses, through the
   base map's own recolour hooks (`--sv-land`, `--sv-coast`, `--sv-border`). NWS's and SPC's fills
   are untouched. What was drawn in dark ink to sit on paper is drawn in paper to sit on the ocean:
   a warning's outline, as the tracker's room cases it, and a discussion's dotted line. Before this
   the map was an 890 by 395 slab of near-white on a dark page. */
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-mapview {
  --sv-land: color-mix(in srgb, var(--graphic-ocean-base) 82%, var(--graphic-paper));
  --sv-coast: color-mix(in srgb, var(--graphic-ocean-base) 50%, var(--graphic-paper));
  --sv-border: color-mix(in srgb, var(--graphic-ocean-base) 62%, var(--graphic-paper));
}
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-mapview :is(.sv-mapframe, .sv-map) { background: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-mapview .sv-map-sea { fill: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-mapview :is(.sv-warning, .sv-md-line, .sv-lead-county) { stroke: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-mapview .sv-lead-label { fill: var(--graphic-paper); stroke: var(--graphic-ocean-base); }
/* The close-ups and the numbers at night, on the same terms: the ocean base for sea and frame, its
   mixes with paper for land and lines, and what was ink on paper drawn in paper on the ocean. The
   badges stay paper with ink digits, as a printed number on any ground. */
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) :is(.home-closer-map, .home-mapview) {
  --sv-land: color-mix(in srgb, var(--graphic-ocean-base) 82%, var(--graphic-paper));
  --sv-border: color-mix(in srgb, var(--graphic-ocean-base) 45%, var(--graphic-paper));
}
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-closer-map { background: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-map-canvas .sv-warning.is-picked { stroke: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-map-canvas .sv-wt.is-picked .sv-watch-edge { stroke: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-map-canvas .home-storm.is-picked .home-storm-cone { stroke: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-closer-locator-land { fill: color-mix(in srgb, var(--graphic-ocean-base) 82%, var(--graphic-paper)); stroke: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-closer-locator-frame { fill: color-mix(in srgb, var(--graphic-paper) 30%, transparent); stroke: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-closer-locator-dot { fill: var(--graphic-paper); stroke: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .sv-map--closer .sv-map-sea { fill: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .sv-map--closer .svc-county { stroke: color-mix(in srgb, var(--graphic-ocean-base) 62%, var(--graphic-paper)); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .sv-map--closer :is(.sv-warning, .sv-lead-county, .svc-lead) { stroke: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .sv-map--closer :is(.svc-county-label, .svc-town, .svc-scale text) { fill: var(--graphic-paper); stroke: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .sv-map--closer .svc-state { fill: color-mix(in srgb, var(--graphic-ocean-base) 30%, var(--graphic-paper)); stroke: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .sv-map--closer .svc-town-dot { fill: var(--graphic-paper); stroke: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .sv-map--closer :is(.svc-motion line, .svc-motion-line, .svc-scale path) { stroke: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .sv-map--closer .svc-motion-head { fill: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .sv-map--closer .svc-storm { fill: var(--graphic-ocean-base); stroke: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-mark-leader { stroke: var(--graphic-paper); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) :is(.ht-basin, .ht-card-map) {
  --sv-land: color-mix(in srgb, var(--graphic-ocean-base) 82%, var(--graphic-paper));
  --sv-coast: color-mix(in srgb, var(--graphic-ocean-base) 50%, var(--graphic-paper));
  background: var(--graphic-ocean-base);
}
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .ht-basin--gap { background: transparent; }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .ht-sea { fill: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .ht-grid { stroke: color-mix(in srgb, var(--graphic-ocean-base) 70%, var(--graphic-paper)); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .ht-eq { stroke: color-mix(in srgb, var(--graphic-ocean-base) 45%, var(--graphic-paper)); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) :is(.ht-eq-label, .ht-basin-name, .ht-basin-note, .ht-grid-label) { fill: color-mix(in srgb, var(--graphic-ocean-base) 25%, var(--graphic-paper)); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) :is(.ht-city, .ht-scale text, .ht-invest-text) { fill: var(--graphic-paper); stroke: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .ht-city-dot { fill: var(--graphic-paper); stroke: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) :is(.ht-past, .ht-scale path) { stroke: var(--graphic-paper); }

:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-city { fill: var(--graphic-paper); stroke: var(--graphic-ocean-base); }
:root:is([data-theme="dark"], [data-dark]:not([data-theme="light"])) .home-city-dot { fill: var(--graphic-paper); stroke: var(--graphic-ocean-base); }

@media (max-width: 719px) {
  /* On a phone the map view takes the map's own shape: in the satellite's 2.25:1 frame the country
     was about 100px tall. */
  /* Under 360px, a 390px phone at 200% zoom among them, the two options share the column in equal
     halves and wrap their words rather than push the page sideways (HB-25); and an Explore card is
     its words alone. Its decorative half (a photograph, or two words set as art) had a third of a
     card too narrow for "overwritten", which ran over the card's heading. */
  @media (max-width: 359px) {
    .home-switch { display: grid; grid-template-columns: 1fr 1fr; }
    .home-switch-option { justify-content: center; text-align: center; padding: 4px 8px; }
    .tool-preview { grid-template-columns: minmax(0, 1fr); }
    .tool-preview > :is(.preview-text-art, picture) { display: none; }
  }
  .home-mapview .sv-mapframe { aspect-ratio: auto; border-radius: 0; margin-inline: calc(-1 * var(--hub-gutter)); }
  .home-mapview .sv-map { width: 100%; height: auto; }
  .home-map-canvas { width: 100%; height: auto; }
  .home-map-down { aspect-ratio: auto; padding: 16px; }
  .home-map-down .home-map-title { font-size: var(--fs-remarks); }
}
