/* TTM raw water dashboard — the glass layer.
 *
 * Pulled in by style.css (@import at its head) so every page gets it, and
 * inlined by build_static.py into the snapshot. It restyles the surfaces
 * style.css and the pages' own <style> blocks already lay out: the bar, the
 * ticker cards, the sheets, the control strips, the popovers, the login box,
 * the feedback and upload panels, the footer. Selectors are prefixed with
 * `body` so they outrank the same class set later in a page's inline style
 * without touching those files.
 *
 * THE RECIPE (docs/design-system.md): a translucent surface over a soft
 * navy-to-paper ambient, blurred and saturated so the ambient shows through,
 * a one-pixel highlight along the top edge, a faint outer edge, a layered
 * shadow. Refraction (an SVG displacement run through backdrop-filter) only
 * on the bar and the popovers, never on a card holding a chart. Motion is
 * transform and opacity only, 160 to 220 ms, ease-out. Reduced transparency
 * and no backdrop-filter support both fall back to a solid paper surface.
 *
 * Technique consulted, not copied: dpawlikowski/liquid-glass (MIT),
 * rdev/liquid-glass-react (MIT), nikdelvin/liquid-glass (MIT),
 * LeonardSEO/liquid-glass-react (MIT); clones in ~/Downloads/glass-refs.
 */

/* ---------- tokens ---------- */
:root {
  --glass-bg:rgba(255,255,255,.90);        /* surface, light: text sits on it, so .90 */
  --glass-bg-strong:rgba(255,255,255,.94); /* popovers, the bar on a phone */
  --glass-bg-weak:rgba(255,255,255,.82);   /* controls that sit on glass */
  --glass-edge:rgba(30,79,156,.16);
  --glass-hi:rgba(255,255,255,.9);         /* the inset top highlight */
  --glass-shadow:0 1px 2px rgba(0,40,70,.05), 0 10px 28px -8px rgba(0,40,70,.14);
  --glass-shadow-pop:0 2px 6px rgba(0,40,70,.08), 0 18px 48px -12px rgba(0,40,70,.28);
  --glass-blur:18px; --glass-blur-pop:24px; --glass-sat:1.4;
  --glass-r:var(--r-card,14px); --glass-r-pop:var(--r-card,14px); /* one radius scale: docs/design-system.md "Chrome scale" */
  --amb-1:rgba(15,76,129,.11); --amb-2:rgba(206,127,12,.06); --amb-3:rgba(42,127,201,.08);
  --ease-out:cubic-bezier(.2,.7,.2,1); --t-fast:160ms; --t-base:200ms;
  /* The floating tab bar's own material (docs/liquid-glass-plan.md):
     more transparent than --glass-bg-strong on purpose, to read as the
     iOS 26 tab-bar look the owner asked for -- 64% white, not 94%. The
     desktop top bar shares this same scale (TK-2026-0102: "the same rim
     and tint as the phone bar"), so .top reads for these tokens below.
     No other surface should: every other surface keeps the tokens above. */
  --gbar-bg:rgba(255,255,255,.64); --gbar-blur:26px; --gbar-sat:1.6;
  --gbar-edge:rgba(30,79,156,.18); --gbar-hi:rgba(255,255,255,.95); --gbar-rim:rgba(0,40,70,.14);
  --gbar-cap-bg:rgba(255,255,255,.55); --gbar-cap-shadow:0 2px 6px rgba(0,40,70,.18);
  --t-cap:180ms;
  /* 19 Sep owner correction: every button filled, navy or gold, white
     text, never a light outlined "secondary" -- fixed across both themes
     (not --navy, which lifts to a pale blue in dark and would fail white
     text at 4.5:1), so a button reads as the same navy everywhere. */
  --btn-navy-bg:rgba(15,76,129,.85); --btn-navy-text:#fff;
  /* The inks are set in style.css, which imports this file at its head
     and therefore wins the cascade; nothing here may redefine them. */
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --glass-bg:rgba(8,38,62,.62); --glass-bg-strong:rgba(8,38,62,.70); --glass-bg-weak:rgba(20,58,88,.50);
    --glass-edge:rgba(232,238,242,.2); --glass-hi:rgba(255,255,255,.18);
    --glass-shadow:0 1px 2px rgba(0,0,0,.25), 0 10px 28px -8px rgba(0,0,0,.45);
    --glass-shadow-pop:0 2px 6px rgba(0,0,0,.3), 0 18px 48px -12px rgba(0,0,0,.6);
    --amb-1:rgba(141,192,224,.16); --amb-2:rgba(224,145,58,.07); --amb-3:rgba(95,176,224,.12);
    --gbar-bg:rgba(8,38,62,.55); --gbar-edge:rgba(232,238,242,.22); --gbar-hi:rgba(255,255,255,.22);
    --gbar-rim:rgba(0,0,0,.35); --gbar-cap-bg:rgba(255,255,255,.16); --gbar-cap-shadow:0 2px 8px rgba(0,0,0,.4);
  }
}
:root[data-theme="dark"] {
  --glass-bg:rgba(8,38,62,.62); --glass-bg-strong:rgba(8,38,62,.70); --glass-bg-weak:rgba(20,58,88,.50);
  --glass-edge:rgba(232,238,242,.2); --glass-hi:rgba(255,255,255,.18);
  --glass-shadow:0 1px 2px rgba(0,0,0,.25), 0 10px 28px -8px rgba(0,0,0,.45);
  --glass-shadow-pop:0 2px 6px rgba(0,0,0,.3), 0 18px 48px -12px rgba(0,0,0,.6);
  --amb-1:rgba(141,192,224,.16); --amb-2:rgba(224,145,58,.07); --amb-3:rgba(95,176,224,.12);
  --gbar-bg:rgba(8,38,62,.55); --gbar-edge:rgba(232,238,242,.22); --gbar-hi:rgba(255,255,255,.22);
  --gbar-rim:rgba(0,0,0,.35); --gbar-cap-bg:rgba(255,255,255,.16); --gbar-cap-shadow:0 2px 8px rgba(0,0,0,.4);
}

/* ---------- the ambient behind the page ---------- */
/* A fixed pseudo-element, not background-attachment:fixed, which repaints
   on every scroll on phones. Three soft radial washes on the paper so the
   blur has something to blur. */
html { overflow-x:clip }
body { position:relative; background:var(--paper) }
body::before { content:""; position:fixed; inset:0; z-index:-1; pointer-events:none;
  background:
    radial-gradient(60rem 36rem at -10% -10%, var(--amb-1), transparent 60%),
    radial-gradient(48rem 32rem at 110% 10%, var(--amb-3), transparent 60%),
    radial-gradient(40rem 28rem at 60% 110%, var(--amb-2), transparent 60%),
    var(--paper) }

/* ---------- the surface ---------- */
/* Two surfaces, one rule (docs/design-system.md, "Glass or solid"):
   GLASS, blurred, where the eye passes over it and the ambient behind gives
   the blur something to show: the bar, the footer, popovers and bottom
   sheets, the login card, the short KPI tickers, the small link cards.
   SOLID paper for anything dense: a table, a chart, a paragraph longer than
   two lines. A blur under a table costs GPU on every scroll and a
   translucent ground under long text costs legibility; neither is worth
   the effect. Both share the edge, the top highlight and the shadow, so the
   family reads as one. */
body .top, body .foot,
body .tk, body .card, body .sheet, body .hero, body .act-kpi, body .act-sec,
body .up-form, body .up-preview, body .up-result, body .up-history,
body .card-link, body .toc, body .fb-id, body .box, body .daypanel, body .rp-note,
body .menu {
  background:var(--glass-bg);
  border:1px solid var(--glass-edge);
  box-shadow:inset 0 1px 0 var(--glass-hi), var(--glass-shadow);
  -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-sat));
          backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-sat));
}
/* The solid set: dense content. */
body .card, body .sheet, body .hero, body .act-sec, body .up-preview, body .up-history,
body .daypanel, body .toc {
  background:var(--paper-2); -webkit-backdrop-filter:none; backdrop-filter:none }
body .tk, body .card, body .sheet, body .hero, body .act-kpi, body .act-sec,
body .up-form, body .up-preview, body .up-result, body .up-history,
body .card-link, body .toc, body .fb-id, body .box, body .daypanel { border-radius:var(--glass-r) }
/* Keep the coloured top rule the ticker cards and the activity KPIs carry. */
body .tk { border-top-width:3px; border-top-color:var(--navy) }
body .act-kpi { border-top:3px solid var(--navy) }
body .card-link { border-top:3px solid var(--navy) }
/* map-page.css also names a .mv (the map viewport); the ticker's move line
   stays in flow whatever else is loaded. */
body .tk .mv { position:static; inset:auto; overflow:visible }
body .tk.accent { border-color:var(--accent); box-shadow:inset 0 0 0 1px var(--accent), var(--glass-shadow) }
/* A ticker card that grows a chart (an inline sparkline is fine) goes solid
   too: the blurred canvas behind would cost GPU on every redraw. */
body .tk:has(canvas), body .act-kpi:has(canvas) {
  -webkit-backdrop-filter:none; backdrop-filter:none; background:var(--paper-2) }

/* ---------- the bar ---------- */
/* TK-2026-0102: the desktop bar takes the same material as the phone's
   floating tab bar (docs/liquid-glass-plan.md §2 and §5) -- the --gbar-*
   tint and rim, not a new blur layer (.top already had backdrop-filter). */
body .top { position:sticky; top:0; z-index:1200;
  background:var(--gbar-bg); border-width:0 0 1px; border-color:var(--gbar-edge);
  box-shadow:inset 0 1px 0 var(--gbar-hi), inset 0 -1px 0 var(--gbar-rim), 0 8px 24px -12px rgba(0,40,70,.18);
  -webkit-backdrop-filter:blur(var(--gbar-blur)) saturate(var(--gbar-sat));
          backdrop-filter:blur(var(--gbar-blur)) saturate(var(--gbar-sat));
  padding-left:env(safe-area-inset-left,0px); padding-right:env(safe-area-inset-right,0px) }
/* 19 Sep owner review, on TK-2026-0102: on a wide screen the bar becomes a
   floating pill of the same material, like the Instagram reference --
   rounded ends, lifted off the top edge with a small margin, not a flush
   full-width strip. Only at the single-row desktop width: under 1024px
   the bar wraps to two rows (style.css) and a pill shape does not fit a
   wrapped header. */
@media (min-width:1024px) {
  body .top { top:12px; border-width:1px;
    margin:12px max(16px, env(safe-area-inset-right,0px)) 0 max(16px, env(safe-area-inset-left,0px));
    border-radius:var(--r-chip);
    box-shadow:inset 0 1px 0 var(--gbar-hi), inset 0 -1px 0 var(--gbar-rim), var(--glass-shadow-pop) }
}
/* The active tab: a lighter capsule instead of the underline. Centering the
   row (style.css has it stretch to the bar's full height) and giving each
   link its own height is what makes the capsule float inside the bar
   instead of reading as a full-height block -- the same "capsule sits
   inside, does not touch the edges" shape as .gbar-cap on the phone. */
body .top-nav { align-items:center }
body .top-nav a:not(.btn-upload) { border-top:0; border-bottom:0; border-radius:var(--r-chip); min-height:36px }
body .top-nav a[aria-current="page"]:not(.btn-upload) {
  color:var(--navy); background:var(--glass-bg-weak); box-shadow:inset 0 1px 0 var(--glass-hi);
  transition:background-color var(--t-cap) var(--ease-out) }
body .top-nav a:active { opacity:.7 }
body .pill, body .lang, body .lockstate, body .top .settings-btn {
  background:var(--glass-bg-weak); border:1px solid var(--glass-edge); border-radius:var(--r-ctl);
  box-shadow:inset 0 1px 0 var(--glass-hi) }
body .lockstate { padding:0 8px; height:28px }
body .lang button { height:28px; min-width:36px; transition:opacity var(--t-fast) var(--ease-out) }
body .lang button.on { background:var(--navy); color:var(--on-navy) }
/* 19 Sep: "nothing blue fill at 100 percent opacity anywhere in the bar" --
   the language switch's pressed state is the one solid-navy fill left in
   .top; inside the bar specifically it becomes the same lighter glass
   capsule as everything else there (the phone sheet's own .lang keeps the
   solid pressed fill, a different, non-"bar" context). */
/* style.css's own ".top .top-side .lang button.on" (4 classes) otherwise
   beats this on specificity alone; matched here rather than shortened. */
body .top .top-side .lang button.on { background:var(--glass-bg-weak); color:var(--navy); box-shadow:inset 0 1px 0 var(--glass-hi) }
/* The date chip and the search/utility pills read as full capsules inside
   the bar, not the smaller --r-ctl radius the same class uses elsewhere. */
body .top .pill { border-radius:var(--r-chip) }
/* Upload keeps its own gold fill, capsule-shaped like every other control. */
body .top-nav a.btn-upload { border-radius:var(--r-chip); box-shadow:inset 0 1px 0 rgba(255,255,255,.35) }

/* ---------- the phone floating tab bar ---------- */
/* Hidden by default; the phone media query below turns it on under 768px,
   where top-nav's own five links are already hidden (style.css). Icon-only:
   each item's name lives in a visually-hidden span so the reader with a
   screen reader gets the same translated word top-nav's link shows, without
   a second copy of the label on screen -- docs/liquid-glass-plan.md, "the
   floating geometry". One blurred layer here, and the bar is the only thing
   under 15% of a 375-wide viewport that gets it; nothing scrolls inside it. */
.gbar { display:none }
@media (max-width:767px) {
  .gbar {
    display:flex; align-items:center; justify-content:space-around; gap:2px;
    position:fixed; z-index:1150;
    left:max(16px, env(safe-area-inset-left,0px)); right:max(16px, env(safe-area-inset-right,0px));
    bottom:calc(16px + env(safe-area-inset-bottom,0px));
    height:60px; padding:0 4px;
    background:var(--gbar-bg); border:1px solid var(--gbar-edge); border-radius:var(--r-chip);
    /* The rim: a light 1px top inset (the refraction hint) and a faint
       darker inset along the bottom edge. 20 Sep, an iPhone screenshot:
       the family's usual outer shadow (--glass-shadow-pop, an 18/48px
       blur with a -12px spread) sits only 16px above the viewport's own
       bottom edge here and some phone browsers render that spread as a
       smeared band rather than a soft fade -- "hugging the bottom wall".
       A small, low-alpha shadow of its own instead: enough to lift the
       bar off the page, nothing that reaches far enough to hit the
       viewport edge. */
    box-shadow:inset 0 1px 0 var(--gbar-hi), inset 0 -1px 0 var(--gbar-rim), 0 4px 16px rgba(0,40,70,.14);
    -webkit-backdrop-filter:blur(var(--gbar-blur)) saturate(var(--gbar-sat));
            backdrop-filter:blur(var(--gbar-blur)) saturate(var(--gbar-sat));
  }
  .gbar-item {
    flex:1 1 0; min-width:44px; min-height:44px; display:flex; align-items:center; justify-content:center;
    color:var(--ink-soft); text-decoration:none; -webkit-tap-highlight-color:transparent;
  }
  .gbar-item[aria-current="page"] { color:var(--navy) }
  .gbar-cap {
    display:flex; align-items:center; justify-content:center; width:46px; height:38px; border-radius:var(--r-chip);
    transition:transform var(--t-fast) var(--ease-out);
  }
  .gbar-item[aria-current="page"] .gbar-cap {
    background:var(--gbar-cap-bg); box-shadow:inset 0 1px 0 var(--gbar-hi), var(--gbar-cap-shadow);
  }
  .gbar-item:active .gbar-cap { transform:scale(.90) }
  .gbar-lbl {
    /* Visually hidden, not display:none: an accessible name, read by a
       screen reader, that never takes screen space. */
    position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
    clip:rect(0,0,0,0); white-space:nowrap; border:0;
  }
  /* The content and footer clearance for the bar's own height is set in
     the phones media query near the end of this file, not here: it touches
     .wrap/.page/.foot, which "the footer" section below also sets, and
     needs to come after that section in source order to win (same
     specificity either way). A weak device (docs/liquid-glass-plan.md, "the
     performance rules") drops the blur and saturate on the bar and shows it
     as a plain tinted pill -- html.glass-lite is set once, before first
     paint, by the inline script at the top of _top.html (TK-2026-0107), on
     deviceMemory <= 2 or hardwareConcurrency <= 4 on a touch device, no
     backdrop-filter support, or prefers-reduced-transparency. The generic
     html.glass-lite rule near the fallbacks section (below) covers every
     other surface; the bar keeps this own copy because it adds its own
     box-shadow instead of just dropping to the shared one. */
  html.glass-lite .gbar {
    background:var(--paper-2); -webkit-backdrop-filter:none; backdrop-filter:none;
    box-shadow:0 1px 0 var(--glass-edge), 0 4px 16px rgba(0,40,70,.14);
  }
}

/* ---------- popovers: rename, options, add chart ---------- */
body .menu { border-radius:var(--glass-r-pop); background:var(--glass-bg-strong);
  box-shadow:inset 0 1px 0 var(--glass-hi), var(--glass-shadow-pop);
  -webkit-backdrop-filter:blur(var(--glass-blur-pop)) saturate(var(--glass-sat));
          backdrop-filter:blur(var(--glass-blur-pop)) saturate(var(--glass-sat));
  transform-origin:top right }
body .ctools .menu, body .hero-tools .menu { transform-origin:top right }
body .menu.open { animation:glass-pop var(--t-base) var(--ease-out) }
@keyframes glass-pop { from { opacity:0; transform:scale(.96) translateY(-4px) } to { opacity:1; transform:none } }

/* ---------- controls on glass ---------- */
body .seg { border-radius:var(--r-ctl); overflow:hidden; border:1px solid var(--glass-edge);
  background:var(--glass-bg-weak); box-shadow:inset 0 1px 0 var(--glass-hi) }
body .seg button { background:transparent; min-height:44px;
  transition:transform var(--t-fast) var(--ease-out), background-color var(--t-cap) var(--ease-out) }
body .seg button:active { transform:scale(.97) }
body .seg button.on { background:var(--navy); color:var(--on-navy) }
/* Every pill-shaped toggle gets the same 180ms capsule move: .seg is the
   generic segmented control, .lang and .pfab's chips/tools are the other
   two families of "one option lit up" control on the site. */
body .lang button, body .pfab .chip, body .pfab .tool, body .aud button, body .fb-scale button, body .fb-vote button {
  transition:background-color var(--t-cap) var(--ease-out), color var(--t-cap) var(--ease-out), transform var(--t-fast) var(--ease-out) }
body .ctl, body .btn, body .more, body .up-btn, body .fb-btn, body .fb-scale button, body .fb-vote button,
body .fb-id input, body .fb-id select, body .fb-filters select, body .up-field input, body .fb-q textarea,
body .maplayers .chip, body .aud button, body .title-btn, body .qmark, body .fb-btn.ghost {
  transition:transform var(--t-fast) var(--ease-out), opacity var(--t-fast) var(--ease-out) }
body .btn, body .more, body .up-btn, body .fb-btn, body .fb-scale button, body .fb-vote button, body .ctl,
body .fb-id input, body .fb-id select, body .fb-filters select, body .up-field input, body .fb-q textarea {
  border-radius:var(--r-ctl) }
/* Form fields stay the light weak-glass surface -- typing on a filled navy
   field reads badly. Only actual buttons move to the filled recipe below. */
body .ctl, body .fb-id input, body .fb-id select, body .fb-filters select, body .up-field input, body .fb-q textarea {
  background:var(--glass-bg-weak); border-color:var(--edge-firm); box-shadow:inset 0 1px 0 var(--glass-hi) }
/* 19 Sep owner correction: no outlined/light "secondary" button any more --
   every button is filled, navy or gold, white text. Secondary (everything
   that isn't the page's one primary action) is --btn-navy-bg, a fixed navy
   at ~85% so it reads as the same colour in both themes (unlike --navy,
   which lifts to a pale blue in dark and would fail white-text contrast),
   blurred like every other glass surface. .primary is the page's one gold
   action (Apply, Save, Upload already has its own .btn-upload gold). */
body .btn:not(.on):not(.primary), body .more, body .up-btn:not(.up-btn-navy):not(.up-btn-gold),
body .fb-scale button:not(.on), body .fb-btn:not(.ghost) {
  background:var(--btn-navy-bg); color:var(--btn-navy-text); border-color:transparent;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22);
  -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-sat));
          backdrop-filter:blur(var(--glass-blur)) saturate(var(--glass-sat)) }
body .btn:not(.on):not(.primary):hover, body .more:hover, body .fb-btn:not(.ghost):hover { filter:brightness(1.1) }
body .btn.primary, body .fb-btn.primary {
  background:var(--accent-ink); color:var(--on-navy); border-color:transparent;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.35) }
body .btn.primary:hover { filter:brightness(1.08) }
body .fb-btn.ghost { background:var(--glass-bg-weak); color:var(--ink-soft); border-color:var(--edge-firm) }
body .btn:active, body .more:active, body .up-btn:active, body .fb-btn:active, body .fb-scale button:active,
body .fb-vote button:active, body .aud button:active, body .maplayers .chip:active, body .box button:active { transform:scale(.97) }
body .up-drop { border-radius:var(--glass-r); background:var(--glass-bg-weak) }
body .fb-chip { border-radius:var(--r-chip) }
/* TK-2026-0084: 44px touch height and a visible focus ring on every field
   in the feedback panel, the ticket form's own controls and .ctl. */
body .fb-id input, body .fb-id select, body .fb-filters select, body .fb-q textarea,
body .ctl, body .fb-scale button, body .fb-vote button, body .fb-btn {
  min-height:44px }
body .fb-id input::placeholder, body .fb-q textarea::placeholder { color:var(--ink-faint); opacity:1 }
body .fb-id input:focus-visible, body .fb-id select:focus-visible, body .fb-filters select:focus-visible,
body .fb-q textarea:focus-visible, body .ctl:focus-visible {
  outline:var(--focus-w) solid var(--focus-color); outline-offset:var(--focus-offset) }

/* ---------- the footer ---------- */
body .foot { border-radius:var(--glass-r) var(--glass-r) 0 0; border-bottom:0;
  padding-bottom:calc(16px + env(safe-area-inset-bottom,0px)) }

/* ---------- refraction: the bar and the popovers only ---------- */
/* The SVG filter lives in _foot.html. Only engines that accept an SVG
   filter inside backdrop-filter take this branch (Chromium today); the
   rest keep the plain blur. Never on a phone: the bar is repainted on every
   scroll frame there and the displacement is not worth it. */
@supports (backdrop-filter: url("#glass-refract")) or (-webkit-backdrop-filter: url("#glass-refract")) {
  @media (min-width:768px) and (prefers-reduced-motion: no-preference) and (prefers-reduced-transparency: no-preference) {
    body .top {
      -webkit-backdrop-filter:url("#glass-refract") blur(var(--gbar-blur)) saturate(var(--gbar-sat));
              backdrop-filter:url("#glass-refract") blur(var(--gbar-blur)) saturate(var(--gbar-sat)) }
    body .menu {
      -webkit-backdrop-filter:url("#glass-refract") blur(var(--glass-blur-pop)) saturate(var(--glass-sat));
              backdrop-filter:url("#glass-refract") blur(var(--glass-blur-pop)) saturate(var(--glass-sat)) }
  }
}
.glass-defs { position:absolute; width:0; height:0; overflow:hidden; pointer-events:none }

/* ---------- fallbacks ---------- */
/* Every surface that actually sets backdrop-filter anywhere on the site,
   in one place: a control filled with --glass-bg-weak and no blur of its
   own (.seg, .pill, .aud, a capsule) is already cheap and never needs a
   fallback here, only a surface that blurs. Kept as one list so the three
   gates below (no support, reduced transparency, a weak device) always
   agree on what "go solid" means. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  body .top, body .foot, body .tk, body .card, body .sheet, body .hero, body .act-kpi, body .act-sec,
  body .up-form, body .up-preview, body .up-result, body .up-history, body .card-link, body .toc,
  body .fb-id, body .box, body .daypanel, body .rp-note, body .menu, body .xs-menu, body .ordbar,
  body .deck-arrange-card, body .glass { background:var(--paper-2) }
  .gbar { background:var(--paper-2) }
}
@media (prefers-reduced-transparency: reduce) {
  body::before { background:var(--paper) }
  body .top, body .foot, body .tk, body .card, body .sheet, body .hero, body .act-kpi, body .act-sec,
  body .up-form, body .up-preview, body .up-result, body .up-history, body .card-link, body .toc,
  body .fb-id, body .box, body .daypanel, body .rp-note, body .menu, body .seg, body .pill, body .lang,
  body .lockstate, body .ctl, body .btn, body .more, body .xs-menu, body .ordbar, body .deck-arrange-card,
  body .glass { background:var(--paper-2);
    -webkit-backdrop-filter:none; backdrop-filter:none }
  .gbar { background:var(--paper-2); -webkit-backdrop-filter:none; backdrop-filter:none }
}
@media (prefers-reduced-motion: reduce) {
  body .menu.open { animation:none }
  body * { transition-duration:0ms !important }
  .gbar-cap { transition:none !important }
}
/* TK-2026-0107: a weak device -- decided once, before first paint, by the
   inline script at the top of _top.html (no CSS.supports/matchMedia check
   belongs in JS when a pure-CSS gate above already covers it, but the
   script folds those two in as well so every consumer that only wants to
   ask "is this page in lite mode" has one flag, on <html>, instead of
   three separate checks). The class-based gate below intentionally
   mirrors the same surface list as the two gates above, not a shorter
   one: a device that qualifies here gets exactly the same solid fallback,
   never a partial one. */
html.glass-lite body .top, html.glass-lite body .foot, html.glass-lite body .tk, html.glass-lite body .card,
html.glass-lite body .sheet, html.glass-lite body .hero, html.glass-lite body .act-kpi, html.glass-lite body .act-sec,
html.glass-lite body .up-form, html.glass-lite body .up-preview, html.glass-lite body .up-result,
html.glass-lite body .up-history, html.glass-lite body .card-link, html.glass-lite body .toc,
html.glass-lite body .fb-id, html.glass-lite body .box, html.glass-lite body .daypanel, html.glass-lite body .rp-note,
html.glass-lite body .menu, html.glass-lite body .xs-menu, html.glass-lite body .ordbar,
html.glass-lite body .deck-arrange-card, html.glass-lite body .glass {
  background:var(--paper-2); -webkit-backdrop-filter:none; backdrop-filter:none }
/* .gbar's own html.glass-lite rule lives with the phone bar above: it adds
   a box-shadow the generic rule here does not need to repeat. */

/* ---------- phones: 375 and 414 ---------- */
@media (max-width:767px) {
  html { overflow-x:clip }
  body .top { top:0 }
  body .top-in { padding-left:max(12px, env(safe-area-inset-left,0px)); padding-right:max(12px, env(safe-area-inset-right,0px)) }
  /* The bar collapses to the mark, the title, the language switch and the
     gear; the date pill goes, the latch keeps its dot. The language switch is
     never hidden: every reader must be able to reach it on every screen. */
  body .top-brand { height:52px }
  body .top-in:has([class*="gear"]) .lockstate { display:none }
  body .top .lang { display:inline-flex !important; margin-right:4px }
  body .top .lang button { min-width:36px; min-height:36px; padding:0 8px }
  /* The tabs: one strip that scrolls sideways and snaps, 44 px tall. */
  body .top-nav { height:44px; scroll-snap-type:x proximity; scroll-padding:0 12px; gap:0;
    padding-left:max(12px, env(safe-area-inset-left,0px)); padding-right:max(12px, env(safe-area-inset-right,0px)) }
  body .top-nav a { scroll-snap-align:start; min-height:44px; padding:0 12px }
  body .top-nav a.btn-upload { height:40px; line-height:40px; min-height:40px; margin:2px 0 2px 4px }
  body .top .settings-btn, body .top [class*="gear"] { min-width:44px; min-height:44px }
  body .lnk, body .back, body summary, body .up-how summary { min-height:44px; display:inline-flex; align-items:center }
  /* 44 px targets. */
  body .lang button, body .lockstate button, body .title-btn, body .seg button, body .btn, body .more,
  body .ctl, body .up-btn, body .fb-btn, body .fb-scale button, body .fb-vote button, body .aud button,
  body .maplayers .chip, body .qmark, body .frow, body button.tag, body .foot a, body .box button,
  body .fb-id select, body .fb-filters select, body .up-field input, body .fb-id input { min-height:44px }
  body .lang, body .lockstate, body .pill { height:44px }
  body .fb-scale button { width:2.75rem; height:2.75rem }
  body .qmark { width:2rem; height:2rem }
  body .foot a { display:inline-flex; align-items:center; padding:0 6px }
  body .foot { flex-direction:column; align-items:flex-start; gap:6px }
  /* Popovers become a sheet from the bottom edge. */
  body .menu { position:fixed; left:0; right:0; bottom:0; top:auto; width:auto; max-height:80dvh; overflow:auto;
    border-radius:var(--glass-r-pop) var(--glass-r-pop) 0 0; padding:.9rem 1rem calc(1rem + env(safe-area-inset-bottom,0px));
    transform-origin:bottom center; z-index:1300 }
  body .menu::before { content:""; display:block; width:36px; height:4px; margin:0 auto 12px; border-radius:2px;
    background:var(--glass-edge) }
  body .menu.open { animation:glass-sheet var(--t-base) var(--ease-out) }
  /* Two cards per row on the ticker and the activity KPIs; wide things scroll
     inside their card, never the page. */
  body .grid12 .span3 { grid-column:span 6 }
  body .act-kpis { grid-template-columns:repeat(2,minmax(0,1fr)) }
  body .card, body .sheet, body .act-sec, body .up-preview, body .up-history, body .daypanel { overflow-x:auto }
  /* A blurred ancestor is the containing block of a fixed sheet; the card
     that owns a popover gives up its blur so the sheet reaches the edge. */
  body .card:has(.menu), body .sheet:has(.menu), body .hero:has(.menu) { -webkit-backdrop-filter:none; backdrop-filter:none }
  body .legend { flex-wrap:wrap }
  /* Cards keep --r-card at every width now (owner, 18 Sep 2026: one radius
     scale, not one that steps down on a phone). */
  /* The floating tab bar's own footprint (16px lift + 60px height) plus a
     little breathing room, so the last card and the footer's links are
     never left sitting under it. Same specificity as "the footer" section
     and the base .wrap/.page rule in style.css; this wins by coming later. */
  body .wrap, body .page { padding-bottom:calc(40px + 76px + env(safe-area-inset-bottom,0px)) }
  body .foot { padding-bottom:calc(16px + 76px + env(safe-area-inset-bottom,0px)) }
  /* feedback-fab.js fixes its own launcher at left:14px bottom:14px with an
     inline <style>; body .pfab outranks that on specificity alone (0,2,0
     against 0,1,0) whatever order the two land in the DOM, so the button
     sits above the bar instead of behind its left end. */
  body .pfab { bottom:calc(76px + 14px + env(safe-area-inset-bottom,0px)) }
}
@keyframes glass-sheet { from { opacity:0; transform:translateY(16px) } to { opacity:1; transform:none } }
