/*
 * CSS for the weewx Tempestas skin.
 * Copyright (C) John Kline 2019-2026
 * Early versions of this skin's predecessor (LiveSeasons) were based on
 * the WeeWX Seasons skin by Tom Keffer and Matthew Wall.
 * See LICENSE for your rights.
 */

/* THE DESIGN FRAME IS 1124 PX WIDE, AND 1REM IS 16 OF THEM.
   Every length on the site is written in rem (1305 px values swept on
   2026-09-12, comments left in px because the numbers below are still
   the design's numbers).  Until then every page pinned `viewport
   width=1124` and let the device shrink the frame to its screen: a
   390-px phone at 0.347, an 834-px tablet at 0.74.  The viewport is
   device-width now, and this rule does that multiplication instead:
   at 1124 and wider the root is 16 px and nothing changes; narrower,
   every rem shrinks by the same factor the device used to apply.
   Lengths that must NOT be here: user units inside SVG (font-size on
   <text>, stroke-width), which stay px, and vw, which was always
   relative to the frame. */
html { font-size: min(16px, calc(100vw * 16 / 1124)); }
/* At 1124 and wider the root is 16 px exactly, stated rather than computed:
   Firefox keeps lengths in sixtieths of a pixel, so 100vw * 16 / 1124 at a
   1124-px window comes out 15.9833, min() takes it, and every table row
   set in `line-height: normal` rounds a pixel short (the reports sheets
   ran 4 percent shorter). */
@media (min-width: 1124px) { html { font-size: 16px; } }
/* A tablet WIDER than the frame used to get the frame scaled UP to fill
   it (the pin's other half: an iPad Pro 11 in landscape showed the 1124
   frame at 1.077), where a desktop window that wide lays the fluid frame
   out at 16 px.  Width cannot tell the two apart; the pointer can. */
@media (pointer: coarse) { html { font-size: calc(100vw * 16 / 1124); } }
/* Form controls do not inherit their size: the UA stylesheet gives
   button, input, select and textarea an absolute 13.333px, which under
   the pin shrank with the frame and under rem would not.  Stated in rem
   at the same value, so a stepper's icon and a slider's thumb scale as
   they always did; every control with a size of its own overrides this. */
button, input, select, textarea { font-size: min(calc(.83333rem + var(--tp-step)), max(.83333rem, var(--tp-cap))); }
/* SMALL TEXT ON PORTRAIT TABLETS IS STEPPED UP ON PURPOSE.
   Under the old pin, iOS Safari enlarged small text on its own, because the
   page was wider than the screen; under device-width nothing does, and every
   site built for phones states its small-text sizes instead.  Every small
   font size in these stylesheets is written
       min(calc(SIZE + var(--tp-step)), max(SIZE, var(--tp-cap)))
   (tools/textstep.py wrote them), which is SIZE wherever the step is 0 and
   SIZE plus the step, never past the cap, on the range below.  SVG text has
   its own pair, in px, because chart user units already scale with the chart.
   Phones take no step: the phone stylesheets' own sizes are what an iPhone
   showed, and a phone rule that reads too small is raised in its own rule.
   The portrait tablet shows the desktop layout shrunk to fit, so its small
   text comes up 2 frame px, never past 16; those are the numbers to tune.
   text-size-adjust keeps any browser from inflating text on top of them. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
/* ONE PAGE, TWO WIDTHS.  Where a page states something differently on a
   phone -- a count, a label, a whole block -- both are in the document
   and the query every phone rule uses shows one.  !important because the
   element's own display rule would otherwise win on specificity. */
@media (max-width: 600px) { .tp-wide { display: none !important; } }
@media (min-width: 601px) { .tp-narrow { display: none !important; } }
/* A GROUP is a wrapper the one-URL page adds only to hide a run of
   siblings together.  It must not become a box: inside a flex or grid
   parent its children would turn into ONE item and lose every gap between
   them (the Graphs page lost 72 px exactly that way).  The query above
   still hides it outright. */
.tp-group { display: contents; }
:root { --tp-step: 0rem; --tp-cap: 0rem; --tp-svg-step: 0px; --tp-svg-cap: 0px; }
@media (min-width: 601px) and (max-width: 1123px) {
  :root { --tp-step: .125rem; --tp-cap: 1rem; --tp-svg-step: 2px; --tp-svg-cap: 16px; }
}

/* THE USER AGENT'S OWN CONTROLS FOLLOW THE PLATE.  Without this the
   browser keeps painting its furniture for a light page on all 48 dark
   ones: the seismograph's <input type="date"> gets a near-black
   calendar glyph on a #19203e field and opens a white picker panel, and
   every scrollbar on the site stays light.  css/celestial-panels.css
   already asserts the theme class "must sit on the root element so
   color-scheme flips with it" -- this is the half that was missing.
   Keyed on the classes theme_boot.inc writes, not on a media query: the
   reader's CHOICE is what these have to follow, and on 'auto' the boot
   has already resolved the system preference into one of them. */
:root.theme-light { color-scheme: light; }
:root.theme-dark { color-scheme: dark; }

:root {
    /* The chrome, moved out of titlebar.inc and menubar.inc in 11.5.
       The page ground it sits on is --tp-page, declared with the rest of
       the panel ladder below.  These four have NO .theme-dark values
       yet: 11.5 moves them somewhere a theme can reach, and choosing
       what dark says is the next release's work.  Every other color in
       this block is 2019 chrome no themed panel touches. */
    /* THE BAND'S SEVERITY LADDER.  The same values css/forecast.css
       paints the alert CARDS with, so the chrome and the page agree about
       what a severity looks like. */
    /* THE FOOTER'S OWN SURFACE.  White in light, and a card in dark --
       the legacy footer inherited the 2019 widget colors and measured
       1.10 against its own background the moment a theme was switched
       on, which is what this set exists to prevent. */
    --tp-chrome-foot:#ffffff;
    --tp-chrome-foot-ink:#343434;
    --tp-chrome-foot-muted:#545e69;
    --tp-chrome-foot-line:#d8dde3;
    --tp-chrome-foot-link:#2b6897;
    --tp-sev-extreme:#7f1d1d;
    --tp-sev-severe:#b91c1c;
    --tp-sev-moderate:#b45309;
    --tp-sev-minor:#367ba3;
    --tp-sev-unknown:#5c6672;
    --tp-sev-quiet:#ffffff;
    --tp-sev-quiet-line:#d8dde3;
    --tp-sev-quiet-ink:#343434;
    --tp-sev-pill:rgba(0, 0, 0, 0.10);
    /* Half of what 12.0 shipped, and uniform: 32 on the desktop
       against the 62-108 that varied by page, 55 on the phone
       twins against 110.
       THE FULL VALUE, not the value minus what follows: this is
       a margin-bottom meeting the next element's margin-top, and
       adjacent margins COLLAPSE to the larger rather than adding.
       Written as 12 on the theory that #contents' 20 would be
       added to it, the gap came out 20 -- the 12 vanished. */
    --tp-chrome-gap:2rem;
    --tp-chrome-face:'ChromeJost', "Avenir Next", Avenir, "Segoe UI",
                     system-ui, sans-serif;
    --tp-chrome-bar:#367ba3;   /* the title bar */
    --tp-chrome-nav:#5A6673;   /* the menu bar and its rules */
    --tp-chrome-ink:#ffffff;   /* type on both */
    --tp-chrome-edge:#e3e3e3;  /* the hairline down either side */

    /* THE PAGE-LEVEL TAB STRIPS -- .submenubarnav, .radarnav and
       celestial.css's .celnav.  Their ground is the same #5A6673 the
       menu bar wears and they have always matched it, but they get
       tokens of THEIR OWN rather than reading --tp-chrome-*: a strip
       belongs to the page it switches, not to the frame, so the two
       have to be able to move apart.  Values here are exactly what was
       rendering before, inline, so this repaints nothing in light.
       Seven templates wrote the ground and the border as a style
       attribute on a <td>, which no stylesheet could override -- the
       same thing 11.5 found on <body> and the two chrome bars, one
       layer in, and the reason the forecast, radar and reports families
       could not follow a theme however ready their own css was. */
    /* The menu bar's own states, which 11.5 left behind when it moved
       the bar, the nav, the ink and the edge: the active item and the
       hover were still written as the keywords SlateGray and #ddd.
       THE ACTIVE VALUE IS THE ONE THING HERE THAT CHANGES.  White on
       SlateGray measures 4.05 against AA's 4.50 and has since 2019 --
       the identical defect the tab strips carried, in the bar directly
       above them, and fixing only the strips would have shipped half a
       fix.  #434d59 is the same answer for the same reason: darkening
       toward the nav's own ground would fix the contrast and destroy
       the selection signal, so the item becomes a darker well instead
       -- white on it 8.59, standing 1.47 off the bar where SlateGray
       stood 1.45.  No dark values: the chrome's dark palette is the
       light/dark release's call, exactly as 11.5 left the other four. */
    /* The active tab's underline.  White on the light bar, but on the
       dark bar the chrome's cream ink reads as a fourth neutral beside
       the nav's own type -- the accent is what says "here". */
    --tp-chrome-active:#ffffff;
    --tp-chrome-hover:#dddddd;     /* was #ddd */
    /* DARKENS, where dark's lightens.  The title bar is #367ba3
       and its ink is white at 4.65 -- already close to the 4.5
       floor -- so ANY lightening of the plate under the theme
       button pushes the label below AA.  Veiling it 18% white
       read 3.35.  The same 18% of black is the same size of
       step (1.36 against the bar, where the white was 1.39) in
       the direction the light bar has room to move. */
    --tp-chrome-btn-hover:rgba(0, 0, 0, .18);
    --tp-chrome-hover-ink:#000000; /* was the keyword black */

    /* CONTROLS -- the date pickers and the seismograph's transport.
       There were two vocabularies for one job: css/graph_date.css's
       2019 set (3px Silver edges, #5A6673 slabs, SlateGray fills) on
       the two reports pages, and css/seismic.css's --sq-btn-* set,
       which 11.2 wrote fresh for the drum's transport rather than
       adopting what already existed.  They differed only in radius,
       height, fill and ink -- two dialects of one language, which reads
       as carelessness.  These are the single definition, sized as the
       reports picker (38px, the larger target for a finger on the
       iPad); both pages read them and neither owns them. */
    --tp-control-face:#ffffff;
    --tp-control-line:#c2c8ce;
    --tp-control-ink:#545e69;
    --tp-control-on:#246a92;
    --tp-control-on-ink:#ffffff;
    --tp-control-hover:#eaeef2;
    --tp-control-h:2.375rem;
    --tp-control-r:.5rem;

    /* The selected tab's UNDERLINE in the 12.0 flat grammar.  Deeper than
       --tp-accent so it stands on the hairline baseline: the pair reads
       4.84 in light, which is the number dark already had. */
    --tp-tab-under:#2a6284;
    --cell-border-color:#bbbbbb;
    /* 12.0.2: LINKS ARE SMALL TEXT AND WERE UNDER AA ON THE PAGE.
       #4282b4 read 3.22 on #e3e3e3, 3.49 on the celestial pages' paper
       and 4.13 on a white card -- passing nowhere it actually lands.
       The page is the worst of the three because the ink is darker than
       every ground, so the LIGHTEST ground gives the most contrast and
       the site's own gray gives the least.  #226a9a is 4.55 / 4.94 /
       5.84 on the same three.  --tp-accent moved in 13.3, when text
       became held to APCA Lc 60 as well with no lower bar for large
       text: #3378a0 read 3.77 / Lc 56.9 on the page, and #246a92 is
       4.61 / Lc 62.8 there and 5.91 / Lc 79.3 on white. */
    --link-color:#226a9a;
    --visited-color:#226a9a;
    /* DARKER THAN THE RESTING LINK, not the 2019 blue it was left at.
       Links moved to #226a9a for AA and this did not, so pointing at one
       took it from 4.55 back to 3.22 on the page -- less legible than
       before the pointer arrived, and the exact value this release
       identified as the defect.  #005a88 is 5.81 / 7.45 / 6.30 on the
       page, a card and the celestial paper, so hovering always makes a
       link MORE legible, which is the relationship the dark plate has
       always had (link 7.73, hover 10.08). */
    --hover-color:#005a88;
    --hival-color:#aa4444;
    --loval-color:#4444aa;
}

/* The one legacy token that a themed panel reaches.  css/statistics.css's
   front-page Highs/Lows widget draws its row rules with
   --cell-border-color, and since 11.4 that widget renders inside .hm,
   which takes .theme-dark -- so without this the rules stayed light gray
   on a dark surface.  The rest of the block above is 2019 chrome that no
   themed panel touches; it moves with the chrome release. */
.theme-dark {
    /* THE PAGE'S DEFAULT INK.  body declares no color, so it has always
       been the user agent's black -- invisible the moment the ground
       goes dark, which is what left the legacy footer at 1.10 against
       its own background.  Setting it HERE rather than on body means
       light is untouched -- and note it IS rendered now: theme_boot.inc
       writes this class onto <html> on all 48 pages, which is 12.0's
       whole point.  The sentence that used to close this comment said
       "nothing renders .theme-dark yet", which was true when the block
       was written and is the opposite of true now. */
    color: var(--tp-ink);

    --cell-border-color:#232b4c;
    /* The rest of the 2019 block the footer and the legacy widgets
       still draw with.  Values from the panel ladder above, so a widget
       and a panel agree on what a rule and a link look like in dark. */
    --link-color:#99c2dd;
    --visited-color:#99c2dd;
    --hover-color:#c2daeb;

    /* The strips on a dark page: the panel ladder's own steps, so a
       strip sits on the page the way a card does rather than keeping a
       light-gray slab in the middle of it. */
    --tp-control-face:#19203e;
    --tp-control-line:#2a3358;
    --tp-control-ink:#b2bae1;
    --tp-control-on:#99c2dd;
    --tp-control-on-ink:#0a0f22;
    --tp-control-hover:#232b4c;

    --tp-tab-under:#99c2dd;
}

/* THE SHARED PANEL LADDER.
   The panel grammar -- white sections, a hairline border, a #367ba3
   eyebrow, muted captions -- was settled by the celestial pages and the
   forecast tabs, and each of those stylesheets carries its own copy of
   the same measured text ladder under its own prefix (--fc-* in
   css/forecast.css, --al-* in css/almanac.css, and celestial's own in
   css/celestial-panels.css).  A fourth copy would have been the easy
   thing to write for the Highs/Lows page; this is that copy promoted
   here instead, so the next page has one to consume rather than one to
   duplicate.

   tempestas.css is linked by every page, so these are available
   everywhere.  The three existing stylesheets are deliberately NOT
   migrated onto them: their values are pinned by tests, and repainting
   shipped pages to prove a refactor is a bad trade.  They migrate when
   the site actually gains its light/dark switch, where the test rewrites
   belong anyway.

   THE VALUES ARE NOT NEW.  They are css/forecast.css's, which were
   derived rather than picked: five tiers evenly stepped in perceptual
   lightness (CIE L*), each clearing WCAG AA on the worst of the five
   grounds it actually lands on (#ffffff, #f5f7f9, #f0f3f6, #e3e3e3 and
   #367ba3).  Do not hand-tune one back.

   There is deliberately NO prefers-color-scheme rule, for the reason
   css/forecast.css records: until the site offers the switch, a reader
   whose OS prefers dark must keep seeing the pages as designed.
   .theme-dark is the switch, and it is already live on the celestial
   panels. */
:root {
    /* THE PAGE GROUND -- and until 11.5 no page ever read it.  All 48
       templates shadowed the body rule with an inline background-color,
       36 writing #e3e3e3 by hand; the body rule itself pointed at a
       --background-color that said #ffffff and painted nothing.  The
       inline attributes are gone and body reads this, so .theme-dark
       below now actually reaches the ground. */
    --tp-page:#e3e3e3;
    /* The gauges page's heartbeat glyph, a hair off the ground it sits on.
       Deliberately near-invisible -- see css/gauges-panel.css. */
    --tp-surface:#ffffff;
    --tp-ink:#222222;
    --tp-ink-2:#343434;
    --tp-ink-3:#3f4955;
    --tp-muted:#545e69;
    /* 4.56 on #e3e3e3 and 5.85 on white.  It was #6a7281, chosen
       against white (4.84) and never measured on the page, where it
       read 3.77 -- the archive pickers' "Latest" sits there. */
    --tp-faint:#5e6574;
    --tp-line:#999999;
    --tp-hair:#d8dde3;
    --tp-hair-2:#eaeef2;
    --tp-rule:#c2c8ce;
    --tp-accent:#246a92;
    --tp-link:#2b6897;
    --tp-hi:#aa4444;
    --tp-lo:#4444aa;
    --tp-tint:#f5f7f9;
    --tp-ok:#2f7d4f;
    /* --tp-ok's counterpart, and it was MISSING: css/seismic.css has
       used var(--tp-bad) for the seismograph's error line since 11.2 and
       no stylesheet ever declared it, so that line rendered in inherited
       ink -- an error message the same color as the text around it.
       The site's own red; 6.47 on the panel surface, where --tp-ok
       reads 5.04. */
    --tp-bad:#b91c1c;
    /* The step between --tp-ok and --tp-bad, for a reading that has
       started to slip but has not failed: the live pill's 1-to-5 minute
       state.  Dark enough to be read as TEXT on the tint, which is how
       that state wears it. */
    --tp-warn:#8a6d03;
    /* THE COUNTDOWN PILL.  A cue meant to be easy not to notice, so it is
       EXEMPT from the contrast standard and held UNDER a ceiling instead
       (the exemption beside APCA_MARK in tests/test_tempestas.py, and
       TestCountdownPillStaysQuiet).  Filled #d5d5d5 is 1.14 on the page;
       the track is the page's own gray, so what has run disappears.  It
       was #cccccc (1.25), carried over from the update bar it replaced,
       and read as too prominent. */
    --tp-count-fill:#d5d5d5;
    --tp-count-track:#e3e3e3;
    --tp-count-edge:#d5d5d5;
    /* Text that sits ON a filled accent ground -- a selected tab, a
       badge.  A token because the fill inverts under .theme-dark. */
    --tp-on-accent:#ffffff;
    --tp-head-rule:#666666;
}

.theme-dark {
    /* THE CHROME'S DARK PLATE.  These four had no dark values at all until
       12.0: the bar stayed #367ba3 and the nav #5A6673 on a navy page, and
       --tp-chrome-edge painted a LIGHT #e3e3e3 hairline down both sides of
       a dark one.  Values are the mockup's "Deep" direction: the chrome
       belongs to the page's own navy family, one step up from
       the ground, rather than sitting on it as a foreign color.
       Measured: chrome ink reads 11.12 on the bar and 12.83 on the nav;
       the bar stands 1.34 off the page ground and the nav 1.17, so the two
       bars still separate from the page and from each other; the edge
       stands 1.38 off the page, which is what a hairline is for. */
    /* The quiet band is a SURFACE and follows the theme; the two
       escalated reds do not -- a fill that means "warning" is the same
       red in both themes, and its white ink with it. */
    --tp-chrome-foot:#111834;
    --tp-chrome-foot-ink:#ded9c9;
    --tp-chrome-foot-muted:#b2bae1;
    --tp-chrome-foot-line:#232b4c;
    --tp-chrome-foot-link:#99c2dd;
    --tp-sev-extreme:#7f1d1d;
    --tp-sev-severe:#b91c1c;
    --tp-sev-moderate:#ebae6b;
    --tp-sev-minor:#88bcde;
    --tp-sev-unknown:#b2bae1;
    --tp-sev-quiet:#111834;
    --tp-sev-quiet-line:#232b4c;
    --tp-sev-quiet-ink:#ded9c9;
    --tp-sev-pill:rgba(255, 255, 255, 0.14);
    --tp-chrome-bar:#152a52;
    --tp-chrome-nav:#111f3d;
    --tp-chrome-ink:#e9e4d4;
    --tp-chrome-edge:#232b4c;
    /* The active tab and hover go LIGHTER than the nav in dark, the way
       every raised surface in the panel grammar does -- NOT darker.  The
       mockup darkened it (rgba(0,0,0,.24) composites to #0d182e) and that
       stands just 1.08 off the nav, a weaker selection signal than the
       1.47 the light plate has and the same defect class as the SlateGray
       tab this release fixed at 4.05.  #26395f stands 1.42 with ink at
       9.01, and hover at 1.20 sits between the two so the ladder reads
       nav -> hover -> chosen. */
    --tp-chrome-active:#99c2dd;
    --tp-chrome-hover:#1b2d54;
    --tp-chrome-btn-hover:rgba(255, 255, 255, .18);
    --tp-chrome-hover-ink:#e9e4d4;

    --tp-page:#0a0f22;
    /* The heartbeat, matched to the LIGHT side's camouflage rather than to
       a legibility target: 1.38 against this ground where the light value
       reads 1.42 against its own.  Silver here would read 10.45 and blink.
       It belongs in THIS block and not the legacy .theme-dark above,
       which the :root between them overrides on <html> -- equal
       specificity, later wins. */
    --tp-surface:#111834;
    --tp-ink:#e9e4d4;
    --tp-ink-2:#ded9c9;
    --tp-ink-3:#d4cfbf;
    --tp-muted:#b2bae1;
    /* = --tp-muted.  Text is held to APCA Lc 60 as well as WCAG 4.5,
       and on every dark ground the site uses the two tiers land 0.7 Lc
       apart at that bar, so faint IS muted: 9.14 / Lc 64.7 on the
       #111834 card, 9.96 / Lc 65.6 on the #0a0f22 page.  It was
       #7a81a3 (Lc 34.5 on the card), and #6e7597 before that. */
    --tp-faint:#b2bae1;
    --tp-line:#2a3358;
    --tp-hair:#232b4c;
    --tp-hair-2:#1b2340;
    --tp-rule:#2a3358;
    --tp-accent:#99c2dd;
    --tp-link:#99c2dd;
    --tp-hi:#ffa289;
    --tp-lo:#a5b7ff;
    --tp-tint:#151c38;
    --tp-ok:#7acb99;
    /* Lighter than the light plate's, for the same reason --tp-ok is:
       #b91c1c is a FILL color in this release and unreadable as text on
       #19203e.  8.11 / Lc 62.2 against that surface; --tp-ok is 8.22 / Lc 62.9. */
    --tp-bad:#faa297;
    /* Lifted off the light plate's #8a6d03 for the same reason as the
       two above: that olive is invisible on #19203e. */
    --tp-warn:#ebae6b;
    /* The light plate's grays are near-white and would glare on #19203e,
       so the dark plate gets its own: a filled part a step up from the
       ground and a track a step below it, in the page's own navy family,
       and quiet like the light pair.  Filled #2e3760 is 1.66 on the page
       and its edge #232b4c 1.38; they were #3d4878 (2.17) and #2a3358. */
    --tp-count-fill:#2e3760;
    --tp-count-track:#1b2340;
    --tp-count-edge:#232b4c;
    --tp-on-accent:#0a0f22;
    --tp-head-rule:#2f3a63;
}

/* Open Sans, served from this skin's own fonts/opensans rather than a font CDN. */
@font-face {
    font-family: 'Open Sans';
    font-weight: normal;
    src: url('../fonts/opensans/OpenSans.woff2') format('woff2'),
    url('../fonts/opensans/OpenSans.woff') format('woff');
    font-display: swap;
}
/* Without this second face every bold on the site is a synthetic bold
   the browser smears from the regular outlines (7.9.2).  Fetched lazily
   -- only pages that render bold Open Sans download it. */
@font-face {
    font-family: 'Open Sans';
    font-weight: bold;
    src: url('../fonts/opensans/OpenSans-Bold.woff2') format('woff2'),
    url('../fonts/opensans/OpenSans-Bold.woff') format('woff');
    font-display: swap;
}

body {
    margin: 0;
    padding: 0;
    border: 0;
    font-family: 'Open Sans', arial, sans-serif;
    background-color: var(--tp-page);
}

a {
    text-decoration: none;
    cursor: pointer;
    color: var(--link-color);
}

a:link {
    color: var(--link-color);
}
a:visited {
    color: var(--visited-color);
}
a:hover {
    color: var(--hover-color);
}


/* THE CHROME'S VOICE.  Jost, self-hosted, chosen on the iPad from three
   candidates served side by side.  A variable woff2 at 26 KB, so one
   file covers every weight the chrome asks for.
   It is the CHROME's face only -- the title bar, the menu bar and the
   footer.  The wordmark keeps Open Sans, which is its own identity, and
   the page content keeps the body stack: this release gives the frame a
   voice of its own, it does not restyle the site's prose. */
@font-face {
    font-family: 'ChromeJost';
    src: url('../fonts/jost/jost.woff2') format('woff2-variations');
    font-weight: 100 900;
    font-display: swap;
}









/* THE THEME CONTROL, on the title bar.
   Every value here is measured against --tp-chrome-bar rather than a
   page surface -- but that token is a DIFFERENT COLOR on each plate
   (#367ba3 light, #152a52 dark), so a ratio quoted here belongs to one
   of them and has to say which.  White on the LIGHT bar reads 4.65,
   which is the tighter of the pair and therefore the one that governs:
   see --tp-chrome-btn-hover, where veiling this button's plate lighter
   on hover took that 4.65 down to 3.35 and under AA, while the dark
   bar's 11.12 had room to spare.  The button is an outline rather than a filled
   well because a filled darker well stands only 1.62-2.02 off the blue,
   which is not enough to read as a control at all -- the same mistake
   that left the menu bar's active tab at 4.05 from 2019 until 11.7.
   The slot is an item of the title bar's grid, placed at the end of its
   right-hand column by `.chbar .tp-theme-slot`, not an overlay. */

.tp-theme-cycle {
  background: transparent; color: var(--tp-chrome-ink);
  border: .0625rem solid rgba(255, 255, 255, .55); border-radius: .5rem;
  /* 96 and 12 are the design's, and 96 was measured to hold all
     three labels -- Auto, Light and Dark -- without the button
     changing width as it cycles, which is the only thing the
     min-width is for.  This shipped at 104/13 for no reason. */
  height: 2.125rem; min-width: 6rem; padding: 0 .75rem;
  font: inherit; font-size: min(calc(.875rem + var(--tp-step)), max(.875rem, var(--tp-cap))); line-height: 2rem; cursor: pointer;
  display: inline-flex; align-items: center; gap: .5rem;
  justify-content: center; white-space: nowrap;
}
.tp-theme-cycle:hover { background: var(--tp-chrome-btn-hover); }
.tp-theme-cycle:focus-visible { outline: .125rem solid var(--tp-chrome-ink);
                                outline-offset: .125rem; }
.tp-theme-icon { font-size: min(calc(.9375rem + var(--tp-step)), max(.9375rem, var(--tp-cap))); line-height: 1; }

/* The control rides in the bar's own grid, after the name, at every
   width: titlebar.inc includes theme_control.inc inside .chbar .chin.
   (What stood here argued for
   the ALERT line instead and measured out the 217px free there.  That is
   not the placement that was built, and it could not be now -- the alert
   line became #chalert-slot, whose whole content the updater swaps, so
   there is no stable room in it to place anything against.) */








#contents {
    clear: both;
    margin: 1.25rem;
}


#reports {
    float: right;
    margin-top: .3125rem;
    margin-right: .625rem;
    margin-bottom: .3125rem;
    text-align: right;
}




.label {
    font-size: 80%;
    vertical-align: top;
    text-align: right;
    padding-top: .25rem;
    padding-right: .3125rem;
}

.data {
    font-weight: bold;
    font-size: 80%;
    vertical-align: top;
    text-align: left;
    padding-top: .25rem;
}

.units {
    font-size: 80%;
    vertical-align: top;
    padding-top: .25rem;
}

.timestamp {
    font-size: 80%;
    font-weight: normal;
}

.hival {
    color: var(--hival-color);
}

.loval {
    color: var(--loval-color);
}


/* THE FOOTER, rebuilt in 12.0.  Three columns over a fine line, aligned
   with the content column the way everything else in this release was:
   the panels' edge is 40px in and their wrap caps at 1240, so the footer
   reproduces both rather than spanning the page.
   It carries the chrome's face, because it IS chrome -- the frame around
   the content, not content. */
.pfoot {
  background: var(--tp-chrome-foot);
  /* THE BRAND RULE, 3px of the title bar's own color, which is what
     closes the page the way the bar opens it.  This shipped as a 1px
     hairline in the footer's line color -- the same weight as the rule
     ABOVE the fine print inside the footer, so the footer's outer edge
     and one of its internal dividers were drawn identically and the
     block stopped reading as a band at all. */
  border-top: .1875rem solid var(--tp-chrome-bar);
  color: var(--tp-chrome-foot-ink);
  font-family: var(--tp-chrome-face);
  margin-top: 2.5rem;
}
.pfoot .pfin {
  max-width: 82.5rem; margin: 0 auto;
  padding: 1.625rem 2.5rem 1.875rem; box-sizing: border-box;
}
/* NOT THREE EQUAL COLUMNS.  The three say different amounts -- a
   two-line blurb, one address, and two sentences of disclaimer -- so
   equal thirds made the third column run two lines deeper than the
   others and the row stopped sitting on one baseline.  Proportioned to
   what they hold, all three come out the same height. */
.pfoot .pfcols {
  display: grid; grid-template-columns: 1.1fr 1fr 1.4fr;
  gap: 1.875rem;
}
/* The column heads are the BRAND's eyebrow, the same 11px letterspaced
   cap the panels use -- not body type in gray.  They shipped at 13px in
   the footer's muted ink, which read as a fourth tier of text rather
   than as a label. */
.pfoot h2 {
  font-size: min(calc(.6875rem + var(--tp-step)), max(.6875rem, var(--tp-cap))); font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--tp-chrome-bar);
  margin: 0 0 .4375rem; border: 0; padding: 0;
}
/* In dark the bar's own navy would vanish into the footer, so the head
   takes the accent the tab underline uses. */
:root.theme-dark .pfoot h2 { color: var(--tp-tab-under); }
.pfoot p { margin: 0; font-size: min(calc(.875rem + var(--tp-step)), max(.875rem, var(--tp-cap))); line-height: 1.55; }
.pfoot .pflead {
  display: block; font-weight: 700; font-size: min(calc(.9375rem + var(--tp-step)), max(.9375rem, var(--tp-cap))); margin-bottom: .125rem;
  text-wrap: balance;
}
.pfoot .pfmute { color: var(--tp-chrome-foot-muted); }
.pfoot a { color: var(--tp-chrome-foot-link); }
/* The fine line: the station's own coordinates and the copyright, which
   used to be the FIRST thing the footer said. */
.pfoot .pffine {
  margin-top: 1.375rem; padding-top: .875rem;
  border-top: .0625rem solid var(--tp-chrome-foot-line);
  font-size: min(calc(.78125rem + var(--tp-step)), max(.78125rem, var(--tp-cap))); color: var(--tp-chrome-foot-muted);
}
/* The phone: one column, and the type scale that was an inline
   font-size:24px until 12.0. */
@media (max-width: 600px){.pfoot .pfcols{ grid-template-columns: 1fr; gap: 1.625rem; }}
@media (max-width: 600px){.pfoot h2{ font-size: min(calc(1.711rem + var(--tp-step)), max(1.711rem, var(--tp-cap))); }}
@media (max-width: 600px){.pfoot p{ font-size: min(calc(2.083rem + var(--tp-step)), max(2.083rem, var(--tp-cap))); }}
/* The lead sets its own size on the desktop (15px, a step ABOVE the
   14px body), so without a phone value it came out 15px on a page whose footer
   body text is 26 -- the emphasized line smaller than what it
   introduces. */
@media (max-width: 600px){.pfoot .pflead{ font-size: min(calc(1.875rem + var(--tp-step)), max(1.875rem, var(--tp-cap))); }}
@media (max-width: 600px){.pfoot .pffine{ font-size: min(calc(1.706rem + var(--tp-step)), max(1.706rem, var(--tp-cap))); }}

/* THE CHROME'S TWO BARS, rebuilt in 12.0.
   Through 11.7 both were 2019 tables inside #title_bar and .topnav: the
   lockup sat at x=14 and the first nav link at x=3, against a content
   column that starts at 40, and the title bar stood 135px tall.  Colors,
   face and the alert band had all been modernized INSIDE that structure,
   which is why it kept looking like the old chrome.

   .chin is the shared wrapper and the whole alignment story: these bars
   are full-bleed, so the wrapper reproduces the content column by hand --
   #contents carries margin:20px and the panel inside it another 20px, so
   the text column starts 40px in and caps at 1240.  1240+40+40 is the box
   that yields those edges at EVERY width, not only at 1124. */
.chin {
  max-width: 82.5rem;
  margin: 0 auto;
  padding: 0 2.5rem;
  box-sizing: border-box;
}

.chbar {
  background: var(--tp-chrome-bar);
  border-top: .0625rem solid var(--tp-chrome-edge);
  font-family: var(--tp-chrome-face);
  position: relative;
}
.chbar .chin {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 1.375rem;
  min-height: 5.5rem;
  padding-top: .625rem; padding-bottom: .625rem;
}
.chlock svg { width: 4.125rem; height: 4.125rem; }
.chbar .chlock { justify-self: start; }
.chbar .tp-theme-slot { justify-self: end; }
/* THE MASTHEAD: the mark at the left, the site's name centered on the
   page with what it covers beneath, the switch at the right.  Equal outer
   columns are what center the name on the page rather than in the space
   between the mark and the switch. */
.chtitle { min-width: 0; text-align: center; }
/* The page's name is the page head's now; the bar keeps it for the
   outline and for screen readers only. */
.chbar .chh1 {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.chname {
  margin: 0; color: var(--tp-chrome-ink);
  font-size: 2.125rem; font-weight: 600; line-height: 1.1; letter-spacing: .01em;
}
.chtag {
  margin: .25rem 0 0; color: var(--tp-chrome-ink);
  font-size: 1rem; font-weight: 500; letter-spacing: .02em;
}

.chnav {
  background: var(--tp-chrome-nav);
  font-family: var(--tp-chrome-face);
}
.chnav .chin { display: flex; gap: .125rem; padding-bottom: .6875rem; }
.chnav a {
  display: inline-block;
  color: var(--tp-chrome-ink);
  text-decoration: none;
  white-space: nowrap;
  /* MEASURED FROM THE INK, not the line box.  padding-bottom is not the
     gap anyone sees: the line box runs on below the baseline by the
     font's descent, and "Forecast" has no descender, so 5px of padding
     painted 11px of air.  line-height:1 tightens the box to the type and
     the padding is set to land the label 13px from the top of the bar --
     where it has always sat -- with 8px under the ink. */
  line-height: 1;
  padding: 1.1875rem .875rem .3125rem;
  font-size: min(calc(.9375rem + var(--tp-step)), max(.9375rem, var(--tp-cap)));
  border-bottom: .1875rem solid transparent;
}
.chnav a:hover { background: var(--tp-chrome-hover); color: var(--tp-chrome-hover-ink); }
.chnav a.active {
  border-bottom-color: var(--tp-chrome-active);
  font-weight: 700;
}
/* AND THE BAR DOES NOT MOVE WHEN YOU CHANGE PAGE.  Bold type is wider
   than regular -- 4 to 8px per label here -- so the active tab grew and
   shoved every tab to its right along with it: Forecast sat at x=125 on
   the home page and x=118 on all nine others, and the whole strip
   twitched sideways on every navigation.
   Each tab reserves its BOLD width whether or not it is the active one:
   a copy of the label, always bold, laid out in a zero-height box that
   is never painted.  content: attr(data-label) is why the anchors carry
   the label twice -- a pseudo-element cannot read its own host's text. */
.chnav a::after {
  content: attr(data-label);
  display: block;
  height: 0;
  overflow: hidden;
  visibility: hidden;
  font-weight: 700;
}
/* THE UNDERLINE FOLLOWS THE INK.  --tp-chrome-active is picked to carry
   against the menu bar, and hovering swaps the ground out from under it:
   in light it is white on the #dddddd hover plate, 1.36, so the mark
   that says which tab you are on disappears at exactly the moment the
   pointer is on it.  Drawn in the hover ink it is 15.46 in light and
   10.65 in dark, and it matches the label it underlines. */
.chnav a.active:hover { border-bottom-color: var(--tp-chrome-hover-ink); }

/* The phone layout: same structure, its own type scale. */
@media (max-width: 600px){.chbar .chin{ min-height: 9.375rem; gap: 1.875rem; }}
/* The mark at 88, and the name and the line beneath it each on one line
   down to a 360-px phone. */
@media (max-width: 600px){.chbar .chlock svg{ width: 5.5rem; height: 5.5rem; }}
@media (max-width: 600px){.chbar .chname{ font-size: 3.1rem; }}
@media (max-width: 600px){.chbar .chtag{ font-size: 1.75rem; }}
/* THE SWITCH SCALES WITH THE BAR IT SITS ON.  Every other element in
   this bar has a phone value -- the mark, the name, the line beneath it,
   the nav -- and the control kept the desktop's 14px/34px, which
   made the one thing on the page a reader has to TAP the smallest thing
   in the chrome.  Found by measuring every chrome element at both widths
   rather than by looking, which is how the band's own scale was missed
   in the first place. */
@media (max-width: 600px){.chbar .tp-theme-cycle{
  font-size: min(calc(1.625rem + var(--tp-step)), max(1.625rem, var(--tp-cap))); height: 3.875rem; min-width: 10.5rem;
  padding: 0 1.375rem; border-radius: .75rem; gap: .75rem;
}}
@media (max-width: 600px){.chbar .tp-theme-icon{ font-size: min(calc(1.879rem + var(--tp-step)), max(1.879rem, var(--tp-cap))); }}
@media (max-width: 600px){.chnav .chin{ flex-wrap: wrap; padding-bottom: .875rem; }}
@media (max-width: 600px){.chnav a{ font-size: min(calc(1.875rem + var(--tp-step)), max(1.875rem, var(--tp-cap))); padding: 1.625rem 1.125rem .5rem; }}

/* THE CHROME'S ALERT BAND, 12.0.  It replaces the "2 NWS Alerts" count
   link, which said the same thing at the same weight for a fog advisory
   and a tornado warning.
   THE ESCALATION IS THE POINT: minor and moderate take a tint behind a
   colored rail, severe and extreme take the color outright.  A rail and a
   dot alone are not a difference -- they would deliver a Dense Fog
   Advisory and a Tornado Warning as the same quiet stripe, which is the
   count link's failure in a nicer shape.
   BOTH EDGES LINE UP WITH THE CONTENT COLUMN, the way the tab strips were
   aligned in 11.7: the panels below start 40px in and end 40px from the
   right, so the band's text does too.  The 4px severity rail is drawn
   OUTSIDE that, which is why the left padding is 36 and not 40 -- the
   text still begins exactly on the content's edge. */
/* THE ONE GAP UNDER THE CHROME.
   It was built out of two to five <br> tags per template plus #contents'
   own 20px margin, which is why it measured 62 on the celestial page,
   64 on the home page, 66 on the forecast, 86 on graphs and 108 on the
   seismograph -- nobody chose any of those.  The <br>s are gone and the
   space is set here, once, so it is the same on every page and can be
   changed in one place.
   ON THE SLOT, not on the nav: the slot is always emitted, so when an
   alert IS in effect the band supplies the gap, and when it is not the
   empty div's own margins collapse through it to the same value.  One
   rule covers both, which is what makes the two states agree. */
#chalert-slot { display: block; margin-bottom: var(--tp-chrome-gap); }

.chalert {
  background: var(--tp-sev-quiet);
  border-top: .0625rem solid var(--tp-sev-quiet-line);
  border-bottom: .0625rem solid var(--tp-sev-quiet-line);
  color: var(--tp-sev-quiet-ink);
  font-family: var(--tp-chrome-face);
}
/* The band shares .chin with both bars, so its text starts on the same
   line the lockup and the first menu link do.  It overrides only the two
   things that are its own: the severity rail, drawn OUTSIDE the content
   edge (hence 36 and not 40, so the dot still lands on 40). */
.chalert .chin {
  display: flex; align-items: center; gap: .6875rem;
  min-height: 2.75rem;
  border-left: .25rem solid var(--tp-sev-moderate);
  padding-left: 2.25rem;
  padding-right: 2.5rem;
}
.chalert.sev-extreme .chin { border-left-color: var(--tp-sev-extreme); }
.chalert.sev-severe  .chin { border-left-color: var(--tp-sev-severe); }
.chalert.sev-minor   .chin { border-left-color: var(--tp-sev-minor); }
.chalert.sev-unknown .chin { border-left-color: var(--tp-sev-unknown); }

/* THE SEVERITY CHIP, in place of a 9px dot that carried
   aria-hidden="true" -- so on the band the severity was encoded by color
   alone AND withheld from assistive tech, with no other route to it
   anywhere in the chrome.  The word is the fix; the color stays, now
   reinforcing the word rather than being the whole of it.

   OUTLINED, because the "+2 more" pill on the same row is filled.

   TWO GROUNDS, which is why the color is not simply the severity token.
   On the quiet tier the chip sits on --tp-sev-quiet and takes the
   severity color: moderate 5.02 light / 7.75 dark, minor 4.65 / 7.73,
   unknown 5.84 / 5.78, all clear of AA.  Severe and extreme never reach
   that tier -- the band FILLS with their color and switches to white ink
   -- so there the chip rides currentColor, the same trick .sevdot used,
   and inherits the 6.47 and 10.02 the fill was already chosen for. */
.sevchip {
  font-size: min(calc(.6875rem + var(--tp-step)), max(.6875rem, var(--tp-cap))); letter-spacing: .09em; text-transform: uppercase;
  font-weight: 700; padding: .125rem .5rem; border-radius: .6875rem;
  border: .0625rem solid var(--tp-sev-moderate);
  color: var(--tp-sev-moderate);
  flex: none;
}
.sev-moderate .sevchip { color: var(--tp-sev-moderate); border-color: var(--tp-sev-moderate); }
.sev-minor    .sevchip { color: var(--tp-sev-minor);    border-color: var(--tp-sev-minor); }
.sev-unknown  .sevchip { color: var(--tp-sev-unknown);  border-color: var(--tp-sev-unknown); }

/* THE HEADLINE IS A LINK, and it must render as though it were not.

   NAMED .chlead, in the chrome's ch* namespace, and NOT .ahead:
   css/forecast.css:383 already owns `.fc .ahead` for an alert card's
   heading, and the alerts, forecast and tides pages load that sheet AND
   carry this band.  The two never touch today -- that rule needs an .fc
   ancestor and the band is chrome, outside #contents, measured -- but
   what keeps them apart is the ancestor, not the name.

   Three things have to be put back, and each of them was going to break
   something visible:

   1. LAYOUT.  .chin is a flex row with gap 11 whose children are the
      dot, the event, the window and the links.  Wrapping the event and
      the window in an anchor makes those two a SINGLE flex child, and
      the 11px between them vanishes.  So the anchor is a flex row of its
      own carrying the same gap: the dot-to-event and window-to-links
      gaps come from .chin as before, and the event-to-window gap now
      comes from here.  Identical geometry, measured.

   2. THE SEVERITY COLOR.  .ast has no color of its own -- it inherits
      from .chalert -- so inside an anchor it would inherit the site's
      link blue instead.  `color: inherit` on the anchor puts the band's
      own ink back and .ast follows it.  .aev keeps its own severity
      color either way, being (0,2,0) against a:link's (0,1,1).  This is
      the trap .chmore's comment below records, arriving one element
      further left.

   3. HOVER.  a:hover repaints to --hover-color at (0,1,1); the rule
      here is (0,2,1) and wins, so hovering leaves the color alone and
      adds an underline instead -- the affordance, and the only state in
      which the band renders differently at all. */
.chalert .chlead,
.chalert .chlead:link,
.chalert .chlead:visited,
.chalert .chlead:hover,
.chalert .chlead:focus {
  display: flex;
  align-items: center;
  gap: .6875rem;
  color: inherit;
  text-decoration: none;
}
.chalert .chlead:hover,
.chalert .chlead:focus-visible { text-decoration: underline; }
.chalert .aev { font-size: min(calc(.9375rem + var(--tp-step)), max(.9375rem, var(--tp-cap))); color: var(--tp-sev-moderate); }
.chalert.sev-minor   .aev { color: var(--tp-sev-minor); }
.chalert.sev-unknown .aev { color: var(--tp-sev-unknown); }
.chalert .ast { font-size: min(calc(.875rem + var(--tp-step)), max(.875rem, var(--tp-cap))); opacity: 0.9; }
/* The count pill is a LINK, not a label.  "+2 more" is a promise that
   there is somewhere to go and see them, so it goes there -- and it is
   the ONE control the band keeps on the alerts page itself, where "See
   details" would point at the page already open. */
/* .chalert .chmore, NOT .chmore.  The pill is a real anchor, so a bare
   .chmore (0,1,0) loses to `a:link` (0,1,1) up this file and the count
   rendered in the site's link blue -- steel on the severe band's red,
   about 2.1, in both themes.  Its sibling .alink escaped the same fate
   only because it happens to be written as .chalert .alink.  The
   :link/:visited pair is belt and braces: it also outranks a:visited,
   which a reader who has opened the alerts page will have. */
.chalert .chmore,
.chalert .chmore:link,
.chalert .chmore:visited {
  font-size: min(calc(.75rem + var(--tp-step)), max(.75rem, var(--tp-cap))); font-weight: 700;
  border-radius: 62.4375rem; padding: .125rem .5rem;
  background: var(--tp-sev-pill);
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}
.chalert .chmore:hover,
.chalert .chmore:focus-visible { text-decoration: underline; }
.chalert .alink {
  margin-left: auto; color: inherit; font-size: min(calc(.875rem + var(--tp-step)), max(.875rem, var(--tp-cap))); font-weight: 600;
  white-space: nowrap;
}

/* SEVERE AND EXTREME: the band becomes the color.  A rail and a dot are
   not a difference -- a Dense Fog Advisory and a Tornado Warning would
   arrive as the same stripe, which is the count link's failure in a
   nicer shape.  White ink in BOTH themes, because the band is a FILL
   rather than a mark on a card: black on #b91c1c measures 2.94, so the
   alert that matters most would have been the hardest line on the page
   to read.  White on it is 6.47. */
.chalert.sev-severe,
.chalert.sev-extreme {
  color: #ffffff;
  border-top: 0;
  border-bottom: 0;
}
.chalert.sev-severe  { background: var(--tp-sev-severe); }
.chalert.sev-extreme { background: var(--tp-sev-extreme); }
.chalert.sev-severe .chin,
.chalert.sev-extreme .chin {
  border-left-color: rgba(0, 0, 0, 0.30);
  min-height: 2.875rem;
}
.chalert.sev-severe .sevchip,
.chalert.sev-extreme .sevchip { color: inherit; border-color: currentColor; }
.chalert.sev-severe .aev,
.chalert.sev-extreme .aev { color: inherit; }
.chalert.sev-severe .ast,
.chalert.sev-extreme .ast { opacity: 1; }
.chalert.sev-severe .alink,
.chalert.sev-extreme .alink { text-decoration: underline; }
.chalert.sev-severe .chmore,
.chalert.sev-extreme .chmore { background: rgba(0, 0, 0, 0.18); }

/* THE DARK FILL TIER NEEDS AN EDGE, and this is measured rather than
   decorative.  A filled band separates from the page on its own in
   light -- severe stands 6.47 against white, extreme 10.02.  On the dark
   page it does not: extreme's #7f1d1d stands 1.74 against #111834 and
   severe stood 2.70, both under the 3.0 a graphical object needs.  The
   loudest thing on the page was the quietest.

   Lightening the fills is not the fix, and not only because it would
   INVERT the ladder (to clear 3.0 on a dark ground, extreme would have
   to end up lighter than severe, so "worse" would read as "paler").
   The severe red is a DECISION: it is the same #b91c1c in both themes,
   deliberately, so a warning is one color a reader learns once.
   So the fills do not move and the tier takes a hairline instead.  At
   0.55 the line composites to #b98a82 over extreme -- 3.35 against the
   fill it edges and 5.83 against the page behind it -- so the band's
   boundary reads on both sides without repainting the warning. */
:root.theme-dark .chalert.sev-severe,
:root.theme-dark .chalert.sev-extreme {
  border-top: .0625rem solid rgba(233, 228, 212, 0.55);
  border-bottom: .0625rem solid rgba(233, 228, 212, 0.55);
}

/* The phone's type scale.  This rule is why the band is not
   simply the desktop band at a smaller width: the phone has room for the
   event and the window on one line only at these sizes.  It replaces a
   selector that was left DANGLING -- `#title_bar.sp ~ .alertband
   .ab-event,` with a comma and no body -- which shipped no phone scale at
   all and made the parser discard the rule that followed it. */
/* KEYED ON THE SLOT'S OWN ATTRIBUTE, not on DOM adjacency.  These said
   `.chnav-sp + .chalert`, which stopped matching the moment the band was
   wrapped in #chalert-slot so an updater could swap it -- the band is no
   longer the nav's next sibling.  The slot is the element that survives
   the swap, so the phone scale holds for the markup that arrives from
   updater.txt as well as the markup the report rendered.  `[id]` is always
   true (the id is how the slot is found); it stands where the twins'
   data-sp flag stood so the specificity did not move when the rules went
   under the width query.  (The first version of this scale was lost once already
   this release, to a dangling selector.) */
/* BIGGER THAN THE DESKTOP'S, not smaller.  These shipped at 14px and
   12.5px -- under the desktop band's own 15 and 14 -- which is the wrong
   direction for this site's phone layout entirely: it is laid out on the
   scaled 1124-px frame and scales type UP, so the menu bar above this band
   is 30px and the tab strip below it 32.  A 14px alert on that page is
   the smallest thing on it, and it is the one line that has to be read.
   The values below sit with the strip and the nav rather than under
   them. */
@media (max-width: 600px){#chalert-slot[id]{ margin-bottom: 3.4375rem; }}
@media (max-width: 600px){#chalert-slot[id] .chin{ min-height: 4.875rem; gap: 1rem; }}
/* AND THE HEADLINE'S OWN GAP, which is the same gap one level down.
   Wrapping the event and the window in .chlead moved the space between
   THOSE two out of .chin and into the anchor -- so this rule scaled the
   dot-to-event and window-to-links gaps to 16 and left the middle one at
   the desktop's 11, at 30px and 26px type.  Measured on the phone:
   16, 11, 16.  The claim that the band renders identically was made from
   a desktop measurement only. */
@media (max-width: 600px){#chalert-slot[id] .chlead{ gap: 1rem; }}
/* The chip scales like every other element in this bar -- 11px against
   the phone's 30px event name would have been the one thing on the band
   that stayed desktop-sized.  Type, not box: the padding is in the same
   proportion at 20px as the desktop's is at 11. */
@media (max-width: 600px){#chalert-slot[id] .sevchip{ font-size: min(calc(1.25rem + var(--tp-step)), max(1.25rem, var(--tp-cap))); padding: .1875rem .8125rem; }}
@media (max-width: 600px){#chalert-slot[id] .aev{ font-size: min(calc(1.875rem + var(--tp-step)), max(1.875rem, var(--tp-cap))); }}
@media (max-width: 600px){#chalert-slot[id] .ast{ font-size: min(calc(1.625rem + var(--tp-step)), max(1.625rem, var(--tp-cap))); }}
@media (max-width: 600px){#chalert-slot[id] .alink{ font-size: min(calc(1.625rem + var(--tp-step)), max(1.625rem, var(--tp-cap))); }}
@media (max-width: 600px){#chalert-slot[id] .chmore{ font-size: min(calc(1.375rem + var(--tp-step)), max(1.375rem, var(--tp-cap))); padding: .25rem .875rem; }}









/* The phone: the same bar at the phone's type scale, and its
   rules are 2px rather than 1. */


/* The menu bar speaks in the title bar's voice: the two bars are one
   object and had been set in two different faces.
   The six page-level TAB STRIPS deliberately do NOT join: a strip belongs
   to the page it switches, not to the frame, so they stay in the body
   face. */



/* gauges.inc's <noscript> warning, out of a style attribute (11.7).
   The color is the CSS keyword it has always been, not a new one --
   this moves it, it does not repaint it.  Named colors are why the hex
   ratchet never saw this line, which is now fixed in the test. */
.noscript-warn {
  color: red;
  text-align: center;
}

/* THE PAGE-LEVEL TAB STRIPS, 12.0: flat text tabs on a hairline with an
   accent underline, aligned with the content column.
   11.7 moved their COLORS out of seven inline attributes and stopped
   there, so through 11.7 they were 2019 slabs wearing tokens: centered,
   bordered, and standing 20px proud of everything below them.
   ALIGNED WITH THE CONTENT, NOT THE PAGE.  A strip sits outside
   #contents, so it has to reproduce that element's geometry by hand:
   #contents carries margin:20px and the panels carry padding:0 20px,
   which puts their edge 40px in, and the wrap inside them caps at 1240
   and centers.  Measured before: the first forecast tab began at x=298
   against the panels' 40. */
.submenubarnav,
.radarnav,
.celnav {
  overflow: visible;
  /* margin-top matches #contents': on some pages the chrome is followed
     by one of these strips and on others by #contents directly, and the
     gap has to be the same either way. */
  margin: 1.25rem 0 1.125rem;
  padding: 0 2.5rem;
  box-sizing: border-box;
}
.submenubarnav table,
.radarnav table,
.celnav table {
  width: 100%;
  max-width: 77.5rem;
  margin: 0 auto;
  border-collapse: collapse;
}
/* Out of seven style attributes and into here (11.7).  The <td> is the
   strip: the wrapper div is transparent and the table only sizes it.

   NO PADDING DECLARATION, deliberately.  The attribute these rules
   replace set background, border and text-align and nothing else, so
   the cell has always used the user agent's 1px -- and writing the
   padding:0 that celestial.css's .celnav td carries took 2px off the
   height of every one of these strips.  Measured, not reasoned: the
   forecast strip went 63px to 61px. */
/* THE PILL RIDES THE STRIP.  A page's LIVE badge or countdown sits at
   the far right of its section's strip, the same at every width.  Only
   the pages with no strip (the Seismograph and About) keep it in the page
   head (.pghead, below), and the two IN-PANEL strips, .pnav and .spnav,
   never carry one: they belong under a page title, not above it.  One
   element per page, with the id; a second copy is a duplicate id.
   THE <td> IS ALREADY THE FLEX ROW, so this only has to stop the pill
   stretching to the tabs' height and let an auto margin do the
   justifying. */
.submenubarnav td.navpill,
.radarnav td.navpill,
.celnav td.navpill {
  display: flex;
  align-items: center;
  white-space: nowrap;
}
.submenubarnav td.navpill .pill,
.radarnav td.navpill .pill,
.celnav td.navpill .pill { margin-left: auto; }
.submenubarnav td,
.radarnav td,
.celnav td {
  background: none;
  border: 0;
  /* A FAINT BASELINE, and this is the whole fix for the LIGHT page.
     .spnav's 2px --tp-head-rule (#666666) is nearly as dark as the
     accent drawn on it -- 1.19 -- so in light the selected tab had no
     edge to stand against and the selection vanished.  In dark the same
     pair measures 4.88, which is why dark read fine and light did not;
     it was never the ink (active against inactive is ~1.35 in BOTH
     themes).  A hairline baseline under a deeper underline puts light at
     4.84 -- dark's own number. */
  border-bottom: .125rem solid var(--tp-hair);
  text-align: left;
  padding: 0;
  display: flex;
  gap: .125rem;
}
.celnav td a { width: auto; }

/* THE PAGE HEAD.  Every page opens with one, on the page ground and never
   inside a card: the title names what the page shows and its muted .over
   says where or when; .pgmeta is the details line.  The aside cell holds
   the pill on a page with no strip to ride (see above), or the moon on
   Celestial Today.  Sized in rem, so every family draws it alike.

     desktop:  title | aside        phone:  title | aside
               title | meta                 meta    meta          */
.pghead {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "title aside" "title meta";
  align-items: end;
  column-gap: 1.5rem;
  border-bottom: .125rem solid var(--tp-head-rule);
  padding-bottom: .5rem;
}
.pghead h2 {
  grid-area: title;
  margin: 0; border: 0; padding: 0;
  font-size: 2.1rem; font-weight: 600; line-height: 1.2;
  letter-spacing: .02em; text-transform: none;
  color: var(--tp-accent);
}
.pghead h2 .over { color: var(--tp-muted); font-weight: 400; }
.pghead > .pill, .pghead > .moonbox { grid-area: aside; justify-self: end; }
.pghead > .pill { margin-bottom: .25rem; }
/* [hidden] has to be restated: the pill's own display outranks the UA's. */
.pghead > .pill[hidden] { display: none; }
.pghead .pgmeta {
  grid-area: meta;
  color: var(--tp-muted); font-size: .95rem; line-height: 1.6;
  text-align: right;
}
@media (max-width: 600px) { .pghead { grid-template-areas: "title aside" "meta meta"; align-items: center; } }
@media (max-width: 600px) { .pghead h2 { font-size: 3.94rem; } }
@media (max-width: 600px) { .pghead { padding-bottom: .75rem; } }
@media (max-width: 600px) { .pghead > .pill { margin-bottom: 0; } }
@media (max-width: 600px) { .pghead .pgmeta { font-size: 1.78rem; text-align: left; } }
/* A page whose content sits outside #contents (the reports pickers) holds
   its head in the same column box the strip above it uses. */
.pgcol { max-width: 82.5rem; margin: 0 auto 1.125rem; padding: 0 2.5rem; box-sizing: border-box; }
.pgcol > .pghead { max-width: 77.5rem; margin: 0 auto; }

/* A card heading's note: prose beside the label, not part of it (the
   Graphs cards' .eyebrow .sub, which css/graphs.css sets the same way). */
.eyebrow .ebnote {
  margin-left: .625rem;
  color: var(--tp-faint); font-weight: 400;
  letter-spacing: .02em; text-transform: none;
}

/* THE GRAPHS PERIOD NAV IS ONE OF THESE STRIPS.  Day / Week / Month /
   Year is the same question the forecast and radar tabs ask, and it was
   the last family still asking it in the 2019 shape: a centered row of
   bordered boxes with the selected one filled solid, standing apart from
   every other switcher on the site.  It has no <table>, so .pnav is both
   the wrapper and the flex row -- 1240 + 40 + 40 is the box that yields
   the same text edges the tabled strips get from padding plus a capped
   table. */
/* NO CONTENT COLUMN OF ITS OWN, unlike the five strips above.  Those
   are page-level elements outside #contents and have to reproduce that
   geometry by hand; this one lives INSIDE the graphs panel, whose
   .grwrap already caps at 1240 and centers, so a second column here
   double-counts it.
   And `margin: 0 auto` was worse than redundant: .grwrap is a COLUMN
   flex, and an auto cross-axis margin on a flex item overrides stretch
   and shrinks the item to its content -- the strip came out 394px wide,
   centered, with its first tab on x=405 instead of on the panel's own
   content line.  Measured, after being written from the assumption that
   this strip sat where the others do. */
.pnav {
  display: flex;
  gap: .125rem;
  margin: 0;
  padding: 0;
  border-bottom: .125rem solid var(--tp-hair);
  justify-content: flex-start;
}
.submenubarnav a,
.radarnav a,
.celnav a,
.pnav a {
  float: none;
  background: none;
  color: var(--tp-muted);
  font-size: min(calc(1rem + var(--tp-step)), max(1rem, var(--tp-cap)));
  font-weight: 600;
  padding: .6875rem 1.125rem .3125rem;
  text-decoration: none;
  /* The underline is drawn on the tab and pulled down OVER the
     baseline, so the two never stack into a 5px rule. */
  border-bottom: .1875rem solid transparent;
  margin-bottom: -.125rem;
}
.submenubarnav a:hover,
.radarnav a:hover,
.celnav a:hover,
.pnav a:hover {
  background: none;
  color: var(--tp-ink);
}
.submenubarnav a.active,
.radarnav a.active,
.celnav a.active,
.pnav a.active {
  background: none;
  color: var(--tp-tab-under);
  border-bottom-color: var(--tp-tab-under);
}
/* AND THE ACTIVE TAB ANSWERS THE POINTER.  .active and :hover above are
   both (0,2,1) and .active is written later, so it won its own hover
   outright and the tab you are on was the one tab in these four strips
   that did nothing at all under the cursor -- while .chnav's does.  Same
   rule as the chrome's: the underline follows the ink, so both take
   --tp-ink.  Measured against --tp-page, not --tp-surface: three of these
   four strips are page-level elements outside #contents (see the note at
   the top of this block) and only .pnav sits on a panel.  That is 12.40
   in light and 14.95 in dark. */
.submenubarnav a.active:hover,
.radarnav a.active:hover,
.celnav a.active:hover,
.pnav a.active:hover {
  color: var(--tp-ink);
  border-bottom-color: var(--tp-ink);
}

/* The phone's type scale.  It was eleven inline font-size attributes
   across four templates -- 32px on the tabs, 37px on the three-up rows --
   which is where the strips' geometry lived too until 12.0, when the
   twins took a -sp class and the scale became a stylesheet's business.
   Since the one-URL merge the same rules sit under the width query.

   AFTER the base rules, and that is load-bearing.  The phone `.submenubarnav a`
   and the base `.submenubarnav a` are BOTH (0,1,1), so the later one wins -- and
   with this block written above them, every phone strip rendered at the
   desktop's 16px under a 30px menu bar.  Specificity did not save it and
   would not have: the fix is source order. */
@media (max-width: 600px){.submenubarnav a,
.radarnav a,
.gr .pnav a{
  font-size: min(calc(2rem + var(--tp-step)), max(2rem, var(--tp-cap)));
  padding: 1rem 1.375rem .5rem;
}}
@media (max-width: 600px){.radarnav a{ font-size: min(calc(1.875rem + var(--tp-step)), max(1.875rem, var(--tp-cap))); }}
/* The phone .celnav a is NOT set here: css/celestial.css owns it and loads
   after this file, so a value here never won.  One home for it. */
tr.forecast_hours td {
  border-bottom:1pt solid #aaaaaa;
  padding: .9375rem;
}
tr.forecast_hours_inner_row td {
  border-bottom:none;
}
.arrow {
  border: solid SlateGray;
  font-size: min(calc(2.5rem + var(--tp-step)), max(2.5rem, var(--tp-cap)));
  border-width: 0 .5rem .5rem 0;
  display: inline-block;
  padding: .5rem;
}
.right_dir {
  transform: rotate(-45deg);
  -webkit-transform: rotate(-45deg);
}
.left_dir {
  transform: rotate(135deg);
  -webkit-transform: rotate(135deg);
}
/* Home page canvas instruments (scripts/current_gauges.js): the Current
   widget's wind compass and rain measuring tubes.  The drawing is
   resolution-independent; these decide the displayed size.  The desktop
   compass is 150 frame px and the tubes 58 by 100; the phone's are 232
   and 107 by 186, the sizes its page carried before the merge.  232, not
   288: the phone masthead keeps its three columns since 12.0.2 and the
   compass shares the width with the temperature and the rain tubes. */
.wind-compass {
    display: block;
    margin: .375rem auto 0 auto;
    width: 9.375rem; height: 9.375rem;
}
.rain-tube {
    display: block;
    margin: .125rem auto 0 auto;
    width: 3.625rem; height: 6.25rem;
}
@media (max-width: 600px){.wind-compass{ width: 14.5rem; height: 14.5rem; }}
@media (max-width: 600px){.rain-tube{ width: 6.6875rem; height: 11.625rem; }}
.gustline {
    color: #777;
}

/* The radar family's strip is styled with the OTHER page-level strips,
   up with .submenubarnav and .celnav -- one grammar for all three since
   12.0.  A second .radarnav block used to sit here, further down the file
   and therefore winning: the radar tabs kept the 2019 float:left, 17px
   and padding:14px 0 while the forecast and celestial tabs had moved to
   the flat grammar, so one family out of three looked untouched. */


/* The live logo (logo.inc + scripts/paw_logo.js, skin 7.9): the mark in
   the title bar, plus its ambient animations. */
.paw-lockup { display: flex; align-items: center; text-decoration: none; }
.pl-cloud-a { animation: paw-drift-a 80s linear infinite; }
.pl-cloud-b { animation: paw-drift-b 120s linear infinite; }
@keyframes paw-drift-a { from { transform: translateX(-4.375rem); } to { transform: translateX(11.875rem); } }
@keyframes paw-drift-b { from { transform: translateX(12.5rem); } to { transform: translateX(-5rem); } }
.pl-rays { animation: paw-sunspin 90s linear infinite; transform-origin: 0px 0px; }
@keyframes paw-sunspin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
.pl-star { animation: paw-twinkle 3.2s ease-in-out infinite; }
.pl-star.pl-t2 { animation-delay: 1.1s; }
.pl-star.pl-t3 { animation-delay: 2.2s; }
@keyframes paw-twinkle { 0%, 100% { opacity: .9; } 50% { opacity: .25; } }
.pl-drop { animation: paw-raindrop 1.1s linear infinite; }
@keyframes paw-raindrop {
    from { transform: translateY(-.875rem); opacity: 0; }
    12% { opacity: 1; }
    85% { opacity: 1; }
    to { transform: translateY(1.625rem); opacity: 0; }
}


/* ============================ THE LIVE PILL =============================
 * How old the data on the page is, painted by common_updater.inc's
 * paintLivePill (and by scripts/panel_gauges.js, which carries its own
 * copy because a standalone .js cannot #include).  It replaced the
 * live-label badge and the O/o heartbeat beside it in 12.0.1: one element
 * that says the age in words, escalates in color, and BEATS once per
 * packet whose stamp actually moved -- which the heartbeat did not, since
 * it flipped on any fetch that returned, including a fetch of a file the
 * station had stopped rewriting.
 *
 * FOUR states where the badge had three: the healthy one FILLS, because
 * "the feed is alive" is the claim that has to read across a room, and
 * everything after it is an outline whose color escalates.
 *
 * These selectors are single-class on purpose, so the three pages that
 * already had a pill of their own -- .ab (About), .rs (Radar), .sq
 * (Seismograph) -- keep theirs: their rules are (0,2,0) and win.
 * Nothing here may be raised to an id selector for the same reason.
 *
 * --tp-beat is the halo color, declared per state, so a beat can never
 * announce a state in another state's color.  border-color is left off
 * the bare .pill deliberately: with no state class it falls to
 * currentColor.  (The expired pill leaned on that with an inline red
 * until 12.0.1; it wears .pill--click now, which sets its own.) */
/* TWO SIZES, ONE SHAPE, AND EVERY INPUT PINNED.
 *
 * The pill is CHROME, so it does not scale with the page it sits in --
 * deliberately, and against how the rest of the phone layout works.
 * Through 12.0 it inherited its font-size from eleven different hosts
 * and its box was in px, so it was not one pill at eleven sizes: the
 * text ran 10.1px to 33.6px while the padding stayed 9px and the radius
 * stayed 10px, and radius-over-height slid from 0.50 (a true capsule on
 * the About page, the only place those numbers were ever judged) to 0.19
 * (the home twin, a rounded rectangle).  On the About twin the result
 * was 19px text on the 1124-px frame, which a
 * phone squeezes to about a third -- roughly six point, unreadable.
 *
 * So: font-size, line-height and font-family are all set HERE, and the
 * box is in em.  Then the shape cannot drift, because it is a multiple
 * of the one number that changes between the two tiers.
 *
 *  - line-height, because `normal` resolves against the FAMILY: 14.0px
 *    text gave a 22px box on gauges and a 25px box on Highs & Lows.
 *  - font-family, because the pill inherited three of them -- the word
 *    LIVE was set in Open Sans, system-ui and ui-monospace depending on
 *    the page.
 *  - border-radius:999px rather than a computed half-height, so the ends
 *    stay fully round at any size by construction; there is no ratio
 *    left to keep in step.
 *
 * .pill-sp is the phone tier, and its rules sit under the width query.
 * A pill only the phone layout shows carries the class in its markup;
 * the class is inert above 600px, so it states the tier without
 * deciding when it applies.
 */
.pill{font-size:min(calc(.875rem + var(--tp-step)), max(.875rem, var(--tp-cap)));line-height:1.36;
      font-family:'Open Sans', arial, sans-serif;
      font-weight:600;letter-spacing:.02em;text-transform:none;
      padding:.2em .9em;border-radius:62.4375rem;border:.0625rem solid;
      white-space:nowrap;display:inline-block;vertical-align:middle}
@media (max-width: 600px){.pill-sp{font-size:min(calc(2rem + var(--tp-step)), max(2rem, var(--tp-cap)))}}
.pill--live{color:var(--tp-on-accent);border-color:var(--tp-ok);
            background:var(--tp-ok);--tp-beat:var(--tp-ok)}
.pill--ok{color:var(--tp-ok);border-color:var(--tp-ok);
          background:var(--tp-tint);--tp-beat:var(--tp-ok)}
.pill--warn{color:var(--tp-warn);border-color:var(--tp-warn);
            background:var(--tp-tint);--tp-beat:var(--tp-warn)}
.pill--down{color:var(--tp-on-accent);border-color:var(--tp-bad);
            background:var(--tp-bad);--tp-beat:var(--tp-bad)}
/* EXPIRED.  A page nobody has looked at for 24 hours stops polling, and
   this pill is the only way back, so it is the one state that asks the
   reader to DO something -- and until 12.0.1 it was the quietest thing on
   the ladder, red text on the page's own ground while every state that
   needs no action was a filled capsule.  It fills now, in the site
   accent rather than red: red here means the data is not to be trusted,
   and an expired page is not broken, it is paused. */
.pill--click{color:var(--tp-on-accent);border-color:var(--tp-accent);
             background:var(--tp-accent);cursor:pointer}

/* ONE BEAT PER PACKET WHOSE STAMP MOVED.  A halo, not a movement: no
   transform and no layout property, so the text inside the pill never
   shifts and the heading it sits in never reflows.  It begins as an
   opaque ring of zero width -- invisible -- and grows outward as it
   fades, which is why the keyframes carry no alpha of their own; an
   opaque color interpolating to `transparent` fades that color out.
   The spread is in em because these pills sit inside headings sized
   anywhere from .42em to 2.1em, and a px halo would be a hairline on
   one and a bruise on another. */
@keyframes tp-beat{
  from{box-shadow:0 0 0 0 var(--tp-beat)}
  to{box-shadow:0 0 0 .55em transparent}
}
.pill--beat{animation:tp-beat 700ms ease-out}
/* A reader who has asked for less motion keeps the pill and its colors
   and loses the beat.  Nothing is withheld: the age is in the pill's own
   text, which is the fact the beat only decorates. */
@media (prefers-reduced-motion:reduce){
  .pill--beat{animation:none}
}

/* ========================= THE COUNTDOWN PILL ==========================
 * The same box as the live pill -- same size per tier, same shape, same
 * round ends -- carrying no words at all.  It replaced a 60x2px (100x6
 * on the phone) two-tone bar built as a pair of inline-styled spans in a
 * javascript string, and it says the same thing the bar said: how much
 * of the wait until the next refresh has run.
 *
 * NO TEXT, EVER.  The width is fixed rather than set by content, and it
 * is the live pill's own width at "LIVE" (4.17em measured), so the two
 * read as the same object on a page that has both.  The &nbsp; the
 * painter puts inside is there only to give the box its line, and
 * color:transparent makes sure nothing can ever show through it.
 *
 * Its one gradient stop is --tp-count-pos, written by paintCountdownPill
 * as a percentage: everything left of it has run, everything right of it
 * has not. */
/* .pill sets display:inline-block, which beats the browser's own
   [hidden]{display:none}; without this, hiding the pill does nothing at
   all and the countdown appears frozen instead of absent. */
.pill[hidden]{display:none}
/* SMALL AND FAINT ON PURPOSE.  A cue meant to be easy not to notice:
   2.8em by .62em, about two-thirds of the half-height bar 12.0.1 drew
   (4.1em by .95em), which read as too prominent.  The same size on the
   phone, which is how it was reviewed there.  The height
   is explicit and the line box is collapsed, so the &nbsp; that gives it a
   line cannot push it back out.  TestCountdownPillStaysQuiet holds the
   size and the colors under a ceiling. */
.pill--count{width:2.8em;height:.62em;box-sizing:border-box;
             line-height:0;overflow:hidden;
             color:transparent;border-color:var(--tp-count-edge);
             background:linear-gradient(to right,
               var(--tp-count-fill) 0 var(--tp-count-pos,0%),
               var(--tp-count-track) var(--tp-count-pos,0%) 100%)}

/* A refresh that did not land.  It keeps counting, because another
   attempt IS coming and the reader wants to know when; the color carries
   the other half -- that the last one failed.  Amber for one miss, which
   on a five-minute interval means a summary a few minutes past its last
   good update; red once two intervals have gone by without a success,
   which is the point at which a reader should stop trusting it.  Only
   the two Reports pages can reach either: every other family hands its
   refresh to an image or a page reload and never learns the outcome. */
.pill--count-warn{--tp-count-fill:var(--tp-warn);--tp-count-edge:var(--tp-warn)}
.pill--count-bad{--tp-count-fill:var(--tp-bad);--tp-count-edge:var(--tp-bad)}

/* WORKING.  A barber pole in the same two colors, looping for as long as
   the fetch takes -- which is the honest shape of it, since nothing
   knows how long that is.  This is what replaced the words CHECKING FOR
   UPDATE, which every family spelled slightly differently. */
@keyframes tp-barber{from{background-position:0 0}to{background-position:1.7em 0}}
.pill--count-fetch{background:repeating-linear-gradient(-45deg,
                     var(--tp-count-fill) 0 .42em,
                     var(--tp-count-track) .42em .85em);
                   animation:tp-barber 800ms linear infinite}

/* LANDED.  One turn of the pill as the new data arrives and the count
   starts again -- a discrete gesture for a discrete event, where the
   barber pole is a state.  Deliberately not the live pill's halo: these
   two mean different things and must not be read as the same signal. */
@keyframes tp-turn{from{transform:rotateY(0)}to{transform:rotateY(360deg)}}
.pill--count-turn{animation:tp-turn 620ms ease-in-out}

@media (prefers-reduced-motion:reduce){
  /* The stripes stay -- they are the "working" signal and they are not
     motion -- but they stop traveling, and the turn does not happen. */
  .pill--count-fetch{animation:none}
  .pill--count-turn{animation:none}
}
