/* lwui — canonical design tokens + component vocabulary.
 *
 * Single source of truth for the look. The preview project
 * (priv/ui_assets/ui/preview/) links this directly; the main dashboard
 * (priv/index.html) still inlines equivalents today and will migrate to
 * link this file one block at a time (then the inline dupes get deleted).
 *
 * Rules of the house — see docs/STYLEGUIDE.md:
 *   - One accent. Color is information, not decoration.
 *   - Enable/disable = .ui-toggle slider. Run/stop = ▶→■ icon button.
 *   - Tabs = text ONLY. Hue = which tab you are on, brightness = whether that
 *     section is enabled, italic = the selected toons disagree. No underline,
 *     no dot, no bar. Never filled boxes.
 *   - Icon buttons are muted + borderless; they sink into a depressed well
 *     on hover, and turn accent when their panel is the active one. Only the
 *     ONE primary action per context is accent (.btn-primary).
 *   - Mutually-exclusive pick (not a tab) = .btn-group; the selected one
 *     looks pressed in (inset). Every button sinks (scale .96) on click.
 */

:root {
  --fg: #e8e8ea;
  --muted: #8a8d93;
  --line: #2a2c30;
  --bg: #0e0f12;
  --bg-2: #16181d;
  --bg-3: #1d2026;
  --row: #1a1c21;
  --accent: #4aa3ff;
  --accent-bg: #16263d;
  /* the dim ends of the two state ramps: a switched-OFF label recedes toward
     the background, and its selected form dims the accent the same way. */
  --fg-dim: #4b4e55;
  --accent-dim: #2c5c8f;
  --ok: #6dd57e;   --ok-bg: #15301c;
  --bad: #ff7b7b;  --bad-bg: #3a1818;
  --warn: #f0c560; --warn-bg: #382a10;
}

/* ---- surfaces ---- */
.kit-surface {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 16px;
}

/* ---- modal header: one row, one baseline, one right margin ---- */
.modal-head { display: flex; align-items: center; gap: 8px;
              margin: 0 0 .6rem; min-height: 28px; }
.modal-head .mh-title { font-size: 14px; font-weight: 600; color: var(--fg);
                        display: flex; align-items: baseline; gap: .4rem;
                        flex-wrap: wrap; min-width: 0; }
.modal-head .mh-spacer { flex: 1 1 auto; }
.modal-head .mh-actions { display: flex; align-items: center; gap: 2px;
                          flex: 0 0 auto; }

/* ---- icon-action button (header actions, close, one-shot ▶) ----
   Muted + borderless. The canonical name is .icon-action; .scanner-icon-btn
   is the legacy alias used throughout index.html today. */
.icon-action, .scanner-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 4px 5px; border-radius: 5px;
  border: 0; background: transparent;
  color: var(--muted); cursor: pointer;
  font-size: 17px; line-height: 1;
}
.icon-action:hover, .scanner-icon-btn:hover { color: var(--fg); background: var(--bg);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .6); transform: translateY(1px); }
.icon-action.cancel, .scanner-icon-btn.cancel { color: var(--bad); background: transparent; }
.icon-action.cancel:hover, .scanner-icon-btn.cancel:hover { background: var(--bad-bg); }

/* ---- primary action button (the ONE accent button per context) ----
   The `.modal button.X` twin on each button rule below raises specificity
   to (0,2,1) so the kit beats app.css's `.modal button` (0,1,1) rule
   REGARDLESS of stylesheet order (kit.css links before app.css). Without
   it, every kit button inside a modal would fall back to the generic
   `.modal button` look. */
.btn-primary, .modal button.btn-primary {
  font-size: 12px; padding: 4px 14px; border-radius: 5px;
  border: 1px solid var(--accent); background: var(--accent-bg);
  color: var(--accent); cursor: pointer;
}
.btn-primary:hover, .modal button.btn-primary:hover { filter: brightness(1.1); }

/* ---- neutral / secondary button ---- */
.btn, .modal button.btn {
  font-size: 12px; padding: 4px 14px; border-radius: 5px;
  border: 1px solid var(--line); background: transparent;
  color: var(--muted); cursor: pointer;
}
.btn:hover, .modal button.btn:hover { color: var(--fg); background: var(--bg-3); }

/* ---- warn / danger button variants (compose with .btn) ----
   Same neutral chrome, tinted text — for cautionary (∞ Farm) and
   destructive/stop (■ Stop, reset) actions that are NOT the primary. */
.btn-warn,   .modal button.btn-warn   { color: var(--warn); }
.btn-warn:hover,   .modal button.btn-warn:hover   { color: var(--warn); background: var(--warn-bg); border-color: var(--warn); }
.btn-danger, .modal button.btn-danger { color: var(--bad); }
.btn-danger:hover, .modal button.btn-danger:hover { color: var(--bad); background: var(--bad-bg); border-color: var(--bad); }

/* ---- small button — dense toolbars (step buttons, inline actions) ---- */
.btn-sm, .modal button.btn-sm { font-size: 11px; padding: 3px 10px; }

/* ---- ui-toggle — slider switch. THE enable/disable control. ----
   Works as <input type="checkbox" class="ui-toggle"> (read/write .checked)
   or <button class="ui-toggle" aria-pressed>. */
.ui-toggle,
input[type="checkbox"].ui-toggle {
  appearance: none; -webkit-appearance: none;
  width: 28px; height: 16px; padding: 0; margin: 0;
  border: 0; outline: 0; border-radius: 999px;
  background: rgba(255, 255, 255, .18);
  position: relative; cursor: pointer; vertical-align: middle;
  transition: background .15s ease; box-sizing: border-box; flex-shrink: 0;
}
.ui-toggle::after,
input[type="checkbox"].ui-toggle::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 12px; height: 12px; border-radius: 50%;
  background: #ffffff; transition: left .15s ease;
}
.ui-toggle[aria-pressed="true"],
input[type="checkbox"].ui-toggle:checked { background: var(--accent); }
.ui-toggle[aria-pressed="true"]::after,
input[type="checkbox"].ui-toggle:checked::after { left: 14px; }
.ui-toggle:hover,
input[type="checkbox"].ui-toggle:hover { background: rgba(255, 255, 255, .26); }
.ui-toggle[aria-pressed="true"]:hover,
input[type="checkbox"].ui-toggle:checked:hover { background: var(--accent); filter: brightness(1.08); }
.ui-toggle:focus-visible,
input[type="checkbox"].ui-toggle:focus-visible { outline: 1px solid var(--accent); outline-offset: 2px; }
/* "mixed" — multiple toons disagree on this setting. Knob centered, warn tint;
   clicking it commits a concrete value to every toon. */
input[type="checkbox"].ui-toggle:indeterminate { background: var(--warn-bg); }
input[type="checkbox"].ui-toggle:indeterminate::after { left: 8px; background: var(--warn); }

/* ---- tabs — TEXT ONLY, no marks. ----
   Canonical name is .tab; .scanner-tab / .fish-tab / .inv-tab / .chat-tab
   are legacy aliases that resolve to this look in index.html.

   A tab answers at most three questions, and typography has exactly three
   independent channels to answer them with — so it needs no dot, no bar and
   no underline:

     HUE         which tab am I on      .active          → accent
     BRIGHTNESS  is this section on     .on / .off       → fg / fg-dim
     STYLE       do the toons agree     .mixed           → italic

   The brightness layer is OPT-IN: a plain strip (chat, scanner, inventory)
   sets neither .on nor .off and keeps the muted/accent pair it always had.
   Only strips whose sections can be switched off carry state, and only a
   bulk edit across disagreeing toons ever sets .mixed.

   Hover must brighten RELATIVE to state — a disabled tab that lit up to full
   --fg under the cursor would read as enabled. */
.tab {
  padding: 5px 12px; background: transparent;
  border: 0;
  color: var(--muted); font-size: 11px; cursor: pointer;
}
.tab:hover  { color: var(--fg); }
.tab.active { color: var(--accent); }

/* state layer */
.tab.off               { color: var(--fg-dim); }
.tab.on                { color: var(--fg); }
.tab.active.off        { color: var(--accent-dim); }
.tab.active.on         { color: var(--accent); }
.tab.off:hover:not(.active) { color: var(--muted); }
.tab.on:hover:not(.active)  { color: #ffffff; }
/* "these toons disagree" — the third channel, free of glyphs and geometry */
.tab.mixed { font-style: italic; }

.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); }

/* ---- press feedback (lab option B) — every kit button sinks on click. ----
   transform-only → compositor level, the cheapest possible feedback. */
.btn, .btn-primary, .icon-action, .scanner-icon-btn {
  transition: color .12s ease, background .12s ease, border-color .12s ease,
              filter .12s ease, box-shadow .12s ease, transform .07s ease;
}
.btn:active, .btn-primary:active { transform: scale(.96); }
.icon-action:active, .scanner-icon-btn:active { transform: translateY(1px) scale(.92); }

/* ---- button group (lab option 2 · inset press) — mutually-exclusive pick. ----
   The selected button looks physically depressed (inner shadow + 1px down);
   siblings stay neutral. For picks that are NOT navigation tabs (use .tab for
   those). Drive selection with .sel or aria-pressed="true". */
.btn-group { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.btn-group > button {
  font-size: 12px; line-height: 1; padding: 7px 13px; border-radius: 6px;
  background: transparent; border: 1px solid var(--line); color: var(--muted);
  cursor: pointer; -webkit-appearance: none; appearance: none;
  transition: color .12s ease, background .12s ease, border-color .12s ease,
              box-shadow .12s ease, transform .08s ease;
}
.btn-group > button:hover {
  color: var(--fg); background: var(--bg-3);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
}
.btn-group > button:active { transform: scale(.96); }
.btn-group > button.sel,
.btn-group > button[aria-pressed="true"] {
  color: var(--fg); background: var(--bg); border-color: var(--line);
  transform: translateY(1px); box-shadow: inset 0 2px 5px rgba(0, 0, 0, .55);
}

/* =================================================================
   COMPONENT PRIMITIVES — emitted by the JS factories in
   web/src/ui/*.ts (createModal, button, numberField, dataTable,
   statusStrip, row/section). One class per reusable element so
   features stop hand-rolling markup + inline styles. Modal-scoped
   twins raise specificity over app.css's `.modal input|button`.
   ================================================================= */

/* ---- modal shell (createModal) ----
   Composes with the app's `.modal` surface. Header stays put; the body
   is the single scroll region, so long panels don't push the × offscreen. */
.ui-modal { display: flex; flex-direction: column; max-height: 92vh; }
.ui-modal > .ui-modal-body { overflow: auto; min-height: 0; flex: 1 1 auto; }
/* Muted meta clause that trails a modal title (label/context). */
.ui-modal .mh-title .meta { color: var(--accent); font-weight: normal; font-size: 11px; }
.ui-modal .mh-sub { font-size: 10px; color: var(--muted); margin: 0 0 .6rem; line-height: 1.4; }

/* ---- row / section — the two layout helpers that replace the
   `display:flex;gap:.5rem;align-items:center;flex-wrap:wrap` inline
   soup and the `border-top:1px solid var(--line);padding-top:.6rem`
   divider that every panel re-typed. ---- */
.ui-row { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.ui-row.tight { gap: .35rem; }
.ui-row .grow { flex: 1 1 auto; }
.ui-section { border-top: 1px solid var(--line); padding-top: .6rem; margin-top: .7rem; }

/* ---- labeled field (numberField / textField) ----
   `<label class="ui-field">text <input class="ui-num"> suffix</label>`.
   `.modal input.ui-num` beats `.modal input { width:100% }`. */
.ui-field { display: inline-flex; align-items: center; gap: .3rem;
            font-size: 11px; color: var(--muted); }
.ui-num, .modal input.ui-num {
  width: 56px; box-sizing: border-box; font-size: 11px; padding: 2px 4px;
  margin: 0; background: var(--bg); color: var(--fg);
  border: 1px solid var(--line); border-radius: 3px;
  font-variant-numeric: tabular-nums; font-family: inherit;
}
.ui-num:focus, .modal input.ui-num:focus { outline: 0; border-color: var(--accent); }
/* free-text twin of .ui-num — wider, proportional, same chrome. Used by the
   settings `text` kind (e.g. an int list edited as "0,1,2"). */
.ui-text, .modal input.ui-text {
  width: 130px; box-sizing: border-box; font-size: 11px; padding: 2px 4px;
  margin: 0; background: var(--bg); color: var(--fg);
  border: 1px solid var(--line); border-radius: 3px; font-family: inherit;
}
.ui-text:focus, .modal input.ui-text:focus { outline: 0; border-color: var(--accent); }
.ui-text::placeholder { color: var(--muted); }

/* ---- status strip (statusStrip) — the tone-coded callout every panel
   re-styled by hand (needreset RED / win GREEN / neutral). ---- */
.ui-strip { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
            padding: .35rem .6rem; border-radius: 3px; font-size: 12px;
            border: 1px solid var(--line); background: var(--bg-2); color: var(--fg); }
.ui-strip.is-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.ui-strip .sep { opacity: .4; }
.ui-strip.is-neutral { border-color: var(--line);   background: var(--bg-2);  color: var(--fg); }
.ui-strip.is-ok      { border-color: var(--accent); }
.ui-strip.is-good    { border-color: var(--ok);     background: var(--ok-bg);   color: var(--ok); }
.ui-strip.is-warn    { border-color: var(--warn);   background: var(--warn-bg); color: var(--warn); }
.ui-strip.is-bad     { border-color: var(--bad);    background: var(--bad-bg);  color: var(--bad); }

/* ---- mono box / rolling log — parsed dumps + step logs. ---- */
.ui-mono { font-size: 11px; border: 1px solid var(--line); border-radius: 3px;
           padding: .5rem; background: var(--bg-2); white-space: pre-wrap;
           font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.ui-log { font-size: 11px; border: 1px solid var(--line); border-radius: 3px;
          padding: .4rem; max-height: 220px; overflow: auto; background: var(--bg-2);
          white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.ui-log .t { color: var(--muted); }

/* ---- data table (dataTable) — the machines/leaderboard tables every
   panel re-declared with per-cell `padding:2px 8px` inline. ---- */
.ui-table { border-collapse: collapse; font-size: 11px; width: 100%;
            font-variant-numeric: tabular-nums; }
.ui-table th { color: var(--muted); text-align: left; font-weight: normal; padding: 2px 8px; }
.ui-table td { padding: 2px 8px; }
.ui-table td.r, .ui-table th.r { text-align: right; }
.ui-table td.c, .ui-table th.c { text-align: center; }
.ui-table tbody tr.click { cursor: pointer; }
.ui-table tbody tr.click:hover { background: var(--row); }
.ui-table tbody tr.sel { background: var(--bg-3); }
.ui-muted { color: var(--muted); }

/* ---- icons (icons.ts) — currentColor glyphs. icon()/iconMarkup() inline the
   full svg; static markup can use the <symbol> sprite via
   `<svg class="ic ic-stroke"><use href="#ic-name"/></svg>` (.ic-stroke/.ic-fill
   carry the paint the inline forms set as attributes). ---- */
.ic { display: block; flex: none; }
/* stroke-width comes from each <symbol> (see spriteMarkup), so a heavier glyph
   like `folder` stays heavy; ic-stroke only sets paint + joins. */
.ic-stroke { fill: none; stroke: currentColor;
             stroke-linecap: round; stroke-linejoin: round; }
.ic-fill { fill: currentColor; }

/* ---- meta — THE muted secondary-text style (captions, hints, counts,
   timestamps). Canonical home for the ~280 inline `color:var(--muted)` sites.
   Scoped `.meta` rules in app.css (10px in dense rows, etc.) still win where
   they apply; this is the bare-`.meta` default. ---- */
.meta { font-size: 11px; color: var(--muted); }

/* ---- chip — small rounded pill/badge (counts, states, tags). ---- */
.ui-chip { display: inline-flex; align-items: center; gap: .3rem; font-size: 11px;
           line-height: 1.6; padding: 0 8px; border-radius: 999px;
           border: 1px solid var(--line); color: var(--muted); background: var(--bg-2);
           font-variant-numeric: tabular-nums; }
button.ui-chip { cursor: pointer; }
button.ui-chip:hover { color: var(--fg); background: var(--bg-3); }
.ui-chip.is-accent { color: var(--accent); border-color: var(--accent); background: var(--accent-bg); }
.ui-chip.is-good   { color: var(--ok);     border-color: var(--ok);     background: var(--ok-bg); }
.ui-chip.is-warn   { color: var(--warn);   border-color: var(--warn);   background: var(--warn-bg); }
.ui-chip.is-bad    { color: var(--bad);    border-color: var(--bad);    background: var(--bad-bg); }
/* removable chip — trailing × for chips that form a user-built set. */
.ui-chip.has-remove { padding-right: 3px; }
.ui-chip-x { border: 0; background: none; cursor: pointer; color: var(--muted);
             font-size: 13px; line-height: 1; padding: 0 3px; border-radius: 999px; }
.ui-chip-x:hover { color: var(--bad); background: var(--bad-bg); }

/* ---- typeahead — debounced search input + result list. ---- */
.ui-typeahead { display: flex; flex-direction: column; gap: .25rem; flex: 1; min-width: 0; }
.ui-typeahead-input { width: 100%; }
.ui-typeahead-results:not(:empty) { border: 1px solid var(--line); border-radius: 6px;
                                    max-height: 230px; overflow-y: auto; background: var(--bg-2); }
.ui-typeahead-row { display: flex; gap: .5rem; align-items: baseline; padding: .3rem .45rem;
                    cursor: pointer; font-size: 12px; border-bottom: 1px solid var(--line-2, var(--line)); }
.ui-typeahead-row:last-child { border-bottom: none; }
.ui-typeahead-row:hover,
.ui-typeahead-row:focus { background: var(--accent-bg); outline: none; }
.ui-typeahead-empty { color: var(--muted); font-size: 12px; padding: .35rem .45rem; }

/* iconCount — compact glyph + number (toon counts, tallies). No pill border;
   tight spacing. Interactive form (a <button>) turns accent on hover. */
.ui-iconcount,
button.ui-iconcount { display: inline-flex; align-items: center; gap: 2px; padding: 0 1px;
                color: var(--muted); font-size: 11px; line-height: 1;
                font-variant-numeric: tabular-nums; background: none; border: 0; }
.ui-iconcount svg { width: 12px; height: 12px; flex: 0 0 auto; }
button.ui-iconcount { cursor: pointer; }
button.ui-iconcount:hover { color: var(--accent); background: none; border: 0; }

/* addTile — the dashed "+" that ends an add-to-able list. Reads as an empty
   SLOT rather than a control: dashed outline, muted glyph, no fill until hover.
   Full width so it lines up with the rows above it. Render it even when the
   list is EMPTY — that is when adding matters most. */
.ui-addtile { display: flex; align-items: center; justify-content: center;
              width: 100%; height: 28px; margin-top: 2px;
              border: 1px dashed var(--line); border-radius: 4px;
              background: transparent; color: var(--muted);
              font-size: 17px; font-weight: 600; line-height: 1;
              cursor: pointer; user-select: none;
              transition: color 80ms ease, border-color 80ms ease, background 80ms ease; }
.ui-addtile:hover { color: var(--accent); border-color: var(--accent); background: var(--bg-3); }
.ui-addtile:active { background: var(--accent-bg); }

/* ---- connectedPanel — a panel docked to an anchor with a notch pointing back
   at it (the "docked notch" pattern). position + flipping are done in JS
   (connected_panel.ts); this is just the surface + the four notch orientations.
   The panel is NOT overflow:hidden (that would clip the protruding notch) — the
   BODY scrolls instead. z-index comes from the layer stack. */
/* Same surface as a .modal (app.css: bg-2 / 1px line / radius 8 / this exact
   shadow) so the panel reads as the SAME window it launched from — a twin, not
   a lightweight popover. z-index is set per-open by connected_panel.ts so it
   sits on top of that window. */
.ui-cpanel { position: fixed; box-sizing: border-box; background: var(--bg-2);
             border: 1px solid var(--line); border-radius: 8px;
             box-shadow: 0 6px 30px rgba(0,0,0,.6); }
.ui-cpanel-body { overflow: auto; padding: 10px 12px; border-radius: 8px; }
/* the notch: a small square rotated into a diamond; two adjacent borders show
   the outward wedge, its bg masks the panel's own border line behind it. `p*`
   = which way it points (pl=left, pr=right, pu=up, pd=down). */
.ui-cpanel-notch { position: absolute; width: 11px; height: 11px;
                   background: var(--bg-2); transform: rotate(45deg); }
.ui-cpanel-notch.pl { border-left: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.ui-cpanel-notch.pr { border-right: 1px solid var(--line); border-top: 1px solid var(--line); }
.ui-cpanel-notch.pu { border-top: 1px solid var(--line); border-left: 1px solid var(--line); }
.ui-cpanel-notch.pd { border-bottom: 1px solid var(--line); border-right: 1px solid var(--line); }

/* ---- tabs factory spacing (the .tabs/.tab look is defined above). ---- */
.ui-tabs { display: flex; flex-direction: column; }
.ui-tabs > .ui-tabpanel { padding-top: .7rem; }

/* ---- select (settingsForm + anywhere a native dropdown fits) ----
   `.modal select.ui-select` beats app.css's generic `.modal select`. */
.ui-select, .modal select.ui-select {
  font-size: 11px; padding: 2px 6px; margin: 0; border-radius: 3px;
  background: var(--bg); color: var(--fg); border: 1px solid var(--line);
}
.ui-select:focus, .modal select.ui-select:focus { outline: 0; border-color: var(--accent); }

/* ---- settings form (settingsForm) — a vertical list of "label … control"
   rows grouped under muted section captions. Opens as a stacked sub-modal. ---- */
/* ---- settings scope bar — WHICH toons this panel writes to. ----
   Sits between the modal header and the scrolling body so it survives page
   navigation: scope belongs to the panel, not to the page you are on. Each
   chip carries its live count, so widening the blast radius is an informed
   click. A chip resolving to 0 toons is dimmed but still selectable — an
   empty scope must read as EMPTY, never silently fall back to "everything". */
.ui-scope { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
            padding: 8px 1rem; border-bottom: 1px solid var(--line); }
.ui-scope-lbl { font-size: 10px; color: var(--muted); text-transform: uppercase;
                letter-spacing: .05em; margin-right: 2px; }
.ui-scope-chip { font-size: 11px; padding: 2px 9px; border-radius: 999px;
                 border: 1px solid var(--line); background: transparent;
                 color: var(--muted); cursor: pointer; display: inline-flex;
                 align-items: center; gap: 5px; }
.ui-scope-chip:hover { color: var(--fg); border-color: var(--muted); }
.ui-scope-chip .n { font-variant-numeric: tabular-nums; font-size: 9px;
                    color: var(--muted); }
.ui-scope-chip.on { color: var(--accent); border-color: var(--accent);
                    background: var(--accent-bg); }
.ui-scope-chip.on .n { color: var(--accent); }
.ui-scope-chip.empty { opacity: .45; }
.ui-scope-n { margin-left: auto; font-size: 10px; color: var(--muted);
              font-variant-numeric: tabular-nums; }

/* A pointer FROM a feature modal INTO its settings page. Used where a modal
   keeps its operational half (a board, a run button, a status line) but its
   persisted config has moved to the registry — so the modal shows the way to
   the one editor instead of growing a second one. */
.ui-set-link-inline { background: none; border: 0; padding: 0; cursor: pointer;
                      color: var(--muted); font-size: 11px; font-family: inherit; }
.ui-set-link-inline:hover { color: var(--accent); }

.ui-settings { display: flex; flex-direction: column; }
.ui-set-section { font-size: 10px; letter-spacing: .05em; text-transform: uppercase;
                  color: var(--muted); margin: .7rem 0 .25rem; }
.ui-set-section:first-child { margin-top: 0; }
.ui-set-row { padding: .4rem 0; }
.ui-set-main { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.ui-set-label { flex: 1 1 auto; font-size: 12px; color: var(--fg); }
/* a control placed inline (trailing) on another field's row, e.g. shop Set next
   to Auto-buy, or the red-packet sources next to Claim-red-packets. */
.ui-set-inline { display: inline-flex; align-items: center; gap: .3rem; flex: 0 0 auto; }
.ui-set-inlabel { font-size: 10px; color: var(--muted); }
.ui-set-flags { display: inline-flex; align-items: center; gap: .45rem; }
.ui-set-status { flex: 0 0 auto; font-size: 10px; color: var(--muted); min-width: 3ch; text-align: right; }
.ui-set-help { font-size: 10px; color: var(--muted); margin-top: .2rem; line-height: 1.35; }
/* link row — opens another module's settings; whole row is the button.
   Doubled-up selectors (with :hover overrides) so these flat rows beat the
   app's boxy `.modal button` default. */
.ui-settings .ui-set-link { display: block; width: 100%; text-align: left;
               background: none; border: 0; border-radius: 0; cursor: pointer;
               padding: .4rem 0; font: inherit; color: inherit; }
.ui-settings .ui-set-link:hover { background: none; }
.ui-settings .ui-set-link:hover .ui-set-label { color: var(--accent); }
.ui-set-link .ui-set-go { flex: 0 0 auto; color: var(--muted); font-size: 15px; }
.ui-set-link:hover .ui-set-go { color: var(--accent); }

/* header back button — replaces the panel title while a settings page is
   shown (`‹ name`); clicking anywhere on it returns to the root list. */
.modal-head .ui-set-back { display: inline-flex; align-items: center; gap: .35rem;
               background: none; border: 0; border-radius: 0; padding: 0;
               cursor: pointer; font: inherit; color: var(--fg); }
.modal-head .ui-set-back:hover { background: none; }
.ui-set-back .ui-set-backchev { color: var(--muted); font-size: 16px; line-height: 1; }
.ui-set-back:hover, .ui-set-back:hover .ui-set-backchev { color: var(--accent); }

/* a legacy feature modal adopted INTO the settings panel as a page: strip its
   window chrome (the panel provides it) and let it fill the body. !important
   beats the inline width/height/position the legacy modals carry. */
.ui-set-embed { width: 100% !important; min-width: 0 !important;
                position: static !important; height: auto !important;
                min-height: 0 !important; max-height: none !important;
                overflow: visible !important; resize: none !important;
                background: none; border: 0; border-radius: 0; padding: 0;
                box-shadow: none; }
/* its own × would fight the panel's — back/× in the header replace it */
.ui-set-embed > .close { display: none; }
/* inline sub-toggle in a `flags` field row (chat-claim sources, …) */
.ui-set-flag { display: inline-flex; align-items: center; gap: .3rem;
               font-size: 11px; color: var(--muted); }

/* compact — dense list: no group headers (dropped in JS), tight rows, help as
   tooltips. */
.ui-settings.compact .ui-set-row  { padding: .18rem 0; }
.ui-settings.compact .ui-set-link { padding: .24rem 0; }
.ui-settings.compact .ui-set-label { font-size: 11.5px; }
.ui-settings.compact .ui-set-flag  { font-size: 10px; gap: .2rem; }
.ui-settings.compact .ui-set-flags { gap: .3rem; }
/* smaller sliders in the dense list */
.ui-settings.compact input.ui-toggle { width: 24px; height: 14px; }
.ui-settings.compact input.ui-toggle::after { width: 10px; height: 10px; }
.ui-settings.compact input.ui-toggle:checked::after { left: 12px; }

/* a collapsed `▸ label (n)` disclosure — a rarely-touched group (e.g. "misc"
   at the bottom, or the module links). The whole group's rows live inside. */
.ui-set-group { margin-top: .3rem; border-top: 1px solid var(--line); }
.ui-set-group > summary { list-style: none; cursor: pointer; padding: .35rem 0 .15rem;
                          font-size: 10px; letter-spacing: .04em; text-transform: uppercase;
                          color: var(--muted); user-select: none; }
.ui-set-group > summary::-webkit-details-marker { display: none; }
.ui-set-group > summary::before { content: '▸ '; }
.ui-set-group[open] > summary::before { content: '▾ '; }
.ui-set-group > summary:hover { color: var(--fg); }
.ui-set-count { color: var(--muted); opacity: .7; }
.ui-set-groupbody { padding-left: .1rem; }
.ui-set-groupbody .ui-set-link { padding: .22rem 0; }

/* selectable chips — a `flags` field (e.g. red-packet sources). Muted when off,
   accent when pressed. Clearer than a row of mini-sliders for "which of these". */
.ui-set-chips { display: inline-flex; align-items: center; gap: .25rem; flex-wrap: wrap; }
.ui-settings .ui-set-chip { font: inherit; font-size: 10px; line-height: 1; cursor: pointer;
               padding: .2rem .4rem; border-radius: 999px;
               border: 1px solid var(--line); background: none; color: var(--muted); }
.ui-settings .ui-set-chip:hover { border-color: var(--muted); color: var(--fg); background: none; }
.ui-settings .ui-set-chip[aria-pressed="true"] { border-color: var(--accent); color: var(--accent);
               background: color-mix(in srgb, var(--accent) 14%, transparent); }
