/* ============================================================================
   mco-web-style · theme/mco-theme.css · v0.1.0
   The Montana Climate Office shared web house style.

   Consumed via pinned CDN tag + SRI (see README.md). Rules for using and
   extending this file live in HOUSE-STYLE.md; guardrails in AGENTS.md.

   Layers, top to bottom:
     1. Design tokens        — dark (:root), light, high-contrast themes
     2. Stacking ladder      — z-index tiers for every floating surface
     3. Reset & accessibility utilities
     4. MapLibre control polish
     5. Component shells     — navbar, toast, tooltip, modal, panel, scrim
     6. Responsive & touch

   Fonts ARE loaded here (since 0.7.0): Outfit + Space Mono are kit-hosted
   in fonts/ and declared at the top of §1, resolved relative to this file so
   they version with it. Consumers preload the two latin files from
   snippets/head.html; the tokens carry system fallbacks.
   ========================================================================== */

/* ── Fonts ────────────────────────────────────────────────────────────────
   Kit-hosted latin + latin-ext subsets of the Google Fonts builds (SIL OFL;
   license texts in fonts/). url() resolves against THIS stylesheet, so a page
   pinned to @X.Y.Z gets that tag's fonts. The latin files should be preloaded
   (snippets/head.html); latin-ext is fetched only when a page actually uses
   a character in its unicode-range.

   font-display: block, not swap. From Google Fonts with display=swap every
   MCO app painted its text in the system font and re-set it in Outfit a beat
   later — the title visibly jumped. Preloaded from the same origin as this
   render-blocking stylesheet, the fonts normally arrive before first paint;
   when they don't, block costs a few ms of invisible text rather than a
   re-set of every line, and the browser still falls back after ~3s if a file
   never arrives. Outfit is a variable font: one file per subset covers every
   weight in use (400–700). */
@font-face {
  font-family: 'Outfit'; font-style: normal; font-weight: 400 700; font-display: block;
  src: url(../fonts/outfit-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Outfit'; font-style: normal; font-weight: 400 700; font-display: block;
  src: url(../fonts/outfit-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Mono'; font-style: normal; font-weight: 400; font-display: block;
  src: url(../fonts/space-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Space Mono'; font-style: normal; font-weight: 400; font-display: block;
  src: url(../fonts/space-mono-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;
}

/* Metric-matched fallbacks (0.7.1). During the block period text is invisible
   but still LAID OUT — in the next family in the stack. Plain Arial sets the
   navbar title ~6% wider and its line box 3px shorter than Outfit, so items
   beside the title shifted when the webfont landed (seen on mesonet-status
   under a throttled connection). These faces rescale a local metric-stable
   font (Arial; Helvetica and Liberation Sans share its metrics) to Outfit's
   proportions, so the layout reserved during the block period is the one
   Outfit then fills.
   - size-adjust: Outfit/Arial advance-width ratio over a corpus of real MCO UI
     strings, measured in Chrome per weight, because Outfit's advances move
     with its weight axis and Arial only has 400 and 700.
   - ascent/descent/line-gap-override: Outfit's own metrics (1.000 / 0.260 /
     0, Space Mono 1.120 / 0.361 / 0) divided by size-adjust, which scales the
     overrides too. Line boxes then match exactly (verified: 15px vs 15px,
     where plain Arial gave 12px).
   - The ITALIC face is fitted to the one italic string in the house style,
     the lockup subtitle ("A service of the Montana Climate Office", §1 —
     every public MCO app carries it),
     which sets the lockup's width in most apps: 169.3px vs Outfit's 169.4px
     (letter-spacing excluded from the fit, since size-adjust doesn't scale
     it). Outfit has no italic, so the real face is a synthesized oblique of
     the upright — same advances as upright.
   Line boxes match exactly in every face. Widths: upright text lands within a
   few percent either way, since no single scale turns Arial's letterforms
   into Outfit's — uppercase Outfit is ~6% narrower than Arial capitals, so an
   uppercase title (weight 500) still reserves ~3–5% too much. Measured on
   all six consumers under a throttled connection (150 ms, 200 KB/s), the
   navbar item beside the lockup moved 16.6px when Outfit landed with plain
   Arial and 0.1px with these faces; mesonet-maintenance, whose uppercase
   title is wider than the subtitle, 11.9px → 5.2px. Space Mono vs Courier
   New is exact (both monospaced: 0.612em vs 0.600em advance). */
@font-face {
  font-family: 'Outfit Fallback'; font-style: normal; font-weight: 400 500;
  src: local('Arial'), local('ArialMT'), local('Helvetica'), local('Liberation Sans');
  size-adjust: 98.80%; ascent-override: 101.21%; descent-override: 26.32%; line-gap-override: 0.00%;
}
@font-face {
  font-family: 'Outfit Fallback'; font-style: normal; font-weight: 600;
  src: local('Arial Bold'), local('Arial-BoldMT'), local('Helvetica Bold'), local('Liberation Sans Bold');
  size-adjust: 95.06%; ascent-override: 105.20%; descent-override: 27.35%; line-gap-override: 0.00%;
}
@font-face {
  font-family: 'Outfit Fallback'; font-style: normal; font-weight: 700;
  src: local('Arial Bold'), local('Arial-BoldMT'), local('Helvetica Bold'), local('Liberation Sans Bold');
  size-adjust: 96.64%; ascent-override: 103.48%; descent-override: 26.90%; line-gap-override: 0.00%;
}
@font-face {
  font-family: 'Outfit Fallback'; font-style: italic; font-weight: 400 500;
  src: local('Arial Italic'), local('Arial-ItalicMT'), local('Helvetica Oblique'), local('Liberation Sans Italic');
  size-adjust: 100.58%; ascent-override: 99.42%; descent-override: 25.85%; line-gap-override: 0.00%;
}
@font-face {
  font-family: 'Space Mono Fallback'; font-style: normal; font-weight: 400;
  src: local('Courier New'), local('CourierNewPSMT'), local('Liberation Mono');
  size-adjust: 101.98%; ascent-override: 109.83%; descent-override: 35.40%; line-gap-override: 0.00%;
}

/* ── 1. Design tokens ─────────────────────────────────────────────────────
   Dark is the default theme. `data-theme` on <html> switches; the anti-flash
   snippet (snippets/anti-flash.html) sets it before first paint.

   Contrast contract (enforced by tools/check-contrast.mjs in CI):
     --text-primary, --text-secondary  ≥ 4.5:1 on deep, surface, AND raised
     --text-muted,  --text-dim         ≥ 4.5:1 on deep and surface ONLY —
                                         never use them on --bg-raised
     --accent-line                     ≥ 3:1 on all three surfaces (1.4.11)
     --text-on-accent                  ≥ 4.5:1 on --accent

   --accent is the MCO brand blue. On dark surfaces it only clears ~2.3–2.9:1,
   so it is a FILL color: use it behind --text-on-accent. For borders, icons,
   or text that must read against a surface, use --accent-line. */
:root {
  --font-ui:        'Outfit', 'Outfit Fallback', system-ui, -apple-system, sans-serif;
  --font-mono:      'Space Mono', 'Space Mono Fallback', ui-monospace, monospace;

  --bg-deep:        #161b22;
  --bg-surface:     #1e2530;
  --bg-raised:      #2c3545;
  --border:         #3a4558;
  --border-glass:   rgba(255,255,255,0.08);
  --text-primary:   #e8ecf0;
  --text-secondary: #c0cad6;
  --text-muted:     #8a99b0;
  --text-dim:       #8494ab;
  --ctrl-border:    #6a7996;
  --accent:         #1a6faf;
  --accent-light:   #5aaee8;
  --accent-dk:      #114f80;
  --accent-line:    #5aaee8;
  --accent-hover:   rgba(26,111,175,0.10);
  --text-on-accent: #ffffff;
  --selection-ring: #5aaee8;
  --brand-gradient: linear-gradient(90deg, var(--accent-dk), var(--accent), var(--accent-light), var(--accent));
  --glass:          rgba(30,37,48,0.82);
  --shadow:         0 4px 24px rgba(0,0,0,0.4);
  --transition:     0.2s cubic-bezier(0.4,0,0.2,1);
  --radius-sm:      4px;
  --radius-md:      8px;
  --radius-lg:      12px;
  --dot-stroke:     #ffffff;
  --input-bg:       #232a35;
  --scrim:          rgba(0,0,0,0.45);
  --ctrl-icon-filter: invert(1);
}

[data-theme="light"] {
  --bg-deep:        #f0f2f5;
  --bg-surface:     #ffffff;
  --bg-raised:      #e8ecf0;
  --border:         #c8cdd5;
  --border-glass:   rgba(0,0,0,0.08);
  --text-primary:   #1a1a2e;
  --text-secondary: #3a3f4b;
  --text-muted:     #5a6070;
  --text-dim:       #5f6675;
  --ctrl-border:    #7e8694;
  --accent:         #1a6faf;
  --accent-light:   #1563a0;
  --accent-dk:      #0d4f7a;
  --accent-line:    #1563a0;
  --accent-hover:   rgba(26,111,175,0.08);
  --text-on-accent: #ffffff;
  --selection-ring: #1563a0;
  --brand-gradient: linear-gradient(90deg, #0a4a7a, #1266a8, #0d7fd4, #1266a8, #0a4a7a);
  --glass:          rgba(255,255,255,0.88);
  --shadow:         0 4px 24px rgba(0,0,0,0.1);
  --dot-stroke:     #2a2a3a;
  --input-bg:       #f4f5f7;
  --scrim:          rgba(0,0,0,0.35);
  --ctrl-icon-filter: none;
}

/* High-contrast: pure-black surfaces, brightened accent, near-white text.
   Adapted from mco-drought-dashboard (the family's first HC theme), with the
   accent tints re-derived from THIS theme's accent (#5bb4ff = rgb(91,180,255))
   rather than the legacy value the dashboard still carries.
   --text-on-accent flips to black: white fails on the brightened accent. */
[data-theme="high-contrast"] {
  --bg-deep:        #000000;
  --bg-surface:     #000000;
  --bg-raised:      #121212;
  --border:         #8a8a8a;
  --border-glass:   rgba(255,255,255,0.28);
  --text-primary:   #ffffff;
  --text-secondary: #f5f5f5;
  --text-muted:     #dcdcdc;
  --text-dim:       #bcbcbc;
  --ctrl-border:    #8a8a8a;
  --accent:         #5bb4ff;
  --accent-light:   #93d0ff;
  --accent-dk:      #2070a8;
  --accent-line:    #93d0ff;
  --accent-hover:   rgba(91,180,255,0.15);
  --text-on-accent: #000000;
  --selection-ring: #93d0ff;
  --brand-gradient: linear-gradient(90deg, #2a9aff, #6ec0ff, #a0d8ff, #6ec0ff, #2a9aff);
  --glass:          rgba(0,0,0,0.96);
  --shadow:         0 4px 24px rgba(0,0,0,0.7);
  --dot-stroke:     #ffffff;
  --input-bg:       #121212;
  --scrim:          rgba(0,0,0,0.8);
  --ctrl-icon-filter: invert(1);
}

/* ── 2. Stacking ladder ───────────────────────────────────────────────────
   One ladder for every floating surface. Add to a tier — don't invent a
   number. MapLibre's own .maplibregl-ctrl-* containers ship with z-index 2
   from the vendor stylesheet, so anything meant to sit over the map controls
   (notably map popups, which ship with NO z-index) has to be above that.
   The detail tier stays BELOW the chrome so a docked detail surface can never
   cover the navbar; <dialog> lives in the browser's top layer and outranks
   all of these regardless. */
:root {
  --z-map-ctrl:      2;   /* vendor .maplibregl-ctrl-* containers          */
  --z-map-notice:   20;   /* empty-state / error callouts over the map     */
  --z-map-progress: 30;   /* loading bars and notes                        */
  --z-map-panel:    40;   /* docked panels (legend, sidebar) + scrim       */
  --z-detail:       60;   /* docked detail surface (station sheet etc.)    */
  --z-drawer:       70;   /* off-canvas drawer, over the detail surface    */
  --z-chrome:       90;   /* secondary chrome bars                         */
  --z-chrome-top:  100;   /* navbar                                        */
  --z-flyout:      150;   /* dropdowns / search results                    */
  --z-cursor:      200;   /* pointer-following tooltip                     */
  --z-toast:       400;   /* toast — above everything                      */
}

/* ── 3. Reset & accessibility utilities ──────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: var(--font-ui);
  background: var(--bg-deep);
  color: var(--text-primary);
  transition: background var(--transition), color var(--transition);
}

/* Reduced motion: blanket CSS clamp. Map camera animations are a JS concern —
   gate them with MCO.reducedMotion() (live-reactive), never a boot snapshot. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.001ms !important;
    animation-duration:  0.001ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* The UA's [hidden] { display: none } loses to any rule that sets display
   explicitly, so flex/grid elements stay visible as empty strips when hidden
   from JS. Restate it with priority. */
[hidden] { display: none !important; }

/* Visually-hidden text for screen-reader announcements + skip-link targets. */
.sr-only {
  position: absolute !important;
  width: 1px !important; height: 1px !important;
  padding: 0 !important; margin: -1px !important;
  overflow: hidden !important; clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important; border: 0 !important;
}

/* ONE universal keyboard focus ring. Never enumerate per-selector rules —
   a new control added later would ship without one. */
:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 2px; }

/* Skip link (WCAG 2.4.1): first focusable element on the page, jumps past the
   chrome. Markup in snippets/skip-link.html. */
.mco-skip-link {
  position: fixed; left: 0.5rem; top: -100px;
  z-index: calc(var(--z-toast) + 10);
  padding: 0.5rem 0.9rem;
  background: var(--accent); color: var(--text-on-accent);
  border-radius: var(--radius-md);
  font-weight: 600; font-size: 0.85rem; text-decoration: none;
}
.mco-skip-link:focus { top: 0.5rem; }

/* ── 4. MapLibre control polish ───────────────────────────────────────────
   Glass treatment for the vendor controls so they read as part of the app
   chrome. --ctrl-icon-filter inverts the vendor's dark icon glyphs on dark
   themes. */
.maplibregl-ctrl-group {
  background: var(--glass) !important;
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--ctrl-border) !important;
  border-radius: var(--radius-md) !important;
  box-shadow: var(--shadow) !important;
  transition: background var(--transition), border-color var(--transition);
}
.maplibregl-ctrl-group button {
  background: transparent !important;
  border-color: var(--ctrl-border) !important;
  transition: background var(--transition);
}
.maplibregl-ctrl-group button:hover { background: var(--accent-hover) !important; }
.maplibregl-ctrl-group button .maplibregl-ctrl-icon { filter: var(--ctrl-icon-filter, none); }
/* Icon for MCO.map.addFitControl()'s "zoom to full extent" button. The
   stroke is dark like the vendor icons so --ctrl-icon-filter flips it too. */
.maplibregl-ctrl-fit .maplibregl-ctrl-icon {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23202020' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='4 8 4 4 8 4'/><polyline points='20 8 20 4 16 4'/><polyline points='4 16 4 20 8 20'/><polyline points='20 16 20 20 16 20'/></svg>");
  background-size: 60% 60%;
  background-position: center;
  background-repeat: no-repeat;
}
.maplibregl-ctrl-attrib { background: var(--glass) !important; color: var(--text-muted) !important; }
.maplibregl-ctrl-attrib a { color: var(--text-secondary) !important; }
.maplibregl-ctrl-attrib button.maplibregl-ctrl-attrib-button { filter: var(--ctrl-icon-filter, none); }
/* Vendor default is 2; restated so the whole stacking ladder is greppable.
   With viewport-fit=cover the corner containers need the safe-area insets
   themselves, or the attribution lands under the home indicator. NOTE:
   env() is inert unless the page sets viewport-fit=cover in its viewport
   meta — see snippets/head.html. */
.maplibregl-ctrl-top-left, .maplibregl-ctrl-top-right,
.maplibregl-ctrl-bottom-left, .maplibregl-ctrl-bottom-right {
  z-index: var(--z-map-ctrl);
}
.maplibregl-ctrl-bottom-left, .maplibregl-ctrl-bottom-right {
  padding-bottom: env(safe-area-inset-bottom);
}
.maplibregl-ctrl-top-left, .maplibregl-ctrl-bottom-left { padding-left: env(safe-area-inset-left); }
.maplibregl-ctrl-top-right, .maplibregl-ctrl-bottom-right { padding-right: env(safe-area-inset-right); }

/* ── 5. Component shells ──────────────────────────────────────────────────── */

/* Navbar. Glass bar with the brand-gradient underline. Structure:
   .mco-navbar > .logo-link + .nav-divider + .brand + .controls + .nav-meta */
.mco-navbar {
  flex-shrink: 0;
  position: relative;
  /* --nav-gap drives BOTH the flex gap and the lockup divider's negative
     margin, so the brand rhythm survives any change to the gap (§6 tightens it
     at ≤1060px). Overriding the gap directly would desynchronise them. */
  --nav-gap: 0.75rem;
  display: flex; align-items: center; gap: var(--nav-gap);
  /* Left padding matches the vertical padding so the 40px logo badge sits
     with square margins in the 52px bar (≈6px on all three sides). */
  padding: 0.4rem max(1rem, env(safe-area-inset-right)) 0.4rem max(0.4rem, env(safe-area-inset-left));
  min-height: 52px;
  background: var(--glass);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap; row-gap: 0.4rem;
  z-index: var(--z-chrome-top);
}
.mco-navbar::after {
  content: ''; position: absolute; bottom: 0; left: 0; right: 0;
  height: 2px; background: var(--brand-gradient); pointer-events: none;
}
.logo-link { display: flex; flex-shrink: 0; }
.logo-img  { width: 40px; height: 40px; border-radius: 6px; display: block; }
.nav-divider { width: 1px; height: 28px; background: var(--border); flex-shrink: 0; }
/* The brand lockup reads as one unit: pull the divider in so logo ↔ divider ↔
   title sit at 0.4rem — the same rhythm as the squared 0.4rem edge margins —
   while the full navbar gap resumes after the lockup. Derived from --nav-gap
   rather than hard-coded, so tightening the gap can't squeeze the lockup: at
   the 0.75rem default this is −0.35rem, and at the ≤1060px 0.5rem gap it
   becomes −0.1rem, holding the 0.4rem rhythm either way. */
.mco-navbar > .nav-divider { margin-inline: calc(0.4rem - var(--nav-gap)); }
.brand { display: flex; flex-direction: column; justify-content: center; flex-shrink: 0; line-height: 1.2; }
.brand-title    { font-size: 0.65rem; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); white-space: nowrap; margin: 0; }
.brand-subtitle { font-size: 0.55rem; font-weight: 400; letter-spacing: 0.04em; font-style: italic; color: var(--text-dim); white-space: nowrap; }
.controls { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; row-gap: 0.3rem; }
.nav-meta { display: flex; align-items: center; gap: 0.4rem; flex-shrink: 0; margin-left: auto; }

/* Buttons. Pressed/hover borders use --accent-line, NOT --accent: the brand
   blue only clears ~2.3–2.9:1 against dark surfaces (WCAG 1.4.11 needs 3:1). */
.nav-btn {
  display: flex; align-items: center; gap: 5px;
  padding: 6px 12px; min-height: 34px;
  border: 1px solid var(--ctrl-border); border-radius: var(--radius-md);
  background: none; color: var(--text-secondary);
  cursor: pointer; font-size: 0.78rem; font-weight: 500;
  font-family: inherit; white-space: nowrap; flex-shrink: 0;
  transition: var(--transition);
}
.nav-btn:hover { background: var(--accent-hover); border-color: var(--accent-line); color: var(--text-primary); }
.nav-btn svg { flex-shrink: 0; stroke: currentColor; }
.nav-btn.icon-only { padding: 0.4rem; min-width: 34px; justify-content: center; }
.nav-btn[aria-pressed="true"] { background: var(--accent-hover); border-color: var(--accent-line); color: var(--text-primary); }
.nav-btn[disabled] { opacity: 0.4; cursor: not-allowed; }
.nav-btn[disabled]:hover { background: none; border-color: var(--ctrl-border); color: var(--text-secondary); }

.mco-btn-info {
  border-radius: 50%; border-color: var(--accent-line);
  color: var(--accent-line); font-size: 1rem; font-weight: 600;
  padding: 0; width: 34px; height: 34px; min-height: 0;
  display: flex; align-items: center; justify-content: center;
}
.mco-btn-info:hover { background: var(--accent); color: var(--text-on-accent); }

/* Segmented buttons: a .nav-btn group with fused borders. Deliberately a
   step shorter than the 34px nav buttons — a same-height fused group reads
   as heavy in the bar; flex centering keeps the baselines aligned. */
.seg-btns { display: flex; gap: 0; }
.seg-btn {
  padding: 4px 10px; min-height: 30px; height: 30px;
  border-radius: 0; border-right-width: 0;
  font-size: 0.74rem; font-weight: 600;
}
.seg-btn:first-child { border-radius: var(--radius-md) 0 0 var(--radius-md); }
.seg-btn:last-child  { border-radius: 0 var(--radius-md) var(--radius-md) 0; border-right-width: 1px; }
.seg-btn[aria-pressed="true"] { background: var(--accent); border-color: var(--accent-line); color: var(--text-on-accent); }

/* Refresh/liveness status: read-only, deliberately subordinated. Add .static
   when the view isn't live (historical modes) to stop the pulse. */
.refresh-status {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-mono);
  font-size: 0.65rem; color: var(--text-muted);
  white-space: nowrap;
  padding: 0 4px;
}
.refresh-status .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent-line);
  box-shadow: 0 0 0 2px var(--accent-hover);
  animation: mco-refresh-pulse 2.4s ease-in-out infinite;
}
@keyframes mco-refresh-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}
@media (prefers-reduced-motion: reduce) {
  .refresh-status .dot { animation: none; }
}
.refresh-status.static .dot { animation: none; box-shadow: none; opacity: 0.55; }

/* Toast. One per page; created for you by MCO.showToast() if absent. */
.mco-toast {
  position: fixed; bottom: 1.5rem; left: 50%;
  transform: translateX(-50%) translateY(0.75rem);
  background: var(--bg-surface);
  border: 1px solid var(--accent-line);
  border-radius: 6px; padding: 0.5rem 1rem;
  font-size: 0.82rem; color: var(--text-primary);
  opacity: 0; transition: opacity 0.2s, transform 0.2s;
  pointer-events: none; z-index: var(--z-toast);
  box-shadow: var(--shadow);
}
.mco-toast.visible { opacity: 1; transform: translateX(-50%) translateY(0); }

/* Pointer-following tooltip. Decorative for AT: keep it aria-hidden and route
   the same content through a live region (see HOUSE-STYLE.md §5). */
.mco-tooltip {
  position: fixed;
  background: var(--glass);
  border: 1px solid var(--accent-line);
  border-radius: 5px;
  padding: 0.3rem 0.6rem;
  font-size: 0.75rem; font-weight: 500;
  color: var(--text-primary);
  pointer-events: none;
  z-index: var(--z-cursor);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.1s;
  backdrop-filter: blur(8px);
}
.mco-tooltip.visible { opacity: 1; }
.mco-tooltip .tooltip-name { font-size: 0.78rem; font-weight: 600; color: var(--text-primary); line-height: 1.2; }
.mco-tooltip .tooltip-sub {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.65rem;
  color: var(--text-muted);
  margin-top: 1px;
}
.mco-tooltip .tooltip-val {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--accent-line);
  font-weight: 600;
  margin-top: 3px;
}

/* Modal: native <dialog class="mco-modal"> wired by MCO.initInfoModal()
   (backdrop click closes, focus restores to the opener). */
.mco-modal { background: transparent; border: none; padding: 1rem; position: fixed; inset: 0; margin: auto; width: fit-content; height: fit-content; max-width: calc(100vw - 2rem); max-height: calc(100dvh - 2rem); }
.mco-modal::backdrop { background: rgba(0,0,0,0.7); backdrop-filter: blur(4px); }
.info-modal-box {
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: 10px; width: min(540px, calc(100vw - 4rem));
  max-height: calc(100dvh - 4rem); overflow-y: auto;
  padding: 1.5rem; box-shadow: var(--shadow);
}
.info-modal-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1.25rem; gap: 1rem; }
.info-modal-title    { font-size: 1.05rem; font-weight: 700; color: var(--accent-line); margin: 0; }
.info-modal-subtitle { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.2rem; }
.modal-close { display: flex; align-items: center; justify-content: center; background: none; border: 1px solid var(--border); border-radius: 6px; color: var(--text-muted); font-size: 1.2rem; cursor: pointer; line-height: 1; flex-shrink: 0; min-width: 34px; min-height: 34px; transition: var(--transition); }
.modal-close:hover { color: var(--text-primary); border-color: var(--accent-line); }
.info-section { margin-bottom: 1.25rem; }
.info-section:last-child { margin-bottom: 0; }
.info-section h3 { font-size: 0.8rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-secondary); margin-bottom: 0.5rem; padding-bottom: 0.3rem; border-bottom: 1px solid var(--border); }
.info-section p, .info-section ul { font-size: 0.85rem; color: var(--text-secondary); line-height: 1.6; }
.info-section ul { padding-left: 1.1rem; }
.info-section li { margin-bottom: 0.2rem; }
/* Links inside prose are underlined, not color-only (WCAG 1.4.1 —
   link-in-text-block). Color alone fails for CVD users; hover-only
   underlines don't count. */
.info-section a { color: var(--accent-line); text-decoration: underline; text-underline-offset: 2px; }
.info-section a:hover { text-decoration-thickness: 2px; }
.info-section kbd {
  font-family: var(--font-mono);
  font-size: 0.75em;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 1px 5px;
  color: var(--text-primary);
}
.info-section code {
  font-family: var(--font-mono);
  font-size: 0.85em;
  background: var(--bg-raised);
  border-radius: 3px;
  padding: 1px 4px;
}

/* Floating glass panel (legend, filter dock, …). Collapse behavior comes from
   MCO.initCollapsible(); the toggle carries aria-expanded, the body gets
   [hidden]. Structure:
     .mco-panel > .mco-panel-head (> .mco-panel-title + .mco-panel-toggle)
                + .mco-panel-body */
.mco-panel {
  position: absolute;
  background: var(--glass);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  z-index: var(--z-map-panel);
  max-width: min(320px, calc(100vw - 2rem));
}
.mco-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.5rem; padding: 0.5rem 0.5rem 0.5rem 0.75rem;
}
.mco-panel-title {
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-secondary);
}
.mco-panel-toggle {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex-shrink: 0;
  background: none; border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-dim); cursor: pointer; font-family: inherit;
  transition: var(--transition);
}
.mco-panel-toggle:hover { color: var(--text-primary); border-color: var(--accent-line); }
/* The caret shows the ACTION, not the state (markup glyph is an up-chevron):
   expanded → points down ("collapse toward the bottom dock"); collapsed →
   points up ("expand upward"). */
.mco-panel-toggle svg { transition: transform var(--transition); transform: rotate(180deg); }
.mco-panel-toggle[aria-expanded="false"] svg { transform: none; }
/* Collapse is animated (slide + fade — ported from mesonet-status), then
   MCO.initCollapsible sets [hidden] so collapsed content leaves the tab
   order. The max-height cap comfortably fits house panels; raise it locally
   for an unusually tall one. Reduced motion clamps the transition. */
.mco-panel-body {
  padding: 0 0.75rem 0.75rem;
  max-height: 500px;
  opacity: 1;
  overflow: hidden;
  transition: max-height var(--transition), opacity var(--transition);
}
.mco-panel-body.is-collapsing { max-height: 0; opacity: 0; }

/* Collapsible search (MCO.initSearchCollapse) ───────────────────────────────
   At compact widths a navbar search field costs more width than it earns, but
   hiding it strands the only keyboard route to a named feature. Instead it
   collapses into a disclosure button grouped with the other nav buttons and
   expands as a full-width overlay bar under the navbar.

   The breakpoint is the ladder's existing 640px compact edge rather than a
   fifth number: compact is exactly where an inline field stops paying for
   itself. Deliberately width-only — unlike MCO.viewport.COMPACT_MQ it has no
   max-height clause, because a short landscape window is still wide enough for
   an inline field. Raised from 460px in v0.6.0: at 460 a control-dense bar
   (mesonet-status) still wrapped to a third row between 461 and 527px, and the
   wrap point drifted with live data in the labels.

   The kit owns only the COLLAPSE mechanics; the field itself stays app-owned
   (the search combobox is deliberately not in the kit). Add .mco-search-collapse
   to your existing search wrapper and .mco-search-toggle to the button:
     <div class="search-wrap mco-search-collapse" id="search-wrap"> … </div>
     <button class="nav-btn icon-only mco-search-toggle"
             aria-expanded="false" aria-controls="search-wrap"> … </button>
   Inner selectors are generic on purpose (input[type=search], kbd), so this
   works whatever the app calls its own parts. */
.mco-search-toggle { display: none; }
@media (max-width: 640px) {
  .mco-search-toggle { display: flex; }
  .mco-navbar .mco-search-collapse { display: none; }
  .mco-navbar .mco-search-collapse.is-open {
    display: flex;
    /* .mco-navbar is the positioned ancestor, so top:100% lands the bar
       directly beneath the whole (possibly wrapped) navbar. */
    position: absolute;
    top: 100%; left: 0; right: 0;
    z-index: var(--z-flyout);
    padding: 0.5rem max(0.75rem, env(safe-area-inset-right))
             0.5rem max(0.75rem, env(safe-area-inset-left));
    background: var(--glass);
    backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow);
  }
  /* Fill the bar — flex-basis beats the field's own fixed and :focus widths. */
  .mco-navbar .mco-search-collapse.is-open input[type="search"],
  .mco-navbar .mco-search-collapse.is-open input[type="search"]:focus {
    flex: 1 1 0; width: auto;
  }
  /* A "press /" hint is a physical-keyboard affordance; in the overlay it would
     sit on top of the text being typed. */
  .mco-navbar .mco-search-collapse.is-open kbd { display: none; }
}

/* Scrim behind off-canvas drawers on compact viewports. Pair with a panel at
   --z-drawer; the scrim sits one tier below it. */
.mco-scrim {
  position: fixed; inset: 0;
  background: var(--scrim);
  z-index: var(--z-map-panel);
}

/* ── 6. Responsive & touch ────────────────────────────────────────────────
   Breakpoint ladder (HOUSE-STYLE.md §3): 1400 shed button labels · 1060
   tighten chrome · 750 collapse the brand to the badge · 640 compact. "Compact" also has
   a height variant — KEEP IN SYNC with MCO.viewport.COMPACT_MQ in
   core/mco-core.js and the copy in snippets/anti-flash.html:
   (max-width: 640px), (max-height: 560px). Layout lives in
   real @media rules (no flash before deferred JS runs); behavior decisions
   (sheet vs popup, auto-collapse) key off MCO.viewport. */
@media (max-width: 1400px) {
  /* .btn-label lives INSIDE its button, so removing it is safe: the house rule
     is that such a button carries a permanent aria-label (§3). */
  .mco-navbar .btn-label { display: none; }
  /* .control-label is usually a <label for> naming a real control, so hide it
     VISUALLY rather than removing it — display:none takes it out of the
     accessibility tree and the input it names is left with NO accessible name
     from 1400px down (axe: label / select-name). Same reasoning as the brand
     lockup collapse. */
  .mco-navbar .control-label {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0);
    white-space: nowrap; border: 0;
  }
  /* Once the label is gone, the icon is left sitting in label-sized padding
     (6px 12px) — visibly wider than the .icon-only buttons next to it in the
     same bar. Square it up so a mixed row of labelled and icon-only buttons
     reads as one set. .mco-btn-info is untouched: its circle is a deliberate
     distinction, and it never carries a .btn-label. */
  .mco-navbar .nav-btn:has(> .btn-label) {
    padding: 0.4rem; min-width: 34px; justify-content: center;
  }
}
@media (max-width: 1060px) {
  /* Tighten via the custom property, never `gap` directly — the lockup
     divider's margin is derived from it. */
  .mco-navbar { padding-right: 0.6rem; --nav-gap: 0.5rem; }
  .nav-meta { gap: 0.3rem; }
}
/* Mobile brand collapse: the logo badge alone carries the identity, because at
   phone widths the controls need every pixel. Both the title and the subtitle
   go, along with the lockup divider that would otherwise dangle beside nothing.

   The lockup is VISUALLY hidden rather than display:none'd on purpose:
   .brand-title is often the page's <h1> (HOUSE-STYLE §3), and a document
   outline shouldn't disappear at a breakpoint. Screen readers still get the
   app name; sighted users get the badge.

   Consumers that keep branding at every width must restore BOTH the lockup's
   static position and the divider — see mesonet-status.

   Scoped to the navbar (0.7.0): a lockup an app moves elsewhere — the top of
   a compact drawer, say (mesonet-explorer) — has room and must stay visible.
   :where() keeps the specificity at the old bare `.brand`, so an existing
   consumer override still wins. */
@media (max-width: 750px) {
  :where(.mco-navbar) .brand {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0);
    white-space: nowrap; border: 0;
  }
  .mco-navbar > .nav-divider { display: none; }
}
@media (max-width: 640px) {
  .refresh-status { display: none; }   /* reclaim space on phones */
}

/* WCAG 2.5.5 — comfortable touch targets on touch-only devices. */
@media (hover: none) {
  .nav-btn, .seg-btn { min-height: 40px; }
  /* The :has() arm covers label-shed buttons squared by §6; above 1400px the
     label makes them wider than 40px anyway, so it's a no-op there. */
  .nav-btn.icon-only, .mco-navbar .nav-btn:has(> .btn-label) { min-width: 40px; }
  .mco-btn-info { width: 40px; height: 40px; }
  .modal-close { min-width: 44px; min-height: 44px; }
  .mco-panel-toggle { width: 36px; height: 36px; }
}
