/*
 * Mockarty Design System — Design Tokens
 * ========================================
 * Single source of truth for all visual properties across 14+ UI applications.
 *
 * Usage:
 *   - External CSS apps: <link rel="stylesheet" href=".../tokens.css">
 *   - Inline CSS apps:   inject via designsystem.TokensCSS() Go helper
 *
 * DO NOT duplicate these values in individual app stylesheets.
 * Apps may override specific tokens for their context (e.g., website uses larger fonts).
 */

/* ────────────────────────────────────────────────
   Inter Font — Local Embed (no Google Fonts CDN)
   ──────────────────────────────────────────────── */

@font-face {
    font-family: 'Inter';
    src: url('fonts/Inter-Variable.woff2') format('woff2');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

/* ────────────────────────────────────────────────
   Responsive Breakpoints (standard reference)
   ──────────────────────────────────────────────────
   CSS custom properties cannot be used inside @media queries,
   so these values are documented here as the canonical reference.
   All component stylesheets MUST use these exact pixel values.

   sm:  480px  — mobile phones (portrait)
   md:  768px  — tablets (portrait)
   lg:  1024px — small desktops / tablets (landscape)
   xl:  1280px — desktops
   2xl: 1440px — large desktops / wide monitors
   ──────────────────────────────────────────────── */

/* ────────────────────────────────────────────────
   Color Palette — Semantic Colors
   ──────────────────────────────────────────────── */

:root {
    /* Global UI scale — shrinks the whole interface ~7% by default for denser,
       more readable layout (≈ working at 93% browser zoom). Applied via
       `html { zoom: var(--ui-scale) }` in main.css. Adjustable in one place. */
    --ui-scale: 0.93;

    /* Zoom-compensated viewport height. Under html{zoom:--ui-scale}, a literal
       var(--vh100) renders at scale×var(--vh100) and leaves a ~7% strip at the bottom of every
       full-height layout. Use var(--vh100) instead of var(--vh100) in height/max-height
       so containers fill the real screen. Works inside calc(): calc(var(--vh100)
       - 64px). One knob — change --ui-scale and both scale + heights track. */
    --vh100: calc(100vh / var(--ui-scale, 1));

    /* Backgrounds */
    --bg-primary: #ffffff;
    --bg-secondary: #f8fafc;
    --bg-tertiary: #e3eaf4;

    /* Elevated surfaces — aliases over the bg ramp. Defined as var()
       references so they follow the active theme automatically (custom
       properties resolve at use-time against the cascaded --bg-* value).
       Some components (tcm.css incl. the TCM "Views" modal) reference these
       directly; without a definition they fell back to a hard-coded #fff and
       rendered white in dark mode. */
    --surface-1: var(--bg-primary);
    --surface-2: var(--bg-secondary);

    /* Text */
    --text-primary: #0a1628;
    --text-secondary: #2a3f5c;
    --text-muted: #4f6480; /* a11y: #586f8e cleared white (5.15) but only 4.25:1 on --bg-tertiary (#e3eaf4), where muted counts/badges actually sit; this clears 5.0:1 there and 6.06:1 on white */

    /* Borders */
    --border-color: #c8d6e8;

    /* Auto-hide scrollbar thumb (light). Paints only while scrolling / on hover. */
    --scrollbar-thumb: rgba(93, 120, 153, 0.45);
    --scrollbar-thumb-hover: rgba(93, 120, 153, 0.72);

    /* Accent */
    --accent-color: #1a73d4;
    --accent-hover: #155fb0;
    --accent-light: #e6f0fd;
    /* Accent colour for TEXT specifically. The brand accent (#1a73d4) is AA on
       pure white (4.7:1) but drops below 4.5:1 once it sits on a faint accent /
       blue tint (chips, selected pills, tinted headers, badge counters) — a
       recurring contrast miss. --accent-text is a slightly darker accent that
       clears AA on white AND on those tints (#1565c0 → 5.75:1 white / 4.75:1 on
       the strongest tint) while staying a recognisable accent blue. Fills,
       borders and icons keep --accent-color; only text routes here. Dark theme
       maps it back to --accent-color (the light-blue accent is already AA on the
       dark canvas). */
    --accent-text: #1565c0;
    /* Text/icon colour to place ON an accent fill (primary buttons, avatars,
       selected chips). Light theme: white on the #1a73d4 accent (4.7:1, AA).
       The dark theme overrides this to a near-black because its accent is a
       light blue (#64b5ff) where white text fails WCAG AA (2.2:1) — see .dark
       block. */
    --accent-contrast: #ffffff;
    /* Text/icon colour to place ON a BRIGHT status fill (success green, info
       cyan, warning amber). These fills are light in BOTH themes, so white text
       fails WCAG AA (success btn was 1.9:1) — use a fixed near-black. Theme-
       invariant on purpose (the fills don't darken in dark mode). */
    --on-bright-fill: #0a1628;
    /* Brand accent gradient — the blue panel/card header fill. Defined once so a
       designer can retune the brand sheen (angle + stops) in a single place. */
    --gradient-accent: linear-gradient(135deg, var(--accent-color), var(--accent-hover));
    /* Secondary accent — purple. Used for status pills on
       manual_pending / paused (audit P1-9). */
    --accent-purple: #9333ea;

    /* ── Selection (UNIFIED, readable) — light ──────────────────────────────
       The single source of truth for "this row/card is selected" everywhere
       (mock list, api-tester tree, TCM, pickers, …). A subtle accent WASH (not
       a saturated fill) + an accent edge: body text and coloured badges stay
       legible on top. Depth variants step the tone darker→lighter so nested
       selected rows still read as a hierarchy without losing contrast. */
    --selected-bg: rgba(45, 140, 240, 0.16);   /* depth 0 / flat lists */
    --selected-bg-2: rgba(45, 140, 240, 0.11); /* nested one level */
    --selected-bg-3: rgba(45, 140, 240, 0.07); /* nested deeper */
    --selected-border: var(--accent-color);
    --selected-text: var(--text-primary);      /* readable on the wash */

    /* Status */
    --success-color: #10b981;
    --danger-color: #ef4444;
    --warning-color: #f59e0b;
    --info-color: #06b6d4;
    /* Severity scale (5 tiers) — fuzzing/security/contract/findings use a finer
       ramp than the 4 status colours. Single source so CSS and the Go report
       exporters agree (they had diverged: fuzzing #ea580c, contract #dc2626…).
       solid = dot/bar/badge fill; -bg = soft chip tint. */
    --severity-critical: #dc2626;
    /* Ink ON the solid critical fill — the ONE case where it has to flip with
       the theme, so it cannot ride --on-bright-fill. #dc2626 takes white at
       4.83:1, but the dark theme lightens the fill to #f87171 where white
       collapses to 2.77:1 and the dark ink reads 6.64:1. Every other status
       fill is bright in BOTH themes, so --on-bright-fill answers those. */
    --on-critical-fill: #ffffff;
    --severity-high: #ea580c;
    --severity-medium: #d97706;
    --severity-low: #0891b2;
    --severity-info: #64748b;
    /* TEXT variants of the severity ramp. The solid hues are tuned as FILLS; used
       as text on their own tint they measure 3.07–4.41:1 (below AA). Same split
       the status colours already have (--danger-text / --warning-text …). */
    --severity-critical-text: #991b1b;
    --severity-high-text: #9a3412;
    --severity-medium-text: #854d0e;
    --severity-low-text: #155e75;
    --severity-info-text: #475569;
    --severity-critical-bg: rgba(220, 38, 38, 0.12);
    --severity-high-bg: rgba(234, 88, 12, 0.12);
    --severity-medium-bg: rgba(217, 119, 6, 0.12);
    --severity-low-bg: rgba(8, 145, 178, 0.12);
    --severity-info-bg: rgba(100, 116, 139, 0.12);
    /* HTTP method colours — single source for every method badge/chip
       (API tester request list, TCM endpoint picker, recorder, collections).
       Swagger-style convention: GET blue, POST green, PUT amber, PATCH purple,
       DELETE red, neutral grey for HEAD/OPTIONS/etc. solid = badge fill;
       -bg = soft tint behind coloured text. */
    --method-get: var(--accent-color);
    --method-post: var(--success-color);
    --method-put: var(--warning-color);
    --method-patch: #a855f7;
    --method-delete: var(--severity-critical); /* #dc2626 keeps white badge text ≥4.5:1; --danger-color (#ef4444) would fail AA */
    --method-neutral: var(--severity-info);
    --method-get-bg: rgba(45, 140, 240, 0.15);
    --method-post-bg: rgba(16, 185, 129, 0.15);
    --method-put-bg: rgba(245, 158, 11, 0.15);
    --method-patch-bg: rgba(168, 85, 247, 0.15);
    --method-delete-bg: rgba(239, 68, 68, 0.15);
    --method-neutral-bg: rgba(100, 116, 139, 0.15);
    /* Short aliases — a few component styles reference the bare names
       (e.g. TCM steward severity dots, filter chip hovers); map them to the
       canonical -color tokens so those elements aren't left colourless. */
    --success: var(--success-color);
    --danger: var(--danger-color);
    --warning: var(--warning-color);
    --info: var(--info-color);
    /* --error-color was referenced in 28 places (wiki, constructor, ui-testing,
       utils, admin) but never DEFINED: the six bare `var(--error-color)` uses
       resolved to nothing (element left colourless) and the rest fell through to
       three diverging hardcoded reds (#ef4444 / #dc3545 / #dc2626) that ignored
       the theme. Alias it like the four above — one red, theme-aware in both. */
    --error-color: var(--danger-color);

    /* Legacy structural aliases. Several component stylesheets (the TCM data
       designer .tcm-data__*, TCM list borders, the test-plans tree picker
       .mty-tree-picker__*, a few admin rules) reference these short names with
       NO var() fallback — they were never defined anywhere, so the property was
       invalid → transparent bg / default-black text, ignoring the theme in BOTH
       light and dark. Alias them to the canonical theme-aware tokens here (one
       place) so every bare usage resolves and adapts per theme. New code should
       use the canonical names directly. */
    --bg: var(--bg-primary);
    --fg: var(--text-primary);
    --text: var(--text-primary);
    --card: var(--bg-secondary);
    --surface: var(--bg-secondary);
    --surface2: var(--bg-tertiary);
    --bg-surface: var(--bg-secondary);
    --accent: var(--accent-color);
    --primary-color: var(--accent-color);
    --muted: var(--text-secondary);
    --fail: var(--danger-color);
    --border: var(--border-color);
    --border-default: var(--border-color);
    --border-subtle: var(--border-color);

    /* Status pill backgrounds + text. Alpha tints so they read correctly over
       BOTH the light and dark surfaces (a solid pastel would be wrong in dark).
       These were previously only ever supplied via inline var() fallbacks;
       defining them centrally fixes the bare usages (e.g. the TCM runs-list
       status pills rendered with no background) and harmonises the scattered
       fallbacks. */
    --success-bg: rgba(16, 185, 129, 0.12);
    /* Status TEXT tokens are darkened on light theme so status text meets WCAG
       AA (4.5:1) on white — the raw --*-color values are tuned for borders/
       fills/icons (3:1) and fail as small text: success #10b981=2.54,
       warning #f59e0b=2.15, info #06b6d4=2.43. Dark theme keeps the bright
       --*-color values (they're already ~10:1 on the dark base). */
    --success-text: #047857;
    --danger-bg: rgba(239, 68, 68, 0.12);
    /* Danger TEXT (small inline errors / failed-status labels) needs WCAG AA
       (4.5:1) for <18px text — but --danger-color #ef4444 on white is only
       3.77:1. Use a darker red here (#dc2626 = 4.74:1 on #fff). The border/glow
       keep --danger-color (non-text → 3:1 is fine). Dark theme overrides this
       to the lighter #f87171 (~6.5:1 on the dark base). */
    --danger-text: #b91c1c; /* a11y: #dc2626 was 4.14:1 on --danger-bg (the tinted pill surface) — fails AA; this clears 5.55:1 */
    --info-bg: rgba(6, 182, 212, 0.12);
    /* a11y: #0e7490 is 5.36:1 on white but only 4.46:1 once it sits on the cyan
       tint above (the pill surface it is designed for) — under AA. Darkened one
       step to clear 6.9:1 there, matching --severity-info-text. */
    --info-text: #155e75;

    /* Warning banner */
    --warning-bg: #fef3c7;
    --warning-fg: #92400e;
    /* amber-800, not amber-700: on the --severity-medium-bg tint (12% amber
       over a light card) #b45309 measures 4.42:1, just under the 4.5 AA floor
       for normal text. Measured in a real browser, not estimated — the tint has
       to be composited against the card to get the true background. #92400e
       gives 6.24:1 with room to spare. */
    --warning-text: #92400e;

    /* ── Badge / chip / tag / pill SHAPE (U3 unification) ───────────────────
       The colour system above (status -bg/-text) is solid; what drifts across
       the ~300 bespoke badge classes is the BOX (padding/radius/font). These
       are the canonical .badge metrics — bespoke badges migrate onto them wave
       by wave so every pill reads at the same scale. Values = current canon,
       so wiring is a zero-shift change. */
    --badge-pad-y: 2px;
    --badge-pad-x: var(--space-2);   /* 8px */
    --badge-radius: var(--radius-pill);
    --badge-font: var(--font-xs);
    --badge-weight: 500;
    /* Two legitimate badge SHAPES: pill (above, status/info) and TAG —
       a rectangular label for protocol/method/type (HTTP, GET, …). The tag
       shape is its own canon so method/protocol pills stay square; values =
       the current protocol/method metrics (zero-shift migration). */
    --badge-tag-radius: var(--radius-xs);   /* 4px */
    --badge-tag-pad-y: 0.15rem;             /* ≈2.4px */
    --badge-tag-pad-x: 0.4rem;              /* ≈6.4px */

    /* Shadows — the base surfaces stay flat by design (--shadow-sm/--shadow/
       --shadow-lg = none). These are kept for compatibility. */
    --shadow-sm: none;
    --shadow: none;
    --shadow-hover: none;
    --shadow-lg: none;

    /* Elevation scale — refined, low-opacity drop shadows for surfaces that
       genuinely FLOAT (popovers, dropdowns, modals, drawers, lightbox). The
       single source so a designer retunes depth everywhere from here; dark
       theme overrides these with stronger values below. 1=resting card,
       2=popover, 3=dropdown/menu, 4=modal, 5=full-screen overlay. */
    --shadow-1: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.10);
    --shadow-2: 0 2px 8px rgba(15, 23, 42, 0.10);
    --shadow-3: 0 6px 18px rgba(15, 23, 42, 0.14);
    --shadow-4: 0 12px 32px rgba(15, 23, 42, 0.18);
    --shadow-5: 0 24px 60px rgba(15, 23, 42, 0.26);
    /* Focus ring — one token for the keyboard/active focus glow. */
    --focus-ring: 0 0 0 3px rgba(45, 140, 240, 0.18);
    --focus-ring-danger: 0 0 0 3px rgba(239, 68, 68, 0.22);

    /* Z-index scale — the app's global overlay layers, value-preserving (each
       token == the magic number it replaced, so stacking order is unchanged).
       Local component stacking (<800: badges over cards, sticky cells) stays
       inline — it's contextual, not a global layer. */
    --z-raised: 900;
    --z-dropdown: 1000;
    --z-popover: 1300;
    --z-overlay: 9000;
    /* Floating docks/chat windows: above page chrome + overlays, but BELOW the
       modal layer so a dialog opened from (or over) a dock is never covered by
       it. The discussions/agent floating dock previously sat at
       --z-modal-content and its "New room" prompt rendered behind it. */
    --z-dock: 9500;
    --z-modal: 10000;
    --z-modal-content: 10001;
    --z-modal-popover: 10050;
    /* The agent-chat FAB is a PERSISTENT affordance — it must stay reachable
       over every surface (owner: «поверх всего, а то пропадает»). It sat at
       --z-modal, TYING with the fullscreen call dock: at an equal z-index the
       later DOM node wins, so the call swallowed the button. Above the modal
       layer, still under --z-toast (a toast is a transient message and must
       never be covered). */
    --z-fab: 10060;
    --z-toast: 100000;

    /* Hover — single standard hover style */
    --hover-overlay: rgba(0, 0, 0, 0.04);
    --hover-overlay-strong: rgba(0, 0, 0, 0.07);

    /* Typography — base 15px, step 2px (11-13-15-17-19-21-25) */
    --font-xs: 0.6875rem;   /* 11px — captions, micro-labels */
    --font-sm: 0.8125rem;   /* 13px — secondary text, labels */
    --font-base: 0.9375rem; /* 15px — body, main content */
    --font-md: 1.0625rem;   /* 17px — emphasized body, subheads */
    --font-lg: 1.1875rem;   /* 19px — section titles */
    --font-xl: 1.3125rem;   /* 21px — page headings */
    --font-2xl: 1.5625rem;  /* 25px — large headings */
    /* Canonical UI font stack — mirrors what main.css sets on <body>. Defined
       here so --font-sans (used by wiki/tcm) resolves to the real stack instead
       of the bare `sans-serif` fallback it used to land on. */
    --font-family-base: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, 'Segoe UI', sans-serif;
    --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;

    /* U3: font-weight scale. 815 hardcoded weights across the tree (600×454,
       500×167, 700×127, 400×36) and no token before. Values == the current
       dominant weights, so wiring them on is zero-shift; one knob to retune the
       whole type system's emphasis (the Apple/JetBrains direction uses a
       different weight ramp). */
    --font-weight-normal: 400;
    --font-weight-medium: 500;
    --font-weight-semibold: 600;
    --font-weight-bold: 700;

    /* Spacing */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-8: 3rem;
    --space-10: 4rem;

    /* Border Radius */
    --radius-xs: 4px;
    --radius-sm: 6px;
    --radius-md: 8px;
    --radius-lg: 12px;
    --radius-xl: 16px;
    --radius-modal: 12px;
    --radius-pill: 999px;

    /* Modal width scale — single source for BOTH the MockartyModal component
       (.mockarty-modal-dialog) and the legacy .modal-content system. Retune
       every modal width here. Owner directive (KIT-1): modals were too wide —
       the scale is sized down toward a compact default; pick the smallest size
       that fits the content. */
    --modal-w-xs: 360px;   /* confirm/prompt, single-field */
    --modal-w-sm: 400px;   /* small forms */
    --modal-w-md: 520px;   /* default */
    --modal-w-lg: 760px;   /* wide forms / 2-col */
    --modal-w-xl: 940px;   /* tables / import preview */
    /* Modal backdrop — one scrim for every overlay so dimming + blur are tuned
       in one place (the MockartyModal canon value). */
    --modal-backdrop: rgba(0, 0, 0, 0.45);
    --modal-backdrop-blur: 6px;

    /* U1: ONE shared radius for interactive controls (buttons, inputs, selects,
       textareas). Owner 2026-06-16: 6px. Change here → every control updates.
       Radius-only, so no box-model shift. */
    --control-radius: var(--radius-sm); /* 6px */

    /* U1: shared box for text controls (input/select/textarea). Values are the
       CURRENT dominant metrics (form-input height 36px, font-md 15px) so wiring
       a control to these tokens is a zero-shift migration. One knob to align the
       input↔select drift (U0 discrepancy #1) and, later, to retune density. */
    --control-h: 36px;
    --control-font: var(--font-md); /* 15px */
    --control-pad-x: var(--space-3); /* 12px */

    /* Shared panel-header height — keeps the first divider aligned across the
       API-tester's three columns (search / request name bar / response header). */
    --at-header-h: 53px;

    /* U1: icon size scale. 513 hardcoded icon sizes across the tree (~10 values,
       dominant 14px) and no token before. These cover the real range; migrate
       `.icon` width/height onto them wave by wave. --icon-sm == current default. */
    --icon-xs: 12px;
    --icon-sm: 14px;
    --icon-md: 16px;
    --icon-lg: 20px;

    /* Button heights (canonical sizes — use .btn .btn-xs/.btn-sm/.btn-md/.btn-lg).
       Align with height tokens: xs=26px, sm=32px, md=36px, lg=44px. */
    --btn-h-xs: 26px;
    --btn-h-sm: 32px;
    --btn-h-md: 36px;
    --btn-h-lg: 44px;
    --btn-pad-x-xs: var(--space-2);
    --btn-pad-x-sm: var(--space-3);
    --btn-pad-x-md: var(--space-4);
    --btn-pad-x-lg: var(--space-5);

    /* Protocol colors */
    --protocol-http: #4A90E2;
    --protocol-grpc: #00BCD4;
    --protocol-mcp: #9C27B0;
    --protocol-soap: #FF9800;
    --protocol-graphql: #E10098;
    --protocol-sse: #43A047;
    --protocol-socket: #795548;
    --protocol-kafka: #212121;
    --protocol-rabbitmq: #FF6F00;
    /* Deeper NATS blue: the brand #27AAE1 drops the white badge text to
       2.7:1. #14719C keeps the hue and reads 5.4:1 in both themes. */
    --protocol-nats: #14719C;
    --protocol-smtp: #1565C0;
    --protocol-s3: #FF9900;

    /* Disabled state — one opacity for every disabled control, so the muted
       look of a disabled button/input/tab tunes in one place. */
    --disabled-opacity: 0.5;

    /* Icon size scale — inline heroicon/svg sizing. */
    --icon-xs: 12px;
    --icon-sm: 14px;
    --icon-md: 16px;
    --icon-lg: 18px;

    /* Line-height scale — body vs heading vs dense rows tune from here. */
    --leading-tight: 1.3;
    --leading-snug: 1.4;
    --leading-normal: 1.5;
    --leading-relaxed: 1.6;

    /* Letter-spacing scale — chiefly for uppercase eyebrows/labels/badges. */
    --tracking-tight: 0.02em;
    --tracking-normal: 0.03em;
    --tracking-wide: 0.04em;
    --tracking-wider: 0.05em;

    /* Transition */
    --transition-fast: 0.15s ease;
    --transition-base: 0.2s ease;
    --transition-slow: 0.3s ease;

    /* Aliases (used by desktop/settings/stores templates) */
    --accent-primary: var(--accent-color);
    --accent-primary-subtle: rgba(45, 140, 240, 0.08);
    --surface-primary: var(--bg-primary);
    --border-primary: var(--border-color);
    --text-tertiary: var(--text-muted);
    /* Compat aliases for non-canonical token names that several pages
       referenced without a fallback (var(--X) with --X undefined → the
       property silently dropped / lost theme-awareness). Mapped to the real
       theme-aware tokens so they resolve correctly in every theme. Prefer the
       canonical name in new code. (Standalone templates with their own :root —
       pdf-report.html, chaos.html — are fixed locally; see BACKLOG.) */
    --text-color: var(--text-primary);
    --bg-hover: var(--bg-secondary);
    --surface-secondary: var(--bg-secondary);
    --surface-tertiary: var(--bg-tertiary);
    --code-bg: var(--bg-secondary);
    --accent-red: var(--danger-color);
    --border-radius-md: var(--radius-md);
    /* 2026-08-07 phantom-token sweep. Every name below was used across the
       product with a hardcoded fallback and NEVER defined, so the fallback was
       the permanent value — frozen, theme-blind, and in several cases a colour
       from a different palette entirely (#3b82f6 where our accent is #1a73d4,
       Bootstrap's #c82333/#218838 hovers, #eee borders). Mapping them to the
       real tokens makes them follow the theme; the redundant fallbacks were
       stripped at the call sites in the same pass. Prefer the canonical name in
       new code — these exist so the existing call sites resolve correctly. */
    --bg-elevated: var(--bg-secondary);
    --bg-input: var(--bg-primary);
    --bg-muted: var(--bg-tertiary);
    /* Scrim that carries TEXT directly (busy/loading overlays). --modal-backdrop
       (0.45) is tuned for a dialog that sits on its own panel; with white text
       straight on the scrim it composites to #8c8c8c over a light page = 3.35:1.
       0.60 clears 5.74:1 and still reads as a scrim. */
    --bg-overlay: rgba(0, 0, 0, 0.60); /* ui-lint-ok: scrim — alpha over any surface */
    /* Inverse SURFACE (toasts that must stand out against the page in BOTH
       themes). Must NOT alias --text-primary: that flips light in dark theme and
       the toast painted white-on-white (1.29:1). Fixed dark chip, like the
       destructive button's pinned red. */
    --bg-inverse: #1e293b; /* ui-lint-ok: fixed inverse surface — white text must read on it in both themes */
    --text-inverse: #ffffff; /* ui-lint-ok: the ink for --bg-inverse; was only defined in the website bundle */
    --surface-3: var(--bg-tertiary);
    --surface-bg: var(--bg-primary);
    --surface-color: var(--bg-secondary);
    --surface-elevated: var(--bg-secondary);
    --surface-elevated-hover: var(--bg-tertiary);
    --text-on-elevated: var(--text-primary);
    --card-bg: var(--bg-secondary);
    --card-border: var(--border-color);
    --hover-bg: rgba(45, 140, 240, 0.08); /* a visible TINT: --bg-hover resolves to --bg-secondary, which is the palette container's own background — hover became invisible */
    --border-color-subtle: var(--border-color);
    --border-color-light: var(--border-color);
    --border-strong: var(--text-tertiary);
    --input-bg: var(--bg-primary);
    --overlay-bg: var(--modal-backdrop);
    /* Semantic colour aliases — the fallbacks here were foreign palettes. */
    --accent-blue: var(--accent-color);
    --accent-soft: var(--accent-light);
    --accent-subtle: var(--bg-tertiary);
    --accent-strong: var(--accent-hover);
    --accent-color-light: var(--accent-light);
    --accent-color-soft: var(--accent-light);
    --accent-color-bg: var(--accent-light);
    --accent-color-10: var(--accent-light);
    --accent-green: var(--success-color);
    --accent-amber: var(--warning-color);
    --accent-warning-subtle: var(--warning-bg);
    --error: var(--danger-color);
    --status-error: var(--danger-color);
    --danger-hover: var(--severity-critical); /* FILL role (button hover) — a text token here made white-on-hover 2.77:1 in dark */
    --danger-light: var(--danger-bg);
    --danger-border: var(--danger-color);
    --danger-color-secondary: var(--danger-text);
    --success-hover: #047857; /* ui-lint-ok: FILL role (button hover) — needs a DARKER green than --success-color in both themes */
    --success-light: var(--success-bg);
    --success-border: var(--success-color);
    --warning-border: var(--warning-color);
    --warning-color-secondary: var(--warning-text);
    --text-warning: var(--warning-text);
    --alert-warning-bg: var(--warning-bg);
    --alert-warning-text: var(--warning-text);
    --bg-success-soft: var(--success-bg);
    --bg-danger-soft: var(--danger-bg);
    --bg-info-soft: var(--info-bg);
    --info-border: var(--info-color);
    --color-assert: var(--accent-color);
    /* Text ON an accent fill — the same trap --accent-contrast exists for:
       a hardcoded #fff fails AA on the light-blue dark-theme accent. */
    --text-on-accent: var(--accent-contrast);
    --primary-contrast: var(--accent-contrast);
    --btn-primary-color: var(--accent-contrast);
    --button-primary-text: var(--accent-contrast);
    /* Typography / scale aliases (duplicated names for the same scale). */
    --font-sans: var(--font-family-base);
    --font-family-mono: var(--font-mono);
    --code-font: var(--font-mono);
    --font-size-xs: var(--font-xs);
    --font-size-sm: var(--font-sm);
    --font-size-md: var(--font-base);
    --border-radius: var(--radius-md);
    --border-radius-sm: var(--radius-sm);
    /* Purple accent under its numeric-scale name (ui-testing's AI button). The
       #8b5cf6 fallback it used to land on never lightened in dark theme;
       --accent-purple already carries a dark variant. */
    --purple-600: #7c3aed; /* ui-lint-ok: FILL role — carries white text, so it must stay dark in BOTH themes (--accent-purple lightens to #c084fc where white is 2.64:1) */
    --surface2-strong: var(--accent-light);
    /* Fixed app top-bar height — referenced by TCM's sticky offsets. */
    --topbar-height: 60px;
}

/* ────────────────────────────────────────────────
   Light Theme
   ──────────────────────────────────────────────── */

.light {
    --bg-primary: #ffffff;
    --bg-secondary: #f8fafc;
    --bg-tertiary: #e3eaf4;
    --text-primary: #0a1628;
    --text-secondary: #2a3f5c;
    --text-muted: #4f6480; /* a11y: #586f8e cleared white (5.15) but only 4.25:1 on --bg-tertiary (#e3eaf4), where muted counts/badges actually sit; this clears 5.0:1 there and 6.06:1 on white */
    --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%234f6480' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5L8 10.5L12 6.5'/%3E%3C/svg%3E");
    --border-color: #c8d6e8;
    --accent-color: #1a73d4;
    --accent-text: #1565c0; /* a11y: darker accent for TEXT, AA on white + accent tints (see :root note) */
    --accent-hover: #155fb0;
    --accent-light: #e6f0fd;
    --accent-contrast: #ffffff;
    --accent-purple: #9333ea;
    /* Selection (light) — see :root for rationale. */
    --selected-bg: rgba(45, 140, 240, 0.16);
    --selected-bg-2: rgba(45, 140, 240, 0.11);
    --selected-bg-3: rgba(45, 140, 240, 0.07);
    --selected-border: var(--accent-color);
    --selected-text: var(--text-primary);
    --success-color: #10b981;
    --danger-color: #ef4444;
    --warning-color: #f59e0b;
    --info-color: #06b6d4;
    --warning-bg: #fef3c7;
    --warning-fg: #92400e;
    --shadow-sm: none;
    --shadow: none;
    --shadow-hover: none;
    --shadow-lg: none;
    --hover-overlay: rgba(0, 0, 0, 0.04);
    --hover-overlay-strong: rgba(0, 0, 0, 0.07);
    --protocol-kafka: #212121;
    --protocol-smtp: #1565C0;
    --accent-primary: var(--accent-color);
    --accent-primary-subtle: rgba(45, 140, 240, 0.08);
    --surface-primary: var(--bg-primary);
    --border-primary: var(--border-color);
    --text-tertiary: var(--text-muted);
}

/* ────────────────────────────────────────────────
   Dark Theme
   ──────────────────────────────────────────────── */

.dark {
    /* Tell the UA we're a dark surface so NATIVE controls (unchecked
       checkbox/radio boxes, select dropdown lists, date pickers, autofilled
       inputs, scrollbars) render their dark variants. Without this the
       unchecked checkbox box and the browser-rendered <option> list stay
       WHITE in dark theme — the recurring "белый чекбокс/дропдаун" class of
       bugs. accent-color (components.css) still paints the checked fill. */
    color-scheme: dark;
    --bg-primary: #080d18;
    --bg-secondary: #0e1524;
    --bg-tertiary: #151e30;
    --text-primary: #d8e4f4;
    --text-secondary: #7e9abb;
    /* #4d6a8a was ~3.2:1 on the dark base — below WCAG AA (4.5:1), the root of
       "muted text is hard to read in dark theme" app-wide. #6b86a6 lifts it to
       ~5:1 while staying clearly de-emphasised vs --text-secondary. Dark-only;
       no layout impact. */
    --text-muted: #7790b0;
    /* Chevron for native selects. A stroked chevron matching the heroicon set,
       not the browser's (or our old solid triangle's) shape — the control is the
       most repeated one in the product, so a foreign glyph reads as "unstyled"
       everywhere at once (owner 2026-08-13: «кнопка шеврона какая-то странная»).
       Defined per theme because a data-URI cannot read a token. */
    --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%237790b0' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5L8 10.5L12 6.5'/%3E%3C/svg%3E");
    --border-color: #1a2740;
    /* Auto-hide scrollbar thumb (dark). */
    --scrollbar-thumb: rgba(107, 134, 166, 0.5);
    --scrollbar-thumb-hover: rgba(107, 134, 166, 0.82);
    --accent-color: #64b5ff;
    --accent-text: var(--accent-color); /* dark: the light-blue accent is already AA on the dark canvas — no darkening */
    --accent-hover: #2d8cf0;
    --accent-light: rgba(45, 140, 240, 0.12);
    /* Dark accent (#64b5ff) is a LIGHT blue — white text on it is only 2.2:1.
       Pair accent fills with near-black text instead (~8.5:1 on #64b5ff,
       ~5.5:1 on the #2d8cf0 hover). Owner-approved 2026-06-17. */
    --accent-contrast: #0d1424;
    --accent-purple: #c084fc;
    /* Selection (dark) — brighter accent wash so the wash is visible on the
       dark canvas while text/badges stay readable. Depth darker→lighter. */
    --selected-bg: rgba(100, 181, 255, 0.22);
    --selected-bg-2: rgba(100, 181, 255, 0.15);
    --selected-bg-3: rgba(100, 181, 255, 0.09);
    --selected-border: var(--accent-color);
    --selected-text: var(--text-primary);
    --success-color: #34d399;
    --danger-color: #f87171;
    /* Dark status TEXT = the bright --*-color values (all ~10:1 on the dark
       base → AA), so routing text from --*-color to --*-text is a no-op on
       dark; only light theme darkens (where the raw colours failed AA). */
    --danger-text: #f87171;
    --warning-color: #fbbf24;
    --info-color: #22d3ee;
    /* Severity — lighter on the dark canvas for legibility; tints a touch stronger. */
    --severity-critical: #f87171;
    --on-critical-fill: var(--on-bright-fill);
    /* Dark theme: the severity ramp already lightens, so those same values ARE
       the readable text colours on the dark canvas. */
    --severity-critical-text: #f87171;
    --severity-high-text: #fb923c;
    --severity-medium-text: #fbbf24;
    --severity-low-text: #22d3ee;
    --severity-info-text: #94a3b8;
    --severity-high: #fb923c;
    --severity-medium: #fbbf24;
    --severity-low: #22d3ee;
    --severity-info: #94a3b8;
    --severity-critical-bg: rgba(248, 113, 113, 0.16);
    --severity-high-bg: rgba(251, 146, 60, 0.16);
    --severity-medium-bg: rgba(251, 191, 36, 0.16);
    --severity-low-bg: rgba(34, 211, 238, 0.16);
    --severity-info-bg: rgba(148, 163, 184, 0.16);
    --success-text: #34d399;
    --warning-text: #fbbf24;
    --info-text: #22d3ee;
    --warning-bg: #422006;
    --warning-fg: #fde68a;
    --shadow-sm: none;
    --shadow: none;
    --shadow-hover: none;
    --shadow-lg: none;
    /* Dark theme: shadows must be deeper (cast against a near-black bg) and the
       focus ring uses the lighter dark-accent. */
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.40), 0 1px 3px rgba(0, 0, 0, 0.30);
    --shadow-2: 0 2px 8px rgba(0, 0, 0, 0.45);
    --shadow-3: 0 6px 18px rgba(0, 0, 0, 0.50);
    --shadow-4: 0 12px 32px rgba(0, 0, 0, 0.55);
    --shadow-5: 0 24px 60px rgba(0, 0, 0, 0.65);
    --focus-ring: 0 0 0 3px rgba(100, 181, 255, 0.30);
    --focus-ring-danger: 0 0 0 3px rgba(248, 113, 113, 0.30);
    --hover-overlay: rgba(255, 255, 255, 0.04);
    --hover-overlay-strong: rgba(255, 255, 255, 0.07);
    --protocol-kafka: #424242;
    /* Keep the deeper SMTP blue in dark too: the lighter #1E88E5 dropped the
       white badge text to 3.7:1 (the badge keeps white text). #1565C0 = 5.8:1. */
    --protocol-smtp: #1565C0;
    --accent-primary: var(--accent-color);
    --accent-primary-subtle: rgba(100, 181, 255, 0.12);
    --surface-primary: var(--bg-primary);
    --border-primary: var(--border-color);
    --text-tertiary: var(--text-muted);
}
