/* Generated by site/scripts/vendor-to-esm.mjs from site/vendor/tokens/ */
/* ============================================================
   Tasty — TIER 1 · PRIMITIVE tokens
   ------------------------------------------------------------
   Raw, context-free values. The ONLY layer that holds literal
   colors / pixels / ms. Numeric scales, no roles, no opinions.

   • DO read these from the SEMANTIC layer.
   • DO NOT reference these directly from components or app code.

   Theme: Catppuccin. Mocha (dark) is the :root baseline; Latte
   (light) re-points the theme-scoped primitives under
   [data-theme="latte"]. Because the theme lives HERE, the
   semantic + component layers can stay theme-agnostic pointers.

   Naming
     --tasty-color-<hue>            single-value accent (no ramp exists)
     --tasty-color-neutral-<step>   elevation ramp, 0 = deepest bg → 1100 = max fg
                                     (step tracks ROLE position, NOT absolute lightness —
                                      in Latte the ramp is non-monotonic by lightness)
     --tasty-size-<px>              dimension scale (value = px)
     --tasty-radius-<px|full>       corner radius scale
     --tasty-font-size-<px>         type size scale (value = px)
     --tasty-font-weight-<n>        weight scale
     --tasty-line-height-<n×100>    line-height scale
     --tasty-duration-<ms>          motion duration scale
   ============================================================ */

:root {
  /* ── Neutral ramp — Mocha (bg 0 → fg 1100) ────────────────── */
  --tasty-color-neutral-0:    #11111b; /* crust    */
  --tasty-color-neutral-100:  #181825; /* mantle   */
  --tasty-color-neutral-200:  #1e1e2e; /* base     */
  --tasty-color-neutral-300:  #313244; /* surface0 */
  --tasty-color-neutral-400:  #45475a; /* surface1 */
  --tasty-color-neutral-500:  #585b70; /* surface2 */
  --tasty-color-neutral-600:  #6c7086; /* overlay0 */
  --tasty-color-neutral-700:  #7f849c; /* overlay1 */
  --tasty-color-neutral-800:  #9399b2; /* overlay2 */
  --tasty-color-neutral-900:  #a6adc8; /* subtext0 */
  --tasty-color-neutral-1000: #bac2de; /* subtext1 */
  --tasty-color-neutral-1100: #cdd6f4; /* text     */

  /* ── Accent hues — Mocha (single value each) ──────────────── */
  --tasty-color-blue:      #89b4fa;
  --tasty-color-green:     #a6e3a1;
  --tasty-color-red:       #f38ba8;
  --tasty-color-yellow:    #f9e2af;
  --tasty-color-peach:     #fab387;
  --tasty-color-mauve:     #cba6f7;
  --tasty-color-teal:      #94e2d5;
  --tasty-color-sky:       #89dceb;
  --tasty-color-lavender:  #b4befe;
  --tasty-color-flamingo:  #f2cdcd;
  --tasty-color-pink:      #f5c2e7;
  --tasty-color-maroon:    #eba0ac;
  --tasty-color-rosewater: #f5e0dc;

  /* ── Absolutes (theme-invariant) ──────────────────────────── */
  --tasty-color-black: #000000;
  --tasty-color-white: #ffffff;

  /* ── Alpha tints (raw, for derived overlays) ──────────────── */
  --tasty-alpha-white-8:  rgba(255, 255, 255, 0.08);
  --tasty-alpha-white-12: rgba(255, 255, 255, 0.12);
  --tasty-alpha-black-8:  rgba(0, 0, 0, 0.08);
  --tasty-alpha-black-12: rgba(0, 0, 0, 0.12);
  --tasty-alpha-black-50: rgba(0, 0, 0, 0.5);  /* modal scrim */

  /* ── Brand (theme-invariant — the watermelon mark) ────────── */
  --tasty-color-melon-flesh: #f25d6b;
  --tasty-color-melon-rind:  #1e7d4f;
  --tasty-color-melon-seed:  #11111b;

  /* ── Platform-native chrome (theme-invariant OS-system colors) ── */
  --tasty-color-os-windows-close: #c42b1c;  /* Windows caption close-hover fill */
  /* macOS traffic lights are an OS-recognized affordance: users expect the exact
     system red/amber/green, NOT the Catppuccin accent shades. Like the Windows
     close fill, these are theme-invariant OS-system colors (no Latte remap). */
  --tasty-color-os-macos-close: #ec6a5e;  /* macOS traffic-light — close  */
  --tasty-color-os-macos-min:   #f4bf4f;  /* macOS traffic-light — minimize */
  --tasty-color-os-macos-zoom:  #61c554;  /* macOS traffic-light — zoom   */

  /* ── Dimension scale (value = px) ─────────────────────────── */
  --tasty-size-0:   0;
  --tasty-size-1:   1px;
  --tasty-size-1-5: 1.5px; /* rail dot cut-out ring — OFF-SCALE hairline (2026-10-07) */
  --tasty-size-2:   2px;
  --tasty-size-3:   3px;
  --tasty-size-4:   4px;
  --tasty-size-6:   6px;  /* compact status dot — dots inside 24px-tall chrome (tab strip, status bar, sidebar rail) */
  --tasty-size-8:   8px;
  --tasty-size-10:  10px; /* off-grid inset kept from the transfer popup (footer pad-y · body gap · well pad-x) — 2026-10-06 */
  --tasty-size-12:  12px;
  --tasty-size-14:  14px;
  --tasty-size-16:  16px;
  --tasty-size-24:  24px;
  /* fixed control dimensions (off-grid, but real product values) */
  --tasty-size-20:  20px;  /* modifier-hint empty-row min height (2026-10-07) */
  --tasty-size-22:  22px;
  --tasty-size-28:  28px;
  --tasty-size-32:  32px;
  --tasty-size-36:  36px;  /* titlebar height (CSD) */
  --tasty-size-40:  40px;  /* image zoom-percent label min width (2026-10-07) */
  --tasty-size-44:  44px;  /* preset-window detail toolbar / surface-settings header height */
  --tasty-size-46:  46px;  /* Windows caption button width */
  --tasty-size-48:  48px;  /* DAG node card height */
  --tasty-size-52:  52px;  /* fixed dialog/settings footer height (28 control + 2 × 12 padding) */
  --tasty-size-56:  56px;  /* banner affordance column reserve (2 × 24 + gaps) */
  --tasty-size-120: 120px; /* explorer favorites pin — lower bound */
  --tasty-size-150: 150px;
  --tasty-size-196: 196px; /* explorer sidebar width */
  --tasty-size-240: 240px; /* tooltip max-width · explorer favorites pin height */
  --tasty-size-320: 320px; /* toast / popover max-width */
  --tasty-size-352: 352px;  /* notifications popup default width */
  /* layout / form-control / measure widths (fixed product dimensions; like 22/46
     above, some are off the 4px grid — real control widths, not spacing rhythm) */
  --tasty-size-64:  64px;  /* file-picker ancestor-crumb floor */
  --tasty-size-88:  88px;
  --tasty-size-90:  90px;
  --tasty-size-96:  96px;  /* file-picker current-folder crumb floor */
  --tasty-size-110: 110px;
  --tasty-size-112: 112px;
  --tasty-size-140: 140px; /* keybinding replacement-slot min width */
  --tasty-size-160: 160px;
  --tasty-size-168: 168px; /* DAG node card width */
  --tasty-size-180: 180px;
  --tasty-size-200: 200px;
  --tasty-size-220: 220px; /* autocomplete dropdown default max-height (~7 rows) */
  --tasty-size-288: 288px;
  --tasty-size-300: 300px;
  --tasty-size-360: 360px; /* info modal max height (boot notice queue) */
  --tasty-size-400: 400px;
  --tasty-size-440: 440px; /* info modal width (boot notice queue) */
  --tasty-size-460: 460px;
  --tasty-size-480: 480px; /* gallery specimen width for wide (>= 440) HTML surfaces (2026-11-01) */
  --tasty-size-560: 560px;
  --tasty-size-600: 600px; /* explorer sidebar "small surface" threshold */
  --tasty-size-620: 620px; /* settings content column max width */
  --tasty-size-540: 540px; /* command palette card width (2026-10-06) */
  --tasty-size-700: 700px; /* settings window height (2026-10-06) */
  --tasty-size-1100: 1100px; /* settings window width (2026-10-06) */

  /* ── Radius scale ─────────────────────────────────────────── */
  --tasty-radius-0:    0px;  /* 2026-10-21 — square corner (popup over an opaque-mode WebView page) */
  --tasty-radius-2:    2px;
  --tasty-radius-4:    4px;
  --tasty-radius-8:    8px;  /* Wayland CSD window top-corner radius */
  --tasty-radius-full: 9999px;

  /* ── Type families ────────────────────────────────────────── */
  --tasty-font-family-sans: -apple-system, BlinkMacSystemFont, "Segoe UI",
                            "Noto Sans", "Helvetica Neue", Arial, sans-serif;
  --tasty-font-family-mono: "D2Coding", "SF Mono", "Cascadia Code",
                            "JetBrains Mono", ui-monospace, "Menlo", "Consolas", monospace;

  /* ── Type size scale (value = px) ─────────────────────────── */
  --tasty-font-size-10: 10px;  /* sub-caption micro-labels (kbd, badge, meta) */
  --tasty-font-size-11: 11px;
  --tasty-font-size-12: 12px;
  --tasty-font-size-13: 13px;
  --tasty-font-size-14: 14px;
  --tasty-font-size-16: 16px;
  --tasty-font-size-17: 17px;  /* "tasty." brand wordmark (mono) — BRANDING exception to the 14px UI cap (see semantic font-size-brand-wordmark) */
  --tasty-font-size-20: 20px;  /* markdown-surface H1 (rendered content, not UI chrome) */
  --tasty-font-size-30: 30px;  /* first-run brand display title — BRANDING exception to the 14px UI cap, same class as the wordmark (see semantic font-size-brand-display) */

  /* ── Weight scale ─────────────────────────────────────────── */
  --tasty-font-weight-400: 400;
  --tasty-font-weight-500: 500;
  --tasty-font-weight-600: 600;
  --tasty-font-weight-700: 700;

  /* ── Line-height scale (value ×100) ───────────────────────── */
  --tasty-line-height-100: 1.0;
  --tasty-line-height-120: 1.2;
  --tasty-line-height-140: 1.4;
  --tasty-line-height-160: 1.6;

  /* ── Letter-spacing scale ─────────────────────────────────── */
  --tasty-letter-spacing-0:  0;
  --tasty-letter-spacing-04: 0.04em;
  --tasty-letter-spacing-07: 0.07em;  /* wide caps — mono 10 headings keep a 1px column gap after integer advance (2026-10-06) */
  --tasty-letter-spacing-n1px: -1px;  /* brand lockup (38px loading screen) only (2026-10-06) */

  /* ── Motion ───────────────────────────────────────────────── */
  --tasty-duration-0:   0ms; /* @kind other */
  --tasty-duration-90:  90ms; /* @kind other */
  --tasty-duration-120: 120ms; /* @kind other */
  --tasty-duration-150: 150ms; /* @kind other */
  --tasty-duration-200: 200ms; /* @kind other */
  --tasty-duration-500: 500ms; /* @kind other */
  --tasty-duration-800: 800ms; /* @kind other */ /* explorer drag-over expand (2026-10-10) */
  /* spinner full-rotation period (a real UI spin, ~1 rev/s). NOT a typo of 90:
     the 90ms export was a dropped digit; see component.spinner-duration. */
  --tasty-duration-900: 900ms; /* @kind other */
  --tasty-duration-1600: 1600ms; /* @kind other */
  --tasty-easing-standard: cubic-bezier(0.2, 0, 0, 1); /* @kind other */

  /* ── Unitless multipliers (UI zoom stops) ─────────────────── */
  --tasty-scale-80:  0.8; /* @kind other */
  --tasty-scale-100: 1; /* @kind other */
  --tasty-scale-120: 1.2; /* @kind other */

  /* ── Opacity ─────────────────────────────────────── */
  /* DIM coefficient — an interactive-but-inactive item (switched-off row, pending cut,
     modal-inert region). NOT a disabled-control signal: disabled controls read the
     disabled ink (--tasty-text-disabled) and no opacity (2026-09-29). Name kept, no rename. */
  --tasty-opacity-disabled: 0.5; /* @kind other */
  /* a lower-tier banner dimmed BEHIND a higher-scope banner (≈60% transparent) */
  --tasty-opacity-recessed: 0.4; /* @kind other */
  /* a DE-EMPHASISED but still READABLE element (skipped / cancelled DAG node):
     0.4 and 0.65 both drop 10-13px text under the 4.5:1 floor once the whole
     card is painted at that opacity, so the readable dim step is 0.75. */
  --tasty-opacity-dimmed: 0.75; /* @kind other */
  /* TINTED-FILL IDIOM — one accent painted as a soft fill + a stronger edge of the
     same hue (info badges, notice blocks, warning callouts, status chips). Two
     coefficients, used as a PAIR; every tinted box in the system reads these two
     and nothing else. Border-only variants (chip remote tag) take the border
     coefficient alone; fill-only variants (warning callout) take the fill alone. */
  --tasty-opacity-tint-fill:   0.12; /* @kind other */
  --tasty-opacity-tint-border: 0.36; /* @kind other */
}

/* ============================================================
   Latte (light) — re-point theme-scoped color primitives only.
   Sizes / type / motion are theme-invariant and not repeated.
   ============================================================ */
[data-theme="latte"] {
  --tasty-color-neutral-0:    #dce0e8; /* crust    */
  --tasty-color-neutral-100:  #e6e9ef; /* mantle   */
  --tasty-color-neutral-200:  #eff1f5; /* base     */
  --tasty-color-neutral-300:  #ccd0da; /* surface0 */
  --tasty-color-neutral-400:  #bcc0cc; /* surface1 */
  --tasty-color-neutral-500:  #acb0be; /* surface2 */
  --tasty-color-neutral-600:  #9ca0b0; /* overlay0 */
  --tasty-color-neutral-700:  #8c8fa1; /* overlay1 */
  --tasty-color-neutral-800:  #7c7f93; /* overlay2 */
  --tasty-color-neutral-900:  #63667c; /* subtext0 — DIVERGES from upstream catppuccin latte (#6c6f85, 4.37:1 on base = AA fail). Darkened to clear 4.5:1 on base (4.99), mantle (4.64), #fff (5.64) while keeping the ramp order: any darker collapses the gap to subtext1 (#5c5f77) and text-muted/secondary stop reading apart. crust (4.26) / surface0 (3.65) stay accepted exceptions — see docs/design/systems/theme.md. */
  --tasty-color-neutral-1000: #5c5f77; /* subtext1 */
  --tasty-color-neutral-1100: #4c4f69; /* text     */

  --tasty-color-blue:      #1e66f5;
  --tasty-color-green:     #40a02b;
  --tasty-color-red:       #d20f39;
  --tasty-color-yellow:    #df8e1d;
  --tasty-color-peach:     #fe640b;
  --tasty-color-mauve:     #8839ef;
  --tasty-color-teal:      #179299;
  --tasty-color-sky:       #04a5e5;
  --tasty-color-lavender:  #7287fd;
  --tasty-color-flamingo:  #dd7878;
  --tasty-color-pink:      #ea76cb;
  --tasty-color-maroon:    #e64553;
  --tasty-color-rosewater: #dc8a78;
}

/* ============================================================
   Tasty — TIER 2 · SEMANTIC tokens
   ------------------------------------------------------------
   Roles & intent. Every value is a reference to a PRIMITIVE —
   never a literal (the two exceptions are color-mix derivations
   and a handful of role-remaps under Latte, both flagged inline).

   • DO read these from components / app code / the COMPONENT layer.
   • DO NOT hardcode a primitive where a semantic role exists.

   Theme is resolved one layer down (primitives), so these are
   theme-agnostic pointers — EXCEPT where a role genuinely maps
   to a different primitive between themes (text-on-accent, the
   derived overlays, focused terminal bg). Those few are
   overridden in the [data-theme="latte"] block below.
   ============================================================ */

/* also on [data-theme] so a scoped theme wrapper (e.g. a gallery Latte specimen)
   re-resolves this tier against its own primitives instead of inheriting :root values */
:root, [data-theme] {
  /* ── Surfaces / elevation (tint, never shadow) ────────────── */
  --tasty-bg-app:        var(--tasty-color-neutral-0);   /* window chrome, deepest */
  --tasty-bg-sidebar:    var(--tasty-color-neutral-100);  /* sidebars, side panels */
  --tasty-bg-panel:      var(--tasty-color-neutral-200);  /* primary content panels */
  --tasty-surface-raised: var(--tasty-color-neutral-300); /* cards, inputs, menus */
  --tasty-surface-hover:  var(--tasty-color-neutral-400); /* hovered list rows */
  --tasty-surface-active: var(--tasty-color-neutral-500); /* selected / pressed rows */

  /* ── Text ─────────────────────────────────────────────────── */
  --tasty-text-primary:     var(--tasty-color-neutral-1100);
  --tasty-text-secondary:   var(--tasty-color-neutral-1000);
  --tasty-text-muted:       var(--tasty-color-neutral-900);
  --tasty-text-disabled:    var(--tasty-color-neutral-700);
  --tasty-text-placeholder: var(--tasty-color-neutral-600);
  /* dim/secondary GLYPH ink — decorative or orienting marks that must recede:
     sidebar collapse chevrons, dim category icons, inert list affordances.
     Same ramp step as placeholder, a DIFFERENT role: placeholder is unentered
     user text, this is chrome. Disabled controls take text-disabled, not this. */
  --tasty-glyph-dim:        var(--tasty-color-neutral-600);
  /* secondary ink on a RAISED ground (surface-raised: menus, dropdown lists). 2026-10-08 (batch 8):
     Mocha = text-secondary (7.1:1); Latte → neutral-1100 because n1000 on surface0 is 4.05:1. */
  --tasty-text-secondary-raised: var(--tasty-color-neutral-1000);
  --tasty-text-on-accent:   var(--tasty-color-neutral-0); /* dark text atop a filled accent (Latte → white) */
  --tasty-text-on-window-close: var(--tasty-color-white); /* glyph atop the (dark) Windows close-hover fill — white in both themes */

  /* ── Accents (role → hue) ─────────────────────────────────── */
  --tasty-accent-primary: var(--tasty-color-blue);   /* primary actions, focus */
  --tasty-accent-info:    var(--tasty-color-sky);
  --tasty-accent-success: var(--tasty-color-green);
  --tasty-accent-warning: var(--tasty-color-yellow);
  /* 2026-10-19: a warning GLYPH that must clear 3:1 on light grounds. Mocha = accent-warning; Latte remaps (yellow #df8e1d is 2.3:1 on base). */
  --tasty-accent-warning-glyph: var(--tasty-accent-warning);
  /* 2026-10-21: danger INK (error text + its glyph) on a RAISED ground (surface-raised cards). Mocha = accent-danger
     (5.43:1 on surface0); Latte remaps (red #d20f39 on surface0 = 3.52:1). Fills / invalid edges keep accent-danger. */
  --tasty-accent-danger-raised: var(--tasty-accent-danger);
  /* 2026-10-27: success INK (text + its glyph) on a success TINT box (accent-success × tint-fill over mantle/base).
     Mocha = accent-success (≈ 8.3–9.0:1); Latte remaps (green #40a02b on its own tint = 2.45:1). Fills / borders keep accent-success. */
  --tasty-accent-success-ink: var(--tasty-accent-success);
  /* attention: a SECOND, deliberately non-yellow "notice" tone for states that
     want a heads-up without the transient/validation reading of warning-yellow.
     Used for plugin needs-attention (rejected / failing). Peach re-points per
     theme in the primitive layer, so no role-remap is needed here. Kept distinct
     from accent-warning (yellow) on purpose.
     NOTE: occupied-SURFACE notices now have their own role (accent-occupied-hard
     below) — split out of attention so the occupancy axis can evolve on its own,
     even though today it maps to the same peach primitive. */
  --tasty-accent-attention: var(--tasty-color-peach);
  --tasty-accent-danger:  var(--tasty-color-red);
  --tasty-accent-agent:   var(--tasty-color-mauve);  /* AI-agent surfaces / actions */
  /* decorative accent — a warm mark on window chrome (a header glyph) that
     carries NO state meaning. Same peach primitive as accent-attention on
     purpose (one warm tone in the system), kept a separate role so a header
     ornament never reads as "this needs attention". */
  --tasty-accent-decorative: var(--tasty-color-peach);
  --tasty-accent-window-close: var(--tasty-color-os-windows-close); /* OS-native window-close hover (Windows CSD) */
  --tasty-accent-attached: var(--tasty-color-lavender); /* workspace claimed by ANOTHER client (multi-client attach) — deliberately NOT danger-red, which means error */
  --tasty-accent-remote:   var(--tasty-color-sky); /* MIRROR: this workspace is a local mirror of a REMOTE tasty instance (an origin/identity, NOT an execution state) — an axis of its own, kept off the status dot */
  --tasty-accent-data:     var(--tasty-color-teal); /* DATA FLOW: a value handed from one task to another (DAG input binding edge). An origin-of-data mark, not a state (2026-10-07) */
  --tasty-accent-route:    var(--tasty-color-lavender); /* ROUTE: a result-chosen path between tasks (DAG transition edge). Same primitive as accent-attached, a different role — never shown on the same surface (2026-10-07) */
  --tasty-accent-move:     var(--tasty-color-pink); /* MOVE SOURCE: the one surface / tab / pane armed by "Move Surface / Tab / Pane" and waiting for "… Here". A pending-action marker, not a state or an attention kind — its own hue so it never reads as notice (yellow/blue), occupancy (green/peach), agent (mauve) or attach (lavender) */

  /* OCCUPANCY — a subject (remote user / AI agent) HOLDS a terminal surface.
     Two tiers share ONE visual channel (the surface border) and are told apart
     by COLOR alone (see the component-tier surface-occupied-* tokens):
       soft (green) — occupied, cooperative signal, NO write restriction. May
                      close or change at any time.
       hard (peach) — soft's notice + readonly (input blocked, mirror-observe).
                      Absorbs the old remote-attach occupancy border.
     Deliberately distinct roles from accent-success (green: busy/success/select)
     and accent-attention (peach: plugin needs-attention) so occupancy meaning
     never bleeds into those. Both hues re-point per theme in the primitive
     layer, so no role-remap is needed here. */
  --tasty-accent-occupied-soft: var(--tasty-color-green);
  --tasty-accent-occupied-hard: var(--tasty-color-peach);

  /* ATTENTION KINDS — an EXTENSIBLE scale, not a pair of one-off colors.
     A background subject (agent / task / remote session) raises "attention" on a
     target (workspace · tab · surface). Each attention KIND gets exactly one
     semantic slot here, and every channel that renders attention (badge, tab
     title, surface border, collapsed rail dot) reads THIS slot — never an
     accent role directly. Adding a kind = one new line here + one line per
     channel in components.css; no channel logic changes.

     The scale has two axes:
       COLOR — each kind points at an existing accent role. No new hue is minted
               for attention; a kind borrows the role whose meaning it already
               shares, so attention never becomes a private palette.
       RANK  — a fixed integer urgency order. When several kinds land on the same
               target, the HIGHEST rank wins the single visual slot (see
               --tasty-attention-rank-* below). Ranks are spaced by 10 so a
               future kind can slot BETWEEN two existing ones without a renumber.

     Live kinds (rank high → low):
       needs-input  30  yellow — blocked, waiting on the human. The only kind
                                 that is a REQUEST; it outranks everything.
       completion   10  blue   — a task finished. FYI, clears on focus.
     Reserved (define when the feature ships, same two lines each):
       error        40  danger-red   — failed and unrecovered
       approval     20  agent-mauve  — an agent asks permission (sits between) */
  --tasty-attention-needs-input: var(--tasty-accent-warning);
  --tasty-attention-completion:  var(--tasty-accent-primary);
  /* fg = readable text/glyph atop the filled kind color (both live kinds are
     bright fills, so both take the on-accent tone; a future dark-fill kind may
     override its own slot) */
  --tasty-attention-needs-input-fg: var(--tasty-text-on-accent);
  --tasty-attention-completion-fg:  var(--tasty-text-on-accent);
  /* notification levels (2026-10-17, notification-center) — the channel's three levels. info is the
     completion blue ON PURPOSE (same hue as the LNB completion count). The counts are a separate axis:
     never summed with surface attention counts, never painted on workspace rows. */
  --tasty-notify-error:      var(--tasty-accent-danger);
  --tasty-notify-warning:    var(--tasty-accent-warning);
  --tasty-notify-info:       var(--tasty-attention-completion);
  --tasty-notify-error-fg:   var(--tasty-text-on-accent);
  --tasty-notify-warning-fg: var(--tasty-text-on-accent);
  /* 2026-10-19: FILLED warning (badge pill) — split from notify-warning so the Latte
     fill can lighten without touching the warning glyph/accent. Mocha = notify-warning. */
  --tasty-notify-warning-fill: var(--tasty-notify-warning);
  --tasty-notify-info-fg:    var(--tasty-text-on-accent);
  --tasty-notify-unread:     var(--tasty-accent-primary);   /* unread dot on a list item */
  /* rank — urgency order, resolved by the implementation, exposed as tokens so
     design and code can't drift apart */
  --tasty-attention-rank-needs-input: 30; /* @kind other */
  --tasty-attention-rank-completion:  10; /* @kind other */

  /* macOS traffic-light roles — OS-recognized affordance colors, theme-invariant
     (point straight at the OS primitives, like accent-window-close). */
  --tasty-accent-macos-close: var(--tasty-color-os-macos-close);
  --tasty-accent-macos-min:   var(--tasty-color-os-macos-min);
  --tasty-accent-macos-zoom:  var(--tasty-color-os-macos-zoom);

  /* idle/inactive indicator tone — deliberately low-contrast so idle dots
     recede and active states (success/agent/warning/danger) pop. Neutral ramp
     → adapts per theme. */
  --tasty-status-idle: var(--tasty-color-neutral-600);

  /* ── Borders ──────────────────────────────────────────────── */
  --tasty-border-default: var(--tasty-color-neutral-300);
  --tasty-border-strong:  var(--tasty-color-neutral-400);
  /* structural FRAME edge — one step above border-strong: pane dividers, the
     popup frame outline + its titlebar underline, the border of a surface whose
     GPU layer is inactive. FOUR places, closed list. A card outline or divider
     INSIDE a panel is a partition → border-strong, never this.
     Same ramp step as surface-active, a different role (that one is a FILL). */
  --tasty-border-frame:   var(--tasty-color-neutral-500);
  --tasty-border-focus:   var(--tasty-color-blue);

  /* ── States — derived overlay tints (Latte → black) ───────── */
  --tasty-overlay-hover:  var(--tasty-alpha-white-8);
  --tasty-overlay-active: var(--tasty-alpha-white-12);
  --tasty-separator:      var(--tasty-alpha-white-8);

  /* ── Elevation — the SOLE shadow exception (floating overlays / CSD scrims).
     Tasty has no general shadow system; surfaces use tint. These two raw
     box-shadows are the one-off scrims floating surfaces are allowed to use,
     so overlay chrome references a token instead of a hardcoded shadow.

     SCOPE RULE (canonical — decides which of the two, no third value exists):
       anchored + scrim-less, floats over live content  → shadow-popover
       centered + scrim-backed, owns the whole viewport → shadow-modal
     The scrim dims the ground but draws no edge; on a dark theme a dark modal
     on a dimmed dark ground loses its silhouette, so a scrim-backed surface
     takes the scrim AND the shadow. Both are single-layer, pure-black-alpha,
     integer-px, non-negative-spread, theme-agnostic — deliberately inside what
     a client-side renderer can express. Geometry (6/18, 20/60) is off the 4px
     grid on purpose: the grid governs the spacing and size axes, not blur.

     Anything floating that is NOT in one of the two lists below gets NO shadow. */

  /* shadow-popover — anchored, scrim-less. Canonical surfaces:
     search bar · tools menu · sidebar context menu · tooltip bubble ·
     autocomplete menu · multiselect / combobox menu · banner shell ·
     banner more-menu · modifier-hint panel · tutorial callout ·
     switch-number overlay · any anchored dropdown that opens over the terminal */
  --tasty-shadow-popover: 0 6px 18px rgba(0, 0, 0, 0.4);
  /* shadow-modal — centered, scrim-backed. Canonical surfaces:
     command palette · ports · remote · settings · plugins · preset editor ·
     git viewer · clipboard viewer · DAG popup · every centered confirm/edit dialog */
  --tasty-shadow-modal:   0 20px 60px rgba(0, 0, 0, 0.55);
  /* shadow-none — 2026-10-21. The explicit "no lift" value: a floating surface whose rect sits on an
     opaque-mode WebView page cannot paint outside its own rect, so a shadow there would be cut to half. */
  --tasty-shadow-none:    none;

  /* ── Scrim — dim layer behind centered modals/palette ─────── */
  --tasty-scrim-bg:          var(--tasty-alpha-black-50);
  --tasty-overlay-top-offset: var(--tasty-size-88); /* top-aligned spawn gap (command palette) */

  /* ── Spacing (4px grid) ───────────────────────────────────── */
  --tasty-space-xs: var(--tasty-size-4);
  --tasty-space-sm: var(--tasty-size-8);
  --tasty-space-md: var(--tasty-size-12);
  --tasty-space-lg: var(--tasty-size-16);
  --tasty-space-xl: var(--tasty-size-24);

  /* ── Measure — readable column / blurb max-widths ──────── */
  --tasty-measure-sm: var(--tasty-size-300);
  --tasty-measure-md: var(--tasty-size-400);
  --tasty-measure-lg: var(--tasty-size-460);
  --tasty-measure-xl: var(--tasty-size-560);

  /* ── Field width — form-control widths (selects, color/number inputs, sliders) ── */
  --tasty-field-width-xs:    var(--tasty-size-90);
  --tasty-field-width-color: var(--tasty-size-110);
  --tasty-field-width-md:    var(--tasty-size-160);
  --tasty-field-width-range: var(--tasty-size-180);
  --tasty-field-width-lg:    var(--tasty-size-200);

  /* ── Border width & radius (system-wide constants) ────────── */
  --tasty-border-width:     var(--tasty-size-1);   /* always 1px */
  --tasty-focus-ring-width: var(--tasty-size-2);   /* 2px accent ring */
  --tasty-radius:           var(--tasty-radius-4); /* default corner */
  --tasty-radius-sm:        var(--tasty-radius-2); /* tight inner elements */
  --tasty-radius-pill:      var(--tasty-radius-full);
  --tasty-radius-none:      var(--tasty-radius-0); /* 2026-10-21 — square corner; opaque WebView overlay edge */

  /* ── Fixed interactive heights ────────────────────────────── */
  --tasty-control-height-tree: var(--tasty-size-22); /* file-tree / sidebar rows */
  --tasty-control-height:      var(--tasty-size-28); /* buttons, inputs, menu rows */
  --tasty-control-height-tab:  var(--tasty-size-24); /* tab strip */
  --tasty-tab-width:           var(--tasty-size-150);
  --tasty-titlebar-height:     var(--tasty-size-36); /* CSD window titlebar (4px grid) */
  --tasty-toolbar-height:      var(--tasty-size-32); /* in-surface CONTAINER toolbar (git diff bar, viewer toolbars) — a container role, NOT control-height 28 or button-height-lg */
  --tasty-status-bar-height:   var(--tasty-size-24); /* workspace-area bottom StatusBar — fixed host-chrome height, OUTSIDE the UI zoom scale (like titlebar / tab strip / caption) */

  /* ── Icon glyph sizing (svg width/height inside controls) ── */
  --tasty-icon-size-xs: var(--tasty-size-12); /* chevrons, close, inline glyphs */
  --tasty-icon-size-sm: var(--tasty-size-14); /* tree / tab / menu icons */
  --tasty-icon-size-md: var(--tasty-size-16); /* toolbar / input / toast icons */
  --tasty-icon-size-lg: var(--tasty-size-24); /* centred state glyph (empty / loading / error blocks) — replaces the off-scale 22 and 26 */

  /* ── Disabled state (2026-09-29) — ink, never opacity ──
     A DISABLED control (non-interactive) keeps a neutral box and drops its ink:
     label + glyphs → disabled ink, fill → state-disabled-fill, edge → state-disabled-border.
     Accent-filled variants lose the accent (no text-disabled on an accent fill). No hover /
     active overlay. An interactive-but-inactive ITEM (switched-off row, pending cut) is not
     disabled — it dims with state-dim-opacity and keeps its own colours. */
  --tasty-state-disabled-fg:      var(--tasty-text-disabled);
  --tasty-state-disabled-fill:    var(--tasty-surface-raised);
  --tasty-state-disabled-border:  var(--tasty-border-default);
  /* 2026-10-14 — the same disabled / read-only box on a RAISED ground (surface-raised: menus,
     banners, popovers). There fill = surface-raised and border-default = surface-raised, so the box
     vanished. On raised, the box steps DOWN one ramp step and takes the strong edge. */
  --tasty-state-disabled-fill-raised:   var(--tasty-bg-panel);
  --tasty-state-disabled-border-raised: var(--tasty-border-strong);
  --tasty-state-dim-opacity:      var(--tasty-opacity-disabled);   /* 0.5 — dimmed ITEMS, not disabled controls */
  --tasty-state-disabled-opacity: var(--tasty-opacity-disabled);   /* DEPRECATED alias (2026-09-29): no control reads it; migrate readers to state-dim-opacity or to the ink rule */

  /* ── Stacked text + one-edge marks ── */
  --tasty-label-detail-gap:     var(--tasty-size-2); /* label line ↔ its detail line in one text stack (permission row, banner title ↔ body, workspace row). On-scale. */
  --tasty-selection-edge-width: var(--tasty-size-2); /* one-edge selection bar (tab indicator, active workspace row, ListCtrl + every selected list row: file handler / git viewer / plugins / remote-attach / preset / file picker). HAIRLINE — zoom-exempt like border-width */

  /* ── Tinted fill + edge (the accent-box idiom) ── */
  --tasty-tint-fill-alpha:   var(--tasty-opacity-tint-fill);   /* 0.12 — accent as a soft fill */
  --tasty-tint-border-alpha: var(--tasty-opacity-tint-border); /* 0.36 — the same accent as the edge */
  /* ── Dashed edge (one rule for every dashed 1px outline: empty-hint box, "Add trigger…" control). CSS draws `dashed`;
     a renderer without CSS dashes draws segments of this length/gap. Corners stay SOLID (the arc is drawn whole); dashes run on the
     straight edges only, centred so each edge starts and ends on a dash. OFF-SCALE like border-width (2026-10-07) */
  --tasty-border-dash:     var(--tasty-size-4);
  --tasty-border-dash-gap: var(--tasty-size-4);
  --tasty-tint-edge-width:   var(--tasty-size-2); /* 2 — the accent-box's one leading edge (Do / Don't callouts, notes). HAIRLINE — zoom-exempt like selection-edge-width (2026-10-06) */

  /* ── Typography roles ─────────────────────────────────────── */
  --tasty-font-ui:   var(--tasty-font-family-sans);
  --tasty-font-mono: var(--tasty-font-family-mono);

  --tasty-font-size-caption: var(--tasty-font-size-11); /* meta, badges, hints */
  --tasty-font-size-micro:   var(--tasty-font-size-10); /* sub-caption micro-labels: kbd, badge, tag, tree/menu meta */
  --tasty-font-size-body:    var(--tasty-font-size-13); /* default UI text */
  --tasty-font-size-heading: var(--tasty-font-size-13); /* same size, weight 600 */
  --tasty-font-size-max:     var(--tasty-font-size-14); /* hard ceiling for UI chrome */
  --tasty-font-size-prose-h1: var(--tasty-font-size-20); /* markdown Heading ANCHOR — rendered CONTENT, exempt from the UI cap. REDEFINED for the egui_commonmark adoption: no longer a per-level pixel size but the top of the library's interpolated heading ladder (the egui `Heading` text-style; H2–H6 are interpolated between this and Body by the library, not individually set). */
  --tasty-font-size-brand-display: var(--tasty-font-size-30); /* first-run shell-setup brand title — BRANDING exception to the 14px UI cap, the second and last one after the wordmark */
  --tasty-font-size-brand-wordmark: var(--tasty-font-size-17); /* "tasty." sidebar wordmark (mono) — sanctioned BRANDING exception to the 14px UI cap, the same way prose-h1 is content-exempt. The ONLY UI-chrome type over 14. */
  --tasty-font-size-term-sm: var(--tasty-font-size-12);
  --tasty-font-size-term:    var(--tasty-font-size-14); /* default terminal cell */
  --tasty-font-size-term-lg: var(--tasty-font-size-16);

  --tasty-font-weight-normal:   var(--tasty-font-weight-400);
  --tasty-font-weight-medium:   var(--tasty-font-weight-500);
  --tasty-font-weight-semibold: var(--tasty-font-weight-600);
  --tasty-font-weight-bold:     var(--tasty-font-weight-700);

  --tasty-line-height-tight: var(--tasty-line-height-100); /* terminal cells / ASCII art */
  --tasty-line-height-term:  var(--tasty-line-height-120); /* terminal reading */
  --tasty-line-height-ui:    var(--tasty-line-height-140); /* UI labels / wrapped text */

  --tasty-letter-spacing-ui:   var(--tasty-letter-spacing-0);
  --tasty-letter-spacing-caps: var(--tasty-letter-spacing-04); /* uppercase micro-labels */
  --tasty-letter-spacing-caps-wide: var(--tasty-letter-spacing-07); /* uppercase MONO 10 section headings (sidebar) — 0.04em quantises to 0 there (2026-10-06) */

  /* ── Motion ───────────────────────────────────────────────── */
  --tasty-motion-term:    var(--tasty-duration-0);   /* terminal content NEVER animates */
  --tasty-motion-ui:      var(--tasty-duration-120); /* UI feedback */
  --tasty-motion-ui-fast: var(--tasty-duration-90);
  --tasty-motion-ui-med:  var(--tasty-duration-150); /* tooltip show delay, unhurried UI reveals */
  --tasty-motion-ui-fade: var(--tasty-duration-200); /* opacity fade-in for discoverability overlays (modifier-hint panel) */
  --tasty-motion-hold-reveal: var(--tasty-duration-500); /* hold-a-key dwell before a discoverability overlay appears */
  --tasty-ease-ui:        var(--tasty-easing-standard);

  /* ── UI scale (sidebar zoom) — one multiplier, read in one place ── */
  --tasty-ui-scale-sm: var(--tasty-scale-80);  /* Small  — compact */
  --tasty-ui-scale-md: var(--tasty-scale-100); /* Medium — default */
  --tasty-ui-scale-lg: var(--tasty-scale-120); /* Large  — roomy */
  --tasty-ui-scale:    var(--tasty-ui-scale-md); /* active value — the only one consumed */

  /* ── Surface KINDS (terminal / markdown · focused / unfocused) ── */
  --tasty-surface-terminal-focused-bg:   var(--tasty-color-black); /* Latte → white */
  --tasty-surface-terminal-focused-fg:   var(--tasty-text-primary);
  --tasty-surface-terminal-unfocused-bg: var(--tasty-bg-panel);
  --tasty-surface-terminal-unfocused-fg: var(--tasty-text-muted);
  --tasty-surface-markdown-focused-bg:   var(--tasty-bg-app); /* crust — the DOCUMENT bed. NOT pure black: black is a terminal convention (raw TTY) and sits outside the Catppuccin palette; prose at 14px on #000 halates. crust also keeps the whole markdown content ladder discriminable above it: crust(page) < mantle(zebra) < base(row) < surface0(header/code) < surface1(grid). Latte flips with bg-app (crust #dce0e8) — no per-theme branch. In the webview render path there is no focus signal, so this is markdown's ONLY background. */
  --tasty-surface-markdown-focused-fg:   var(--tasty-text-primary);
  --tasty-surface-markdown-unfocused-bg: var(--tasty-bg-sidebar);
  --tasty-surface-markdown-unfocused-fg: var(--tasty-text-muted);

  /* ── Terminal specifics ───────────────────────────────────── */
  --tasty-selection-bg:           var(--tasty-color-neutral-500);
  --tasty-vi-cursor-bg:           var(--tasty-color-lavender);
  --tasty-search-match-bg:        color-mix(in srgb, var(--tasty-color-yellow) 30%, transparent);
  --tasty-search-match-active-bg: color-mix(in srgb, var(--tasty-color-yellow) 70%, transparent);
  --tasty-search-match-active-fg: var(--tasty-color-neutral-200); /* 2026-10-10: cell ink inside the ACTIVE match — dark on yellow (Mocha 6.95:1) */

  /* ── ANSI 16 (derived from the palette) ───────────────────── */
  --tasty-ansi-black:          var(--tasty-color-neutral-400);
  --tasty-ansi-red:            var(--tasty-color-red);
  --tasty-ansi-green:          var(--tasty-color-green);
  --tasty-ansi-yellow:         var(--tasty-color-yellow);
  --tasty-ansi-blue:           var(--tasty-color-blue);
  --tasty-ansi-magenta:        var(--tasty-color-mauve);
  --tasty-ansi-cyan:           var(--tasty-color-teal);
  --tasty-ansi-white:          var(--tasty-color-neutral-1000);
  --tasty-ansi-bright-black:   var(--tasty-color-neutral-600);
  --tasty-ansi-bright-red:     var(--tasty-color-red);
  --tasty-ansi-bright-green:   var(--tasty-color-green);
  --tasty-ansi-bright-yellow:  var(--tasty-color-yellow);
  --tasty-ansi-bright-blue:    var(--tasty-color-blue);
  --tasty-ansi-bright-magenta: var(--tasty-color-mauve);
  --tasty-ansi-bright-cyan:    var(--tasty-color-sky);
  --tasty-ansi-bright-white:   var(--tasty-color-neutral-1100);

  /* ── Brand roles ──────────────────────────────────────────── */
  --tasty-brand-melon-flesh: var(--tasty-color-melon-flesh);
  --tasty-brand-melon-rind:  var(--tasty-color-melon-rind);
  --tasty-brand-melon-seed:  var(--tasty-color-melon-seed);
}

/* ============================================================
   Latte role-remaps — the ONLY semantic values that differ by
   theme (because the role points at a different primitive, not
   just a different shade the ramp already handles).
   ============================================================ */
[data-theme="latte"] {
  /* active search match (2026-10-10): Latte yellow 70% under text-primary is 3.94:1 → 50% (4.68:1) with dark ink.
     Normal match stays 30% (5.50:1). */
  --tasty-search-match-active-bg: color-mix(in srgb, var(--tasty-color-yellow) 50%, transparent);
  --tasty-search-match-active-fg: var(--tasty-color-neutral-1100);
  --tasty-text-on-accent: var(--tasty-color-white);

  /* notification warning badge (2026-10-19): Latte yellow #df8e1d under white = 2.62:1 and under
     text (n1100) = 3.06:1 — no ink clears 4.5 on the full yellow. Fill = yellow 50% over white
     (≈ #efc78e) + n1100 ink = 5.01:1 (glyph same ink, 5.01 ≥ 3). Same move as search-match-active.
     Global text-on-accent and notify-warning (glyph/accent) are NOT changed. */
  --tasty-notify-warning-fill: color-mix(in srgb, var(--tasty-color-yellow) 50%, var(--tasty-color-white));
  --tasty-notify-warning-fg:   var(--tasty-color-neutral-1100);
  /* warning glyph (2026-10-19): yellow 50% toward text n1100 ≈ #967043 — 4.54:1 on white, ≈3.6:1 on a warning tint box (≥ 3 icon floor). */
  --tasty-accent-warning-glyph: color-mix(in srgb, var(--tasty-color-yellow) 50%, var(--tasty-color-neutral-1100));
  /* danger ink on raised (2026-10-21): red 60% toward text n1100 ≈ #a4224a — 4.79:1 on surface0, 6.54:1 on base. Same move as accent-warning-glyph. */
  --tasty-accent-danger-raised: color-mix(in srgb, var(--tasty-color-red) 60%, var(--tasty-color-neutral-1100));
  /* success ink on tint (2026-10-27): green 60% toward black ≈ #26601a — 5.55:1 on mantle+tint, 5.94:1 on base+tint (CSS 0.12 and egui 31/255 agree to 0.01).
     Toward n1100 (the accent-warning-glyph move) greys the green before 4.5 (30% = #486756, 4.59). */
  --tasty-accent-success-ink: color-mix(in srgb, var(--tasty-color-green) 60%, var(--tasty-color-black));

  /* disabled ink — Latte's light end of the ramp is compressed (neutral-600..800
     sit within 1.2:1 of each other), so the same ramp step reads fainter than in
     Mocha. One step up restores parity: Mocha n700 on panel 4.44 · Latte n800 3.49.
     Still strictly below text-muted (n900) → the enabled > disabled order holds.
     No contrast TARGET for disabled (WCAG 1.4.3 / 1.4.11 exempt it) — */
  --tasty-text-disabled: var(--tasty-color-neutral-800);

  /* secondary-on-raised — Latte n1000 on surface0 = 4.05:1 (< 4.5). n1100 = 5.17:1. The theme rule
     "on surface0 or darker, text-primary" applied as a role remap; rest = hover ink in Latte, hover
     still reads by the overlay-hover fill. */
  --tasty-text-secondary-raised: var(--tasty-color-neutral-1100);

  --tasty-overlay-hover:  var(--tasty-alpha-black-8);
  --tasty-overlay-active: var(--tasty-alpha-black-12);
  --tasty-separator:      var(--tasty-alpha-black-8);

  --tasty-surface-terminal-focused-bg: var(--tasty-color-white);
  /* markdown-focused-bg intentionally NOT overridden here — it tracks bg-app (Latte crust). */
}

/* ============================================================
   Tasty — TIER 3 · COMPONENT tokens
   ------------------------------------------------------------
   Per-component knobs. Every value references a SEMANTIC token
   (never a primitive, never a literal). This is the contract an
   implementer reads: "Button background = --tasty-button-primary-bg".
   Re-theme a component by overriding only its tokens; the whole
   chain (primitive → semantic → component) keeps it consistent.

   Naming: --tasty-<component>-<part>[-<state>]
   States: -hover / -active / -focus / -disabled / -checked / -on
   ============================================================ */

/* also on [data-theme] so a scoped theme wrapper (e.g. a gallery Latte specimen)
   re-resolves this tier against its own primitives instead of inheriting :root values */
:root, [data-theme] {
  /* ─────────────────  CORE  ───────────────── */

  /* Button */
  --tasty-button-height:           var(--tasty-control-height);      /* 28 */
  --tasty-button-height-sm:        var(--tasty-control-height-tab);  /* 24 */
  --tasty-button-height-lg:        var(--tasty-size-32);             /* 32 */
  --tasty-button-padding-x:        var(--tasty-space-md);
  --tasty-button-gap:              var(--tasty-space-sm);
  --tasty-button-radius:           var(--tasty-radius);
  --tasty-button-font-size:        var(--tasty-font-size-body);
  --tasty-button-font-weight:      var(--tasty-font-weight-medium);
  --tasty-button-fg:               var(--tasty-text-primary);
  --tasty-button-overlay-hover:    var(--tasty-overlay-hover);
  --tasty-button-overlay-active:   var(--tasty-overlay-active);
  --tasty-button-primary-bg:       var(--tasty-accent-primary);
  --tasty-button-primary-fg:       var(--tasty-text-on-accent);
  --tasty-button-agent-bg:         var(--tasty-accent-agent);
  --tasty-button-agent-fg:         var(--tasty-text-on-accent);
  --tasty-button-danger-bg:        var(--tasty-accent-danger);
  --tasty-button-danger-fg:        var(--tasty-text-on-accent);
  --tasty-button-secondary-bg:     var(--tasty-surface-raised);
  --tasty-button-secondary-border: var(--tasty-border-default);
  --tasty-button-secondary-border-hover: var(--tasty-border-strong);
  --tasty-button-ghost-fg:         var(--tasty-text-secondary);
  --tasty-button-ghost-fg-hover:   var(--tasty-text-primary);
  /* 2026-10-21 — ghost on a RAISED ground (Button ground="raised": add cards, menus' inline buttons). Latte text-secondary
     on surface0 = 4.05:1; the raised role = 5.17:1 (Mocha 7.10). Hover ink unchanged. */
  --tasty-button-ghost-raised-fg:       var(--tasty-text-secondary-raised);
  --tasty-button-ghost-raised-fg-hover: var(--tasty-text-primary);
  /* disabled — every variant draws the same neutral box (2026-09-29); ghost keeps no fill/edge */
  --tasty-button-disabled-bg:      var(--tasty-state-disabled-fill);
  --tasty-button-disabled-border:  var(--tasty-state-disabled-border);
  --tasty-button-disabled-fg:      var(--tasty-state-disabled-fg);   /* label + leading/trailing icons */
  /* Secondary on a banner shell (banner-bg = surface-raised): one ramp step up so the box reads */
  --tasty-banner-button-bg:        var(--tasty-surface-hover);
  --tasty-banner-button-border:    var(--tasty-border-frame);

  /* IconButton — square */
  --tasty-icon-button-size:        var(--tasty-control-height);     /* 28 */
  --tasty-icon-button-size-sm:     var(--tasty-size-24);
  --tasty-icon-button-radius:      var(--tasty-radius);
  --tasty-icon-button-fg:          var(--tasty-text-secondary);
  --tasty-icon-button-fg-hover:    var(--tasty-text-primary);
  --tasty-icon-button-overlay-hover:  var(--tasty-overlay-hover);
  --tasty-icon-button-bg-active:   var(--tasty-overlay-active);

  /* Badge — solid status pill */
  --tasty-badge-size:              var(--tasty-size-16);
  --tasty-badge-dot-size:          var(--tasty-status-dot-size);
  --tasty-badge-padding-x:         var(--tasty-space-xs);
  --tasty-badge-radius:            var(--tasty-radius-sm);
  --tasty-badge-font-size:         var(--tasty-font-size-micro);  /* 10 — count pills read smaller than caption */
  --tasty-badge-font-weight:       var(--tasty-font-weight-bold);
  --tasty-badge-bg:                var(--tasty-surface-raised);
  --tasty-badge-fg:                var(--tasty-text-secondary);
  /* Badge VARIANTS — every variant is a bg/fg PAIR at the component tier, so a
     badge never reaches into an accent role by hand. attention-* variants are
     the ones the attention scale drives (workspace row counts); the rest are
     the pre-existing product variants, now named instead of inlined. */
  --tasty-badge-danger-bg:         var(--tasty-accent-danger);
  --tasty-badge-danger-fg:         var(--tasty-text-on-accent);
  --tasty-badge-primary-bg:        var(--tasty-attention-completion);      /* completion count — blue */
  --tasty-badge-primary-fg:        var(--tasty-attention-completion-fg);
  --tasty-badge-warning-bg:        var(--tasty-attention-needs-input);     /* needs-input count — yellow */
  --tasty-badge-warning-fg:        var(--tasty-attention-needs-input-fg);
  --tasty-badge-agent-bg:          var(--tasty-accent-agent);
  --tasty-badge-agent-fg:          var(--tasty-text-on-accent);
  --tasty-badge-success-bg:        var(--tasty-accent-success);
  --tasty-badge-success-fg:        var(--tasty-text-on-accent);
  --tasty-badge-neutral-bg:        var(--tasty-surface-active);
  --tasty-badge-neutral-fg:        var(--tasty-text-primary);
  /* disabled host row (ListCtrl) — the ink rule: neutral fill, disabled ink, no accent fill (2026-09-29) */
  --tasty-badge-disabled-bg:       var(--tasty-state-disabled-fill);
  --tasty-badge-disabled-fg:       var(--tasty-state-disabled-fg);
  --tasty-badge-group-gap:         var(--tasty-space-xs);  /* 4 — gap when two kind badges share a row's trailing slot */

  /* Tag — outlined chip */
  --tasty-tag-gap:                 var(--tasty-space-xs);
  --tasty-tag-size:                var(--tasty-size-16);
  --tasty-tag-dot-size:            var(--tasty-size-8);
  --tasty-tag-padding-x:           var(--tasty-space-sm);  /* 8 — outlined chips need more room than a count pill */
  --tasty-tag-radius:              var(--tasty-radius-sm);
  --tasty-tag-font-size:           var(--tasty-font-size-micro);
  --tasty-tag-bg:                  var(--tasty-surface-raised);
  --tasty-tag-fg:                  var(--tasty-text-secondary);
  --tasty-tag-border:              var(--tasty-border-default);
  /* disabled (own prop or a disabled ListCtrl row) — neutral box + disabled ink; accent fill and tint edge drop out (2026-09-29) */
  --tasty-tag-disabled-bg:         var(--tasty-state-disabled-fill);
  --tasty-tag-disabled-border:     var(--tasty-state-disabled-border);
  --tasty-tag-disabled-fg:         var(--tasty-state-disabled-fg);

  /* Kbd — keycap */
  --tasty-kbd-size:                var(--tasty-size-16);
  --tasty-kbd-gap:                 var(--tasty-size-3);
  --tasty-kbd-shadow-depth:        var(--tasty-size-2);  /* 3D keycap bottom edge */
  --tasty-kbd-padding-x:           var(--tasty-space-xs);
  --tasty-kbd-radius:              var(--tasty-radius-sm);
  --tasty-kbd-font:                var(--tasty-font-mono);
  --tasty-kbd-font-size:           var(--tasty-font-size-micro);
  --tasty-kbd-bg:                  var(--tasty-surface-raised);
  --tasty-kbd-fg:                  var(--tasty-text-secondary);
  --tasty-kbd-border:              var(--tasty-border-strong);

  /* ─────────────────  FORMS  ───────────────── */

  /* Input */
  --tasty-input-height:            var(--tasty-control-height);
  --tasty-input-padding-x:         var(--tasty-space-md);
  --tasty-input-gap:               var(--tasty-space-sm);
  --tasty-input-radius:            var(--tasty-radius);
  --tasty-input-font-size:         var(--tasty-font-size-body);
  --tasty-input-bg:                var(--tasty-surface-raised);
  --tasty-input-fg:                var(--tasty-text-primary);
  --tasty-input-placeholder:       var(--tasty-text-placeholder);
  --tasty-input-border:            var(--tasty-border-default);
  --tasty-input-border-focus:      var(--tasty-border-focus);
  --tasty-input-border-invalid:    var(--tasty-accent-danger);
  --tasty-input-icon-fg:           var(--tasty-text-muted);
  /* read-only — not editable NOW, but the value is information (Appearance colour rows at Default).
     Same neutral box as disabled; the value keeps a readable ink. Selectable + copyable, focusable. (2026-09-29) */
  --tasty-input-readonly-bg:       var(--tasty-state-disabled-fill);
  --tasty-input-readonly-border:   var(--tasty-state-disabled-border);
  /* 2026-10-21 (html-address-idle-contrast): was text-secondary — Latte n1000 on the read-only box (surface0) = 4.05:1.
     → text-secondary-raised: Mocha unchanged (text-secondary 7.10) · Latte n1100 5.17. Read-only stays ≠ disabled. */
  --tasty-input-readonly-fg:       var(--tasty-text-secondary-raised);
  /* 2026-10-14 — Input on a RAISED ground (Input ground="raised"): every state steps the box down
     one ramp step + strong edge so it never merges with surface-raised. Ink is unchanged per state. */
  --tasty-input-raised-bg:              var(--tasty-bg-panel);
  --tasty-input-raised-border:          var(--tasty-border-strong);
  --tasty-input-readonly-raised-bg:     var(--tasty-state-disabled-fill-raised);
  --tasty-input-readonly-raised-border: var(--tasty-state-disabled-border-raised);
  /* 2026-10-14 — centred confirm/edit dialog ground (rename popup: workspace · tab · category ·
     explorer item; every overlays-dialogs frame). Input assumes a ground no higher than this. */
  --tasty-dialog-bg:                    var(--tasty-bg-panel);

  /* Select */
  --tasty-select-height:           var(--tasty-control-height);
  --tasty-select-padding-x:        var(--tasty-space-md);
  --tasty-select-radius:           var(--tasty-radius);
  --tasty-select-font-size:        var(--tasty-font-size-body);
  --tasty-select-bg:               var(--tasty-surface-raised);
  --tasty-select-fg:               var(--tasty-text-primary);
  --tasty-select-border:           var(--tasty-border-default);
  --tasty-select-border-focus:     var(--tasty-border-focus);
  --tasty-select-chevron-fg:       var(--tasty-text-muted);
  --tasty-select-chevron-room:     var(--tasty-size-28); /* right padding reserved for the chevron */
  /* 2026-10-21 — Select on a RAISED ground (Select ground="raised"), same step-down as Input raised. */
  --tasty-select-raised-bg:              var(--tasty-bg-panel);
  --tasty-select-raised-border:          var(--tasty-border-strong);
  --tasty-select-raised-disabled-bg:     var(--tasty-state-disabled-fill-raised);
  --tasty-select-raised-disabled-border: var(--tasty-state-disabled-border-raised);
  --tasty-select-chevron-offset:   var(--tasty-space-sm);
  --tasty-select-placeholder:      var(--tasty-input-placeholder); /* 2026-10-19 — Select(placeholder): trigger ink while no value is chosen → text-placeholder */

  /* MultiSelect — the Select sibling that holds many values. The closed
     trigger deliberately aliases the Select tokens (one family in a form);
     the open menu aliases the MenuItem/menu-container tokens, and the rows
     are Checkbox. Only three values are genuinely new: the empty-summary
     tone, the bulk-action row color, and the menu width cap. */
  --tasty-multiselect-height:           var(--tasty-select-height);          /* → control-height 28 */
  --tasty-multiselect-padding-x:        var(--tasty-select-padding-x);       /* → space-md 12 */
  --tasty-multiselect-radius:           var(--tasty-select-radius);          /* → radius 4 */
  --tasty-multiselect-font-size:        var(--tasty-select-font-size);       /* → body 14 */
  --tasty-multiselect-bg:               var(--tasty-select-bg);              /* → surface-raised */
  --tasty-multiselect-fg:               var(--tasty-select-fg);              /* → text-primary */
  --tasty-multiselect-border:           var(--tasty-select-border);          /* → border-default */
  --tasty-multiselect-border-hover:     var(--tasty-border-strong);          /* same hover as Select */
  --tasty-multiselect-border-focus:     var(--tasty-select-border-focus);    /* → border-focus (also the OPEN state) */
  --tasty-multiselect-chevron-fg:       var(--tasty-select-chevron-fg);      /* → text-muted */
  --tasty-multiselect-chevron-room:     var(--tasty-select-chevron-room);    /* → 28 reserved right padding */
  --tasty-multiselect-chevron-offset:   var(--tasty-select-chevron-offset);  /* → space-sm */
  --tasty-multiselect-summary-fg:       var(--tasty-text-primary);           /* N-selected / all summary */
  --tasty-multiselect-summary-fg-empty: var(--tasty-text-placeholder);       /* 0 selected reads as placeholder */
  --tasty-multiselect-menu-bg:          var(--tasty-menu-bg);                /* → surface-raised */
  --tasty-multiselect-menu-border:      var(--tasty-menu-border);            /* → border-strong (2026-10-07) */
  --tasty-multiselect-menu-radius:      var(--tasty-menu-radius);            /* → radius */
  --tasty-multiselect-menu-shadow:      var(--tasty-shadow-popover);         /* popover lift */
  --tasty-multiselect-menu-padding:     var(--tasty-space-xs);               /* 4 inner ring around the rows */
  --tasty-multiselect-menu-gap:         var(--tasty-space-xs);               /* 4 trigger → menu gap */
  --tasty-multiselect-menu-max-height:  var(--tasty-autocomplete-max-height);/* → 220 (~7 rows), then scrolls */
  --tasty-multiselect-menu-max-width:   var(--tasty-size-320);               /* menu may outgrow the trigger up to this */
  --tasty-multiselect-row-height:       var(--tasty-menu-item-height);       /* → control-height 28 */
  --tasty-multiselect-row-padding-x:    var(--tasty-menu-item-padding-x);    /* → space-md 12, matches the trigger */
  --tasty-multiselect-row-gap:          var(--tasty-space-sm);               /* 8 box → label */
  --tasty-multiselect-row-fg:           var(--tasty-text-primary);           /* NOT muted — Latte contrast on surface-raised */
  --tasty-multiselect-row-bg-hover:     var(--tasty-overlay-hover);          /* pointer hover wash */
  --tasty-multiselect-row-bg-active:    var(--tasty-surface-active);         /* keyboard ↑/↓ row only — never "checked" */
  --tasty-multiselect-all-fg:           var(--tasty-accent-primary);         /* bulk select/clear action row */
  --tasty-multiselect-separator:        var(--tasty-separator);              /* under the bulk row */

  /* SearchableSelect (2026-10-19) — Select trigger (select-* tokens, unchanged) over a menu popover
     whose top is a search field. Generalises the font combo; font-combo-* now alias these. */
  --tasty-select-search-trigger-border-hover: var(--tasty-border-strong);       /* same hover as Select */
  --tasty-select-search-value-placeholder:    var(--tasty-text-placeholder);    /* no value selected */
  --tasty-select-search-menu-gap:             var(--tasty-space-xs);            /* 4 trigger → popup */
  --tasty-select-search-menu-max-width:       var(--tasty-size-320);            /* popup may outgrow the trigger up to this */
  --tasty-select-search-list-max-height:      var(--tasty-size-300);            /* whole popup cap; only the option list scrolls */
  --tasty-select-search-inset:                var(--tasty-space-xs);            /* popup inner padding = search field inset (field = list − 2×4) */
  --tasty-select-search-field-height:         var(--tasty-input-height);        /* → control-height 28 */
  --tasty-select-search-field-padding-x:      var(--tasty-space-sm);            /* 8 */
  --tasty-select-search-field-gap:            var(--tasty-input-gap);           /* → space-sm 8 icon → text */
  --tasty-select-search-field-bg:             var(--tasty-input-raised-bg);     /* Input on a raised ground → bg-panel */
  --tasty-select-search-field-border:         var(--tasty-input-raised-border); /* → border-strong */
  --tasty-select-search-field-border-focus:   var(--tasty-input-border-focus);  /* edge only, no ring (trigger carries the ring) */
  --tasty-select-search-field-fg:             var(--tasty-input-fg);            /* → text-primary */
  --tasty-select-search-placeholder:          var(--tasty-input-placeholder);   /* → text-placeholder */
  --tasty-select-search-icon-fg:              var(--tasty-input-icon-fg);       /* → text-muted */
  --tasty-select-search-status-fg:            var(--tasty-text-secondary-raised); /* no-match / loading row ink on surface-raised */

  /* Checkbox */
  --tasty-checkbox-size:           var(--tasty-size-16);
  --tasty-checkbox-radius:         var(--tasty-radius-sm);
  --tasty-checkbox-bg:             var(--tasty-surface-raised);
  --tasty-checkbox-border:         var(--tasty-border-strong);
  --tasty-checkbox-bg-checked:     var(--tasty-accent-primary);
  --tasty-checkbox-check-fg:       var(--tasty-text-on-accent);
  --tasty-checkbox-border-focus:   var(--tasty-border-focus);

  /* Switch */
  --tasty-switch-track-width:      var(--tasty-size-28);
  --tasty-switch-track-height:     var(--tasty-size-16);
  --tasty-switch-thumb-size:       var(--tasty-size-12);
  --tasty-switch-thumb-inset:      var(--tasty-size-2);
  --tasty-switch-thumb-travel:     var(--tasty-size-12); /* track-width − thumb − 2×inset = 28−12−4 */
  --tasty-switch-radius:           var(--tasty-radius-pill);
  --tasty-switch-track-bg:         var(--tasty-surface-active);
  --tasty-switch-track-bg-on:      var(--tasty-accent-primary);
  --tasty-switch-thumb-bg:         var(--tasty-text-muted);     /* off — dim thumb */
  --tasty-switch-thumb-bg-on:      var(--tasty-text-on-accent); /* on  — thumb atop accent */

  /* ─────────────────  NAVIGATION  ───────────────── */

  /* Tab */
  --tasty-tab-height:              var(--tasty-control-height-tab);
  --tasty-tab-strip-width:         var(--tasty-tab-width);
  --tasty-tab-padding-x:           var(--tasty-space-sm);
  --tasty-tab-gap:                 var(--tasty-space-sm);
  --tasty-tab-bg:                  var(--tasty-bg-sidebar);
  --tasty-tab-bg-active:           var(--tasty-bg-panel);
  --tasty-tab-fg:                  var(--tasty-text-muted);
  --tasty-tab-fg-hover:            var(--tasty-text-secondary);
  --tasty-tab-fg-active:           var(--tasty-text-primary);
  /* Tab title — attention tint. The title color carries FOUR states in one
     channel; resolution order is needs-input → completion → active → rest.
     Attention clears on focus, so an active tab never shows an attention tint
     (the ordering is defensive, not a live collision). */
  --tasty-tab-fg-needs-input:      var(--tasty-attention-needs-input);
  --tasty-tab-fg-completion:       var(--tasty-attention-completion);
  --tasty-tab-indicator:           var(--tasty-accent-primary);
  --tasty-tab-separator:           var(--tasty-separator);
  --tasty-tab-close-size:          var(--tasty-size-16);
  --tasty-tab-close-radius:        var(--tasty-radius-sm);
  --tasty-tab-indicator-width:     var(--tasty-selection-edge-width); /* 2 — hairline, zoom-exempt */
  --tasty-tab-dot-size:            var(--tasty-status-dot-size-compact); /* 6 — dense chrome (24px strip), not the generic 8 */
  --tasty-tab-icon-size:           var(--tasty-icon-size-sm);
  --tasty-tab-status-gap:          var(--tasty-space-xs); /* 4 — between right-cluster items: html marker · move glyph · busy dot · close (2026-09-29) */
  /* tab-strip scroll arrows (chevron icons) — enabled is an ACTIVE control glyph (1.4.11 3:1),
     disabled takes the one disabled ink. Ground = surface-raised (the strip's arrow
     cell). Flat ratios: Mocha muted 5.65 · disabled 3.40; Latte muted 3.65 · disabled 2.56. */
  --tasty-tab-scroll-arrow-fg:          var(--tasty-text-muted);
  --tasty-tab-scroll-arrow-fg-disabled: var(--tasty-text-disabled);
  /* shape (2026-09-29) — every horizontal tab-bar scroll arrow (pane strip + horizontal_tab_bar_with_arrows):
     chevron ICON (never the < > characters), square cell = strip height, no own fill. */
  --tasty-tab-scroll-arrow-width:      var(--tasty-control-height-tab);  /* 24 */
  --tasty-tab-scroll-arrow-glyph-size: var(--tasty-icon-size-sm);        /* 14 — was xs 12; a 1px stroke at 12 lands on a pixel seam and rasters at ~0.5 alpha (2026-10-06) */
  --tasty-tab-scroll-arrow-hover-bg:   var(--tasty-overlay-hover);       /* enabled side only */
  --tasty-tab-scroll-arrow-move-fg:    var(--tasty-move-source-glyph);   /* the side that hides the move-source target */

  /* TreeRow */
  --tasty-tree-row-height:         var(--tasty-control-height-tree);
  --tasty-tree-row-indent:         var(--tasty-space-md);  /* 12 — per-level indent step (on 4px grid) */
  --tasty-tree-row-gap:            var(--tasty-space-xs);
  --tasty-tree-row-font-size:      var(--tasty-font-size-body);
  --tasty-tree-row-meta-font-size: var(--tasty-font-size-micro);
  --tasty-tree-row-fg:             var(--tasty-text-secondary);
  --tasty-tree-row-fg-active:      var(--tasty-text-primary);
  --tasty-tree-row-bg-hover:       var(--tasty-overlay-hover);
  --tasty-tree-row-bg-active:      var(--tasty-surface-active);

  /* MenuItem (+ menu container) */
  --tasty-menu-bg:                 var(--tasty-surface-raised);
  --tasty-menu-border:             var(--tasty-border-strong); /* was border-default (= surface-raised, invisible) — 2026-10-07: every menu-shaped popover draws a visible edge */
  --tasty-menu-radius:             var(--tasty-radius);
  --tasty-menu-separator:          var(--tasty-border-strong); /* 2026-11-03 — .tasty-menu-sep fill. A menu divider is a partition INSIDE a popup → border-strong (semantic.css border rule), not separator (white/black 8% = in-surface hairline) */
  --tasty-menu-item-height:        var(--tasty-control-height);
  --tasty-menu-item-padding-x:     var(--tasty-space-md);
  --tasty-menu-item-radius:        var(--tasty-radius-sm);
  --tasty-menu-item-fg:            var(--tasty-text-secondary-raised); /* 2026-10-08 (batch 8) — was text-secondary: Latte 4.05:1 on surface-raised. The raised-ground role is secondary in Mocha (7.1) and primary in Latte (5.17) */
  --tasty-menu-item-fg-hover:      var(--tasty-text-primary);
  --tasty-menu-item-bg-hover:      var(--tasty-overlay-hover);
  /* 2026-10-08 (batch 8) — SELECTED option row (Select / dropdown open list: the current value).
     Selection = ink + trailing check, NO fill. Fills stay reserved for pointer hover (overlay-hover)
     and keyboard-active (surface-active), so a selected row that is also hovered reads both. */
  --tasty-menu-item-selected-fg:   var(--tasty-menu-item-fg-hover);  /* → text-primary */
  --tasty-menu-item-check-fg:      var(--tasty-accent-primary);      /* trailing check glyph */
  --tasty-menu-item-check-size:    var(--tasty-icon-size-sm);        /* 14 */
  /* a label that cannot fit even its FIXED copy inside the menu's max width wraps; the row grows */
  --tasty-menu-item-wrap-padding-y: var(--tasty-space-xs);           /* 4 top/bottom on a wrapped row (min-height stays menu-item-height) */
  --tasty-menu-item-shortcut-font-size: var(--tasty-font-size-micro);

  /* AutoComplete — free-text trigger + floating candidate dropdown (typeahead).
     The trigger reuses the Input token chain (--tasty-input-*); the dropdown
     reuses the menu container + MenuItem language. Two row states: pointer
     hover = overlay-hover (light wash), keyboard ↑/↓ active = surface-active
     (a step stronger); keyboard-active wins when a row is both. Overflow past
     --tasty-autocomplete-max-height scrolls inside the dropdown via the shared
     .tasty-scroll bar (neutral-400 thumb) — no new scrollbar token. */
  --tasty-autocomplete-max-height:      var(--tasty-size-220);        /* default dropdown cap (~7 rows) — caller overrides via maxDropdownHeight */
  --tasty-autocomplete-menu-bg:         var(--tasty-menu-bg);         /* → surface-raised */
  --tasty-autocomplete-menu-border:     var(--tasty-menu-border);     /* → border-strong (2026-10-07) */
  --tasty-autocomplete-menu-radius:     var(--tasty-menu-radius);     /* → radius */
  --tasty-autocomplete-menu-shadow:     var(--tasty-shadow-popover);  /* popover lift */
  --tasty-autocomplete-row-height:      var(--tasty-menu-item-height);   /* → control-height 28 */
  --tasty-autocomplete-row-padding-x:   var(--tasty-menu-item-padding-x); /* → space-md */
  --tasty-autocomplete-row-fg:          var(--tasty-menu-item-fg);       /* → text-secondary */
  --tasty-autocomplete-row-fg-active:   var(--tasty-menu-item-fg-hover); /* → text-primary */
  --tasty-autocomplete-row-bg-hover:    var(--tasty-overlay-hover);      /* pointer hover wash */
  --tasty-autocomplete-row-bg-active:   var(--tasty-surface-active);     /* keyboard-active (stronger) */
  --tasty-autocomplete-match-fg:        var(--tasty-accent-primary);     /* typeahead match highlight */
  --tasty-autocomplete-empty-fg:        var(--tasty-text-muted);         /* no-match / empty row */

  /* DrillDown — generic master→detail CONTENT-SWAP layout (list view ⇄ detail
     view, no side-by-side split). The detail view carries a pinned BACK BAR:
     a ghost ← IconButton (chevronLeft glyph) + the detail title + an optional
     right-aligned actions slot (the canonical home for a per-detail action
     like "Apply", kept clear of any surrounding modal footer). Back bar is a
     36px band with a bottom hairline; the swap is INSTANT (no motion) unless
     the caller opts in — honouring the calm / 0ms-terminal system. */
  --tasty-drilldown-backbar-height:     var(--tasty-size-36);        /* back-bar band height */
  --tasty-drilldown-backbar-padding-x:  var(--tasty-space-sm);       /* 8 — aligns the ← with content edge */
  --tasty-drilldown-backbar-padding-y:  var(--tasty-space-xs);       /* 4 */
  --tasty-drilldown-backbar-gap:        var(--tasty-space-sm);       /* ← ↔ title ↔ actions */
  --tasty-drilldown-backbar-border:     var(--tasty-separator);      /* bottom divider under the bar */
  --tasty-drilldown-title-font-size:    var(--tasty-font-size-body); /* 13 — detail title */
  --tasty-drilldown-title-font-weight:  var(--tasty-font-weight-semibold);
  --tasty-drilldown-title-fg:           var(--tasty-text-primary);

  /* ─────────────────  FEEDBACK  ───────────────── */

  /* Toast */
  --tasty-toast-min-height:        var(--tasty-control-height);
  --tasty-toast-padding-x:         var(--tasty-space-md);
  --tasty-toast-padding-y:         var(--tasty-space-sm);
  --tasty-toast-gap:               var(--tasty-space-sm);
  --tasty-toast-radius:            var(--tasty-radius);
  --tasty-toast-accent-width:      var(--tasty-size-3);
  --tasty-toast-max-width:         var(--tasty-size-320);
  --tasty-toast-hint-font-size:    var(--tasty-font-size-micro);
  --tasty-toast-bg:                var(--tasty-surface-raised);
  --tasty-toast-fg:                var(--tasty-text-primary);
  --tasty-toast-border:            var(--tasty-border-strong);   /* surface1 — a Toast is a FLOATING overlay (like Tooltip / Banner, which both use border-strong); it needs the stronger edge to lift off content. Corrected from border-default: source already paints surface1, so this is the value-preserving, family-consistent choice. */
  --tasty-toast-accent-info:       var(--tasty-accent-info);
  --tasty-toast-accent-success:    var(--tasty-accent-success);
  --tasty-toast-accent-warning:    var(--tasty-accent-warning);
  --tasty-toast-accent-danger:     var(--tasty-accent-danger);
  --tasty-toast-accent-agent:      var(--tasty-accent-agent);

  /* Tooltip — hover bubble (HelpHint, and any hover hint). Opaque raised card,
     1px edge, popover lift; no arrow. Copy is muted, terse. */
  --tasty-tooltip-bg:              var(--tasty-surface-raised);
  --tasty-tooltip-border:          var(--tasty-border-strong);
  --tasty-tooltip-fg:              var(--tasty-text-secondary);
  --tasty-tooltip-radius:          var(--tasty-radius);
  --tasty-tooltip-shadow:          var(--tasty-shadow-popover);
  --tasty-tooltip-padding-x:       var(--tasty-space-sm);   /* 8 */
  --tasty-tooltip-padding-y:       var(--tasty-space-xs);   /* 4 */
  --tasty-tooltip-font-size:       var(--tasty-font-size-caption); /* 12 */
  --tasty-tooltip-line-height:     var(--tasty-line-height-ui);
  --tasty-tooltip-max-width:       var(--tasty-size-240);   /* wraps 2–3 sentences */
  --tasty-tooltip-offset:          var(--tasty-space-xs);   /* 4 gap from anchor */
  --tasty-tooltip-delay:           var(--tasty-motion-ui-med); /* short hover reveal delay */

  /* HelpHint — inline circular "?" glyph next to a label. Hover-only (no click),
     muted at rest, brightens on hover; anchors a Tooltip. */
  --tasty-help-hint-size:          var(--tasty-icon-size-sm);      /* 14 */
  --tasty-help-hint-color:         var(--tasty-text-muted);
  --tasty-help-hint-color-hover:   var(--tasty-text-secondary);
  --tasty-help-hint-gap:           var(--tasty-space-xs);          /* 4 from label */

  /* Banner — the 4th overlay family (after Modal / Popup / Toast).
     A floating, top-anchored notice that sits at the top of a scope's content
     area (View / Workspace / Pane / Tab / Surface) — NEVER over the tab bar.
     Focus-less, consumes its own mouse, and unlike a Toast it can hold actions.
     Full width minus side margins, variable height, optional TTL countdown.
     Severity is per-banner (no Info/Warn/Error kind) — each banner styles its
     own leading glyph; these tokens are the shared shell. */
  --tasty-banner-bg:                  var(--tasty-surface-raised);    /* surface0 fill */
  --tasty-banner-fg:                  var(--tasty-text-primary);
  --tasty-banner-border:              var(--tasty-border-strong);     /* 1px edge */
  --tasty-banner-radius:              var(--tasty-radius-8);          /* 8px — softer than the 4px system default, reads as a floating panel */
  --tasty-banner-shadow:              var(--tasty-shadow-popover);    /* floating lift (reuses the popover scrim — no new shadow system) */
  --tasty-banner-margin:              var(--tasty-space-sm);          /* 8px top / left / right — NO bottom margin (it floats) */
  --tasty-banner-padding-x:           var(--tasty-space-md);          /* 12 */
  --tasty-banner-padding-y:           var(--tasty-space-sm);          /* 8 */
  --tasty-banner-gap:                 var(--tasty-space-md);          /* leading glyph ↔ body ↔ actions */
  --tasty-banner-icon-fg:             var(--tasty-text-muted);        /* default leading glyph tone (a banner may override per severity) */
  --tasty-banner-title-font-size:     var(--tasty-font-size-body);    /* 13 — primary line */
  --tasty-banner-body-font-size:      var(--tasty-font-size-caption); /* 11 — supporting line */
  --tasty-banner-countdown-font:      var(--tasty-font-mono);         /* TTL seconds, top-right */
  --tasty-banner-countdown-font-size: var(--tasty-font-size-micro);   /* 10 */
  --tasty-banner-countdown-fg:        var(--tasty-text-muted);
  --tasty-banner-recessed-opacity:    var(--tasty-opacity-recessed);  /* lower-scope banner dimmed behind a higher one (≈60% transparent) */
  --tasty-banner-fade:                var(--tasty-motion-ui);         /* 120ms alpha appear / dismiss — position never moves (non-terminal, so fade is allowed) */
  /* INSET placement — for a scope whose content is a native layer tasty cannot
     paint over (the HTML WebView). The banner keeps its shell but the content
     area starts BELOW it: margin 8 on all four sides, the page is pushed down. */
  --tasty-banner-inset-gap:           var(--tasty-space-sm);          /* 8 — banner bottom → page top (inset only) */

  /* HTML surface — script-blocked notice (inset banner) + tab marker */
  --tasty-html-script-banner-glyph:   var(--tasty-accent-info);       /* lock glyph — informational, not a warning */
  --tasty-html-script-marker-fg:      var(--tasty-glyph-dim);         /* tab-strip lock after dismiss (blocked, notice hidden) */
  --tasty-html-script-marker-allowed-fg: var(--tasty-text-muted);     /* tab-strip script glyph while allowed this session */
  --tasty-html-script-marker-size:    var(--tasty-icon-size-xs);      /* 12 — sits inside the 24px tab strip */
  --tasty-html-script-marker-hit:     var(--tasty-size-16);           /* 16 square cell around the 12 glyph — lock click / hover target */
  --tasty-html-script-marker-hover-bg: var(--tasty-overlay-hover);    /* lock only; scriptFile has no hover fill */
  /* HTML surface — remote-content-blocked notice (inset banner) + address-field marker — 2026-11-01 */
  --tasty-html-remote-banner-glyph:   var(--tasty-accent-info);       /* globeOff — informational, same tone as the script notice */
  --tasty-html-remote-marker-fg:      var(--tasty-glyph-dim);         /* globeOff in the address field while blocked + notice hidden */
  --tasty-html-remote-marker-allowed-fg: var(--tasty-text-muted);     /* globe in the address field while allowed for this document */
  --tasty-html-remote-marker-size:    var(--tasty-icon-size-sm);      /* 14 — same as the field's leading kind glyph */
  --tasty-html-remote-marker-hit:     var(--tasty-size-20);           /* 20 square cell inside the 28 field — click / hover target */
  --tasty-html-remote-marker-hover-bg: var(--tasty-overlay-hover);    /* blocked only; allowed has no hover fill */
  /* HTML surface — address bar band (host egui, ABOVE the WebView; the page rect starts below it) — 2026-10-12 */
  --tasty-html-address-bar-height:    var(--tasty-size-40);           /* 40 — same as the markdown address bar */
  --tasty-html-address-bar-bg:        var(--tasty-bg-sidebar);
  --tasty-html-address-bar-border:    var(--tasty-separator);         /* 1px rule under the band */
  --tasty-html-address-bar-padding-x: var(--tasty-space-sm);          /* 8 */
  --tasty-html-address-bar-gap:       var(--tasty-space-sm);          /* 8 — nav cluster ↔ field */
  --tasty-html-address-bar-nav-gap:   var(--tasty-space-xs);          /* 4 — back · forward · reload */
  --tasty-html-address-field-min-width: var(--tasty-size-96);         /* field floor before the band drops Forward */
  --tasty-html-address-error-height:  var(--tasty-size-24);           /* inline error row under the band (part of the band) */
  --tasty-html-address-error-glyph:   var(--tasty-accent-danger);
  --tasty-html-address-value-fg:      var(--tasty-text-secondary-raised); /* 2026-10-21 idle real address on input-bg (surface0) — Mocha 7.10 · Latte 5.17 (was text-secondary 4.05) */
  /* Markdown surface — address bar band (2026-10-21, folder button + editable mirror). Same box as the HTML band. */
  --tasty-md-address-bar-height:      var(--tasty-html-address-bar-height);    /* 40 */
  --tasty-md-address-bar-bg:          var(--tasty-html-address-bar-bg);        /* bg-sidebar */
  --tasty-md-address-bar-border:      var(--tasty-html-address-bar-border);    /* 1px separator below */
  --tasty-md-address-bar-padding-x:   var(--tasty-html-address-bar-padding-x); /* 8 */
  --tasty-md-address-bar-gap:         var(--tasty-html-address-bar-gap);       /* 8 — cluster ↔ field ↔ Go */
  --tasty-md-address-bar-nav-gap:     var(--tasty-html-address-bar-nav-gap);   /* 4 — Folder · Reload */
  --tasty-md-address-field-min-width: var(--tasty-html-address-field-min-width); /* 96 field floor */
  --tasty-md-address-go-hide-below:   var(--tasty-field-width-md);             /* 160 — Go hides when the field would be narrower */
  --tasty-md-address-error-height:    var(--tasty-html-address-error-height);  /* 24 error row (part of the band) */
  --tasty-md-address-error-glyph:     var(--tasty-html-address-error-glyph);   /* accent-danger */
  --tasty-md-address-value-fg:        var(--tasty-html-address-value-fg);      /* 2026-10-21 idle real path — same box, same fix as HTML */
  --tasty-md-address-pending-fg:      var(--tasty-text-secondary);             /* 2026-10-21 “Still waiting for the remote…” row on bg-sidebar */
  --tasty-md-address-stale-bg:        var(--tasty-accent-primary);             /* stale Reload fill (mirror doc changed) */
  --tasty-md-address-stale-fg:        var(--tasty-text-on-accent);             /* stale Reload glyph */
  /* HTML mirror — remote size banners + receiving state (2026-10-12) */
  --tasty-html-size-banner-glyph:     var(--tasty-accent-info);       /* large-file ask — a question, not a warning */
  --tasty-html-limit-banner-glyph:    var(--tasty-accent-warning);    /* cumulative limit reached — auto receive paused */
  --tasty-html-receiving-fg:          var(--tasty-text-muted);        /* receiving / paused / notice copy in the page rect */
  --tasty-html-receiving-glyph:       var(--tasty-glyph-dim);         /* static glyph while paused */
  --tasty-banner-glyph-offset:        var(--tasty-size-1);            /* 1 — leading glyph nudge onto the title's cap line */
  --tasty-banner-more-reason-glyph-offset: var(--tasty-size-1);       /* 1 — 2026-11-03: info glyph (14) top offset on the ⋯ menu reason line (caption 11 × line-height-ui). Separate role from banner-glyph-offset */
  --tasty-banner-text-gap:            var(--tasty-label-detail-gap);  /* 2 — title ↔ body (every banner) */
  --tasty-banner-narrow-below:        var(--tasty-size-440);          /* SCOPE content width (before banner margins) below which the action wraps under the body — surface / tab / pane = that content, workspace = union of panes under the tab bar; × stays top-right (2026-10-09 b10) */

  /* Banner "more" (⋯) menu — a SECOND affordance in the banner's top-right
     column, left of the × , and the anchored menu it opens. The menu reuses the
     Tools-menu popup language verbatim (surface-raised fill, border-strong edge,
     shadow-popover lift, 28px MenuItem rows) — nothing new there. What IS new:
     the trigger shares the × reveal rule (banner hover, plus stays lit while the
     menu is open), and the menu's width is ELASTIC because every row interpolates
     a program name of arbitrary length. */
  --tasty-banner-more-column-gap:     var(--tasty-space-xs);        /* 4 between ⋯ and × in the affordance column */
  --tasty-banner-more-reserve:        var(--tasty-size-56);         /* body's right padding — clears both buttons (2 × 24 + 4 + 4) */
  --tasty-banner-more-menu-bg:        var(--tasty-surface-raised);  /* same fill as the Tools menu */
  --tasty-banner-more-menu-border:    var(--tasty-border-strong);
  --tasty-banner-more-menu-radius:    var(--tasty-menu-radius);
  --tasty-banner-more-menu-shadow:    var(--tasty-shadow-popover);
  --tasty-banner-more-menu-padding:   var(--tasty-space-xs);        /* 4 inner ring around the rows */
  --tasty-banner-more-menu-offset:    var(--tasty-space-xs);        /* 4 gap below the trigger (flips above when it would overflow) */
  --tasty-banner-more-menu-min-width: var(--tasty-size-200);        /* short app names still read as a menu, not a chip */
  --tasty-banner-more-menu-max-width: var(--tasty-size-288);        /* long app names truncate instead of growing past the card */
  --tasty-banner-more-app-font:       var(--tasty-font-mono);       /* the interpolated program name is a process name → mono */
  --tasty-banner-more-app-fg:         var(--tasty-menu-item-fg);    /* 2026-10-08 (batch 8) — was text-primary. The whole row (fixed copy + app name) takes the menu row ink; the name is set apart by mono only */
  --tasty-banner-more-app-fg-hover:   var(--tasty-menu-item-fg-hover);

  /* StatusDot — EXECUTION state only (running / idle / agent / waiting / error).
     No mirror/remote color here: the sky "remote-linked" slot was removed so the
     dot carries live activity alone. Remote-mirror identity lives on the workspace
     row (see Workspace row · mirror below), never on this dot. */
  --tasty-status-dot-size:         var(--tasty-size-8);
  /* COMPACT dot — the same status role inside 24px-tall chrome (tab strip,
     workspace status bar, collapsed sidebar rail). 8 crowds a 24px row once a
     ring or a label sits beside it; 6 is the dense-chrome member of the dot
     family. NOT a second meaning — same colors, smaller footprint. */
  --tasty-status-dot-size-compact: var(--tasty-size-6);
  --tasty-status-dot-idle:         var(--tasty-status-idle);
  --tasty-status-dot-success:      var(--tasty-accent-success);
  --tasty-status-dot-warning:      var(--tasty-accent-warning);
  --tasty-status-dot-danger:       var(--tasty-accent-danger);
  --tasty-status-dot-agent:        var(--tasty-accent-agent);
  /* Attention kinds on the dot channel (collapsed rail). ONE dot, one color:
     needs-input > completion > running/busy. Two dots were rejected — at the
     compact 6px rail dot a second dot reads as noise and still can't carry a count. */
  --tasty-status-dot-needs-input:  var(--tasty-attention-needs-input);
  --tasty-status-dot-completion:   var(--tasty-attention-completion);
  --tasty-status-dot-attached-ring: var(--tasty-accent-attached);
  --tasty-status-dot-attached-ring-width:  var(--tasty-size-2);
  --tasty-status-dot-attached-ring-offset: var(--tasty-size-2);
  --tasty-status-dot-pulse-duration: var(--tasty-duration-1600);
  /* Collapsed-rail cut-out ring (2026-10-07 batch 4): drawn on EVERY rail dot,
     busy included, so the dot separates from the avatar it overlaps. OFF-SCALE
     hairline (outside the UI zoom), like the other edge widths. */
  --tasty-status-dot-ring:         var(--tasty-bg-sidebar);
  --tasty-status-dot-ring-width:   var(--tasty-size-1-5);

  /* Workspace status bar (24px host chrome) */
  --tasty-statusbar-dot-size:      var(--tasty-status-dot-size-compact); /* 6 — same dense-chrome member as the tab dot */
  --tasty-statusbar-glyph:         var(--tasty-glyph-dim);   /* leading item glyphs (branch, shell, grid) */
  --tasty-statusbar-theme-glyph:   var(--tasty-glyph-dim);   /* theme indicator is a GLYPH, not a colored dot — light/dark is not a status */
  /* ONE size for every inline glyph in the bar (branch, shell, grid, theme). The
     theme cell is the same box in both themes — a dimension must not depend on
     which theme is active; sun/theme optical parity is corrected in the ASSET
     (icons/sun.svg ray extent), never by giving one glyph a different size. */
  --tasty-statusbar-glyph-size:    var(--tasty-icon-size-xs); /* 12 — smallest inline glyph step; scales with the UI scale like the rest */

  /* Git viewer diff toolbar — a CONTAINER height, deliberately not button-height
     (28): the toolbar is a 32px bar that holds 28px controls with 2px of air. */
  --tasty-git-toolbar-height:      var(--tasty-toolbar-height); /* 32 — re-pointed at the shared container-toolbar role (value unchanged) */

  /* Workspace row — mirror (remote-linked) indicator. A small remote-link glyph
     leading the workspace name marks a row as a local MIRROR of a remote tasty
     instance. It is a separate axis from the status dot (execution), the attached
     ring (claimed by another client), and the notification badge — so it never
     collides with any of them. Collapsed rail reuses the same fg on a corner chip. */
  --tasty-workspace-mirror-fg:     var(--tasty-accent-remote);
  --tasty-workspace-mirror-icon-size: var(--tasty-icon-size-xs);
  --tasty-workspace-mirror-gap:    var(--tasty-space-xs);
  /* 2026-10-12 — collapsed-rail mirror chip: a full-strength 1px sky edge so the chip
     reads on every avatar state (idle / active overlay fill / hover). Bed stays the rail
     colour so the 8px glyph keeps its contrast; the 2px bg-sidebar halo still separates it. */
  --tasty-workspace-mirror-chip-border: var(--tasty-workspace-mirror-fg);
  --tasty-workspace-mirror-chip-bg:     var(--tasty-bg-sidebar);
  /* 2026-10-12 — collapsed-rail category `---` line (and the flat-mode top divider):
     avatar-ink colour, 2px. A deliberate exception to the 1px border rule — it is a mark
     at avatar-glyph weight, not a box edge. */
  --tasty-rail-category-line-width:    var(--tasty-focus-ring-width);  /* 2 */
  --tasty-rail-category-line:          var(--tasty-text-muted);        /* = idle avatar glyph */
  --tasty-rail-category-line-hover:    var(--tasty-text-secondary);
  /* 2026-10-14 — rail mark lengths: own roles, no longer borrowed from avatar height / move chip */
  --tasty-rail-category-line-length:   var(--tasty-size-24);   /* category `---` line */
  --tasty-rail-flat-divider-length:    var(--tasty-size-28);   /* top divider when categories are off */
  --tasty-workspace-mirror-chip-size:  var(--tasty-size-12);   /* rail avatar mirror chip (bottom-right) */
  /* Workspace row (expanded sidebar) — the status dot sits in a RESERVED slot the
     size of the attached mark's full bbox: 8 + 2×(2+2) = 16. Reserved on every
     row (attached or not) so title x never shifts. Ring clearances at scale 1:
     card edge 8 · active accent bar (0..2) 6 · label 4. Body x = 8 + 16 + 4 = 28. */
  --tasty-workspace-row-padding-x: var(--tasty-space-sm);   /* 8 — row left inset */
  --tasty-workspace-dot-slot:      var(--tasty-size-16);    /* 16 at scale 1 — DERIVED in Theme: round(dot) + 2 × (round(ring-width) + round(ring-offset)); never rounded on its own (0.85 → 15, 1.2 → 18). Square; height does not grow the row. */
  --tasty-workspace-row-active-bar-width: var(--tasty-selection-edge-width); /* 2 — hairline, zoom-exempt */
  --tasty-workspace-dot-gap:       var(--tasty-space-xs);   /* 4 — slot → title / subtitle / pill */

  /* Move source — the pending "Move Surface / Tab / Pane" target (app-wide, max one).
     IS the target  → dashed ring drawn INSIDE the target rect (surface rect · tab cell
                      rect · whole pane rect = tab strip + content), painted LAST, above
                      notification / occupancy borders, occupancy overlay and the active-
                      tab indicator. Dashes cover; gaps show whatever is underneath.
     CONTAINS it    → the nearest VISIBLE container (inactive tab cell, or workspace row
                      / rail avatar when it lives in another workspace) shows the move
                      glyph. Static — no motion, so no reduced-motion variant. */
  --tasty-move-source-ring:        var(--tasty-accent-move);
  --tasty-move-source-ring-width:  var(--tasty-focus-ring-width);  /* 2 — the "pointing ring" weight */
  --tasty-move-source-dash:        var(--tasty-size-4);            /* 4 on */
  --tasty-move-source-dash-gap:    var(--tasty-size-4);            /* 4 off */
  --tasty-move-source-glyph:       var(--tasty-accent-move);
  --tasty-move-source-glyph-size:  var(--tasty-icon-size-xs);      /* 12 — tab cell · workspace row */
  --tasty-move-source-chip-size:   var(--tasty-size-12);           /* rail avatar corner chip (bottom-left) */
  --tasty-move-source-chip-glyph-size: var(--tasty-size-8);

  /* Auto-attach refused — rail avatar corner chip (top-left) · 2026-10-07 batch 5 */
  --tasty-attach-refusal-glyph:           var(--tasty-accent-warning);
  --tasty-attach-refusal-chip-size:       var(--tasty-move-source-chip-size);       /* 12 */
  --tasty-attach-refusal-chip-glyph-size: var(--tasty-move-source-chip-glyph-size); /* 8 */

  /* Spinner — indeterminate scan/install progress */
  --tasty-spinner-size:            var(--tasty-size-16);
  --tasty-spinner-track:           var(--tasty-surface-active);
  --tasty-spinner-indicator:       var(--tasty-accent-primary);
  --tasty-spinner-duration:        var(--tasty-duration-900); /* 900ms full rotation — matches source SPIN_PERIOD=0.9s. Corrected from duration-90 (a dropped digit; 90ms strobes at ~11 rev/s). */

  /* Loading overlay — a Spinner laid over EXISTING content (surface · tab · LNB card · rail avatar),
     never a new slot. Non-blocking, no dim mask; a bed only as big as the slot it covers. 2026-10-16 */
  --tasty-loading-overlay-indicator:        var(--tasty-spinner-indicator);   /* arc ink, every placement (track = arc ink @ 22%, Spinner's own) */
  --tasty-loading-overlay-delay:            var(--tasty-motion-ui-fade);      /* 200 — show only if still loading after this; hide is immediate */
  --tasty-loading-overlay-surface-size:     var(--tasty-spinner-size);        /* 16 */
  --tasty-loading-overlay-surface-bed-size: var(--tasty-size-24);             /* square bed, bottom-right of the surface rect */
  --tasty-loading-overlay-surface-bed-bg:   var(--tasty-surface-raised);
  --tasty-loading-overlay-surface-bed-border: var(--tasty-border-default);
  --tasty-loading-overlay-surface-inset:    var(--tasty-space-sm);            /* 8 from the surface rect's right + bottom edges */
  --tasty-loading-overlay-tab-size:         var(--tasty-tab-icon-size);       /* 14 — sits exactly on the tab's kind-icon slot */
  --tasty-loading-overlay-lnb-size:         var(--tasty-icon-size-sm);        /* 14 — centred in the 16 workspace-dot-slot */
  --tasty-loading-overlay-rail-size:        var(--tasty-size-28);             /* ring around the initial, inside the 36 avatar */

  /* Notifications (2026-10-17, notification-center) — level count badge, LNB / rail summary, popup list item.
     Badge = glyph + count pill, fixed order error › warning › info, 0 = not drawn, > max = "99+". */
  --tasty-notification-badge-error-bg:      var(--tasty-notify-error);
  --tasty-notification-badge-error-fg:      var(--tasty-notify-error-fg);
  --tasty-notification-badge-warning-bg:    var(--tasty-notify-warning-fill); /* 2026-10-19: was notify-warning (Latte 2.62:1) */
  --tasty-notification-badge-warning-fg:    var(--tasty-notify-warning-fg);
  --tasty-notification-badge-info-bg:       var(--tasty-notify-info);
  --tasty-notification-badge-info-fg:       var(--tasty-notify-info-fg);
  --tasty-notification-badge-glyph-size:    var(--tasty-size-10);   /* level glyph inside the 16 pill */
  --tasty-notification-badge-glyph-gap:     var(--tasty-size-2);
  --tasty-notification-badge-group-gap:     var(--tasty-space-xs);  /* 4 — badges side by side (LNB, popup toolbar) */
  --tasty-notification-badge-stack-gap:     var(--tasty-size-2);    /* 2 — badges stacked (rail) */
  --tasty-notification-badge-max:           99; /* @kind other */                     /* above → "99+" */
  --tasty-notification-summary-pad:         var(--tasty-space-sm);  /* 8 — block padding, same as the Tools block */
  --tasty-notification-summary-row-height:  var(--tasty-size-24);   /* line 1 = ghost sm button height */
  --tasty-notification-summary-pad-x:       var(--tasty-space-sm);
  --tasty-notification-summary-icon-gap:    var(--tasty-space-sm);  /* bell ↔ label, same as Button leading icon */
  --tasty-notification-summary-label-fg:    var(--tasty-text-secondary);
  --tasty-notification-summary-label-fg-empty: var(--tasty-text-muted);
  --tasty-notification-summary-preview-font-size: var(--tasty-font-size-caption);
  --tasty-notification-summary-preview-fg:  var(--tasty-text-secondary);
  --tasty-notification-summary-meta-fg:     var(--tasty-text-muted);
  --tasty-notification-summary-bg-hover:    var(--tasty-overlay-hover);
  --tasty-notification-summary-bg-open:     var(--tasty-overlay-active);  /* popup is open */
  --tasty-notification-rail-width:          var(--tasty-size-44);   /* rail block — fits "99+" with glyph */
  --tasty-notification-item-pad-x:          var(--tasty-space-md);  /* 12 */
  --tasty-notification-item-pad-y:          var(--tasty-space-sm);  /* 8 */
  --tasty-notification-item-line-gap:       var(--tasty-size-2);
  --tasty-notification-item-glyph-size:     var(--tasty-icon-size-sm);  /* 14 */
  --tasty-notification-item-glyph-gap:      var(--tasty-space-sm);
  --tasty-notification-item-error-glyph:    var(--tasty-notify-error);
  --tasty-notification-item-warning-glyph:  var(--tasty-accent-warning-glyph); /* 2026-10-27: was notify-warning (Latte 2.31 rest / 1.94 hover) → Latte 3.99 / 3.35; Mocha identical */
  --tasty-notification-item-info-glyph:     var(--tasty-notify-info);
  --tasty-notification-item-title-fg:       var(--tasty-text-primary);    /* unread */
  --tasty-notification-item-title-fg-read:  var(--tasty-text-secondary);
  --tasty-notification-item-title-weight:   var(--tasty-font-weight-semibold); /* unread */
  --tasty-notification-item-body-fg:        var(--tasty-text-secondary);
  --tasty-notification-item-body-fg-read:   var(--tasty-text-muted);
  --tasty-notification-item-meta-fg:        var(--tasty-text-muted);
  --tasty-notification-item-closed-fg:      var(--tasty-text-placeholder); /* "closed" source tag */
  --tasty-notification-item-more-fg:        var(--tasty-accent-primary);   /* "More" / "Less" text toggle */
  --tasty-notification-item-unread-dot:     var(--tasty-notify-unread);
  --tasty-notification-item-unread-dot-size: var(--tasty-badge-dot-size);
  --tasty-notification-item-body-lines:     2; /* @kind other */   /* popup — clamp, "More" expands */
  --tasty-notification-item-body-lines-wide: 3; /* @kind other */  /* fullscreen stage */
  --tasty-notification-item-bg-hover:       var(--tasty-overlay-hover);
  --tasty-notification-item-bg-selected:    var(--tasty-surface-active);
  --tasty-notification-item-separator:      var(--tasty-separator);
  --tasty-notifications-popup-height:       var(--tasty-size-400);  /* 352 × 400 default */
  --tasty-notifications-toolbar-height:     var(--tasty-control-height);  /* 28 — counts + Mark all read */

  /* Surface border — OCCUPANCY & COMPLETION. Three surface states ride ONE
     visual channel (the terminal / markdown surface's border) and are separated
     by COLOR alone, so the three must stay mutually discriminable at a 1–2px edge:
       occupied-soft  green  — a subject holds the surface (cooperative, writable)
       occupied-hard  peach  — same notice + readonly (absorbs the old attach border)
       highlight-done blue   — surface-highlight: finished-task attention, clears on focus
     Occupancy is the 1px edge; the completion highlight is 2px. Completion stays
     BLUE (accent-primary, matching the live source) rather than the ADR's "sky":
     blue is the most discriminable of the three against green + peach — sky sits
     too close to occupied-soft green at a thin border. Source priority (occupancy
     suppresses the completion border while a surface is held) is an implementation
     rule, not a token. */
  --tasty-surface-occupied-soft-border:  var(--tasty-accent-occupied-soft);
  --tasty-surface-occupied-hard-border:  var(--tasty-accent-occupied-hard);
  --tasty-surface-occupied-border-width: var(--tasty-border-width);      /* 1px */
  --tasty-surface-highlight-done-border: var(--tasty-attention-completion); /* blue — completion notice */
  /* NeedsInput edge — same 2px weight as completion (both are attention, one
     channel), different kind color. PRIORITY on the border channel:
       needs-input (2px yellow) > occupancy (1px green/peach) > completion (2px blue)
     NeedsInput outranks occupancy because occupancy means "held, working, as
     expected" — exactly the reading that would hide a session that has STOPPED
     to ask the human something. Completion stays below occupancy (completion is drawn under the occupancy edge). */
  --tasty-surface-highlight-input-border: var(--tasty-attention-needs-input);
  --tasty-surface-highlight-input-width:  var(--tasty-focus-ring-width);  /* 2px */
  --tasty-surface-highlight-done-width:  var(--tasty-focus-ring-width);  /* 2px */

  /* ─────────────────  DATA  ───────────────── */

  /* Table */
  --tasty-table-cell-height:       var(--tasty-control-height);
  --tasty-table-cell-padding-x:    var(--tasty-space-md);
  --tasty-table-cell-padding-y:    var(--tasty-space-sm);
  --tasty-table-font-size:         var(--tasty-font-size-body);
  --tasty-table-header-font-size:  var(--tasty-font-size-caption);
  --tasty-table-header-font-weight: var(--tasty-font-weight-medium);
  --tasty-table-header-tracking:   var(--tasty-letter-spacing-caps);
  --tasty-table-cell-font:         var(--tasty-font-mono);
  --tasty-table-header-bg:         var(--tasty-bg-sidebar);
  --tasty-table-header-fg:         var(--tasty-text-muted);
  --tasty-table-row-fg:            var(--tasty-text-secondary);
  --tasty-table-row-bg-hover:      var(--tasty-overlay-hover);
  --tasty-table-row-bg-selected:   var(--tasty-surface-active);
  --tasty-table-border:            var(--tasty-separator);

  /* ListCtrl — full-width, row-selectable NAVIGATION list (a "pick one to
     drill into" list, NOT a data grid — Table is the grid). Each row: primary
     label + optional description + optional leading icon / trailing slot, and
     a drill-in chevron. Rows share the list-idiom state palette used by TreeRow
     / MenuItem / Table: hover = overlay-hover, selected = surface-active + a 2px
     accent left bar. Divided by the standard separator hairline. Pair with the
     DrillDown layout (list → detail). */
  --tasty-listctrl-row-min-height:   var(--tasty-size-36);         /* comfortable nav row (fits label + description) */
  --tasty-listctrl-row-padding-x:    var(--tasty-space-md);        /* 12 */
  --tasty-listctrl-row-padding-y:    var(--tasty-space-sm);        /* 8 */
  --tasty-listctrl-row-gap:          var(--tasty-space-sm);        /* icon ↔ text ↔ trailing */
  --tasty-listctrl-radius:           var(--tasty-radius-sm);
  --tasty-listctrl-font-size:        var(--tasty-font-size-body);  /* 13 — label */
  --tasty-listctrl-label-fg:         var(--tasty-text-secondary);
  --tasty-listctrl-label-fg-active:  var(--tasty-text-primary);    /* hover / selected label */
  --tasty-listctrl-desc-fg:          var(--tasty-text-muted);
  --tasty-listctrl-desc-font-size:   var(--tasty-font-size-caption); /* 11 — secondary line */
  --tasty-listctrl-icon-fg:          var(--tasty-text-muted);
  --tasty-listctrl-chevron-fg:       var(--tasty-text-muted);      /* trailing drill-in affordance */
  --tasty-listctrl-row-bg-hover:     var(--tasty-overlay-hover);
  --tasty-listctrl-row-bg-selected:  var(--tasty-surface-active);
  --tasty-listctrl-selected-bar:     var(--tasty-accent-primary);  /* 2px accent left bar on the selected row */
  --tasty-listctrl-selected-bar-width: var(--tasty-selection-edge-width); /* 2 — hairline, zoom-exempt. Same role as tab indicator / workspace row (2026-09-29) */
  --tasty-listctrl-divider:          var(--tasty-separator);

  /* Markdown-surface inline GFM table — a SEPARATE renderer from the shared
     Table widget above (read-only: no hover / selection / sort / sticky header).
     Grid + zebra tuned to stay legible on BOTH the focused surface (~#000) and
     the bg-panel gallery card. Value ladder, deepest → lightest:
       mantle (zebra) < base (row) < surface0 (header) < surface1 (grid line).
     Grid line is the lightest element so it always reads above every fill. */
  --tasty-md-table-border:          var(--tasty-border-strong);  /* surface1 — opaque, "확실하게" (separator's 8% alpha is why it was invisible) */
  --tasty-md-table-header-bg:       var(--tasty-surface-raised);  /* surface0 — lightest fill band = header chrome */
  --tasty-md-table-header-fg:       var(--tasty-text-primary);    /* brighter than body → signals header (egui has no synthetic bold weight) */
  --tasty-md-table-row-bg:          var(--tasty-bg-panel);        /* base — odd rows + the table's own opaque fill */
  --tasty-md-table-row-bg-zebra:    var(--tasty-bg-sidebar);      /* mantle — even rows, one subtle step darker */
  --tasty-md-table-cell-fg:         var(--tasty-text-secondary);  /* body prose tone */
  --tasty-md-table-cell-padding-x:  var(--tasty-space-sm);        /* 8px */
  --tasty-md-table-cell-padding-y:  var(--tasty-space-xs);        /* 4px — old ~6px snapped onto the 4px grid */

  /* Markdown DOCUMENT (webview render path — render.rs theme_css()). The doc bed is
     one fixed background: the webview has no focused/unfocused signal, so md-doc-bg
     is markdown's only background. Value ladder, page → lightest element:
       crust (doc bg) < mantle (table zebra) < base (—) < surface0 (code fill) < surface1 (grid). */
  --tasty-md-doc-bg:                var(--tasty-surface-markdown-focused-bg); /* → --md-bg */
  --tasty-md-doc-fg:                var(--tasty-text-secondary);
  --tasty-md-code-bg:               var(--tasty-surface-raised);   /* → --md-code-bg (block + inline code) */
  --tasty-md-code-border:           var(--tasty-separator);
  --tasty-md-quote-bar:             var(--tasty-border-strong);
  --tasty-md-quote-fg:              var(--tasty-text-muted);
  --tasty-md-quote-bar-width:       var(--tasty-selection-edge-width); /* 2 — blockquote left bar; hairline, zoom-exempt. Plugin's 3px (border-w × 3) is retired (2026-10-06) */
  --tasty-md-rule:                  var(--tasty-separator);

  /* Explorer surface — sidebar 2-region split. The sidebar is NOT one scroll
     column: "Files" owns the top region (flex, own scroll) and "Favorites" is
     PINNED to the bottom at a fixed height, so a long tree can never push the
     favorites list out of view. The boundary is a fixed 1px border, not an
     inline separator that flows with content.
       height = --tasty-explorer-favorites-pin-height (240) when the sidebar body
       is >= --tasty-explorer-favorites-pin-threshold (600); below that it becomes
       --tasty-explorer-favorites-pin-ratio (40%) of the body height, floored at
       --tasty-explorer-favorites-pin-min-height (120). Snap the ratio result to
       the 4px grid. Not user-resizable — pure computed value. */
  --tasty-explorer-sidebar-width:              var(--tasty-size-196);
  --tasty-explorer-favorites-pin-height:       var(--tasty-size-240); /* default pinned region height */
  --tasty-explorer-favorites-pin-threshold:    var(--tasty-size-600); /* below this body height → use the ratio */
  --tasty-explorer-favorites-pin-ratio:        0.4; /* @kind other */ /* small-surface shrink factor */
  --tasty-explorer-favorites-pin-min-height:   var(--tasty-size-120); /* caption + ~3 rows still fit */
  --tasty-explorer-split-border:               var(--tasty-separator); /* fixed boundary line */

  /* Listening-ports popup — favorites section + star toggle.
     The popup frame stays 660×520; the favorites region is BOUNDED so it can
     never push the table out: caption row (22) + a list capped at
     --tasty-port-favorites-max-height (112 = 5 × 22px rows), i.e. the list
     starts scrolling at the 6th favorite. Star column width is shared by the
     table's tight leading column and the favorites rows so the stars align. */
  --tasty-port-favorites-bg:         var(--tasty-bg-sidebar);          /* section tone, apart from the table body */
  --tasty-port-favorites-border:     var(--tasty-separator);           /* boundary to the table */
  --tasty-port-favorites-row-height: var(--tasty-control-height-tree); /* 22 — denser than a table row */
  --tasty-port-favorites-max-height: var(--tasty-size-112);            /* 5 rows, then scroll */
  --tasty-port-star-col-width:       var(--tasty-size-28);             /* leading tight column in the table */
  --tasty-port-process-col-min-width: var(--tasty-size-200);           /* Process column — a MINIMUM, not a fixed width: the column grows with the table and never shrinks below this. The gallery specimen pins 200 only because its stage is fixed. */
  --tasty-port-addr-col-min-width:   var(--tasty-size-140);            /* 2026-10-08 (batch 8) — Address floor AND width: Address stays here; ALL spare width goes to Process */
  --tasty-port-star-on:              var(--tasty-accent-warning);      /* registered — gold, as Explorer favorites */
  --tasty-port-star-off:             var(--tasty-text-muted);          /* unregistered — muted outline */
  --tasty-port-state-none-dot:       var(--tasty-status-dot-idle);     /* NONE badge (favorite not in the scan) */

  /* ─────────────────  OVERLAYS (gallery specimen layouts)  ───────────────── */
  /* Plugins window */
  --tasty-plugins-list-width:       var(--tasty-size-288); /* left installed-list column */
  --tasty-plugins-header-glyph:     var(--tasty-accent-decorative); /* window-header ornament — peach, but decorative: NOT accent-attention */
  /* Plugin avatar — the square initial mark that stands in for a plugin icon
     (a plugin manifest carries no image). TWO sizes only: sm on list rows,
     lg on a detail/preview identity block. The tint is mixed into
     surface-raised (a FIXED bed) rather than the row background, so the mark
     reads identically on a default, hover and selected row. Colour does NOT
     vary by plugin — the manifest has no category field, so the mark carries
     identity (the initial), not classification. */
  --tasty-plugin-avatar-size-sm:    var(--tasty-size-32); /* Installed / Attention list row */
  --tasty-plugin-avatar-size-lg:    var(--tasty-size-46); /* detail identity + Add-plugin manifest preview */
  --tasty-plugin-avatar-radius:     var(--tasty-radius);
  --tasty-plugin-avatar-fg:         var(--tasty-accent-primary); /* the initial */
  --tasty-plugin-avatar-bg:         color-mix(in srgb, var(--tasty-accent-primary) 18%, var(--tasty-surface-raised));
  --tasty-plugin-avatar-border:     color-mix(in srgb, var(--tasty-accent-primary) 38%, transparent);
  --tasty-plugin-avatar-border-width: var(--tasty-border-width);
  --tasty-plugin-avatar-initial-weight: var(--tasty-font-weight-normal); /* was bold — no bold mono face ships; size + colour carry the mark (2026-10-07) */
  --tasty-plugin-avatar-initial-font-size-sm: var(--tasty-font-size-body); /* 13 — list (32) (2026-10-07) */
  --tasty-plugin-avatar-initial-font-size-lg: var(--tasty-font-size-max); /* 14 — detail (46); was 16, no UI-cap exception (2026-10-07) */
  /* Settings window */
  --tasty-settings-sidebar-width:   var(--tasty-size-200); /* L2 left nav */
  --tasty-settings-row-min-height:  var(--tasty-size-32);  /* one settings/toggle row */
  --tasty-settings-label-width:     var(--tasty-size-150); /* Settings Row label column (name the 150 the rows already used; font-override grid shares it) — 2026-10-06 */
  --tasty-settings-label-max-width: var(--tasty-size-240); /* 2026-10-08 (batch 8) — label column = the subtab's longest label, clamped to [settings-label-width 150, this]; longer labels wrap */
  --tasty-settings-label-gap:       var(--tasty-space-lg); /* 16 label column → control column */
  --tasty-settings-row-caption-gap: var(--tasty-space-xs); /* 4 row → its own caption / callout directly under it */
  --tasty-settings-content-max-width: var(--tasty-size-620); /* content column cap — cards, tables and notice blocks all stop here */
  /* Settings › General › Permissions (macOS) — four states, told apart by
     GLYPH + WORD, colour only as a third channel. Unknown and Not observable
     share the muted ink but never the glyph. */
  --tasty-perm-granted-fg:      var(--tasty-accent-success);   /* check */
  --tasty-perm-missing-fg:      var(--tasty-accent-warning);   /* alertCircle */
  --tasty-perm-unknown-fg:      var(--tasty-text-muted);       /* helpCircle */
  --tasty-perm-unobservable-fg: var(--tasty-text-muted);       /* eyeOff */
  --tasty-perm-status-gap:      var(--tasty-space-xs);         /* glyph ↔ word */
  --tasty-perm-row-height:      var(--tasty-settings-row-min-height); /* 32 */

  /* Info modal — boot notice queue (one message at a time). Shell unchanged
     from the product: 440 wide, 140..360 tall; the body scrolls, the button row
     never does. */
  --tasty-info-modal-width:      var(--tasty-size-440);
  --tasty-info-modal-min-height: var(--tasty-size-140);
  --tasty-info-modal-max-height: var(--tasty-size-360);
  --tasty-info-modal-title-edge: var(--tasty-border-default); /* 1px rule under the title bar — always drawn (shell rule, every message) */
  --tasty-info-modal-para-gap:   var(--tasty-space-md);        /* 12 between paragraphs */
  --tasty-info-modal-scroll-edge: var(--tasty-border-default); /* 1px rule, shown only on the side that has hidden content */

  /* CenterState — the one centred empty / loading / error block (file picker,
     remote attach, Settings › Misc › Scripts). No fixed height: it centres in
     the region it replaces. The sub line slot is always reserved so switching
     variants never moves the glyph. */
  --tasty-center-state-glyph-size: var(--tasty-icon-size-lg);  /* 24 — was 22 (CENTER_GLYPH_SIZE) and 26 (EMPTY_STATE_GLYPH_SIZE) */
  --tasty-center-state-gap:        var(--tasty-space-sm);      /* 8 glyph → title */
  --tasty-center-state-line-gap:   var(--tasty-space-xs);      /* 4 title → sub */
  --tasty-center-state-glyph-fg:   var(--tasty-glyph-dim);
  --tasty-center-state-error-fg:   var(--tasty-accent-danger);
  --tasty-center-state-title-fg:   var(--tasty-text-secondary);
  --tasty-center-state-sub-fg:     var(--tasty-text-muted);
  --tasty-center-state-max-width:  var(--tasty-measure-sm);    /* 300 — wraps before it */
  --tasty-center-state-action-gap: var(--tasty-space-md);      /* 12 sub slot → optional action (Button secondary · sm). The action is outside the centring. */
  /* Keybindings › Import / Export — the diff table + option-migration card.
     These were raw --tasty-size-* reads in the spec jsx; opened as named
     component tokens so the implementing side reads tokens, not constants. */
  --tasty-kb-ie-select-column-width: var(--tasty-size-32);  /* diff-table leading checkbox column */
  --tasty-kb-ie-action-column-width: var(--tasty-size-288); /* DEPRECATED 2026-10-10 (option migration removed) — no live reader; kept until the implementing side drops it. Was: migration-row action label column */
  --tasty-kb-ie-from-column-width:   var(--tasty-size-120); /* DEPRECATED 2026-10-10 (option migration removed) — no live reader. Was: migration-row original shortcut column */
  --tasty-kb-ie-slot-height:         var(--tasty-control-height-tab); /* 24 — read-only replacement slot chip */
  --tasty-kb-ie-slot-min-width:      var(--tasty-size-140); /* slot chip min width — keeps empty and filled slots the same size */
  --tasty-kb-ie-notice-inset:        var(--tasty-space-md);  /* notice / action-card inner padding, both axes (the spec jsx's 14 was off-grid — snapped to 12) */
  /* File picker (Open / Save) — breadcrumb measures. Two different axes:
     ONE crumb's own cap (past it that crumb ellipsises) and the hidden-ancestor
     menu's width band. The path's middle elision is a render rule, not a width. */
  --tasty-fp-crumb-max-width:      var(--tasty-size-180); /* a single crumb's cap — longer folder names ellipsise inside the crumb */
  --tasty-fp-crumb-menu-min-width: var(--tasty-size-180); /* … menu floor — same measure as a crumb, so short names still read as a menu */
  --tasty-fp-crumb-menu-max-width: var(--tasty-size-320); /* … menu ceiling (same as popover/toast) — longer labels ellipsise instead of growing */
  /* Crumb FLOORS — the caps above are maxima; these are the widths a crumb may
     not shrink past. The path bar allocates in priority order: current folder,
     then parent, then root, then the … menu. Squeeze order and the final
     single-crumb fallback are specified in the gallery file-picker specimens. */
  --tasty-fp-crumb-min-width:         var(--tasty-size-64); /* an ANCESTOR crumb's floor — below this it moves into the … menu */
  --tasty-fp-crumb-current-min-width: var(--tasty-size-96); /* the CURRENT folder's floor — it never goes below this while any ancestor is still painted */
  --tasty-fp-bar-hysteresis:          var(--tasty-size-8);  /* grow-back margin: a crumb returns only when 8px MORE than its floor is free, so a drag-resize can't flap */
  --tasty-fp-popup-min-width:         var(--tasty-size-320); /* the picker's own floor — clamped by the owning surface, which always wins */
  /* File picker section insets (2026-09-29) — every section starts on ONE line (fp-inset-start = the list's space-md),
     so the header glyph, first crumb, list-header Name, row icon and footer label share a column. 10 / 14 / 6 / 5 / 30 retire. */
  --tasty-fp-inset-start:             var(--tasty-space-md);  /* 12 — header · path bar · list header · rows · footer */
  --tasty-fp-inset-end:               var(--tasty-space-sm);  /* 8 — header / path bar trailing IconButton edge */
  --tasty-fp-section-gap:             var(--tasty-space-sm);  /* 8 — item gap inside header / path bar / footer rows */
  --tasty-fp-header-pad-y:            var(--tasty-space-sm);  /* 8 — header height = 8 + 24 (IconButton sm) + 8 = 40, content-driven, no fixed token */
  --tasty-fp-path-pad-y:              var(--tasty-space-xs);  /* 4 — path bar = 4 + 24 + 4 = 32 */
  --tasty-fp-list-head-pad-y:         var(--tasty-space-xs);  /* 4 — mono micro (10) uppercase column labels */
  --tasty-fp-row-pad-y:               var(--tasty-space-xs);  /* 4 — FpRow */
  --tasty-fp-footer-pad-y:            var(--tasty-space-sm);  /* 8 — footer x inset = fp-inset-start on both sides */
  --tasty-fp-footer-label-width:      var(--tasty-size-64);   /* "File name" label column */
  --tasty-fp-filter-height:           var(--tasty-control-height); /* 28 — the file-type filter chip matches the Input beside it (was 30) */
  --tasty-fp-folder-file-fg:          var(--tasty-state-disabled-fg); /* 2026-10-21 folder mode — inert file row ink (glyph, name, size, date); no opacity */
  --tasty-fp-folder-path-fg:          var(--tasty-input-readonly-fg); /* folder mode — target path readout ink (text-secondary) in the read-only box */
  --tasty-fp-folder-hint-fg:          var(--tasty-text-muted);        /* folder mode — footer hint line under the Folder row */
  --tasty-fp-filter-max-width:        var(--tasty-field-width-md); /* 160 — READ-ONLY filter chip: content width up to this, then front-kept ellipsis + tooltip with the full list (2026-10-06) */

  /* Popup over a WebView page in OPAQUE mode (2026-10-21).
     The host can paint only inside the popup rect and each dropdown rect; those pixels are fully opaque.
     A box whose rect (or whose parent shell's rect) intersects a visible opaque-mode page takes BOTH, on the
     WHOLE box (never per edge). Its existing 1px border (border-frame / border-strong / menu-border) stays and
     is the only silhouette — no extra edge. Scrim rule lives in the contract, not a token: no scrim at all. */
  --tasty-webview-opaque-radius: var(--tasty-radius-none);  /* replaces radius / menu-radius on the box */
  --tasty-webview-opaque-shadow: var(--tasty-shadow-none);  /* replaces shadow-popover / shadow-modal on the box */

  /* Shared popup title bar — right-hand buttons (× , and fullscreen when the popup declares a stage) (2026-09-29).
     The title is centred on the STRIP: both sides reserve the same width,
     reserve(N) = edge-inset + N × btn-size + (N − 1) × btn-gap + text-gap  → N=1: 32 · N=2: 60. */
  --tasty-popup-title-btn-size:       var(--tasty-icon-button-size-sm); /* 24 — was 20 in the product */
  --tasty-popup-title-btn-gap:        var(--tasty-space-xs);  /* 4 — fullscreen ↔ close */
  --tasty-popup-title-edge-inset:     var(--tasty-space-xs);  /* 4 — rightmost button ↔ strip edge */
  --tasty-popup-title-text-gap:       var(--tasty-space-xs);  /* 4 — leftmost button ↔ title ellipsis */
  --tasty-convert-popup-width:        var(--tasty-size-240);  /* convert-surface list popup width (was literal 200) — band 176 fits the ja title; × ui_scale like every popup width (2026-09-29) */
  --tasty-notifications-popup-width:  var(--tasty-size-352);  /* notifications popup default width (352 × 400) — the one popup with a fullscreen stage today */
  /* File HANDLER picker — the Recent row's trailing "when" fragment. The column
     is RESERVED at the widest vocabulary member (the absolute date, 10 mono
     D2Coding chars at 11px = 55) so a bucket change while the popup is open — it recomputes
     every frame — never reflows the id beside it. The when never shrinks and
     never drops: the id yields, it already has a front-elision rule. */
  --tasty-fh-when-width:              var(--tasty-size-56);
  /* Color swatch (settings color editor) */
  --tasty-swatch-size:              var(--tasty-size-16);
  --tasty-swatch-radius:            var(--tasty-radius-sm);
  /* Remote tool */
  --tasty-remote-label-col:         var(--tasty-size-112); /* form label grid column */
  --tasty-remote-filter-menu-width: var(--tasty-size-240); /* protocol-filter menu — BORDER-BOX width (outer edge to outer edge). Content = this minus the frame the popup crate draws (14 total at scale 1 → 226). The mock's 236 was a raw literal, snapped to the existing 240 primitive (same measure as tooltip max-width). All widths in this family read as border box. */
  /* Explorer view toggle (SegToggle — grid / list / detail). A SEGMENT: it picks a
     value (how the same listing is drawn), it does not switch content, so it takes the
     segment recipe from 2026-09-17 R1 — accent fill + on-accent ink. surface-active is
     row-selection only and never a segment fill. Off = text-muted, hover = overlay-hover,
     container = surface-raised + border-default (unchanged). */
  --tasty-segtoggle-on-bg:          var(--tasty-accent-primary);
  --tasty-segtoggle-on-fg:          var(--tasty-text-on-accent);
  /* Remote file transfer — progress / error popups (PopupDef, centered on the
     scrim) + the Settings › General › Remote transfer rows. The progress bar is
     the system's first determinate bar: a recessed 4px track with an accent
     fill; no animation (fill width follows bytes — terminal motion stays 0ms). */
  --tasty-transfer-popup-width:     var(--tasty-size-400); /* progress + error frame width */
  /* Transfer popup insets (2026-10-06) — ON-SCALE: multiply by ui_scale with the width. Line heights are
     NOT tokens: derive each text line from its font (line-height-ui × font-size). */
  --tasty-transfer-pad-x:           var(--tasty-size-14);  /* header · body · footer left/right inset, body padding */
  --tasty-transfer-header-pad-y:    var(--tasty-space-md); /* 12 */
  --tasty-transfer-body-gap:        var(--tasty-size-10);  /* between body blocks / file-row lines */
  --tasty-transfer-footer-pad-y:    var(--tasty-size-10);
  --tasty-transfer-well-pad-y:      var(--tasty-space-sm); /* 8 — failure-reason well */
  --tasty-transfer-well-pad-x:      var(--tasty-size-10);
  --tasty-progress-height:          var(--tasty-size-4);   /* bar thickness */
  --tasty-progress-radius:          var(--tasty-radius-sm);
  --tasty-progress-track-bg:        var(--tasty-bg-app);   /* recessed track (darker than the panel) */
  --tasty-progress-fill-bg:         var(--tasty-accent-primary); /* determinate fill */
  /* Preset editor — WYSIWYG surface split zone. The 30%-edge overlay shown on
     hover of a surface leaf in edit mode; clicking it splits toward that edge.
     Accent-tinted (this is the same intent as a live split/drop preview) —
     translucent fill + a stronger leading edge marking the new split line. */
  --tasty-preset-split-zone-bg:     color-mix(in srgb, var(--tasty-accent-primary) 22%, transparent);
  --tasty-preset-split-zone-border: color-mix(in srgb, var(--tasty-accent-primary) 55%, transparent);
  --tasty-preset-split-divider-width: var(--tasty-selection-edge-width); /* 2 — the one-edge split line inside the zone band; hairline, zoom-exempt (2026-10-06) */
  --tasty-preset-leaf-selected-ring-width: var(--tasty-focus-ring-width); /* 2 — ring AROUND the selected leaf; on-scale like dag-node-selected-ring-width (2026-10-06) */
  /* Preset editor — surface-leaf preview VALUE SUMMARY. On an UNSELECTED leaf
     (read-only preview AND edit mode), below the kind icon + kind name we list
     the leaf's configured fields as `label value` rows (cwd / startup / file /
     url — whatever KindCatalog declares), so same-kind leaves are told apart
     without opening the form. Labels muted-micro, values secondary-caption, both
     mono (they are paths / commands). Degrade order is a structural rule (JS
     consts, not tokens): full (icon+kind+summary) → drop summary when the box is
     < ~96px wide or < ~72px tall → drop the kind label too (icon only) when the
     short axis < 46px (the same floor as the split-zone min). Empty fields hide
     their row. Path-like fields (cwd / file) truncate at the FRONT (keep the
     tail); commands / urls truncate at the end. Selected leaf shows the SAME
     summary (parameters are edited on the surface settings screen below). */
  --tasty-preset-leaf-label-fg:        var(--tasty-text-muted);        /* row label tone */
  --tasty-preset-leaf-label-font-size: var(--tasty-font-size-micro);   /* 10 */
  --tasty-preset-leaf-value-fg:        var(--tasty-text-secondary);    /* row value tone */
  --tasty-preset-leaf-value-font-size: var(--tasty-font-size-caption); /* 11 — one step over the label */
  --tasty-preset-leaf-summary-gap:     var(--tasty-space-xs);          /* 4 — between summary rows AND kind-name ↔ summary */
  /* Preset editor — SURFACE SETTINGS SCREEN. Editing a leaf's parameters
     (kind · cwd · startup · kind-declared fields) no longer happens inside the
     leaf box (which clipped the form in small cells); it takes over the whole
     right detail column — toolbar AND preview — as three stacked boxes:
     header (what is being edited) / scrolling body (one-column form) / fixed
     footer (Cancel · OK, right-aligned, Settings-footer grammar). Structure
     edits stay auto-saved on the preview; the settings screen is a DRAFT that
     lands only on OK. While it is open the left list + L1 scope tabs dim to the
     disabled opacity and ignore input (switching away would silently drop the
     draft). */
  --tasty-preset-cfg-header-height:   var(--tasty-size-44);   /* = detail toolbar it replaces */
  --tasty-preset-cfg-footer-height:   var(--tasty-size-52);   /* fixed: 28 control + 12 top/bottom */
  --tasty-preset-cfg-footer-padding-x: var(--tasty-size-14);  /* same as Settings footer */
  --tasty-preset-cfg-form-max-width:  var(--tasty-size-460);  /* form column cap on wide windows */
  --tasty-preset-cfg-form-padding:    var(--tasty-space-lg);  /* 16 around the body */
  --tasty-preset-cfg-field-gap:       var(--tasty-space-md);  /* 12 between fields */
  --tasty-preset-cfg-draft-fg:        var(--tasty-accent-warning); /* "unsaved" dot + caption in the header */
  --tasty-preset-cfg-dim-opacity:     var(--tasty-state-dim-opacity); /* list + scope tabs while open (inert, not disabled) */
  --tasty-cut-pending-opacity:        var(--tasty-state-dim-opacity); /* explorer: file cut, waiting for paste (icon + label) */

  /* ─────────────────  CHROME · SWITCH-NUMBER OVERLAY  ───────────────── */
  /* Transient number keycaps painted over tabs / workspaces WHILE the
     tab_switch_modifier (over tabs) or workspace_switch_modifier (over
     workspaces) is held — a live preview of the 1–9 / 0 switch shortcuts.
     The keycap REPLACES the item's leading indicator in place (tab icon /
     workspace status dot / collapsed letter avatar) — no scrim, no reflow.
     Reuses the Kbd keycap; only the active-item accent fill + the appear
     timing are new. Implementers read the *currently bound* modifier from
     the same keybindings source the shortcut uses. */
  --tasty-switch-overlay-size:       var(--tasty-kbd-size);          /* 16 — keycap footprint; fills the icon/dot slot */
  --tasty-switch-overlay-bg:         var(--tasty-kbd-bg);            /* surface-raised keycap fill (inactive items) */
  --tasty-switch-overlay-fg:         var(--tasty-kbd-fg);            /* keycap digit (inactive items) */
  --tasty-switch-overlay-border:     var(--tasty-kbd-border);        /* border-strong keycap edge */
  --tasty-switch-overlay-shadow-depth: var(--tasty-kbd-shadow-depth);/* 2 — keycap bottom edge, matches Kbd */
  --tasty-switch-overlay-active-bg:  var(--tasty-accent-primary);    /* current tab/workspace = accent-filled keycap */
  --tasty-switch-overlay-active-fg:  var(--tasty-text-on-accent);    /* digit atop the accent fill */
  --tasty-switch-overlay-fade:       var(--tasty-motion-ui-fast);    /* 90ms ease appear; release dismisses at 0ms */

  /* ─────────────────  CHROME · MODIFIER HINT PANEL  ───────────────── */
  /* A floating, focus-less discoverability panel painted while a modifier
     (Ctrl / Cmd / Option / Shift — Win/Linux: Ctrl / Alt / Shift) is HELD.
     It lists every shortcut chord that contains the held modifier, grouped
     by chord (size-ascending, then Ctrl→Cmd/Alt→Option→Shift), plus any
     special role the chord carries (e.g. Ctrl = tab-switch numbers, Shift =
     TUI mouse-capture bypass). Draggable + edge-resizable, an X for a
     this-session dismiss; it NEVER takes keyboard focus and must not read as
     a focus window — hence the muted (sidebar) drag strip, not a titlebar.
     Timing: appears after a 500ms hold, fades opacity 0.2→1 over 200ms;
     release dismisses at 0ms (reduced-motion → instant, no fade). */
  --tasty-modhint-width:         var(--tasty-size-180);   /* default width — same as the 180px open sidebar (panel sits at the sidebar's foot) */
  --tasty-modhint-height:        var(--tasty-size-400);   /* default height */
  --tasty-modhint-min-width:     var(--tasty-size-180);   /* resize lower bound — down to the sidebar width so it can stay flush */
  --tasty-modhint-min-height:    var(--tasty-size-240);   /* resize lower bound */
  --tasty-modhint-bg:            var(--tasty-bg-panel);   /* opaque floating fill (never translucent — it sits over live output) */
  --tasty-modhint-border:        var(--tasty-border-strong);
  --tasty-modhint-radius:        var(--tasty-radius);     /* 4 — chrome, not a marketing card */
  --tasty-modhint-shadow:        var(--tasty-shadow-popover); /* anchored + scrim-less → popover per the semantic.css SCOPE RULE (was shadow-modal; a floating panel is not a modal) */
  --tasty-modhint-header-bg:     var(--tasty-bg-sidebar); /* muted drag strip — deliberately NOT a focus titlebar */
  --tasty-modhint-header-height: var(--tasty-size-28);
  --tasty-modhint-section-gap:   var(--tasty-space-md);   /* 12 — between chord sections */
  --tasty-modhint-role-bg:       var(--tasty-surface-active); /* special-role row wash — distinguishes it from a keycap row */
  --tasty-modhint-role-fg:       var(--tasty-accent-primary); /* special-role leading glyph */
  --tasty-modhint-empty-fg:      var(--tasty-text-muted);     /* empty-chord placeholder text ("No shortcuts bound") — one step below a keycap row's text-secondary; no wash, no glyph, no keycap */
  --tasty-modhint-grip-fg:       var(--tasty-border-strong);  /* bottom-right resize grip lines */
  --tasty-modhint-fade:          var(--tasty-motion-ui-fade); /* 200ms opacity 0.2→1 on appear */
  --tasty-modhint-hold-delay:    var(--tasty-motion-hold-reveal); /* 500ms hold before it appears */

  /* ─────────────────  CHROME · TITLEBAR (CSD)  ───────────────── */
  /* Client-Side-Decoration window titlebar. Shared across the macOS /
     Windows / Linux variants in ui_kits/terminal/titlebar/. Height is
     the semantic --tasty-titlebar-height. */
  --tasty-titlebar-bg:               var(--tasty-bg-app);
  --tasty-titlebar-bg-inactive:      var(--tasty-bg-sidebar);   /* unfocused window */
  --tasty-titlebar-border:           var(--tasty-separator);
  --tasty-titlebar-fg:               var(--tasty-text-secondary);
  --tasty-titlebar-fg-inactive:      var(--tasty-text-muted);

  /* window control buttons (min / max / restore / close) */
  --tasty-titlebar-button-fg:        var(--tasty-text-muted);
  --tasty-titlebar-button-fg-hover:  var(--tasty-text-primary);
  --tasty-titlebar-button-hover-bg:  var(--tasty-overlay-hover);
  --tasty-titlebar-button-active-bg: var(--tasty-overlay-active);
  --tasty-titlebar-close-hover-bg:   var(--tasty-accent-window-close); /* Windows-style red fill */
  --tasty-titlebar-close-hover-fg:   var(--tasty-text-on-window-close);

  /* macOS traffic lights (drawn by Tasty in CSD mode) */
  --tasty-titlebar-traffic-size:     var(--tasty-size-12);
  --tasty-titlebar-traffic-close:    var(--tasty-accent-macos-close);
  --tasty-titlebar-traffic-min:      var(--tasty-accent-macos-min);
  --tasty-titlebar-traffic-zoom:     var(--tasty-accent-macos-zoom);
  --tasty-titlebar-traffic-inactive: var(--tasty-surface-active); /* unfocused: all dots grey */

  /* Windows caption-button hit width (Snap Layouts target on maximize) */
  --tasty-titlebar-caption-width:    var(--tasty-size-46);

  /* Linux DE circular window buttons (min/max/close) — 24px, like caption-width
     this sits OUTSIDE the UI zoom scale (a fixed OS-affordance dimension). */
  --tasty-titlebar-window-button-size: var(--tasty-size-24);

  /* Wayland CSD framing — when Tasty draws its own decorations the client paints
     rounded top corners + a 1px outline + a soft drop shadow, and the shadow
     MARGIN is the band that holds the 8-direction resize edges. The shadow is a
     deliberate one-off (Tasty has no general shadow system; surfaces use tint). */
  /* ZOOM POLICY: titlebar-csd-radius + titlebar-csd-shadow-margin are px-fixed
     and do NOT follow host UI zoom — same column as titlebar-height / traffic-
     size / caption-width / window-button-size. They are OS window-frame chrome;
     scaling only the corner radius + shadow band while the titlebar height stays
     fixed would look broken. Implementers must expose both as zoom-EXCLUDED
     fields (do not apply the primitive-alias→zoom-multiply default). */
  --tasty-titlebar-csd-radius:        var(--tasty-radius-8);          /* 8px top corners — px-fixed, no UI zoom */
  --tasty-titlebar-csd-border:        var(--tasty-border-strong);     /* 1px window outline */
  /* INTENTIONAL EXCEPTION to the two-shadow scope rule: this is OS window-frame
     chrome, not a UI overlay, and the -8px spread is functional — it keeps the
     falloff inside the 8px shadow-margin band that carries the resize edges.
     A renderer that cannot express negative spread must widen its shadow type;
     do NOT substitute shadow-modal or drop the spread. */
  --tasty-titlebar-csd-shadow:        0 18px 50px -8px rgba(0,0,0,.5); /* one-off CSD scrim */
  --tasty-titlebar-csd-shadow-margin: var(--tasty-size-8);            /* 8px → resize-edge band — px-fixed, no UI zoom (see ZOOM POLICY above) */

  /* ─────────────────  CHROME · SIDEBAR (full + collapsed rail)  ───────────────── */
  /* Fixed sidebar geometry — the full sidebar header + section list and the 52px
     collapsed rail. Like the titlebar / tab strip, these host-chrome dimensions sit
     OUTSIDE the UI zoom scale. Font roles map onto the existing UI type steps; the
     wordmark is the one sanctioned over-cap size (branding). */
  --tasty-sidebar-logo-size:                  var(--tasty-size-22); /* full-sidebar header watermelon mark */
  --tasty-sidebar-logo-collapsed-size:        var(--tasty-size-24); /* collapsed-rail logo */
  --tasty-sidebar-wordmark-font:              var(--tasty-font-mono);
  --tasty-sidebar-wordmark-font-size:         var(--tasty-font-size-brand-wordmark); /* 17 — "tasty." wordmark; BRANDING exception to the 14px UI cap */
  --tasty-sidebar-section-heading-font-size:  var(--tasty-font-size-micro);       /* 10 — "WORKSPACES" caps section heading */
  --tasty-sidebar-section-heading-tracking:   var(--tasty-letter-spacing-caps-wide); /* 0.07em (was caps 0.04em) — 2026-10-06 */
  --tasty-sidebar-wordmark-tracking:          var(--tasty-letter-spacing-ui); /* 0 — 2026-10-06 */
  --tasty-sidebar-button-label-font-size:     var(--tasty-font-size-caption);     /* 11 — Tools/Plugins/Settings ghost-button labels (snapped from source 12 → the 11px caption step; UI type scale stays 10/11/13/14) */
  --tasty-sidebar-collapsed-slot-width:       var(--tasty-size-32); /* collapsed icon / workspace slot width */
  --tasty-sidebar-collapsed-icon-height:      var(--tasty-size-22); /* collapsed icon slot height */
  --tasty-sidebar-collapsed-workspace-height: var(--tasty-size-28); /* collapsed workspace-avatar slot height */

  /* Workspace-category header (sidebar folders). The header is a BAND, not a
     bare caption: it sits on the app tier (one step DOWN from the sidebar) with
     hairlines top and bottom, so the section boundary reads as elevation — the
     same face-over-face logic used everywhere else — instead of colour. Label
     steps up from muted to secondary + bold so the 10px caps line out-ranks the
     13px workspace rows below it. The trailing count is a low-emphasis meta
     number that yields to the hover-reveal `+` button. */
  --tasty-sidebar-category-header-bg:        var(--tasty-bg-app);
  --tasty-sidebar-category-header-border:    var(--tasty-separator);
  --tasty-sidebar-category-header-fg:        var(--tasty-text-secondary);
  --tasty-sidebar-category-header-weight:    var(--tasty-font-weight-bold);
  --tasty-sidebar-category-header-pad-y:     var(--tasty-space-sm);
  --tasty-sidebar-category-header-pad-x:     var(--tasty-space-sm);
  --tasty-sidebar-category-header-count-fg:  var(--tasty-text-disabled);
  --tasty-sidebar-category-header-count-font-size: var(--tasty-font-size-micro);

  /* ─────────────────  SURFACE · TASK DAG VIEW  ───────────────── */
  /* Read-only observation of an agent-built task DAG — the full-tab graph
     surface and the workspace-scoped DAG popup (ui_kits/terminal/overlays/
     dag_view.jsx). NOT a node editor: no port/socket affordance, no drag-to-
     move; layout is computed, so these tokens carry only geometry + state
     colour. Node box is FIXED (168 × 48 in graph space) at every zoom level —
     the level-of-detail tiers swap CONTENT, never the box, so a 0.5s data poll
     can never move a node. */
  --tasty-dag-node-width:            var(--tasty-size-168);
  --tasty-dag-node-height:           var(--tasty-size-48);
  --tasty-dag-node-padding-x:        var(--tasty-space-sm);       /* 8 */
  --tasty-dag-node-padding-y:        var(--tasty-space-xs);       /* 4 */
  --tasty-dag-node-row-gap:          var(--tasty-space-xs);       /* 4 — name row ↔ meta row */
  --tasty-dag-node-gap:              var(--tasty-space-sm);       /* 8 — glyph ↔ text */
  --tasty-dag-node-radius:           var(--tasty-radius);         /* 4 */
  --tasty-dag-node-bar-width:        var(--tasty-size-3);         /* leading status bar */
  --tasty-dag-node-bg:               var(--tasty-surface-raised);
  --tasty-dag-node-border:           var(--tasty-border-strong);
  --tasty-dag-node-fg:               var(--tasty-text-primary);   /* task name */
  --tasty-dag-node-meta-fg:          var(--tasty-text-muted);     /* kind + duration */
  --tasty-dag-node-name-font-size:   var(--tasty-font-size-body);    /* 13 */
  --tasty-dag-node-meta-font-size:   var(--tasty-font-size-micro);   /* 10 — status caps + duration */
  --tasty-dag-node-hover-bg:         var(--tasty-overlay-hover);
  --tasty-dag-node-selected-ring:    var(--tasty-accent-primary);
  --tasty-dag-node-selected-ring-width: var(--tasty-focus-ring-width); /* 2 */
  --tasty-dag-node-dim-opacity:      var(--tasty-opacity-dimmed);      /* skipped / cancelled node — 0.75, still 4.5:1 */

  /* Task status — the 8 execution states. Colour is ONE of two channels; the
     glyph (◦ ❯ ◑ ✓ ✗ − ⊘ ?) + the spelled label carry the same information, so
     nothing here is colour-only. Every value points at an accent role. */
  --tasty-dag-status-waiting:      var(--tasty-status-idle);
  --tasty-dag-status-ready:        var(--tasty-accent-info);
  --tasty-dag-status-running:      var(--tasty-accent-primary);
  --tasty-dag-status-succeeded:    var(--tasty-accent-success);
  --tasty-dag-status-failed:       var(--tasty-accent-danger);
  --tasty-dag-status-cancelled:    var(--tasty-text-disabled);
  --tasty-dag-status-skipped:      var(--tasty-text-disabled);
  --tasty-dag-status-unknown:      var(--tasty-accent-warning);
  --tasty-dag-status-partially-failed: var(--tasty-accent-attention); /* 2026-10-07 — ROLLUP only: finished, mixed outcome. Peach = between danger red and success green; not failed red */
  /* node fill per state — only the states that must WIN attention get a wash;
     terminal + idle states stay on the plain raised surface. */
  --tasty-dag-status-waiting-bg:   var(--tasty-dag-node-bg);
  --tasty-dag-status-ready-bg:     var(--tasty-dag-node-bg);
  --tasty-dag-status-running-bg:   color-mix(in srgb, var(--tasty-accent-primary) 16%, var(--tasty-surface-raised));
  --tasty-dag-status-succeeded-bg: var(--tasty-dag-node-bg);
  --tasty-dag-status-failed-bg:    color-mix(in srgb, var(--tasty-accent-danger) 12%, var(--tasty-surface-raised));
  --tasty-dag-status-cancelled-bg: var(--tasty-dag-node-bg);
  --tasty-dag-status-skipped-bg:   var(--tasty-dag-node-bg);
  --tasty-dag-status-unknown-bg:   color-mix(in srgb, var(--tasty-accent-warning) 10%, var(--tasty-surface-raised));
  --tasty-dag-status-partially-failed-bg: color-mix(in srgb, var(--tasty-accent-attention) 12%, var(--tasty-surface-raised));
  /* TEXT tone per state. The state colour is the RECEDE channel (3px bar, border,
     glyph on a chip); at 10px uppercase it is not always readable, so the spelled
     label has its own role. waiting borrows text-muted; the two DIMMED states
     (cancelled / skipped) must clear 4.5:1 AFTER the 0.75 card dim, so they take
     text-secondary — muted would land at 3.3:1 once multiplied. */
  --tasty-dag-status-waiting-label:   var(--tasty-text-muted);
  --tasty-dag-status-ready-label:     var(--tasty-dag-status-ready);
  --tasty-dag-status-running-label:   var(--tasty-dag-status-running);
  --tasty-dag-status-succeeded-label: var(--tasty-dag-status-succeeded);
  --tasty-dag-status-failed-label:    var(--tasty-dag-status-failed);
  --tasty-dag-status-cancelled-label: var(--tasty-text-secondary);
  --tasty-dag-status-skipped-label:   var(--tasty-text-secondary);
  --tasty-dag-status-unknown-label:   var(--tasty-dag-status-unknown);
  --tasty-dag-status-partially-failed-label: var(--tasty-dag-status-partially-failed);
  /* 2026-10-07 — running PHASE: awaiting_input is the only phase with its own tone (needs-input yellow,
     the same signal as the attention channel). postprocessing / retry_wait keep the running tokens. */
  --tasty-dag-phase-awaiting:       var(--tasty-attention-needs-input);
  --tasty-dag-phase-awaiting-bg:    color-mix(in srgb, var(--tasty-attention-needs-input) 12%, var(--tasty-surface-raised));
  --tasty-dag-phase-awaiting-label: var(--tasty-dag-phase-awaiting);
  --tasty-dag-header-count-fg:      var(--tasty-dag-row-count-fg); /* 2026-10-07 — tab header "6/8 done" = list row counter */

  /* Graph geometry — layer depth + sibling gap (4px grid). */
  --tasty-dag-layer-gap:             var(--tasty-size-32);  /* along the flow axis */
  --tasty-dag-sibling-gap:           var(--tasty-size-24);  /* across the flow axis */
  --tasty-dag-canvas-bg:             var(--tasty-bg-panel);
  --tasty-dag-canvas-dot:            var(--tasty-border-default); /* dot-grid mark */
  --tasty-dag-canvas-dot-size:       var(--tasty-size-1);
  --tasty-dag-canvas-dot-gap:        var(--tasty-size-16);
  --tasty-dag-canvas-padding:        var(--tasty-space-lg);  /* 16 — fit-to-view inset */

  /* Edges — relation is carried by DASH PATTERN + colour together. */
  --tasty-dag-edge-width:            var(--tasty-size-1);
  --tasty-dag-edge-depends:          var(--tasty-border-strong);
  --tasty-dag-edge-fallback:         var(--tasty-accent-attention);
  --tasty-dag-edge-reduce:           var(--tasty-accent-info);
  --tasty-dag-edge-dash-fallback:    6 3; /* @kind other */
  --tasty-dag-edge-dash-reduce:      2 3; /* @kind other */
  --tasty-dag-edge-corner-radius:    var(--tasty-radius);   /* orthogonal elbow */
  --tasty-dag-edge-arrow-size:       var(--tasty-size-8);
  --tasty-dag-edge-dim-opacity:      var(--tasty-opacity-recessed);
  --tasty-dag-edge-highlight:        var(--tasty-accent-primary); /* edges of the selected node */

  /* Canvas chrome — zoom cluster + minimap, bottom-right overlay. */
  --tasty-dag-chrome-height:         var(--tasty-control-height);  /* 28 */
  --tasty-dag-chrome-bg:             var(--tasty-bg-sidebar);
  --tasty-dag-chrome-border:         var(--tasty-border-strong);
  --tasty-dag-chrome-fg:             var(--tasty-text-secondary);
  --tasty-dag-chrome-inset:          var(--tasty-space-sm);        /* 8 from the canvas edge */
  --tasty-dag-minimap-width:         var(--tasty-size-160);
  --tasty-dag-minimap-height:        var(--tasty-size-112);
  --tasty-dag-minimap-bg:            var(--tasty-bg-sidebar);
  --tasty-dag-minimap-node:          var(--tasty-border-strong);
  --tasty-dag-minimap-viewport:      var(--tasty-accent-primary);
  --tasty-dag-minimap-min-surface:   var(--tasty-size-560); /* below this the minimap is dropped */

  /* Runner badge — host runner thread: on/off, crashed, ready + running counts.
     The state that must be NOTICED is runner-off-with-work-ready; it takes the
     warning tone and exposes a resume hint slot. */
  --tasty-dag-runner-height:         var(--tasty-control-height-tree); /* 22 */
  --tasty-dag-runner-padding-x:      var(--tasty-space-sm);
  --tasty-dag-runner-gap:            var(--tasty-space-sm);
  --tasty-dag-runner-radius:         var(--tasty-radius-sm);
  --tasty-dag-runner-bg:             var(--tasty-surface-raised);
  --tasty-dag-runner-border:         var(--tasty-border-strong);
  --tasty-dag-runner-fg:             var(--tasty-text-secondary);
  --tasty-dag-runner-idle-fg:        var(--tasty-text-muted);
  --tasty-dag-runner-stalled-fg:     var(--tasty-accent-warning);
  --tasty-dag-runner-stalled-border: color-mix(in srgb, var(--tasty-accent-warning) 45%, transparent);
  --tasty-dag-runner-stalled-bg:     color-mix(in srgb, var(--tasty-accent-warning) 10%, var(--tasty-surface-raised));
  --tasty-dag-runner-crashed-fg:     var(--tasty-accent-danger);
  --tasty-dag-runner-crashed-border: color-mix(in srgb, var(--tasty-accent-danger) 45%, transparent);
  --tasty-dag-runner-crashed-bg:     color-mix(in srgb, var(--tasty-accent-danger) 12%, var(--tasty-surface-raised));

  /* Cycle-detected banner — a warning strip pinned to the canvas top. The graph
     keeps rendering behind it (this surface observes, it never hides state). */
  --tasty-dag-cycle-bg:              color-mix(in srgb, var(--tasty-accent-warning) 14%, var(--tasty-bg-panel));
  --tasty-dag-cycle-border:          color-mix(in srgb, var(--tasty-accent-warning) 45%, transparent);
  --tasty-dag-cycle-fg:              var(--tasty-accent-warning);
  --tasty-dag-cycle-height:          var(--tasty-control-height); /* 28 */

  /* Node detail — right-side panel on a wide surface, bottom sheet when narrow. */
  --tasty-dag-detail-width:          var(--tasty-size-288);
  --tasty-dag-detail-sheet-height:   var(--tasty-size-220); /* narrow-width bottom sheet */
  --tasty-dag-detail-bg:             var(--tasty-bg-sidebar);
  --tasty-dag-detail-border:         var(--tasty-border-default);
  --tasty-dag-detail-padding:        var(--tasty-space-md);
  --tasty-dag-detail-log-bg:         var(--tasty-bg-app);
  --tasty-dag-detail-log-max-height: var(--tasty-size-160); /* error tail — scrolls past this */
  --tasty-dag-detail-out-max-height: var(--tasty-size-112); /* stdout tail */

  /* DAG list row (popup) — a ListCtrl row plus a trailing summary cluster
     (rollup chip + done/total). Density follows ListCtrl. */
  --tasty-dag-row-height:            var(--tasty-listctrl-row-min-height); /* 36 */
  --tasty-dag-row-summary-gap:       var(--tasty-space-sm);
  --tasty-dag-row-count-fg:          var(--tasty-text-muted);
  --tasty-dag-row-count-font-size:   var(--tasty-font-size-caption); /* 11 mono — "7/12" */
  --tasty-dag-popup-width:           var(--tasty-size-560);
  --tasty-dag-popup-height:          var(--tasty-size-460);

  /* 2026-10-06 — command palette list cap · remote protocol filter */
  --tasty-palette-list-max-height:     var(--tasty-size-320); /* list grows to 11 rows then scrolls; ON-SCALE with control-height. List padding = space-sm on all four sides (no extra footer gap) */
  --tasty-remote-filter-dropdown-width: var(--tasty-size-240); /* BORDER-BOX width (includes the 1px border) of the protocol-filter popover; content = 240 − 2 */

  /* 2026-10-06 (batch 2) — palette width · settings window · toast offset · brand lockup · font preview · CodeArea */
  --tasty-palette-width:               var(--tasty-size-540);  /* command palette card; ON-SCALE (× UI zoom) */
  --tasty-settings-window-width:       var(--tasty-size-1100); /* settings window inner + min size — OFF-SCALE (2026-10-07) */
  --tasty-settings-window-height:      var(--tasty-size-700);
  --tasty-toast-stack-offset-bottom:   var(--tasty-size-36);   /* MAIN window-scope stack: status bar 24 + 12. OFF-SCALE — the status bar and the toast structural dims are outside the UI zoom (2026-10-07) */
  --tasty-loading-lockup-tracking:     var(--tasty-letter-spacing-n1px); /* "tasty." 38px loading lockup */
  --tasty-font-preview-min-width:      var(--tasty-field-width-lg); /* one preview box narrower than this → Focused / Unfocused stack */
  --tasty-font-preview-padding-y:      var(--tasty-space-sm);
  --tasty-font-preview-padding-x:      var(--tasty-space-md);
  --tasty-font-preview-line-height:    var(--tasty-line-height-ui); /* 1.4 × the surface's effective font size */
  --tasty-codearea-font-size:          var(--tasty-font-size-caption); /* same as Input mono */
  --tasty-codearea-line-height:        var(--tasty-line-height-ui);
  --tasty-codearea-padding-y:          var(--tasty-space-xs);
  --tasty-codearea-padding-x:          var(--tasty-space-sm);
  --tasty-codearea-gutter-width:       var(--tasty-space-xl);  /* 24 — two-digit line numbers */
  --tasty-codearea-gutter-bg:          var(--tasty-bg-sidebar);
  --tasty-codearea-gutter-fg:          var(--tasty-text-muted);
  --tasty-codearea-gutter-border:      var(--tasty-separator);
  --tasty-codearea-error-fg:           var(--tasty-accent-danger); /* gutter number + message glyph on the error line */
  --tasty-codearea-max-height:         var(--tasty-size-200);  /* then scrolls */

  /* 2026-10-07 (batch 3) */
  --tasty-search-bar-border:           var(--tasty-menu-border);   /* floating find band — same edge as menus (→ border-strong) */
  --tasty-popup-content-margin:        var(--tasty-space-xs);      /* inner ring of EVERY popup frame: anchored menus (Tools menu, rail category menu, banner more-menu, multiselect) AND dialog-type popups that share popup::content_margin() — 4 */
  --tasty-modhint-grip-size:           var(--tasty-icon-size-xs);  /* 12 corner resize grip */
  --tasty-modhint-grip-inset:          var(--tasty-size-2);        /* grip offset from the right / bottom edge; ON-SCALE */
  --tasty-modhint-row-font-size:       var(--tasty-font-size-caption); /* 11 — chord row label · role row text · empty row (was raw 12) */
  --tasty-modhint-row-min-height:      var(--tasty-size-24);       /* chord row */
  --tasty-modhint-empty-row-min-height: var(--tasty-size-20);     /* empty-chord placeholder row */
  --tasty-modhint-row-gap:             var(--tasty-size-6);        /* role row padding-y (padding-x = space-sm) */
  --tasty-trigger-menu-min-width:      var(--tasty-field-width-lg);      /* Scripts "Add trigger…" menu — 200 */
  --tasty-trigger-menu-max-height:     var(--tasty-autocomplete-max-height); /* → 220, then scrolls */
  --tasty-image-handle-size:           var(--tasty-size-6);        /* floating-selection corner handle; ON-SCALE; centred on the corner (offset = −size/2) */
  --tasty-image-popup-width:           var(--tasty-size-300);      /* New Image · Save As card */
  --tasty-image-popup-pad-top:         var(--tasty-space-md);      /* 12 */
  --tasty-image-popup-pad-x:           var(--tasty-size-14);
  --tasty-image-popup-gap:             var(--tasty-size-10);       /* title → body and body → buttons */
  --tasty-image-popup-btn-gap:         var(--tasty-space-sm);      /* 8 between buttons and between size fields */
  --tasty-image-popup-title-font-size: var(--tasty-font-size-max); /* 14 */
  --tasty-image-popup-title-weight:    var(--tasty-font-weight-semibold);
  --tasty-image-size-input-width:      var(--tasty-size-64);
  --tasty-image-path-row-gap:          var(--tasty-size-6);        /* path input → browse IconButton */
  --tasty-image-zoom-font-size:        var(--tasty-font-size-caption); /* "100%" — mono 11 */
  --tasty-image-zoom-min-width:        var(--tasty-size-40);
  --tasty-explorer-min-height:         var(--tasty-size-180);      /* split-drag stops here (2026-10-08, was 160): internal tabs 28 + toolbar 44 + Detail header 28 + 2 rows 56 + status line 24 */
  --tasty-explorer-favorites-hide-below: var(--tasty-size-240);    /* body height under which Favorites hides (Files only): fav floor 120 + Files 120 */
  --tasty-explorer-error-fg:           var(--tasty-accent-danger); /* folder read error glyph */
  --tasty-dag-edge-binding:            var(--tasty-accent-data);   /* input binding — data handed downstream */
  --tasty-dag-edge-dash-binding:       8 2 2 2;                    /* @kind other */ /* dash-dot — apart from reduce 2 3 / fallback 6 3 */
  --tasty-dag-edge-transition:         var(--tasty-accent-route);  /* result-chosen route */
  --tasty-dag-edge-dash-transition:    10 4;                       /* @kind other */ /* long dash */
  --tasty-dag-edge-selected-width:     var(--tasty-focus-ring-width); /* the SELECTED transition draws 2 */
  --tasty-toast-stack-offset-bottom-settings: var(--tasty-size-64); /* Settings window-scope stack: footer 52 + 12. ON-SCALE — the footer is inside the UI zoom */
  /* 2026-10-07 (batch 4) */
  --tasty-tools-menu-min-width:        var(--tasty-size-160);      /* Tools menu grows to its longest label; 160 is the floor */
  --tasty-tools-menu-max-width:        var(--tasty-size-240);      /* past this a label ellipsizes at the END */
  --tasty-explorer-state-compact-below: var(--tasty-size-120);     /* content body under this → status screens switch to the one-line compact row */
  --tasty-md-callout-icon-gap:         var(--tasty-space-xs);      /* callout header: marker → type icon → label, 4 each */
  --tasty-md-callout-marker-size:      var(--tasty-icon-size-sm);  /* collapsible callout chevron (replaces the <details> default marker) */
  /* 2026-10-08 (batch 8) — Settings › Keybindings › Plugins subtab. Rides the settings Row grid
     (label 150 + gap 16), NOT the 288 Import/Export column: 288 + 16 + 160 + 8 + 200 + 8 + Reset
     overflows the 620 content cap. One command = ONE control line; every control on it is md 28. */
  --tasty-kb-plugin-title-width:    var(--tasty-settings-label-width); /* 150 — command title column; titles wrap, never ellipsise */
  --tasty-kb-plugin-title-gap:      var(--tasty-settings-label-gap);   /* 16 */
  --tasty-kb-plugin-picker-width:   var(--tasty-field-width-lg);       /* 200 — "Plugin:" Select */
  --tasty-kb-plugin-mode-width:     var(--tasty-field-width-md);       /* 160 — Inherit / Custom / None */
  --tasty-kb-plugin-slot-width:     var(--tasty-field-width-lg);       /* 200 — inherit-source Select · key Input · "(Unassigned)" all occupy this one slot */
  --tasty-kb-plugin-control-gap:    var(--tasty-space-sm);             /* 8 between controls on the line */
  --tasty-kb-plugin-control-height: var(--tasty-control-height);       /* 28 — Select, Input and Reset alike */
  --tasty-kb-plugin-row-min-height: var(--tasty-settings-row-min-height); /* 32 control line */
  --tasty-kb-plugin-row-padding-y:  var(--tasty-space-xs);             /* 4 above + below a command block */
  --tasty-kb-plugin-caption-gap:    var(--tasty-space-xs);             /* 4 control line → its caption (resolved key / parse error) */
  --tasty-kb-plugin-separator:      var(--tasty-separator);            /* 1px between command blocks; none after the last */
  --tasty-kb-plugin-list-gap:       var(--tasty-space-md);             /* 12 picker row → first command */
  --tasty-kb-plugin-caption-fg:     var(--tasty-text-muted);           /* "Inherited (Ctrl+C)" */
  --tasty-kb-plugin-none-fg:        var(--tasty-text-muted);           /* "(Unassigned)" */
  --tasty-kb-plugin-error-fg:       var(--tasty-accent-danger);        /* key parse error caption */
  --tasty-kb-plugin-draft-dot:      var(--tasty-accent-primary);       /* unsaved draft change on this command */
  --tasty-kb-plugin-draft-dot-size: var(--tasty-status-dot-size-compact); /* 6 */
  /* 2026-10-09 (batch 9) — attach size sync · split sibling · image load failed · settings row gap · explorer file operations */
  --tasty-attach-sync-glyph: var(--tasty-accent-warning); /* attach size-sync failure banner glyph (same tone as the refusal banner) */
  --tasty-attach-sync-name-max-width: var(--tasty-size-160); /* one surface name in the banner body ellipsises here; fixed copy never cut */
  --tasty-split-sibling-min-height: var(--tasty-size-56); /* a new split never leaves a non-explorer sibling under this (tab strip 28 + one 28 row); else refused */
  --tasty-image-error-fg: var(--tasty-accent-danger); /* image canvas load-failed glyph + title (missing / decode) */
  --tasty-settings-row-gap: var(--tasty-space-md); /* 12 between settings rows (was 8 in app, 14 literal in kit) */
  --tasty-explorer-toolbar-compact-below: var(--tasty-size-440); /* cell width under which the toolbar file-command group folds into one More menu */
  --tasty-explorer-name-error-fg: var(--tasty-accent-danger); /* new-item / inline-rename name error (border + message) */
  --tasty-explorer-drop-target-border: var(--tasty-accent-primary); /* 1px inset ring on the folder row / cell / tree node / list body under a drag */
  --tasty-explorer-drop-target-bg: color-mix(in srgb, var(--tasty-accent-primary) calc(var(--tasty-tint-fill-alpha) * 100%), transparent); /* drop target fill */
  --tasty-explorer-drag-move-fg: var(--tasty-accent-primary); /* drag chip badge — move */
  --tasty-explorer-drag-copy-fg: var(--tasty-accent-success); /* drag chip badge — copy */
  --tasty-explorer-drag-refused-fg: var(--tasty-accent-danger); /* drag chip badge — refused */
  --tasty-explorer-drag-chip-max-width: var(--tasty-size-240); /* drag chip next to the pointer */
  --tasty-explorer-progress-height: var(--tasty-size-2); /* progress bar on the status line top edge */
  --tasty-explorer-progress-track: var(--tasty-surface-raised); /* progress track */
  --tasty-explorer-progress-fill: var(--tasty-accent-primary); /* progress fill (no animation; follows bytes) */
  --tasty-explorer-conflict-width: var(--tasty-size-400); /* name-conflict popup (surface-scoped) */
  --tasty-explorer-preview-width: var(--tasty-size-288); /* preview panel default width */
  --tasty-explorer-preview-min-width: var(--tasty-size-200); /* preview splitter lower bound */
  --tasty-explorer-preview-max-width: var(--tasty-size-460); /* preview splitter upper bound */
  --tasty-explorer-grid-thumb-size: var(--tasty-size-40); /* Grid cell icon slot (all cells) — image thumbnails fit here */
  --tasty-explorer-props-width: var(--tasty-size-360); /* Properties popup */
  --tasty-explorer-delete-list-max-height: var(--tasty-size-160); /* 2026-10-12 Delete permanently confirm: target list scrolls past ~4 two-line rows; frame = info-modal-width / max-height */
  --tasty-explorer-props-label-width: var(--tasty-size-96); /* Properties field label column */
  --tasty-explorer-search-bar-height: var(--tasty-size-36); /* filter / search bar under the toolbar */
  --tasty-explorer-search-folder-col-width: var(--tasty-size-160); /* Detail “Folder” column in recursive results */
  --tasty-explorer-match-fg: var(--tasty-autocomplete-match-fg); /* matched part of a name while filtering */
  /* 2026-10-09 batch 10 */
  --tasty-image-too-large-fg: var(--tasty-accent-warning); /* image canvas "too large" glyph — refused by limit, file is fine (warning tone) */
  --tasty-explorer-name-error-max-width: var(--tasty-size-240); /* name error message box max width (new item / rename) */
  --tasty-explorer-list-min-width: var(--tasty-size-200); /* listing min width beside the preview panel; panel hides under preview-min + this (400) */
  --tasty-explorer-props-padding-x: var(--tasty-size-14); /* Properties popup inner side padding (popup family inset, as conflict / rename) */
  --tasty-explorer-props-row-min-height: var(--tasty-size-24); /* Properties field row min height */
  --tasty-explorer-props-row-line: var(--tasty-size-20); /* Properties field label / value line height */
  --tasty-explorer-props-row-pad-top: var(--tasty-size-2); /* Properties field row top nudge */
  --tasty-explorer-preview-header-height: var(--tasty-size-40); /* preview panel header (name + kind · size) */
  --tasty-file-drop-overlay-bg: color-mix(in srgb, var(--tasty-accent-primary) calc(var(--tasty-tint-fill-alpha) * 100%), transparent); /* window file-drop overlay fill (0.12, same as explorer-drop-target-bg) */
  --tasty-file-drop-overlay-border: color-mix(in srgb, var(--tasty-accent-primary) calc(var(--tasty-tint-border-alpha) * 100%), transparent); /* window file-drop overlay 1px edge (0.36 tint-border; was local alpha 153) */
  --tasty-file-drop-overlay-fg: var(--tasty-accent-primary); /* window file-drop overlay glyph + label (full ink for 4.5:1; was 60% alpha) */
  --tasty-kb-record-width: var(--tasty-kb-ie-slot-min-width); /* Keybindings shortcut record button width (140, same as Import/Export slot) */
  --tasty-kb-record-height: var(--tasty-kb-ie-slot-height); /* Keybindings shortcut record button height (24) */
  --tasty-kb-record-add-width: var(--tasty-size-32); /* Keybindings add-shortcut (+) button width */
  --tasty-kb-row-gap: var(--tasty-space-sm); /* gap between shortcut rows (8 — denser than settings-row-gap 12; no divider) */
  --tasty-font-combo-list-max-height: var(--tasty-select-search-list-max-height); /* 2026-10-19: alias of the shared SearchableSelect token (value unchanged, 300) */
  --tasty-font-combo-search-inset: var(--tasty-select-search-inset); /* 2026-10-19: alias of the shared SearchableSelect token (value unchanged, 4) */
  /* 2026-10-10 (batch 11) — attach names floor · explorer link / cursor / marquee / hidden / create-in · shortcut record states · CLI runs in UI copy */
  --tasty-attach-sync-name-min-width: var(--tasty-size-40); /* names-line floor: a name shrinks to here before ellipsis; a shorter name keeps its own width (no padding box) */
  --tasty-explorer-link-glyph: var(--tasty-text-muted); /* trailing link glyph after a symlink name (all views) */
  --tasty-explorer-link-glyph-size: var(--tasty-icon-size-xs); /* trailing link glyph size (12) */
  --tasty-explorer-link-broken-fg: var(--tasty-accent-warning); /* broken symlink: link glyph replaces the item glyph in this tone; name stays text-secondary */
  --tasty-explorer-cursor-ring: var(--tasty-border-focus); /* keyboard current item: 1px inset ring (keyboard-driven only), drawn over selection too */
  --tasty-explorer-cursor-ring-width: var(--tasty-border-width); /* keyboard current item ring width (1, inset — never moves layout) */
  --tasty-explorer-marquee-bg: color-mix(in srgb, var(--tasty-accent-primary) calc(var(--tasty-tint-fill-alpha) * 100%), transparent); /* drag-select rectangle fill (0.12 tint-fill) */
  --tasty-explorer-marquee-border: color-mix(in srgb, var(--tasty-accent-primary) calc(var(--tasty-tint-border-alpha) * 100%), transparent); /* drag-select rectangle 1px edge (0.36 tint-border) */
  --tasty-explorer-autoscroll-zone: var(--tasty-size-24); /* drag-select / drag auto-scroll band at the list top and bottom edges; no visual indicator */
  --tasty-explorer-hidden-fg: var(--tasty-text-muted); /* hidden item name + glyph while Show hidden files is on (no opacity — cut items keep 0.5) */
  --tasty-explorer-create-indent: var(--tasty-space-lg); /* create-in-folder input row indent under the target folder row (16) */
  --tasty-kb-record-border: var(--tasty-border-default); /* shortcut record button / add / None slot border at rest */
  --tasty-kb-record-border-hover: var(--tasty-border-strong); /* shortcut record button / add / None slot border on hover (fill unchanged) */
  --tasty-kb-record-empty-fg: var(--tasty-text-muted); /* “None” slot label on a shortcut row with no binding */
  --tasty-ui-code-font: var(--tasty-font-mono); /* CLI command / option run inside UI copy (size = the sentence size) */
  --tasty-ui-code-bg: var(--tasty-surface-raised); /* CLI run fill inside UI copy */
  --tasty-ui-code-fg: var(--tasty-text-primary); /* CLI run ink inside UI copy */
  --tasty-ui-code-padding-x: var(--tasty-space-xs); /* CLI run side padding (no vertical padding — line height unchanged) */
  --tasty-ui-code-radius: var(--tasty-radius-sm); /* CLI run corner radius */
  /* 2026-10-10 (batch 12) — boot error · approval danger choice · explorer address / create / drag / props / preview · settings narrow stack · code run on raised · plugin record slots */
  --tasty-boot-form-width: var(--tasty-size-360); /* boot error screen form column (same 360 as first-run shell setup; no card) */
  --tasty-boot-error-glyph: var(--tasty-accent-danger); /* boot error screen leading alertCircle glyph (title stays text-primary) */
  --tasty-approval-danger-bg: color-mix(in srgb, var(--tasty-accent-danger) calc(var(--tasty-tint-fill-alpha) * 100%), var(--tasty-bg-panel)); /* agent approval danger choice fill — OPAQUE mix over bg-panel (Mocha 56,43,61 · Latte 236,214,222) */
  --tasty-approval-danger-border: var(--tasty-accent-danger); /* agent approval danger choice 1px border (vs surface-raised: Mocha 5.43 · Latte 3.52) */
  --tasty-approval-danger-fg: var(--tasty-text-primary); /* agent approval danger choice label (on fill: Mocha 9.18 · Latte 5.80) */
  --tasty-explorer-address-pending-delay: var(--tasty-motion-ui-fade);
  --tasty-explorer-remote-queue-head-gap: var(--tasty-space-xs); /* 2026-10-14 — remote queue head “On ‹host› · one at a time”: gap between text pieces and host mark = a word space */ /* remote address check: spinner appears only after 200ms (no flash on a fast round trip) */
  --tasty-explorer-address-pending-size: var(--tasty-icon-size-sm); /* remote address check spinner in the address field trailing slot */
  --tasty-explorer-create-field-max-width: var(--tasty-size-240); /* create-in-folder / new item name field max width; caption follows at space-sm */
  --tasty-explorer-drag-expand-delay: var(--tasty-duration-800); /* dragging over a closed tree folder expands it after this hover time */
  --tasty-explorer-props-partial-fg: var(--tasty-accent-warning); /* Properties folder size: “some folders couldn’t be read” glyph + line */
  --tasty-explorer-preview-body-min-height: var(--tasty-size-120); /* preview panel body under this height → body hidden, header only */
  --tasty-settings-row-stack-gap: var(--tasty-space-xs); /* settings Row stacked (narrow): label → control vertical gap */
  --tasty-settings-row-stack-hysteresis: var(--tasty-space-lg); /* settings Row returns side by side only when the control fits with this much spare width */
  --tasty-ui-code-bg-on-raised: var(--tasty-bg-panel); /* CLI run fill when the container is surface-raised (toast, tooltip, raised card) — ink on it Mocha 11.34 · Latte 7.06 */
  --tasty-kb-plugin-record-height: var(--tasty-kb-plugin-control-height); /* Keybindings › Plugins Custom record slots — 28, one height per control line */
  --tasty-kb-os-reserved-fg: var(--tasty-accent-warning); /* “may be taken by the OS” caption on a text-entered shortcut */
  /* 2026-10-19 — plugin trust box (unverified warning) · notice column · restore / save-halt notice · pending indicator */
  --tasty-plugin-trust-title-fg: var(--tasty-text-primary); /* trust box title ink (all kinds except trusted) — tone moves to the glyph only */
  --tasty-plugin-trust-body-fg: var(--tasty-text-secondary); /* trust box cause + risk paragraphs */
  --tasty-plugin-trust-glyph-danger: var(--tasty-accent-danger); /* unverified (unknown key · missing .pub · unsigned · signature error) glyph */
  --tasty-plugin-trust-glyph-warning: var(--tasty-accent-warning-glyph); /* permissions-changed glyph */
  --tasty-plugin-trust-glyph-success: var(--tasty-accent-success-ink); /* trusted glyph — 2026-10-27: was accent-success (Latte 2.45:1); Mocha identical */
  --tasty-plugin-trust-body-trusted-fg: var(--tasty-accent-success-ink); /* 2026-10-27: trusted one-line body (plugins.trust_trusted_body) — Latte ≥ 5.5:1 on mantle/base+tint */
  --tasty-notice-column-inset: var(--tasty-space-sm); /* window top-right notice column: inset from the work area edge (under window banners / top-right search bar) */
  --tasty-notice-column-gap: var(--tasty-space-sm); /* gap between stacked notices in the column */
  --tasty-notice-column-width: var(--tasty-size-360); /* notice column width (= search bar width); max = work area − 2 × inset */
  --tasty-restore-notice-width: var(--tasty-notice-column-width); /* restore-failure / save-halt non-focus notice width */
  --tasty-restore-notice-glyph: var(--tasty-notify-error); /* leading alertCircle in the notice body (title stays text-primary) */
  --tasty-restore-notice-padding: var(--tasty-space-md); /* notice body padding */
  --tasty-restore-notice-gap: var(--tasty-space-sm); /* notice body row gap */
  --tasty-restore-notice-body-fg: var(--tasty-text-secondary); /* notice explanation ink */
  --tasty-restore-notice-label-fg: var(--tasty-text-muted); /* “Saved layout” mono caption over the path */
  --tasty-restore-notice-path-bg: var(--tasty-bg-sidebar); /* path box fill (selectable, wraps anywhere) */
  --tasty-restore-notice-path-border: var(--tasty-border-default); /* path box 1px edge */
  --tasty-restore-notice-path-fg: var(--tasty-text-primary); /* path text */
  --tasty-pending-indicator-height: var(--tasty-size-24); /* response-wait chip height */
  --tasty-pending-indicator-padding-x: var(--tasty-space-sm); /* chip side padding */
  --tasty-pending-indicator-gap: var(--tasty-space-sm); /* spinner · label · time gap */
  --tasty-pending-indicator-radius: var(--tasty-radius); /* chip radius */
  --tasty-pending-indicator-bg: var(--tasty-surface-raised); /* chip fill */
  --tasty-pending-indicator-border: var(--tasty-border-strong); /* chip 1px edge (floating, like Toast / Banner) */
  --tasty-pending-indicator-fg: var(--tasty-text-secondary-raised); /* label ink on surface-raised */
  --tasty-pending-indicator-time-fg: var(--tasty-text-primary); /* elapsed time ink */
  --tasty-pending-indicator-sep-fg: var(--tasty-text-muted); /* “·” separator */
  --tasty-pending-indicator-spinner-size: var(--tasty-icon-size-sm); /* chip spinner 14 */
  --tasty-pending-indicator-compact-below: var(--tasty-size-200); /* notice column narrower than this → spinner + time only */
  --tasty-pending-indicator-delay: var(--tasty-loading-overlay-delay); /* show only if still pending after 200ms (same as LoadingOverlay) */
  /* 2026-10-19 — Settings › Handler › File Detectors / File Handlers: registered rows + user add card */
  --tasty-file-handler-form-bg: var(--tasty-surface-raised); /* add card fill (= Hook Handlers add card) */
  --tasty-file-handler-form-border: var(--tasty-border-default); /* add card 1px edge */
  --tasty-file-handler-form-padding: var(--tasty-space-md); /* add card inset 12 */
  --tasty-file-handler-form-row-gap: var(--tasty-space-sm); /* 8 between card rows */
  --tasty-file-handler-form-label-fg: var(--tasty-text-secondary-raised); /* field labels + card title on surface-raised (Latte → n1100, ≈5.2:1) */
  --tasty-file-handler-form-caption-fg: var(--tasty-text-secondary-raised); /* "Saved as user/…" + defaults captions on surface-raised */
  --tasty-file-handler-form-error-fg: var(--tasty-accent-danger-raised); /* field / cross-field error glyph + line on surface-raised — 2026-10-21 was accent-danger (Latte 3.52); now Mocha 5.43 · Latte 4.79 */
  --tasty-file-handler-priority-width: var(--tasty-field-width-xs); /* 90 — priority number field (was 80 hard-coded) */
  --tasty-file-handler-select-width: var(--tasty-field-width-lg); /* 200 — Detector Select */
  --tasty-file-handler-kind-width: var(--tasty-field-width-md); /* 160 — Action Select */
  --tasty-file-handler-prio-fg: var(--tasty-text-muted); /* "prio N" on row line 1 */
  --tasty-file-handler-summary-fg: var(--tasty-text-secondary); /* row line 2: rules / detector → action, mono term-sm */
  --tasty-file-handler-empty-fg: var(--tasty-text-muted); /* empty-list line */
  /* 2026-10-21 — follow-up: existing-id info line + removed user rules in the summary */
  --tasty-file-handler-form-info-fg: var(--tasty-text-secondary-raised); /* "{id} exists — your rules are added" info line (info glyph + caption) */
  --tasty-file-handler-summary-removed-fg: var(--tasty-text-muted); /* user rule pieces struck through while "your rules removed on save" */
  /* 2026-10-21 — narrow pending rows: the id never shrinks below this; past it the pending Tag drops to its own line */
  --tasty-file-handler-id-min-width: var(--tasty-size-96);
  /* 2026-10-21 — Explorer Properties: Open footer · permission editor · time format */
  --tasty-explorer-props-footer-border: var(--tasty-separator); /* rule above the Open footer */
  --tasty-explorer-props-perm-border: var(--tasty-border-strong); /* permission editor box edge on bg-panel */
  --tasty-explorer-props-perm-cell-width: var(--tasty-size-56); /* role label column + each r / w / x column — 2026-10-21: box spans the CONTENT width (label column included), 4 × 56 + 2 × 8 + 2 × 1 = 242 ≤ 330 */
  --tasty-explorer-props-perm-error-fg: var(--tasty-accent-danger); /* apply failed line on bg-panel — Mocha 7.08 · Latte 4.80 */
  --tasty-explorer-props-perm-locked-fg: var(--tasty-text-muted); /* "Only the owner can change permissions" under the value */
  --tasty-explorer-props-value-fg: var(--tasty-text-secondary); /* field value ink (names the literal the rows already used) */
  /* 2026-10-21 — Settings › Terminal › Performance: when saved values apply (replaces the warning-yellow restart notice) */
  --tasty-settings-apply-note-fg: var(--tasty-text-secondary); /* on bg-panel — Mocha ≈ 8.0 · Latte ≈ 5.1 */
  --tasty-settings-apply-note-glyph: var(--tasty-text-muted); /* info glyph (non-text, ≥ 3:1) */
  --tasty-settings-apply-note-gap: var(--tasty-space-sm); /* glyph → text */
  /* 2026-10-22 — Properties permission box in a narrow Explorer cell */
  --tasty-explorer-props-perm-grid-min-width: calc(4 * var(--tasty-explorer-props-perm-cell-width) + 2 * var(--tasty-space-sm) + 2 * var(--tasty-border-width)); /* 242 — box ≥ this: role column + R W X grid */
  --tasty-explorer-props-perm-stack-min-width: calc(3 * var(--tasty-explorer-props-perm-cell-width) + 2 * var(--tasty-space-sm) + 2 * var(--tasty-border-width)); /* 186 — box ≥ this: heads over 3 cells, role name above its row; below: labelled list */
  --tasty-explorer-props-perm-role-gap: var(--tasty-space-xs); /* role name line → its checkboxes (stack / list) */
  /* 2026-10-22 — settings number field: fits its value, unit outside */
  --tasty-number-field-min-width: var(--tasty-field-width-xs); /* 90 — floor; grows to value length + 1ch caret room, capped at the row's available width */
  --tasty-number-field-unit-gap: var(--tasty-space-sm); /* field → unit word → "no limit" */
  --tasty-number-field-unit-fg: var(--tasty-text-secondary); /* unit word / "no limit" after the field — on bg-panel Mocha ≈ 8.0 · Latte ≈ 5.1 */
  --tasty-number-field-note-fg: var(--tasty-text-secondary); /* range line under the field */
}



/* ============================================================
   Tasty — base layer
   Minimal element defaults so specimen cards, components, and
   UI kits share a consistent baseline. Opt-in: applies to
   document defaults only, no aggressive resets.
   ============================================================ */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body {
  margin: 0;
  font-family: var(--tasty-font-ui);
  font-size: var(--tasty-font-size-body);
  line-height: var(--tasty-line-height-ui);
  color: var(--tasty-text-primary);
  background: var(--tasty-bg-app);
}

/* monospace / terminal contexts */
.tasty-mono,
code,
kbd,
pre,
samp {
  font-family: var(--tasty-font-mono);
  font-variant-ligatures: contextual;
}

/* selection mirrors the terminal selection color */
::selection {
  background: var(--tasty-selection-bg);
  color: var(--tasty-text-primary);
}

/* a thin, on-brand focus ring used across components */
:where(button, a, input, select, textarea, [tabindex]):focus-visible {
  outline: var(--tasty-focus-ring-width) solid var(--tasty-border-focus);
  outline-offset: 1px;
}

/* scrollbars — subtle, surface-tinted */
.tasty-scroll {
  scrollbar-width: thin;
  scrollbar-color: var(--tasty-color-neutral-400) transparent;
}
.tasty-scroll::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
.tasty-scroll::-webkit-scrollbar-thumb {
  background: var(--tasty-color-neutral-400);
  border-radius: var(--tasty-radius-pill);
  border: 2px solid transparent;
  background-clip: content-box;
}
.tasty-scroll::-webkit-scrollbar-thumb:hover {
  background: var(--tasty-color-neutral-500);
  background-clip: content-box;
}
