/* TTM raw water dashboard — overview page.
 *
 * ---------------------------------------------------------------------------
 * DESIGN INTENT (written before the code, per the interface-design process)
 *
 * WHO. The Kepala Bagian Teknik at IPA II Panaikang, on a phone at the intake
 * or a shared screen in the control room, between the morning meter reading
 * and the shift handover. He wants one answer: is the river still giving us
 * water, and how far off the sheet are we today.
 *
 * DOMAIN, the plant's world, not the software's:
 *   - the intake weir at Lekopancing on the Maros, and the raw-water channel
 *   - the totalizer dial: a cumulative meter read once a day, today's volume
 *     is the difference between two readings, in tabular digits
 *   - the log sheet (laporan harian): ruled columns, one line per day,
 *     pencilled figures, a date at the top
 *   - the staff gauge: painted graduations on the reservoir wall, the level
 *     read against a rule
 *   - the dry-season band: August to October, when the river falls under the
 *     demand line and the whole year is decided
 *   - Q80, debit andalan, the dependable flow the river keeps four years in five
 *
 * COLOUR WORLD: the RAW system, docs/brand/raw-brand-system.md. The
 * dashboard and the deck are read by the same people and must be recognised
 * as the same organisation:
 *   - navy #004770 is the structure: headings, rules, the current tab, links
 *   - gold #CE7F0C is the accent and is spent once per view, on the thing
 *     that matters; on the Overview that is this year's abstraction dots
 *   - red #980000 and green #38761D are semantic, bad and good, nothing else
 *   - grey #888888 for captions and the footer, as on every slide
 *   - the ground is a warm paper white; in dark the navy is the ground
 *   - Montserrat throughout, figures set tabular
 *
 * SIGNATURE for the Overview: THE MARK ON THE RULE. Every reading is hung
 * under a graduated rule and its condition is a painted tick on that rule,
 * the way a staff gauge marks a level, never a box around it. It appears in:
 *   1. the instrument strip: one rule, six readings hung under it, the
 *      severity is a coloured tick standing on the rule above each figure
 *   2. the sparkline: the last reading is a tick dropped to the baseline
 *   3. the hero's range track: the handles are ticks on the hydrograph
 *   4. the scenario scale: a tick at the 2023 mean on every bar
 *   5. the severity pills: a tick before the word, not a filled badge
 *   6. the section heads: an ink rule with the heading hung off it
 *
 * DEFAULTS REPLACED:
 *   - the six-tile metric card grid → one ruled instrument strip
 *   - the verdict card above the chart → the chart leads, full width; the
 *     verdict is its headline sentence, set in the house pattern (a full
 *     sentence with the figures in bold)
 *   - uniform bordered/rounded sections → flat ruled sheets; border, radius
 *     and shadow are each spent once, by role: radius on popovers, shadow on
 *     the options menu, a box only on the map
 *
 * CHROME vs MARKS. Navy is the chrome: ground rules, ink, the underline that
 * means "you are here". Data marks use the validated RAW chart steps the deck
 * uses (lifted navy, gold, lifted red), so a reader who has seen the deck
 * recognises the chart. Semantic state (--good/--warn/--crit) only ever says
 * what condition something is in, always beside a word or a shape.
 * ---------------------------------------------------------------------------
 */

/* The glass layer: surfaces, popovers, motion and the phone rules. Kept in
   its own file so the recipe is one place; build_static.py inlines it. */
@import url("glass.css");

/* Montserrat, self-hosted: one variable latin file (35 KB, weights 100 to
   900), swapped in when it lands so the text never waits on it. Fetched from
   Google Fonts on 16 Sep 2026; SIL Open Font License. */
@font-face {
  font-family:"Montserrat"; font-style:normal; font-weight:100 900; font-display:swap;
  src:url("fonts/montserrat-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

/* ---------- tokens: the RAW system ---------- */
/* docs/brand/raw-brand-system.md. Navy is the structure, gold is the accent
   and is spent once per view, red and green say bad and good and nothing
   else. The structural names the layout rules use (--edge, --grid, --paper)
   are aliases onto that system so a rule reads as a role, not a hex. */
:root {
  color-scheme: light dark;

  /* brand */
  --navy:#004770; --navy-deep:#1C466D; --navy-mid:#275C8D; --navy-bright:#005E91;
  --gold:#CE7F0C; --bronze:#C38330; --grey:#5A6B77; --grey-dark:#3F3F3F;

  /* ground and ink */
  --paper:#FBF9F4; --paper-2:#FFFFFF; --paper-3:#F1EDE4;
  /* Captions: 5.7:1 on white, 4.9:1 on the sunk paper (scripts/contrast_check.py).
     --gold-ink is the gold where it is text or a button fill (5.8:1 with
     white on it); --gold-on-navy the gold on the navy report cover (4.9:1).
     Marks and rules keep --gold itself. */
  --ink:#11212B; --ink-soft:#47606F; --ink-faint:#52697C;
  --gold-ink:#915805; --gold-on-navy:#EFA84A;
  /* Ink on a navy, gold, red or green fill: white in light; in dark those
     fills are lifted, so the ground is the ink (6:1 and better). */
  --on-navy:#FFFFFF;
  --rule:rgba(0,71,112,.24);
  --edge:rgba(0,71,112,.12); --edge-soft:rgba(0,71,112,.07);
  --edge-firm:var(--rule); --edge-ink:rgba(0,71,112,.55); --grid:rgba(0,71,112,.07);
  --dry:rgba(0,71,112,.08);

  /* Chart marks. The brand navy fails as a fill (raw-brand-system.md §2), so
     the marks use the validated steps the deck uses: a lifted navy, the gold
     unchanged, a lifted red for the bad case. Same three in both themes. */
  --s1:#1173A3; --s2:#CE7F0C; --s3:#B0342A; --s1-wash:rgba(17,115,163,.12);

  /* Semantic: only where the sign is the point. Watch is a navy step so the
     only non-navy tick on the rule is red. */
  --good:#38761D; --warn:#275C8D; --crit:#980000;
  --good-wash:rgba(56,118,29,.10); --warn-wash:rgba(39,92,141,.10); --crit-wash:rgba(152,0,0,.08);
  --flat:var(--grey); --flat-wash:rgba(136,136,136,.12);

  /* Montserrat is the face. Figures are Montserrat with tabular figures; the
     brand has no mono. */
  --sans:"Montserrat",ui-sans-serif,-apple-system,"Segoe UI",Calibri,Arial,sans-serif;
  --num:var(--sans);
  --cond:var(--sans);

  /* Aliases for the names dashboard/build_static.py's SVG map still reads
     with getPropertyValue and writes into SVG attributes (hex, not var()). */
  --ink-mute:#52697C; --rule-soft:rgba(0,71,112,.07);
  --surface:#FFFFFF; --sunk:#F1EDE4;
  --tip-bg:#004770;
}

/* Dark: the navy becomes the ground, the gold stays the single accent, the
   structural navy is lifted so it still reads as a colour on its own ground.
   Written twice so an explicit theme stamp wins in both directions while the
   un-stamped default follows the OS. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --navy:#8DC0E0; --navy-deep:#A9CFE6; --navy-mid:#6FA8D6; --navy-bright:#5FB0E0;
    --gold:#E0913A; --bronze:#CF9A4E; --grey:#8FA3B2; --grey-dark:#C4D2DC;
    --paper:#06233A; --paper-2:#0B2D48; --paper-3:#123A58;
    --ink:#E8EEF2; --ink-soft:#B4C6D3; --ink-faint:#8FA9BC; --gold-ink:#E0913A; --gold-on-navy:#EFA84A; --on-navy:#06233A;
    --rule:rgba(232,238,242,.26);
    --edge:rgba(232,238,242,.13); --edge-soft:rgba(232,238,242,.08);
    --edge-ink:rgba(232,238,242,.6); --grid:rgba(232,238,242,.08);
    --dry:rgba(141,192,224,.10);
    --s1:#3E9AD0; --s2:#E0913A; --s3:#E0645A; --s1-wash:rgba(62,154,208,.16);
    --good:#7CC95A; --warn:#6FA8D6; --crit:#F07A72;
    --good-wash:rgba(124,201,90,.14); --warn-wash:rgba(111,168,214,.14); --crit-wash:rgba(240,122,114,.14);
    --flat-wash:rgba(143,163,178,.16);
    --ink-mute:#8FA9BC; --rule-soft:rgba(232,238,242,.08);
    --surface:#0B2D48; --sunk:#123A58;
    --tip-bg:#1C466D;
  }
}
:root[data-theme="dark"] {
  --navy:#8DC0E0; --navy-deep:#A9CFE6; --navy-mid:#6FA8D6; --navy-bright:#5FB0E0;
  --gold:#E0913A; --bronze:#CF9A4E; --grey:#8FA3B2; --grey-dark:#C4D2DC;
  --paper:#06233A; --paper-2:#0B2D48; --paper-3:#123A58;
  --ink:#E8EEF2; --ink-soft:#B4C6D3; --ink-faint:#8FA9BC; --gold-ink:#E0913A; --gold-on-navy:#EFA84A; --on-navy:#06233A;
  --rule:rgba(232,238,242,.26);
  --edge:rgba(232,238,242,.13); --edge-soft:rgba(232,238,242,.08);
  --edge-ink:rgba(232,238,242,.6); --grid:rgba(232,238,242,.08);
  --dry:rgba(141,192,224,.10);
  --s1:#3E9AD0; --s2:#E0913A; --s3:#E0645A; --s1-wash:rgba(62,154,208,.16);
  --good:#7CC95A; --warn:#6FA8D6; --crit:#F07A72;
  --good-wash:rgba(124,201,90,.14); --warn-wash:rgba(111,168,214,.14); --crit-wash:rgba(240,122,114,.14);
  --flat-wash:rgba(143,163,178,.16);
  --ink-mute:#8FA9BC; --rule-soft:rgba(232,238,242,.08);
  --surface:#0B2D48; --sunk:#123A58;
  --tip-bg:#1C466D;
}

/* ---------- frame ---------- */
/* One 8 px scale for the whole shell: the bar is 56, the gutters 24 and 12,
   the card padding 16, the card radius 4. The content column is 1440 wide;
   a control-room screen is 1920 and the sheet should not run edge to edge. */
* { box-sizing:border-box }
html { -webkit-text-size-adjust:100% }
body {
  margin:0; background:var(--paper); color:var(--ink);
  font:14px/1.5 var(--sans); font-feature-settings:"tnum" 1, "cv11" 1;
  -webkit-font-smoothing:antialiased;
  /* No overflow on body: with html already clipping sideways (glass.css),
     an overflow on body too makes body its own scroll container and the
     sticky bar then never sticks. The clip on html is enough. */
}
img { max-width:100% }
/* Glancing is the job, so every figure is tabular. */
.n, .v, table { font-family:var(--num); font-variant-numeric:tabular-nums }
a { color:var(--navy) }
a:focus-visible, button:focus-visible, summary:focus-visible {
  outline:2px solid var(--navy); outline-offset:2px;
}
.mute { color:var(--ink-faint) }
.lab, .k { font-family:var(--cond); font-size:.7rem; letter-spacing:.06em;
           text-transform:uppercase; color:var(--ink-faint); font-weight:600 }
/* Headings take the brand navy, as on every slide. */
h1, h2, h3 { color:var(--navy) }
.wrap, .page { max-width:1440px; margin:0 auto; padding:16px 12px 40px; min-width:0 }
@media (min-width:768px) { .wrap, .page { padding:16px 24px 48px } }
.page-narrow { max-width:48rem }

/* ---------- the bar ---------- */
/* One 56 px line: the mark, the plant and the page, the pages as tabs, and
   on the right the date of the sheet in front of you, the language and the
   latch. The current tab is the navy underline; nothing else in the bar is
   coloured, so the gold stays free for the content. */
.top { position:sticky; top:0; z-index:30; background:var(--paper-2);
       border-bottom:1px solid var(--edge) }
.top-in { max-width:1440px; margin:0 auto; padding:0 24px; height:56px;
          display:flex; align-items:stretch; gap:16px }
.top-brand { display:flex; align-items:center; gap:12px; flex:none; max-width:40%;
             text-decoration:none; color:var(--navy) }
.top-brand .logo { height:28px; width:auto; display:block; flex:none }
:root[data-theme="dark"] .logo { background:#fff; padding:2px 4px; border-radius:3px }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .logo { background:#fff; padding:2px 4px; border-radius:3px }
}
.top-title { font:600 14px/1.2 var(--sans); letter-spacing:-.01em; color:var(--navy);
             white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0 }
.top-title .pg { color:var(--ink-soft); font-weight:500 }
.top-title .pg:not(:empty)::before { content:"·"; margin:0 6px; color:var(--ink-faint) }
.top-nav { display:flex; align-items:stretch; gap:4px; flex:1 1 auto; min-width:0; overflow-x:auto;
           scrollbar-width:none; -webkit-overflow-scrolling:touch }
.top-nav::-webkit-scrollbar { display:none }
.top-nav a { display:flex; align-items:center; padding:0 10px; white-space:nowrap;
             font:600 12px/1 var(--sans); letter-spacing:.06em; text-transform:uppercase;
             color:var(--ink-soft); text-decoration:none;
             border-top:2px solid transparent; border-bottom:2px solid transparent }
.top-nav a:hover { color:var(--ink) }
.top-nav a[aria-current="page"] { color:var(--navy); border-bottom-color:var(--navy) }
.top-nav a:focus-visible { outline-offset:-2px }
.top-side { margin-left:auto; display:flex; align-items:center; gap:12px; flex:none }
.pill { display:inline-flex; align-items:center; gap:5px; height:28px; padding:0 10px;
        border:1px solid var(--edge-firm); border-radius:4px; white-space:nowrap;
        font:500 12px/1 var(--sans); color:var(--ink-soft) }
.pill b { font-weight:600; color:var(--ink) }
.pill:has(b:empty) { display:none }
.lang { display:inline-flex; border:1px solid var(--edge-firm); border-radius:4px; overflow:hidden }
.lang button { background:none; border:0; height:26px; padding:0 8px; cursor:pointer;
               font:600 11px/1 var(--sans); letter-spacing:.06em; color:var(--ink-faint) }
.lang button:hover { color:var(--ink) }
.lang button.on { background:var(--navy); color:var(--paper-2) }
.lang button:focus-visible { outline-offset:-2px }
/* The latch: a dot and a word. Unlocked is a form whose only control is the
   Lock link; locked is a link to the code page that comes back here. */
.lockstate { display:inline-flex; align-items:center; gap:6px; margin:0;
             font:500 12px/1 var(--sans); color:var(--ink-soft); text-decoration:none; white-space:nowrap }
.lockstate i { width:8px; height:8px; border-radius:50%; background:var(--good); flex:none }
.lockstate.locked i { background:var(--ink-faint) }
.lockstate button { background:none; border:0; padding:0 0 0 6px; margin:0; cursor:pointer;
                    font:600 12px/1 var(--sans); color:var(--navy); text-decoration:underline;
                    text-underline-offset:2px; border-left:1px solid var(--edge-firm); padding-left:8px }
.lockstate button:hover { color:var(--ink) }
/* Between 1024 and 1279 the bar keeps one row and drops the words beside
   the date and the dot; below 1024 it is two rows: 48 for the mark and the
   controls, 40 for the tabs, which scroll sideways in one strip. */
@media (max-width:1279px) {
  .pill > span:first-child { display:none }
  .lockstate > span { display:none }
  .top-nav a { padding:0 8px }
}
@media (max-width:1023px) {
  .top-in { flex-wrap:wrap; height:auto; padding:0 12px; gap:0 12px }
  .top-brand { height:48px; flex:1 1 auto; min-width:0; max-width:none; order:1 }
  .top-brand .logo { height:24px }
  .top-title .pg { display:none }
  .top-side { order:2; gap:8px }
  .top-nav { order:3; flex:1 0 100%; height:40px; margin:0 -12px; padding:0 12px;
             border-top:1px solid var(--edge-soft) }
  .pill { padding:0 8px; height:26px }
  .lockstate button { padding-left:6px }
}
@media (max-width:479px) { .pill { display:none } }

/* ---------- the meta line under the page heading ---------- */
/* Days on record, period, series, in view, reports read: one muted 12 px
   line inside the content, never in the bar. */
.pmeta { margin:0 0 12px; display:flex; flex-wrap:wrap; gap:2px 16px; min-height:0;
         font:12px/1.5 var(--sans); color:var(--ink-faint); font-variant-numeric:tabular-nums }
.pmeta > * { display:inline-flex; gap:4px; align-items:baseline }
.pmeta > *:empty { display:none }
.pmeta b { font-weight:600; color:var(--ink-soft) }
.pmeta:not(:has(> :not(:empty))) { display:none }
.page-h { margin:0 0 4px; font:700 18px/1.25 var(--sans); letter-spacing:-.012em; color:var(--navy) }
.page-lede { margin:0 0 12px; font-size:14px; color:var(--ink-soft); max-width:70ch }

/* ---------- cards ---------- */
/* Every block is a card: 1 px edge, 4 px radius, 16 px inside. The card
   heading is 13 px navy caps; the body is the 14 px base. */
.card { background:var(--paper-2); border:1px solid var(--edge); border-radius:4px; padding:16px; min-width:0 }
.card-h, .shead h2, .strip-head h2, .act-sec h3, .up-head h2, .up-preview .shead h2, .up-history .shead h2 {
  margin:0; font:600 13px/1.3 var(--sans); letter-spacing:.06em; text-transform:uppercase; color:var(--navy) }
.card + .card { margin-top:16px }
/* Tables scroll inside their card. The page body never does. */
.card table, .card .table-scroll, .tbl-wrap { max-width:100% }
.tbl-wrap, .table-scroll, .chart-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch }

/* ---------- the hero: the river against demand, and what it says ---------- */
.hero { padding:16px; margin:16px 0 0; background:var(--paper-2); border:1px solid var(--edge); border-radius:4px }
.hero-head { display:flex; gap:.6rem 2rem; align-items:flex-start }
.verdict { flex:1 1 auto; min-width:0 }
.verdict .head { display:flex; flex-wrap:wrap; gap:.3rem .8rem; align-items:baseline }
/* The house headline: one full sentence, the figures in bold inside it. */
.verdict .say { margin:.35rem 0 0; font-size:1.3rem; line-height:1.3; font-weight:400;
                letter-spacing:-.01em; max-width:38ch; text-wrap:balance; color:var(--navy) }
@media (min-width:56rem) { .verdict .say { font-size:1.4rem; max-width:44ch } }
.verdict .say b { font-family:var(--num); font-weight:700; letter-spacing:-.02em }
.verdict ul { margin:.55rem 0 0; padding:0; list-style:none;
              display:flex; flex-wrap:wrap; gap:.15rem 1.4rem }
.verdict li { font-size:.8rem; color:var(--ink-soft); display:flex; gap:.45rem; align-items:baseline }
.verdict li::before { content:""; width:1px; height:.7em; background:var(--edge-firm); flex:none;
                      align-self:center }
.hero-tools { position:relative; margin-left:auto; padding-top:.15rem }
.hero-title { margin:1rem 0 .2rem; font-family:var(--cond); font-size:.72rem; letter-spacing:.08em;
              text-transform:uppercase; color:var(--ink-faint); font-weight:600 }
.hero-plot { border-top:1px solid var(--edge-ink); padding-top:.4rem }
.caption { margin:.7rem 0 0; font-size:.875rem; line-height:1.5; color:var(--ink); max-width:78ch }

/* ---------- the monitoring grid ---------- */
/* Twelve columns on an 8 px scale. Every block on the Overview sits on it:
   the ticker cards span three columns (two on a phone), the analysis
   table seven and its reading list five. Borders are 1 px, nothing is
   filled with a gradient, and the only accent is the gold on the one card
   that needs the eye. */
.grid12 { display:grid; grid-template-columns:repeat(12,minmax(0,1fr)); gap:16px }
@media (max-width:48rem) { .grid12 { gap:8px } }
.span3 { grid-column:span 3 } .span5 { grid-column:span 5 } .span6 { grid-column:span 6 } .span7 { grid-column:span 7 }
@media (max-width:68rem) { .span3 { grid-column:span 4 } }
@media (max-width:48rem) { .span3 { grid-column:span 6 } .span5, .span7 { grid-column:span 12 } }

/* ---------- the ticker strip ---------- */
.strip { margin:0 }
/* Nine ticker cards on the 12-column grid, one dense block with no empty
   cell: treated water (the hero) takes four columns over two rows, the
   other eight take two columns each, four to a row. Between 48 and 68rem
   the hero takes six columns over two rows and the rest three each; on a
   phone the hero is full width and the rest two to a row. */
.ticker { grid-auto-rows:auto; align-items:stretch }
.ticker .tk { grid-column:span 2 }
.ticker .tk.hero { grid-column:span 4; grid-row:span 2 }
@media (max-width:68rem) { .ticker .tk { grid-column:span 3 } .ticker .tk.hero { grid-column:span 6 } }
@media (max-width:48rem) { .ticker .tk { grid-column:span 6 } .ticker .tk.hero { grid-column:span 12; grid-row:auto } }
.strip-head { display:flex; gap:4px 24px; align-items:baseline; flex-wrap:wrap; margin:0 0 8px }
.strip-head .help { margin:0; font-size:.78rem; color:var(--ink-faint); max-width:80ch }
.tk { border:1px solid var(--edge); background:var(--paper-2); padding:16px; min-width:0; border-radius:4px;
      display:flex; flex-direction:column; gap:4px; border-top:3px solid var(--navy) }
@media (max-width:48rem) { .tk { padding:12px } .tk .v { font-size:1.6rem } .tk .v small { display:block; margin:2px 0 0 } }
.tk.tone-good { border-top-color:var(--good) }
.tk.tone-bad { border-top-color:var(--crit) }
/* The one card the eye must find: the salinity card while the seven-day
   mean is running above the thirty-day mean. */
.tk.accent { border-color:var(--gold); border-top-color:var(--gold); box-shadow:inset 0 0 0 1px var(--gold) }
.tk .k { line-height:1.25; min-height:2.2em; display:flex; align-items:flex-end; white-space:normal; overflow-wrap:anywhere }
.tk .k > span { min-width:0 }
.tk .v { font:800 1.9rem/1.05 var(--num); letter-spacing:-.03em; color:var(--navy); overflow-wrap:anywhere;
         font-variant-numeric:tabular-nums }
.tk .v small { font:600 .66rem/1 var(--cond); letter-spacing:.06em; color:var(--ink-faint);
               margin-left:6px; text-transform:uppercase }
.tk .v2 { font:500 .82rem/1.2 var(--num); color:var(--ink-soft); font-variant-numeric:tabular-nums }
.tk .v2 b { font-weight:700; color:var(--ink) }
.tk .mv { display:flex; flex-wrap:wrap; gap:0 6px; align-items:baseline; font:500 .74rem/1.3 var(--num);
          color:var(--ink-soft); font-variant-numeric:tabular-nums }
.tk .mv .ar { font-size:.62rem }
.tk .mv .d { font-weight:700; color:var(--ink) }
.tk .mv .p { font-weight:600 }
.tk .mv .vs { color:var(--ink-faint); font-family:var(--sans); font-weight:400 }
.tk .mv.good .ar, .tk .mv.good .d, .tk .mv.good .p { color:var(--good) }
.tk .mv.bad .ar, .tk .mv.bad .d, .tk .mv.bad .p { color:var(--crit) }
.tk .ft { font-size:.72rem; line-height:1.35; color:var(--ink-faint); margin-top:2px }
.tk .ex { margin-top:auto; padding-top:8px; border-top:1px solid var(--edge-soft); display:flex; flex-direction:column;
          gap:2px; font-size:.74rem }
.tk .ex .d { font:600 .8rem/1.2 var(--num); color:var(--ink); font-variant-numeric:tabular-nums }
.tk .ex.mute { color:var(--ink-faint) }

/* ---------- the executive summary and the analysis ---------- */
.summary { margin:0; font-size:.98rem; line-height:1.6; color:var(--ink); max-width:90ch }
/* Key facts: six tiles under the two sentences, a sparkline or a delta on
   each, every figure with its unit, the citation kept in the foot line. */
.kf-h { margin:0 0 .4rem }
.summary-date { margin:1rem 0 4px }
.an-lab + .table-scroll { margin-top:4px }
.kf { display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:10px }
@media (max-width:64rem) { .kf { grid-template-columns:repeat(3,minmax(0,1fr)) } }
@media (max-width:40rem) { .kf { grid-template-columns:repeat(2,minmax(0,1fr)) } }
.kf-t { border:1px solid var(--edge); border-top:3px solid var(--navy); border-radius:8px; padding:.6rem .7rem .65rem;
  background:var(--paper-2); display:flex; flex-direction:column; gap:.3rem; min-width:0 }
.kf-t .k { line-height:1.3; min-height:2.4em; display:flex; align-items:flex-end }
.kf-t .v { font:800 1.35rem/1.1 var(--num); letter-spacing:-.02em; color:var(--navy); overflow-wrap:anywhere;
  display:flex; flex-wrap:wrap; align-items:baseline; gap:0 .3rem }
.kf-t .v small { font:600 .62rem/1 var(--cond); letter-spacing:.06em; text-transform:uppercase; color:var(--ink-faint) }
.kf-t .sparkwrap { margin:.1rem 0 }
.kf-t .spark { width:100%; height:32px; display:block }
.kf-d { display:flex; flex-wrap:wrap; gap:0 5px; align-items:baseline; font:500 .72rem/1.3 var(--num); color:var(--ink-soft);
  font-variant-numeric:tabular-nums }
.kf-d .ar { font-size:.6rem } .kf-d .d { font-weight:700; color:var(--ink) } .kf-d .p { font-weight:600 }
.kf-d .vs { color:var(--ink-faint); font-family:var(--sans); font-weight:400 }
.kf-d.good .ar, .kf-d.good .d, .kf-d.good .p { color:var(--good) }
.kf-d.bad .ar, .kf-d.bad .d, .kf-d.bad .p { color:var(--crit) }
.kf-f { margin-top:auto; padding-top:.35rem; border-top:1px solid var(--edge-soft); font-size:.7rem; line-height:1.4; color:var(--ink-faint) }
.summary b { font-family:var(--num) }
a.cite { text-decoration:none; font-family:var(--num); font-size:.8em; font-weight:600; color:var(--navy);
         vertical-align:baseline; padding:0 1px }
a.cite:hover { text-decoration:underline }
.an-lab { margin:14px 0 0 }
.readings { margin:4px 0 0; padding:0; list-style:none; border-top:1px solid var(--edge-ink) }
@media (min-width:48rem) { .readings { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:0 24px } }
.readings li { padding:10px 0; border-bottom:1px solid var(--edge-soft); font-size:.875rem; line-height:1.5;
               color:var(--ink); display:flex; flex-direction:column; gap:.3rem }
/* Number first, direction second, meaning third. */
.readings .rd-lead { display:flex; flex-wrap:wrap; gap:.15rem .7rem; align-items:baseline }
.readings .rd-n { font:800 1.35rem/1.1 var(--num); letter-spacing:-.02em; color:var(--navy); font-variant-numeric:tabular-nums }
.readings .rd-dir { font:600 .78rem/1.3 var(--num); color:var(--ink-soft); font-variant-numeric:tabular-nums }
.readings .rd-dir.good { color:var(--good) } .readings .rd-dir.bad { color:var(--crit) }
.readings .rd-text { color:var(--ink-soft) }
.readings .rd-text b { font-family:var(--num); font-variant-numeric:tabular-nums; color:var(--ink) }
/* The mini-bar under a reading: a track and a fill; the tone repeats what
   the sentence already says, never on its own. */
.readings .mbar { display:block; height:6px; background:var(--paper-3); border-radius:3px; overflow:hidden }
.readings .mbar b { display:block; height:100%; background:var(--navy); min-width:2px }
.readings .mbar.good b { background:var(--good) }
.readings .mbar.bad b { background:var(--crit) }
#tbl-weeks td.bad { color:var(--crit); font-weight:600 }
.refs { margin:0; padding-left:1.6rem; font-size:.8rem; line-height:1.5; color:var(--ink-soft) }
.refs li { padding:4px 0; border-bottom:1px solid var(--edge-soft) }
.refs li:target { color:var(--ink); background:var(--paper-3) }

/* ---------- sheets: rules, not a wall of cards ---------- */
/* A section is a ruled sheet: an ink rule, the heading hung off it on the
   left, the explanation on the right, the content under both. */
.sheet { background:var(--paper-2); border:1px solid var(--edge); border-radius:4px;
         margin:16px 0 0; padding:16px; min-width:0 }
.shead { position:relative }
.shead .help { margin:4px 0 0; font-size:.8rem; line-height:1.5; color:var(--ink-soft); max-width:60ch }
.sbody { margin-top:12px; min-width:0 }
@media (min-width:62rem) {
  .sheet { display:grid; grid-template-columns:16rem 1fr; column-gap:32px }
  .shead .help { max-width:none }
  .sbody { margin-top:0 }
}
.note { margin:.6rem 0 0; font-size:.78rem; line-height:1.5; color:var(--ink-faint);
        max-width:70ch }

/* Charts and tables scroll inside themselves. The page body never does. */
.chart-scroll, .table-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch }
.chart-scroll { padding-bottom:.2rem }

table { border-collapse:collapse; width:100%; font:.8rem/1.45 var(--num);
        font-variant-numeric:tabular-nums }
th, td { border-bottom:1px solid var(--edge-soft); padding:.32rem .5rem; text-align:right;
         white-space:nowrap }
th { font:600 .64rem/1.3 var(--cond); letter-spacing:.08em; text-transform:uppercase;
     color:var(--navy); border-bottom-color:var(--navy) }
th:first-child, td:first-child { text-align:left; padding-left:0 }
th:last-child, td:last-child { padding-right:0 }
td.wrap-cell { white-space:normal; font-family:var(--sans); min-width:16rem;
               overflow-wrap:anywhere; color:var(--ink-soft); text-align:left }
td.n { text-align:right } td.n:first-child, td.n.lead { text-align:left }

/* ---------- state in form, not only in colour ---------- */
/* A tick before the word, not a filled badge. */
.tag { display:inline-flex; align-items:center; gap:.4rem;
       font:600 .68rem/1.5 var(--cond); letter-spacing:.07em; text-transform:uppercase;
       padding:.05rem 0; color:var(--ink-soft) }
.tag::before { content:""; width:3px; height:.85em; background:currentColor; flex:none }
.tag.critical { color:var(--crit) }
.tag.high     { color:var(--warn) }
.tag.medium   { color:var(--ink-soft) }
.tag.good     { color:var(--good) }
.tag.plain::before, .tag.dashed::before { display:none }
.tag.dashed { border:1px dashed var(--edge-firm); color:var(--ink-faint); padding:.05rem .35rem }
.sevbar { display:flex; flex-wrap:wrap; gap:.4rem 1.2rem; margin:0 0 .6rem }
/* The pill is also the filter. Pressed, it is underlined in its own colour,
   so the state is a shape and not a shade. */
button.tag { background:none; border:0; border-bottom:2px solid transparent; cursor:pointer;
             font-family:var(--cond); padding:.1rem 0 .05rem }
button.tag:hover { border-bottom-color:var(--edge-firm) }
button.tag.on { border-bottom-color:currentColor }
.latest { margin:0 0 .8rem; font-size:.84rem; color:var(--ink-soft);
          display:flex; flex-wrap:wrap; gap:.3rem .7rem; align-items:baseline }
.flist { list-style:none; margin:0; padding:0; border-top:1px solid var(--edge-firm) }
.frow { border-bottom:1px solid var(--edge-soft); padding:.5rem 0 .55rem .1rem; cursor:pointer;
        display:grid; grid-template-columns:1rem 1fr; gap:.15rem .35rem }
.frow:hover { background:var(--paper-2) }
.frow:focus-visible { outline:2px solid var(--navy); outline-offset:-2px }
.frow::before { content:"+"; grid-row:1/4; color:var(--ink-faint); font-family:var(--num);
                font-size:.8rem; line-height:1.5 }
.frow[aria-expanded="true"]::before { content:"−" }
.fhead { display:flex; flex-wrap:wrap; gap:.25rem .7rem; align-items:baseline; min-width:0 }
.fcheck { font-size:.78rem; color:var(--ink); overflow-wrap:anywhere }
.fdate { font-size:.74rem; color:var(--ink-faint); margin-left:auto; white-space:nowrap }
.fsum { margin:0; font-size:.84rem; line-height:1.45; color:var(--ink-soft);
        overflow-wrap:anywhere }
.urow { border-bottom:1px solid var(--edge-soft); padding:.55rem 0 .6rem;
        display:flex; flex-direction:column; gap:.2rem }
.urow .fsum .lab { display:block; font-size:.62rem; margin-top:.15rem }
.urow .fdate { white-space:normal; text-align:right }
.fdetail { margin:.2rem 0 0; font-size:.8rem; line-height:1.5; color:var(--ink-soft);
           padding:.3rem 0 .3rem .7rem; border-left:2px solid var(--edge-firm); overflow-wrap:anywhere }
.more { display:inline-block; margin:.6rem 0 0; background:none; color:var(--ink-soft);
  border:1px solid var(--edge-firm); padding:.36rem .7rem; font:inherit;
  font-size:.8rem; cursor:pointer }
.more:hover { color:var(--ink); border-color:var(--edge-ink) }

/* ---------- chart options and per-chart range ---------- */
.title-btn { background:none; border:0; color:var(--ink-soft); cursor:pointer; padding:.3rem 0;
             display:inline-flex; align-items:center; gap:.4rem; text-align:left;
             font:600 .72rem/1 var(--cond); letter-spacing:.08em; text-transform:uppercase }
.title-btn:hover { color:var(--navy) }
.caret { width:5px; height:5px; border:solid currentColor; border-width:0 1.3px 1.3px 0;
         transform:rotate(45deg) translateY(-1px); opacity:.45; flex:none }
/* The one popover on the page: the one place a radius and a shadow are spent. */
.menu { display:none; position:absolute; z-index:40; top:2rem; right:0;
        background:var(--paper-3); border:1px solid var(--edge-firm); border-radius:6px;
        padding:.9rem 1rem; width:min(24rem,calc(100vw - 2rem)); box-shadow:0 10px 30px rgba(0,40,70,.22) }
.menu.open { display:block }
.mlabel { font:600 .66rem/1.3 var(--cond); letter-spacing:.09em; text-transform:uppercase;
          color:var(--ink-faint); margin:.85rem 0 .35rem }
.mlabel:first-child { margin-top:0 }
.mrow { display:flex; gap:.5rem 1rem; align-items:center; flex-wrap:wrap }
.seg { display:inline-flex; border:1px solid var(--edge-firm) }
.seg button { background:var(--paper-2); border:0; color:var(--ink-soft); font:inherit;
              font-size:.82rem; padding:.26rem .65rem; cursor:pointer;
              display:inline-flex; align-items:center; gap:.35rem }
.seg button.on { background:var(--navy); color:var(--paper) }
:root[data-theme="dark"] .seg button.on { color:#06233A }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .seg button.on { color:#06233A } }
.sw { display:inline-flex; gap:2px }
.sw i { width:8px; height:8px; display:block }

/* The range under the hero. The chart draws what sits between the two
   handles; the track is the whole year's hydrograph so the handles have the
   river itself to be dragged across. Same construction as the daily-record
   page's tracks. */
.crange { margin:.3rem 0 0 }
.crange:empty { display:none }
.crange .track { position:relative; height:46px; border-bottom:1px solid var(--edge-ink);
                 margin-bottom:.3rem }
.crange .track svg { position:absolute; inset:0; width:100%; height:100%; display:block }
.crange .track .mask { position:absolute; top:0; bottom:0; background:var(--paper);
                       opacity:.72; pointer-events:none }
.crange .track { touch-action:none; cursor:ew-resize; user-select:none; -webkit-user-select:none }
.crange input[type=range] { position:absolute; bottom:-11px; left:0; width:100%; margin:0;
      -webkit-appearance:none; appearance:none; background:none; pointer-events:none; height:22px }
.crange input[type=range]::-webkit-slider-thumb { -webkit-appearance:none; pointer-events:none;
      width:3px; height:22px; background:var(--series-1,var(--s1));
      border:0; box-shadow:0 0 0 4px var(--paper) }
.crange input[type=range]::-moz-range-thumb { pointer-events:none; width:3px; height:22px;
      border-radius:0; background:var(--series-1,var(--s1)); border:0 }
.crange input[type=range].held::-webkit-slider-thumb { width:5px; box-shadow:0 0 0 4px var(--paper), 0 0 0 6px var(--series-1,var(--s1)) }
.crange input[type=range].held::-moz-range-thumb { width:5px }
.crange input[type=range]:focus-visible { outline:none }
.crange input[type=range]:focus-visible::-webkit-slider-thumb { box-shadow:0 0 0 3px var(--navy) }
.crnote { display:flex; gap:.4rem 1rem; align-items:baseline; flex-wrap:wrap; margin:.45rem 0 0;
          max-width:none }
.lnk { background:none; border:0; padding:0; color:var(--navy); font:inherit;
       font-size:.78rem; cursor:pointer; text-decoration:underline; text-underline-offset:2px }
.lnk:hover { color:var(--ink) }

/* ---------- the reading under the pointer ---------- */
.hoverable { position:relative }
.hoverable svg { touch-action:pan-y; cursor:crosshair }
.tip { position:absolute; z-index:2; pointer-events:none; display:flex; flex-direction:column;
       gap:.1rem; padding:.4rem .6rem; border-radius:3px; background:var(--tip-bg); color:#fff;
       font:.74rem/1.45 var(--num); font-variant-numeric:tabular-nums; max-width:min(22rem, 92vw) }
.tip[hidden] { display:none }
.tip b { font-family:var(--cond); font-weight:600; letter-spacing:.02em }
.tip .pct { font-weight:600 }
.tip .pct.low { color:#F07A72 }
.tiphint { margin:.35rem 0 0 }
@media (hover:hover) and (pointer:fine) { .tiphint { display:none } }
/* The line under every chart saying the popup reads the whole column. A
   small ruled mark before it, so it reads as an instruction, not a caption. */
.colhint { margin:.35rem 0 0; display:flex; align-items:center; gap:.45rem; font-size:.74rem }
.colhint::before { content:""; width:1px; height:.9em; border-left:1px dashed var(--ink-faint);
                   box-shadow:2px 0 0 -1px var(--ink-faint) }

/* The forecast section: method chips, the single-handle tracks, the
   skill table. */
.chips { display:flex; flex-wrap:wrap; gap:.4rem .5rem; margin:.7rem 0 0 }
.chip { display:inline-flex; align-items:center; gap:.4rem; background:var(--paper-2);
        border:1px solid var(--edge-firm); color:var(--ink-soft); font:inherit; font-size:.78rem;
        padding:.22rem .6rem; cursor:pointer }
.chip i { width:10px; height:3px; display:block; opacity:.35 }
.chip.on { color:var(--ink); border-color:var(--ink-soft) }
.chip.on i { opacity:1 }
.crange.hz { margin-top:.9rem }
.crange.hz .hzlabel { margin:0 0 .25rem }
.crange.hz .track { height:22px; border-bottom:1px solid var(--edge-ink); cursor:pointer }
.crange.hz input[type=range] { pointer-events:auto; bottom:-11px }
.crange.hz input[type=range]::-webkit-slider-thumb { pointer-events:auto }
.crange.hz input[type=range]::-moz-range-thumb { pointer-events:auto }
.crange.hz .stops { display:flex; justify-content:space-between; margin:.75rem 0 0 }
.crange.hz .stops .lnk { font-size:.78rem; color:var(--ink-faint) }
.crange.hz .stops .lnk.on { color:var(--ink); font-weight:600 }
#tbl-fc { margin-top:.9rem }
#tbl-fc td.best { font-weight:700; color:var(--ink) }
#tbl-fc td.noskill { color:var(--crit) }
#tbl-fc tr.on td:first-child { color:var(--ink) }
#tbl-fc .swm { display:inline-block; width:10px; height:3px; margin:0 .4rem .2em 0 }

/* ---------- chart cards: Chart.js, the same construction as the daily record ---------- */
/* A canvas needs a box with a height, or Chart.js draws it at zero. Tall
   is the hero; the rest are the working height of a record chart. */
.cwrap { position:relative; height:300px; width:100% }
.cwrap.tall { height:330px }
.cwrap.ratio { height:90px; margin-top:.2rem }
.legend.overlays label { display:inline-flex; align-items:center; gap:.35rem; cursor:pointer }
/* The hero's key: the series legend and the overlay toggles share one
   wrapping row; the hint sits under them on its own line. */
#flow-legend { display:flex; flex-wrap:wrap; gap:.3rem 1.6rem; align-items:baseline }
#flow-legend .legend { margin-top:.5rem }
#flow-legend .note { flex-basis:100%; order:9; margin-top:.2rem }
.caption.readthis { color:var(--ink-soft) }
@media (max-width:40rem) { .cwrap { height:260px } .cwrap.tall { height:300px } }
.ccard { margin:0 0 1.6rem }
.ccard:last-of-type { margin-bottom:.4rem }
.ccard .chead { display:flex; gap:.4rem 1.2rem; align-items:flex-start; margin:0 0 .5rem }
.ctitle { margin:0; flex:1 1 auto; font-size:.95rem; font-weight:600; line-height:1.35; color:var(--navy);
          letter-spacing:-.008em; max-width:60ch }
.ctools { position:relative; margin-left:auto; flex:none }
.ctools .menu { top:1.9rem }
.ctl { background:var(--paper-2); color:var(--ink); border:1px solid var(--edge-firm);
       padding:.3rem .45rem; font:inherit; font-size:.85rem; min-width:0 }
.ctl:focus-visible { outline:2px solid var(--navy); outline-offset:1px }
.title-in { flex:1 1 14rem }
.menu .more { padding:.2rem .55rem; font-size:.78rem }
/* The "?" beside the Q80 sentence: a small ring, the expander for the
   definition and its source. */
.caption.q80 { margin-top:.5rem }
.qmark { width:1.25rem; height:1.25rem; border-radius:50%; border:1px solid var(--navy); margin-left:.15rem;
         background:none; color:var(--navy); font:600 .72rem/1 var(--num); cursor:pointer;
         display:inline-flex; align-items:center; justify-content:center; vertical-align:middle }
.qmark:hover, .qmark[aria-expanded="true"] { background:var(--navy); color:var(--paper) }
.qmark:focus-visible { outline:2px solid var(--navy); outline-offset:2px }

/* ---------- legends ---------- */
.legend { display:flex; flex-wrap:wrap; gap:.4rem 1.1rem; margin:.7rem 0 0;
          font-size:.75rem; color:var(--ink-soft) }
.legend span { display:inline-flex; align-items:center; gap:.4rem }
.legend i { width:14px; height:3px; flex:none }
.legend i.ring { width:9px; height:9px; border-radius:50%; background:none;
                 border:2px solid currentColor }
.legend i.dot { width:5px; height:5px; border-radius:50%; opacity:.5 }
.legend i.wash { height:10px; opacity:.3 }

/* ---------- where the water comes from ---------- */
.intakes { display:flex; flex-direction:column; border-top:1px solid var(--edge-firm) }
.irow { display:grid; grid-template-columns:minmax(6rem,9rem) 1fr 3.6rem; gap:0 .8rem;
        align-items:center; padding:.5rem 0; border-bottom:1px solid var(--edge-soft) }
.irow .k { font-family:var(--sans); font-size:.82rem; text-transform:none; letter-spacing:0;
           color:var(--ink-soft); font-weight:500; overflow-wrap:anywhere }
.irow .bars { display:flex; flex-direction:column; gap:2px }
.irow .bars i { display:block; height:8px; min-width:2px }
.irow .bars .was { background:var(--ink-faint); opacity:.5 }
.irow .v { text-align:right; font:600 .82rem/1 var(--num) }

/* ---------- scenarios: a graduated scale, the 2023 mean marked on it ---------- */
.scn { display:flex; flex-direction:column; border-top:1px solid var(--edge-firm) }
.srow { display:grid; grid-template-columns:1fr auto; gap:.15rem .7rem;
        align-items:baseline; padding:.6rem 0 .65rem; border-bottom:1px solid var(--edge-soft) }
.srow .k { font-family:var(--sans); font-size:.9rem; text-transform:none;
           letter-spacing:0; color:var(--ink); font-weight:500 }
.srow .v { text-align:right; font:600 .95rem/1.2 var(--num); white-space:nowrap }
.srow .v small { display:block; font:400 .72rem/1.3 var(--sans); color:var(--ink-faint) }
.srow .bar { grid-column:1/-1; position:relative; height:8px; background:var(--paper-3);
             margin-top:.2rem }
.srow .bar i { display:block; height:100%; position:absolute; left:0; top:0 }
/* The tick at the 2023 mean: the mark on the rule, again. */
.srow .bar em { position:absolute; top:-4px; bottom:-4px; width:2px; background:var(--ink);
                transform:translateX(-1px) }
.srow .why { grid-column:1/-1; font-size:.78rem; color:var(--ink-faint);
             line-height:1.45; max-width:64ch }
.scn-scale { position:relative; height:1.1rem; margin-top:.4rem; font:.66rem/1 var(--num);
             color:var(--ink-faint) }
.scn-scale span { position:absolute; top:0; white-space:nowrap }
.scn-scale .mean { transform:translateX(-50%); color:var(--ink); font-weight:600 }

/* ---------- models ---------- */
.models-say { margin:0 0 .5rem; font-size:.95rem; line-height:1.45; max-width:60ch }
details.tech { margin-top:.7rem }
details.tech summary { cursor:pointer; font:600 .72rem/1.4 var(--cond); letter-spacing:.08em;
                       text-transform:uppercase; color:var(--ink-soft); padding:.3rem 0;
                       list-style:none; display:inline-flex; align-items:center; gap:.45rem }
details.tech summary::-webkit-details-marker { display:none }
details.tech summary::before { content:"+"; font-family:var(--num); color:var(--ink-faint) }
details.tech[open] summary::before { content:"−" }
details.tech summary:hover { color:var(--ink) }
details.tech > :not(summary) { margin-top:.6rem }
.verdict-pre { margin:.9rem 0 0; padding:.7rem 0 .7rem .8rem; border-left:2px solid var(--edge-firm);
               font:.75rem/1.5 var(--num); color:var(--ink-soft);
               overflow-x:auto; white-space:pre-wrap; overflow-wrap:anywhere }
/* Scoped to the overview's main: the daily record has its own .gauge, the
   brush track, and shares this stylesheet for the bar and the footer. */
#app .gauge { height:7px; background:var(--paper-3); overflow:hidden;
              margin:.5rem 0 0; max-width:24rem }
#app .gauge i { display:block; height:100%; background:var(--s1) }
.notready { margin:0; font-size:.9rem }

/* ---------- map: the one boxed thing, because a map has edges ---------- */
#map { height:min(60vh,380px); border:1px solid var(--edge-firm); background:var(--paper-3) }
#map.map-offline { height:auto; display:flex; align-items:center; justify-content:center;
                   padding:2rem 1rem; text-align:center }
.leaflet-container { font:12px/1.4 var(--sans) !important; background:var(--paper-3) }

/* ---------- build / empty states ---------- */
.building { border-top:1px solid var(--edge-ink); padding:1rem 0; margin-top:1rem }
.building .v { font:600 1.2rem/1.1 var(--num) }
.skel { height:170px; margin:.8rem 0 0;
        background:linear-gradient(90deg,var(--paper-3),var(--paper-2),var(--paper-3)) }
@media (prefers-reduced-motion:no-preference) {
  .skel { background-size:200% 100%; animation:sweep 1.4s ease-out infinite }
}
@keyframes sweep { to { background-position:-200% 0 } }
#loading.mute { padding:1.2rem 0 }

/* ---------- foot ---------- */
/* One line: the two names, the build and the cache time, the two links. */
.foot { border-top:1px solid var(--edge); margin:32px auto 0; max-width:1440px; padding:12px 24px 16px;
        display:flex; flex-wrap:wrap; gap:4px 16px; align-items:baseline;
        font:400 11px/1.5 var(--sans); color:var(--ink-faint) }
.foot .foot-links { margin-left:auto; display:inline-flex; gap:12px }
.foot a { color:var(--ink-soft); text-decoration:none }
.foot a:hover { color:var(--navy); text-decoration:underline }
@media (max-width:767px) { .foot { padding:12px 12px 16px } .foot .foot-links { margin-left:0 } }

/* ---------- upload ---------- */
.up-head { margin:0 0 16px }
.up-head h2 { font:700 18px/1.25 var(--sans); letter-spacing:-.012em; text-transform:none }
.up-lead { margin:4px 0 0; color:var(--ink-soft); max-width:70ch }
.up-how { margin:8px 0 0; font-size:.8rem; color:var(--ink-soft); max-width:80ch }
.up-how summary { cursor:pointer; color:var(--navy); font-weight:600 }
.up-how p { margin:8px 0 0 }
.up-form { background:var(--paper-2); border:1px solid var(--edge); border-radius:4px; padding:16px }
.up-drop { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px;
           padding:24px 16px; border:1px dashed var(--edge-ink); border-radius:4px; background:var(--paper);
           cursor:pointer; text-align:center }
.up-drop:hover, .up-drop.over { border-color:var(--navy) }
.up-drop input { position:absolute; width:1px; height:1px; opacity:0; overflow:hidden }
.up-drop-big { font:600 15px/1.3 var(--sans); color:var(--navy) }
.up-drop-sub { font-size:.78rem; color:var(--ink-faint) }
.up-row { display:flex; flex-wrap:wrap; gap:8px 16px; align-items:flex-end; margin-top:16px }
.up-field { display:flex; flex-direction:column; gap:4px; flex:1 1 14rem; max-width:24rem }
.up-field input { background:var(--paper); color:var(--ink); border:1px solid var(--edge-firm); border-radius:4px;
                  padding:8px 10px; font:inherit }
.up-btn { border:1px solid var(--edge-firm); border-radius:4px; background:var(--paper-2); color:var(--ink);
          padding:8px 14px; font:600 13px/1.2 var(--sans); cursor:pointer; min-height:36px }
.up-btn:disabled { opacity:.5; cursor:default }
.up-btn-navy { background:var(--navy); border-color:var(--navy); color:var(--paper-2) }
.up-btn-gold { background:var(--gold-ink); border-color:var(--gold-ink); color:var(--on-navy) }
.up-warn { color:var(--crit) }
.up-preview, .up-result, .up-history { background:var(--paper-2); border:1px solid var(--edge); border-radius:4px;
                                        padding:16px; margin-top:16px }
.up-preview .shead .mute, .up-history .shead .mute { margin:4px 0 12px; font-size:.8rem }
.up-table th.num, .up-table td.num { text-align:right }
.up-table th, .up-table td { text-align:left }
.up-staged { margin:4px 0 0 }

/* activity */
.act-head { margin:0 0 16px }
.act-head h2 { font:700 18px/1.25 var(--sans); letter-spacing:-.012em; margin:0 0 4px; max-width:40rem }
.act-lede { color:var(--ink-soft); max-width:70ch; margin:0 }
.act-kpis { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:16px; margin:0 0 16px }
@media (max-width:48rem) { .act-kpis { grid-template-columns:repeat(2, minmax(0,1fr)); gap:8px } }
.act-kpi { background:var(--paper-2); border:1px solid var(--edge); border-top:3px solid var(--navy);
           border-radius:4px; padding:16px; min-width:0 }
.act-kpi b { display:block; font:800 1.9rem/1.05 var(--num); letter-spacing:-.03em; color:var(--navy); margin-top:4px }
.act-sec { background:var(--paper-2); border:1px solid var(--edge); border-radius:4px; padding:16px; margin:0 0 16px; min-width:0 }
.act-sec h3 { margin:0 0 12px }
.act-cols { display:grid; grid-template-columns:1fr 2fr; gap:16px }
@media (max-width:48rem) { .act-cols { grid-template-columns:1fr } }
.act-bars { display:flex; align-items:flex-end; gap:.6rem; height:9rem; border-bottom:1px solid var(--edge); padding-bottom:1.3rem; position:relative }
.act-bar { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:flex-end; height:100%; position:relative }
.act-bar-fill { width:100%; max-width:3rem; background:var(--navy); min-height:2px; border-radius:2px 2px 0 0 }
.act-bar-n { font-size:.72rem; color:var(--ink-soft); margin-bottom:.2rem }
.act-bar-lab { position:absolute; bottom:-1.2rem; font-family:var(--cond); font-size:.68rem; color:var(--ink-faint) }
.act-tbl { width:100%; border-collapse:collapse; font-size:.86rem }
.act-tbl th { text-align:left; font-family:var(--cond); font-size:.68rem; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-faint); border-bottom:1px solid var(--edge); padding:.35rem .5rem .35rem 0 }
.act-tbl td { padding:.4rem .5rem .4rem 0; border-bottom:1px solid var(--rule-soft); vertical-align:top }
.act-tbl .n { text-align:right; font-variant-numeric:tabular-nums }
.act-tbl .mono { font-family:ui-monospace, SFMono-Regular, Menlo, monospace; font-size:.8rem }
.act-note { color:var(--ink-faint); font-size:.78rem; margin:.8rem 0 0 }
.act-note code { font-size:.75rem }
.act-empty { color:var(--ink-soft) }

/* map */
/* ---------- Overview map (static/map.js, templates/_map.html) ---------- */
.mapwrap { display:flex; flex-direction:column; gap:.6rem }
.maptools { display:flex; flex-wrap:wrap; gap:.5rem .9rem; align-items:center }
.maplayers { display:flex; flex-wrap:wrap; gap:.35rem }
.maplayers .chip { font:500 12px/1 var(--sans); color:var(--ink-soft); background:var(--paper-2);
  border:1px solid var(--edge-firm); border-radius:999px; padding:.4rem .7rem; cursor:pointer;
  display:inline-flex; align-items:center; gap:.4rem; min-height:2rem }
.maplayers .chip.on { color:var(--ink); border-color:var(--edge-ink); background:var(--paper-3) }
.maplayers .chip:not(.on) .sw { opacity:.35 }
.maplayers .sw { display:inline-block; width:12px; height:12px; border-radius:50% }
.sw-sites { background:#004770 } .sw-route { background:none; border-top:2px dashed #004770; height:0; border-radius:0 }
.sw-basins { background:rgba(0,71,112,.15); border:1px dashed #004770; border-radius:2px }
.sw-rain { background:rgba(17,115,163,.6); border:1px solid #fff }
.sw-reach { background:#fff; border:3px solid #004770 }
.mapgrid { display:grid; grid-template-columns:1fr; gap:.6rem }
@media (min-width:56rem) { .mapgrid { grid-template-columns:minmax(0,2fr) minmax(16rem,1fr) } }
#site-map { height:min(60vh,420px); min-height:260px; border:1px solid var(--edge-firm); background:var(--paper-3);
  border-radius:3px; overflow:hidden }
#site-map.is-sat { background:#0b1a24 }
#site-map .leaflet-control-attribution { font-size:10px; opacity:.9 }
#site-map .leaflet-popup-content { margin:10px 12px; font:12.5px/1.45 var(--sans) }
#site-map .leaflet-popup-content .note { margin:.3rem 0 0; font-size:11.5px; color:#47606F }
#site-map .leaflet-popup-content .msrc { display:block; margin-top:.3rem; font-size:10.5px; color:var(--ink-faint) }
#site-map .leaflet-popup-content b.n { font:600 1.3rem/1.1 var(--num); color:#004770 }
#site-map .leaflet-popup-content .mbig { margin:.3rem 0 0 }
#site-map .leaflet-popup-content .mspark { width:100%; height:auto; display:block; margin:.25rem 0; color:#004770 }
.mtip { font:12px var(--sans) !important; padding:2px 6px !important }
.mreach { width:14px; height:14px; border-radius:50%; background:#fff; border:3px solid #004770;
  box-shadow:0 0 0 2px rgba(255,255,255,.7) }
.mappanel { border:1px solid var(--edge-firm); border-radius:3px; padding:.8rem .9rem; background:var(--paper-2);
  min-height:6rem; display:flex; flex-direction:column; gap:.35rem }
.mappanel .mute { margin:0; color:var(--ink-faint); font-size:.9rem }
.mappanel .mhead { display:flex; justify-content:space-between; align-items:flex-start; gap:.5rem }
.mappanel h3 { margin:0; font:600 1rem/1.25 var(--sans); color:var(--ink) }
.mappanel .mkind { margin:.25rem 0 0; display:flex; flex-wrap:wrap; gap:.3rem }
.mappanel .tag.gold { color:var(--gold); border-color:var(--gold) }
.mappanel .mclose { background:none; border:0; color:var(--ink-faint); font:400 1.4rem/1 var(--sans);
  cursor:pointer; padding:0 .2rem; min-width:2rem; min-height:2rem }
.mappanel .mlabel { margin:.5rem 0 0; font:500 10.5px/1.2 var(--sans); letter-spacing:.06em;
  text-transform:uppercase; color:var(--ink-faint) }
.mappanel .mbig { margin:.1rem 0 0; display:flex; align-items:baseline; gap:.2rem; flex-wrap:wrap }
.mappanel .mbig b.n { font:600 1.7rem/1.1 var(--num); color:var(--navy); font-variant-numeric:tabular-nums }
.mappanel .mbig .unit { color:var(--ink-soft); font-size:.9rem }
.mappanel .msparkbox { display:flex; flex-direction:column; gap:.15rem }
.mappanel .mspark { width:100%; height:auto; max-height:56px; display:block; color:var(--navy) }
.mappanel .mrange { font:11px/1.2 var(--num); color:var(--ink-faint) }
.mappanel .mrecon { margin:.15rem 0 0; display:flex; gap:.45rem; align-items:flex-start; font-size:.86rem; color:var(--ink) }
.mappanel .mrecon i { flex:none; width:9px; height:9px; border-radius:50%; margin-top:.35em; background:var(--flat) }
.mappanel .mrecon.good i { background:var(--good) } .mappanel .mrecon.warn i { background:var(--gold) }
.mappanel .mrecon.crit i { background:var(--crit) }
.mappanel .note { margin:.3rem 0 0; font-size:.8rem; color:var(--ink-soft) }
.mappanel .mfind { color:var(--ink-faint) }
#map-legend2 { display:flex; flex-wrap:wrap; gap:.35rem 1rem; font-size:.8rem; color:var(--ink-soft) }
#map-legend2 > span { display:inline-flex; align-items:center; gap:.4rem }
#map-legend2 i { display:inline-block; width:11px; height:11px; border-radius:50%; border:1.5px solid #fff;
  box-shadow:0 0 0 1px var(--edge-firm) }
@media (max-width:40rem) { #site-map { height:52vh } .mappanel .mbig b.n { font-size:1.5rem } }
/* end map */

/* shell */
/* A thin gold line at the top while the shell swaps one page for another. */
html.shell-busy::before { content:""; position:fixed; left:0; top:0; height:2px; width:100%; z-index:9999;
  background:linear-gradient(90deg,transparent,var(--gold),transparent); background-size:50% 100%;
  animation:shellsweep .9s linear infinite }
@keyframes shellsweep { from { background-position:-50% 0 } to { background-position:150% 0 } }
/* end shell */

/* KPI card sparkline (raw water in, treated water out) */
.tk .sparkwrap{margin:6px 0 4px}
.tk .spark{display:block;width:100%;height:40px}

/* The upload tab is the one action in the bar: solid gold button, white text.
   This is the view's single gold accent. */
.top-nav a.btn-upload{background:var(--gold-ink);color:var(--on-navy);border-radius:4px;padding:6px 12px;font-weight:600;border-bottom:0;text-transform:none;letter-spacing:0}
.top-nav a.btn-upload:hover{filter:brightness(1.08);color:var(--on-navy)}
.top-nav a.btn-upload[aria-current="page"]{box-shadow:inset 0 0 0 2px var(--navy);border-bottom:0}

/* Upload button sits inside the bar, not across its full height */
.top-nav a.btn-upload{align-self:center;height:32px;padding:0 12px;line-height:32px;border-top:0}

/* Vote buttons wrap inside their cell instead of spilling past the table edge */
.fb-vote{flex-wrap:wrap}
.act-table td:last-child, td.act-status{min-width:9rem}
.act-table{table-layout:fixed;width:100%}

/* ---------- the alert strip (dashboard/monitoring.py, under the bar) ---------- */
.alert-strip { max-width:1440px; margin:8px auto 0; padding:0 12px; display:flex; gap:8px; flex-wrap:wrap }
@media (min-width:768px) { .alert-strip { padding:0 24px } }
.alert-chip { display:inline-flex; align-items:center; gap:6px; min-height:32px; padding:4px 10px; border-radius:8px;
  font:500 12px/1.3 var(--sans); color:var(--ink); text-decoration:none; background:var(--glass-bg-weak, var(--paper-2));
  border:1px solid var(--edge-firm); box-shadow:inset 0 1px 0 var(--glass-hi, transparent) }
.alert-chip b { font:700 10px/1 var(--sans); letter-spacing:.06em; padding:3px 6px; border-radius:4px; color:var(--on-navy); background:var(--navy) }
.alert-chip.sev-warning b { background:var(--gold-ink) }
.alert-chip.sev-critical b { background:var(--crit, #980000) }
.alert-chip em { font-style:normal; color:var(--ink-faint) }
.alert-chip.acked { opacity:.7 }

/* Settings: the cog reads as settings, with its name beside it on wide screens */
.top .settings-btn{display:inline-flex;align-items:center;gap:6px;padding:0 10px;width:auto;min-width:36px;height:36px;white-space:nowrap;flex:none;font:600 12px/1 var(--sans);letter-spacing:.04em;text-transform:uppercase;color:var(--ink-soft)}
.top .settings-btn .settings-label{display:none}
@media (min-width:900px){.top .settings-btn .settings-label{display:inline}}
.top .top-side .lang{margin-right:4px;flex:none}
.top .top-side .lang button{height:36px;min-width:40px;padding:0 10px;font:600 12px/1 var(--sans);border:0;background:transparent;color:var(--ink-soft);cursor:pointer}
.top .top-side .lang button.on{background:var(--navy);color:var(--on-navy)}
.top .top-side{gap:8px;flex-wrap:nowrap}

/* The language switch is always visible, on every page and every width. */
.top .lang { display:inline-flex !important; visibility:visible !important }

/* ---------- the Overview lead band: hero left, map right ---------- */
/* Two columns from 64rem, the hero taking seven twelfths and the map the
   rest, both stretched to one height so the map reads as the chart's
   companion and not as a footnote. Under that the map drops below. */
.band { display:grid; grid-template-columns:1fr; gap:16px; margin:16px 0 0; align-items:stretch }
.band > .hero { margin:0; min-width:0 }
.band > .mapcard { margin:0; display:flex; flex-direction:column; min-width:0 }
/* Beside the hero the map card takes the hero's height and no more (height
   0 plus min-height 100% resolves against the row the hero sets); its tool
   strip and legends scroll inside the card, so the forecast under the band
   is reached within the second screen at 1280. */
@media (min-width:64rem) {
  .band > .mapcard { height:0; min-height:100%; overflow:hidden }
  .band > .mapcard .sbody { overflow:auto; -webkit-overflow-scrolling:touch }
}
.band > .mapcard .sbody { margin-top:10px; display:flex; flex-direction:column; flex:1 1 auto; min-height:0 }
.band > .mapcard .mapwrap { display:flex; flex-direction:column; flex:1 1 auto; min-height:0 }
.band > .mapcard .shead .help { max-width:none }
@media (min-width:64rem) {
  .band { grid-template-columns:minmax(0,8fr) minmax(0,4fr) }
  .band > .mapcard #site-map[data-mode="preview"] { height:auto; flex:1 1 auto; min-height:380px }
}

/* ---------- KPI card glyphs ---------- */
.tk .k { gap:.45rem }
.tk .k .ic { width:18px; height:18px; flex:none; color:var(--navy); stroke:currentColor; margin-bottom:.1em }

/* ---------- in plain words ---------- */
.schem { margin:0 0 1rem; padding:.6rem .6rem .4rem; border:1px solid var(--edge); background:var(--paper); border-radius:4px }
.schem-scroll { min-width:0 }
.schem svg { width:100%; height:auto; display:block; min-width:0 }
.schem figcaption { margin:.5rem 0 0; font-size:.76rem; line-height:1.45; color:var(--ink-faint) }
.schem-swipe { display:none }
/* On a phone the drawing keeps its 1200 px width and scrolls sideways in
   its own box; the caption says so first. */
@media (max-width:48rem) {
  .schem-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; scroll-snap-type:x proximity }
  .schem svg { min-width:1000px }
  .schem-swipe { display:inline; font-weight:600; color:var(--navy) }
}
/* Five term cards in a row: glyph, term, a twelve-word definition, and the
   analogy revealed on hover, focus or tap (the card is a button, so the tap
   works on a phone and the keyboard reaches it). */
.terms { margin:0; display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px }
@media (max-width:64rem) { .terms { grid-template-columns:repeat(3,minmax(0,1fr)) } }
@media (max-width:40rem) { .terms { grid-template-columns:repeat(2,minmax(0,1fr)) } }
.terms .term { position:relative; display:flex; flex-direction:column; gap:.3rem; align-items:flex-start; text-align:left;
  min-width:0; padding:.7rem .75rem .75rem; border:1px solid var(--edge); border-top:3px solid var(--navy); border-radius:8px;
  background:var(--paper-2); color:var(--ink); font:inherit; cursor:pointer }
.terms .term:hover, .terms .term:focus-visible, .terms .term[aria-expanded="true"] { border-color:var(--navy) }
.terms .term:focus-visible { outline:2px solid var(--navy); outline-offset:2px }
.terms .term .ic { width:22px; height:22px; color:var(--navy); stroke:currentColor }
.terms .term-t { font:600 .82rem/1.3 var(--sans); color:var(--navy) }
.terms .term-d { font-size:.78rem; line-height:1.45; color:var(--ink-soft) }
.terms .term-a { display:none; margin-top:.2rem; padding-top:.4rem; border-top:1px dashed var(--edge-firm);
  font-size:.76rem; line-height:1.45; color:var(--ink-soft); font-style:italic }
.terms .term-a b { display:block; font:600 .62rem/1.3 var(--cond); letter-spacing:.08em; text-transform:uppercase;
  color:var(--ink-faint); font-style:normal; margin-bottom:.15rem }
.terms .term-a i { font-style:italic }
.terms .term[aria-expanded="true"] .term-a { display:block }
@media (hover:hover) and (pointer:fine) { .terms .term:hover .term-a, .terms .term:focus-visible .term-a { display:block } }
.howto { margin:1.1rem 0 0; padding-top:.8rem; border-top:1px solid var(--edge-firm) }
.callouts { margin:.4rem 0 0; padding:0; list-style:none; display:grid; grid-template-columns:repeat(auto-fit,minmax(15rem,1fr)); gap:10px 20px }
.callouts li { font-size:.8rem; line-height:1.5; color:var(--ink-soft); display:flex; gap:.5rem; align-items:flex-start }
.callouts li b { flex:none; font:700 .72rem/1.2 var(--cond); letter-spacing:.06em; text-transform:uppercase; color:var(--on-navy);
                 background:var(--navy); padding:.3rem .45rem; border-radius:3px; margin-top:.1rem; white-space:nowrap }

/* ---------- four figures at a glance ---------- */
.visuals { margin-top:8px }
.vis { border:1px solid var(--edge); background:var(--paper-2); padding:12px 14px; min-width:0; border-radius:4px;
       display:flex; flex-direction:column }
.vis .ctitle { font-size:.82rem; margin:0 0 .4rem }
.cwrap.vis-plot { height:320px }
.vis .caption { margin:.6rem 0 0; font-size:.875rem; line-height:1.5; color:var(--ink) }
.vis .caption b, .caption b, .summary b, .readthis b { font-family:var(--num); font-variant-numeric:tabular-nums; color:var(--navy) }
.vis .ctitle { font-size:.95rem }
/* Two to a row on a wide screen, full width and 260 px tall on a phone;
   the body-prefixed phone rule in glass.css only touches .span3. */
@media (max-width:48rem) { body .visuals .span6 { grid-column:span 12 } .cwrap.vis-plot { height:260px } }

/* ---------- glossary: a dotted underline and a definition on hover or focus ---------- */
dfn.gl { font-style:normal; text-decoration:underline dotted var(--navy); text-underline-offset:3px; text-decoration-thickness:1px;
         cursor:help; position:relative; outline:none }
dfn.gl::after { content:attr(data-tip); position:absolute; left:0; bottom:calc(100% + 6px); z-index:20; width:max-content; max-width:min(34ch,80vw);
                font:400 .74rem/1.45 var(--sans); letter-spacing:0; color:#fff; background:var(--tip-bg); padding:.45rem .6rem; border-radius:3px;
                opacity:0; pointer-events:none; transition:opacity .12s; text-align:left }
dfn.gl:hover::after, dfn.gl:focus::after, dfn.gl:focus-visible::after { opacity:1 }
dfn.gl:focus { text-decoration-color:var(--gold) }

/* ---------- the working detail ---------- */
.appx { margin:32px 0 0; padding:0 0 6px; border-bottom:2px solid var(--navy); font:700 .74rem/1.3 var(--cond);
        letter-spacing:.1em; text-transform:uppercase; color:var(--navy) }


/* The ticker cards are flat: a solid surface, a one-pixel edge and a
   three-pixel top rule in the tone colour, no blur and no shadow (the glass
   recipe in glass.css put a blurred band across the top of every card on
   a desktop; the `body` prefix here outranks it). The treated-water card
   leads the strip with a two-pixel navy edge and a six percent tint of its
   trend colour, no more. */
body .ticker .tk { background:var(--paper-2); border:1px solid var(--edge); border-top:3px solid var(--navy);
  box-shadow:none; -webkit-backdrop-filter:none; backdrop-filter:none; padding:16px; border-radius:8px }
body .ticker .tk.tone-good { border-top-color:var(--good) }
body .ticker .tk.tone-bad { border-top-color:var(--crit) }
body .ticker .tk.accent { border-color:var(--edge); border-top-color:var(--gold); box-shadow:none }
body .ticker .tk.hero { border:1px solid transparent; box-shadow:none }
.tk.hero .v b { font-size:2.35rem }
/* The trend frame: a two-pixel outline in the trend colour with a soft
   outer glow, green above the seven-day mean, red below, and a five
   percent tint; no navy edge. */
body .ticker .tk.hero.tone-good { outline:2px solid #1f7a4d; background:color-mix(in srgb, #1f7a4d 5%, var(--paper-2));
  box-shadow:0 0 0 2px rgba(31,122,77,.35), 0 8px 24px rgba(31,122,77,.18) }
body .ticker .tk.hero.tone-bad { outline:2px solid #b3261e; background:color-mix(in srgb, #b3261e 5%, var(--paper-2));
  box-shadow:0 0 0 2px rgba(179,38,30,.35), 0 8px 24px rgba(179,38,30,.18) }
.tk.hero .k { color:var(--navy) }
@media (max-width:48rem) { body .ticker .tk { padding:12px } }


/* ---------- the chart action strip (static/chart-export.js) ----------
   A 32 px glass strip at a card's top right: PNG, CSV, link, share, copy
   image as icon buttons, each labelled for the screen reader and the
   tooltip. Under 480 px the icons fold into one "…" button with a menu that
   spells the actions out. Inside a card header it sits in the flex row;
   on a card without a header it floats over the top-right corner. */
.xstrip { position:relative; display:flex; align-items:center; height:32px; flex:none; margin-left:auto;
  background:var(--glass-bg-weak, rgba(255,255,255,.82)); border:1px solid var(--glass-edge, var(--edge));
  border-radius:8px; box-shadow:inset 0 1px 0 var(--glass-hi, rgba(255,255,255,.6));
  -webkit-backdrop-filter:blur(var(--glass-blur, 18px)) saturate(var(--glass-sat, 1.4));
          backdrop-filter:blur(var(--glass-blur, 18px)) saturate(var(--glass-sat, 1.4)); z-index:3 }
.xs-host { position:relative }
.xs-host > .xstrip { position:absolute; top:.55rem; right:.6rem }
.xs-host > .ctitle, .xs-host > h3 { padding-right:11.5rem }
.xs-row { display:flex; align-items:center; height:100% }
.xs-btn { width:30px; height:30px; display:inline-flex; align-items:center; justify-content:center; background:none; border:0;
  color:var(--ink-soft); cursor:pointer; padding:0; border-radius:6px }
.xs-btn svg { width:15px; height:15px }
.xs-btn:hover, .xs-btn:focus-visible { color:var(--navy); background:var(--edge-soft) }
.xs-btn:focus-visible { outline:2px solid var(--navy); outline-offset:-2px }
.xs-more { display:none }
.xs-menu { position:absolute; top:calc(100% + 6px); right:0; min-width:11rem; display:none; flex-direction:column; padding:.3rem;
  background:var(--glass-bg-strong, var(--paper-2)); border:1px solid var(--glass-edge, var(--edge)); border-radius:8px;
  box-shadow:0 10px 28px -10px rgba(0,40,70,.28); z-index:40;
  -webkit-backdrop-filter:blur(var(--glass-blur-pop, 24px)); backdrop-filter:blur(var(--glass-blur-pop, 24px)) }
.xs-menu.open { display:flex }
.xs-menu button { display:flex; align-items:center; gap:.55rem; width:100%; background:none; border:0; text-align:left;
  font:500 .8rem/1.2 var(--sans); color:var(--ink); padding:.5rem .6rem; border-radius:5px; cursor:pointer }
.xs-menu button svg { width:15px; height:15px; color:var(--ink-soft); flex:none }
.xs-menu button:hover, .xs-menu button:focus-visible { background:var(--edge-soft); color:var(--navy); outline:none }
@media (max-width:480px) {
  .xs-row { display:none }
  .xs-more { display:inline-flex }
  .xs-host > .ctitle, .xs-host > h3 { padding-right:2.6rem }
}
.xs-toast { position:fixed; left:50%; bottom:calc(1.4rem + env(safe-area-inset-bottom, 0px)); transform:translate(-50%, 8px);
  background:var(--tip-bg); color:#fff; font:500 .8rem/1.3 var(--sans); padding:.55rem .9rem; border-radius:6px; z-index:200;
  opacity:0; pointer-events:none; transition:opacity .15s, transform .15s }
.xs-toast.on { opacity:1; transform:translate(-50%, 0) }
.xs-zip { margin-left:.6rem }


/* ---------- the desktop page plan (1280 to 1920) ----------
   Row 1 the ticker block; row 2 the hero chart (8) beside the map (4);
   row 3 the executive summary, full width, tiles in one row of six; row 4
   the forecast (8) beside the channel-loss card (4); row 5 the four small
   charts two by two; then the plain words, the analysis, the actions,
   Bangkok, the sources and the working detail. Every section has a
   scroll margin so an anchor never lands under the fixed bar. */
main > section, main > .band > section, main > .band, main > h2, section[id], [id].card, [id].vis { scroll-margin-top:calc(var(--bar-h, 56px) + 12px) }
.band-fc { margin-top:16px }
.band-fc > section { margin:0; min-width:0 }
.band-fc > #channel-loss { display:flex; flex-direction:column }
.band-fc > #channel-loss[hidden] { display:none }
@media (min-width:64rem) {
  .band-fc > #channel-loss .sbody { overflow:auto }
  .band-fc > #channel-loss:not([hidden]) { height:0; min-height:100% }
}
/* Sections that follow the band keep one rhythm. */
#sec-summary, #sec-visuals { margin-top:16px }
/* Inside the forecast band the two sheets stack their heading over the
   body: the side column would take a third of an eight-column card. */
@media (min-width:62rem) {
  .band-fc > .sheet { display:block }
  .band-fc > .sheet .sbody { margin-top:12px }
  .band-fc > .sheet .shead .help { max-width:80ch }
}
/* The hero ticker card spans two rows, so its sparkline takes the room:
   the last fourteen days at a height the eye can read. */
.tk.hero .sparkwrap { flex:1 1 auto; display:flex; flex-direction:column; justify-content:center; min-height:0 }
.tk.hero .spark { height:120px; max-height:100% }
@media (max-width:48rem) { .tk.hero .spark { height:56px } }
.tk.hero .hero-mini { position:relative; display:block; min-height:120px }
.tk.hero .hero-mini canvas { position:absolute; inset:0; width:100% !important; height:100% !important }
@media (max-width:48rem) { .tk.hero .hero-mini { min-height:90px } }
/* The four small charts: two by two. A card that has not mounted is
   hidden, so the grid holds only what rendered and leaves no blank card. */
.visuals .vis[hidden] { display:none }

/* ---------- the bar on every width, and the phone sheet ----------
   The bar is one line of 56 px from 1024 up, two lines (48 and 40) between
   768 and 1023, and one line of 52 px under 768 where the tabs fold into
   the hamburger. --bar-h is the height the anchors keep clear of. */
:root { --bar-h:56px }
@media (max-width:1023px) { :root { --bar-h:88px } }
@media (max-width:767px) { :root { --bar-h:52px } }
.top-nav a.btn-upload { display:inline-flex; align-items:center; gap:6px }
.top-nav a.btn-upload svg { flex:none }
.top-nav a.btn-upload .lbl-short { display:none }
/* Under 1440 the bar has no room for the long label beside the date pill
   on the pages whose title carries a page name: icon and the short word. */
@media (max-width:1439px) {
  .top-nav a.btn-upload .lbl-long { display:none }
  .top-nav a.btn-upload .lbl-short { display:inline }
}
.top-menu-btn { display:none; width:44px; height:44px; align-items:center; justify-content:center; flex:none;
  background:none; border:1px solid var(--edge-firm); border-radius:6px; color:var(--navy); cursor:pointer; padding:0 }
.top-menu-btn .mi-close { display:none }
.top-menu-btn[aria-expanded="true"] .mi-open { display:none }
.top-menu-btn[aria-expanded="true"] .mi-close { display:block }
.top-sheet { display:none }
@media (max-width:767px) {
  /* One row: the mark and title, the upload button, the language switch, the gear, the hamburger. */
  body .top-in { flex-wrap:nowrap; height:52px; padding:0 12px; gap:8px; align-items:center }
  body .top-brand { height:52px; flex:1 1 auto; min-width:0; order:1 }
  body .top-brand .logo { height:22px }
  body .top-nav { order:2; flex:none; height:auto; margin:0; padding:0; border:0; overflow:visible }
  body .top-nav a:not(.btn-upload) { display:none }
  body .top-nav a.btn-upload { height:36px; min-height:36px; line-height:36px; padding:0 10px; margin:0 }
  body .top-side { order:3; gap:6px }
  body .top-side .settings { display:none }
  /* The switch moves into the sheet on a phone (glass.css keeps it visible
     in the bar on every other width). */
  body .top .top-side > .lang { display:none !important }
  body .top-menu-btn { display:inline-flex }
  /* The sheet: glass, full width under the bar, the same links as tabs. */
  .top-sheet:not([hidden]) { display:block; position:fixed; left:0; right:0; top:var(--bar-h); z-index:29;
    max-height:calc(100dvh - var(--bar-h)); overflow:auto; padding:12px 12px calc(16px + env(safe-area-inset-bottom, 0px));
    background:var(--glass-bg-strong, var(--paper-2)); border-bottom:1px solid var(--glass-edge, var(--edge));
    box-shadow:0 16px 32px -16px rgba(0,40,70,.28);
    -webkit-backdrop-filter:blur(var(--glass-blur-pop, 24px)) saturate(var(--glass-sat, 1.4));
            backdrop-filter:blur(var(--glass-blur-pop, 24px)) saturate(var(--glass-sat, 1.4));
    animation:glass-sheet var(--t-base, .2s) var(--ease-out, ease-out) }
  .sheet-nav { display:flex; flex-direction:column }
  .sheet-nav a { display:flex; align-items:center; min-height:46px; padding:0 10px; border-radius:6px;
    font:600 14px/1 var(--sans); color:var(--ink); text-decoration:none }
  .sheet-nav a[aria-current="page"] { color:var(--navy); background:var(--edge-soft); box-shadow:inset 3px 0 0 var(--navy) }
  .sheet-row { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:10px 10px 4px;
    border-top:1px solid var(--edge); margin-top:8px }
  .sheet-row .lang { display:inline-flex }
  .sheet-lab { margin:10px 10px 2px }
  .sheet-settings { display:flex; flex-direction:column; gap:2px }
  .sheet-settings a, .sheet-settings .lockstate button { display:block; width:100%; text-align:left; padding:11px 10px;
    font:600 14px/1.2 var(--sans); color:var(--ink); text-decoration:none; background:none; border:0; border-radius:6px }
  .sheet-settings .settings-who { padding:8px 10px; font:500 12px/1.3 var(--sans); color:var(--ink-soft) }
  .sheet-settings .settings-who b { display:block; color:var(--ink) }
  .sheet-settings .lockstate { display:block; margin:0 }
  html.sheet-open { overflow:hidden }
}

/* ---------- the gate: login and change-password ---------- */
/* One photograph of the plant under a navy scrim, one glass card. The card
   is white glass over the scrim in the light theme and a thin white veil in
   the dark one; the inks are the page tokens, so both themes are checked by
   scripts/contrast_check.py. Gold is spent once, on the sign-in button. */
.gate-bg { position:fixed; inset:0; z-index:-1; background:#06233A center/cover no-repeat; }
.gate-bg img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; opacity:0; transition:opacity 320ms ease-out; }
.gate-bg img.in { opacity:1; }
.gate-bg::after { content:""; position:absolute; inset:0;
  background:linear-gradient(180deg, rgba(6,35,58,.62) 0%, rgba(6,35,58,.48) 45%, rgba(6,35,58,.78) 100%); }
body.gate-page { display:grid; place-items:center; grid-template-rows:1fr auto; min-height:100dvh; padding:24px 16px 0; background:#06233A; }
.gate { position:relative; width:100%; max-width:24rem; padding:28px 28px 24px; border-radius:16px;
  background:var(--gate-bg); color:var(--ink); border:1px solid rgba(255,255,255,.55);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.7), 0 1px 2px rgba(0,20,40,.12), 0 24px 60px -20px rgba(0,20,40,.55);
  -webkit-backdrop-filter:blur(24px) saturate(1.4); backdrop-filter:blur(24px) saturate(1.4); }
:root { --gate-bg:rgba(255,255,255,.82); --gate-field:rgba(255,255,255,.92); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --gate-bg:rgba(255,255,255,.18); --gate-field:rgba(6,35,58,.55); } }
:root[data-theme="dark"] { --gate-bg:rgba(255,255,255,.18); --gate-field:rgba(6,35,58,.55); }
:root[data-theme="dark"] .gate, :root:not([data-theme="light"]) .gate { border-color:rgba(255,255,255,.28); box-shadow:inset 0 1px 0 rgba(255,255,255,.35), 0 24px 60px -20px rgba(0,10,25,.7); }
@media (prefers-color-scheme: light) { :root:not([data-theme="dark"]) .gate { border-color:rgba(255,255,255,.55); } }
@supports not (backdrop-filter:blur(1px)) { .gate { background:var(--paper-2); } }
@media (prefers-reduced-transparency: reduce) { .gate { background:var(--paper-2); backdrop-filter:none; -webkit-backdrop-filter:none; } }
.gate-lang { position:absolute; top:14px; right:14px; }
.gate-logo { display:block; height:28px; width:auto; margin:0 0 18px; }
/* The mark is navy on transparent; in the dark theme it sits on a white tile
   the way the bar's logo does (see .logo above). */
:root[data-theme="dark"] .gate-logo { background:#fff; padding:3px 6px; border-radius:4px; height:32px; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .gate-logo { background:#fff; padding:3px 6px; border-radius:4px; height:32px; } }
.gate h1 { font:700 22px/1.2 var(--sans); letter-spacing:-.01em; margin:0 0 4px; color:var(--ink); }
.gate-tag { font:500 13px/1.4 var(--sans); color:var(--ink-soft); margin:0 0 20px; }
.gate-note { font:400 13px/1.45 var(--sans); color:var(--ink-soft); margin:-8px 0 16px; }
.gate label { display:block; font:600 12px/1 var(--sans); color:var(--ink-soft); margin:0 0 6px; }
.gate input { width:100%; min-height:44px; padding:10px 12px; margin:0 0 14px; border-radius:8px;
  font:500 15px/1.2 var(--sans); color:var(--ink); background:var(--gate-field); border:1px solid var(--edge-ink); }
.gate input::placeholder { color:var(--ink-faint); opacity:1; }
.gate input:focus-visible { outline:2px solid var(--navy); outline-offset:1px; }
.gate input#code { font:600 20px/1 var(--sans); letter-spacing:.3em; text-align:center; font-variant-numeric:tabular-nums; margin-bottom:0; }
.gate-submit { width:100%; min-height:44px; margin:4px 0 0; border:0; border-radius:8px; cursor:pointer;
  background:#CE7F0C; color:#06233A; font:700 14px/1.2 var(--sans); }
.gate-submit:hover { background:#DB8A16; }
.gate-submit:active { transform:scale(.99); }
.gate-submit:focus-visible { outline:2px solid var(--ink); outline-offset:2px; }
.gate-more { margin-top:16px; border-top:1px solid var(--edge); padding-top:10px; }
.gate-more summary { cursor:pointer; font:600 12px/1.3 var(--sans); color:var(--ink-soft); min-height:32px; display:flex; align-items:center; list-style:none; }
.gate-more summary::-webkit-details-marker { display:none; }
.gate-more summary::before { content:"+"; display:inline-block; width:14px; font-weight:700; }
.gate-more[open] summary::before { content:"\2013"; }
.gate-more p { margin:8px 0 10px; font-size:12px; line-height:1.45; color:var(--ink-soft); }
.gate-err { color:var(--crit); font:500 13px/1.4 var(--sans); margin:12px 0 0; }
.gate-help { font:400 13px/1.4 var(--sans); color:var(--ink-soft); margin:18px 0 0; }
.gate-link { display:inline-block; margin-top:8px; font:600 12px/1.3 var(--sans); color:var(--ink-soft); text-decoration:underline; text-underline-offset:2px; }
.gate-link:hover { color:var(--ink); }
/* The footer sits on the scrim, not on glass: plain white text, no box. */
body.gate-page .foot { border:0; background:transparent; box-shadow:none; backdrop-filter:none; -webkit-backdrop-filter:none; color:rgba(255,255,255,.85); }
body.gate-page .foot a { color:rgba(255,255,255,.85); }
@media (max-width:420px) { .gate { padding:24px 20px 20px; max-width:100%; } body.gate-page { padding:16px 12px 0; } }

/* ---------- photo strip: a panel header over one of TTM's photographs ---------- */
/* Reduced height, lazy, webp with a jpg fallback, navy scrim under the
   text. Used by the Overview plain-words panel and the upload page. */
.pstrip { position:relative; min-height:120px; margin:0 0 12px; grid-column:1 / -1; border-radius:var(--glass-r,12px); overflow:hidden;
  background:#004770; display:flex; align-items:flex-end; }
.pstrip img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:center 60%; }
.pstrip::after { content:""; position:absolute; inset:0;
  background:linear-gradient(180deg, rgba(0,71,112,.25) 0%, rgba(0,71,112,.55) 50%, rgba(0,40,64,.88) 100%); }
.pstrip-text { position:relative; z-index:1; padding:28px 16px 12px; max-width:64ch; }
.pstrip-text h2 { color:#fff; margin:0; }
.pstrip-text .help { color:rgba(255,255,255,.88); }
.pstrip-credit { position:absolute; right:10px; bottom:6px; z-index:1; font:400 10px/1.2 var(--sans); color:rgba(255,255,255,.7); }
@media (max-width:640px) { .pstrip { min-height:96px; } .pstrip-text { padding:20px 12px 10px; } }

/* Phones: the chart tools sit in their own row above the headline, so the
   sentence gets the full width instead of a narrow column beside a dropdown. */
@media (max-width:640px) {
  .hero-head { flex-wrap:wrap }
  .hero-head .hero-tools { order:-1; width:100%; display:flex; justify-content:flex-end; gap:8px }
  .hero-head .verdict { flex:1 1 100%; width:100% }
  .verdict .say { font-size:1.12rem; max-width:none; text-wrap:pretty }
  .verdict .head { gap:.2rem .6rem }
  .hero .notes, .hero .facts { font-size:.9rem }
}


/* Login polish: a compact remember row, quieter glass, a simple footer */
.gate .remember { display:flex; align-items:center; gap:10px; margin:6px 0 14px; font-size:.9rem; color:var(--ink-soft); cursor:pointer }
.gate .remember input[type=checkbox] { width:18px !important; height:18px !important; height:18px; min-height:0; min-width:0; margin:0; accent-color:var(--navy); flex:none; padding:0; border-radius:4px }
:root { --gate-bg:rgba(255,255,255,.88) }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --gate-bg:rgba(10,32,50,.62) } }
:root[data-theme="dark"] { --gate-bg:rgba(10,32,50,.62) }
.gate { box-shadow:inset 0 1px 0 rgba(255,255,255,.45), 0 2px 6px rgba(0,20,40,.18), 0 30px 70px -24px rgba(0,20,40,.65) }
body.gate-page .foot { background:transparent; border:0; box-shadow:none; backdrop-filter:none; -webkit-backdrop-filter:none }
body.gate-page .foot, body.gate-page .foot a { color:rgba(255,255,255,.72); font-size:12px }
body.gate-page .foot { display:flex; flex-wrap:wrap; justify-content:center; gap:4px 14px; text-align:center; padding:12px }
body.gate-page .foot .n, body.gate-page .foot .foot-links a { display:none }


/* Phone sheet: readable, opaque enough; the bar's own language switch hides
   because the sheet carries one */
@media (max-width:1023px) {
  .top-sheet:not([hidden]) { background:rgba(255,255,255,.96) }
  :root[data-theme="dark"] .top-sheet:not([hidden]) { background:rgba(10,32,50,.96) }
  @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .top-sheet:not([hidden]) { background:rgba(10,32,50,.96) } }
  .top-sheet .sheet-row .lang { display:inline-flex !important; position:static; margin:0 }
  .top-sheet .sheet-row .lang button { height:40px; min-width:48px }
}
@media (max-width:767px) { .top .top-side .lang { display:none !important } }
.settings-menu { background:rgba(255,255,255,.97) !important }
:root[data-theme="dark"] .settings-menu { background:rgba(10,32,50,.97) !important }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .settings-menu { background:rgba(10,32,50,.97) !important } }
