/* design-system.css — the Claude Design language (Kye's 2026-08-18 mocks), shared.
   ONE sheet for the panel pages being refreshed against those mocks (cover standards
   first; layer mapping and import profiles reuse it). It carries the TOKENS and the
   component families the mocks share — chips, glyphs, grid rows, section heads,
   toolbars — named generically (ds-*) so no page owns them.

   TWO PALETTES, ON THE APP'S OWN CONVENTION (admin-theme.css): the LIGHT values sit on
   bare :root and the mock's DARK values override under :root[data-theme="dark"] — the
   attribute theme.js stamps on <html>, which is the only state it has (light or dark,
   never an unstamped "system" one). EVERY colour token is defined in BOTH scopes: one
   defined only in the dark scope is the defect this structure exists to prevent — the
   refreshed panels stayed black through the theme toggle because the mock's palette was
   pinned here as fixed hexes (Kye, 2026-08-18: "the new designs wont toggle to light and
   stay black").

   The DARK half is the mock, byte for byte — it is the design that was signed off and
   nothing in it moved when the light half was added. The LIGHT half has no mock: it is
   derived from admin-theme.css's light theme so a refreshed panel sits in the same
   family as the rest of the app, and it is TUNED rather than inverted — every ink is at
   least as legible on a light row as its dark counterpart is on a dark one, because the
   glyphs carry meaning (▲ set here, = inherits, · tolerance) and a mark nobody can read
   is a fact nobody is told.

   NEAR-NEIGHBOUR GREYS GET THEIR OWN TOKENS. The mocks paint several one-step-off
   values (#9a9a9a beside #8d8d8d, #2f2f2f beside #2a2a2a, #8b8b8b beside #8d8d8d).
   Each keeps its own token rather than being rounded into the nearest ramp step:
   rounding would move the shipped dark rendering, which is the one thing this change
   must not do. In light they are free to converge.

   NO LAYOUT OPINIONS ABOUT ANY PARTICULAR GRID: column templates are per-page (two
   grids on one page already disagree about widths). This sheet skins the rows, the
   heads and the footers; the page says how many columns it has.

   NO display:none IN ANY MEDIA BLOCK — the compact-page rule (shrink, never hide)
   applies to every page that loads this sheet, so this sheet must not break it. */

:root {
  /* ══ LIGHT (no mock; derived from admin-theme.css's light theme) ══ */
  /* ── surfaces ── */
  --ds-page:#f5f6f8;            /* behind the panel, standalone only */
  --ds-bg:#ffffff;              /* the panel shell */
  --ds-bar:#f0f1f3;             /* toolbar strip */
  --ds-row:#fbfbfc;             /* data rows */
  --ds-row-hov:#f1f4f8;
  --ds-head:#f3f4f6;            /* grid heads + footers */
  --ds-field:#ffffff;           /* inputs */
  --ds-field-focus:#f4f8ff;
  --ds-chipbg:#ffffff;          /* chip-select boxes */
  --ds-nav:#ffffff;             /* the standalone pages' own top bar */
  --ds-panel:#ffffff;           /* widget plumbing (uiSelect menus, uiConfirm, toasts)
                                   where a pane pins those vars onto this palette */
  --ds-panel-2:#eef2f7;         /* …and its second surface (hover rows) */
  --ds-quiet-bg:#eef0f3;        /* ghost buttons; a chip this setting does not reach */
  --ds-seg:#f7f8fa;             /* segmented control */
  --ds-seg-hov:#e9edf2;
  --ds-pop-hov:#eef2f7;         /* popover row under the cursor */
  --ds-accent-wash:#eaf2ff;     /* accent-tinted surface: a drop zone under a drag */
  --ds-on-bg:#dbe9ff;           /* a chip/row the setting DOES reach, or is picked */
  /* ── rules ── */
  --ds-line:#d0d5db;            /* section rules */
  --ds-line-2:#dde1e6;          /* grid borders */
  --ds-line-3:#c6ccd4;          /* control borders */
  --ds-line-4:#a3aab4;          /* control borders, hover */
  --ds-rule:#e5e8ec;            /* row separators (30px rows) */
  --ds-rule-2:#e8ebef;          /* row separators (28px rows) */
  --ds-shell:#d7dce2;           /* the panel's outer border, and the quiet field borders
                                   drawn to match it */
  --ds-edge:#d3d8de;            /* the filter box; the group-head underline */
  --ds-faint:#c3c9d1;           /* scrollbar thumb; the stat separator dot; the dashed
                                   drop-zone and empty-state borders */
  --ds-quiet-line:#dde1e6;      /* the quiet chip's border */
  --ds-on-line:#a9c8f5;         /* the reached/picked chip's border */
  /* ── ink ── */
  --ds-ink:#1a1d21;
  --ds-ink-hi:#0d1014;          /* a ghost control under the cursor: the brightest ink */
  --ds-name:#2b3138;            /* mono names — files, models, layers */
  --ds-ink-2:#3d434b;           /* secondary numerals */
  --ds-ink-3:#4e555e;           /* ghost-button labels */
  --ds-ink-4:#5c636c;           /* clause references, queued rows, dataset kinds */
  --ds-mut:#5f6773;             /* muted text; the "=" glyph */
  --ds-mut-5:#616973;           /* the mock's second muted grey: import hints, counts */
  --ds-mut-2:#6b7280;           /* column heads */
  --ds-mut-4:#737b87;           /* the word beside a stat's numeral */
  --ds-mut-3:#79818d;           /* legends, breadcrumbs, chip tags */
  --ds-none:#828a96;            /* absent values ("—") */
  --ds-dim:#8b939e;             /* placeholders; the "·" glyph */
  /* ── accents ── */
  --ds-accent:#1f6feb;
  --ds-accent-hov:#1a5fd0;
  --ds-accent-edge:#1257c9;     /* a filled button's border under the cursor */
  --ds-on-accent:#ffffff;       /* ink ON a filled accent control — white in both themes */
  --ds-link:#1257c9;
  --ds-link-hov:#0b46a3;
  --ds-on-ink:#1257c9;          /* ink on --ds-on-bg */
  --ds-amber:#9a6b00;           /* "set here"; warnings that are facts, not faults */
  --ds-amber-soft:rgba(154,107,0,.10);
  --ds-green:#1e7d36;           /* "a rule matched" — the mock's confirmed "=" glyph */
  --ds-green-2:#1a7a48;         /* the same fact said as a status word ("done") */
  --ds-red:#c73529;             /* refused saves only */
  --ds-red-soft:rgba(199,53,41,.07);
  /* Clash lifecycle pills (the clash panel joined this sheet 2026-08-22). green-soft
     completes the amber-soft/red-soft series for "resolved"; the purple pair carries
     "reappeared" — a state that is neither a confirmation nor a refusal, so it gets its
     own hue rather than borrowing a service-class ink. Seeded from the clash panel's
     shipped values so nothing signed-off moved. */
  --ds-green-soft:rgba(30,125,54,.10);
  --ds-purple:#6d28d9;
  --ds-purple-soft:rgba(109,40,217,.12);
  --ds-shadow:0 8px 24px rgba(20,24,31,.14);   /* popovers */
  /* ── the removal dialog (Kye's "Remove profile dialog" mock; read by ui.js's
        uiConfirm/uiForm box, which aliases them so the nine pages that do not link
        this sheet still paint). Their own tokens, not --ds-green/--ds-amber/--ds-red:
        a consequence line says something different from "a rule matched", "set here"
        and "this save was refused", and the mock paints it in its own pair. In DARK
        they are the mock's literals; in LIGHT they converge on the ramp this sheet
        already carries, which is what the near-neighbour note above permits.
        --ds-danger is the same terracotta in both scenes on purpose: a destructive
        verb button carries its own ground, reads 4.7:1 against white ink either way,
        and must not become a different colour depending on the page it opens over.
        Hover follows each scene's convention — lighter on dark, deeper on light,
        the same way --ds-accent-hov does. ── */
  --ds-ok:#1e7d36;              /* the check: this consequence is SAFE / unaffected */
  --ds-warn:#9a6b00;            /* the triangle: what actually STOPS */
  --ds-danger:#c0503f;          /* the destructive verb button */
  --ds-danger-hov:#a8412f;
  --ds-on-danger:#ffffff;       /* ink ON it — white in both themes */
  --ds-scrim:rgba(16,28,44,.45);               /* the page behind a modal */
  --ds-shadow-modal:0 12px 40px rgba(20,24,31,.22);  /* a modal lifts further than a popover */
  /* ── service-class inks: ONE PAIR PER CLASS, read by .ds-chip--x / .ds-dot--x.
        These lived as hexes inside cover-standards-app.js / service-map-app.js
        (CLASS_INK) and were emitted INLINE — which outranks every rule in this sheet,
        so the chips and dots kept the mock's dark paint on a light row long after the
        rest of the panel had learned the light scene. They are tokens now, and the
        LIGHT half keeps each class in its DARK hue family: a saturated ink on a pale
        tint of the same hue. TWO PAIRS ARE THE PANEL FAMILY'S OWN, in both scenes —
        water IS the reached/picked pair (--ds-on-bg / --ds-on-ink / --ds-on-line) and
        unknown IS the quiet pair (--ds-quiet-bg / --ds-mut-5 / --ds-quiet-line) — so
        "honoured" and "absent" read the same across these pages. `unknown` is a REAL
        class and keeps its own legible neutral grey; it is not an unpainted default.
        ONE LIGHT DEPARTURE, in the language of the comms one already in CLASS_INK: the
        mock's two greens (sewer, stormwater) and its three greys (comms, structure,
        unknown) separate by lightness on black, which a light row cannot repeat, so
        each pair takes a small hue step as well — no two classes sit closer here than
        the closest pair already sits in the mock. ── */
  --ds-cls-sewer-fg:#1e7a4e;        --ds-cls-sewer-bg:#e7f4ed; --ds-cls-sewer-bd:#b9ddca;
  --ds-cls-water-fg:#1257c9;        --ds-cls-water-bg:#dbe9ff; --ds-cls-water-bd:#a9c8f5;
  --ds-cls-stormwater-fg:#0a5c3c;   --ds-cls-stormwater-bg:#ddefe6; --ds-cls-stormwater-bd:#aed7c2;
  --ds-cls-comms-fg:#2d3f57;        --ds-cls-comms-bg:#eaeef4; --ds-cls-comms-bd:#ccd5e1;
  --ds-cls-power-lv-fg:#915300;     --ds-cls-power-lv-bg:#f7eee0; --ds-cls-power-lv-bd:#e6d2ae;
  --ds-cls-power-hv-fg:#a83c1c;     --ds-cls-power-hv-bg:#f9e9e3; --ds-cls-power-hv-bd:#eec9ba;
  --ds-cls-gas-fg:#6d6a00;          --ds-cls-gas-bg:#f3f2dc; --ds-cls-gas-bd:#dcd9a4;
  --ds-cls-fuel-fg:#6b3fa0;         --ds-cls-fuel-bg:#f0e9f9; --ds-cls-fuel-bd:#d8c8ee;
  --ds-cls-irrigation-fg:#12776e;   --ds-cls-irrigation-bg:#e0f1ef; --ds-cls-irrigation-bd:#b0d9d4;
  --ds-cls-structure-fg:#47607d;    --ds-cls-structure-bg:#ecf0f4; --ds-cls-structure-bd:#cfd7e0;
  --ds-cls-unknown-fg:#616973;      --ds-cls-unknown-bg:#eef0f3; --ds-cls-unknown-bd:#dde1e6;
  /* ── the IN-MAP FLOATING PANEL family (ds-fp-*), read by the component block at the
        foot of this sheet. "fp" is the floating panel, not one page: the flood /
        hydrology panel and the best-fit road panel are the same card in the same stack
        slot and wear the same strips.
        NINE values the rest of the sheet has no role for. The panel's shell, rules,
        heads, chips, ghost buttons, links and footer are ALREADY tokens above and are
        reused verbatim — these are only what the two mocks introduce. Seven came from
        the flood mock (task #17); --ds-fp-cut / --ds-fp-fill came from the road mock's
        panel (#18) and are its earthworks figures — see their note below.
        THREE OF THEM ARE THE SAME IN BOTH SCENES, on the --ds-danger precedent:
        --ds-fp-pick / --ds-fp-on-pick is a yellow button carrying its own ground, and
        the two ramps are LEGEND KEYS TO A MAP RASTER. A legend that re-tunes per theme
        stops naming the pixels it points at, so both ramps are the renderer's own stops
        (depth = measure.js _RAMP_TURQUOISE, 0-10 m, foam at the shore; frequency = the
        DEA Water Observations WMS) and they do not move when the page does. Only the
        hovers follow each scene's convention, lighter on dark and deeper on light. ── */
  --ds-fp-field:#ffffff;        /* the mock's inset field ground — its own role: on a
                                   dark card it sits ABOVE the shell, not below it */
  --ds-fp-guide-bd:#bcd6f7;     /* border of the guided (accent-wash) chip */
  --ds-fp-pick:#e8b820;         /* the amber "pick from the map" CTA */
  --ds-fp-pick-hov:#d9a908;
  --ds-fp-on-pick:#141414;      /* ink ON the amber — near-black in both themes */
  --ds-fp-depth-ramp:linear-gradient(90deg,#f6fcfc,#b0ece9,#60d6d2,#28b2ba,#18829e,#105c80,#0a3a5e);
  --ds-fp-freq-ramp:linear-gradient(90deg,#e8f2ff,#8ec2ff,#2f7bff,#0a3fb0);
  /* EARTHWORKS, the family's other panel: cut and fill are ONE PAIR and must read as a
     pair — not "a warning" and "a link". The best-fit road panel's figures block has
     always drawn them in these two, and they are only moving address; --ds-red is
     refused saves and --ds-amber is a fact-not-a-fault, so neither can carry a volume. */
  --ds-fp-cut:#c2410c;
  --ds-fp-fill:#1d6fd0;
  /* ── type (one face in both themes; deliberately NOT repeated in the dark scope) ── */
  --ds-font:Arial,Helvetica,sans-serif;
  --ds-mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace;
}

/* ══ DARK — the mock's palette, unchanged. Every value below is the literal this sheet
      shipped with; the light block above is the addition, not a re-tune of this one. ══ */
:root[data-theme="dark"] {
  /* ── surfaces ── */
  --ds-page:#0e0e0e;            /* behind the panel, standalone only */
  --ds-bg:#141414;              /* the panel shell */
  --ds-bar:#161616;             /* toolbar strip */
  --ds-row:#171717;             /* data rows */
  --ds-row-hov:#1c1c1c;
  --ds-head:#1a1a1a;            /* grid heads + footers */
  --ds-field:#101010;           /* inputs */
  --ds-field-focus:#0c1522;
  --ds-chipbg:#1e1e1e;          /* chip-select boxes */
  --ds-nav:#171717;
  --ds-panel:#1e1e1e;
  --ds-panel-2:#262626;
  --ds-quiet-bg:#1c1c1c;
  --ds-seg:#191919;
  --ds-seg-hov:#1f1f1f;
  --ds-pop-hov:#1f2937;
  --ds-accent-wash:#131a24;
  --ds-on-bg:#12263f;
  /* ── rules ── */
  --ds-line:#262626;            /* section rules */
  --ds-line-2:#2a2a2a;          /* grid borders */
  --ds-line-3:#333;             /* control borders */
  --ds-line-4:#4a4a4a;          /* control borders, hover */
  --ds-rule:#202020;            /* row separators (30px rows) */
  --ds-rule-2:#1f1f1f;          /* row separators (28px rows) */
  --ds-shell:#2c2c2c;
  --ds-edge:#2f2f2f;
  --ds-faint:#3a3a3a;
  --ds-quiet-line:#2e2e2e;
  --ds-on-line:#274a70;
  /* ── ink ── */
  --ds-ink:#e9e9e9;
  --ds-ink-hi:#fff;
  --ds-name:#d8d8d8;
  --ds-ink-2:#c9c9c9;           /* secondary numerals */
  --ds-ink-3:#b9b9b9;           /* ghost-button labels */
  --ds-ink-4:#9a9a9a;
  --ds-mut:#8d8d8d;             /* muted text; the "=" glyph */
  --ds-mut-5:#8b8b8b;
  --ds-mut-2:#7d7d7d;           /* column heads */
  --ds-mut-4:#767676;
  --ds-mut-3:#6d6d6d;           /* legends, breadcrumbs, chip tags */
  --ds-none:#6a6a6a;            /* absent values ("—") */
  --ds-dim:#5f5f5f;             /* placeholders; the "·" glyph */
  /* ── accents ── */
  --ds-accent:#1f6feb;
  --ds-accent-hov:#3b82f6;
  --ds-accent-edge:#4a9aff;
  --ds-on-accent:#fff;
  --ds-link:#4c9aff;
  --ds-link-hov:#7ab5ff;
  /* the picked-chip pair is the mock's own and deliberately matches CLASS_INK's
     water ink, so "honoured" reads the same across the panel family */
  --ds-on-ink:#7ab5ff;
  --ds-amber:#e0a33e;           /* "set here"; warnings that are facts, not faults */
  --ds-amber-soft:rgba(224,163,62,.09);
  --ds-green:#3fb27f;           /* "a rule matched" — the mock's confirmed "=" glyph */
  --ds-green-2:#5fbf94;
  --ds-red:#e5534b;             /* refused saves only */
  --ds-red-soft:rgba(229,83,75,.05);
  /* clash lifecycle pills — see the light scope's note; dark values are the panel's
     shipped dark pills, byte for byte */
  --ds-green-soft:rgba(63,178,127,.16);
  --ds-purple:#c4b5fd;
  --ds-purple-soft:rgba(167,139,250,.16);
  --ds-shadow:0 8px 24px rgba(0,0,0,.5);
  /* ── the removal dialog — the mock's own hexes, byte for byte ── */
  --ds-ok:#4a9c6d;              /* the check: this consequence is SAFE / unaffected */
  --ds-warn:#c79544;            /* the triangle: what actually STOPS */
  --ds-danger:#c0503f;          /* the destructive verb button */
  --ds-danger-hov:#d2604e;
  --ds-on-danger:#ffffff;
  --ds-scrim:rgba(0,0,0,.55);
  --ds-shadow-modal:0 12px 40px rgba(0,0,0,.6);
  /* ── service-class inks — CLASS_INK's own literals, byte for byte. Nothing the
        mock painted moved; the hexes only changed address. ── */
  --ds-cls-sewer-fg:#5fbf94;        --ds-cls-sewer-bg:#141f1a; --ds-cls-sewer-bd:#25382e;
  --ds-cls-water-fg:#7ab5ff;        --ds-cls-water-bg:#12263f; --ds-cls-water-bd:#274a70;
  --ds-cls-stormwater-fg:#3fb27f;   --ds-cls-stormwater-bg:#14231c; --ds-cls-stormwater-bd:#274733;
  --ds-cls-comms-fg:#d9e2ec;        --ds-cls-comms-bg:#232a31; --ds-cls-comms-bd:#39434d;
  --ds-cls-power-lv-fg:#e0a33e;     --ds-cls-power-lv-bg:#2b2314; --ds-cls-power-lv-bd:#4a3a1c;
  --ds-cls-power-hv-fg:#d1603d;     --ds-cls-power-hv-bg:#2a1a14; --ds-cls-power-hv-bd:#4a2c1f;
  --ds-cls-gas-fg:#e8c14a;          --ds-cls-gas-bg:#2a2412; --ds-cls-gas-bd:#4a3f1c;
  --ds-cls-fuel-fg:#a86bd8;         --ds-cls-fuel-bg:#241a2e; --ds-cls-fuel-bd:#3e2c52;
  --ds-cls-irrigation-fg:#4fb3a5;   --ds-cls-irrigation-bg:#122624; --ds-cls-irrigation-bd:#234441;
  --ds-cls-structure-fg:#9aa6b2;    --ds-cls-structure-bg:#1c2126; --ds-cls-structure-bd:#2c333a;
  --ds-cls-unknown-fg:#8b8b8b;      --ds-cls-unknown-bg:#1c1c1c; --ds-cls-unknown-bd:#2e2e2e;
  /* ── the in-map floating panel — the mock's own literals. #1a1a1a is the mock's field
        ground and deliberately NOT --ds-field (#101010): on this card the inputs sit a
        step ABOVE the shell, not below it. The pick pair and both ramps repeat their
        light values on purpose (see the light scope). ── */
  --ds-fp-field:#1a1a1a;
  --ds-fp-guide-bd:#2a3a52;
  --ds-fp-pick:#e8b820;
  --ds-fp-pick-hov:#f5c832;
  --ds-fp-on-pick:#141414;
  --ds-fp-depth-ramp:linear-gradient(90deg,#f6fcfc,#b0ece9,#60d6d2,#28b2ba,#18829e,#105c80,#0a3a5e);
  --ds-fp-freq-ramp:linear-gradient(90deg,#e8f2ff,#8ec2ff,#2f7bff,#0a3fb0);
  --ds-fp-cut:#ff9f5a;
  --ds-fp-fill:#6fb4ff;
}

/* ── scrollbars, the mock's own ─────────────────────────────────────────── */
.ds ::-webkit-scrollbar, .ds::-webkit-scrollbar { width:8px; height:8px; }
.ds ::-webkit-scrollbar-thumb, .ds::-webkit-scrollbar-thumb { background:var(--ds-faint); border-radius:4px; }
.ds ::-webkit-scrollbar-track, .ds::-webkit-scrollbar-track { background:transparent; }

/* ── toolbar row ────────────────────────────────────────────────────────── */
.ds-toolbar { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  padding:9px 14px; background:var(--ds-bar); border-bottom:1px solid var(--ds-line); }
.ds-sp { flex:1; }

/* ── chip-select: a labelled box around a (uiSelect-upgraded) native <select>.
      CLASS-SCOPED restyle of the .usel trigger only — never an #id rule on any
      <select>: an #id rule outranks uiSelect's two-class .usel .usel-native and
      paints a ghost copy of the value over the trigger (#road-panel did). ──── */
.ds-chipsel { display:flex; align-items:center; gap:6px; padding:4px 8px;
  background:var(--ds-chipbg); border:1px solid var(--ds-line-3); border-radius:4px;
  min-width:150px; max-width:280px; }
.ds-chipsel-tag { font:400 10px/1 var(--ds-font); letter-spacing:.08em; color:var(--ds-mut-3); flex:none; }
.ds-chipsel .usel, .ds-chipsel select { flex:1; min-width:0; width:100%; }
.ds-chipsel .usel-trigger { background:transparent; border:0; padding:2px 0; min-height:0;
  font-size:12px; color:var(--ds-ink); justify-content:flex-end; text-align:right; }
.ds-chipsel .usel-trigger:hover { border:0; }
.ds-chipsel .usel-caret { opacity:.45; }

/* ── stats: mono numeral + muted word, dot-separated ────────────────────── */
.ds-stats { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }
/* …and it COLLAPSES when it is empty, the same way .ds-notes and .ds-fp-note do, and for
   the same reason: every host is a `display:flex; gap:8px` .ds-toolbar, so an emptied
   .ds-stats is not nothing — it is an 8px gap sitting between the spacer and the next
   control, on exactly the paths where the count was cleared because a request was refused.
   All four hosts empty it by innerHTML = "" (audit-app, cover-standards-app,
   import-profile-app, service-map-app), all four sit between a .ds-sp and a control, and
   none gives it a width or leans on it as a spacer — each toolbar has its own .ds-sp — so
   this collapses a stray gap on four surfaces and moves nothing else.
   OUTSIDE EVERY @media BLOCK, which this sheet has none of: an :empty collapse is a STATE
   ("there is nothing to show"), not a breakpoint hiding evidence, and that exemption is
   the only reason a display:none is allowed to live in the shared sheet at all. */
.ds-stats:empty { display:none; }
.ds-stat { display:inline-flex; align-items:baseline; gap:4px; }
.ds-stat b { font:500 12px/1 var(--ds-mono); color:var(--ds-ink); }
.ds-stat b .of { color:var(--ds-mut-3); font-weight:400; }
.ds-stat span { font:400 11px/1 var(--ds-font); color:var(--ds-mut-4); }
.ds-stat.warn b, .ds-stat.warn span { color:var(--ds-amber); }
.ds-statdot { color:var(--ds-faint); }

/* ── buttons ────────────────────────────────────────────────────────────── */
.ds-btn { display:inline-flex; align-items:center; gap:5px; padding:4px 10px;
  background:transparent; border:1px solid var(--ds-line-3); border-radius:4px;
  font:400 11px/1.3 var(--ds-font); color:var(--ds-ink-3); cursor:pointer; }
.ds-btn:hover { border-color:var(--ds-line-4); color:var(--ds-ink-hi); }
.ds-btn:disabled { opacity:.55; cursor:default; }
.ds-btn--primary { background:var(--ds-accent); border-color:var(--ds-accent); color:var(--ds-on-accent); font-weight:500; padding:4px 12px; }
.ds-btn--primary:hover { background:var(--ds-accent-hov); border-color:var(--ds-accent-hov); color:var(--ds-on-accent); }
.ds-btn--slim { padding:2px 7px; border-radius:3px; }
.ds-btn--x { border:0; padding:0 4px; color:var(--ds-mut-3); font-size:13px; line-height:1; background:none; cursor:pointer; }
.ds-btn--x:hover { color:var(--ds-red); }

/* ── the refusal banner: full-width bar, amber left rule, ONE sentence.
      A state, not an alarm — nothing has failed; a decision has not been made. ── */
.ds-banner { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:7px 14px; background:var(--ds-amber-soft); border-left:3px solid var(--ds-amber);
  font:400 12px/1.45 var(--ds-font); color:var(--ds-ink); }
.ds-banner b { font-weight:600; }

/* ── note lines: one short fact each, the fuller why in title= ──────────── */
.ds-notes { display:flex; flex-direction:column; gap:4px; }
.ds-notes:empty { display:none; }
.ds-note { font:400 11.5px/1.45 var(--ds-font); color:var(--ds-mut);
  border-left:2px solid var(--ds-line-2); padding:2px 0 2px 9px; }
.ds-note.warn { border-left-color:var(--ds-amber); color:var(--ds-ink); }
.ds-note b { font-weight:600; color:var(--ds-ink); }

/* ── section head: title · breadcrumb · controls · legend ───────────────── */
.ds-sechead { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:8px; }
.ds-sectitle { font:600 12px/1.3 var(--ds-font); letter-spacing:.02em; color:var(--ds-ink); }
.ds-crumb { font:400 11px/1.3 var(--ds-font); color:var(--ds-mut-3); }
.ds-legend { font:400 10px/1.4 var(--ds-font); color:var(--ds-mut-3); letter-spacing:.04em; white-space:nowrap; }
.ds-legend .g-eq { color:var(--ds-mut); }
.ds-legend .g-set { color:var(--ds-amber); }
.ds-legend .g-tol { color:var(--ds-dim); }

/* ── grids: bordered host; heads, rows, footers. Column templates are the
      page's; every wide grid scrolls inside its own .ds-hscroll host. ────── */
.ds-grid { border:1px solid var(--ds-line-2); border-radius:5px; overflow:hidden; background:var(--ds-row); }
.ds-hscroll { overflow-x:auto; }
.ds-gridhead { background:var(--ds-head); font:400 9px/1.2 var(--ds-font);
  letter-spacing:.09em; color:var(--ds-mut-2); text-transform:uppercase; }
.ds-grouphd { font-size:9px; letter-spacing:.1em; color:var(--ds-mut-3);
  border-bottom:1px solid var(--ds-edge); padding-bottom:3px; }
.ds-rw { border-bottom:1px solid var(--ds-rule); background:var(--ds-row); }
.ds-rw:hover { background:var(--ds-row-hov); }
.ds-rw--slim { border-bottom-color:var(--ds-rule-2); }
.ds-cell-num { text-align:right; font:400 11px/1.3 var(--ds-mono); color:var(--ds-ink-2);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ds-cell-ref { color:var(--ds-ink-4); }
.ds-cell-ref .clause { color:var(--ds-mut-3); font-size:10px; margin-left:5px; }
.ds-cell-none { color:var(--ds-none); }
.ds-vrule { border-left:1px solid var(--ds-line-2); height:20px; }
.ds-name { font:400 11px/1.3 var(--ds-mono); color:var(--ds-name);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ds-name em { font-family:var(--ds-font); color:var(--ds-mut-3); font-style:italic; }
.ds-gridfoot { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:7px 10px; background:var(--ds-head); font:400 11px/1.4 var(--ds-font); color:var(--ds-mut-2); }
.ds-gridfoot b { font:500 11px/1 var(--ds-mono); color:var(--ds-ink-2); }
.ds-micro { font:400 11px/1.4 var(--ds-font); color:var(--ds-mut-3); }
.ds-msg { font:500 11.5px/1.4 var(--ds-font); color:var(--ds-ink-2); }
.ds-msg.bad { color:var(--ds-red); }
.ds-empty { font:400 12px/1.5 var(--ds-font); color:var(--ds-mut-3); text-align:center;
  padding:14px 12px; }
.ds-empty b { color:var(--ds-ink); font-weight:600; }

/* ── inputs: mono numerals, quiet until focused. type=number keeps the
      badInput contract; the spinners go — a stray scroll over a focused
      spinner silently changing 0.6 to 0.7 is a wrong answer nobody sees. ── */
input.ds-num { width:100%; box-sizing:border-box; height:20px; padding:0 4px;
  background:var(--ds-field); border:1px solid var(--ds-line-3); border-radius:3px;
  color:var(--ds-ink); font:400 11px/1 var(--ds-mono); text-align:center; outline:none; }
input.ds-num:focus { border-color:var(--ds-accent); background:var(--ds-field-focus); }
input.ds-num::placeholder { color:var(--ds-dim); }
input.ds-num::-webkit-outer-spin-button, input.ds-num::-webkit-inner-spin-button { -webkit-appearance:none; margin:0; }
input.ds-num { -moz-appearance:textfield; appearance:textfield; }
.ds-ro { font:400 11px/1.3 var(--ds-mono); color:var(--ds-ink-2); text-align:center; display:block; }
.ds-ro.ds-cell-none { color:var(--ds-none); }

/* ── the SRC glyph column: one mark, the why in title= ──────────────────── */
.ds-glyph { display:inline-block; text-align:center; font:400 11px/1 var(--ds-mono); cursor:default; }
.ds-glyph--eq { color:var(--ds-mut); }     /* =  inherits the standard */
.ds-glyph--set { color:var(--ds-amber); }  /* ▲  set here */
.ds-glyph--tol { color:var(--ds-dim); }    /* ·  test tolerance */

/* ── service identity dot + class chip ──────────────────────────────────────
      THE COLOUR IS A CLASS, NEVER AN INLINE STYLE. The two apps emit
      `ds-dot ds-dot--water` / `ds-chip ds-chip--water` off their class-key table and
      the paint comes from the token pairs above, so it follows the theme for free.
      They used to emit style="background:#12263f;color:#7ab5ff;border:1px solid …"
      straight out of CLASS_INK, and an inline declaration outranks every rule in this
      sheet: 243 of 243 chips rendered as near-black pills on the light row and 7 of 9
      identity dots fell under 3:1 against it. A class cannot be outranked by the
      sheet, which is the whole reason this is a class.
      The BARE families carry the `unknown` pair, so a chip or dot that somehow reaches
      the page without a modifier is still painted and still readable rather than
      transparent. Modifiers follow the bare rules deliberately — same specificity,
      later wins. ── */
.ds-dot { width:6px; height:6px; border-radius:2px; flex:none; display:inline-block;
  background:var(--ds-cls-unknown-fg); }
.ds-chip { display:inline-block; padding:1px 7px; border-radius:9px; font:400 10px/1.5 var(--ds-font);
  white-space:nowrap; max-width:100%; overflow:hidden; text-overflow:ellipsis; box-sizing:border-box;
  background:var(--ds-cls-unknown-bg); color:var(--ds-cls-unknown-fg); border:1px solid var(--ds-cls-unknown-bd); }
.ds-dot--sewer { background:var(--ds-cls-sewer-fg); }
.ds-chip--sewer { background:var(--ds-cls-sewer-bg); color:var(--ds-cls-sewer-fg); border-color:var(--ds-cls-sewer-bd); }
.ds-dot--water { background:var(--ds-cls-water-fg); }
.ds-chip--water { background:var(--ds-cls-water-bg); color:var(--ds-cls-water-fg); border-color:var(--ds-cls-water-bd); }
.ds-dot--stormwater { background:var(--ds-cls-stormwater-fg); }
.ds-chip--stormwater { background:var(--ds-cls-stormwater-bg); color:var(--ds-cls-stormwater-fg); border-color:var(--ds-cls-stormwater-bd); }
.ds-dot--comms { background:var(--ds-cls-comms-fg); }
.ds-chip--comms { background:var(--ds-cls-comms-bg); color:var(--ds-cls-comms-fg); border-color:var(--ds-cls-comms-bd); }
.ds-dot--power-lv { background:var(--ds-cls-power-lv-fg); }
.ds-chip--power-lv { background:var(--ds-cls-power-lv-bg); color:var(--ds-cls-power-lv-fg); border-color:var(--ds-cls-power-lv-bd); }
.ds-dot--power-hv { background:var(--ds-cls-power-hv-fg); }
.ds-chip--power-hv { background:var(--ds-cls-power-hv-bg); color:var(--ds-cls-power-hv-fg); border-color:var(--ds-cls-power-hv-bd); }
.ds-dot--gas { background:var(--ds-cls-gas-fg); }
.ds-chip--gas { background:var(--ds-cls-gas-bg); color:var(--ds-cls-gas-fg); border-color:var(--ds-cls-gas-bd); }
.ds-dot--fuel { background:var(--ds-cls-fuel-fg); }
.ds-chip--fuel { background:var(--ds-cls-fuel-bg); color:var(--ds-cls-fuel-fg); border-color:var(--ds-cls-fuel-bd); }
.ds-dot--irrigation { background:var(--ds-cls-irrigation-fg); }
.ds-chip--irrigation { background:var(--ds-cls-irrigation-bg); color:var(--ds-cls-irrigation-fg); border-color:var(--ds-cls-irrigation-bd); }
.ds-dot--structure { background:var(--ds-cls-structure-fg); }
.ds-chip--structure { background:var(--ds-cls-structure-bg); color:var(--ds-cls-structure-fg); border-color:var(--ds-cls-structure-bd); }
.ds-dot--unknown { background:var(--ds-cls-unknown-fg); }
.ds-chip--unknown { background:var(--ds-cls-unknown-bg); color:var(--ds-cls-unknown-fg); border-color:var(--ds-cls-unknown-bd); }

/* ── filter input + segmented control ───────────────────────────────────── */
.ds-filter { display:flex; align-items:center; gap:6px; padding:0 7px;
  background:var(--ds-field); border:1px solid var(--ds-edge); border-radius:4px; height:24px; }
.ds-filter .glass { color:var(--ds-dim); font-size:11px; }
.ds-filter input { flex:1; min-width:0; background:transparent; border:none; outline:none;
  color:var(--ds-ink); font:400 11px/1.3 var(--ds-font); }
.ds-filter input::placeholder { color:var(--ds-dim); }
.ds-seg { display:flex; gap:1px; background:var(--ds-line-2); border-radius:4px; overflow:hidden; }
.ds-seg button { padding:4px 10px; background:var(--ds-seg); color:var(--ds-ink-4); border:0;
  font:400 11px/1.3 var(--ds-font); cursor:pointer; }
.ds-seg button:hover { color:var(--ds-ink-hi); background:var(--ds-seg-hov); }
.ds-seg button.on { background:var(--ds-accent); color:var(--ds-on-accent); }

/* ── key chips (a stored row said back to the reader) ───────────────────── */
.ds-key { font:400 10.5px/1 var(--ds-mono); color:var(--ds-ink-2); background:var(--ds-field);
  border:1px solid var(--ds-line-2); border-radius:3px; padding:3px 6px; display:inline-block; }

/* ── format chips: a file format said as one mark (import/export pages).
      Blue (--on) = the setting reaches this reader; grey = it does not. Both
      pairs are tokens, and their DARK values are the mock's own literals —
      deliberately CLASS_INK's water/unknown inks, so "honoured" and "absent"
      read the same across the panel family. WHICH formats go blue is the
      page's — fed from its graded
      mirrors of the server's refusal lists, never decided here. ───────────── */
.ds-chips { display:flex; flex-wrap:wrap; gap:4px; min-width:0; }
.ds-fmt { display:inline-block; padding:1px 6px; border-radius:3px;
  font:400 10px/1.6 var(--ds-mono); white-space:nowrap;
  background:var(--ds-quiet-bg); color:var(--ds-mut-5); border:1px solid var(--ds-quiet-line); }
.ds-fmt--on { background:var(--ds-on-bg); color:var(--ds-on-ink); border-color:var(--ds-on-line); }

/* ── links in the design language ───────────────────────────────────────── */
.ds a, a.ds-link { color:var(--ds-link); text-decoration:none; }
.ds a:hover, a.ds-link:hover { color:var(--ds-link-hov); }

/* ══ ds-fp-*: THE IN-MAP FLOATING PANEL ═══════════════════════════════════════
   Kye's "Flood hydrology panel" mock, at its DEFAULTS — rounded corners, #1f6feb
   accent, compact density. The mock's corners/accent/density knobs are Design-Canvas
   preview toggles, not features: nothing here reads a variant.

   TWO PANELS SHARE THIS, which is why nothing below carries an #id and nothing is
   scoped to #flood-panel: the flood/hydrology panel and the best-fit road panel are
   the same card in the same bottom-left stack slot. The page still says how many
   columns its grids have and which strips it owns — this block skins the shell, the
   strips, the heads, the controls and the footer.

   SELECTS ARE STYLED THROUGH .usel, NEVER BY ELEMENT ID. uiSelect replaces every
   <select> on the page with a .usel wrapper and parks the native one at 1px/opacity:0
   behind the trigger; an #id rule on a <select> outranks the two-class .usel
   .usel-native hide and paints a ghost copy of the value over the trigger — the bug
   that showed #road-panel's ground-surface label twice. .ds-fp-sel addresses the
   WRAPPER, and keeps a bare-<select> twin so the control still reads before uiSelect
   upgrades it (and in the jsdom bench, which never runs the upgrade).

   NO display:none IN A MEDIA BLOCK anywhere below — and, per this sheet's own rule,
   no display:none at all outside an :empty collapse. Every hidden thing in these
   panels is hidden by STATE (.show / .open / .on, set by measure.js, map.js, hydro.js
   and weather.js), and each panel writes that state pair scoped to its own id. ═════ */

/* ── the card ── */
.ds-fp { box-sizing:border-box; background:var(--ds-bg); color:var(--ds-ink);
  border:1px solid var(--ds-shell); font:13px/1.4 var(--ds-font); }

/* ── header: name, spacer, close ── */
.ds-fp-hd { display:flex; align-items:center; gap:8px; padding:9px 12px;
  background:var(--ds-bar); border-bottom:1px solid var(--ds-line);
  font-size:14px; font-weight:600; letter-spacing:-.01em; }
.ds-fp-sp { flex:1; }
.ds-fp-x { flex:none; border:0; background:none; padding:0 2px; line-height:1;
  font:400 14px/1 var(--ds-font); color:var(--ds-mut-3); cursor:pointer; }
.ds-fp-x:hover { color:var(--ds-ink-hi); }

/* ── strips: one concern each, ruled off from the next ── */
.ds-fp-strip { padding:8px 12px; border-bottom:1px solid var(--ds-rule);
  display:flex; flex-direction:column; gap:6px; }
.ds-fp-strip--row { flex-direction:row; align-items:center; gap:6px; }

/* ── heads and field labels: the mock's two letter-spaced 9px greys ── */
.ds-fp-h { flex:none; font:400 9px/1.3 var(--ds-font); letter-spacing:.09em; color:var(--ds-mut-2); }
.ds-fp-lab { flex:none; font:400 9px/1.3 var(--ds-font); letter-spacing:.05em; color:var(--ds-mut-2); }
.ds-fp-fld { display:flex; flex-direction:column; gap:2px; min-width:0; }
.ds-fp-grp { font:400 9px/1.3 var(--ds-font); letter-spacing:.09em; color:var(--ds-mut-2); margin:9px 0 3px; }
.ds-fp-grp:first-child { margin-top:0; }
.ds-fp-note { font:400 10px/1.45 var(--ds-font); color:var(--ds-mut-3); }
.ds-fp-note:empty { display:none; }

/* ── a labelled control on ONE line: 9px head, then the control taking the rest.
      The mock's ENGINE / DRAINAGE / SOIL LOSS / LOOK rows. ── */
.ds-fp-row { display:flex; align-items:center; gap:6px; }
.ds-fp-row > .ds-fp-sel, .ds-fp-row > .ds-fp-fld { flex:1; min-width:0; }

/* ── grids: the page picks the template, these are the two the mock uses ── */
.ds-fp-g3 { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:6px; }
.ds-fp-g2 { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:6px; }
.ds-fp-sp2 { grid-column:span 2; }

/* ── chips: the ghost control. .on is the ARMED state every ⊕ pick tool shares. ── */
.ds-fp-chip { box-sizing:border-box; height:24px; display:flex; align-items:center;
  justify-content:center; gap:4px; padding:0 7px; background:none;
  border:1px solid var(--ds-line-3); border-radius:4px;
  font:400 11px/1 var(--ds-font); color:var(--ds-ink-3); cursor:pointer;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ds-fp-chip:hover { border-color:var(--ds-line-4); color:var(--ds-ink-hi); }
.ds-fp-pk { flex:none; font-size:10px; color:var(--ds-mut-3); }          /* the ⊕ mark */
.ds-fp-chip.on { background:var(--ds-on-bg); border-color:var(--ds-accent); color:var(--ds-on-ink); }
.ds-fp-chip.on .ds-fp-pk { color:var(--ds-on-ink); }
/* the GUIDED chip — a route into a calculator rather than a layer on the map */
.ds-fp-chip--guide { border-color:var(--ds-fp-guide-bd); background:var(--ds-accent-wash); color:var(--ds-on-ink); }
.ds-fp-chip--guide:hover { border-color:var(--ds-accent); color:var(--ds-on-ink); }
/* a chip that runs the full width of its strip */
.ds-fp-chip--wide { width:100%; }

/* ── the amber PICK cta, and its armed ring ── */
.ds-fp-pick { box-sizing:border-box; height:24px; display:flex; align-items:center;
  justify-content:center; gap:5px; padding:0 10px; border:0; border-radius:4px;
  background:var(--ds-fp-pick); color:var(--ds-fp-on-pick);
  font:600 11px/1 var(--ds-font); cursor:pointer; white-space:nowrap; }
.ds-fp-pick:hover { background:var(--ds-fp-pick-hov); }
.ds-fp-pick.on { box-shadow:inset 0 0 0 2px var(--ds-fp-on-pick); }

/* ── the filled accent cta (Flood, Run) ── */
.ds-fp-cta { box-sizing:border-box; height:26px; width:100%; display:flex; align-items:center;
  justify-content:center; gap:5px; padding:0 12px; border:0; border-radius:4px;
  background:var(--ds-accent); color:var(--ds-on-accent);
  font:500 11px/1 var(--ds-font); cursor:pointer; white-space:nowrap; }
.ds-fp-cta:hover { background:var(--ds-accent-hov); }
.ds-fp-cta--auto { width:auto; height:24px; padding:0 10px; }

/* ── inputs ── */
.ds-fp-in { box-sizing:border-box; width:100%; min-width:0; height:24px; padding:0 7px;
  background:var(--ds-fp-field); border:1px solid var(--ds-quiet-line); border-radius:4px;
  color:var(--ds-ink); font:400 12px/1 var(--ds-mono); outline:none; }
.ds-fp-in:hover { border-color:var(--ds-line-4); }
.ds-fp-in:focus { border-color:var(--ds-accent); background:var(--ds-field-focus); }
.ds-fp-in::placeholder { color:var(--ds-dim); }
.ds-fp-in--area { height:auto; padding:6px 7px; line-height:1.45; font-size:10px; resize:vertical; }

/* ── select faces: the .usel TRIGGER, plus the bare-<select> twin ── */
.ds-fp-sel { display:block; min-width:0; }
.ds-fp-sel .usel, .ds-fp-sel select:not(.usel-native) { display:flex; width:100%; min-width:0; }
.ds-fp-sel .usel-trigger, .ds-fp-sel select:not(.usel-native) {
  box-sizing:border-box; width:100%; min-width:0; height:24px; padding:0 8px;
  background:var(--ds-fp-field); border:1px solid var(--ds-quiet-line); border-radius:4px;
  color:var(--ds-ink-2); font:400 11px/1 var(--ds-font); cursor:pointer; }
.ds-fp-sel .usel-trigger:hover, .ds-fp-sel select:not(.usel-native):hover { border-color:var(--ds-line-4); }
.ds-fp-sel .usel-lbl { flex:1; min-width:0; }
.ds-fp-sel .usel-caret { color:var(--ds-dim); opacity:.9; }

/* ── the 22px tile: the kind-mark button, and the accent tick beside it ── */
.ds-fp-tile { box-sizing:border-box; flex:none; width:22px; height:22px; padding:0;
  display:flex; align-items:center; justify-content:center;
  background:var(--ds-fp-field); border:1px solid var(--ds-quiet-line); border-radius:4px;
  color:var(--ds-ink-2); font:400 12px/1 var(--ds-font); cursor:pointer; }
.ds-fp-tile:hover { border-color:var(--ds-line-4); background:var(--ds-chipbg); }
.ds-fp-tile--on { background:var(--ds-accent); border-color:var(--ds-accent); color:var(--ds-on-accent); }
.ds-fp-tile--on:hover { background:var(--ds-accent-hov); border-color:var(--ds-accent-hov); }
/* …and the tile AS A CHECKBOX: the mock's accent ✓ beside the GROUND picker is a real
   <input type=checkbox> laid over the tile, so it keeps its keyboard and its screen
   reader while the tile is what you see. BOTH panels' ground rows are this control —
   #flood-panel's "In view" tick and #road-panel's — which is why it lives here rather
   than twice in index.html. (#flood-panel still carries the private copy it shipped
   with in task #17; folding it onto this class is a one-line markup change owned by
   whoever next touches that panel.) */
.ds-fp-tile--cb { position:relative; }
.ds-fp-tile--cb input { position:absolute; left:0; top:0; width:100%; height:100%;
  margin:0; opacity:0; cursor:pointer; }
.ds-fp-tile--cb > span { pointer-events:none; opacity:.35; }
.ds-fp-tile--cb:has(input:checked) { background:var(--ds-accent); border-color:var(--ds-accent); color:var(--ds-on-accent); }
.ds-fp-tile--cb:has(input:checked):hover { background:var(--ds-accent-hov); border-color:var(--ds-accent-hov); }
.ds-fp-tile--cb:has(input:checked) > span { opacity:1; }
.ds-fp-tile--cb:has(input:focus-visible) { outline:2px solid var(--ds-accent); outline-offset:1px; }

/* ── sliders: label · track · mono value cell ── */
.ds-fp-slider { display:flex; align-items:center; gap:8px; min-height:18px; }
.ds-fp-slider > span { flex:none; width:74px; font:400 11px/1.3 var(--ds-font); color:var(--ds-ink-4); }
.ds-fp-slider input[type=range] { flex:1; min-width:0; height:4px; margin:0;
  -webkit-appearance:none; appearance:none; background:var(--ds-quiet-line);
  border-radius:3px; accent-color:var(--ds-accent); outline:none; cursor:pointer; }
.ds-fp-slider input[type=range]::-webkit-slider-thumb { -webkit-appearance:none; appearance:none;
  box-sizing:border-box; width:12px; height:12px; border-radius:50%;
  background:var(--ds-accent); border:2px solid var(--ds-bg); cursor:pointer; }
.ds-fp-slider input[type=range]::-moz-range-thumb { box-sizing:border-box;
  width:12px; height:12px; border-radius:50%;
  background:var(--ds-accent); border:2px solid var(--ds-bg); cursor:pointer; }
.ds-fp-val { flex:none; width:52px; text-align:right;
  font:400 11px/1 var(--ds-mono); color:var(--ds-name); font-variant-numeric:tabular-nums; }

/* ── colour ramps: a legend keyed to what the map actually painted ── */
.ds-fp-ramp { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.ds-fp-ramp .bar { display:block; height:6px; border-radius:3px; background:var(--ds-fp-depth-ramp); }
.ds-fp-ramp .lab { display:flex; justify-content:space-between;
  font:400 9px/1 var(--ds-mono); color:var(--ds-mut-3); }
.ds-fp-ramp--freq .bar { height:9px; background:var(--ds-fp-freq-ramp); }

/* ── links said as text (a download, an example) ── */
.ds-fp-link { border:0; background:none; padding:0; font:400 10px/1.3 var(--ds-font);
  color:var(--ds-link); text-decoration:none; cursor:pointer; }
.ds-fp-link:hover { color:var(--ds-link-hov); }

/* ── checkbox rows: name on top, what it is underneath ── */
.ds-fp-cb { display:flex; align-items:flex-start; gap:8px; padding:4px 0; cursor:pointer;
  font:400 11px/1.35 var(--ds-font); color:var(--ds-ink-2); }
.ds-fp-cb input { flex:none; margin:1px 0 0; accent-color:var(--ds-accent); }
.ds-fp-cb small { display:block; font-size:10px; color:var(--ds-mut-3); }


/* ── the disclosure strip: a section the mock omits but the panel keeps, folded away
      until it is wanted. ── */
.ds-fp-disc { border-bottom:1px solid var(--ds-rule); }
.ds-fp-disc-h { box-sizing:border-box; width:100%; display:flex; align-items:center; gap:8px;
  padding:8px 12px; background:none; border:0; text-align:left; cursor:pointer;
  font:400 9px/1.3 var(--ds-font); letter-spacing:.09em; color:var(--ds-mut-2); }
.ds-fp-disc-h:hover { color:var(--ds-ink-hi); }
.ds-fp-chev { flex:none; width:6px; height:6px; border-right:1.5px solid currentColor;
  border-bottom:1.5px solid currentColor; transform:rotate(-45deg);
  transition:transform .15s ease; opacity:.7; }
.ds-fp-disc.open .ds-fp-chev { transform:rotate(45deg); }
/* The COLLAPSE ITSELF is not here. This sheet holds no display:none except the :empty
   collapses — the shrink-never-hide rule applies to every page that loads it, and a
   display:none living in the shared sheet is a hide no page can see. Each panel writes
   its own two-line pair (`.ds-fp-disc-b { display:none } / .open > … { display:block }`)
   scoped to its own id, beside the rest of its state rules. What IS shared is the skin. */
.ds-fp-disc-b { padding:0 12px 9px; }

/* ── footer: what persists, and the one control that ends it ── */
.ds-fp-ft { display:flex; align-items:center; gap:6px; padding:7px 12px; background:var(--ds-head); }
.ds-fp-ft > span { font:400 11px/1.35 var(--ds-font); color:var(--ds-mut-3); }
.ds-fp-ft button { flex:none; padding:4px 10px; background:none;
  border:1px solid var(--ds-line-3); border-radius:4px;
  font:400 11px/1 var(--ds-font); color:var(--ds-ink-3); cursor:pointer; }
.ds-fp-ft button:hover { border-color:var(--ds-line-4); color:var(--ds-ink-hi); }

/* ════════════════════════════════════════════════════════════════════════════
   ADDITIVE, 2026-08-23 — the two families the four new surfaces need.

   Added for documents / reports / permits / photos coming onto the sheet. Both
   are STRICTLY ADDITIVE: they introduce no token, and they restyle nothing that
   already existed. That is a hard requirement rather than good manners — this
   file is loaded by nine surfaces that already shipped (cover-standards,
   service-map, import-profile, index's flood + road panels, admin's three panes,
   audit, organizations, access-admin), so a changed declaration here is a change
   to all of them, made blind, in a commit whose message says "photos".

   Every colour below is a --ds-* token that this sheet already carries in BOTH
   palettes, so both families toggle. Nothing here is a literal.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── fields: a label above a control, in the sheet's density ──────────────────
   The four incoming pages hold ~40 inputs between them, and every one of them is
   currently either raw UA chrome or a page-local recipe that has drifted from the
   next page's. This is the one shape they all are: a 9px uppercase label sitting
   directly above a 24px control, both inside a flex column that may shrink.

   min-width:0 on .ds-fld is load-bearing, not defensive. A flex item's default
   min-width is auto, so a long value in a narrow column refuses to shrink and
   pushes its neighbours off the row instead — which is exactly how a filter bar
   that fits at 1400px silently clips its last control at 1100px. */
.ds-fld { display:flex; flex-direction:column; gap:2px; min-width:0; }
.ds-lab { font:400 9px/1.3 var(--ds-font); letter-spacing:.05em; color:var(--ds-mut-2); }
.ds-in { height:24px; padding:0 8px; font:400 11px/1 var(--ds-font); color:var(--ds-ink);
  background:var(--ds-field); border:1px solid var(--ds-line-3); border-radius:4px; outline:none; }
.ds-in:focus { border-color:var(--ds-accent); background:var(--ds-field-focus); }
.ds-in::placeholder { color:var(--ds-dim); }
/* A textarea is the same control with its height released — height:auto rather
   than a fixed one, because a fixed height on a resizable box fights the user's
   own drag on the next render. */
.ds-in--area { height:auto; padding:6px 7px; line-height:1.45; resize:vertical; }
.ds-in--mono { font-family:var(--ds-mono); }

/* ── dialogs: the four parts every modal on these pages already has ───────────
   Scrim, box, head strip, footer strip. The four pages each reinvented these and
   arrived at four different border radii and three different scrims.

   NO `display` RULE AND NO `z-index`, DELIBERATELY, and both omissions are the
   whole reason this family is safe to adopt without rewiring anything:

   - display: three of the four pages open and close these by writing
     element.style.display from JS. An inline declaration outranks any class, so a
     `display:none` here would be dead CSS on those pages and a live trap on the
     fourth — and "adopt the shared dialog family" would silently become "rewire
     every modal's open/close path", which is not a restyle.
   - z-index: the four pages stack at 200, 900 and 9000, against ui.js's own 12000
     for uiConfirm/uiToast. A number chosen here would be wrong on at least two of
     them, and the failure mode is a dialog that opens BEHIND the thing that
     opened it. Each page keeps its own stacking; this family only paints. */
.ds-modal-back { position:fixed; inset:0; background:var(--ds-scrim); }
.ds-modal-box { background:var(--ds-bg); border:1px solid var(--ds-shell); border-radius:6px;
  box-shadow:var(--ds-shadow-modal); overflow:hidden; }
.ds-modal-hd { display:flex; align-items:center; gap:8px; padding:11px 14px;
  border-bottom:1px solid var(--ds-line-2); }
.ds-modal-hd h3 { margin:0; font:600 13px/1.3 var(--ds-font); color:var(--ds-ink); }
.ds-modal-ft { display:flex; align-items:center; gap:8px; padding:10px 14px;
  background:var(--ds-head); border-top:1px solid var(--ds-line-2); }
