/* IBM Plex Mono 500 IS NOT SHIPPED. The Google request asked for 400, 500,
   600 and 700, and measured on six pages including the two that carry the
   only font-weight:500 rules, the 500 face never loaded: nothing in this site
   renders monospace at 500, so it was a file in the offline shell that no
   reader ever fetched. If a mono 500 is ever wanted, it comes back the same
   way the others did. */
/* ---- FONTS, SELF HOSTED (P-1) ---------------------------------------------
   Every page used to fetch its fonts from fonts.googleapis.com and
   fonts.gstatic.com, which means every reader of every page announced
   themselves to Google before a word rendered. On a site whose privacy page
   promises no trackers and whose About page lists where each number comes
   from, that was the one third party in the path nobody had agreed to. Sports
   and Crypto were moved off it first; this is the same move.

   THESE ARE GOOGLE'S OWN FILES, UNCHANGED, and that is deliberate. They are
   the exact woff2 subsets the Google CSS pointed at, with the exact
   unicode-range values, so the rendering is identical to what shipped
   yesterday: same variable axes (Newsreader carries wght 200-800 and opsz
   6-72, Inter wght 100-900), same glyph coverage, same weight clamping. The
   only change is who serves them.

   WHY NOT SUBSET THEM FURTHER. Measured: subsetting to the 18 non-ASCII
   characters this site actually uses plus all of Latin-1 and Latin Extended-A
   saves 6 percent, because Newsreader's bulk is its two variable axes and not
   its glyph count. Getting to the ~60 KB a sibling site ships means pinning
   the optical-size axis, which changes the letterforms: the wordmark renders
   at 64px and the serif body at 14.5px, and one pinned opsz cannot serve both
   the way the design does today. Privacy was the goal; the bytes are the same
   ones the browser fetched before, and after P-2 they are immutable for a
   year.

   latin-ext is kept because US place names carry accents: the Census set this
   site now draws includes Cañon City and Espanola, and unicode-range means a
   reader only fetches it when such a character actually appears.

   font-display:swap is Google's own setting, preserved. The fallback stacks
   in --serif, --sans and --mono are what render during the swap.
   ========================================================================== */
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;
  src:url('/assets/fonts/plexmono-roman-400-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;
  src:url('/assets/fonts/plexmono-roman-600-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:700;font-display:swap;
  src:url('/assets/fonts/plexmono-roman-700-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;
  src:url('/assets/fonts/plexmono-roman-400-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;
  src:url('/assets/fonts/plexmono-roman-600-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:700;font-display:swap;
  src:url('/assets/fonts/plexmono-roman-700-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;
  src:url('/assets/fonts/inter-roman-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;
  src:url('/assets/fonts/inter-roman-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Newsreader';font-style:italic;font-weight:400 500;font-display:swap;
  src:url('/assets/fonts/newsreader-italic-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Newsreader';font-style:italic;font-weight:400 500;font-display:swap;
  src:url('/assets/fonts/newsreader-italic-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:'Newsreader';font-style:normal;font-weight:400 600;font-display:swap;
  src:url('/assets/fonts/newsreader-roman-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:'Newsreader';font-style:normal;font-weight:400 600;font-display:swap;
  src:url('/assets/fonts/newsreader-roman-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}

/* ============================================================================
   GoCheckMyWeather - design system
   Media-desk chassis (News/Sports/Crypto dashboard language) on the family
   master palette. Two modes: light is default, dark follows system preference
   unless the user overrides via html[data-theme]. Every text/ground pair here
   was contrast-checked to WCAG AA before it shipped; Medium Teal #3F8F97 and
   Soft Teal #79B8BE are decorative-only in light mode (family -ink standard).
   Motion discipline: nothing autoplays, nothing blinks; transitions are subtle
   and removed under prefers-reduced-motion.
   ========================================================================== */

:root{
  --paper:#F5F4EF;      --card:#FFFFFF;       --ink:#1C1E1F;
  /* An OPAQUE companion to --card, for anything that other content scrolls
     behind. In live-sky mode --card is rgba(12,18,26,.88), and a pinned
     control painted with it let the chips sliding under it show through:
     measured on the section nav, where COASTAL ghosted through the pinned
     WATER chip. Same colour, no alpha. */
  --card-solid:#FFFFFF;
  --muted:#585E62;      --faint:#5F6569;      --line:#E3E0D6;
  --rule:#184454;       /* Deep Teal: masthead rule, brand, kickers */
  --accent:#3F8F97;     /* Medium Teal: decorative accents only */
  --accent-soft:#79B8BE;/* Soft Teal: decorative data accents only */
  --teal-ink:#2A6B72;   /* teal TEXT that must pass AA */
  --copper:#9C4A21;     /* Burnt Copper: alert-adjacent accents only */
  --copper-ink:#8F431D; /* copper TEXT that must pass AA */
  --link:#155A74;       --link-hover:#0D3D50;
  --chip-bg:#ECE9DF;    --chip-fg:#44494E;
  --sev-extreme-fg:#7A1F1F; --sev-extreme-bg:#F6DEDE;
  --sev-severe-fg:#8F431D;  --sev-severe-bg:#F7E4D9;
  --sev-moderate-fg:#6E5300;--sev-moderate-bg:#F6ECCB;
  --sev-minor-fg:#28546B;   --sev-minor-bg:#DFEAF1;
  --ok-fg:#1F6B45;
  --map-sea:#E4EBEB; --map-land:#F8F7F1; --map-coast:#A9B8B8; --map-line:#CBD2CC;
  --map-county:#B9C6C4;
  --map-label:#242B30; --map-label-halo:rgba(255,255,255,.95);
  --shadow:0 22px 50px -34px rgba(20,26,30,.4);
  --serif:'Newsreader',Georgia,'Times New Roman',serif;
  --sans:'Inter',system-ui,-apple-system,sans-serif;
  --mono:'IBM Plex Mono',ui-monospace,'SFMono-Regular',monospace;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]){
    --paper:#1F2021; --card:#2B2B2B; --card-solid:#2B2B2B; --ink:#EDEBE6;
    --muted:#ADB2B3; --faint:#A0A5A7; --line:#3A3B3C;
    --rule:#79B8BE; --accent:#3F8F97; --accent-soft:#79B8BE;
    --teal-ink:#8FC6CC; --copper:#E0906A; --copper-ink:#E0906A;
    --link:#8FC6CC; --link-hover:#B6DCE0;
    --chip-bg:#353637; --chip-fg:#C4C9CA;
    --sev-extreme-fg:#F0B9B9; --sev-extreme-bg:#3A2020;
    --sev-severe-fg:#EFB694;  --sev-severe-bg:#3A2A1E;
    --sev-moderate-fg:#EAD28A;--sev-moderate-bg:#393012;
    --sev-minor-fg:#A9CBDD;   --sev-minor-bg:#1E3340;
    --ok-fg:#7FC9A0;
    --map-sea:#191F22; --map-land:#2A2E30; --map-coast:#55605F; --map-line:#3C4442;
  --map-county:#48534F;
    --map-county:#48534F;
  --map-label:#DCE2E5; --map-label-halo:rgba(8,12,16,.95);
    --shadow:0 22px 50px -30px rgba(0,0,0,.7);
  }
}
:root[data-theme=dark]{
  --paper:#1F2021; --card:#2B2B2B; --card-solid:#2B2B2B; --ink:#EDEBE6;
  --muted:#ADB2B3; --faint:#A0A5A7; --line:#3A3B3C;
  --rule:#79B8BE; --accent:#3F8F97; --accent-soft:#79B8BE;
  --teal-ink:#8FC6CC; --copper:#E0906A; --copper-ink:#E0906A;
  --link:#8FC6CC; --link-hover:#B6DCE0;
  --chip-bg:#353637; --chip-fg:#C4C9CA;
  --sev-extreme-fg:#F0B9B9; --sev-extreme-bg:#3A2020;
  --sev-severe-fg:#EFB694;  --sev-severe-bg:#3A2A1E;
  --sev-moderate-fg:#EAD28A;--sev-moderate-bg:#393012;
  --sev-minor-fg:#A9CBDD;   --sev-minor-bg:#1E3340;
  --ok-fg:#7FC9A0;
  --map-sea:#191F22; --map-land:#2A2E30; --map-coast:#55605F; --map-line:#3C4442;
  --map-county:#48534F;
  --map-label:#DCE2E5; --map-label-halo:rgba(8,12,16,.95);
  --shadow:0 22px 50px -30px rgba(0,0,0,.7);
}

*{box-sizing:border-box}
[hidden]{display:none!important}
html{scroll-behavior:smooth}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--sans);font-size:17px;line-height:1.6;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
.wrap{max-width:1080px;margin:0 auto;padding:0 22px}
.narrow{max-width:760px}
a{color:var(--link);text-decoration:none}
a:hover{color:var(--link-hover);text-decoration:underline}
img{max-width:100%;height:auto}
:focus-visible{outline:3px solid var(--rule);outline-offset:2px;border-radius:2px}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.skip-link{position:absolute;left:-9999px;top:0;background:var(--card);color:var(--ink);
  padding:10px 16px;z-index:200;border:2px solid var(--rule);border-radius:0 0 6px 0;font-weight:600}
.skip-link:focus{left:0}

/* ---- system bar safe areas (the TWA is the site) ----------------------------
   Added 2026-08-25. Android 15 draws apps targeting SDK 35+ edge to edge, and
   this app IS this site inside Chrome: there is no native onCreate to call
   enableEdgeToEdge() in, so the inset handling lives here, in CSS, and ships
   to installed phones with no Play review. viewport-fit=cover (set on every
   page and in the guide generator) is what makes env() nonzero.

   The rule is the design rule: the SKY stays full bleed (.wxsky is fixed,
   inset:0, and deliberately gets no padding, so the gradient runs behind the
   status bar), and the CONTENT is inset so the masthead never sits under the
   clock and the page bottom never hides under the gesture handle. On devices
   without a cutout every env() is 0 and all of this is inert. */
:root{
  --sat:env(safe-area-inset-top,0px);
  --sab:env(safe-area-inset-bottom,0px);
  --sal:env(safe-area-inset-left,0px);
  --sar:env(safe-area-inset-right,0px);
}
body{padding-left:var(--sal);padding-right:var(--sar);padding-bottom:var(--sab)}

/* ---- masthead -------------------------------------------------------------- */
.top-rule{height:4px;background:var(--rule)}
.masthead{background:var(--card);border-bottom:1px solid var(--line)}
.masthead .wrap{padding-top:calc(16px + var(--sat));padding-bottom:12px}
.mh-top{display:flex;align-items:baseline;justify-content:space-between;gap:16px;flex-wrap:wrap}
.mh-dateline{font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.mh-family{font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);font-weight:600}
.mh-brand{display:flex;align-items:center;gap:12px;text-decoration:none;margin-top:8px}
.mh-brand:hover{text-decoration:none}
.mh-mark{width:38px;height:38px;flex:none}
.mh-word{font-family:var(--serif);font-weight:600;color:var(--ink);line-height:1;
  font-size:clamp(28px,5.2vw,46px);letter-spacing:-.01em}
.mh-word-site{color:var(--rule);font-style:italic}
.mh-slogan{font-family:var(--serif);font-style:italic;color:var(--muted);
  font-size:clamp(13px,1.6vw,16px);text-align:right;max-width:240px;margin-left:auto}
.mh-nav{border-top:1px solid var(--line);border-bottom:1px solid var(--line);background:var(--card);
  position:sticky;top:var(--sat);z-index:50}
.mh-nav .wrap{display:flex;gap:2px;align-items:center;overflow-x:auto}
.mh-nav a{font-family:var(--mono);font-size:12.5px;letter-spacing:.05em;text-transform:uppercase;
  color:var(--muted);padding:11px 12px;white-space:nowrap;border-bottom:3px solid transparent}
.mh-nav a:hover{color:var(--ink);text-decoration:none}
.mh-nav a.active{color:var(--ink);border-bottom-color:var(--rule);font-weight:600}
.theme-toggle{margin-left:auto;font-family:var(--mono);font-size:12px;letter-spacing:.05em;
  text-transform:uppercase;color:var(--muted);background:none;border:1px solid var(--line);
  border-radius:6px;padding:6px 10px;cursor:pointer;white-space:nowrap}
.theme-toggle:hover{color:var(--ink);border-color:var(--muted)}
/* L-1. Between roughly 600 and 800px the nav wrapped and About sat alone on a
   second row beside the theme button, on every page. The rule that did it was
   deliberate: it traded a scrolling strip for wrapped rows at a legible size
   with 44px targets, on the reasoning that a tiny strip scrolling off-screen
   with no hint was worse. That reasoning is sound and the conclusion was not,
   because the band it applies to is narrower than it looks: below 640px this
   nav is hidden entirely (see the .tabbar rule near the end of this file) and
   the tab bar takes over, so the wrap only ever showed between 641 and 700px,
   where it produced exactly the orphan row the audit found.

   So the row scrolls, and the objection is answered rather than ignored: the
   legible size and the tap targets stay below 900px, and a fade at the right
   edge says there is more. One row at every width where this nav is visible
   at all. */
@media (max-width:900px){
  /* a two or three row bar must not ride sticky over a small screen; one row
     may, and does */
  .mh-nav a{font-size:14px;padding:12px 13px}
  .theme-toggle{min-height:44px;padding:6px 14px}
  /* THE FADE IS THE HINT the old comment asked for, and it is painted OVER the
     bar rather than masked ONTO it. A mask on the scroller fades whatever sits
     at the right edge permanently, and what sits there is the theme control:
     scrolling the container does not move the mask, so the control would be
     half transparent at every scroll position. An overlay outside the scroller
     dims the ground, not the content, and it is drawn in the bar's own colour
     rather than a guess at it, which is why sky mode restates it below. */
  /* NO position HERE. Writing position:relative for the overlay's sake
     silently cancelled the bar's stickiness, which the base rule sets and
     which is wanted now that the bar is one row: the old rule made it static
     only because a two or three row bar riding over a small screen was too
     much furniture. Sticky is itself a positioned value, so it is already the
     containing block the overlay below needs. */
  .mh-nav .wrap{scrollbar-width:none}
  .mh-nav .wrap::-webkit-scrollbar{display:none}
  .mh-nav::after{
    content:"";position:absolute;right:0;top:0;bottom:0;width:26px;
    background:linear-gradient(90deg,rgba(0,0,0,0),var(--card));
    pointer-events:none;
  }
  /* the toggle is the last thing in the scroll rather than pinned: pinning it
     needs a second scroll container, and this nav is static markup repeated
     across nineteen pages */
  .theme-toggle{margin-left:auto}
}
/* the sky paints its own ground under the bar, so the fade has to match it or
   it reads as a grey smear over a blue sky */
:root.sky-active .mh-nav::after{
  background:linear-gradient(90deg,rgba(0,0,0,0),rgba(10,15,22,.72));
}

/* ---- section heads and cards ---------------------------------------------- */
main{padding:26px 0 40px}
.sec-head{display:flex;align-items:baseline;gap:14px;margin:26px 0 14px}
.sec-head h2{font-family:var(--serif);font-weight:600;font-size:24px;margin:0;letter-spacing:-.01em}
.sec-head .bar{flex:1;height:1px;background:var(--line)}
.sec-head .sec-note{font-family:var(--mono);font-size:11px;color:var(--faint);white-space:nowrap}
/* nowrap is right on a wide screen and wrong on a phone: the hurricane
   section head carries "advisory 46 | 1100 AM HST Sun Aug 23 2026", which
   measured 271px and pushed the whole page 29px sideways at 390. It wraps
   below 640 instead, where there is no room to hold it on one line. */
@media (max-width:640px){
  .sec-head .sec-note{white-space:normal;min-width:0}
  .sec-head{flex-wrap:wrap;row-gap:4px}
}
.card{background:var(--card);border:1px solid var(--line);border-radius:10px;
  box-shadow:var(--shadow);padding:18px 20px;margin:0 0 18px}
.card h3{font-family:var(--serif);font-weight:600;font-size:19px;margin:0 0 10px;letter-spacing:-.01em}
.card .kicker{display:block;font-family:var(--mono);font-size:11px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--teal-ink);font-weight:600;margin-bottom:4px}
.card-foot{display:flex;flex-wrap:wrap;gap:6px 16px;justify-content:space-between;align-items:baseline;
  border-top:1px solid var(--line);margin-top:14px;padding-top:10px}
.stamp{font-family:var(--mono);font-size:11px;color:var(--faint)}
.stamp.stale{color:var(--copper-ink);font-weight:600}
.src{font-family:var(--mono);font-size:11px;color:var(--faint)}
/* ---- Today against the record book ----------------------------------------
   One shared scale from the coldest this date has ever been to the warmest,
   with today's forecast range, the normal, and the current reading on it.

   THE GRADIENT IS SAMPLED FROM THE SCALE IN JS, not declared here. A fixed
   gradient painted across the bar would put red at the right hand end whether
   that end is 94 degrees or 44, which makes the colour decorative. Sampling
   the temperature ramp at the real values means a colour on this bar always
   means the same temperature, and the delineation from blue through green and
   yellow to red is the full saturated ramp rather than a washed out three
   stop version of it.

   Every mark carries its own label in ONE row. The reading used to float above
   the bar with its number on a second line and a separate legend below that,
   which put four values on three rows and read as clutter. */
.rb{margin-top:6px}
.rb-cap{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;
  color:#C9D2D8;margin:0 0 8px}
.rb-track{position:relative;height:58px;margin:30px 0 8px}
/* the bar is a thin rail; the marks hang below it, never on top of the colour */
.rb-rail{position:absolute;left:0;right:0;top:14px;height:9px;border-radius:99px;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.25)}

/* today's forecast range: a bracket over the bar, because it is a different
   kind of claim from the historical marks under it */
.rb-today{position:absolute;top:6px;height:7px;border-radius:99px;
  border:1.5px solid rgba(255,255,255,.85);border-bottom:0;
  box-shadow:0 -1px 6px rgba(0,0,0,.35)}
.rb-today-lab{position:absolute;top:-16px;transform:translateX(-50%);white-space:nowrap;
  font-family:var(--mono);font-size:10px;letter-spacing:.06em;color:#F4F1EA;
  text-shadow:0 1px 4px rgba(0,0,0,.8)}

.rb-tick{position:absolute;top:10px;transform:translateX(-50%);text-align:center;z-index:2}
.rb-tick i{display:block;width:2px;height:17px;margin:0 auto;border-radius:2px;
  background:rgba(255,255,255,.75);box-shadow:0 0 0 1px rgba(10,15,20,.45)}
.rb-tick b{display:block;font-family:var(--mono);font-size:10px;font-weight:400;
  margin-top:6px;white-space:nowrap;color:#DDE4E8;text-shadow:0 1px 4px rgba(0,0,0,.75)}
.rb-norm i{background:#fff;width:3px}
.rb-norm b{color:#fff;font-weight:600}

/* the reading: the only mark here that is a measurement rather than
   climatology, so it is the only one that is lit */
.rb-now{z-index:3}
.rb-now i{width:4px;height:25px;background:#fff;margin-top:-4px;
  box-shadow:0 0 14px 3px var(--t-ink-dark,#fff), 0 0 26px 7px var(--t-ink-dark,transparent)}
.rb-now b{color:#fff;font-weight:700;font-size:11px}

/* ---- hurricane impact view row + per-view notes (wx-hurricane.js) -------- */
.hx-views{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 12px}
.hx-views .btn-quiet{min-height:44px}
.hx-viewbox{margin-top:10px}
.hx-viewbox p{margin:8px 0 0;font-size:13.5px;line-height:1.6}
.hx-viewbox .map-legend{margin:10px 0 0}

/* arrival-time isochrone labels (Leaflet tooltips, permanent). While the
   arrival view is selected the city labels step back so the times own the
   map's text layer. */
.arr-lab{background:transparent;border:0;box-shadow:none;
  font-family:var(--mono);font-size:10.5px;font-weight:600;color:var(--ink);
  text-shadow:0 0 4px var(--card),0 0 7px var(--card)}
.arr-lab::before{display:none}
.map-frame.view-arrival .wx-city{opacity:.4}

/* the wind-odds probability ramp: NOAA's own 10-class scale as one bar */
.pw-ramp{display:inline-block;vertical-align:middle;width:150px;height:12px;border-radius:6px;
  border:1px solid var(--line);
  background:linear-gradient(90deg,#267300,#38A800,#55FF00,#E6E600,#FFD37F,#E69800,#FFAA00,#E60000,#A83800,#A900E6)}

/* ---- W-20, W-22: the storm map --------------------------------------------
   The second thing on the card, under the header, never folded. At phone
   width it runs to the card's edges and is square; wider, it keeps the tall
   frame. The satellite row sits on it, the overlay chips under it. */
.hx-hero{margin:18px 0 6px}
.hx-caution{margin:0 0 10px;font-size:13px;line-height:1.5;color:var(--muted)}
.hx-hero .sat-views{margin:0 0 8px}
.hx-hero .hx-views{margin:8px 0 6px}
@media (max-width:640px){
  .map-frame.hx-map{height:auto;aspect-ratio:1/1;margin:0 -20px;border-radius:0;border-left:0;border-right:0}
  /* each control row stays ONE row at phone width: compact chips, and a row
     that still overflows (Surge depth makes five) scrolls sideways */
  .hx-hero .hx-views{flex-wrap:nowrap;overflow-x:auto;gap:6px;scrollbar-width:none;margin-right:-20px;padding-right:20px}
  .hx-hero .hx-views::-webkit-scrollbar{display:none}
  .hx-hero .hx-views .btn-quiet{flex:none;padding:0 10px;font-size:13.5px}
}
/* the storm's position: the tropical cyclone symbol, hollow, a dot's size
   plus a third, dark line on a light halo in both themes. pointer-events:none
   so the tau=0 dot under it keeps its tap. */
.tc-sym{pointer-events:none}
.tc-sym svg{display:block;width:100%;height:100%;overflow:visible}
.tc-sym g{fill:none;stroke-linecap:round}
.tc-sym .tc-halo{stroke:#fff;stroke-width:4.4;opacity:.9}
.tc-sym .tc-line{stroke:#111;stroke-width:2}
/* each forecast position's day and hour, as the advisory gives them */
.fp-lab{background:transparent;border:0;box-shadow:none;padding:0;
  font-family:var(--mono);font-size:10.5px;font-weight:600;color:var(--ink);
  text-shadow:0 0 2px var(--card),0 0 3px var(--card),0 0 4px var(--card)}
.fp-lab::before{display:none}
.hc-credits{margin:18px 0 0}
/* a compact storm card (W-20, W-22): the whole header, and Map at its end */
.sc-open{display:block;margin:12px 0 2px;min-height:44px}
/* W-22: below the map, closed folds, each summary one line of fact */
.storm-card .hx-folds .card + .card{margin-top:0}
.hx-folds .card{border-top:1px solid var(--line)}
.hx-fold summary{list-style:none;cursor:pointer;position:relative;padding:11px 0 11px 18px}
.hx-fold summary::-webkit-details-marker{display:none}
.hx-fold summary::before{content:"";position:absolute;left:1px;top:17px;width:6px;height:6px;
  border-right:1.5px solid var(--link);border-bottom:1.5px solid var(--link);transform:rotate(-45deg)}
.hx-fold[open] summary::before{transform:rotate(45deg);top:15px}
.hx-fold summary:focus-visible{outline:3px solid var(--rule);outline-offset:2px;border-radius:4px}
.hx-fold-k{display:block;font-family:var(--mono);font-size:11px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--teal-ink);font-weight:600}
.hx-fold-line{display:block;margin-top:3px;font-size:14px;line-height:1.45;color:var(--ink)}
.hx-fold-body{padding:0 0 12px}
.hx-fold-meta{font-family:var(--mono);font-size:11px;color:var(--faint);margin:0 0 8px}
.hx-shaded{color:var(--muted)}
.bo-offshore{margin-left:8px;font-family:var(--mono);font-size:10px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint)}

/* dropped to a second line because it collided with a label already placed */
.rb-tick-drop b{margin-top:24px}
.rb-say{font-size:15px;line-height:1.55;color:var(--ink);margin:22px 0 0}
@media (max-width:640px){
  .rb-track{height:64px}
  .rb-tick b{font-size:9px}
  /* four labels in one row will collide on a phone, so they alternate */
  .rb-tick:nth-child(even) b{margin-top:20px}
}

/* ---- the week ---------------------------------------------------------------
   Four columns, matching the design prototype: what day, what it will be like,
   how warm across one shared scale, and how likely to rain. The earlier version
   showed only the bars and pushed conditions, rain and wind into a collapsed
   table, which made the board prettier and less useful.

   The bar tints by its own two temperatures, so a cold day and a hot day of the
   same swing do not look identical. Colour is never the only carrier: both
   numbers sit at the ends of every bar.

   The low and high labels ride the bar ends rather than sitting in fixed
   columns, which is what makes the week's shape legible at a glance. That needs
   horizontal padding on the rail so a label at 0 or 100 percent has somewhere
   to go. */
.wk{margin:2px 0 0}
.wk-head,.wk-row{display:grid;grid-template-columns:6.4em minmax(0,1fr) 6.2em 7.2em 4.6em;
  gap:0 16px;align-items:center}
/* the type size sits on the spans, not the head, so the head's em columns are
   the rows' em columns and each label stands over its own column */
.wk-head{padding-bottom:9px;border-bottom:1px solid var(--line)}
.wk-head>span{font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--faint);white-space:nowrap}
.wk-h-right{text-align:right}
.wk-row{padding:11px 0;border-bottom:1px solid var(--line)}
.wk-row:last-child{border-bottom:0}
.wk-day{font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wk-cond{display:flex;align-items:center;gap:10px;min-width:0}
.wk-cond-text{font-family:var(--serif);font-style:italic;font-size:14.5px;color:var(--ink);line-height:1.35}
/* 44px is a measured floor, not a taste call. Below it these illustrations stop
   distinguishing themselves from each other, which would make the icon actively
   misleading rather than merely small. overflow:visible so a ray or a streak
   reaching past the viewBox is never clipped. */
/* D-5: the separator between the day and night rain chances, spaced so the two
   figures read as two facts rather than one run-on number. */
.wk-pop-sep{margin:0 .4em;color:var(--faint)}
/* B-2: the icon family at 20px in the rows */
.wk-icon{flex:none;width:20px;height:20px;display:block}
.wk-icon svg{width:100%;height:100%;display:block;overflow:visible}
/* L-7: the secondary mark. Small, bottom-right, sitting on the primary
   illustration with a ring in the card ground so it reads as a separate mark
   rather than part of the drawing underneath. 40 percent is the smallest that
   still says which condition it is at the 44px size these were measured at.
   The primary needs position:relative only when a mark is actually present,
   hence .has-sub rather than changing .wk-icon for every row. */
.wk-icon.has-sub{position:relative}
.wk-icon-sub{position:absolute;right:-5px;bottom:-5px;width:12px;height:12px;
  border-radius:50%;background:var(--card);box-shadow:0 0 0 2px var(--card);
  display:block;overflow:visible}
.wk-icon-sub svg{width:100%;height:100%;display:block}
:root.sky-active .wk-icon-sub{background:#101A24;box-shadow:0 0 0 2px #101A24}
.wk-track{position:absolute;left:0;right:0;top:11px;height:4px;border-radius:99px;background:var(--chip-bg)}
.wk-bar{position:absolute;top:7px;height:12px;border-radius:99px}
.wk-lo,.wk-hi{position:absolute;top:5px;font-family:var(--mono);font-size:12.5px;line-height:16px;
  white-space:nowrap;font-weight:600;color:var(--t-ink-light)}
.wk-t,.wkb-lo,.wkb-hi{color:var(--t-ink-light)}
/* The temperature ink is emitted as BOTH values and chosen here, so it can
   never be picked for a ground that changes a moment later. The saturated ramp
   fails AA as text on a white card at every stop (1.68 to 3.34), so light mode
   uses the same hues darkened until each clears 4.5; dark and sky grounds keep
   the saturated values. */
:root.sky-active .wk-lo,:root.sky-active .wk-hi,
:root[data-theme=dark] .wk-lo,:root[data-theme=dark] .wk-hi,
:root.sky-active .wk-t,:root.sky-active .wkb-lo,:root.sky-active .wkb-hi,
:root[data-theme=dark] .wk-t,:root[data-theme=dark] .wkb-lo,:root[data-theme=dark] .wkb-hi{color:var(--t-ink-dark)}
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]) .wk-lo,:root:not([data-theme=light]) .wk-hi,
  :root:not([data-theme=light]) .wk-t,:root:not([data-theme=light]) .wkb-lo,:root:not([data-theme=light]) .wkb-hi{color:var(--t-ink-dark)}
}
.wk-lo{transform:translateX(-100%);padding-right:9px}
.wk-hi{padding-left:9px}
/* B-4: the row's numbers as text, high first; the band above draws them */
.wk-temps{font-family:var(--mono);font-size:12.5px;font-weight:600;white-space:nowrap}
.wk-t-sep{margin:0 .35em;color:var(--faint);font-weight:400}

/* ---- B-4: the week as a band ----------------------------------------------
   One scale for every day, so a bar's length is the day's swing and its place
   is where the day sits in the week. The numbers ride the bar ends; the track
   is inset so a number at either end of the scale has room. */
.wkb{margin:6px 0 12px;padding-bottom:10px;border-bottom:1px solid var(--line)}
.wkb-row{display:grid;grid-template-columns:4.6em minmax(0,1fr) 3.6em;gap:0 10px;align-items:center;height:30px}
.wkb-day{font-family:var(--mono);font-size:11.5px;font-weight:600;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wkb-track{position:relative;height:100%;margin:0 2.5em;min-width:0}
.wkb-bar,.wkb-only{position:absolute;top:50%;height:8px;border-radius:99px;transform:translateY(-50%)}
.wkb-only{width:8px;margin-left:-4px}
.wkb-lo,.wkb-hi{position:absolute;top:50%;font-family:var(--mono);font-size:11px;font-weight:600;
  line-height:14px;white-space:nowrap}
.wkb-lo{transform:translate(-100%,-50%);padding-right:5px}
.wkb-hi{transform:translateY(-50%);padding-left:5px}
.wkb-now{position:absolute;top:50%;width:4px;height:16px;border-radius:2px;background:#fff;
  box-shadow:0 0 0 1.5px rgba(10,14,20,.7);transform:translate(-50%,-50%)}
.wkb-rain{display:flex;align-items:center;gap:6px;font-family:var(--mono);font-size:11px;
  color:var(--teal-ink);font-weight:600}
.wkb-rain.dry{color:var(--faint);font-weight:400}
.wkb-raintrack{flex:none;width:22px;height:6px;border-radius:3px;background:var(--chip-bg);overflow:hidden}
.wkb-rainbar{display:block;height:100%;border-radius:3px;background:#3D8FC6}
@media (min-width:721px){
  .wkb-row{grid-template-columns:6.4em minmax(0,1fr) 5.4em;gap:0 16px}
  .wkb-raintrack{width:40px}
}
.wk-meta{font-family:var(--mono);font-size:10.5px;color:var(--faint);text-align:right;line-height:1.5}
.wk-meta b{display:block;font-weight:600;color:var(--teal-ink)}
.wk-meta b.dry{color:var(--faint);font-weight:400}
/* Narrow: the bar keeps its own line under the day and conditions, because at
   this width four columns would crush the rail to nothing and the labels at its
   ends would collide. */
@media (max-width:720px){
  .wk-head{display:none}
  .wk-row{grid-template-columns:1fr auto;gap:2px 12px;padding:12px 0}
  .wk-cond{grid-column:1;font-size:14px}
  .wk-meta{grid-column:2;grid-row:1/3;align-self:center}
  /* D-5's two figures stack here rather than sitting inline. The meta column is
     auto-width at this breakpoint, so the inline form widened it enough to
     squeeze the condition text into a third line and break L-6's two-line
     rule. Stacked, the column stays about as wide as "42% rain" was. */
  .wk-meta .wk-pop-two{display:flex;flex-direction:column;align-items:flex-end;line-height:1.25}
  .wk-meta .wk-pop-sep{display:none}
  .wk-day{grid-column:1;grid-row:1}
  /* the numbers share the day's line, at its right, so the row stays two lines */
  .wk-temps{grid-column:1;grid-row:1;justify-self:end}
}

.board{display:grid;grid-template-columns:1fr 1fr;gap:18px;align-items:start}
.board .card{margin:0;min-width:0}
.board .span2{grid-column:1/-1}
@media (max-width:820px){.board{grid-template-columns:1fr}}
.table-scroll{overflow-x:auto}

/* ---- degrade and skeleton states ------------------------------------------ */
.unavail{color:var(--muted);font-size:15px}
.unavail a{font-weight:600}
.failsafe{border-left:4px solid var(--copper);padding:10px 14px;background:var(--chip-bg);
  border-radius:0 8px 8px 0;color:var(--ink);font-size:15px}
.failsafe a{font-weight:600}
.skel{background:var(--chip-bg);border-radius:6px;min-height:14px}
.skel+.skel{margin-top:10px}
.reveal-in{opacity:1;transition:opacity .35s ease}
@media (prefers-reduced-motion:reduce){.reveal-in{transition:none}}

/* ---- location bar ---------------------------------------------------------- */
.loc-bar{background:var(--card);border:1px solid var(--line);border-radius:10px;
  padding:14px 18px;margin:0 0 8px;display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.loc-bar form{display:flex;gap:8px;flex-wrap:wrap;align-items:center;flex:1;min-width:260px}
.loc-bar label{font-family:var(--mono);font-size:11px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted);font-weight:600}
.loc-bar input[type=text]{flex:1;min-width:150px;font:inherit;font-size:15px;color:var(--ink);
  background:var(--paper);border:1px solid var(--line);border-radius:7px;padding:8px 12px}
.loc-bar input[type=text]::placeholder{color:var(--faint)}
.btn{font-family:var(--sans);font-weight:600;font-size:14px;color:#FFFFFF;
  background:var(--rule);border:1px solid var(--rule);border-radius:7px;
  padding:8px 14px;cursor:pointer}
:root[data-theme=dark] .btn,html[data-theme=dark] .btn{color:#1A2A2E}
@media (prefers-color-scheme:dark){:root:not([data-theme=light]) .btn{color:#1A2A2E}}
.btn:hover{filter:brightness(1.08)}
.btn-quiet{font-family:var(--sans);font-weight:600;font-size:14px;color:var(--teal-ink);
  background:none;border:1px solid var(--line);border-radius:7px;padding:8px 14px;cursor:pointer}
.btn-quiet:hover{border-color:var(--teal-ink)}
/* toggle buttons keep a constant name; pressed state shows visually too */
.btn-quiet[aria-pressed=true]{background:var(--chip-bg);border-color:var(--teal-ink);box-shadow:inset 0 1px 3px rgba(0,0,0,.15)}
.loc-current{display:flex;gap:10px;align-items:center;font-size:15px}
.loc-current b{font-weight:600}
.loc-clear{font-family:var(--mono);font-size:11px;color:var(--muted);background:none;
  border:1px solid var(--line);border-radius:6px;padding:4px 8px;cursor:pointer}
.loc-clear:hover{color:var(--ink)}
.loc-note{font-size:13.5px;color:var(--muted);width:100%}
.loc-choices{display:flex;flex-wrap:wrap;gap:8px;width:100%;margin-top:4px}

/* ---- winter figures on the week rows (G-9) ---------------------------------
   Snow and ice join the rain and wind in the meta column, and only on the days
   the office actually publishes an amount. Each carries its own title saying
   how many six hour forecasts it is the sum of. */
.wk-snow{display:block;color:var(--link);font-weight:600}
.wk-ice{display:block;color:var(--sev-severe-fg);font-weight:600}

/* ---- sun and moon on the week rows (G-4) -----------------------------------
   A fifth column: sunrise over sunset in a short clock form, with the moon
   drawn from its illumination beside them. The short form is deliberate, since
   this column sits at the end of a row that already carries four things and
   "7:07 AM" is the string that overflowed L-4's sixth tile. */
.wk-sun{display:flex;flex-direction:column;align-items:flex-end;justify-content:center;gap:1px;
  font-family:var(--mono);font-size:10px;color:var(--faint);line-height:1.35}
.wk-sun-t{white-space:nowrap}
.wk-moon{width:14px;height:14px;margin-top:2px;overflow:visible}
.wk-moon-dark{fill:none;stroke:var(--line);stroke-width:1.1}
.wk-moon-lit{fill:var(--faint)}
/* MUST SIT AFTER THE BASE RULE ABOVE, not in the row's own narrow block
   further up the file. That block is earlier in source order, so the base rule
   won and the phone kept the stacked column: measured at 45px a row where it
   should have been 16, which is 50px of extra height on every one of seven
   rows. Equal specificity means the later rule wins, and this is the later
   rule. */
@media (max-width:720px){
  /* A fifth column has nowhere to go in a two column row, so the cell becomes
     a horizontal strip under the condition text instead. */
  .wk-sun{grid-column:1;grid-row:3;flex-direction:row;align-items:center;
    justify-content:flex-start;gap:8px;margin-top:0}
  .wk-moon{margin-top:0;width:12px;height:12px}
}

/* ---- alerts on this device (G-1) -------------------------------------------
   A card, because it is a commitment rather than a toggle: it carries what the
   site will do, what it will not, and the limit, in every one of its four
   states including the ones with no button. */
.nt-box{background:var(--card);border:1px solid var(--line);border-radius:10px;
  box-shadow:var(--shadow);padding:18px 20px;margin:18px 0 0}
.nt-say{font-size:14.5px;line-height:1.55;margin:0 0 12px;max-width:62ch}
.nt-act{display:flex;flex-wrap:wrap;gap:10px}
.nt-status{font-family:var(--mono);font-size:12px;color:var(--teal-ink);margin:10px 0 0}
/* The limit is not fine print. Same size as the offer above it, because a
   reader deciding whether to depend on this needs both at once. */
.nt-limit{font-size:13.5px;line-height:1.55;color:var(--muted);margin:12px 0 0;
  padding-top:12px;border-top:1px solid var(--line);max-width:62ch}

/* ---- share this board (G-8) -----------------------------------------------
   Sits beside the Change control, in the same quiet register: this is an
   action a reader looks for rather than one the page pushes at them. */
.wx-share{font-family:var(--mono);font-size:11px;color:var(--muted);background:none;
  border:1px solid var(--line);border-radius:6px;padding:4px 8px;cursor:pointer}
.wx-share:hover{color:var(--ink)}
.wx-share:focus-visible{outline:3px solid var(--rule);outline-offset:2px}
.wx-share-said{font-family:var(--mono);font-size:11px;color:var(--teal-ink)}

/* ---- rivers: the flood-stage fact, once and then marked (L-8) --------------
   The eighty-word sentence repeated on every card and buried the readings it
   explained. It is said once above the cards; each card carries a chip, with
   the full sentence as its title, so a reader looking at one card still learns
   the threshold was not merely omitted. */
.rv-nodef-once{font-size:13.5px;line-height:1.55;color:var(--muted);margin:0 0 14px;max-width:66ch}
.rv-nodef-chip{display:inline-block;font-family:var(--mono);font-size:11px;letter-spacing:.04em;
  color:var(--chip-fg);background:var(--chip-bg);border-radius:999px;padding:4px 10px;margin-top:8px}

/* ---- an area being watched (D-4) -------------------------------------------
   Not .unavail, which is the class for something missing: an area at 70
   percent is the opposite of missing, and it should not inherit the muted
   tone the page uses for an absence. */
.hc-watched{font-size:15px;line-height:1.55;color:var(--ink);margin:0}

/* ---- the forecaster's discussion (G-6) -------------------------------------
   The only page on this site carrying a person's prose rather than a machine's
   numbers, and it is set like prose: a reading measure, generous leading, and
   the office's own section names as real headings. */
.afd-who{font-size:15px;margin:0 0 6px}
.afd-intro{font-size:14px;color:var(--muted);margin:0 0 18px;max-width:60ch}
.afd-sec{margin:0 0 22px}
.afd-h{font-family:var(--mono);font-size:12px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--teal-ink);margin:0 0 2px;font-weight:600}
/* Ours, not theirs, so it never reads as part of the heading. */
.afd-hint{font-size:12.5px;color:var(--faint);font-style:italic;margin:0 0 10px}
.afd-p{font-size:15.5px;line-height:1.62;margin:0 0 12px;max-width:68ch}
.afd-list{margin:0 0 12px;padding-left:20px}
.afd-list li{font-size:15.5px;line-height:1.6;margin:0 0 8px;max-width:66ch}
.afd-row{font-family:var(--mono);font-size:13.5px;color:var(--ink);margin:0 0 4px}
.afd-sig{font-size:13.5px;color:var(--muted);font-style:italic;margin:18px 0 0}
.afd-raw{margin:20px 0 0;border-top:1px solid var(--line);padding-top:12px}
.afd-raw summary{font-family:var(--mono);font-size:12.5px;color:var(--link);cursor:pointer}
.afd-raw summary:hover{color:var(--link-hover)}
/* The product as issued is a teletype document: fixed width, and it scrolls
   sideways rather than being rewrapped, because rewrapping it here would
   defeat the only reason it is on the page. */
.afd-pre{font-family:var(--mono);font-size:11.5px;line-height:1.5;color:var(--muted);
  white-space:pre;overflow-x:auto;margin:10px 0 0;-webkit-overflow-scrolling:touch}

/* ---- the radar preview's own note ------------------------------------------
   Sits under the picture, because a clear radar and a broken one look the same
   and this card reads its own pixels to tell them apart. */
.radar-note{font-size:13px;color:var(--muted);margin:10px 0 0}

/* ---- air quality (G-4) -----------------------------------------------------
   EPA's own six categories. The colour is never the only carrier: the category
   word sits beside the number, and the AA-safe foreground tokens are reused
   from the severity scale rather than EPA's own fills, which are chosen for
   maps and fail contrast as text. */
.air-head{display:flex;align-items:baseline;gap:14px}
.air-aqi{font-family:var(--serif);font-size:clamp(40px,5vw,58px);line-height:1;margin:0}
.air-aqi.aq1{color:var(--ok-fg)}
.air-aqi.aq2{color:var(--sev-moderate-fg)}
.air-aqi.aq3{color:var(--sev-severe-fg)}
.air-aqi.aq4{color:var(--sev-extreme-fg)}
.air-aqi.aq5{color:var(--sev-extreme-fg)}
.air-aqi.aq6{color:var(--sev-extreme-fg)}
.air-words{display:flex;flex-direction:column;gap:2px}
.air-cat{font-size:17px;font-weight:600;margin:0}
.air-param{font-family:var(--mono);font-size:12px;color:var(--muted);margin:0}
.air-list{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-wrap:wrap;gap:6px 14px;
  font-family:var(--mono);font-size:12.5px;color:var(--muted)}
.air-area,.air-note{font-size:13px;color:var(--muted);margin:10px 0 0}

/* ---- UV on the sun card (G-4) ----------------------------------------------
   The band rides after the number, smaller, because the number is the reading
   and the band is what it means. */
.astro-uv-cat{font-size:.58em;color:var(--muted);text-transform:none;letter-spacing:.01em}

/* ---- pressure tendency (G-4) -----------------------------------------------
   The arrow rides beside the reading, smaller than it, because the number is
   the measurement and the arrow is a qualifier. Never the only carrier of the
   direction: the word is in the cell's label. */
.wx-press-arrow{font-size:.62em;margin-left:5px;color:var(--muted);vertical-align:middle}

/* ---- the hour strip's wind row (G-3) ---------------------------------------
   The arrow is a shape, not a colour: it reads the same for anyone who cannot
   separate these two greys. */
.hs-wind-arrow{fill:var(--accent-soft);opacity:.95}
.hs-wind-sp{font-family:var(--mono);font-size:11px;fill:var(--muted);text-anchor:middle}
/* F-4: the labeled rows under the band. The first column carries the unit. */
.hs-wind-sp.first{text-anchor:start}
.hs-rowlab{font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.06em;fill:var(--faint)}
.hs-rain{font-family:var(--mono);font-size:11px;font-weight:600;fill:var(--teal-ink);text-anchor:middle}
.hs-rain.dry{fill:var(--muted);font-weight:400}
.hs-drop{fill:var(--teal-ink)}
.hs-drop.dry{fill:var(--faint)}
.hs-cap{font-size:12px;color:var(--muted);margin:4px 0 0}
/* The hour panel carries more phrases now, so it wraps rather than clipping
   the ones at the end. */
.hstrip-detail{display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline}
.hstrip-line{white-space:normal}

/* ---- saved places (G-2) ----------------------------------------------------
   A row of the places the reader keeps, with the one on screen marked. It
   WRAPS rather than scrolling sideways: six short chips fit in two rows on a
   phone, and a horizontally scrolling strip hides places off the edge, which
   is the opposite of the point. */
.wx-places{width:100%;margin-top:8px}
.pl-list{display:flex;flex-wrap:wrap;gap:8px;list-style:none;margin:0;padding:0}
.pl-item{display:flex;align-items:stretch;border:1px solid var(--line);
  border-radius:999px;overflow:hidden;background:var(--card)}
/* The chip the reader is looking at. Not color alone: the weight and the
   border carry it too, for anyone who cannot see the fill. */
.pl-item.is-on{border-color:var(--ink);background:var(--ink)}
.pl-go{font:inherit;font-size:13.5px;line-height:1;color:var(--ink);background:none;
  border:0;padding:10px 12px;cursor:pointer;min-height:40px}
.pl-item.is-on .pl-go{color:var(--card);font-weight:600;cursor:default}
.pl-go:hover:not(:disabled){background:var(--chip-bg)}
.pl-x{font:inherit;font-size:15px;line-height:1;color:var(--muted);background:none;
  border:0;border-left:1px solid var(--line);padding:10px 11px;cursor:pointer;min-height:40px}
.pl-item.is-on .pl-x{color:var(--card);border-left-color:rgba(255,255,255,.35)}
.pl-x:hover{color:var(--copper)}
.pl-item.is-on .pl-x:hover{color:#fff}
.pl-note{font-size:12.5px;color:var(--muted);margin:6px 0 0}

/* ---- alert banner ----------------------------------------------------------
   Severity chips use the NWS severity scale; the event band color is the
   official weather.gov event color, carried as a border so alert text always
   sits on our AA-verified grounds. */
.alerts-live{margin:14px 0 4px}
.alert{background:var(--card);border:1px solid var(--line);border-left:6px solid var(--copper);
  border-radius:8px;padding:14px 18px;margin:0 0 10px;box-shadow:var(--shadow)}
.alert-head{display:flex;flex-wrap:wrap;gap:8px 12px;align-items:center}
.alert-event{font-family:var(--serif);font-weight:600;font-size:19px;letter-spacing:-.01em}
.sev{font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  font-weight:600;border-radius:5px;padding:2px 8px}
.sev-extreme{color:var(--sev-extreme-fg);background:var(--sev-extreme-bg)}
.sev-severe{color:var(--sev-severe-fg);background:var(--sev-severe-bg)}
.sev-moderate{color:var(--sev-moderate-fg);background:var(--sev-moderate-bg)}
.sev-minor,.sev-unknown{color:var(--sev-minor-fg);background:var(--sev-minor-bg)}
.alert-headline{margin:8px 0 0;font-size:15.5px}
.alert-meta{font-family:var(--mono);font-size:11.5px;color:var(--faint);margin-top:6px}
.alert details{margin-top:10px}
.alert summary{cursor:pointer;font-weight:600;font-size:14px;color:var(--link)}
.alert-text{white-space:pre-wrap;font-size:14.5px;line-height:1.55;color:var(--ink);
  background:var(--paper);border:1px solid var(--line);border-radius:7px;
  padding:12px 14px;margin:10px 0 0;font-family:var(--sans)}
.alert-links{display:flex;flex-wrap:wrap;gap:14px;margin-top:10px;font-size:14px}
.alert-links a{font-weight:600}
.storm-line{margin-top:10px;padding-top:10px;border-top:1px solid var(--line);font-size:14.5px;color:var(--muted)}

/* ---- the condition hero -------------------------------------------------------
   The one expressive surface on the site. It wears the CURRENT SKY: the
   gradient and the motion layer are driven by the live observation, not by
   decoration. Family chassis (same band/scrim/pause pattern as the News,
   Sports and Crypto desks) so a reader crossing between sites stays home.

   Register safeguards, non-negotiable:
   - When an alert is active the hero goes QUIET: motion stops, the video is
     removed, and the palette drops to neutral. The site never looks pretty
     while a warning is up, and the alert always renders ABOVE this band.
   - Every stop below was contrast-checked through the scrim; the faintest
     text on the lightest sky still clears AA.
   - prefers-reduced-motion removes every animation and the video outright.
   ============================================================================ */
.wx-hero{position:relative;overflow:hidden;border-radius:14px;margin:14px 0 18px;
  border:1px solid var(--line);box-shadow:var(--shadow);isolation:isolate;
  min-height:360px;display:flex;flex-direction:column;justify-content:flex-end;
  background:linear-gradient(180deg,#3C4B5A,#5A6874 72%,#7E8992)}
.wx-hero[data-tod=day][data-wx=sun]{background:linear-gradient(180deg,#1D5A8F,#3E7CAC 54%,#C9954E)}
.wx-hero[data-tod=day][data-wx=partly]{background:linear-gradient(180deg,#2B5A85,#4A769B 60%,#8FA0AC)}
.wx-hero[data-tod=day][data-wx=cloud]{background:linear-gradient(180deg,#3A4956,#55636F 70%,#828C95)}
.wx-hero[data-tod=day][data-wx=rain]{background:linear-gradient(180deg,#1E2F3C,#374E5C 62%,#5A6E7C)}
.wx-hero[data-tod=day][data-wx=storm]{background:linear-gradient(172deg,#18213A,#2E2A4A 42%,#57405A 68%,#8F4E38 88%,#B0763F)}
.wx-hero[data-tod=day][data-wx=snow]{background:linear-gradient(180deg,#384C64,#566B82 62%,#8DA2B3)}
.wx-hero[data-tod=day][data-wx=fog]{background:linear-gradient(180deg,#434B53,#5F676F 70%,#88909A)}
.wx-hero[data-tod=night]{background:linear-gradient(180deg,#0E1626,#1B2740 58%,#3C4A63)}
.wx-hero[data-tod=night][data-wx=storm]{background:linear-gradient(174deg,#0B1020,#1E1B33 44%,#3A2C42 72%,#5C3A2C)}
.wx-hero[data-tod=night][data-wx=fog]{background:linear-gradient(180deg,#1A1E24,#2B3138 62%,#454C55)}
/* Alert active: the sky steps back so the warning above owns the page.
   The selector carries all three attributes on purpose. The condition rules
   above are [data-tod][data-wx] and would otherwise outrank a plain
   [data-alert] rule, leaving a cheerful sky under a live warning. */
.wx-hero[data-tod][data-wx][data-alert=true]{
  background:linear-gradient(180deg,#242A2E,#33393E 70%,#434A50)}

/* The loop sits UNDER the condition sky's tint rather than replacing it.
   At full opacity the footage would hide the sky entirely and the hero would
   stop responding to the weather, which is the whole point of it. Holding the
   video back lets the sky's color wash through: the room takes on the tone of
   the actual sky outside, and both the motion and the condition read at once.

   CONTRAST, measured not assumed: footage is not uniformly dark (this clip
   has a bright screen and a lamp), so a bright pixel LIGHTENS the ground under
   the text. Composited worst case is a pure-white video pixel over the
   lightest sky stop. At video .55 the scrim must reach .62 for the smallest
   text to clear AA; the video-on scrim below runs .72, which lands every sky
   at 6.2 or better. Re-run that check before easing either number. */
.wx-hero-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1;
  opacity:0;transition:opacity .8s ease}
.wx-hero-video.on{opacity:.55}
/* deeper scrim whenever real footage is running */
.wx-hero.video-on .wx-scrim{
  background:linear-gradient(180deg,rgba(10,15,20,.34) 0%,rgba(10,15,20,.46) 30%,rgba(10,15,20,.72) 52%,rgba(10,15,20,.84) 100%)}
.wx-hero[data-alert=true] .wx-hero-video{display:none}
/* with real footage running, the drawn weather effects step back to a hint,
   so we never render rain falling inside a room */
.wx-hero.video-on .wx-fx{opacity:.18}
.wx-hero.video-on[data-wx=storm] .wx-fx,.wx-hero.video-on[data-wx=rain] .wx-fx{opacity:.3}

/* ---- the pre-location identity hero ---------------------------------------
   THE VIDEO LIVES HERE, AND ONLY HERE. Before a location is set there is no
   observation for footage to contradict, so a fixed loop is honest: the job of
   this screen is to say what the site IS, not what the weather is. The moment a
   location exists the Living Sky takes over, because then a sunlit room behind
   a blizzard would be a small lie.

   The band is the brand's deep teal on purpose, and NOT the grey default.
   Neutral grey is not neutral: it is the visual language of loading, broken and
   empty, which is the worst thing a front door can say. This gradient never
   appears on the located board, so it can never be misread as a condition.

   It carries no data-wx or data-tod attribute, which is what keeps it out of
   the condition rules above. Those are [data-tod][data-wx] at three classes,
   so a plain .wx-hero-identity would lose to them; the two-class selector here
   only has to beat bare .wx-hero. */
.wx-hero.wx-hero-identity{
  min-height:400px;
  background:linear-gradient(180deg,#0E2A33,#17404A 58%,#2C5F66)}

/* Measured, not assumed, and the reason this rule exists at all. This hero
   carries more copy than the conditions hero, so its text reaches higher up
   the band, into the region where the standard video scrim is only .46. There,
   against the worst case of a pure white video pixel over the lightest
   gradient stop, #DDE4E8 lands at 4.39 and FAILS AA. Starting the floor at .58
   puts that same text at 5.95 and the faintest at 4.98. Re-run the check
   before easing either number. */
.wx-hero.wx-hero-identity.video-on .wx-scrim{
  background:linear-gradient(180deg,rgba(10,15,20,.58) 0%,rgba(10,15,20,.66) 40%,rgba(10,15,20,.80) 100%)}

.wx-id-eyebrow{font-family:var(--mono);font-size:11.5px;letter-spacing:.09em;
  text-transform:uppercase;color:#C9D2D8;margin:0 0 10px}
.wx-id-head{font-family:var(--serif);font-weight:400;font-size:clamp(26px,4.2vw,40px);
  line-height:1.14;color:#fff;margin:0 0 10px;max-width:20ch}
.wx-id-say{font-size:15.5px;line-height:1.55;color:#DDE4E8;margin:0 0 18px;max-width:54ch}
.wx-id-act{display:flex;flex-wrap:wrap;gap:10px 16px;align-items:center}
.wx-id-cta{font-family:var(--sans);font-size:15px;font-weight:600;cursor:pointer;
  padding:11px 18px;border-radius:9px;border:1px solid #F4F1EA;
  background:#F4F1EA;color:#12222A}
.wx-id-cta:hover{background:#fff;border-color:#fff}
.wx-id-cta:focus-visible{outline:3px solid #fff;outline-offset:3px}
@media (max-width:560px){
  .wx-hero.wx-hero-identity{min-height:340px}
  .wx-id-act{gap:10px 12px}
  .wx-id-cta{width:100%}
}

/* condition motion layer: weather, not decoration */
.wx-fx{position:absolute;inset:0;z-index:1;pointer-events:none;opacity:.55}
.wx-hero[data-alert=true] .wx-fx{display:none}
/* THE STRIPES ARE GONE. Rain here was a repeating-linear-gradient of 1px
   white lines scrolled diagonally: a moving wallpaper, laid over the sky
   engine's actual falling drops, so the hero had two rains at once and the
   flat one was the one you noticed. The drops carry it now. What is left on
   this layer is a faint wash that deepens the sky under heavy precipitation,
   with no pattern in it at all. */
.wx-hero[data-wx=rain] .wx-fx,.wx-hero[data-wx=storm] .wx-fx{
  background-image:linear-gradient(180deg,rgba(120,150,175,.10),rgba(90,120,150,.16));
  animation:none}
.wx-hero[data-wx=storm] .wx-fx{opacity:.62}
/* Snow had the same doubling rain did: four radial-gradient dots tiled every
   200px and scrolled, under the sky engine's own drifting flakes. A tiled
   pattern reads as a pattern, because every fourth flake is in exactly the
   same place as the first. Removed for the same reason, leaving the wash. */
.wx-hero[data-wx=snow] .wx-fx{
  background-image:linear-gradient(180deg,rgba(226,238,250,.10),rgba(198,216,236,.16));
  animation:none}
.wx-hero[data-wx=cloud] .wx-fx,.wx-hero[data-wx=partly] .wx-fx,.wx-hero[data-wx=fog] .wx-fx{
  background-image:radial-gradient(60% 42% at 18% 26%,rgba(255,255,255,.16),transparent 70%),
    radial-gradient(52% 38% at 72% 18%,rgba(255,255,255,.13),transparent 72%);
  animation:wx-drift 42s ease-in-out infinite alternate}
.wx-hero[data-wx=fog] .wx-fx{opacity:.85;
  background-image:linear-gradient(0deg,rgba(255,255,255,.13) 0 18%,transparent 18% 34%,rgba(255,255,255,.10) 34% 52%,transparent 52%)}
.wx-hero[data-tod=day][data-wx=sun] .wx-fx{
  background-image:radial-gradient(38% 30% at 78% 16%,rgba(255,236,190,.42),transparent 70%);animation:none}
@keyframes wx-drift{from{background-position:0 0}to{background-position:80px 0}}

/* The scrim stays light where there is only sky and deepens under the text,
   so the gradient reads as weather instead of as a colored panel. The ramp
   reaches .58 by 52 percent, which is where content starts: every text tone
   below was verified to clear AA against the LIGHTEST stop of every sky at
   that alpha (worst case 5.65, snow). Do not soften the ramp without
   re-running that check. */
.wx-scrim{position:absolute;inset:0;z-index:2;
  background:linear-gradient(180deg,rgba(10,15,20,.18) 0%,rgba(10,15,20,.26) 30%,rgba(10,15,20,.58) 52%,rgba(10,15,20,.78) 100%)}
.wx-hero-inner{position:relative;z-index:3;padding:26px 28px 22px;color:#FFFFFF}

/* ---- B-1: the sky inside the hero (wx-herosky.js). Prefix hsky-, NOT hs-:
   the hourly strip owns hs-, and a daypart class named hs-day inherited the
   strip's dashed stroke onto every cloud ellipse, measured on the canary sheet. ---------------------------
   One absolutely placed layer under the scrim and the content. Every moving
   part animates transform or opacity ONLY: those run on the compositor without
   layout or repaint, which is what keeps a frame under 16 ms on a mid-range
   phone. Nothing here animates a filter, a gradient, a size or a position. */
.wx-hero > .hsky{position:absolute;inset:0;z-index:0;overflow:hidden;pointer-events:none;border-radius:inherit}
.hsky > div{position:absolute;inset:0}
.hsky-base{inset:0}
.hsky-glow,.hsky-moon-glow{inset:0}
.hsky-sun{position:absolute;width:64px;height:64px;margin:-32px 0 0 -32px;border-radius:50%;
  background:radial-gradient(circle,#FFF6D8 0%,#FFE39A 45%,rgba(255,214,120,0) 72%)}
.hsky-dusk .hsky-sun{background:radial-gradient(circle,#FFE0B0 0%,#F4A060 48%,rgba(236,130,80,0) 74%)}
.hsky .hsky-moon{position:absolute;inset:auto;width:108px;height:108px;margin:-54px 0 0 -54px}
.hsky-moon svg{display:block;width:100%;height:100%}
/* W-4: the moon inside the sky band, sized to it, so no change to the card's
   height can move it into the tiles */
.wx-hero-board .wx-skyspace{position:relative}
.hsky-moon-in{position:absolute;top:50%;right:0;height:min(72px,calc(100% - 12px));aspect-ratio:1;
  transform:translateY(-50%);pointer-events:none;
  filter:drop-shadow(0 0 18px rgba(220,226,240,.18))}
.hsky-moon-in svg{display:block;width:100%;height:100%}
.hsky-stars i{position:absolute;border-radius:50%;background:#F4F1EA;
  animation:hsky-twinkle 43s linear infinite}
/* one star at a time: each is dim for all but about a second of its cycle */
@keyframes hsky-twinkle{0%,96%,100%{opacity:inherit;transform:scale(1)}97.5%{opacity:1;transform:scale(1.9)}}
.hsky-clouds{inset:0}
.hsky-cloud{position:absolute;left:0;will-change:transform;animation:hsky-drift linear infinite}
.hsky-cloud svg{display:block}
@keyframes hsky-drift{from{transform:translateX(-360px)}to{transform:translateX(calc(var(--hw,375px) + 20px))}}
.hsky-deck{inset:0}
.hsky-band{position:absolute;left:-15%;width:130%;will-change:transform;animation:hsky-deck ease-in-out infinite alternate}
@keyframes hsky-deck{from{transform:translateX(-4%)}to{transform:translateX(4%)}}
.hsky-precip i{position:absolute;top:-60px}
.hsky-drop{background:linear-gradient(180deg,rgba(210,225,240,0),rgba(210,225,240,.9));border-radius:1px;
  transform:rotate(var(--lean,0deg));animation:hsky-fall linear infinite}
@keyframes hsky-fall{
  from{transform:translate(0,0) rotate(var(--lean,0deg))}
  to{transform:translate(calc(var(--drift,0px) * -1),900px) rotate(var(--lean,0deg))}}
.hsky-flake{border-radius:50%;background:#F7FAFD;animation:hsky-snow linear infinite}
@keyframes hsky-snow{
  0%{transform:translate(0,0)}25%{transform:translate(10px,225px)}50%{transform:translate(-6px,450px)}
  75%{transform:translate(8px,675px)}100%{transform:translate(0,900px)}}
.hsky-fogwash{inset:0;background:linear-gradient(180deg,rgba(236,238,238,.10),rgba(236,238,238,.30))}
.hsky-fogband{position:absolute;left:-20%;right:auto;width:140%;height:26%;
  background:radial-gradient(50% 50% at 50% 50%,rgba(240,242,242,.30),rgba(240,242,242,0) 70%);
  animation:hsky-fogdrift linear infinite alternate}
@keyframes hsky-fogdrift{from{transform:translateX(-8%)}to{transform:translateX(8%)}}
.hsky-flash{inset:0;background:#E8ECFF;opacity:0;animation:hsky-flash 60s linear infinite}
/* one flicker cluster a minute: dark for 58 seconds, two quick pulses */
@keyframes hsky-flash{0%,95.8%,96.6%,97.4%,100%{opacity:0}96.1%{opacity:.26}97.0%{opacity:.14}}
/* Reduced motion: everything stands still where it is, and the flash never
   fires. The scene stays; only the motion goes. */
.hsky-still *,.hsky-still *::before{animation:none!important}
.hsky-still .hsky-flash{display:none}
@media (prefers-reduced-motion:reduce){.hsky *{animation:none!important}.hsky .hsky-flash{display:none}}
/* top right, clear of the source credit that sits bottom right */
.wx-hero .hero-pause{position:absolute;right:12px;top:12px;z-index:4;width:38px;height:38px;
  border-radius:50%;border:1px solid rgba(255,255,255,.4);background:rgba(8,13,20,.72);
  color:#fff;font-size:13px;line-height:1;cursor:pointer;
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
.wx-hero .hero-pause:hover{border-color:#fff}
.wx-hero .hero-pause:focus-visible{outline:3px solid #fff;outline-offset:2px}

.wx-place{font-family:var(--mono);font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;
  color:#DDE4E8;margin:0 0 10px;display:flex;flex-wrap:wrap;gap:6px 14px;align-items:baseline}
.wx-place .wx-quiet-note{color:#C9D2D8;text-transform:none;letter-spacing:.02em;font-size:11.5px}
/* The numeral is tinted by its own temperature. Solid fill, not a
   background-clip:text gradient: this element runs line-height .95, and a
   paint box shorter than the glyphs renders the BOTTOM OF EVERY DIGIT
   transparent, which reads as numbers with their feet cut off. Escaping that
   means line-height 1 plus vertical padding and negative margins to restore the
   spacing, and the result is a fragile box for no gain the eye can see at a
   single temperature.

   Contrast measured where the numeral actually sits, 16 to 41 percent down the
   hero, over the BLUE UPPER HALF of each condition sky rather than the bright
   bottom stop I first assumed: worst case 3.19 on a sunny day, against the 3.0
   floor for large text. */
.wx-temp{font-family:var(--mono);font-weight:600;font-size:clamp(68px,13vw,140px);line-height:.95;
  letter-spacing:-.03em;color:var(--t-ink-dark)}
.wx-cond{font-family:var(--serif);font-size:clamp(24px,3.2vw,34px);color:#fff;line-height:1.25}
.wx-feels{font-size:17px;color:#DDE4E8;margin-top:4px}
/* THE RAIL. Seven readings at 16px spread across a thousand pixels is the same
   problem twice: enormous gaps between the cells and no reason for any value to
   be large. Five bordered cells fill the width, so each value can carry its own
   weight. Rain keeps its tint so the eye lands there first.
   auto-fit is deliberate over a fixed repeat(5): cells are conditional (feels
   like only renders when it differs from the reading, sunset flips to sunrise
   after dark, the scrubbed state carries fewer), and a fixed five would leave
   empty columns whenever the feed is thin. */
/* EVERY VALUE IN THESE GRIDS WAS INDENTED 40px, and nobody put it there.
   They are definition lists: <dt class="nk"> for the label and <dd class="nv">
   for the value, which is the right markup for a label and its value. But a
   <dd> carries a UA default margin-inline-start of 40px, and the rules below
   set margin-top without ever resetting it, so the value sat 40px right of its
   own label. Measured on the phone: "Feels like" began at x=61 and "86" at
   x=101 inside the same tile.

   It affected four grids, not one: the hero stats, the sun and moon grid, the
   severe facts and the storm stats. The guides pages already reset it, which
   is why only these leaked. Reset once, here, on the element that carries the
   default rather than in four places that would each have to remember. */
dd.nv{margin-inline-start:0}

/* A-5: FOUR COLUMNS, FIXED, because the hero now always draws eight tiles and
   8 is 4x2. It was `repeat(auto-fit,minmax(150px,1fr))`, which let the rail's
   width choose the column count: six at 1440px, so eight tiles read as six
   and a stranded two. A fixed count is the point; the phone rule below makes
   it two, and 8 is 2x4 there too. */
.wx-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  margin-top:22px;border-top:1px solid rgba(255,255,255,.22);
  background:rgba(6,11,17,.28);border-radius:12px;overflow:hidden}
.wx-stats > *{padding:13px 16px 14px;border-left:1px solid rgba(255,255,255,.12);min-width:0}
.wx-stats > *:first-child{border-left:0}
/* the second row of four: its first cell has no left rule and every cell gets
   the rule above it, the same pair the phone layout uses for its rows */
@media (min-width:641px){
  .wx-stats > *:nth-child(4n+1){border-left:0}
  .wx-stats > *:nth-child(n+5){border-top:1px solid rgba(255,255,255,.12)}
}
.wx-stats .nk{font-family:var(--mono);font-size:10px;letter-spacing:.15em;text-transform:uppercase;
  color:#C9D2D8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}
/* 25px is the designed size and it holds from about 1250px up. Below that the
   sixth tile (dew point, L-4) narrows every cell, and "7:22 PM" clipped inside
   a 154px tile at 1024px, measured. The value shrinks with the rail rather
   than being cut; the floor keeps it a headline number on a phone, where the
   two-column rule below sets its own 21px. */
.wx-stats .nv{font-family:var(--mono);font-size:clamp(19px,1.9vw,25px);font-weight:600;color:#fff;
  margin-top:6px;letter-spacing:-.02em;line-height:1}
.wx-hero .card-foot{border-top-color:rgba(255,255,255,.22);margin-top:16px}
.wx-hero .stamp,.wx-hero .src{color:#C9D2D8}
.wx-hero .stamp.stale{color:#F0C08A}
.wx-hero-skel{padding:26px 28px;min-height:200px}
.wx-hero-skel .skel{background:rgba(255,255,255,.14)}
@media (max-width:640px){
  .wx-hero-inner{padding:20px 18px 18px}
  /* two up on a phone, and an odd last cell takes the full width rather than
     leaving a half-empty row */
  .wx-stats{grid-template-columns:repeat(2,minmax(0,1fr))}
  .wx-stats > *{padding:11px 14px 12px}
  .wx-stats > *:nth-child(odd){border-left:0}
  .wx-stats > *:nth-child(n+3){border-top:1px solid rgba(255,255,255,.12)}
  .wx-stats > *:last-child:nth-child(odd){grid-column:1/-1}
  .wx-stats .nv{font-size:21px}
}
@media (prefers-reduced-motion:reduce){
  .wx-fx{animation:none!important}
  .wx-hero-video{display:none}
}

/* ---- hourly strip ----------------------------------------------------------- */
.hourly{display:flex;gap:0;overflow-x:auto;padding:4px 0 8px;margin:0;list-style:none;
  scrollbar-width:thin}
.hourly li{flex:0 0 74px;text-align:center;border-right:1px solid var(--line);padding:2px 4px}
.hourly li:last-child{border-right:none}
.hourly .ht{font-family:var(--mono);font-size:11px;color:var(--faint);text-transform:uppercase}
.hourly .hv{font-family:var(--mono);font-size:18px;font-weight:600;margin:2px 0}
.hourly .hp{font-family:var(--mono);font-size:11.5px;color:var(--teal-ink)}
.hourly .hp.dry{color:var(--faint)}

/* ---- the hour strip -----------------------------------------------------------
   Scrollable hourly forecast (wx-chart.js). Two kinds of ground in one
   component, and the split decides every colour here:

   ON THE SKY BAND (temp numerals, day names, the observation ring): the band
   is the real daypart colours from the sky engine, identical in both themes,
   so these inks are fixed values that never consult the theme. Every numeral
   and name carries a dark halo (paint-order stroke) because the band runs
   from near-black night to bright day blue and no single ink clears both
   bare. Measured with the halo composited over each band stop: on the
   brightest (#2E86AE) the halo ground is #102837, where the dark ink ramp's
   worst stop measures 6.11; across all four daypart grounds the ramp spans
   6.11 to 11.27 and the day-name white measures 13.4 at worst. Verified
   computationally; re-measure if the halo alpha or the band stops change.

   ON THE CARD (hour labels, percent labels, detail line, table): normal
   theme tokens, already measured on the cards.

   Nothing here animates on its own; the only motion is the reader's own
   scrolling, and centring respects prefers-reduced-motion. */
.hstrip-wrap{position:relative}
.hstrip{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;cursor:grab;
  scrollbar-width:thin;scrollbar-color:var(--faint) transparent}
.hstrip.drag{cursor:grabbing;user-select:none}
.hstrip::-webkit-scrollbar{height:8px}
.hstrip::-webkit-scrollbar-thumb{background:var(--line);border-radius:99px}
.hstrip:focus-visible{outline:3px solid var(--rule);outline-offset:2px;border-radius:8px}
.hs-svg{display:block}
.hstrip-fade{position:absolute;top:0;bottom:10px;width:38px;pointer-events:none;z-index:2;
  opacity:0;transition:opacity .2s}
@media(prefers-reduced-motion:reduce){.hstrip-fade{transition:none}}
/* ONE GRADIENT PER EDGE, over the band only (F-2). The fade was two stacked
   gradients, the band's colour over the card's, and the card layer ran the
   full height UNDER the band layer; in the sky theme --card is a dark
   rgba(12,18,26,.88), so wherever the band gradient turned transparent the
   dark layer showed through and smeared the last visible hour's icon and
   temperature (Jack's iPhone and production at 375, 2026-09-21). Before that
   it was a single --card gradient, white in the light theme (2.07:1 on the
   last numeral). Now the fade is the band's own edge colour, set per side by
   wx-chart.js, to transparent, and it covers the band's height only: the hour
   labels below sit on the card and need no fade. --hs-band and --hs-band-h
   carry fallbacks so a fade still fades if the chart renders without them. */
.hstrip-fade{background-repeat:no-repeat;bottom:auto;top:var(--hs-band-top,0);height:var(--hs-band-h,100%)}
.hstrip-fade.l{left:0;background-image:linear-gradient(90deg,var(--hs-band,#0B1D33),transparent)}
.hstrip-fade.r{right:0;background-image:linear-gradient(270deg,var(--hs-band,#0B1D33),transparent)}

/* marks on the band: halo first, ink over it */
.hs-tv{font-family:var(--mono);font-size:13px;font-weight:600;text-anchor:middle;
  paint-order:stroke;stroke:rgba(8,14,22,.78);stroke-width:3.2px;stroke-linejoin:round}
/* W-3: F-7's floor for day names, 11px caps */
.hs-dayname{font-family:var(--mono);font-size:11px;font-weight:600;fill:#F4F1EA;letter-spacing:.12em;
  paint-order:stroke;stroke:rgba(8,14,22,.78);stroke-width:3px;stroke-linejoin:round}
.hs-line-under{fill:none;stroke:rgba(8,14,22,.66);stroke-width:5;stroke-linejoin:round;stroke-linecap:round}
.hs-line{fill:none;stroke:rgba(244,241,234,.85);stroke-width:2.5;stroke-linejoin:round;stroke-linecap:round}
.hs-dot{stroke:rgba(8,14,22,.9);stroke-width:1.7}
.hs-day{stroke:rgba(244,241,234,.28);stroke-width:1;stroke-dasharray:3 4}
.hs-sel{fill:rgba(244,241,234,.14)}

/* the observation ring, a measurement against a projection. The card charts
   used --copper-ink, measured on cards; this ring sits on the sky band, so it
   is a fixed lighter copper with its own halo-weight stroke. */
.hs-obs-ring-under{fill:none;stroke:rgba(8,14,22,.78);stroke-width:5.5}
.hs-obs-ring{fill:none;stroke:#F0A45E;stroke-width:2.5}
.hs-obs-tie-under{stroke:rgba(8,14,22,.78);stroke-width:3.5;stroke-dasharray:3 3}
.hs-obs-tie{stroke:#F0A45E;stroke-width:1.5;stroke-dasharray:3 3;opacity:.9}
.hs-obs-label{font-family:var(--mono);font-size:12px;font-weight:600;fill:#F0C08A;
  paint-order:stroke;stroke:rgba(8,14,22,.78);stroke-width:3px;stroke-linejoin:round}
.hs-obs-mid{text-anchor:middle}

/* below the band: the card's own ground and tokens */
.hs-base{stroke:var(--line);stroke-width:1}
.hs-hl{font-family:var(--mono);font-size:11px;fill:var(--faint);text-anchor:middle}
.hs-hit{cursor:pointer}

.hstrip-detail{display:flex;gap:14px;align-items:baseline;flex-wrap:wrap;
  border-top:1px solid var(--line);margin-top:10px;padding-top:10px;min-height:24px}
.hstrip-when{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.hstrip-line{font-size:14.5px;color:var(--ink)}
.hs-table{margin-top:10px}
.hs-table summary{cursor:pointer;font-size:13.5px;color:var(--link);font-weight:600}
.hs-table summary:hover{text-decoration:underline}

/* screen-reader-only text: the hero strip announces its selected hour without
   drawing a panel, because the hero above it already shows the same reading.
   (This once said "the NOW card", which was removed along with its styles.) */
.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ---- the forecast page's week: rows that open to the as-issued text ---------
   Reuses the board's shared-scale bar idiom. The row is a real button so it is
   keyboard reachable and announces its own state; the bar is aria-hidden
   because both numbers are printed beside it as text. */
.wk-lede{font-size:15.5px;line-height:1.55;color:var(--muted);margin:0 0 14px}
.wk-open{border-bottom:1px solid var(--line)}
.wk-open:last-child{border-bottom:0}
.wk-row-btn{display:grid;grid-template-columns:minmax(84px,auto) minmax(0,2.4fr) minmax(140px,1.6fr) auto auto;
  gap:14px;align-items:center;width:100%;text-align:left;background:none;border:0;
  padding:11px 4px;cursor:pointer;color:inherit;font:inherit}
/* the condition column was 1.5fr against a 2fr track, which wrapped
   "Chance Showers And Thunderstorms" onto five lines and left every row a
   different height. It now gets the larger share and is capped at two lines. */
.wk-row-btn .wk-cond-text{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;line-height:1.35}
.wk-row-btn:hover{background:rgba(127,127,127,.06)}
.wk-row-btn:focus-visible{outline:3px solid var(--rule);outline-offset:-3px;border-radius:6px}
.wk-nums{display:flex;align-items:center;gap:10px;min-width:0}
.wk-nums b{font-family:var(--mono);font-size:13px;font-weight:600;width:34px;flex:none}
/* THE CLASS NAMES ARE SHARED AND THESE COLOURS WERE NOT SCOPED, which is why
   the home board's 7-day rail lost its temperature ink in the light theme.
   The rail near the top of this file sets `color:var(--t-ink-light)` on
   `.wk-lo,.wk-hi` at specificity 0,1,0; these two rules had the same
   specificity and came later in the file, so they won everywhere. In the dark
   and sky themes the rail was saved by `:root.sky-active .wk-lo` at 0,2,1,
   which outranks them, and that is exactly why the ink appeared in Auto and
   vanished in Light. Chrome named the winning rule when asked:
   `.wk-lo -> var(--muted)`, beating `.wk-lo, .wk-hi -> var(--t-ink-light)`.

   Scoped to `.wk-row-btn`, which is the component that owns them, these rows
   now take the same two-ink pair as the rail: the forecast page's week band
   carries warmth in both themes instead of one grey and one near-black. */
.wk-row-btn .wk-lo{text-align:right}
/* The home board's 7-day rail positions .wk-lo/.wk-hi absolutely against its
   track (the rule near the top of this file). These forecast rows reuse the
   class names but lay the numbers out in a flex .wk-nums, which is static, so
   the inherited absolute positioning threw every day's low and high to the
   card's top-left corner, all fourteen on top of each other over the summary
   line, at every width. Seen on iPhone and iPad. Restore normal flow here. */
.wk-row-btn .wk-lo,.wk-row-btn .wk-hi{position:static;top:auto;transform:none;padding:0}
.wk-track{position:relative;flex:1;height:7px;border-radius:99px;background:rgba(127,127,127,.18);min-width:60px}
.wk-bar{position:absolute;top:0;height:7px;border-radius:99px}
.wk-onlylow{position:absolute;top:-2px;width:4px;height:11px;border-radius:2px;transform:translateX(-2px)}
.wk-rw{display:flex;align-items:baseline;gap:12px;font-family:var(--mono);font-size:12px;white-space:nowrap}
.wk-rain{color:var(--teal-ink);font-weight:600}
.wk-rain.dim{color:var(--faint);font-weight:400}
.wk-wind{color:var(--faint)}
.wk-chev{font-size:9px;color:var(--faint);transition:transform .15s}
@media(prefers-reduced-motion:reduce){.wk-chev{transition:none}}
.wk-row-btn[aria-expanded=true] .wk-chev{transform:rotate(180deg)}
.wk-panel{padding:2px 4px 16px}
.wk-as{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--faint);margin:12px 0 6px;font-weight:600}
.wk-panel p{margin:0 0 6px}
/* L-2. At 699px the row overprinted: measured on the live site, .wk-nums
   content spilled 124px past its own column and reached 110px into the rain
   and wind cell, on three of seven rows. The cause is the five column grid.
   .wk-nums needs about 138px for its own contents (two 34px figures, a 60px
   minimum track and two 10px gaps) and .wk-rw is an auto column holding nowrap
   text, so neither can give way; below roughly 740px of viewport the grid
   hands .wk-nums less than it needs and its flex children paint outside it.

   This page's main is .narrow, capped at 760px inside 22px gutters, so 760 is
   also the width at which the row stops being squeezed at all. The stacked
   layout that already served phones now serves the whole band up to there,
   which is the audit's own second option ("or the row stacks at that width as
   it does on the phone") and reuses a layout that is already proven rather
   than inventing a third one for 120px of width. */
@media(max-width:760px){
  .wk-row-btn{grid-template-columns:1fr auto;gap:6px 10px;padding:12px 4px}
  .wk-row-btn .wk-cond{grid-column:1/-1;order:3}
  .wk-nums{grid-column:1/-1;order:4}
  .wk-rw{grid-column:1/-1;order:5}
  .wk-chev{order:2}
}

/* ---- 7-day board ------------------------------------------------------------ */
.days{width:100%;border-collapse:collapse}
.days th{font-family:var(--mono);font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--faint);text-align:left;font-weight:600;padding:4px 10px 8px;border-bottom:1px solid var(--line)}
.days td{padding:9px 10px;border-bottom:1px solid var(--line);font-size:14.5px;vertical-align:top}
.days tr:last-child td{border-bottom:none}
.days .d-hi,.days .d-lo{font-family:var(--mono);font-weight:600;white-space:nowrap}
.days .d-lo{color:var(--muted);font-weight:500}

/* ---- radar and images -------------------------------------------------------- */
.radar-frame{position:relative;border:1px solid var(--line);border-radius:8px;overflow:hidden;
  background:var(--chip-bg)}
.radar-frame img{display:block;width:100%;height:auto}
.radar-ctl{display:flex;gap:10px;align-items:center;margin-top:10px;flex-wrap:wrap}
/* ---- B-5: radar as the second hero -----------------------------------------
   The canvas is the picture; the time sits in its corner and the reader is the
   dot at its center. The legend is the NEXRAD scale the frames are drawn in,
   which is the scale the radar page names as standard. */
.rh-canvas{display:block;width:100%;height:auto;background:#5f5f5f}
.rh-time{position:absolute;top:8px;right:8px;padding:3px 8px;border-radius:6px;
  background:rgba(8,12,18,.78);color:#F4F1EA;font-family:var(--mono);font-size:12px;font-weight:600;
  line-height:16px;white-space:nowrap}
.rh-time:empty{display:none}
.rh-me{position:absolute;left:50%;top:50%;width:10px;height:10px;margin:-5px 0 0 -5px;border-radius:50%;
  background:#fff;box-shadow:0 0 0 2px rgba(8,12,18,.85)}
.rh-legend{display:flex;align-items:center;gap:8px;margin-top:8px;font-family:var(--mono);font-size:10.5px;
  color:var(--faint)}
.rh-ramp{flex:1;max-width:220px;height:6px;border-radius:3px;
  background:linear-gradient(90deg,#04E9E7,#019FF4,#0300F4,#02FD02,#01C501,#008E00,#FDF802,#E5BC00,#FD9500,#FD0000,#D40000,#BC0000,#F800FD,#9854C6,#FDFDFD)}
.rh-ctl{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;margin-top:10px}
.rh-line{flex:1;min-width:200px}
.rh-scrub{width:100%;margin:0;accent-color:var(--teal-ink)}
.rh-ends{display:flex;justify-content:space-between;font-family:var(--mono);font-size:10.5px;color:var(--faint);
  margin-top:2px}
.rh-more{margin:10px 0 0}

/* ---- sun and moon ------------------------------------------------------------ */
.astro-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px 18px}
.astro-grid .nk{font-family:var(--mono);font-size:10.5px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--faint)}
.astro-grid .nv{font-family:var(--mono);font-size:16px;font-weight:600;white-space:nowrap}

/* ---- national board ---------------------------------------------------------- */
.stat-row{display:flex;flex-wrap:wrap;gap:8px 26px;align-items:baseline}
.stat-row .big{font-family:var(--mono);font-weight:600;font-size:34px;line-height:1}
.stat-row .lbl{font-family:var(--mono);font-size:11px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--faint)}
.state-table{width:100%;border-collapse:collapse;margin-top:8px}
.state-table td{padding:6px 8px;border-bottom:1px solid var(--line);font-size:14px}
.state-table tr:last-child td{border-bottom:none}
.state-table .st{font-family:var(--mono);font-weight:600}
.state-table .ct{font-family:var(--mono);text-align:right;color:var(--muted)}
.evt-list{margin:8px 0 0;padding:0;list-style:none}
.evt-list li{padding:7px 0;border-bottom:1px solid var(--line);font-size:14.5px;
  display:flex;gap:10px;align-items:baseline;flex-wrap:wrap}
.evt-list li:last-child{border-bottom:none}
.evt-count{font-family:var(--mono);font-weight:600;color:var(--muted)}

/* ---- alerts page ------------------------------------------------------------- */
.sev-group{margin:0 0 26px}
.sev-group>h3,.sev-group>h4{display:flex;gap:10px;align-items:center}

/* ---- forecast page ----------------------------------------------------------- */
.period{border-bottom:1px solid var(--line);padding:14px 0}
.period:last-child{border-bottom:none}
.period h4{font-family:var(--serif);font-size:18px;margin:0 0 4px;font-weight:600}
.period .p-line{font-family:var(--mono);font-size:13px;color:var(--muted);margin-bottom:6px}
.period p{margin:4px 0 0;font-size:15.5px}
.hr-detail{width:100%;border-collapse:collapse;margin-top:8px}
/* L-3. AT 375px THIS TABLE CLIPPED, and width:100% is why: the table was made
   to fit the card, so six columns squeezed instead of the scroller scrolling,
   and Sky was cut to "Partl Clou" at the card edge while Time and Wind wrapped
   to three lines. Stacked into one labelled block per hour below 640px, which
   is the option the work list prefers over dropping Humidity; Humidity stays
   because this table is the hourly strip's text equivalent and an equivalent
   missing a column the graphic carries is not one.

   The header row is hidden from the eye and kept for the ear: with the table
   displayed as blocks, each cell carries its own column name through
   data-label, and the roles set in wx-forecast.js keep the header association
   that display:block would otherwise strip. */
@media (max-width:640px){
  .hr-detail,
  .hr-detail tbody,
  .hr-detail tr,
  .hr-detail td{display:block;width:100%}
  .hr-detail thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
  .hr-detail tr{border-bottom:1px solid var(--line);padding:9px 0 10px}
  .hr-detail tr:last-child{border-bottom:0}
  .hr-detail td{border:0;padding:2px 2px;display:flex;gap:12px;align-items:baseline;justify-content:space-between}
  .hr-detail td::before{content:attr(data-label);color:var(--faint);font-family:var(--mono);
    font-size:11px;letter-spacing:.05em;text-transform:uppercase;flex:none}
  .hr-detail td:empty{display:none}
  /* the hour leads its own block, at reading size rather than cell size */
  .hr-detail td:first-child{font-size:15px;font-weight:600;padding-bottom:4px}
  .hr-detail td:last-child{text-align:right}
  /* nothing to scroll once the rows stack, so the region stops advertising it */
  .table-scroll{overflow-x:visible}
  /* F-9: one line an hour, "1 PM  56°  7%  ENE 5 mph", in the mono; humidity
     and sky fold under the line and open on a tap. The six-row block per hour
     (above) made 156 hours a very long scroll; it stays on the desktop. */
  .hr-detail tr.hr-line{display:grid;grid-template-columns:4.4em 3em 4em minmax(0,1fr);column-gap:8px;
    align-items:baseline;padding:8px 0;cursor:pointer}
  .hr-detail tr.hr-line td{display:block;width:auto;padding:0;border:0;font-size:13.5px;font-family:var(--mono)}
  .hr-detail tr.hr-line td::before{content:none}
  .hr-detail tr.hr-line td:first-child{font-size:13.5px;font-weight:600;padding:0}
  .hr-detail tr.hr-line td.hr-wind{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .hr-detail tr.hr-line td.hr-more{display:none}
  .hr-detail tr.hr-line.hr-open td.hr-more{display:flex;grid-column:1/-1;gap:10px;margin-top:6px;font-family:var(--sans,inherit)}
  .hr-detail tr.hr-line.hr-open td.hr-more::before{content:attr(data-label);color:var(--faint);font-family:var(--mono);
    font-size:11px;letter-spacing:.05em;text-transform:uppercase;flex:none;width:6em}
  .hr-detail tr.hr-line:focus-visible{outline:2px solid var(--rule);outline-offset:2px}
}

.hr-detail th{font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--faint);text-align:left;padding:4px 8px;border-bottom:1px solid var(--line)}
.hr-detail td{font-size:13.5px;padding:5px 8px;border-bottom:1px solid var(--line)}
.hr-detail td.mono{font-family:var(--mono)}

/* ---- prose pages -------------------------------------------------------------- */
.prose{font-size:16.5px}
.prose h2{font-family:var(--serif);font-weight:600;font-size:24px;margin:30px 0 10px;letter-spacing:-.01em}
.prose h3{font-family:var(--serif);font-weight:600;font-size:19px;margin:22px 0 8px}
.prose ul{padding-left:22px}
.prose li{margin:6px 0}
.lede{font-family:var(--serif);font-size:20px;line-height:1.55;color:var(--ink)}

/* ---- tides and rivers (Phase 4) ------------------------------------------------
   The stage bar is a measuring scale, not a risk gradient we invented: the
   marks ARE the official action, minor, moderate and major stages, and the
   fill only shows where the water sits among them. Every mark clears the 3:1
   non-text contrast bar against its track in BOTH modes, which is why the
   colors differ by mode; re-check before changing any of them. */
.tide-now{display:flex;flex-wrap:wrap;gap:6px 18px;align-items:baseline;margin:2px 0 12px}
.tide-now-v{font-family:var(--mono);font-weight:600;font-size:34px;line-height:1;letter-spacing:-.01em}
.tide-now-k{font-family:var(--mono);font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;color:var(--faint)}
.tide-now-t{font-size:13.5px;color:var(--muted);margin-top:2px}
.tide-next{margin:0 0 12px;font-size:16px;font-weight:600}
.tide-past td{color:var(--faint)}

.rv-head{display:flex;flex-wrap:wrap;gap:4px 14px;align-items:baseline}
.rv-name{font-family:var(--serif);font-weight:600;font-size:19px;letter-spacing:-.01em}
.rv-dist{font-family:var(--mono);font-size:11.5px;color:var(--faint)}
.rv-read{display:flex;flex-wrap:wrap;gap:8px 14px;align-items:center;margin-top:8px}
.rv-stage{font-family:var(--mono);font-weight:600;font-size:26px;letter-spacing:-.01em}
.rv-when{margin:4px 0 0;font-family:var(--mono);font-size:11.5px;color:var(--faint)}
.rv-cat{font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  font-weight:600;border-radius:5px;padding:3px 9px}
.rv-cat-no_flooding{color:#1F6B45;background:#F0F6F2}
.rv-cat-action{color:#6E5300;background:#F6ECCB}
.rv-cat-minor{color:#8F431D;background:#F7E4D9}
.rv-cat-moderate{color:#8A2E14;background:#F6DFD7}
.rv-cat-major{color:#7A1F1F;background:#F6DEDE}
.rv-nodef{margin:12px 0 0;font-size:14px;color:var(--muted);
  border-left:3px solid var(--line);padding-left:12px}

.rv-bar-wrap{margin-top:14px}
.rv-bar{position:relative;height:14px;border-radius:7px;background:var(--chip-bg);overflow:hidden}
.rv-fill{position:absolute;left:0;top:0;bottom:0;background:var(--teal-ink);opacity:.85}
.rv-tick{position:absolute;top:-3px;bottom:-3px;width:2.5px;border-radius:2px}
.rv-sw{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:5px;vertical-align:-1px}
.rv-tick-action{background:#7A5A12}
.rv-tick-minor{background:#9A4517}
.rv-tick-moderate{background:#7E260F}
.rv-tick-major{background:#5F1414}
.rv-legend{display:flex;flex-wrap:wrap;gap:4px 16px;margin:8px 0 0;
  font-family:var(--mono);font-size:11px;color:var(--muted)}
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]) .rv-cat-no_flooding{color:#7FC9A0;background:#16281F}
  :root:not([data-theme=light]) .rv-cat-action{color:#EAD28A;background:#393012}
  :root:not([data-theme=light]) .rv-cat-minor{color:#EFB694;background:#3A2A1E}
  :root:not([data-theme=light]) .rv-cat-moderate{color:#F0A98A;background:#3A2318}
  :root:not([data-theme=light]) .rv-cat-major{color:#F0B9B9;background:#3A2020}
  :root:not([data-theme=light]) .rv-tick-action{background:#E8CB86}
  :root:not([data-theme=light]) .rv-tick-minor{background:#F0A87C}
  :root:not([data-theme=light]) .rv-tick-moderate{background:#F2907A}
  :root:not([data-theme=light]) .rv-tick-major{background:#F3A6A6}
}
:root[data-theme=dark] .rv-cat-no_flooding{color:#7FC9A0;background:#16281F}
:root[data-theme=dark] .rv-cat-action{color:#EAD28A;background:#393012}
:root[data-theme=dark] .rv-cat-minor{color:#EFB694;background:#3A2A1E}
:root[data-theme=dark] .rv-cat-moderate{color:#F0A98A;background:#3A2318}
:root[data-theme=dark] .rv-cat-major{color:#F0B9B9;background:#3A2020}
:root[data-theme=dark] .rv-tick-action{background:#E8CB86}
:root[data-theme=dark] .rv-tick-minor{background:#F0A87C}
:root[data-theme=dark] .rv-tick-moderate{background:#F2907A}
:root[data-theme=dark] .rv-tick-major{background:#F3A6A6}

/* ---- severe-weather mode -------------------------------------------------------
   Deeper, not louder. The panel earns attention through POSITION and SIZE,
   never through a color we invented: the only strong color here is the
   official weather.gov event color on the left edge. No countdown, no pulse,
   no siren fill behind body text. */
.severe-panel{background:var(--card);border:1px solid var(--line);border-left:6px solid var(--copper);
  border-radius:10px;padding:18px 22px;margin:0 0 14px;box-shadow:var(--shadow)}
.sv-head{display:flex;flex-wrap:wrap;gap:6px 14px;align-items:baseline}
.sv-event{font-family:var(--serif);font-weight:600;font-size:26px;letter-spacing:-.01em}
.sv-by{font-family:var(--mono);font-size:11.5px;color:var(--faint)}
.sv-here{margin:10px 0 0;font-size:16px;font-weight:600;color:var(--ink)}
.sv-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));
  gap:12px 20px;margin:16px 0 0;padding-top:14px;border-top:1px solid var(--line)}
.sv-facts .nk{font-family:var(--mono);font-size:10.5px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--faint)}
.sv-facts .nv{font-family:var(--mono);font-size:16px;font-weight:600;margin-top:2px}
.sv-instruction{margin-top:18px;padding:14px 16px;background:var(--paper);
  border:1px solid var(--line);border-radius:8px}
.sv-instruction p{margin:6px 0 0;font-size:17px;line-height:1.55;white-space:pre-wrap}
.sv-route{margin:14px 0 0;font-size:14.5px;color:var(--muted)}
.sv-route a{font-weight:600;text-decoration:underline}
.severe-map{margin:0 0 18px}
.severe-map .map-frame{height:380px}
@media (max-width:640px){
  .sv-event{font-size:22px}
  .severe-panel{padding:15px 16px}
  .severe-map .map-frame{height:300px}
}

/* ---- storm cards (2026-08-24, from Jack's draft) -------------------------------
   Closed by default so every active system fits on one screen. The name is the
   hero, in caps and tier-coloured; the class chip sits beside it; the sustained
   wind and its position on the Saffir-Simpson scale sit beneath. Every tier
   colour was measured on both grounds: all clear 4.5 on the dark card and on
   white, and the WORDS always carry the category so colour never stands alone. */
/* the historical record card, kept visually distinct from the live systems
   above it so history is never mistaken for a forecast */
.hist-card{border-left:3px solid var(--line)}
.hist-lede{font-size:15.5px;line-height:1.55;margin:0 0 12px}
.hist-list{list-style:none;padding:0;margin:0 0 12px;display:grid;gap:8px}
.hist-list li{font-size:14.5px;line-height:1.5;padding-left:14px;position:relative}
.hist-list li::before{content:"";position:absolute;left:0;top:9px;width:5px;height:5px;
  border-radius:50%;background:var(--faint)}
.hist-list b{font-weight:600}
/* underlined for the same reason the handoff link is: axe's link-in-text-block
   wants a link distinguishable from surrounding prose by more than colour, and
   these sit mid-sentence inside each item. */
.hist-list a,.hist-more a{text-decoration:underline}
.hist-more{margin:0;font-size:14px}

.storm-index{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 16px}
.sx-chip{display:inline-flex;align-items:center;gap:8px;font-family:var(--mono);font-size:12px;
  padding:6px 12px;border:1px solid var(--line);border-radius:999px;background:var(--card);
  color:var(--ink);text-decoration:none;white-space:nowrap}
.sx-chip:hover{border-color:var(--muted);text-decoration:none}
.sx-chip:focus-visible{outline:3px solid var(--rule);outline-offset:2px}
/* W-6: the dot is the storm's tier fill, the same in both themes */
.sx-dot{width:9px;height:9px;border-radius:50%;flex:none;background:var(--tf,#8A93A0)}

/* ONE CARD PER STORM, as the draft has it, and it is also what makes the
   header measurable: the name, class chip and advisory line used to sit on
   the page with no opaque ancestor, so they rode on the fixed sky at z-index
   -1 and axe, unable to find a ground, assumed white and reported 1.95. A
   real card gives them a real ground. The inner cards flatten so the storm
   reads as one object rather than a stack of boxes. */
.storm-card{margin:0 0 22px;background:var(--card);border:1px solid var(--line);
  border-radius:14px;padding:18px 20px;box-shadow:var(--shadow)}
:root.sky-active .storm-card{background:rgba(12,18,26,.96);border-color:rgba(255,255,255,.12)}
/* W-6: the board's card ground in dark, #181C22, where the tier inks were
   measured (Cat 4 4.80 to 1). On the page's #2B2B2B card Cat 4 and Cat 5 read
   3.97, and the sky card at .88 over a pale sky 3.84; at .96 it holds 4.85. */
:root[data-theme=dark] .storm-card{background:#181C22}
@media (prefers-color-scheme: dark){:root:not([data-theme=light]):not(.sky-active) .storm-card{background:#181C22}}
.storm-card.is-hit{border-left:3px solid var(--copper-ink)}
.storm-card .card{background:none;border:0;box-shadow:none;padding:0;margin:0}
.storm-card .board{display:block}
.storm-card .card + .card{margin-top:18px}
/* W-6, THE STORM CARD TAKES ITS TIER (Jack, 2026-09-22; tokens and their
   contrast in scripts/storm-tier-test.mjs). --tf is the tier's fill, the same
   in both themes, for bars, dots and the eye's glow; --ti its ink for text,
   one per theme, 4.5 to 1 or better on the card. The colour follows the
   storm's intensity only. */
.storm-card{position:relative;--ti:var(--ti-l)}
.wc-tier{--ti:var(--ti-l)}
:root[data-theme=dark] .storm-card,:root[data-theme=dark] .wc-tier{--ti:var(--ti-d)}
@media (prefers-color-scheme: dark){:root:not([data-theme=light]) .storm-card,:root:not([data-theme=light]) .wc-tier{--ti:var(--ti-d)}}
:root.sky-active .storm-card,:root.sky-active .wc-tier{--ti:var(--ti-d)}
.storm-card[data-tier=td],.wc-tier[data-tier=td]{--tf:#6FA8DC;--ti-d:#6FA8DC;--ti-l:#2E6EA6;--tglow:.16}
.storm-card[data-tier=ts],.wc-tier[data-tier=ts]{--tf:#3FC4C4;--ti-d:#3FC4C4;--ti-l:#137A7A;--tglow:.20}
.storm-card[data-tier=c1],.wc-tier[data-tier=c1]{--tf:#E6D24A;--ti-d:#E6D24A;--ti-l:#7A6600;--tglow:.24}
.storm-card[data-tier=c2],.wc-tier[data-tier=c2]{--tf:#F0B52F;--ti-d:#F0B52F;--ti-l:#8A5E00;--tglow:.28}
.storm-card[data-tier=c3],.wc-tier[data-tier=c3]{--tf:#F08424;--ti-d:#F08424;--ti-l:#A84C08;--tglow:.34}
.storm-card[data-tier=c4],.wc-tier[data-tier=c4]{--tf:#E8592A;--ti-d:#E8592A;--ti-l:#B83A12;--tglow:.34}
.storm-card[data-tier=c5],.wc-tier[data-tier=c5]{--tf:#D7263D;--ti-d:#EC5066;--ti-l:#A8152B;--tglow:.34}
.storm-card[data-tier=post],.wc-tier[data-tier=post]{--tf:#8A93A0;--ti-d:#8A93A0;--ti-l:#5B6470;--tglow:.16}
/* the eye: sized by placeEye to the corner, behind the name only */
.sc-eye{position:absolute;top:0;right:0;width:0;height:0;color:var(--ti);pointer-events:none;z-index:0}
.sc-eye::before{content:"";position:absolute;inset:0;border-radius:50%;
  background:radial-gradient(circle,var(--tf) 0%,transparent 70%);opacity:var(--tglow)}
.sc-eye svg{position:relative;display:block;width:100%;height:100%;overflow:visible}
.sc-head,.sc-meta{position:relative;z-index:1}
.sc-head{display:flex;flex-direction:column;align-items:flex-start;gap:6px;margin:0 0 4px}
.sc-chip{font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;
  font-weight:700;padding:6px 11px;border-radius:6px;white-space:nowrap;line-height:1.3;
  color:var(--ti);box-shadow:inset 0 0 0 1.5px var(--ti)}
/* the 1.5px line is an inset shadow: Chrome snaps a 1.5px border to 1px */
.sc-name{font-family:var(--serif);font-weight:500;letter-spacing:0;font-size:44px;line-height:.94;color:var(--ink)}
.sc-meta{font-family:var(--mono);font-size:11px;color:var(--faint);margin:0 0 10px}

.sc-summary{padding-bottom:4px}
.sc-wind{display:flex;align-items:baseline;gap:12px;margin:0 0 9px}
.sc-wind b{font-family:var(--serif);font-size:92px;font-weight:400;line-height:.8;color:var(--ti);
  font-variant-numeric:lining-nums}
.sc-wind span{font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.sc-scale{margin:0 0 4px}
.sc-track{display:grid;grid-template-columns:repeat(7,1fr);gap:5px}
.sc-seg{position:relative;height:7px;border-radius:99px;background:var(--sf);opacity:.28}
.sc-seg[data-seg=td]{--sf:#6FA8DC}.sc-seg[data-seg=ts]{--sf:#3FC4C4}.sc-seg[data-seg=c1]{--sf:#E6D24A}
.sc-seg[data-seg=c2]{--sf:#F0B52F}.sc-seg[data-seg=c3]{--sf:#F08424}.sc-seg[data-seg=c4]{--sf:#E8592A}
.sc-seg[data-seg=c5]{--sf:#D7263D}
/* lit in the tier's own fill: the segment's colour for a Saffir-Simpson
   tier, the grey for a post-tropical system's wind */
.sc-seg.on{background:var(--tf);opacity:1;box-shadow:0 0 10px var(--tf)}
.sc-labels{display:grid;grid-template-columns:repeat(7,1fr);gap:5px;margin-top:5px;
  font-family:var(--mono);font-size:10px;letter-spacing:.08em;color:var(--faint);text-align:center}
.sc-labels .on{color:var(--ti);font-weight:700}
/* four tiles, the hero's style */
.storm-stats.sc-tiles{grid-template-columns:1fr 1fr;gap:7px;margin-top:10px}
.sc-tiles > div{background:rgba(28,30,31,.04);border:1px solid var(--line);border-radius:10px;padding:7px 11px 8px}
:root[data-theme=dark] .sc-tiles > div,:root.sky-active .sc-tiles > div{background:rgba(0,0,0,.22);border-color:rgba(255,255,255,.07)}
@media (prefers-color-scheme: dark){:root:not([data-theme=light]) .sc-tiles > div{background:rgba(0,0,0,.22);border-color:rgba(255,255,255,.07)}}
.sc-tiles .nk{display:block;font-size:9.5px;line-height:1.2;letter-spacing:.12em}
.sc-tiles .nv{margin:2px 0 0;font-size:19px;line-height:1.15;font-weight:500;color:var(--ink);white-space:nowrap}
.sc-tiles .nv-unit{font-size:12px;font-weight:400;color:var(--faint)}
@media (min-width:641px){
  .sc-name{font-size:52px}
  .sc-wind b{font-size:112px}
}

.sc-rel{display:flex;gap:10px;align-items:flex-start;border-top:1px solid var(--line);
  margin-top:12px;padding:12px 0 2px;font-size:14.5px;line-height:1.5}
.sc-rel .sc-ic{flex:none;font-size:11px;line-height:1.6;color:var(--faint)}
.sc-rel.hit .sc-ic{color:var(--copper-ink)}
.sc-rel b{display:block;font-weight:600}
.sc-rel small{display:block;margin-top:4px;font-size:13px;color:var(--muted)}
.sc-rel p{margin:0;color:var(--muted)}

/* the Storm handoff, gated in JS to a watch or warning over the reader */
.storm-handoff{border-top:1px solid var(--line);margin-top:12px;padding-top:10px;font-size:14.5px}
/* underlined on purpose: axe's link-in-text-block wants a link distinguishable
   from surrounding prose by more than colour, and it measured 1.41 against the
   body text in sky mode. An underline satisfies it without inventing a colour. */
.storm-handoff a{font-weight:600;text-decoration:underline}
@media (max-width:640px){
  .sc-track,.sc-labels{gap:4px}
}

/* ---- maps and the hurricane center (Phase 2) ----------------------------------- */
.map-frame{height:440px;border:1px solid var(--line);border-radius:8px;overflow:hidden;background:var(--map-sea)}
.map-frame.tall{height:520px}
@media (max-width:640px){.map-frame,.map-frame.tall{height:340px}}
/* PHONE IN LANDSCAPE. The app was locked to portrait until version 5, so this
   case could not happen; unlocking rotation without handling it would trade one
   problem for another. A short wide viewport is exactly where a map wants the
   width, but a fixed 440 or 520px frame is then TALLER THAN THE SCREEN and the
   reader scrolls a map that should already be whole. Measured on radar.html at
   812x375: the frame came out 518px against a 375px viewport.

   Capped to the viewport instead, less the masthead and nav, so the map fills
   the landscape screen and stops there. */
@media (orientation:landscape) and (max-height:500px){
  .map-frame,.map-frame.tall,.severe-map .map-frame{height:calc(100vh - 96px);min-height:220px}
  .tide-strip{max-height:calc(100vh - 120px)}
}
.leaflet-container{font-family:var(--sans);font-size:12px}
.leaflet-container:focus-visible{outline:3px solid var(--rule);outline-offset:-3px}
.leaflet-control-zoom a{background:var(--card)!important;color:var(--ink)!important;border-color:var(--line)!important}
/* THE MAP CREDIT, under the frame and on one line. Leaflet's own control sat
   inside the map at the bottom right and the sentence naming both basemap
   sources is 98 characters, which wrapped to THREE lines at 375px and covered
   the corner of the picture on every map. The control is removed in
   WXMap.create, so the two rules that used to style it are gone with it. This
   matches the `.src` line every other card on the site carries. */
.map-credit{font-family:var(--mono);font-size:9.5px;line-height:1.5;color:var(--faint);
  margin:6px 0 0;letter-spacing:.02em}
/* radar loop controls */
.rl-scrub{flex:1;min-width:150px;display:flex;align-items:center}
.rl-scrub input[type=range]{width:100%;accent-color:var(--teal-ink);cursor:pointer}
.rl-scrub input[type=range]:disabled{cursor:default;opacity:.5}
.rl-warnings{margin-top:10px}
.rl-warnings .ww-list li{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}

/* city labels: legible over radar without competing with it. The halo is what
   keeps the name readable on top of a green echo or a warning polygon. */
.wx-city{width:auto!important;height:auto!important;margin:0!important;pointer-events:none}
.wx-city-in{display:flex;align-items:center;gap:4px;white-space:nowrap}
/* near the right edge the name sits left of its dot so it never clips. The
   transform is on this inner element on purpose: Leaflet owns the transform
   on the marker root and overriding it would break positioning. */
.wx-city.flip .wx-city-in{flex-direction:row-reverse;transform:translateX(-100%)}
.wx-city-dot{width:4px;height:4px;border-radius:50%;background:var(--map-label);
  box-shadow:0 0 0 1.5px var(--map-label-halo);flex:none}
.wx-city-name{font-family:var(--sans);font-size:11px;font-weight:600;letter-spacing:.01em;
  color:var(--map-label);
  text-shadow:0 0 3px var(--map-label-halo),0 0 3px var(--map-label-halo),
              0 0 5px var(--map-label-halo),1px 1px 0 var(--map-label-halo)}

/* "Where it is": the advisory's own landmark lines. Sits under the stats and
   above the reaches-you verdict, because it answers a different question and
   must not be mistaken for one: these distances are to Cuba or Bermuda, never
   to the reader. The list is plain and unbulleted so it reads as a small table
   of facts rather than as advice. */
.sc-ref{margin-top:12px}
/* filled from the advisory, which a compact card reads only once opened */
.sc-ref:empty{display:none}
.sc-ref-k{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted);margin-bottom:5px}
.sc-ref-list{list-style:none;margin:0;padding:0;
  font-family:var(--sans);font-size:13.5px;line-height:1.75;color:var(--ink)}
.sc-ref-list b{font-weight:700;font-variant-numeric:tabular-nums}
.sc-ref small{display:block;margin-top:5px;font-size:11.5px;color:var(--muted)}

/* The one line telling an Android visitor the app exists. Deliberately not a
   floating overlay: it sits in the document under the board, so it cannot
   cover content, cannot appear above an active alert, and costs nothing in
   layout shift. Quiet enough that dismissing it is easy and ignoring it is
   easier. */
.getapp{display:block;margin:26px 0 4px;padding:14px 16px;
  border:1px solid var(--line);border-radius:10px;background:var(--card)}
.getapp-txt{font-size:14px;line-height:1.6;color:var(--ink)}
.getapp-txt b{font-weight:700}
.getapp-row{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:11px}
.getapp-btn{display:inline-flex;align-items:center;min-height:44px;padding:0 18px;
  border-radius:8px;background:var(--rule);color:#fff;font-family:var(--sans);
  font-size:14px;font-weight:600;text-decoration:none}
.getapp-btn:hover{background:var(--link-hover);color:#fff;text-decoration:none}
/* --rule is a DEEP teal in light mode and a SOFT one in dark and sky, so white
   on it measured 2.23:1 there. The button keeps the brand fill and flips its
   text to ink instead of picking a second background colour: #10222A on
   #79B8BE measures 7.3:1. */
:root[data-theme=dark] .getapp-btn,
:root.sky-active .getapp-btn{color:#10222A}
:root[data-theme=dark] .getapp-btn:hover,
:root.sky-active .getapp-btn:hover{background:var(--accent-soft);color:#10222A}
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]) .getapp-btn{color:#10222A}
  :root:not([data-theme=light]) .getapp-btn:hover{background:var(--accent-soft);color:#10222A}
}
.getapp-btn:focus-visible{outline:2px solid var(--teal-ink);outline-offset:2px}
.getapp-no{min-height:44px;padding:0 6px;background:none;border:0;cursor:pointer;
  font-family:var(--sans);font-size:13.5px;color:var(--muted);text-decoration:underline}
.getapp-no:hover{color:var(--ink)}
.getapp-no:focus-visible{outline:2px solid var(--teal-ink);outline-offset:2px}

/* ---- the tide curve ------------------------------------------------------
   A wide SVG inside a horizontally scrolling strip. The strip scrolls
   natively, so wheel, trackpad, touch and arrow keys all work without any
   script; the pointer drag in wx-tide-chart.js is an enhancement on top.
   overscroll-behavior-x keeps a sideways flick inside the chart instead of
   triggering the browser's back gesture. */
.tide-chart-wrap{margin:14px 0 4px}
.tide-strip{overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;
  border:1px solid var(--line);border-radius:10px;background:var(--card);
  cursor:grab;-webkit-overflow-scrolling:touch}
.tide-strip.drag{cursor:grabbing;user-select:none}
.tide-strip:focus-visible{outline:2px solid var(--teal-ink);outline-offset:2px}
.tide-svg{display:block}

/* Night as a wash rather than a block: it has to read as background behind
   the curve, never as a data band of its own. */
.tide-night rect{fill:var(--ink);opacity:.055}
.tide-days line{stroke:var(--line);stroke-width:1}
.tide-daylab{font-family:var(--sans);font-size:10px;font-weight:600;fill:var(--muted);
  letter-spacing:.04em}

.tide-area{fill:var(--accent);opacity:.16}
.tide-line{fill:none;stroke:var(--teal-ink);stroke-width:2;stroke-linejoin:round}

/* High and low share one shape and are told apart by fill AND by the label
   sitting above or below the point, so the distinction never rests on colour
   alone. */
.tide-marks circle{stroke:var(--card);stroke-width:1.5}
.tide-marks circle.hi{fill:var(--teal-ink)}
.tide-marks circle.lo{fill:var(--copper-ink)}
.tide-mark-t{font-family:var(--mono);font-size:9.5px;fill:var(--ink);text-anchor:middle}

.tide-sun line{stroke:var(--muted);stroke-width:1.2}
.tide-suntxt{font-family:var(--mono);font-size:9px;fill:var(--muted);text-anchor:middle}

.tide-now-line{stroke:var(--copper-ink);stroke-width:1.4;stroke-dasharray:3 3}
.tide-now-lab{font-family:var(--mono);font-size:9.5px;fill:var(--copper-ink)}
/* at the very end of the window the label sits on the other side of its own
   line, anchored away from the frame (Jack, finding D) */
.tide-now-lab.flip,.tide-daylab.flip{text-anchor:end}
/* a label that flipped to one side of its point anchors away from the frame
   (Jack, finding D) */
.tide-suntxt.a-start,.tide-mark-t.a-start{text-anchor:start}
.tide-suntxt.a-end,.tide-mark-t.a-end{text-anchor:end}

.tide-hint{font-size:12px;color:var(--muted);margin:8px 0 0}
/* A derived curve says so in its own line rather than looking like the
   station's own published series. */
.tide-derived,.tide-nocurve{margin-top:6px}

/* Country and territory names. Deliberately the opposite of a city label: no
   dot, letterspaced caps, centred on its anchor rather than offset from a
   point, so it reads as the name of an AREA. The halo is the same one the
   city labels use, because these sit over coastline, radar and cone fill and
   have to survive all three. Territories drop one step in weight and opacity
   so Bermuda does not compete with Cuba. */
.wx-country{width:auto!important;height:auto!important;margin:0!important;pointer-events:none}
.wx-country-name{display:block;transform:translate(-50%,-50%);white-space:nowrap;
  font-family:var(--sans);font-size:10.5px;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:var(--map-label);opacity:.82;
  text-shadow:0 0 3px var(--map-label-halo),0 0 3px var(--map-label-halo),
              0 0 5px var(--map-label-halo),1px 1px 0 var(--map-label-halo)}
.wx-country.dep .wx-country-name{font-size:9.5px;font-weight:600;opacity:.62;letter-spacing:.09em}

/* ---- radar legend and empty-view note -------------------------------------
   The ramp is NOAA's own colour scale for this layer, read out of its
   GetLegendGraphic response rather than approximated, so the key cannot drift
   from the imagery it explains. */
/* SPC severe outlook. The chip takes its colour from the government's own
   fill for that category, so the swatch beside the sentence is the same colour
   as the shading on the map. */
.spc-box{margin-top:14px}
.spc-say{font-size:14.5px;line-height:1.6;color:var(--ink);margin:0}
.spc-chip{display:inline-block;padding:2px 9px;border-radius:99px;margin-right:8px;
  font-family:var(--mono);font-size:11px;font-weight:600;color:#14202B;
  border:1px solid rgba(0,0,0,.28);vertical-align:1px}
.spc-none{font-size:14px;color:var(--muted);margin:0}
.spc-stamp{font-family:var(--mono);font-size:11px;color:var(--faint);margin:6px 0 0}

.wxr-legend{margin-top:0}
.wxr-legend-h{font-family:var(--serif);font-size:16px;margin:0 0 10px}
.wxr-key{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px}
.wxr-key-label{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint)}
.wxr-ramp{display:block;flex:1 1 220px;min-width:200px;height:14px;border-radius:3px;
  border:1px solid var(--line)}
.wxr-ends{display:flex;justify-content:space-between;flex:1 1 100%;
  font-family:var(--mono);font-size:10.5px;color:var(--faint)}
.wxr-note{font-size:13.5px;line-height:1.55;color:var(--muted);margin:10px 0 0}
.wxr-swatches{display:flex;flex-wrap:wrap;gap:8px 18px;align-items:center;margin-top:14px}
.wxr-sw{display:inline-flex;align-items:center;gap:7px;font-size:13.5px;color:var(--ink)}
.wxr-sw i{width:12px;height:12px;border-radius:3px;flex:none;border:1px solid rgba(0,0,0,.25)}
.rl-local{margin-bottom:16px}
.rl-local:empty{margin-bottom:0}
.rl-empty{margin:10px 0 0;font-size:13.5px;line-height:1.55;color:var(--muted)}
/* The loop's palette line. Not a warning: the frames being in the standard
   NEXRAD palette is the normal, decided state, so it reads as a note and not
   as the orange stale stamp the dead frame list used to leave on this page. */
/* The record book's failure state. Same type scale as the card it replaces,
   quieter colour: it is an absence being named, not a warning. */
.rb-out{margin:14px 0 0}
.rb-out .rb-cap{margin:0 0 4px}
.rb-out .rb-say{color:var(--muted)}
/* L-6: the board's condition text, and the issued wording behind it. The
   button is the text itself, not a separate control, so the row gains no
   furniture; the dotted underline is the only thing that says it opens. */
/* W-5: every row the same control, one face for the word, one mark to open */
.wk-cond.wk-cond{display:block}
.wk-cond-btn{display:flex;align-items:flex-start;gap:10px;width:100%;background:none;border:0;padding:0;margin:0;
  color:inherit;text-align:left;cursor:pointer;font:inherit}
.wk-cond-btn .wk-cond-text{font-family:var(--serif);font-style:normal;font-weight:400;font-size:19px;
  line-height:1.3;color:var(--ink)}
/* the icon and the chevron sit on the word's FIRST line, so a word that wraps
   does not pull them to the middle of two lines */
.wk-cond-btn .wk-icon{margin-top:calc((1.3em - 20px) / 2)}
.wk-cond-btn .wk-icon{font-size:19px}
@media (max-width:640px){.wk-cond-btn .wk-icon{font-size:17px}}
.wk-cond-btn .wk-chev{flex:none;margin-left:2px;margin-top:.55em;font-size:9px;color:var(--faint);transition:transform .15s}
.wk-cond-btn[aria-expanded=true] .wk-chev{transform:rotate(180deg)}
.wk-cond-btn:focus-visible{outline:2px solid var(--rule);outline-offset:3px;border-radius:4px}
@media (prefers-reduced-motion:reduce){.wk-cond-btn .wk-chev{transition:none}}
@media (max-width:640px){.wk-cond-btn .wk-cond-text{font-size:17px}}
.wk-cond-issued{margin:6px 0 0 30px;font-family:var(--mono);font-size:12px;line-height:1.45;
  color:var(--muted);max-width:44ch}
/* What the site covers, above a disambiguation list. Quiet: it is an answer to
   a question the reader has not asked out loud, not a warning. */
.loc-scope{font-size:12.5px;line-height:1.5;color:var(--faint);margin:0 0 8px;flex-basis:100%}
/* The one unfiltered number on the site, saying so. */
.count-note{font-size:12.5px;line-height:1.5;color:var(--faint);margin:8px 0 0}
.rl-palette{margin:8px 0 0;font-size:13px;line-height:1.55;color:var(--muted)}
/* Warned areas, grouped by hazard family. Each group is a disclosure whose
   summary always states its counts, so folding one hides the place names but
   never the fact that something is in effect. */
.ww-groups{margin:0}
.rl-source{font-size:13px;color:var(--muted);margin:8px 0 0}
.ww-hint{font-size:13px;color:var(--muted);margin:0 0 9px}
/* warned shapes are tappable; they reveal their own text in the list below */
.leaflet-interactive{cursor:pointer}
.ww-group{border:1px solid var(--line);border-radius:9px;margin:0 0 8px;
  background:var(--chip-bg)}
.ww-group summary{display:flex;align-items:center;gap:9px;cursor:pointer;
  padding:10px 13px;font-size:14px;list-style:none}
.ww-group summary::-webkit-details-marker{display:none}
.ww-group summary::after{content:"\25BC";margin-left:auto;font-size:9px;color:var(--faint);
  transition:transform .15s ease}
.ww-group[open] summary::after{transform:rotate(180deg)}
.ww-group summary:hover{color:var(--ink)}
.ww-group summary:focus-visible{outline:3px solid var(--rule);outline-offset:-2px;border-radius:9px}
.ww-group summary .sw{width:12px;height:12px;border-radius:3px;flex:none;
  border:1px solid rgba(0,0,0,.3)}
.ww-count{font-family:var(--mono);font-size:11.5px;color:var(--muted);font-weight:400}
/* county-wide alerts have no polygon, so they are marked as such rather than
   implying a shape on the map that does not exist */
.ww-scope{display:block;font-family:var(--mono);font-size:10.5px;color:var(--faint);margin-top:2px}
.ww-zone{opacity:.95}
.ww-group .ww-list{margin:0;padding:2px 13px 12px 13px;list-style:none}
/* Each entry is now a full as-issued alert card from WX.alertItem, the same
   component /alerts renders, so the official wording cannot drift between the
   two surfaces. The row styling that used to sit here is gone: the card brings
   its own severity chip and colour band. */
.ww-group .ww-item{padding:10px 0;border-top:1px solid var(--line)}
.ww-group .ww-item:first-child{border-top:0}
/* focus ring when a tap on the map sends the reader here */
.ww-group .ww-item:focus-visible{outline:3px solid var(--rule);outline-offset:3px;border-radius:8px}
.ww-group .ww-item .alert{margin:0}
.ww-area{font-family:var(--mono);font-size:11.5px;color:var(--muted);margin:6px 0 0;line-height:1.5}
@media (prefers-reduced-motion:reduce){.ww-group summary::after{transition:none}}

.ww-intro{font-size:13.5px;color:var(--ink);margin:10px 0 6px}
.ww-more{font-size:13px;color:var(--copper-ink);margin:8px 0 0;font-weight:600}

.map-legend{display:flex;flex-wrap:wrap;gap:6px 16px;margin-top:10px;font-family:var(--mono);font-size:11.5px;color:var(--muted)}
.map-legend .sw{display:inline-block;width:12px;height:12px;border-radius:3px;vertical-align:-1px;margin-right:6px;border:1px solid var(--line)}
.storm-class{font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--copper-ink);font-weight:600}
.storm-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));gap:10px 18px;margin-top:12px}
.storm-stats .nk{font-family:var(--mono);font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;color:var(--faint)}
.storm-stats .nv{font-family:var(--mono);font-size:16px;font-weight:600}
.pos-table{width:100%;border-collapse:collapse}
.pos-table th{font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--faint);text-align:left;padding:4px 8px;border-bottom:1px solid var(--line)}
.pos-table td{font-size:13.5px;padding:6px 8px;border-bottom:1px solid var(--line);font-family:var(--mono)}
.pos-table tr:last-child td{border-bottom:none}
.ww-list{margin:8px 0 0;padding:0;list-style:none}
.ww-list li{padding:7px 0;border-bottom:1px solid var(--line);font-size:14.5px}
.ww-list li:last-child{border-bottom:none}
.ww-list .sw{display:inline-block;width:12px;height:12px;border-radius:3px;margin-right:8px;vertical-align:-1px}
/* The old buoy strip's rules are gone with the markup they styled. Coastal
   and Hurricane built their own rows from the NWS station record, in .buoy /
   .b-id / .b-vals; all four pages now render WXBuoy's card in .bo-buoy /
   .bo-row, so nothing referenced these any more. Verified repo-wide before
   deleting, the same check P-2's stylesheet audit used. */
.center-strip{background:var(--card);border:1px solid var(--line);border-left:6px solid var(--copper);border-radius:8px;padding:12px 18px;margin:14px 0 4px;box-shadow:var(--shadow);display:flex;flex-wrap:wrap;gap:8px 18px;align-items:baseline}
.center-strip b{font-family:var(--serif);font-size:18px}
.center-strip a{font-weight:600}
/* The raw advisory is paragraphs, not columns, so it wraps and a phone reads
   it without scrolling sideways (Jack, finding D). pre-wrap already broke the
   lines at spaces; overflow-wrap takes the one thing it cannot break, an
   unbroken run of characters longer than the column, which left 6px of
   sideways scroll on a 375px phone (measured). The mono face stays. */
.adv-text{white-space:pre-wrap;overflow-wrap:anywhere;font-family:var(--mono);font-size:13px;line-height:1.55;background:var(--paper);border:1px solid var(--line);border-radius:7px;padding:12px 14px;margin:10px 0 0;overflow-x:auto}

/* ---- the Living Sky ------------------------------------------------------------
   The board wears the weather. The base scene is built ONLY from the observed
   condition and the daypart, so it is always literally true; a hazard is a
   treatment layered over it (see wx-sky.js). Card scrim below is a MEASURED
   floor, not a taste call: at 0.68 over the lightest possible sky (day/clear
   bottom stop #8FCBDF) the three text tones clear AA at 7.44, 5.67 and 4.60.
   The prototype shipped 10px labels at 48 percent opacity, which measured
   2.09 there. Do not soften either number without re-running that check. */
.wxsky{position:fixed;inset:0;z-index:-1;overflow:hidden;pointer-events:none}
.wxsky-grad{position:absolute;inset:0}
.wxsky-fx{position:absolute;inset:0;overflow:hidden}
.wxsky-wash{position:absolute;inset:0;pointer-events:none}
/* multiply so the hazard can only DARKEN the true scene, never recolour it */
.wxsky-hazard{position:absolute;inset:0;pointer-events:none;mix-blend-mode:multiply}

/* the sky is decorative; it must never be announced or focusable */
@media (prefers-reduced-motion:reduce){
  .wxsky-fx *{animation:none!important}
}

/* illustration animations */
@keyframes rot{to{transform:rotate(360deg)}}

/* B-2: the falling, shimmering and gusting keyframes that lived here moved
   parts of the old condition art, which the icon family replaced. */
/* sun / moon */
.wxsky-orb{position:absolute;border-radius:50%;transition:all 1.4s cubic-bezier(.2,.7,.2,1)}
.wxsky-sun{width:120px;height:120px;background:radial-gradient(circle at 42% 38%,#FFF6DA,var(--glow) 42%,rgba(242,193,78,.35) 66%,transparent 72%);
  filter:blur(.4px);box-shadow:0 0 70px 24px rgba(242,193,78,.20),0 0 190px 70px rgba(242,193,78,.09)}
.wxsky-rays{position:absolute;width:520px;height:520px;left:50%;top:50%;transform:translate(-50%,-50%);
  /* REPEATING, and that word is the whole fix. A plain conic-gradient whose
     last stop sits at 22deg fills 22deg to 360deg with that final colour, so
     this drew exactly ONE 3deg wedge on the sun and rotated it: a ball on a
     stick. 22.5deg divides 360 exactly, giving 16 evenly spaced rays with no
     seam where the gradient wraps. */
  background:repeating-conic-gradient(from 0deg,rgba(255,240,200,.16) 0 3deg,transparent 3deg 22.5deg);
  border-radius:50%;-webkit-mask:radial-gradient(circle,transparent 28%,#000 46%,transparent 74%);
  mask:radial-gradient(circle,transparent 28%,#000 46%,transparent 74%);animation:spin 190s linear infinite}
@keyframes spin{to{transform:translate(-50%,-50%) rotate(360deg)}}
.wxsky-moon{width:86px;height:86px;background:radial-gradient(circle at 38% 34%,#FBF7EC,#C9CFD8 58%,#9AA6B4);
  box-shadow:0 0 60px 18px rgba(210,225,245,.20),inset -16px -6px 0 -2px rgba(8,17,28,.62)}
/* stars */
.wxsky-star{position:absolute;background:#fff;border-radius:50%;opacity:0;animation:tw 5s ease-in-out infinite}
@keyframes tw{0%,100%{opacity:.15}50%{opacity:.9}}
/* clouds: soft blurred blobs, drifting */
.wxsky-cloud{position:absolute;border-radius:50%;filter:blur(26px);opacity:.5;animation:drift linear infinite}
@keyframes drift{from{transform:translateX(-22vw)}to{transform:translateX(122vw)}}
/* ---- precipitation, drawn as it falls --------------------------------------
   Jack's read, 2026-09-19: "the rain falling, it needs to look like actual
   rain falling not just lines."

   Every faller LEANS by exactly the angle it travels, so the streak is its
   path instead of a bar crossing it, and the drift is in vh like the fall so
   that stays true at any viewport shape. The lean and the drift arrive
   together from wx-sky.js, which computes one from the other. A faller with
   neither still works: both custom properties default to no lean at all. */
.wxsky-drop{position:absolute;animation:fall linear infinite;
  will-change:transform}
@keyframes fall{
  from{transform:translate3d(0,-16vh,0) rotate(var(--lean,0deg))}
  to{transform:translate3d(calc(var(--drift,0) * 1vh),114vh,0) rotate(var(--lean,0deg))}
}
/* The head is bright and the tail fades, which is the direction a drop is
   actually moving. Width, brightness and length come per drop from its depth
   band. */
.wxsky-rain{border-radius:2px;
  background:linear-gradient(transparent,rgba(198,226,246,.92))}
.wxsky-hail{border-radius:50%;background:rgba(226,240,252,.95)}
/* Snow drifts rather than falls, and the wander is written into the one
   transform: a second animation on transform would fight this one, and a sway
   done with margins would relayout on every frame for every flake. */
.wxsky-flake{border-radius:50%;background:rgba(240,248,255,.92);
  animation-name:driftfall}
@keyframes driftfall{
  0%{transform:translate3d(0,-16vh,0)}
  25%{transform:translate3d(1.6vh,16vh,0)}
  50%{transform:translate3d(-1.2vh,48vh,0)}
  75%{transform:translate3d(1.8vh,80vh,0)}
  100%{transform:translate3d(0,114vh,0)}
}
/* lightning */
.wxsky-streak{position:absolute;height:2px;border-radius:2px;background:linear-gradient(90deg,transparent,rgba(226,238,252,.5),transparent);animation:blow linear infinite}
@keyframes blow{from{transform:translateX(-30vw)}to{transform:translateX(130vw)}}

/* CONTRAST IS CARRIED BY THE SCRIM ALONE, NOT BY THE BLUR.
   backdrop-filter makes the true text ground unpredictable: it depends on
   whatever sky happens to sit behind the card, and neither axe nor we can
   compute it (axe measured #5A5E63 where the colour math predicted #364D59,
   and 51 nodes failed). So the scrim is opaque enough to guarantee the ratio
   on its own and the blur is left as pure depth. The sky reads in the gaps
   between cards and around the page, which is where the atmosphere actually
   lives. Do not thin this scrim to show more sky through the text. */
/* ---- sky mode ---
   SPECIFICITY NOTE: these rules must use :root.sky-active, not html.sky-active.
   The theme blocks are :root[data-theme=dark] and :root:not([data-theme=light]),
   both (0,2,0); html.sky-active is only (0,1,1) and silently loses, which put
   the dark-mode link token back on the footer at 4.15 against a 4.5 floor.
   :root.sky-active ties at (0,2,0) and wins on source order from here.
   ------------------------------------------------------------------
   Active only when a location is set, i.e. when there is real weather to
   represent. Every token below was measured against the LIGHTEST possible sky
   (day/clear bottom stop #8FCBDF) through the 0.68 card scrim: ink 7.86,
   muted 5.99, faint 4.86, link 5.55, kicker 5.38. Overriding the custom
   properties means every existing component inherits sky-safe colour with no
   per-component work.

   The masthead keeps its OWN scrim rather than sitting naked on the sky: the
   wordmark directly on a bright day sky measures 1.58, which is why the
   prototype's transparent masthead could not ship as drawn. */
:root.sky-active{
  /* --faint was #B4C2CA, which measured 4.29 on the masthead's translucent
     ground in sky mode and failed AA by a hair. #C9D4DA is 5.20 there, and
     higher still on the card ground, so nothing else regresses. */
  --ink:#F4F1EA; --muted:#CBD6DC; --faint:#C9D4DA;
  --line:rgba(255,255,255,.14);
  --card:rgba(12,18,26,.88);
  --card-solid:#0C121A;
  --paper:transparent;
  --link:#9FD6DE; --link-hover:#C6E8EF;
  --teal-ink:#9BD3DC;
  --copper-ink:#F0B98A;
  --chip-bg:rgba(255,255,255,.10); --chip-fg:#DCE6EA;
  --shadow:0 22px 50px -30px rgba(0,0,0,.55);
}
:root.sky-active body{background:transparent}
:root.sky-active .card,
:root.sky-active .loc-bar,
:root.sky-active .alert,
:root.sky-active .severe-panel,
:root.sky-active .center-strip{
  background:var(--card);
  border-color:rgba(255,255,255,.12);
  -webkit-backdrop-filter:blur(9px);backdrop-filter:blur(9px);
}
:root.sky-active .masthead,
:root.sky-active .mh-nav{
  background:rgba(10,15,22,.72);
  border-color:rgba(255,255,255,.12);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
}
:root.sky-active .top-rule{background:rgba(255,255,255,.22)}
:root.sky-active .mh-word{color:#F4F1EA}
:root.sky-active .mh-word-site{color:#9BD3DC}
:root.sky-active footer.site{background:rgba(10,15,22,.72);border-color:rgba(255,255,255,.12);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}
:root.sky-active .theme-toggle{border-color:rgba(255,255,255,.18)}
/* THE SECTION HEAD WAS THE ONE THING PAINTING STRAIGHT ONTO THE SKY.
   Every other surface earns a ground somewhere up its ancestor chain (cards,
   the location bar, alerts, the masthead, the footer). The section head did
   not: html, body and .sec-head were all transparent, so its true background
   was the sky gradient itself, and sky-mode had already switched its text to
   near-white for a dark sky.

   Measured against the lightest stop of a clear-day sky, rgb(137,195,214):
   the heading was 1.72 and its note 1.06. Not marginal, invisible. It read
   correctly at dawn and at night, which is exactly why it survived the first
   pass; only a bright afternoon exposes it.

   A scrim at .78 puts the heading at 10.6 and the note at 6.6 on that same
   worst case, and the panel reads as deliberate rather than as a patch. */
:root.sky-active .sec-head{
  background:rgba(12,18,26,.78);border-radius:9px;padding:9px 14px;
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
:root.sky-active .sec-head h2{color:#F4F1EA}
:root.sky-active .sec-head .bar{background:rgba(255,255,255,.16)}
:root.sky-active .skel{background:rgba(255,255,255,.10)}
/* The alerts page has the same defect .sec-head had, in three more places:
   its section heads, its severity group heads and its trailing source line
   sit straight on the sky with no card under them. Measured against the
   lightest clear-day stop rgb(143,203,223), the group head ran 2.22 and the
   source line 1.30. Both read fine at dawn and at night, which is why they
   survived; a bright afternoon is what exposes them. Same remedy and the
   same .78 as the section head above, so the page reads as one system. The
   group head hugs its own content, since a full width bar per severity
   stacks into three heavy slabs. */
:root.sky-active .al-sec{
  background:rgba(12,18,26,.78);border-radius:9px;padding:9px 14px;
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
:root.sky-active .sev-group>h4{
  display:inline-flex;background:rgba(12,18,26,.78);border-radius:9px;padding:7px 13px;
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
:root.sky-active .al-foot{
  background:rgba(12,18,26,.78);border-radius:9px;padding:8px 13px;
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
/* the alert keeps its official event colour band on the left */
:root.sky-active .alert-text,
:root.sky-active .adv-text,
:root.sky-active .sv-instruction{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.14)}

/* delight affordances stand down once the situation is a watch or a warning */
:root.sky-active[data-tier=watch] .sky-note,
:root.sky-active[data-tier=warning] .sky-note{display:none}
.sky-note{margin:10px 0 0;font-family:var(--serif);font-style:italic;font-size:15px;color:var(--muted)}
/* In the hero the ground is the scrim, not a card, so the colour is the hero's
   own proven value rather than a token: #DDE4E8 is the same one .wx-feels uses
   and was measured against the deepest scrim stop. */
.wx-hero .sky-note{color:#DDE4E8}

/* The condition illustration. Sized well inside the hero rather than fitted to
   it: the hero is overflow:hidden, and art fitted exactly loses pixels top and
   bottom the moment a viewBox overshoots its box. overflow:visible on the svg
   itself means a stray stroke is never clipped either. Purely decorative, so it
   sits under the text layer and takes no pointer events. */
/* The reading and the condition illustration side by side, the proportions the
   design uses. The art was a small badge pinned to the top corner, which left a
   hole beside the temperature exactly where the eye goes next.

   overflow:visible on the svg because the hero is overflow:hidden and art
   fitted tight to its box loses a ray or a streak that reaches past the
   viewBox. */
/* The art column used to be .85fr, which on a 1036px hero made it 409px wide
   holding a 196px drawing: 107px of dead space on its left and 106px on its
   right. It is sized to the artwork now, and the artwork fills it, so the
   reading gets the width back and the gaps close. */
.wx-hero-grid{display:grid;grid-template-columns:auto minmax(0,1fr);gap:0 30px;
  align-items:center}
/* THE LOCKUP: the number and the artwork read as one object, not two islands.
   Measured on the live card at 1060px before this change: the temperature
   ended at x=283 and the art began at x=670, a 387px hole through the middle
   at the height the eye lands first, in a card 707px tall. */
.wx-lock{display:flex;align-items:center;gap:4px;min-width:0}
.wx-side{min-width:0}
/* no art for this condition: the lockup is just the number, and the side
   column takes the width back rather than leaving a hole beside it */
/* under a live alert the drawing stays but steps back, so the warning above
   keeps the page */

/* ---- the hero's reading, rail and hour wash -----------------------------------
   Every ink here was measured against the card ground AFTER the hour wash is
   applied, at all four dayparts. Worst cases: rain #8EC9E8 8.74, labels 6.13,
   values 13.95, the vs-normal arrows 6.68 and 5.35. The wash is capped at 0.16
   precisely so it can never become load-bearing for legibility, the same rule
   the scrim follows: it tints the hour, it does not carry the text. */

/* W-11 item 2: the board follows the finger between places and settles back
   with one transition. The transform is the only thing that moves: no reload,
   no scroll to the top, and the height is held in script across the swap so
   the page cannot collapse and snap back. A reader who asked for less motion
   gets the place change with none of it. */
#main{transition:transform .18s cubic-bezier(.22,.61,.36,1),opacity .14s linear;will-change:auto}
#main.pt-swap{will-change:transform,opacity}
@media (prefers-reduced-motion:reduce){
  #main{transition:none}
}

/* today's high and low, beside the reading */
.wx-hilo{display:flex;gap:26px;margin-top:14px}
.wx-hilo-row{display:flex;align-items:baseline;gap:8px;font-family:var(--mono);font-size:20px}
.wx-hilo-row em{font-style:normal;color:#9AA3A9;font-size:9.5px;letter-spacing:.16em;
  text-transform:uppercase}
.wx-hilo-row b{font-weight:600;color:var(--t-ink-dark,#F4F1EA)}

/* vs normal: the archive figure the card already cited but never showed.
   The words say above/below too, so the arrow is never the only carrier. */
.wx-norm{display:inline-flex;align-items:center;gap:8px;margin:12px 0 0;
  font-family:var(--mono);font-size:12px;letter-spacing:.02em;
  padding:5px 12px;border-radius:999px;background:rgba(244,241,234,.08);
  border:1px solid rgba(244,241,234,.16);color:#DDE4E8}
.wx-norm b{font-weight:500;color:#F4F1EA}
.wx-norm-arrow{font-size:11px;line-height:1}
.wx-norm.up .wx-norm-arrow{color:#F09248}
.wx-norm.down .wx-norm-arrow{color:#6C97E0}

/* THE RAIL: bordered cells, not a footer row of equal-weight numbers. Rain is
   tinted because it is the thing most people opened the page to find. */
/* values never wrap mid-unit: wind was breaking to "SSE 8" / "mph" */
.wx-stats .nv{white-space:nowrap}
/* the clock time, which is the longest value here and the least like a
   headline number; sized to fit the cell the sixth tile made narrower (L-4) */
/* `.wx-stat-time` used to shrink a clock time here. Its only user was the
   Sunset tile, which left the hero for A-5; the astro card still prints it,
   at its own size. */
.wx-stats .wx-rain-cell .nv.rain{color:#8EC9E8}
.wx-stats .wx-rain-cell{position:relative}
/* `.wx-rain-cell:empty{display:none}` used to sit here. The cell is never
   empty now: it is drawn with a dash and filled when the week lands, because
   hiding it reflowed the grid on arrival and left seven tiles for good when
   the forecast never came. */
/* A reading that is not reported: the dash, dimmed, so the eye passes over it
   to the numbers that are there. */
.wx-stats .nv.nv-none{color:rgba(255,255,255,.42);font-weight:400}
/* The line under a value: the moon's phase name, the UV band. Small and
   allowed to wrap, because .nv is nowrap and "Waxing gibbous" is fourteen
   characters that would not fit a phone cell at a headline size. */
.wx-stats .nv .nv-sub{display:block;font-family:var(--sans);font-size:11px;font-weight:500;
  letter-spacing:0;color:#C9D2D8;white-space:normal;line-height:1.3;margin-top:2px}

/* the hour wash: one tinted layer keyed to the daypart, so 5 PM and 8 PM do
   not look identical on a card that is otherwise flat charcoal */
.wx-hero .wx-hourwash{position:absolute;inset:0;pointer-events:none;z-index:0;
  opacity:0;transition:opacity .5s,background .5s}
@media(prefers-reduced-motion:reduce){.wx-hero .wx-hourwash{transition:none}}
:root.sky-active .wx-hero .wx-hourwash{opacity:1}
.wx-hero[data-tod=day] .wx-hourwash{background:linear-gradient(160deg,rgba(46,134,174,.16),transparent 78%)}
.wx-hero[data-tod=night] .wx-hourwash{background:linear-gradient(160deg,rgba(11,29,51,.16),transparent 78%)}
.wx-hero[data-wx=storm] .wx-hourwash,.wx-hero[data-wx=rain] .wx-hourwash{background:linear-gradient(160deg,rgba(90,110,130,.16),transparent 78%)}

/* the hero on a scrubber: a scrubbed hour must never read as the present.
   Both wear opaque chips because the amber measured 3.61 bare on the bright
   hero band; on the chip ground it clears 9. The dot pulse is decorative and
   stops under reduced motion. */
.wx-when{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);
  font-size:11px;letter-spacing:.08em;text-transform:uppercase;white-space:nowrap;
  background:rgba(10,16,24,.72);border:1px solid rgba(242,193,78,.55);color:#F2C14E;
  border-radius:99px;padding:4px 11px;margin-left:auto}
.wx-backnow{font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  background:rgba(10,16,24,.72);border:1px solid rgba(244,241,234,.4);color:#F4F1EA;
  border-radius:99px;padding:4px 11px;cursor:pointer;opacity:0;pointer-events:none;transition:opacity .15s}
.wx-backnow.on{opacity:1;pointer-events:auto}
.wx-backnow:hover{border-color:#F4F1EA}
.wx-backnow:focus-visible{outline:3px solid #fff;outline-offset:2px}
@media(prefers-reduced-motion:reduce){.wx-backnow{transition:none}}
/* min-height matches the drawing rather than exceeding it: the reading column
   is ~108px tall, so an oversized row left 88px of air beside it. */

/* THE ART ANIMATIONS. These never ran. The illustrations carry class="spin-slow",
   "spin-med" and "spin-fast", and the stylesheet only defined .wxsky-spin-slow,
   a different name, with the other two missing entirely. Ten of the twenty four
   conditions were drawing rays, shimmer and storm bands that simply sat still,
   which is why the hero looked flat next to the prototype.

   transform-origin is the CENTRE OF THE VIEWBOX, not of the element. These are
   200x200 SVGs and a group rotating about its own bounding box would wobble off
   axis instead of turning about the sun. */
.wx-cyclone svg .spin-med,
.wx-cyclone svg .spin-fast{transform-origin:100px 100px}
.wx-cyclone svg .spin-med{animation:rot 42s linear infinite}
.wx-cyclone svg .spin-fast{animation:rot 13s linear infinite}


@media (prefers-reduced-motion:reduce){
  .wx-cyclone svg .spin-med,.wx-cyclone svg .spin-fast{animation:none}
}
@media (max-width:820px){
  /* Single column, and the READING stays first. The design prototype floats
     the art above it on narrow screens; on a site whose whole argument is data
     before decoration, pushing the actual temperature below an illustration on
     the smallest screen is the wrong way round. */
  .wx-hero-grid{grid-template-columns:1fr;gap:10px}
}

/* Under sky mode the hero stops painting its own gradient: the full-bleed
   Living Sky behind the page is the atmosphere now, and the hero becomes a
   card floating on it. Its private fx and scrim layers stand down so the two
   systems never fight. */
:root.sky-active .wx-hero{background:rgba(12,18,26,.88)!important;min-height:0;
  border:1px solid rgba(255,255,255,.12);
  -webkit-backdrop-filter:blur(9px);backdrop-filter:blur(9px)}
:root.sky-active .wx-hero .wx-fx,
:root.sky-active .wx-hero .wx-scrim,
:root.sky-active .wx-hero .wx-hero-video,
:root.sky-active .wx-hero .hero-pause{display:none}
:root.sky-active .wx-hero-inner{padding:22px 24px 18px}
/* ...EXCEPT THE BOARD'S HERO, which draws its own sky since B-1. It keeps its
   height and its scrim, and drops the backdrop blur: behind an opaque sky that
   blur is invisible and would still re-blur the page sky's animation under it
   on every frame. Specificity 0,4,0 so it outranks the three rules above. */
:root.sky-active .wx-hero.wx-hero-board{min-height:560px;-webkit-backdrop-filter:none;backdrop-filter:none}
:root.sky-active .wx-hero.wx-hero-board .wx-scrim{display:block}
/* The pale skies: every snow state, and fog and overcast by day and at dusk.
   Measured on the branch deploy with the two-screenshot probe, the board's
   light scrim left the 108px number at 1.47:1 over a snow day and the mono
   lines at 2.4 to 3.0, because these grounds sit near luminance 0.22 where the
   text needs 0.09. Over the text zone they get the scrim that makes it so;
   the other twelve states keep the board's band. */
.wx-hero.wx-hero-board[data-sky-family="snow"] .wx-scrim,
.wx-hero.wx-hero-board[data-sky-family="fog"]:not([data-sky-daypart="night"]) .wx-scrim,
.wx-hero.wx-hero-board[data-sky-family="cloudy"]:not([data-sky-daypart="night"]) .wx-scrim{
  background:linear-gradient(180deg,rgba(10,15,20,.70) 0%,rgba(10,15,20,.68) 45%,rgba(10,15,20,.74) 62%,rgba(10,15,20,.84) 100%)}
:root.sky-active .wx-hero.wx-hero-board .wx-hero-inner{padding:10px 15px 16px}
@media (min-width:641px){
  :root.sky-active .wx-hero.wx-hero-board{min-height:540px}
  :root.sky-active .wx-hero.wx-hero-board .wx-hero-inner{padding:20px 24px 22px}
}

/* ---- footer -------------------------------------------------------------------- */
footer.site{border-top:1px solid var(--line);background:var(--card);margin-top:30px}
footer.site .wrap{padding:26px 22px 34px}
.frow{display:flex;flex-wrap:wrap;gap:12px 26px;align-items:baseline;justify-content:space-between}
.fbrand{font-family:var(--serif);font-weight:600;font-size:20px}
.flinks{display:flex;flex-wrap:wrap;gap:4px 18px;font-size:14px}
.fnote{font-size:13px;color:var(--muted);margin:14px 0 0;line-height:1.6}
.fnote b{color:var(--ink)}
/* links inside running text carry a non-color cue (WCAG 1.4.1) */
.fnote a,.prose p a,.prose li a,.lede a,.unavail a,.failsafe a,.loc-note a{text-decoration:underline}

/* ---- small screens --------------------------------------------------------------- */
@media (max-width:640px){
  body{font-size:16px}
  .card{padding:15px 16px}
  .mh-slogan{display:none}
  .now-grid{grid-template-columns:repeat(auto-fit,minmax(104px,1fr))}
  .days .d-wind{display:none}
  .days th,.days td{padding:8px 6px;font-size:13.5px}
}
@media (max-width:360px){
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation:none!important;transition:none!important}
}

/* Ambient ground on the non-board pages. The layer itself is the same fixed
   element the Living Sky uses, so it inherits the z-index and the pointer
   behaviour that were already settled there. */
/* PROSE NEEDS A GROUND, and this is measured rather than assumed. Body copy
   painted straight onto the ambient layer fails everywhere: 4.34 on the brand
   gradient's lightest stop, 2.02 at dusk, and 1.08 on the day sky, which is
   pale blue. The panel is the same rgba(12,18,26,.88) the cards use, whose
   pairings were already verified, and it takes the worst case to 9.10.

   It also makes the contrast COMPUTABLE. Without an opaque ancestor, axe walks
   up to nothing and assumes white, which reported 23 failures at 1.64 that were
   measuring an imaginary ground. A real panel means the checker and the eye
   agree. */
:root.sky-active main.prose{background:rgba(12,18,26,.88);border:1px solid rgba(255,255,255,.14);
  border-radius:12px;padding:26px 30px;margin-top:22px;margin-bottom:30px;
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
@media (max-width:640px){:root.sky-active main.prose{padding:20px 18px}}
:root.sky-active .prose{color:var(--ink)}
:root.sky-active .prose h1,:root.sky-active .prose h2,
:root.sky-active .prose h3{color:#F4F1EA}
:root.sky-active .prose a{color:var(--link)}
:root.sky-active .gd-index li{border-color:rgba(255,255,255,.16)}
:root.sky-active .gd-cta{background:rgba(12,18,26,.88);border-color:rgba(255,255,255,.16)}
:root.sky-active .crumb a{color:#CBD6DC}

/* ---- guide pages ------------------------------------------------------------
   Server-rendered prose, readable with JavaScript off, which is the entire
   reason these pages exist. They reuse .prose from the About page and add only
   what the guides need. */
.crumb{font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--faint);margin:0 0 18px}
.crumb a{color:var(--muted)}
.prose .lede{font-family:var(--serif);font-size:19px;line-height:1.6;color:var(--ink);
  margin:0 0 26px}
.gd-list{margin:18px 0 0;padding:0}
.gd-list dt{font-family:var(--mono);font-size:12.5px;letter-spacing:.05em;font-weight:600;
  color:var(--teal-ink);margin-top:16px}
.gd-list dd{margin:5px 0 0;padding:0 0 0 0;color:var(--ink);line-height:1.6}
.gd-faq{margin:14px 0 0}
.gd-faq dt{font-family:var(--serif);font-size:17px;font-weight:600;margin-top:20px;color:var(--ink)}
.gd-faq dd{margin:6px 0 0;color:var(--muted);line-height:1.65}
.gd-cta{margin:30px 0 0;padding:18px 20px;border:1px solid var(--line);border-radius:10px;
  background:var(--card)}
.gd-cta p{margin:0}
.gd-cta p + p{margin-top:10px}
.gd-cta-link{font-weight:600}
.gd-rel{margin:12px 0 0;padding-left:20px}
.gd-rel li{margin:6px 0}
.gd-index{list-style:none;margin:22px 0 0;padding:0}
.gd-index li{border-top:1px solid var(--line);padding:16px 0}
.gd-index li:last-child{border-bottom:1px solid var(--line)}
.gd-index a{font-family:var(--serif);font-size:19px;font-weight:600}
.gd-index span{display:block;color:var(--muted);font-size:14.5px;line-height:1.55;margin-top:4px}
.gd-src{margin-top:28px;padding-top:14px;border-top:1px solid var(--line);
  font-size:13px;color:var(--faint);line-height:1.6}

/* ---- the Water landing page --------------------------------------------------
   Added 2026-08-25. Coastal, Rivers, Boating and Surfing answer four different
   questions, and the nav could not carry four more items: ten already wrapped to
   three rows on a phone, and Boating plus Surfing would have made it four.
   Consolidating them behind one Water item holds the nav at three rows while the
   site gains two pages.

   Deliberately a real page and not a dropdown: this site has no menu pattern
   anywhere, a hover menu cannot be linked to or shared, and on a phone it is the
   worst interaction available. The hub pattern is already proven by guides.

   The two groups are the whole point. Coastal and Rivers answer "is the water
   coming to me". Boating and Surfing answer "should I go out on it". Passive and
   active, different urgency, different consequence when wrong. */
.wx-h1{font-family:var(--serif);font-weight:600;font-size:clamp(28px,4vw,40px);
  letter-spacing:-.015em;color:var(--ink);margin:26px 0 10px}
.wx-lede{font-size:1.02rem;line-height:1.65;color:var(--body);margin:0 0 8px;max-width:62ch}
.wx-note{font-size:13px;line-height:1.65;color:var(--muted);margin:30px 0 10px;max-width:70ch}
.wtr-group{margin-top:34px}
/* A-6: the fold an inland reader sees. The line is a real <summary>, styled as
   a row the reader can tap rather than a heading, with the three page names on
   the right so it says what it holds. It sits on --card-solid because the live
   sky can move behind anything left transparent. */
.wtr-fold{margin-top:22px;border:1px solid var(--line);border-radius:12px;background:var(--card-solid)}
.wtr-fold-line{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;
  gap:4px 16px;padding:14px 16px;cursor:pointer;list-style:none;min-height:44px}
.wtr-fold-line::-webkit-details-marker{display:none}
.wtr-fold-t{font-family:var(--serif);font-size:17px;font-weight:600;color:var(--ink)}
.wtr-fold-t::before{content:"\25B8";display:inline-block;margin-right:8px;color:var(--teal-ink);
  transition:transform .15s}
.wtr-fold[open] .wtr-fold-t::before{transform:rotate(90deg)}
@media (prefers-reduced-motion:reduce){.wtr-fold-t::before{transition:none}}
.wtr-fold-k{font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint)}
.wtr-fold-line:focus-visible{outline:3px solid var(--rule);outline-offset:2px;border-radius:12px}
.wtr-fold > .wtr-grid{margin:0;padding:0 16px 16px}
/* Rivers alone leads, so it takes the full width rather than half of a
   two-column row; and three cards in the fold are three columns where there is
   room, or two with the odd one spanning, never two and a stranded one. */
.wtr-inland .wtr-group .wtr-grid{grid-template-columns:1fr}
@media (min-width:760px) and (max-width:999px){
  .wtr-fold > .wtr-grid > :last-child:nth-child(odd){grid-column:1/-1}
}
@media (min-width:1000px){.wtr-fold > .wtr-grid{grid-template-columns:repeat(3,minmax(0,1fr))}}
.wtr-group h2{font-family:var(--serif);font-weight:600;font-size:20px;color:var(--ink);
  margin:0 0 4px;letter-spacing:-.01em}
.wtr-group h2::after{content:"";display:block;width:44px;height:2px;background:var(--rule);
  margin-top:8px}
.wtr-grid{display:grid;grid-template-columns:1fr;gap:14px;margin-top:16px}
@media(min-width:760px){.wtr-grid{grid-template-columns:1fr 1fr}}
.wtr-card{display:flex;flex-direction:column;background:var(--card);border:1px solid var(--line);
  border-radius:12px;padding:18px 20px 16px;text-decoration:none;
  transition:border-color .15s,transform .15s}
.wtr-card:hover{text-decoration:none;border-color:var(--rule);transform:translateY(-2px)}
.wtr-k{font-family:var(--mono);font-size:10.5px;letter-spacing:.15em;text-transform:uppercase;
  color:var(--teal-ink);font-weight:600}
.wtr-t{font-family:var(--serif);font-size:19px;font-weight:600;color:var(--ink);margin-top:6px;
  line-height:1.25}
.wtr-card p{font-size:14px;line-height:1.6;color:var(--muted);margin:8px 0 0}
.wtr-live{font-family:var(--mono);font-size:13px;color:var(--ink);margin-top:12px;min-height:0}
.wtr-live:empty{display:none}
.wtr-go{font-family:var(--mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--teal-ink);font-weight:600;margin-top:14px}

/* ---- Boating: the marine forecast card ---------------------------------------
   Added 2026-08-25. One question: can I go out, and when does it get ugly.

   The card is deliberately FORECAST ONLY. NDBC realtime2 sends no CORS header,
   so a browser cannot read observed wave height, period or water temperature,
   and rather than add this site's first data-fetching function the card says so
   and links the buoy's own page. That is why there is no observation column. */
.bt-card{padding:0;overflow:hidden}
.bt-band{display:flex;align-items:center;gap:10px;padding:12px 18px;
  font-family:var(--mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase;
  border-bottom:1px solid var(--line)}
.bt-band-t{font-weight:600}
.bt-band-z{margin-left:auto;text-transform:none;letter-spacing:.02em;color:var(--muted);
  font-size:11.5px;text-align:right}
.bt-dot{width:9px;height:9px;border-radius:50%;flex:none}
/* Severity from the official event name only, never from our reading of the
   numbers. Unknown is grey: it is not the same claim as clear. */
.bt-band[data-tone=clear]{background:rgba(46,125,90,.14);color:#1F6B45}
.bt-band[data-tone=clear] .bt-dot{background:#1F6B45}
.bt-band[data-tone=advisory]{background:rgba(240,146,72,.16);color:#8A5200}
.bt-band[data-tone=advisory] .bt-dot{background:#C87A22}
.bt-band[data-tone=gale]{background:rgba(220,20,60,.15);color:#9A2231}
.bt-band[data-tone=gale] .bt-dot{background:#DC143C}
.bt-band[data-tone=unknown]{background:rgba(120,130,140,.14);color:var(--muted)}
.bt-band[data-tone=unknown] .bt-dot{background:var(--faint)}
:root[data-theme=dark] .bt-band[data-tone=clear],
:root.sky-active .bt-band[data-tone=clear]{color:#7FD3A6}
:root[data-theme=dark] .bt-band[data-tone=advisory],
:root.sky-active .bt-band[data-tone=advisory]{color:#F0B45A}
:root[data-theme=dark] .bt-band[data-tone=gale],
:root.sky-active .bt-band[data-tone=gale]{color:#F08090}

.bt-inner{padding:20px 18px 18px}
.bt-lead{display:grid;grid-template-columns:1fr;gap:16px}
@media(min-width:620px){.bt-lead{grid-template-columns:auto auto 1fr;gap:34px}}
.bt-k{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.15em;
  text-transform:uppercase;color:var(--muted)}
.bt-v{display:block;font-family:var(--serif);font-size:clamp(30px,5vw,44px);line-height:1.04;
  color:var(--ink);margin-top:6px;letter-spacing:-.02em}
.bt-seas .bt-v{font-weight:500}
.bt-when{font-size:clamp(15px,2vw,19px);font-family:var(--mono);letter-spacing:0;color:var(--muted)}
.bt-sub{display:block;font-family:var(--mono);font-size:12px;color:var(--muted);margin-top:4px}
/* an absence must not read as a reading */
.bt-none{font-family:var(--mono);font-size:15px;font-weight:400;color:var(--muted);letter-spacing:0}

.bt-breakdown{font-family:var(--mono);font-size:14px;margin:18px 0 0;letter-spacing:.01em}
/* The wave-detail TEXT, which is held to 4.5:1, not to the 3:1 the waveform
   strokes are held to. The light-theme chop was #2A7FA8 and measured 4.47:1
   on a white card: a near miss rather than a visible problem, but it is the
   -ink token standard, so the text darkens and the stroke colour in
   waveform() keeps the decorative #8EC9E8 untouched. #27789E measures 4.9:1.
   The swell at #3A62A8 already measured 6.0:1 and is unchanged. */
.bt-chop{color:#27789E}
.bt-swell{color:#3A62A8}
:root[data-theme=dark] .bt-chop,:root.sky-active .bt-chop{color:#8EC9E8}
:root[data-theme=dark] .bt-swell,:root.sky-active .bt-swell{color:#7FA0E0}
.bt-tell{font-size:15.5px;line-height:1.6;color:var(--ink);margin:10px 0 0;max-width:62ch}

/* The waveform is data, not decoration: two trains at their true relative
   wavelength, L = 1.56 T squared, with their sum filled underneath. */
.bt-wave-wrap{margin-top:18px}
.bt-wave{width:100%;height:118px;display:block}
.bt-wave-sum{fill:rgba(120,170,210,.16)}
.bt-wave-base{stroke:var(--line);stroke-width:1;stroke-dasharray:3 5}
.bt-wave-legend{display:flex;flex-wrap:wrap;gap:16px;margin:8px 0 0;
  font-family:var(--mono);font-size:11.5px;color:var(--muted)}
.bt-wave-legend span{display:inline-flex;align-items:center;gap:7px}
.bt-wave-legend i{width:14px;height:3px;border-radius:2px;flex:none}

.bt-next{padding:13px 18px;border-top:1px solid var(--line);margin:0;
  font-family:var(--mono);font-size:13px;color:var(--muted)}
.bt-next-k{font-size:10px;letter-spacing:.15em;text-transform:uppercase;margin-right:10px;
  color:var(--faint)}
/* The zone chooser. The first native select on the site, so it is styled from
   the same tokens as .btn-quiet rather than inventing a control language:
   appearance:none removes the platform chrome, and the caret is a background
   SVG so it inherits the theme instead of staying black on dark. min-height
   44px is the tap target; the max-width keeps a long zone name from running
   the full 1040 column and turning into a line nobody can aim at. */
.bt-pick{display:flex;flex-direction:column;gap:6px;margin:0 0 16px}
.bt-pick-k{font-family:var(--mono);font-size:11px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted)}
.bt-pick-sel{font-family:var(--sans);font-size:15px;color:var(--ink);
  background-color:var(--card);border:1px solid var(--line);border-radius:7px;
  padding:10px 38px 10px 12px;min-height:44px;max-width:520px;width:100%;
  cursor:pointer;appearance:none;-webkit-appearance:none;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23585E62' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 13px center;background-size:12px 8px}
.bt-pick-sel:hover{border-color:var(--teal-ink)}
.bt-pick-sel:focus-visible{outline:2px solid var(--teal-ink);outline-offset:2px}
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]) .bt-pick-sel{
    background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23ADB2B3' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E")}
}
:root[data-theme=dark] .bt-pick-sel{
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23ADB2B3' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E")}

/* Body text that sits DIRECTLY on the live sky, in sky-active mode only.
   The sky is a gradient that changes through the day: measured at 2 PM off
   Charleston it ran #1B4E7A at the top to #8FCBDF at the bottom, and --muted
   (#CBD6DC) against that bottom stop is 1.20:1. That is not a near miss, it
   is invisible, and no static token can fix it because the background is not
   static. Cards are unaffected, they already carry var(--card).

   The scrim is the treatment .sec-head, .al-sec and .al-foot already use for
   the same reason, so this introduces no new visual language. Over the worst
   case sky it composites to about #293B45, which puts --muted at 7.8:1.
   Normal light and dark themes paint a real --paper behind the text and are
   left alone. */
:root.sky-active .bt-zonenote,
:root.sky-active .bt-sib,
:root.sky-active .wx-note{
  background:rgba(12,18,26,.78);border-radius:9px;padding:9px 14px;
  backdrop-filter:blur(3px)}

/* The zone chooser's LABEL is the same case and was missed on the first pass:
   the select carries var(--card) and reads fine, but "Water you are heading
   for" above it is bare --muted on the sky and measured 1.47:1. The scrim
   goes on the whole .bt-pick block rather than the label alone, so the label
   and the control it names read as one panel instead of a floating chip. */
:root.sky-active .bt-pick{
  background:rgba(12,18,26,.78);border-radius:9px;padding:10px 14px 12px;
  backdrop-filter:blur(3px)}

/* The surf card carries FOUR lead cells where the boating card carries three,
   and the Surf Zone Forecast writes its wind out in words ("South winds around
   10 mph") where the coastal waters forecast abbreviates ("S winds 10 kt").
   Water temperature is a phrase too ("In the mid 80s"). At the shared .bt-v
   display size those sentences outweighed the surf height, which is the one
   number this page exists to show, and pushed the fourth cell onto its own
   row.

   So surf keeps the display size and the supporting cells step down to sans.
   Two by two, NOT four across: this page reads inside .wrap.narrow, so the
   column is about 620px however wide the window is, and four cells of this
   text squeezed the last one down to roughly 24px and broke "Rest of today"
   across three lines. Widening the breakpoint would not have helped, because
   the constraint is the measure and not the viewport. */
@media(min-width:620px){.bt-lead.bt-lead-surf{grid-template-columns:auto 1fr;gap:20px 30px}}
.bt-lead-surf .bt-wind,.bt-lead-surf .bt-water{
  font-family:var(--sans);font-weight:600;font-size:clamp(17px,2.1vw,22px);
  line-height:1.3;letter-spacing:0}

/* Names the product the swell block came from. The Surf Zone Forecast has no
   wave period, so the swell is read from the coastal waters forecast, which
   describes the water off the beach rather than the wave breaking on it.
   Saying so is not optional: without it the card would be claiming a
   measurement of the break that nobody issues. */
.bt-swellsrc{font-size:12.5px;line-height:1.6;color:var(--muted);margin:14px 0 2px}

/* The surf card's tide line resolves the reader's own station after the card
   paints, so the slot holds a placeholder first. The zone's issued line, when
   it names somewhere else, sits under it at reduced weight: it is context for
   the number above, not a competing reading. */
.bt-tide-issued{display:block;font-family:var(--sans);font-size:12px;
  color:var(--muted);margin-top:5px}

/* Links sitting INSIDE a block of text carry an underline, because the base
   `a` rule on this site is text-decoration:none and colour alone is not a
   distinction (WCAG 1.4.1). This is the same treatment .sv-route, .hist-list
   and .storm-handoff already use. Caught late: the buoy links render after a
   lookup resolves, so an axe run that finished before they landed reported
   the page clean. */
.bt-obs-links a,.bt-sib a{text-decoration:underline}

/* Hawaii's surf table, by the direction a shore faces. Printed in full under
   the chooser because a surfer deciding where to drive compares shores rather
   than clicking through them one at a time, and on Oahu the spread is real:
   north facing 0-2 ft against south facing 4-6 ft on the same morning. The
   selected row is marked with weight and a rule, not colour alone. */
.sf-shore{margin-top:18px}
.sf-shore-pick{margin-bottom:10px}
.sf-shore-table{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:12.5px}
.sf-shore-table caption{text-align:left;font-family:var(--sans);font-size:12px;
  color:var(--muted);padding-bottom:7px}
.sf-shore-table th,.sf-shore-table td{padding:6px 10px;border-bottom:1px solid var(--line);
  text-align:right;white-space:nowrap}
.sf-shore-table th[scope=row]{text-align:left;font-weight:500;color:var(--ink)}
.sf-shore-table thead th{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);font-weight:500}
.sf-shore-table tbody tr.on th[scope=row],.sf-shore-table tbody tr.on td{font-weight:700}
.sf-shore-table tbody tr.on{box-shadow:inset 2px 0 0 var(--teal-ink)}

.bt-zonenote{font-size:13px;line-height:1.6;color:var(--muted);margin:12px 0 0}
.bt-obs{margin-top:16px}
.bt-obs p{font-size:14px;line-height:1.65;color:var(--muted);margin:8px 0 0}
.bt-obs-links{font-family:var(--mono);font-size:13px}
.bt-obs-links:empty{display:none}
.bt-sib{font-size:13.5px;color:var(--muted);margin-top:18px}

/* ============================================================================
   CHROME MODES. Two root classes, set before first paint by the head script:
   in-app (the iOS app, which draws a native tab bar) and tabbar (standalone
   display, the Android app, and phone-width browsers, where wx-tabbar.js draws
   the site's own bar). In both, the header strip, nav rows, theme button and
   footer are chrome twice over and cost a quarter of a phone screen, so these
   rules hide ONLY that chrome. The Living Sky, ambient tinting, radar and
   satellite loops, the cyclone marker and every card stay exactly as they are.
   The disclaimer, operator line and family link move to the More sheet (native
   on iOS, the site's own otherwise) and stay in the footer on About and Privacy.
   ========================================================================== */
.in-app .top-rule, .in-app .mh-top, .in-app .mh-nav, .in-app .mh-slogan, .tabbar .top-rule, .tabbar .mh-top, .tabbar .mh-nav, .tabbar .mh-slogan{display:none}
.in-app footer.site, .tabbar footer.site{display:none}
.in-app body.page-about footer.site, .in-app body.page-privacy footer.site, .tabbar body.page-about footer.site, .tabbar body.page-privacy footer.site{display:block}
.in-app .masthead .wrap, .tabbar .masthead .wrap{padding-top:calc(6px + var(--sat));padding-bottom:6px}
.in-app .mh-brand, .tabbar .mh-brand{margin-top:0;gap:8px}
.in-app .mh-mark, .tabbar .mh-mark{width:26px;height:26px}
.in-app .mh-word, .tabbar .mh-word{font-size:20px}
.in-app main, .tabbar main{padding-bottom:12px}
/* In-app alert fold. Closed: head + in-effect line only. The chevron is the
   affordance; the whole head is the target (44px+ tall already). */
.in-app .alert-fold .alert-head, .tabbar .alert-fold .alert-head{cursor:pointer;padding-right:28px;position:relative}
.in-app .alert-fold .alert-head::after, .tabbar .alert-fold .alert-head::after{content:"";position:absolute;right:6px;top:50%;width:9px;height:9px;margin-top:-7px;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg);opacity:.7;transition:transform .18s ease}
.in-app .alert-fold.alert-open .alert-head::after, .tabbar .alert-fold.alert-open .alert-head::after{transform:rotate(-135deg);margin-top:-2px}
.in-app .alert-fold:not(.alert-open)>*:not(.alert-head):not(.alert-meta), .tabbar .alert-fold:not(.alert-open)>*:not(.alert-head):not(.alert-meta){display:none}
.in-app .alert-fold:not(.alert-open) .alert-meta, .tabbar .alert-fold:not(.alert-open) .alert-meta{margin-bottom:0}
.in-app .alert-fold:not(.alert-open) .alert-areas, .tabbar .alert-fold:not(.alert-open) .alert-areas{display:none}
.in-app .alert-fold .alert-head:focus-visible, .tabbar .alert-fold .alert-head:focus-visible{outline:2px solid var(--accent,#27789E);outline-offset:2px;border-radius:4px}
/* Radar beacon: the reader's own position over the reflectivity. The pulse is
   the "you" cue and nothing else on the map pulses. GPS fixes get an accuracy
   ring; a ZIP or address centroid gets a plain pin, because a centroid can sit
   miles from the reader and the map must not pretend otherwise. */
.wx-beacon{width:0;height:0;position:relative}
.wx-beacon .bk-dot{position:absolute;left:-7px;top:-7px;width:14px;height:14px;border-radius:50%;background:#2B9FD9;border:2.5px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,.35),0 1px 4px rgba(0,0,0,.5)}
.wx-beacon .bk-pulse{position:absolute;left:-7px;top:-7px;width:14px;height:14px;border-radius:50%;background:#2B9FD9;opacity:.55;animation:bk-pulse 2.4s ease-out infinite}
.wx-beacon.bk-approx .bk-dot{background:#E8B04B;border-radius:2px;transform:rotate(45deg)}
.wx-beacon.bk-approx .bk-pulse{display:none}
@keyframes bk-pulse{0%{transform:scale(1);opacity:.55}100%{transform:scale(3.2);opacity:0}}
@media (prefers-reduced-motion:reduce){.wx-beacon .bk-pulse{animation:none;transform:scale(1.8);opacity:.25}}
.wx-beacon-ring{stroke:#2B9FD9;fill:#2B9FD9}
/* In-app fold for the rest-of-state alert list. */
.al-sec-btn{all:unset;display:block;width:100%;box-sizing:border-box;cursor:pointer;position:relative;padding-right:28px;font:inherit;color:inherit;min-height:44px}
.al-sec-btn::after{content:"";position:absolute;right:8px;top:50%;width:9px;height:9px;margin-top:-7px;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg);opacity:.7;transition:transform .18s ease}
.al-fold.al-open .al-sec-btn::after{transform:rotate(-135deg);margin-top:-2px}
.in-app .al-fold:not(.al-open)>*:not(.al-sec), .tabbar .al-fold:not(.al-open)>*:not(.al-sec){display:none}
.al-sec-btn:focus-visible{outline:2px solid var(--accent,#27789E);outline-offset:2px;border-radius:4px}
:root.sky-active .al-sec-btn:focus-visible,:root.sky-active .alert-fold .alert-head:focus-visible{outline-color:#9BD3DC}

/* ============================================================================
   THE SITE'S OWN TAB BAR (wx-tabbar.js). Shown in tabbar mode: standalone
   display (home screen, the Android app) and phone-width browsers. Opaque
   card ground on purpose: it sits over the Living Sky, and text over a moving
   gradient is the contrast trap this file has already been bitten by.
   ========================================================================== */
.wx-tabbar{position:fixed;left:0;right:0;bottom:0;z-index:60;display:flex;align-items:stretch;justify-content:space-around;background:var(--tb-ground,#FFFFFF);border-top:1px solid var(--line);padding:5px 2px calc(5px + env(safe-area-inset-bottom,0px))}
/* --tb-ground is OPAQUE on purpose. var(--card) is 88% alpha in sky mode, and
   page text showed through behind the labels at 320px: the contrast trap this
   file's notes describe. Light: paper white. Dark and sky: the card tone at
   full alpha. */
:root.sky-active,:root[data-theme=dark]{--tb-ground:#0F161E}
@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--tb-ground:#0F161E}}
.wx-tabbar[hidden]{display:none}
.tb-item{flex:0 1 auto;min-width:0;min-height:48px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;padding:4px 1px;border:0;border-radius:8px;background:none;color:var(--muted);font-family:var(--sans,inherit);font-size:12px;font-weight:600;letter-spacing:.01em;line-height:1.1;text-decoration:none;cursor:pointer;-webkit-tap-highlight-color:transparent}
.tb-item svg{width:24px;height:24px;display:block}
/* P-9: 12px, not 10.5px, which is under the platform minimum for a tab label.
   The tracking comes down a hair to pay for the width, and the items share the
   bar evenly (flex:1 1 0) instead of each asking for 110px, so six labels at
   the larger size still fit a 375px phone without one of them ellipsizing.
   Measured at 320, 375 and 430. */
.tb-item .tb-label{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tb-item[aria-current=page]{color:var(--teal-ink);font-weight:700;box-shadow:inset 0 2px 0 currentColor}
.tb-item:hover{color:var(--ink);text-decoration:none}
.tb-item:focus-visible{outline:3px solid var(--rule);outline-offset:-3px}
/* P-9 again, and this is the width it mattered most at. This block dropped
   the label to 9.5px under 360px, which is further below the platform minimum
   than the 10.5px the audit flagged, and the 320px phone is exactly where a
   small label is hardest to hit. Held at 12px; the icon gives up the two
   pixels instead, and the labels were measured at 320 with no ellipsis. */
/* P-9, and the measurement that decided the shape of this rule. Six equal
   columns in 320px give each label 53px, and "Hurricane" at 12px needs 57, so
   equal columns forced either an ellipsis or type below the platform minimum.
   The six labels together only come to 244px, so the items size to their
   CONTENT and share the leftover space around them: every label fits whole at
   12px, measured at 320, 375 and 430. */
@media (max-width:360px){.tb-item{font-size:12px;letter-spacing:-.01em;padding:4px 0;gap:2px}.tb-item svg{width:21px;height:21px}}
.tabbar body{padding-bottom:calc(84px + env(safe-area-inset-bottom,0px))}
.tabbar .getapp{bottom:calc(66px + env(safe-area-inset-bottom,0px))}
.tb-sheet{position:fixed;inset:0;z-index:70;background:rgba(6,12,18,.55);display:flex;align-items:flex-end;justify-content:center}
.tb-sheet[hidden]{display:none}
.tb-panel{width:100%;max-width:560px;max-height:82vh;overflow:auto;background:var(--tb-ground,#FFFFFF);color:var(--ink);border-radius:16px 16px 0 0;padding:12px 18px calc(18px + env(safe-area-inset-bottom,0px));box-shadow:0 -8px 30px rgba(0,0,0,.35)}
.tb-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}
.tb-head h2{font-family:var(--serif);font-size:20px;margin:0}
.tb-close{min-height:44px;padding:0 14px;border:1px solid var(--line);border-radius:999px;background:none;color:var(--ink);font:600 14px/1 var(--sans,inherit);cursor:pointer}
.tb-close:focus-visible,.tb-theme button:focus-visible,.tb-links a:focus-visible{outline:3px solid var(--rule);outline-offset:2px}
/* --rule stays Deep Teal in sky mode for light-system readers: 1.73:1 on the bar's
   ground, invisible. The sky teal-ink clears 11:1 there. Measured, not guessed. */
:root.sky-active .tb-item:focus-visible,:root.sky-active .tb-close:focus-visible,:root.sky-active .tb-theme button:focus-visible,:root.sky-active .tb-links a:focus-visible{outline-color:#9BD3DC}
.tb-panel:focus{outline:none}
.tb-links a{display:block;padding:13px 0;border-bottom:1px solid var(--line);color:var(--ink);font-size:16px;text-decoration:none}
.tb-links a:last-child{border-bottom:0}
.tb-links a[aria-current=page]{color:var(--teal-ink);font-weight:600}
.tb-k{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);margin:16px 0 6px}
.tb-theme{display:flex;gap:8px}
.tb-theme button{flex:1;min-height:44px;border:1px solid var(--line);border-radius:8px;background:none;color:var(--ink);font:500 14px/1 var(--sans,inherit);cursor:pointer}
.tb-theme button[aria-pressed=true]{background:var(--chip-bg);font-weight:700}
.tb-note{font-size:12.5px;line-height:1.5;color:var(--muted);margin:16px 0 0}
@media (prefers-reduced-motion:no-preference){.tb-panel{animation:tb-up .18s ease-out}}
@keyframes tb-up{from{transform:translateY(14px);opacity:0}to{transform:none;opacity:1}}

/* ---- A-1: the place is the title, the list lives behind it ---------------
   Phones only; the module does not run on a pointer screen, and these rules
   are scoped to what it builds so the desktop row is untouched.

   currentColor throughout, because the control sits on two very different
   grounds: the hero's sky gradient (#DDE4E8 text) and a section head on the
   page (--ink). One rule reading the colour it inherits is right on both;
   two hard-coded colours would be wrong on one of them. */
.pt-row{display:inline-flex;align-items:center;gap:10px;margin-left:auto;flex:none}
.sec-head .pt-row{align-self:center}
/* On a heading that is a block rather than a flex row, the Water hub's visible
   h1, the control is an inline box and inline boxes ignore auto margins, so it
   sat flush against the word: "WaterCHANGE". Measured on the A-6 deploy. */
h1.pt-head > .pt-row{margin-left:12px;vertical-align:middle}
.pt-dots{display:inline-flex;align-items:center;gap:2px}
/* A 7px dot with a 24px hit area: WCAG 2.5.8's minimum without a 24px dot.
   The padding carries the target, the background carries the dot. */
.pt-dot{width:24px;height:24px;padding:0;border:0;background:none;cursor:pointer;
  position:relative;flex:none;border-radius:999px;color:inherit}
.pt-dot::after{content:"";position:absolute;inset:0;margin:auto;width:7px;height:7px;
  border-radius:999px;background:currentColor;opacity:.42}
.pt-dot.here::after{opacity:1;width:8px;height:8px}
.pt-dot:focus-visible{outline:2px solid currentColor;outline-offset:-2px}
/* Not 44px: this sits in a 16px title row, and a 44px control there would give
   back a third of the height A-1 exists to reclaim. 28px with a 24px text box
   clears WCAG 2.5.8 and keeps the row a title. */
.pt-name{min-height:28px;padding:0 2px;border:0;background:none;cursor:pointer;
  color:inherit;font-family:var(--mono);font-size:11px;letter-spacing:.07em;
  text-transform:uppercase;text-decoration:underline;text-underline-offset:3px;flex:none}
.pt-name:focus-visible{outline:2px solid currentColor;outline-offset:2px;text-decoration:none}
.pt-tap{cursor:pointer}

/* The panel. The tab bar's sheet already taught a reader here what a sheet
   from the bottom means, so this is the same object: same ground token, same
   radius, same scrim, same animation. */
.pt-panel{position:fixed;inset:0;z-index:72;background:rgba(6,12,18,.55);
  display:flex;align-items:flex-end;justify-content:center}
.pt-panel[hidden]{display:none}
.pt-panel-in{width:100%;max-width:560px;max-height:82vh;overflow:auto;
  background:var(--tb-ground,#FFFFFF);color:var(--ink);border-radius:16px 16px 0 0;
  padding:12px 18px calc(18px + env(safe-area-inset-bottom,0px));
  box-shadow:0 -8px 30px rgba(0,0,0,.35)}
.pt-panel-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}
.pt-panel-head h2{font-family:var(--serif);font-size:20px;margin:0}
.pt-done{min-height:44px;padding:0 16px;border:1px solid var(--line);border-radius:999px;
  background:none;color:var(--ink);font:600 14px/1 var(--sans,inherit);cursor:pointer}
.pt-done:focus-visible{outline:3px solid var(--rule);outline-offset:2px}
:root.sky-active .pt-done:focus-visible{outline-color:#9BD3DC}
/* The bar is MOVED into the panel, so it arrives wearing its card: a card
   inside a sheet is a doubled border. Flattened here, not restyled there,
   because on the desktop it is still a card. */
.pt-panel .loc-bar{background:none;border:0;border-radius:0;padding:0;margin:0}
.pt-panel .wx-places{margin-top:0;margin-bottom:14px}
/* The page behind a sheet does not scroll under the reader's thumb. */
:root.pt-open,:root.pt-open body{overflow:hidden}
@media (prefers-reduced-motion:no-preference){.pt-panel-in{animation:tb-up .18s ease-out}}
@media print{.wx-tabbar,.tb-sheet{display:none}.tabbar body{padding-bottom:0}}
@media (orientation:landscape) and (max-height:500px){.tabbar .map-frame, .tabbar .map-frame.tall, .tabbar .severe-map .map-frame{height:calc(100vh - 156px)}}


/* ---- close-zoom map detail (G-5) -----------------------------------------
   County lines sit UNDER the town names and under the radar imagery: they are
   there to orient, and a boundary drawn over a reflectivity cell would hide
   the thing the reader came for. */
.wx-county-line{pointer-events:none}
/* County names read as the quiet layer a paper map uses: letterspaced, caps,
   no dot, and lighter than a town name, so at zoom nine a reader can tell at a
   glance which of the two kinds of name they are looking at. */
.wx-county{pointer-events:none}
.wx-county-name{font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;
  text-transform:uppercase;white-space:nowrap;opacity:.72;color:var(--map-label);
  text-shadow:0 0 3px var(--map-label-halo),0 0 3px var(--map-label-halo),
              0 0 5px var(--map-label-halo)}

/* THE LIGHT BASEMAP OPTION. Radar reflectivity is a saturated palette designed
   for a pale background, and on the dark basemap the greens and blues at the
   low end sit close to the land color. This redeclares the map tokens ON THE
   CONTAINER, which is why WXMap reads them from the container and not from
   documentElement: the page stays dark and the map alone goes light. */
.map-frame.map-light{--map-sea:#E4EBEB;--map-land:#F8F7F1;--map-coast:#A9B8B8;
  --map-line:#CBD2CC;--map-county:#B9C6C4;
  --map-label:#242B30;--map-label-halo:rgba(255,255,255,.95)}

/* The basemap button lives in the loop's control bar now, beside Play loop,
   so it needs no row or label of its own. It stays last in that bar, after
   the frame stamp, because the frame you are looking at matters more than the
   colour of the land under it. */
.map-basemap-btn{flex:none;order:9}

/* ---- buoy observations (D-7) ---------------------------------------------
   A reading beside the forecast, with each field's own age. The age is set in
   the mono face and quieter than the value: it is a qualifier on the number,
   and a reader scanning for the wave height should find the number first. */
.bo-strip{display:grid;gap:14px;margin:14px 0 0}
.bo-buoy{border:1px solid var(--line);border-radius:8px;padding:12px 14px}
.bo-id{font-family:var(--mono);font-size:12px;letter-spacing:.04em;color:var(--muted);
  margin-bottom:8px}
.bo-row{display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline;padding:3px 0}
.bo-k{font-family:var(--mono);font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--faint);flex:0 0 118px}
.bo-v{font-family:var(--mono);font-size:16px;font-weight:600;color:var(--ink)}
.bo-age{font-family:var(--mono);font-size:11px;color:var(--faint)}
.bo-note{font-size:13px;color:var(--muted);margin:8px 0 0;line-height:1.5}
@media (max-width:520px){
  /* The label stops reserving a column and sits above its value, so a long
     label never squeezes the number onto a second line. */
  .bo-k{flex:0 0 100%}
}

/* ---- measured water level over the prediction (D-6) ----------------------
   Heavier and unfilled, over the predicted curve's filled area. The
   prediction is the shape of the day and reads as a region; the measurement
   is a fact and reads as a line. Colour is the site's link accent rather than
   a warning colour: a tide above its prediction is information, not an alert,
   and the alert colours on this site mean an NWS product. */
.tide-obs-line{fill:none;stroke:var(--link);stroke-width:2;stroke-linejoin:round;
  stroke-linecap:round;opacity:.95}
.tide-obs-note{color:var(--ink)}
.tide-swap{margin:6px 0 0}

/* ---- section navigation ---------------------------------------------------
   The row of links between the pages of one section, and back to its hub.
   See assets/wx-sectionnav.js for why it exists: the app hides the masthead
   nav, so a reader on the Surfing page had no visible way back and no way
   across to Rivers.

   IT SCROLLS RATHER THAN WRAPS. Five labels do not fit across 375px, and a
   second row of chips pushes the page's own content below the fold on exactly
   the device this is for. Horizontal scroll keeps it one row at any width and
   is the same idiom the masthead nav already uses. */
.secnav{margin:0 0 14px}
.secnav-list{display:flex;gap:6px;align-items:center;list-style:none;margin:0;padding:2px 0;
  overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.secnav-list::-webkit-scrollbar{display:none}
.secnav-list li{flex:none}
.secnav-item{display:inline-block;font-family:var(--mono);font-size:12px;letter-spacing:.06em;
  text-transform:uppercase;padding:7px 12px;border:1px solid var(--line);border-radius:999px;
  color:var(--muted);text-decoration:none;white-space:nowrap;background:var(--card)}
a.secnav-item:hover{color:var(--ink);border-color:var(--muted)}
/* Focus must be visible on a scrolling row: without the offset the ring sits
   under the neighbouring chip. */
a.secnav-item:focus-visible{outline:2px solid var(--link);outline-offset:2px}
/* The page you are on. A span, not a link, so it reads as a position rather
   than a destination. */
.secnav-item.here{color:var(--ink);border-color:var(--ink);font-weight:600;cursor:default}
/* The hub carries a leading arrow because it is also the way back, which is
   the job the app had no control for at all.

   AND IT IS PINNED. The row scrolls, and once the current chip is brought
   into view the FIRST chip is the one that leaves: measured on the deploy at
   375px, a reader on Surfing could see Coastal, Rivers, Boating and Surfing
   and the way back had scrolled off the left. The way back is the one control
   that must never be unreachable, so it sticks to the left edge and the rest
   pass behind it.

   Five uppercase chips need about 364px and there are 343 to give at 375, so
   fitting them all was measured and rejected; shrinking the type enough to
   fit would have cost legibility on the device this is for. */
.secnav-list li:first-child{position:sticky;left:0;z-index:2}
.secnav-item.hub{background:var(--card-solid);box-shadow:8px 0 8px -5px var(--card-solid)}
.secnav-item.hub::before{content:"\2190";margin-right:6px;opacity:.75}
.secnav-item.hub.here::before{content:none}
/* On the hub page itself there is nowhere to go back to, so it does not need
   to hold the edge and can scroll with everything else. */
.secnav-list li:first-child:has(.here){position:static}

/* In the app there is no browser chrome above this, so it is the first thing
   under the wordmark and wants a little more room from it. */
.in-app .secnav, .tabbar .secnav{margin-top:4px}

/* ---- the week as one curve (L-11) -----------------------------------------
   The hour by hour strip drew all 156 published hours, about 10,000px of
   chart, twenty seven screens on a phone. The strip now keeps 48 hours and
   the whole window is this curve, which fits the card at any width.

   preserveAspectRatio="none" on the SVG is what makes it fit: the viewBox is
   1000 wide and the element is 100 percent, so the curve stretches
   horizontally and keeps its height. Text inside would stretch with it, which
   is why every label sets its own font-size in CSS rather than inheriting the
   scaled one, and why this chart carries no icons. */
.wk-curve{margin:10px 0 0}
.wk-curve-svg{display:block;max-width:100%;overflow:visible}
/* W-1. The night bands are a faint tint of the card's own text colour, so
   they sit on the card in every scheme. They were --map-sea, which follows the
   phone's scheme; in light with Auto the sky turns the card dark and the text
   light, but --map-sea stayed light (228,235,235), and every label on a night
   band read light on light at about 1.2:1. */
.wk-night rect{fill:var(--wk-night-fill,#000);opacity:var(--wk-night-op,.28)}
/* the band moves AWAY from the labels' colour: on a light card the labels are
   dark, so a faint dark tint; on a dark card (the sky theme, whatever the
   phone's scheme, or the Dark theme) the labels are light, so a darker band */
:root{--wk-night-fill:#1B2A33;--wk-night-op:.035}
:root[data-theme=dark],:root.sky-active{--wk-night-fill:#000;--wk-night-op:.28}
@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--wk-night-fill:#000;--wk-night-op:.28}}
.wk-day line{stroke:var(--line);stroke-width:1;opacity:.8}
/* F-7: real pixels now; day names 11px caps with tracking, figures 12px */
.wk-dayname{fill:var(--muted);font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.12em;
  text-anchor:middle;text-transform:uppercase}
.wk-line{fill:none;stroke:var(--copper);stroke-width:2.5;stroke-linejoin:round;
  stroke-linecap:round;vector-effect:non-scaling-stroke}
.wk-marks circle.hi{fill:var(--copper)}
.wk-marks circle.lo{fill:var(--teal)}
.wk-mark{font-family:var(--mono);font-size:12px;font-weight:600}
.wk-nowlab{font-family:var(--mono);font-size:12px;fill:var(--copper-ink)}
.wk-mark.hi{fill:var(--copper-ink)}
.wk-mark.lo{fill:var(--teal-ink)}
/* vector-effect keeps the now line one pixel wide despite the horizontal
   stretch; without it the same 1px becomes a visible bar. */
.wk-now{stroke:var(--copper);stroke-width:1;stroke-dasharray:3 3;opacity:.9;
  vector-effect:non-scaling-stroke}
.wk-curve-note{font-size:13px;color:var(--muted);margin:6px 0 0;line-height:1.5}

/* ============================================================================
   B-3: THE HERO, BUILT TO THE BOARD (docs/weather-hero-board.png).
   Scoped to .wx-hero-board, the located hero, so the identity hero and the
   unavailable hero keep their own layout. Positions are measured against the
   board at 375px by scratchpad/b3-positions.mjs, within 8px.

   Top row: the place in serif with A-1's dots, the day at the right. The
   number, 108px Newsreader, the degree a superscript. Beside it the
   condition, feels-like, and today's ends in one mono line. The one-liner
   under the number. Open sky. The eight tiles, 4x2, at the bottom.
   ========================================================================== */
/* on a phone the hero bleeds to within 1.5px of the screen's edges, as the
   board's phone cards do: the page wrap's 22px padding made it 331px wide */
.wx-hero.wx-hero-board{min-height:560px;margin:10px 0 14px;border:1px solid rgba(255,255,255,.10);
  display:flex;flex-direction:column;justify-content:stretch}
.wx-hero-board .wx-hero-inner{flex:1;display:flex;flex-direction:column;padding:10px 15px 16px}
/* the scrim is a band behind the words, not a darkening of the whole card:
   the middle of the sky has to show, and the tiles carry their own ground.
   Deepened where the number sits after the branch deploy measured it at
   2.72:1 in coral (100F) and in blue (25F) over a clear day and a rain day;
   the pale skies get a heavier one of their own, above. */
.wx-hero-board .wx-scrim{background:linear-gradient(180deg,rgba(6,10,16,.58) 0%,rgba(6,10,16,.48) 30%,
  rgba(6,10,16,.36) 40%,rgba(6,10,16,.16) 50%,rgba(6,10,16,0) 60%,rgba(6,10,16,0) 76%,rgba(6,10,16,.22) 100%)}
.wx-hero-board .wx-titlebar{display:flex;align-items:center;gap:10px;min-height:30px;padding:0 6px}
.wx-hero-board .wx-place{flex:1;min-width:0;margin:0;font-family:var(--serif);font-size:20px;font-weight:600;
  letter-spacing:0;text-transform:none;color:#FFFFFF;align-items:center}
.wx-hero-board .wx-place .wx-quiet-note{font-family:var(--mono);font-size:11px}
.wx-hero-board .wx-dow{font-family:var(--mono);font-size:12px;font-weight:600;letter-spacing:.16em;color:#E4E9ED}
.wx-hero-board .wx-hero-grid{display:flex;align-items:flex-start;gap:14px;margin-top:6px;padding:0 6px}
/* the number sits 7px lower than the condition beside it, as on the board */
.wx-hero-board .wx-lock{display:block;flex:none;padding-top:7px}
/* under a cloud deck the reading hangs below the band (the board's Seattle) */
.wx-hero-board[data-sky-deck="1"] .wx-hero-grid{margin-top:39px}
.wx-hero-board .wx-temp{font-family:var(--serif);font-weight:400;font-size:108px;line-height:.86;
  letter-spacing:-.03em;color:var(--t-ink-dark,#F4F1EA);white-space:nowrap}
.wx-hero-board .wx-deg{font-size:.3em;font-weight:400;vertical-align:top;position:relative;top:.16em;
  margin-left:.05em;letter-spacing:0}
.wx-hero-board .wx-side{flex:1;min-width:0;padding-top:12px}
.wx-hero-board .wx-cond{font-family:var(--serif);font-size:30px;line-height:1.1;color:#FFFFFF}
.wx-hero-board .wx-feels{font-family:var(--mono);font-size:13px;color:#E4E9ED;margin-top:12px}
/* one line, never two: its longest form, "L 71° · sunrise 7:11", is 20
   characters, about 156px at 13px mono, in a 190px column at 375px */
/* W-9 finding A: THE LINE WRAPS, THE SEGMENTS DO NOT. With the next sunrise
   on it after dark, "H 81° · L 70° · sunrise 6:53" measures 389px against a
   375px phone and a hero grid 365px wide, and nowrap cut it at "sunrise 6:".
   The page hides horizontal overflow and the element's own box ended at 359,
   so only the TEXT ran past it, which is why the sweep did not see it either
   (its check now measures text ranges). Each segment keeps nowrap so no
   reading breaks across lines, and the separator rides at the END of the
   segment before it, so a wrapped line begins with "sunrise", never "·". */
.wx-hero-board .wx-hilo{display:block;margin-top:7px;font-family:var(--mono);font-size:13px;color:#E4E9ED;white-space:normal}
.wx-hero-board .wx-hilo-row{display:inline;font-size:13px;white-space:nowrap}
.wx-hero-board .wx-hilo-row em{font-style:normal;color:#E4E9ED;font-size:13px;letter-spacing:0;text-transform:none}
.wx-hero-board .wx-hilo-row b{font-weight:700;color:#FFFFFF}
.wx-hero-board .wx-hilo-sep{color:#B9C3CB}
.wx-hero-board .sky-note-slot{margin:9px 0 0;padding:0 6px}
.wx-hero-board .sky-note{font-family:var(--serif);font-style:italic;font-size:18px;line-height:1.45;
  color:#F4F1EA;margin:0;max-width:34ch}
.wx-hero-board .wx-skyspace{flex:1;min-height:60px}
/* the tiles: eight, 4x2 at every width, each on its own dark ground */
.wx-hero-board .wx-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px 7px;
  margin:0;background:none;border:0;border-radius:0;overflow:visible}
.wx-hero-board .wx-stats > *{background:rgba(9,14,21,.70);border:1px solid rgba(255,255,255,.09);
  border-radius:10px;padding:7px 9px 7px;min-height:50px;min-width:0;border-left:1px solid rgba(255,255,255,.09);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
.wx-hero-board .wx-stats .nk{font-family:var(--mono);font-size:8.5px;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;color:#C3CCD3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wx-hero-board .wx-stats .nv{font-family:var(--mono);font-size:16px;font-weight:500;color:#FFFFFF;
  margin:4px 0 0;line-height:1.15;white-space:nowrap;display:flex;align-items:center;gap:5px}
.wx-hero-board .wx-stats .nv.nv-none{color:rgba(255,255,255,.62)}
.wx-hero-board .wx-stats .nv .nv-sub{display:block;position:absolute;font-size:10px;margin:0}
.wx-hero-board .wx-stats .nv{position:relative}
/* The small text sits UNDER the value on the tiles that carry one. The
   generic rule above parks it absolutely, which is right for the note that
   overlays an empty tile offline and wrong for a tile with a number in it:
   read on the preview, "chance today" printed on top of "20%" (Jack's strike
   20, 2026-09-23). Wind had the override already; rain and the moon need it
   for the same reason. */
.wx-hero-board .wx-stats .wx-stat-wind .nv .nv-sub,
.wx-hero-board .wx-stats .wx-rain-cell .nv .nv-sub,
.wx-hero-board .wx-stats .wx-stat-moon .nv .nv-sub{position:static;font-family:var(--mono);font-size:10px;color:#C3CCD3}
.wx-hero-board .wx-stats .wx-stat-wind .nv{flex-wrap:wrap;row-gap:0}
.wx-hero-board .wx-stats .wx-stat-uv .nv .nv-sub{position:static;font-size:10px;color:#C3CCD3;margin-left:2px}
.wx-hero-board .tile-compass,.wx-hero-board .tile-moon{display:inline-block;width:15px;height:15px;flex:none}
.wx-hero-board .tile-arc{display:inline-block;width:20px;height:12px;flex:none;color:#FFFFFF}
.wx-hero-board .tile-compass svg,.wx-hero-board .tile-moon svg,.wx-hero-board .tile-arc svg{display:block;width:100%;height:100%}
.wx-hero-board .wx-press-arrow{font-size:.8em}
@media (min-width:641px){
  .wx-hero.wx-hero-board{min-height:540px}
  .wx-hero-board .wx-hero-inner{padding:20px 24px 22px}
  .wx-hero-board .wx-place{font-size:24px}
  .wx-hero-board .wx-cond{font-size:36px}
  .wx-hero-board .wx-side{padding-left:6px}
  .wx-hero-board .sky-note{font-size:20px}
  .wx-hero-board .wx-stats{gap:10px}
  .wx-hero-board .wx-stats > *{padding:11px 14px 12px;min-height:64px}
  .wx-hero-board .wx-stats .nk{font-size:10px}
  .wx-hero-board .wx-stats .nv{font-size:22px}
}
@media (max-width:640px){
  .wx-hero.wx-hero-board{margin-left:-20.5px;margin-right:-20.5px}
  /* F-1. On a phone the card's height is its content: the title row, the
     number, the one-liner, a band of sky, the tiles. The fixed 560px left a
     screen's worth of empty sky between the one-liner and the tiles on Jack's
     iPhone, so the hero filled the first screen and nothing said to scroll;
     now it ends inside the top 70 percent of the viewport. The sky is still
     the card's whole background, at whatever height the content makes. The
     number scales with the width, 110px at 375 to 124px at 430. The desktop
     keeps its proportions (the block above). */
  :root .wx-hero.wx-hero-board,:root.sky-active .wx-hero.wx-hero-board{min-height:0}
  .wx-hero-board .wx-hero-inner,:root.sky-active .wx-hero.wx-hero-board .wx-hero-inner{padding:10px 8px 10px}
  .wx-hero-board .wx-temp{font-size:clamp(110px,calc(110px + (100vw - 375px) * .2545),124px)}
  .wx-hero-board .wx-cond{font-size:30px}
  .wx-hero-board .sky-note{font-size:20px}
  .wx-hero-board .wx-skyspace{flex:none;height:84px;min-height:0}
  /* The shorter card puts the one-liner near half its height, where the
     desktop's band had thinned to .16: "Clear overhead." measured 3.96:1 on a
     clear day. On a phone the band reaches past the one-liner. */
  .wx-hero-board .wx-scrim{background:linear-gradient(180deg,rgba(6,10,16,.58) 0%,rgba(6,10,16,.50) 32%,
    rgba(6,10,16,.44) 52%,rgba(6,10,16,.18) 62%,rgba(6,10,16,0) 70%,rgba(6,10,16,.22) 100%)}
  .wx-hero-board[data-sky-deck="1"] .wx-hero-grid{margin-top:22px}
  /* the tiles at reading size: labels 12px, values 26px in the mono. Four
     across leaves each tile about 74px, so the small marks (the compass tick,
     the sunset arc, the pressure arrow, the moon) move into the label row's
     right end, and a unit is printed small beside its number. */
  .wx-hero-board .wx-stats{gap:6px}
  .wx-hero-board .wx-stats > *{position:relative;padding:7px 5px 8px;min-height:0}
  .wx-hero-board .wx-stats .nk{font-size:12px;letter-spacing:0;overflow:visible;text-overflow:clip}
  .wx-hero-board .wx-stats .nv{font-size:26px;margin-top:6px;position:static;gap:3px}
  .wx-hero-board .wx-stats .tile-compass,.wx-hero-board .wx-stats .tile-moon,
  .wx-hero-board .wx-stats .tile-arc,.wx-hero-board .wx-stats .wx-press-arrow{position:absolute;top:8px;right:5px}
  .wx-hero-board .wx-stats .wx-press-arrow{font-size:12px;line-height:1}
  .wx-hero-board .wx-stats .nv-unit{font-size:12px;align-self:flex-end;padding-bottom:4px}
}
.wx-hero-board .wx-stats .nv-unit{font-size:.55em;color:#C3CCD3}
/* In the hero, A-1's Change control is a small chevron beside the dots: the
   board shows the place and its dots and no word. It stays a real button, in
   the tab order with its full aria-label, and the name itself stays tappable. */
.wx-hero-board .pt-name{font-size:0;min-height:28px;min-width:24px;text-decoration:none;padding:0 4px}
.wx-hero-board .pt-name::after{content:"\25BE";font-size:13px;color:#E4E9ED}
.wx-hero-board .pt-name:focus-visible{outline:2px solid #FFFFFF;outline-offset:2px}
/* the record book and the source line, directly under the hero */
.wx-recordcard{margin:0 0 18px}
.wx-recordcard .wx-normslot:empty{display:none}
.wx-recordcard .wx-normslot .wx-norm{margin:0 0 10px}

/* ---- the last-known board ----------------------------------------------------
   "As of 4:12 PM, 3 hours ago. No connection." with a Retry, directly under
   the place. It sits on the sky, so it carries its own dark ground and is read
   against that ground, never against a moving gradient. */
.wx-asof{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;margin:6px 6px 2px;padding:6px 10px;
  border-radius:8px;background:rgba(8,12,18,.78);color:#F4F1EA;font-family:var(--mono);font-size:12px;
  line-height:1.4;position:relative;z-index:4}
.wx-asof-retry{font:inherit;font-weight:600;color:#0B0F14;background:#9FD6E6;border:0;border-radius:6px;
  padding:4px 12px;min-height:28px;cursor:pointer}
.wx-asof-retry:focus-visible{outline:2px solid #F4F1EA;outline-offset:2px}

/* F-5: the raindrop and the wind mark before a row's rain and wind, the same
   two the hourly strip's rows carry */
.rg{display:inline-block;vertical-align:-1px;margin-right:4px;line-height:0}
.rg svg{display:block}
.rg-rain{color:var(--teal-ink)}
.dry .rg-rain,.dim .rg-rain,.wk-rain.dim .rg-rain{color:var(--faint)}
.rg-wind{color:var(--faint)}
.wk-meta .wk-windline{display:block}

/* F-8: the theme mark in the hero's title row, and its word for a moment */
.wx-theme{display:inline-flex;align-items:center;gap:6px;flex:none}
.wx-theme-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;
  border:0;border-radius:8px;background:rgba(8,12,18,.35);color:#E4E9ED;cursor:pointer}
.wx-theme-btn:focus-visible{outline:2px solid #FFFFFF;outline-offset:2px}
.wx-theme-word{font-family:var(--mono);font-size:12px;font-weight:600;color:#E4E9ED;opacity:0;transition:opacity .2s}
.wx-theme-word.on{opacity:1}
@media (prefers-reduced-motion:reduce){.wx-theme-word{transition:none}}


/* ==========================================================================
   W-7, THE WATER CARDS: ONE SKELETON (assets/wx-watercard.js). The sky
   hero's bones and the storm card's ramp: the tier tokens above are shared
   through .wc-tier[data-tier]. The dark ground is the storm card's #181C22,
   where the inks hold 4.5 to 1 (the page's #2B2B2B card takes Cat 4 and Cat 5
   to 3.97).
   ========================================================================== */
.wc-card{position:relative;overflow:hidden;padding:16px 18px 14px}
:root[data-theme=dark] .wc-card{background:#181C22}
@media (prefers-color-scheme: dark){:root:not([data-theme=light]):not(.sky-active) .wc-card{background:#181C22}}
:root.sky-active .wc-card{background:rgba(12,18,26,.96)}
/* off the ladder: an alert the product's tiers do not include, named as
   issued in the site's alert copper; none: the check failed or the product
   publishes no tier here */
.wc-tier[data-tier=off]{--tf:var(--copper-ink);--ti-d:var(--copper-ink);--ti-l:var(--copper-ink)}
.wc-tier[data-tier=none]{--tf:#8A93A0;--ti-d:#8A93A0;--ti-l:#5B6470}
.wc-titlerow{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin:0 0 10px}
.wc-name{font-family:var(--serif);font-weight:500;font-size:24px;line-height:1.1;margin:0;color:var(--ink)}
.wc-where{font-family:var(--mono);font-size:10.5px;letter-spacing:.04em;color:var(--faint);text-align:right;line-height:1.45}
.wc-badge{display:inline-block;font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;
  font-weight:700;padding:6px 11px;border-radius:6px;line-height:1.3;
  color:var(--ti);box-shadow:inset 0 0 0 1.5px var(--ti);margin:0 0 4px}
.wc-also{font-size:13.5px;line-height:1.45;color:var(--ink);margin:6px 0 0}
.wc-hero{display:flex;align-items:baseline;flex-wrap:wrap;gap:4px 12px;margin:6px 0 0}
.wc-num{font-family:var(--serif);font-weight:400;font-size:74px;line-height:.9;color:var(--ink);
  font-variant-numeric:lining-nums;letter-spacing:-.01em}
.wc-num-none{font-size:30px;line-height:1.1;color:var(--muted)}
.wc-unit{font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.wc-cond{font-family:var(--serif);font-size:26px;line-height:1.15;margin:8px 0 0;color:var(--ink)}
.wc-line{font-family:var(--serif);font-style:italic;font-size:18px;line-height:1.4;margin:6px 0 0;color:var(--muted)}
.wc-scene{margin:14px -18px 0;overflow:hidden}
.wc-scene svg{display:block}
.wc-cap{font-family:var(--mono);font-size:9.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);margin:6px 0 0}
.wc-sea{fill:#1E4E6B;opacity:.30}
.wc-wave{fill:none;stroke:#6FB6D8;stroke-width:1.6;stroke-linecap:round}
.wc-wave-1{opacity:.7}.wc-wave-2{opacity:.45}
.wc-sun{fill:#E8B64C;filter:drop-shadow(0 0 6px rgba(232,182,76,.55))}
.wc-moon-dark{fill:#1A2238;opacity:.85}
.wc-moon-lit{fill:#EDE6D2}
.wc-boat path{fill:var(--ink);opacity:.85}
.wc-still{stroke:#6FB6D8;stroke-width:1.4}
.wc-staff{fill:#C9D1D8}
.wc-staff-tick{stroke:#46525E;stroke-width:1}
.wc-stage-mark{stroke:var(--tf);stroke-width:2}
.wc-level{fill:var(--ti,#8A93A0)}
.wc-night{fill:var(--ink);opacity:.06}
.wc-tide-pred{fill:none;stroke:var(--teal-ink);stroke-width:1.4;opacity:.75}
.wc-tide-meas{fill:none;stroke:#D9A441;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.wc-tide-pt{fill:var(--teal-ink)}
.wc-tide-now{stroke:var(--copper-ink);stroke-width:1.4;stroke-dasharray:3 3}
.wc-tide-nowlab{font-family:var(--mono);font-size:10px;fill:var(--copper-ink)}
.wc-tiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:7px;margin:14px 0 0}
.wc-tile{background:rgba(28,30,31,.04);border:1px solid var(--line);border-radius:10px;padding:7px 8px 8px;min-width:0}
:root[data-theme=dark] .wc-tile,:root.sky-active .wc-tile{background:rgba(0,0,0,.22);border-color:rgba(255,255,255,.07)}
@media (prefers-color-scheme: dark){:root:not([data-theme=light]) .wc-tile{background:rgba(0,0,0,.22);border-color:rgba(255,255,255,.07)}}
.wc-tile .nk{display:block;font-family:var(--mono);font-size:9px;line-height:1.25;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
/* a unit wraps under its number rather than clipping (the board's Boating
   tiles clipped "NE 5 to 10 kt") */
.wc-tile .nv{margin:3px 0 0;font-family:var(--mono);font-size:14.5px;line-height:1.2;font-weight:500;color:var(--ink);
  overflow-wrap:break-word}
.wc-tile .nv-unit{font-size:10.5px;font-weight:400;color:var(--faint);margin-left:3px;white-space:nowrap}
.wc-extra{margin-top:12px}
.wc-extra .bt-next,.wc-extra .bt-swellsrc{padding-left:0;padding-right:0}
.wc-ladder{margin:14px 0 0}
.wc-track,.wc-labels{display:grid;gap:5px}
.wc-seg{height:7px;border-radius:99px;background:var(--tf);opacity:.28}
.wc-seg.on{opacity:1;box-shadow:0 0 10px var(--tf)}
.wc-labels{margin-top:5px;font-family:var(--mono);font-size:9.5px;letter-spacing:.04em;color:var(--faint);text-align:center}
.wc-labels .on{color:var(--ti);font-weight:700}
.wc-card .card-foot{margin-top:14px}
@media (min-width:641px){
  .wc-card{padding:20px 24px 16px}
  .wc-scene{margin-left:-24px;margin-right:-24px}
  .wc-num{font-size:84px}
  .wc-tile .nv{font-size:18px}
}
.wc-slot{min-height:560px}

/* ==========================================================================
   W-9 item 2: HOME'S ALERT BAND. The widget's own band, on the page: the dot,
   the highest alert's name in its tier ink, when it ends, a count when there
   are more, and a chevron. Five stacked cards used to fill the first screen
   and push the reading off it. The cards are one tap away and are the Alerts
   tab's own cards.
   ========================================================================== */
.al-band{display:flex;align-items:center;gap:8px;width:100%;margin:0 0 10px;
  padding:9px 12px;border:0;border-radius:10px;cursor:pointer;text-align:left;
  background:rgba(10,16,23,.92);color:var(--al-ink,#E4E9ED);
  font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase}
.al-band:focus-visible{outline:3px solid var(--al-ink,#E4E9ED);outline-offset:2px}
.al-dot{width:7px;height:7px;border-radius:50%;flex:none;background:var(--al-dot,#E4E9ED)}
.al-name{font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.al-until{margin-left:auto;white-space:nowrap;letter-spacing:.04em;text-transform:none;font-size:10.5px}
.al-more{font-weight:700;flex:none}
.al-chev{flex:none;font-size:10px;transition:transform .15s ease}
.al-band.is-open .al-chev{transform:rotate(180deg)}
.al-cards{margin:0 0 12px}
.al-cards[hidden]{display:none}
@media (max-width:640px){
  .al-band{font-size:10px;padding:8px 10px;gap:6px}
  .al-until{font-size:9.5px}
}
/* the hero's observed stamp (W-9 item 1): the moment as well as the condition */
.wx-hero-board .wx-obsat{display:block;font-family:var(--mono);font-size:10.5px;color:#C3CCD3;margin-top:3px}
.wx-hero-board .wx-obsat.stale{color:#F0B52F}
