/* Copyright 2026 by John A Kline.  See LICENSE.txt for your rights.

   The sample report's three pages -- 7 Day, Hourly and Alerts -- as white
   section cards on a light gray page, sized in em so one set of breakpoints
   scales the whole thing.

   THEMING.  Every color here is a custom property, never a literal.  :root
   carries the light values; the dark counterparts are redefined under
   prefers-color-scheme, so the page follows the reader's own setting with no
   toggle, no javascript and no stored preference for a copier to inherit.
   That means the WHOLE page is tokenized -- background, text, rules and the
   navigation bar -- not just the parts that were obviously colored.

   The --wx-* properties are a different thing and are NOT set here: they are
   weewx-nws's own icon contract (see bin/user/nwsicons.py), and their light
   defaults are already what this page wants, so a skin that defines nothing
   renders them exactly as shipped.  Only their dark counterparts are stated,
   in the dark block below.

   Charts need nothing beyond this file: every mark bin/user/nwsskin.py emits
   carries a class and no inline color. */

:root {
  --fc-page:#e3e3e3;
  --fc-surface:#ffffff;
  --fc-ink:#222222;
  --fc-ink-2:#343434;
  --fc-ink-3:#3f4955;
  --fc-muted:#545e69;
  --fc-faint:#6a7281;
  --fc-line:#999999;
  --fc-hair:#d8dde3;
  --fc-hair-2:#eaeef2;
  --fc-rule:#c2c8ce;
  /* Accent reaches THREE grounds -- the page, the card and the tint -- and
     the page title is accent text on the page.  Held to the text bar there
     (WCAG 4.5 AND APCA Lc 60, whatever the size), #3378a0 read 3.77 / Lc
     56.9.  #226b93 is the lightest value on the same hue clearing both
     bars on all three grounds. */
  --fc-accent:#226b93;
  --fc-link:#3f7bab;
  --fc-hi:#aa4444;
  --fc-lo:#4444aa;
  --fc-band:#eef1f5;
  --fc-grid:#e2e6eb;
  --fc-grid-2:#c8ced6;
  /* The chart axes and the 7 Day chart's dividing rule are marks a reader
     has to find, so they clear WCAG 3.0 AND APCA Lc 30 -- on the card and
     on the night shading the rule crosses.  #b9c0c9 read 1.83 on the card;
     #858c94 is the lightest value on its hue clearing both grounds. */
  --fc-axis:#858c94;
  --fc-rain:#2f6ea8;
  --fc-dew:#2f7d70;
  --fc-tint:#f5f7f9;
  --fc-tint-2:#f0f3f6;
  --fc-ok:#2f7d4f;
  /* The page-heading rule, and text that sits ON a filled accent or
     severity ground -- the current nav tab, an in-effect badge.  Tokens
     rather than literals precisely because the fill inverts in dark mode:
     white on a pale blue tab would be the one unreadable thing left. */
  --fc-head-rule:#666666;
  --fc-on-accent:#ffffff;
  /* Alert severity.  NWS's own ranking, toned to sit on this page. */
  --fc-sev-extreme:#7f1d1d;
  --fc-sev-severe:#b23a24;
  --fc-sev-moderate:#b45309;
  --fc-sev-minor:#367ba3;
  --fc-sev-unknown:#5c6672;
  --fc-warn-bg:#fff6e5;
  --fc-warn-line:#e0b972;
  --fc-warn-ink:#6b4a10;
  /* The page navigation.  Its own tokens rather than borrowed ones: the bar
     is the only element that sits on the page ground rather than on a card,
     so it is the only one whose contrast is measured against --fc-page. */
  --fc-nav-bg:#ffffff;
  --fc-nav-ink:#3f4955;
  --fc-nav-hover:#f0f3f6;
}

/* ---- the panel ------------------------------------------------------ */
.fc{padding:26px 20px 34px;margin-top:18px;font-size:16px;color:var(--fc-ink);
    text-align:left}
.fc .fcwrap{max-width:1240px;margin:0 auto;display:flex;flex-direction:column;gap:26px}
.fc section{background:var(--fc-surface);border:1px solid var(--fc-line);
            padding:18px 20px 16px}
.fc .eyebrow{color:var(--fc-accent);font-weight:600;font-size:.8em;
             letter-spacing:.08em;text-transform:uppercase;margin:0 0 12px;
             border:0;padding:0}
.fc .caption{color:var(--fc-muted);font-size:.85em;margin:14px 0 0;max-width:86ch;
             line-height:1.55}
.fc .caption b{color:var(--fc-ink-3)}
.fc .cfoot{color:var(--fc-muted);font-size:.82em;border-top:1px solid var(--fc-rule);
           padding-top:10px}
.fc a{color:var(--fc-link);text-decoration:none}

.fc .chead{display:flex;justify-content:space-between;align-items:flex-end;gap:24px;
           border-bottom:2px solid var(--fc-head-rule);padding-bottom:8px}
.fc .sub{color:var(--fc-muted);font-size:.95em;padding-bottom:4px}
/* --fc-muted and not --fc-faint on .sub and .cfoot is deliberate: both sit
   OUTSIDE any <section>, so they render on --fc-page rather than on a card,
   and faint is not derived against that ground.  Nothing on these pages puts
   faint-colored text outside a section. */

/* ---- drawn icons (weewx-nws 5.2) ------------------------------------
   Three class names, all weewx-nws's contract: size them, do not rename
   them.  Sizing hangs off .wxi alone, so all three land in the same box
   whatever the row is. */
.fc .wxi{display:block}
/* A condition NWS added after the drawn set shipped: its own raster, asked
   for at ?size=large, contained rather than stretched. */
.fc img.wxi-fallback{border-radius:4px;object-fit:contain}
/* NWS's own `unknown` condition, which its icon service answers 400 for --
   weewx-nws renders an empty box rather than hand the reader a broken
   image.  A faint dashed outline says "no icon for this hour"; an empty
   box on its own reads as a layout bug.  OUTLINE, not border: outlines
   take no space, so this cannot nudge a 34px hour-table cell. */
.fc .wxi-unknown{outline:1px dashed var(--fc-rule);outline-offset:-1px;
                 border-radius:4px}

/* ---- Right now ------------------------------------------------------ */
.fc .nowgrid{display:grid;grid-template-columns:96px 1fr 210px;gap:20px;align-items:start}
.fc .nowicon .wxi{width:96px;height:96px}
.fc .nowname{font-size:1.35em;font-weight:600;color:var(--fc-accent);margin-bottom:6px}
.fc .nowprose{margin:0;line-height:1.55;max-width:64ch;font-size:.98em}
.fc .nowstats{border-left:1px solid var(--fc-rule);padding-left:18px}
.fc .stat{display:flex;flex-direction:column;margin-bottom:12px}
.fc .sl{color:var(--fc-muted);font-size:.78em;letter-spacing:.06em;text-transform:uppercase}
.fc .sv{font-size:1.15em;font-variant-numeric:tabular-nums}
.fc .hival{color:var(--fc-hi)}
.fc .loval{color:var(--fc-lo)}

/* ---- the week ------------------------------------------------------- */
.fc .colhead,.fc .day{display:grid;grid-template-columns:112px 236px 1fr;gap:18px}
.fc .ch3{display:grid;grid-template-columns:40px 50px 1fr 116px;gap:12px}
.fc .colhead{color:var(--fc-muted);font-size:.72em;letter-spacing:.08em;
             text-transform:uppercase;padding-bottom:8px;
             border-bottom:1px solid var(--fc-line);align-items:end}
.fc .colhead .chr{text-align:right}
.fc .days{display:flex;flex-direction:column}
.fc .day{align-items:center;padding:14px 0;border-top:1px solid var(--fc-hair)}
.fc .day:first-child{border-top:0}
.fc .when{line-height:1.2}
.fc .wd-name{display:block;font-weight:600;font-size:1.05em}
.fc .wd-date{display:block;color:var(--fc-muted);font-size:.82em}
.fc .temps{display:flex;align-items:center;gap:10px;
           font-variant-numeric:tabular-nums;font-size:1.05em}
.fc .lo{color:var(--fc-lo);width:2.6em;text-align:right}
.fc .hi{color:var(--fc-hi);width:2.6em;text-align:left;font-weight:600}
.fc .track{position:relative;flex:1;height:8px;border-radius:4px;background:var(--fc-grid)}
.fc .fill{position:absolute;top:0;height:8px;border-radius:4px;
          background:linear-gradient(90deg,var(--fc-lo) 0%,var(--fc-hi) 100%)}
/* A day whose daylight period has already ended: one marker at the low. */
.fc .dot{position:absolute;top:-2px;width:12px;height:12px;margin-left:-6px;
         border-radius:50%;background:var(--fc-lo)}
.fc .hi.part{color:var(--fc-muted);font-size:.78em;font-weight:400}
.fc .sun{color:var(--fc-muted);font-size:.76em;margin-top:6px;text-align:center;
         font-variant-numeric:tabular-nums}
.fc .lines{display:flex;flex-direction:column;gap:6px;min-width:0}
.fc .line{display:grid;grid-template-columns:40px 50px 1fr 116px;gap:12px;align-items:start}
.fc .ic .wxi{width:40px;height:40px}
.fc .prose{margin:0;font-size:.84em;line-height:1.5;color:var(--fc-ink-2)}
.fc .kind{color:var(--fc-muted);font-weight:600}
.fc .chip{font-size:.84em;text-align:right;font-variant-numeric:tabular-nums;
          line-height:1.5;color:var(--fc-muted)}
/* The chance of rain moved next to the icon, so it reads as one unit with
   it -- the association NWS bakes into its own raster -- and is left-aligned
   against the icon rather than right-aligned against the prose. */
.fc .chip-pop,.fc .hr-pop{text-align:left}
.fc .pop{color:var(--fc-rain);font-weight:600;white-space:nowrap}
/* --fc-muted, not --fc-rule: that one is a hairline color and reads at
   1.69 against the card, which is fine for a border and not for a glyph
   that means "no data". */
.fc .pop-na{color:var(--fc-muted);font-weight:400}
/* The droplet keeps the Rain column self-describing once the heading has
   scrolled off -- the phone twin is seven tall rows. */
.fc .drop{width:.62em;height:.78em;fill:currentColor;margin-right:.28em;
          vertical-align:-.06em}
.fc .w{display:block}
.fc .wd{display:block;font-size:.86em}

/* ---- charts ---------------------------------------------------------
   Shared by the sparkline, the day chart and the week chart. */
.fc .chartwrap{position:relative}
.fc svg.chart{width:100%;height:auto;display:block;touch-action:pan-y;cursor:crosshair}
/* The charts are focusable so a keyboard can walk them.  The ring is keyed
   off a `kbd` class the script sets, NOT off :focus-visible: the engines
   disagree about an SVG, and Chromium drew the ring after a plain mouse
   click while Firefox drew nothing. */
.fc svg.chart:focus{outline:none}
.fc svg.chart.kbd:focus{outline:2px solid var(--fc-accent);outline-offset:2px}
/* container-type on .sparkwrap and NOT on .chartwrap: the Hourly page's two
   charts share that class and have no chips, and containment they do not need
   is containment that can only surprise someone later. */
.fc .sparkwrap{margin:0 0 18px;container-type:inline-size}
/* The 7 Day chart has its own card, whose padding already does this. */
.fc .twoweek .sparkwrap{margin-bottom:0}
.fc .sparkcap{color:var(--fc-muted);font-size:.8em;margin:6px 0 0}
.fc svg.chart .night{fill:var(--fc-band)}
.fc svg.chart .hgrid{stroke:var(--fc-grid);stroke-width:1}
.fc svg.chart .axis{stroke:var(--fc-axis);stroke-width:1}
.fc svg.chart .vgrid{stroke:var(--fc-grid-2);stroke-width:1;stroke-dasharray:2 3}
.fc svg.chart .tline{fill:none;stroke:var(--fc-hi);stroke-width:2;
                     stroke-linejoin:round;stroke-linecap:round}
.fc svg.chart .tdot{fill:var(--fc-hi)}
/* The 7 Day chart's two halves.  What the station RECORDED takes a neutral
   gray and what NWS predicts keeps the forecast red, which is the split the
   page is about -- and the record is deliberately the QUIETER of the two,
   one step down the ink ladder and thinner, because this is a forecast page
   and the week ahead is its subject.  The seam between them is the most
   prominent structural line the page has.  Every value here is an existing
   token, so the dark palette needs nothing derived for this.  .adot is a
   lone reading with an outage on both sides: a broken stroke draws nothing
   at all for a single point. */
.fc svg.chart .aline{fill:none;stroke:var(--fc-muted);stroke-width:1.6;
                     stroke-linejoin:round;stroke-linecap:round}
.fc svg.chart .adot{fill:var(--fc-muted)}
/* --fc-axis, the chart's mark color for structure a reader has to find: the
   rule runs the full height of the chart, across the night shading, and its
   token clears the mark bar on both grounds. */
.fc svg.chart .seam{stroke:var(--fc-axis);stroke-width:1.5}
.fc svg.chart .dline{fill:none;stroke:var(--fc-dew);stroke-width:1.6;
                     stroke-dasharray:4 3;stroke-linejoin:round}
.fc svg.chart .parea{fill:var(--fc-rain);opacity:.8}
.fc svg.chart .ylab{fill:var(--fc-muted);font-size:10px;text-anchor:end;
                    font-family:'Open Sans',arial,sans-serif}
.fc svg.chart .xlab{fill:var(--fc-muted);font-size:11px;text-anchor:middle;
                    font-family:'Open Sans',arial,sans-serif}
.fc svg.chart .striplab{fill:var(--fc-muted);font-size:10px;letter-spacing:.09em;
                        text-transform:uppercase;
                        font-family:'Open Sans',arial,sans-serif}
.fc svg.chart .peak{text-anchor:end;text-transform:none;letter-spacing:0;font-style:italic}
/* ---- the two chips that name the 7 Day chart's halves ----------------
   PAST n DAYS ACTUAL and FORECASTED TEMPERATURES, one filled rectangle each
   side of the rule, matched in width with the words centered in them.  Each
   box carries its own half's curve color -- --fc-muted is .aline, --fc-hi is
   .tline -- so the label IS the swatch.
   MARKUP, NOT SVG <text>, and NWSSkin's CHIP_GAP comment carries the whole
   argument: a filled box must be exactly as wide as the words in it, and the
   words, the type size and the FACE all vary, so the browser sizes the box
   and nothing computes a width.  It is also the only way the words are
   readable AT ALL by a screen reader -- the chart carries role="img", which
   hides every <text> inside it. */
.fc .seamlegend{position:absolute;pointer-events:none;
                /* Centered on the rule, and vertically on the middle of the
                   chart's own label band -- which arrives as a bare number of
                   viewBox units from the code that opened the band, so the
                   band's depth is stated once, there.  The px value is the
                   fallback for a browser without container queries. */
                top:20px;top:calc(100cqw * var(--band-mid) / 1040);
                left:var(--seam);transform:translate(-50%,-50%);
                /* EQUAL WIDTHS WITHOUT MEASURING ANYTHING.  Two 1fr columns
                   under a max-content container both take the max-content
                   width of the WIDER item, so the boxes match and the text
                   centers in each -- and the browser, not the markup, is what
                   knows how wide the words are.  The pair straddles the rule
                   with a gutter of --chip-gap units, so each chip stands
                   CHIP_GAP clear of it.
                   THE GAP ARRIVES AS A BARE NUMBER of viewBox units and is
                   converted here.  It cannot be a percentage: a percentage gap
                   inside a max-content grid is measured against a width that
                   is not yet known, and resolves to zero.  And it cannot be
                   written here as a constant -- NWSSkin owns it, because the
                   same number decides whether there is room for the pair. */
                display:grid;grid-template-columns:1fr 1fr;width:max-content;
                column-gap:16px;column-gap:calc(100cqw * var(--chip-gap) / 1040)}
/* The chip type is sized in the CHART'S OWN units -- 100cqw of the wrapper is
   1040 of them -- so it tracks the chart's other labels as the page narrows,
   which an em ladder rooted in the page's 16px cannot do.
   13, not the 10 .striplab takes: these two are the KEY to the chart, and
   without them a reader cannot tell which half is a measurement and which is
   a prediction, so they outrank every other label on the plot.
   line-height:1 is load-bearing, not tidiness: at `normal` the fallback face
   gives about 1.36, which takes the narrow-screen chip off the top of the
   viewBox and down into the plot at once.  With it the box is 1.6em tall,
   whatever the face. */
.fc .seamchip{font-size:13px;font-size:calc(100cqw * 13 / 1040);
              font-family:'Open Sans',arial,sans-serif;font-weight:600;
              letter-spacing:.06em;white-space:nowrap;text-align:center;
              line-height:1;padding:.3em 1.05em;color:var(--fc-on-accent);
              background:var(--fc-muted)}
.fc .seamchip.fcast{background:var(--fc-hi)}
/* THE READOUT CLEARS THE CHIP BAND, and only on the chart that has one.  The
   readout is anchored top:0 right:0 of the same wrapper and is sized in PAGE
   pixels, while the chips are sized in the chart's units -- two ladders that
   cross, so whether they collide depends on the width.  They do not at 1280;
   at 800 the readout covered the forecast chip by 69x20, hiding the label
   this change exists to add exactly while the reader is working the chart.
   40 is the band: 12 units of plot padding plus NWSSkin.CHIP_BAND, which owns
   the number -- tests/test_nwsskin.py fails if the two drift apart.  The
   readout then starts at the top of the plot, where it has always been
   content to sit on the curve for as long as a pointer is down. */
.fc .twoweek .readout{top:calc(100cqw * 40 / 1040)}
/* The crosshair is hidden by a CLASS, not by the html `hidden` attribute:
   `hidden` is not valid on an SVG <g> (Nu rejects it) and the UA's [hidden]
   rule does not reach SVG children anyway, so it was never doing the hiding.
   Without this rule the crosshair paints at (0,0) whenever nothing is being
   pointed at. */
.fc .cross.off{display:none}
.fc .cross .cx{stroke:var(--fc-muted);stroke-width:1;stroke-dasharray:3 3}
.fc .cross .cdot{fill:var(--fc-hi);stroke:var(--fc-surface);stroke-width:1.4}
/* Over the observed half the dot follows that half's line, so the crosshair
   never paints a forecast-colored mark on a measurement. */
.fc .cross.past .cdot{fill:var(--fc-muted)}
.fc .cross .cdotd{fill:var(--fc-dew);stroke:var(--fc-surface);stroke-width:1.2}
.fc .readout{position:absolute;top:0;right:0;background:var(--fc-surface);
             border:1px solid var(--fc-rule);border-radius:5px;padding:6px 10px;
             font-size:.8em;display:flex;gap:12px;align-items:baseline;
             font-variant-numeric:tabular-nums;pointer-events:none;
             box-shadow:0 1px 4px rgba(0,0,0,.08)}
/* A class with display:flex outranks the UA's [hidden] rule. */
.fc .readout[hidden]{display:none}
.fc .readout b{color:var(--fc-ink-3);font-weight:600}
.fc .readout .ro-t{color:var(--fc-hi);font-weight:600}
.fc .readout .ro-d{color:var(--fc-dew)}
.fc .readout .ro-r{color:var(--fc-rain)}
.fc .readout .ro-o{color:var(--fc-ink-3)}
.fc .readout .ro-x{color:var(--fc-faint)}
.fc .legend{display:flex;flex-wrap:wrap;gap:6px 22px;color:var(--fc-muted);
            font-size:.8em;margin-bottom:10px}
.fc .legend span{display:flex;align-items:center;gap:7px}
.fc .legend i{width:16px;height:3px;display:inline-block;border-radius:2px}
.fc .legend .sw-t{background:var(--fc-hi)}
/* Dashed, because the line is dashed. */
.fc .legend .sw-d{background:none;border-top:3px dashed var(--fc-dew);height:0}
.fc .legend .sw-r{background:var(--fc-rain);height:10px;border-radius:2px;opacity:.8}
.fc .legend .sw-n{background:var(--fc-band);height:12px;border:1px solid var(--fc-hair)}

/* ---- Hourly --------------------------------------------------------- */
.fc .hcards{display:grid;grid-template-columns:repeat(12,1fr);gap:6px}
.fc .hcard{text-align:center;padding:8px 2px;border-radius:6px;background:var(--fc-tint)}
.fc .hcard .ht{color:var(--fc-muted);font-size:.76em;letter-spacing:.03em}
.fc .hcard .wxi{width:42px;height:42px;margin:6px auto 2px}
.fc .hcard .htemp{font-size:1.1em;font-weight:600;font-variant-numeric:tabular-nums}
.fc .hcard .hpop{font-size:.74em;margin-top:2px;min-height:1em}
.fc .daytabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px}
/* box-sizing and min-width give every tab the same width.  Without them the
   row is ragged: "Today" is wider than a three-letter weekday, and even the
   weekdays differ by a pixel or two on glyph widths alone, which reads as
   sloppy rather than as deliberate.  5.5em clears the widest label at both
   the 16px and 15px base sizes this skin uses. */
.fc .daytab{font:inherit;cursor:pointer;background:var(--fc-tint-2);color:var(--fc-ink-3);
            border:1px solid var(--fc-grid-2);border-radius:6px;padding:7px 16px;
            line-height:1.15;text-align:center;box-sizing:border-box;min-width:5.5em}
.fc .daytab .dt1{display:block;font-weight:600;font-size:.9em}
.fc .daytab .dt2{display:block;color:var(--fc-muted);font-size:.72em}
.fc .daytab.on{background:var(--fc-accent);
                                     border-color:var(--fc-accent);color:var(--fc-on-accent)}
/* One on-accent color, not two.  The muted second tone reached only
   3.64 on the accent fill at .72em, and the lightest value that passes
   is indistinguishable from white -- so the differentiation comes from
   size and weight, which it already did. */
.fc .daytab.on .dt2{color:var(--fc-on-accent)}
.fc .daypane.off{display:none}
.fc .daysun{color:var(--fc-muted);font-size:.8em;margin-bottom:10px}
.fc .hhead,.fc .hrow{display:grid;
  grid-template-columns:70px 34px 52px 1fr 52px 56px 58px 96px;gap:12px;
  align-items:center;padding-right:16px}
.fc .hhead{color:var(--fc-muted);font-size:.72em;letter-spacing:.08em;
           text-transform:uppercase;padding-bottom:8px;
           border-bottom:1px solid var(--fc-line)}
.fc .hhead .chr{text-align:right}
.fc .hrow{padding-top:8px;padding-bottom:8px;border-bottom:1px solid var(--fc-hair-2)}
.fc .hrow.nightrow{background:var(--fc-tint)}
.fc .hr-t{color:var(--fc-ink-3);font-size:.86em;font-variant-numeric:tabular-nums}
.fc .hr-i .wxi{width:34px;height:34px}
.fc .hr-f{font-size:.84em;min-width:0}
.fc .hr-n{text-align:right;font-size:.9em;font-variant-numeric:tabular-nums}
.fc .hr-n.dim{color:var(--fc-faint)}
.fc .hr-w{text-align:right;font-size:.84em;color:var(--fc-muted);
          font-variant-numeric:tabular-nums;line-height:1.35}
.fc .hr-w .w,.fc .hr-w .wd{display:inline;font-size:1em}
.fc .hr-w .wd::before{content:" "}

/* ---- Alerts --------------------------------------------------------- */
.fc .acountline{margin:0;font-size:.95em;color:var(--fc-ink-3)}
.fc .alert{border-left:6px solid var(--fc-sev-unknown)}
/* .82em, not .7em: the dot sits inside .ahead and is sized in em with no
   font-size of its own, so it was scaled by the heading's default size along
   with everything else in that row.  Pinning .ahead to 1em fixed the text and
   would have quietly shrunk the dot from 13.1px to 11.2px; this keeps the
   rendered pixel and lets the rule say what it means. */
/* The severity, NAMED and colored, in the slot that used to hold a bare
   colored dot.  Outlined rather than filled so the one FILLED chip on the row
   stays the status badge -- two filled chips side by side read as one
   control, and they say different things.  The rail down the card's left edge
   now reinforces this rather than being the only place the severity lives. */
.fc .alert .sevchip{font-size:.72em;letter-spacing:.09em;text-transform:uppercase;
                    font-weight:600;padding:3px 9px;border-radius:11px;flex:none;
                    color:var(--fc-sev-unknown);
                    border:1px solid var(--fc-sev-unknown)}
.fc .sev-extreme{border-left-color:var(--fc-sev-extreme)}
.fc .sev-extreme .sevchip{color:var(--fc-sev-extreme);border-color:var(--fc-sev-extreme)}
.fc .sev-severe{border-left-color:var(--fc-sev-severe)}
.fc .sev-severe .sevchip{color:var(--fc-sev-severe);border-color:var(--fc-sev-severe)}
.fc .sev-moderate{border-left-color:var(--fc-sev-moderate)}
.fc .sev-moderate .sevchip{color:var(--fc-sev-moderate);border-color:var(--fc-sev-moderate)}
.fc .sev-minor{border-left-color:var(--fc-sev-minor)}
.fc .sev-minor .sevchip{color:var(--fc-sev-minor);border-color:var(--fc-sev-minor)}
/* What NWS recommends doing, in a reader's words.  Outlined and uncolored:
   the severity chip beside it carries the color, and the status badge stays
   the one filled chip on the row. */
.fc .alert .respchip{font-size:.72em;letter-spacing:.09em;text-transform:uppercase;
                     font-weight:600;padding:3px 9px;border-radius:11px;flex:none;
                     color:var(--fc-ink-3);border:1px solid var(--fc-rule)}
/* font-size:1em is load-bearing.  .ahead is a HEADING element, so without it
   the browser's default heading size leaks into every em inside -- the event
   name, the badge and the note are all sized relative to their parent, and
   they came out half again too big.  Pinning the row to the card's own size
   makes the three below mean what they say, whatever heading level the
   document outline ends up needing. */
.fc .ahead{display:flex;align-items:center;gap:12px;margin:0 0 10px;border:0;
           padding:0;flex-wrap:wrap;font-size:1em}
.fc .aevent{font-size:1.75em;font-weight:600;color:var(--fc-ink)}
.fc .badge{font-size:.72em;letter-spacing:.09em;text-transform:uppercase;
           font-weight:600;padding:4px 10px;border-radius:11px}
.fc .badge.on{background:var(--fc-sev-severe);color:var(--fc-on-accent)}
.fc .badge.soon{background:var(--fc-warn-bg);color:var(--fc-warn-ink);
                border:1px solid var(--fc-warn-line)}
.fc .badge.past{background:var(--fc-band);color:var(--fc-muted)}
.fc .anote{margin-left:auto;color:var(--fc-muted);font-size:.96em}
/* Left-aligned in a measured column, which is what retires the hand-rolled
   wrap_text() that inserted <br> at computed character positions. */
.fc .aline{margin:0;font-size:1.02em;line-height:1.5;max-width:74ch;color:var(--fc-ink-2)}
.fc .asub{margin:8px 0 0;font-size:.86em;color:var(--fc-muted);max-width:82ch;
          line-height:1.5}
/* The areas the alert covers.  A long list folds behind its count with no
   script: <details> does it. */
.fc .aarea{margin:8px 0 0;font-size:.86em;color:var(--fc-muted);max-width:82ch;
           line-height:1.5}
.fc details.aarea summary{cursor:pointer}
.fc details.aarea p{margin:4px 0 0}
.fc .awindow{display:flex;align-items:center;gap:12px;margin:16px 0;font-size:.8em;
             color:var(--fc-muted);font-variant-numeric:tabular-nums}
.fc .aw-t{flex:none}
.fc .aw-t i{color:var(--fc-faint)}
.fc .aw-bar{position:relative;flex:1;height:8px;border-radius:4px;background:var(--fc-grid)}
.fc .aw-fill{position:absolute;left:0;top:0;height:8px;border-radius:4px;
             background:var(--fc-grid-2)}
.fc .aw-now{position:absolute;top:-4px;width:3px;height:16px;
            background:var(--fc-sev-severe);margin-left:-1px}
.fc .asecs{border-top:1px solid var(--fc-grid);padding-top:14px}
/* Sized for the longest single word the live feed produces (THUNDERSTORMS);
   break-word is the backstop for the next unguessable one. */
.fc .asec{display:grid;grid-template-columns:152px 1fr;gap:16px;margin-bottom:12px}
.fc .ak{color:var(--fc-accent);font-weight:600;font-size:.76em;letter-spacing:.08em;
        text-transform:uppercase;padding-top:2px;overflow-wrap:break-word}
.fc .av{font-size:.92em;line-height:1.5;max-width:76ch}
/* Unlabeled prose -- three quarters of real alerts are nothing else -- runs
   full width at a measure clear of NWS's 58-68 character teletype wrap, so
   reflowed text reads as reflowed. */
.fc .alead{margin-bottom:12px}
.fc .aprose{margin:0 0 10px;font-size:.92em;line-height:1.55;max-width:76ch}
.fc .alead .aprose,.fc .alead .abul li{max-width:94ch}
.fc .aprose:last-child{margin-bottom:0}
.fc .abul{margin:6px 0 0;padding-left:18px}
.fc .abul li{font-size:.92em;line-height:1.5;margin-bottom:5px;max-width:74ch}
.fc .ado{background:var(--fc-tint);border-left:3px solid var(--fc-accent);
         padding:14px 16px;margin:6px 0 14px}
.fc .ado p{margin:6px 0 0;font-size:.92em;line-height:1.55;max-width:76ch}
/* One footer line, not two tables of CAP plumbing. */
.fc .ameta{margin:0;color:var(--fc-faint);font-size:.76em;line-height:1.6;
           border-top:1px solid var(--fc-grid);padding-top:10px}
.fc .allclear{display:flex;gap:20px;align-items:flex-start}
.fc .allclear .tick{width:44px;height:44px;flex:none;fill:none;stroke:var(--fc-ok);
                    stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.fc .acname{margin:0 0 8px;font-size:1.4em;font-weight:600;color:var(--fc-ok);
            border:0;padding:0}
.fc .acbody{margin:0 0 10px;font-size:.95em;line-height:1.6;max-width:70ch;
            color:var(--fc-ink-3)}


/* ---- the page and its navigation ------------------------------------
   These pages are standalone -- there is no site shell above them -- so the
   page ground, the heading and the nav bar all belong to this stylesheet.
   The nav is the only element that sits on --fc-page rather than on a card,
   which is why it carries its own tokens: its contrast is measured against a
   different ground from everything else here. */
html{color-scheme:light dark}
body{margin:0;background:var(--fc-page);color:var(--fc-ink);
     font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,
                 Arial,sans-serif}
.fc h1.ctitle{margin:0;font-size:2.1em;font-weight:600;color:var(--fc-accent);
              letter-spacing:-.01em;line-height:1.1}
.fc h1.ctitle .over{color:var(--fc-muted);font-weight:400}

.nav{max-width:1240px;margin:0 auto;padding:14px 20px 0}
.nav ul{list-style:none;display:flex;gap:6px;margin:0;padding:0}
.nav a{display:block;padding:10px 22px;text-decoration:none;font-size:.95em;
       font-weight:600;background:var(--fc-nav-bg);color:var(--fc-nav-ink);
       border:1px solid var(--fc-line);border-bottom:0}
.nav a:hover{background:var(--fc-nav-hover)}
.nav a.current{background:var(--fc-accent);color:var(--fc-on-accent);
               border-color:var(--fc-accent)}

/* ---- narrower viewports ---------------------------------------------
   One responsive page rather than a desktop/smartphone pair.  What changes
   is STRUCTURE -- multi-column rows become stacked ones, wide grids lose
   columns -- while the type stays at a readable size rather than being
   scaled up to suit a fixed zoomed canvas. */
@media (max-width: 880px) {
  .fc{padding:20px 14px 28px}
  .nav{padding:10px 14px 0}
  .nav ul{flex-wrap:wrap}
  .fc .chead{flex-direction:column;align-items:flex-start;gap:6px}
  .fc h1.ctitle{font-size:1.7em}
  .fc .nowgrid{grid-template-columns:88px 1fr;gap:16px}
  .fc .nowicon .wxi{width:88px;height:88px}
  .fc .nowstats{grid-column:1 / -1;border-left:0;border-top:1px solid var(--fc-rule);
                padding:14px 0 0;display:flex;flex-wrap:wrap;gap:12px 32px}
  .fc .stat{margin-bottom:0}
  /* The day row stops being three columns beside each other and becomes
     three bands stacked, which is the only way the forecast prose keeps a
     readable measure at this width. */
  .fc .colhead{display:none}
  .fc .day{grid-template-columns:1fr;gap:10px;padding:16px 0}
  .fc .when{display:flex;align-items:baseline;gap:12px}
  .fc .wd-name{display:inline}
  .fc .wd-date{display:inline}
  .fc .sun{text-align:left}
  .fc .line{grid-template-columns:40px 3.4em 1fr;gap:10px}
  /* Wind moves under the prose rather than off the edge. */
  .fc .line .chip:last-child{grid-column:2 / -1;text-align:left}
  .fc .asec{grid-template-columns:1fr;gap:2px}
  .fc .ahead{flex-wrap:wrap;gap:10px}
  .fc .anote{margin-left:0;width:100%}
  .fc .awindow{flex-wrap:wrap;justify-content:space-between}
  .fc .aw-bar{flex-basis:100%;order:3;margin-top:6px}
}

/* SVG text is in USER UNITS, so it shrinks with the chart.  The charts are
   drawn on a 1040-unit viewBox and stretched to the column width: at 880px
   that is roughly half scale and at 390px roughly a third, which takes a 10px
   axis label down to 3px.  These are the sizes that keep it readable, and
   they are the responsive replacement for the fixed-canvas twin the design
   came from -- which never hit this because it never actually got smaller. */
@media (max-width: 880px) {
  .fc svg.chart .ylab{font-size:17px}
  .fc svg.chart .xlab{font-size:19px}
  .fc svg.chart .striplab{font-size:17px}
  .fc .seamchip{font-size:18px;font-size:calc(100cqw * 18 / 1040)}
}

@media (max-width: 620px) {
  .fc{font-size:15px}
  .fc svg.chart .ylab{font-size:26px}
  .fc svg.chart .xlab{font-size:28px}
  .fc svg.chart .striplab{font-size:22px}
  /* 21, not the 22 .striplab takes: at 22 the pair clears the plot edge by
     nine viewBox units on a full seven-day archive, which is inside the
     font-substitution slop that clipped the axis in 6.1.1.  21 leaves 29.
     Bold uppercase reads at 21 as well as regular does at 22. */
  .fc .seamchip{font-size:21px;font-size:calc(100cqw * 21 / 1040)}
  /* The rain strip's 100/50/0 are only 16 viewBox units apart, so at a label
     size a phone can read they collide with each other and with the
     temperature scale.  They are the most inferable marks on the chart --
     the strip is captioned and annotated with its own peak -- so on a narrow
     screen they go and the temperature scale stays. */
  .fc svg.chart .rainlab{display:none}
  .fc .hcards{grid-template-columns:repeat(4,1fr);gap:5px}
  .fc .hcard .wxi{width:34px;height:34px}
  /* Dew point and humidity are the two columns a narrow hour table can
     lose: both are also drawn on the day chart directly above it. */
  .fc .hhead,.fc .hrow{grid-template-columns:3.4em 30px 3.2em 1fr 2.4em;gap:8px}
  .fc .hhead > :nth-child(6),.fc .hhead > :nth-child(7),
  .fc .hrow > :nth-child(6),.fc .hrow > :nth-child(7){display:none}
  .fc .hr-i .wxi{width:30px;height:30px}
  .fc .hr-w{display:none}
  .fc .hhead > :last-child{display:none}
  .fc .nowgrid{grid-template-columns:1fr}
  .fc .nowicon .wxi{width:72px;height:72px}
}

/* ---- dark ------------------------------------------------------------
   prefers-color-scheme only.  No toggle: a toggle needs javascript, storage
   and a visible control, and everyone who copies this skin would inherit a
   widget they may not want.  The reader's own setting is the whole input.

   DERIVED, NOT PICKED.  The objective is the icon palette's rule
   (bin/user/nwsicons.py) generalized: PROMINENCE IS MEASURED AGAINST THE
   GROUND A THING ACTUALLY SITS ON, AND A THING'S PROMINENCE FOLLOWS ITS
   MEANING -- severity for the alert rails, tier depth for the text ladder.
   Never absolute lightness.  Per token, hue and chroma are held and only
   lightness moves, so heat stays red and the accent stays blue.

   Which grounds each token lands on, and whether it is text or a mark
   there, is read from the RULES; tests/test_nws_css.py holds the map.
   Text clears WCAG 4.5 AND APCA Lc 60, whatever its size, and a mark
   clears WCAG 3.0 AND Lc 30.  On this card Lc 60 puts every light text
   color at CIE L* 73 or above, whatever its hue, and that floor -- not
   taste -- is why the text here is as light as it is.  Every rule is
   asserted over BOTH palettes, and light passes too: that is what shows
   the rules describe the design instead of having been fitted to the new
   numbers.  It also caught live light-mode failures, --fc-accent twice.

   Three results here are not what intuition suggests, and none is a bug:

   * THE TINTS SIT ABOVE THE CARD, not below it as in light.  A tint means
     "differentiated within a card"; in light it can only go darker because
     white is the gamut ceiling.  That was never a decision about depth.

   * --fc-on-accent INVERTS, white to a near-black navy.  The accent and
     severity fills are LIGHTER here than in light, so readable text on
     them is dark.  White on a pale blue tab would be the one unreadable
     thing left.

   * --fc-page is placed by perceptual distance from the card, not by
     lightness delta.  The card is already at L*12.6, so preserving light's
     delta clamped the page to pure black -- losing the relationship rather
     than preserving it.

   --fc-surface and --fc-tint are FIXED at these values.  nwsicons.DARK was
   derived against exactly those two grounds, and
   tests/test_nws_css.py::TestDarkSpecifics::test_the_icon_grounds_are_unmoved
   pins them; moving either invalidates every icon color below. */
@media (prefers-color-scheme: dark) {
:root {
  /* grounds */
  --fc-page:#000425;
  --fc-surface:#111834;
  --fc-tint:#151c38;
  --fc-tint-2:#1a213d;
  /* the ink ladder -- five tiers, evenly stepped in CIE L* */
  --fc-ink:#f5f5f5;
  --fc-ink-2:#e4e4e4;
  --fc-ink-3:#cad5e4;
  --fc-muted:#bac5d2;
  --fc-faint:#acb5c5;
  /* structural lines: seen, not read, so they get a prominence band rather
     than a contrast bar -- and none may out-shout the text it separates.
     --fc-axis is the one mark among them, and clears the mark bar. */
  --fc-line:#666a8c;
  --fc-hair:#2c314f;
  --fc-hair-2:#1f2542;
  --fc-rule:#3d4262;
  --fc-grid:#252a48;
  --fc-grid-2:#383d5c;
  --fc-axis:#767a9d;
  --fc-band:#1c2240;
  --fc-head-rule:#999cc1;
  /* chromatic text and marks */
  --fc-accent:#7cbbe8;
  --fc-link:#81b9ec;
  --fc-hi:#ff9690;
  --fc-lo:#b2aaff;
  --fc-rain:#82b8f7;
  --fc-dew:#75c1b2;
  --fc-ok:#76c491;
  --fc-on-accent:#131a36;
  /* alert severity -- prominence follows severity, which the first
     hand-picked dark palette inverted (an ordinary advisory out-shouting a
     warning) */
  --fc-sev-extreme:#ffbfb4;
  --fc-sev-severe:#ff9d81;
  --fc-sev-moderate:#ff9a52;
  --fc-sev-minor:#7bbbe6;
  --fc-sev-unknown:#aab5c2;
  /* the "begins later" badge */
  --fc-warn-bg:#2a2518;
  --fc-warn-line:#553a00;
  --fc-warn-ink:#dab072;
  /* the navigation, the only element that sits on the page ground */
  --fc-nav-bg:#111834;
  --fc-nav-ink:#cad5e4;
  --fc-nav-hover:#1a213d;

  /* The drawn forecast icons.  Their LIGHT defaults live in
     bin/user/nwsicons.py and are already what this page wants, so they are
     deliberately NOT restated here -- a copy could only drift from the
     module.  These are the dark counterparts, and they ARE the module's own
     nwsicons.DARK, derived against --fc-surface #111834 rather than
     guessed.  tests/test_nws_css.py pins this list to the module -- one test
     for a drifted value or a stale property name, another for a light
     default restated here rather than left to the module.

     THE CLOUD RAMP IS INVERTED HERE AND THAT IS DELIBERATE: --wx-cloud, the
     FRONT cloud, is the darkest of the three and --wx-cloud-3 the lightest,
     the opposite of light mode.  In light the BACK cloud is the more
     prominent one, so reproducing that relationship on a dark ground
     requires the back cloud to be lighter.  Do not "fix" it.

     --wx-eye is deliberately absent: the cyclone's center is not painted at
     all, so the card shows through it on any ground. */
  --wx-bolt:#E1A904; --wx-cloud:#525963; --wx-cloud-2:#85909E;
  --wx-cloud-3:#B0BCCB; --wx-cold:#7FBFFF; --wx-dust:#CAA369;
  --wx-fog:#A2AAB7; --wx-hot:#FF7361; --wx-moon:#A1AAB8;
  --wx-rain:#7FBFFF; --wx-sleet:#8DC4FE; --wx-smoke:#A2AAB7;
  --wx-snow:#659ECD; --wx-sun:#F2B704; --wx-sunray:#E1A904;
  --wx-swirl:#C5D3E5; --wx-swirl-2:#9DA8B5; --wx-swirl-3:#7B838C;
  --wx-wind:#C5D3E5;
  --wx-op-band:.74; --wx-op-tube:.38;
}
}
