:root {
  /* Every theme already declares --theme-spec-scheme and nothing ever read it,
     so the browser picked native control colours from the OS instead of from
     the theme the reader chose. That is why the inactive severity buttons on
     /quality rendered as grey-on-grey: they fell through to the UA button face,
     resolved against a dark OS while the app was in a light theme. */
  color-scheme: var(--theme-spec-scheme, light);
  /* --muted at 11-13px on a 12% accent or domain tint measured 4.38:1; the
     tints are what the badges and chips use. Nudged toward the text end. */
  --bg: #F5F5F7; --card: #FFFFFF; --text: #1D1D1F; --muted: #5E5E63;
  --link: #0055B3; --accent: #0A84FF; --border: rgba(0,0,0,.08);
  --hsan: #0A84FF; --bcm: #30D158;
  --danger: #D70015; --warning: #9A4D00; --success: #147A3A;
  /* #FF9F0A is the dark-surface amber and measured 2.06:1 on --card here.
     This is the same hue taken down to an AA-safe level for light pages;
     the dark themes keep the bright one. */
  --compare: #A85700; --risk: #A85700; --ask: #BF5AF2; --ask-accent-strong: #A855F7;
  /* --accent and --ask identify things; as 11-14px text they measured
     3.07-3.65:1 on the surfaces this app puts them on. Fills and borders keep
     the brand value, text takes these. */
  --accent-text: #0055B3; --ask-text: #8E2BC4;
  /* Filled controls take the *text* variant as their fill and this as their
     label: white on #0A84FF measured 3.65:1 on light and 2.47:1 on midnight,
     and white cannot reach AA on that blue. Light themes get the deeper fill
     and keep white; dark themes get the light fill and a near-black label. */
  --on-accent-solid: #FFFFFF;
  --on-accent: #FFFFFF; --text-on-accent: var(--on-accent);
  --space-0: 0;
  --space-1: 2px;
  --space-2: 4px;
  --space-3: 8px;
  --space-4: 12px;
  --space-5: 16px;
  --space-6: 20px;
  --space-7: 24px;
  --space-8: 32px;
  --space-9: 40px;
  --text-xs: 12px;
  --text-sm: 14px;
  --text-caption: 11px;
  --text-label: 13px;
  --text-body: 17px;
  --text-title-sm: 16px;
  --text-title-md: 22px;
  --text-h3: 22px;
  --text-h2: 28px;
  --text-h1: 34px;
  --text-metric-sm: 24px;
  --text-metric-md: 30px;
  --line-compact: 1.16;
  --line-title: 1.1;
  --line-normal: 1.35;
  --line-body: 1.75;
  --font-weight-medium: 500;
  --font-weight-semibold: 650;
  --font-weight-bold: 700;
  --font-weight-strong: 750;
  --font-weight-heavy: 800;
  --tracking-normal: 0;
  --tracking-label: var(--tracking-normal);
  --radius-sm: 6px;
  --radius: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-pill: 999px;
  --sidebar-w: 260px;
  --theme-spec-id: light;
  --theme-spec-name: "Light";
  --theme-spec-scheme: light;
  --surface-app: var(--bg);
  --surface-card: var(--card);
  --surface-well: color-mix(in srgb, var(--bg) 82%, var(--card));
  --surface-elevated: color-mix(in srgb, var(--card) 92%, transparent);
  --surface-hover: color-mix(in srgb, var(--accent) 8%, transparent);
  --apple-material: color-mix(in srgb, var(--card) 82%, transparent);
  --apple-material-strong: color-mix(in srgb, var(--card) 94%, var(--bg));
  --coverage-track: var(--border);
  --matrix-cell-bg: color-mix(in srgb, var(--card) 88%, var(--bg));
  --empty-state-border: var(--border);
  --text-primary: var(--text);
  --text-secondary: var(--muted);
  /* --muted is tuned against --card and a 12% tint. A backdrop-filtered panel
     over --overlay-scrim-strong is neither: the Spotlight paints #C9C9C9 on
     light and #BCBCBD on graphite, where --muted measures 3.89 and 3.30.
     Derived, not hand-picked, so it flips direction per theme the way P106's
     domain ramp does — darker than --muted on a light theme, lighter on a
     dark one, because it mixes toward whatever --text the theme declares.
     70% cleared AA but left graphite at 4.62 across eight pages; 60% puts the
     floor at 5.17 graphite / 5.85 light, measured from painted pixels. */
  --muted-on-glass: color-mix(in srgb, var(--muted) 60%, var(--text));
  --hairline: var(--border);
  --overlay-scrim: rgba(0,0,0,.28);
  --overlay-scrim-strong: rgba(0,0,0,.32);
  --mark-bg: color-mix(in srgb, var(--accent) 25%, transparent);
  --mark-bg-soft: color-mix(in srgb, var(--accent) 18%, transparent);
  --mark-bg-subtle: color-mix(in srgb, var(--accent) 14%, transparent);
  --chart-ring-bg: var(--border);
  --chart-bar-bg: var(--border);
  --chart-line: var(--success);
  --chart-line-alt: var(--accent);
  --chart-fill: color-mix(in srgb, var(--accent) 14%, transparent);
  --chart-critical: var(--danger);
  --chart-warning: var(--warning);
  --chart-ok: var(--success);
  --diagram-l1: #2F80ED;
  --diagram-l1-fill: #DCEBFF;
  --diagram-l1-text: #123A69;
  --diagram-l2: #8B5CF6;
  --diagram-l2-fill: #EDE7FF;
  --diagram-l2-text: #3E256F;
  --diagram-l3: #13A7A5;
  --diagram-l3-fill: #DDF7F5;
  --diagram-l3-text: #0F4B4A;
  --diagram-l4: #E99A2C;
  --diagram-l4-fill: #FFF1D6;
  --diagram-l4-text: #5B390D;
  --diagram-l5: #E75D8A;
  /* Referenced by --domain-distributed-systems since it was written and never
     defined, so that domain rendered with no colour anywhere. */
  --diagram-l6: #2E9E5B;
  --diagram-l5-fill: #FFE4EE;
  --diagram-l5-text: #6A1F3A;
  --diagram-line: #697386;
  --diagram-surface: #F8FAFC;
  --domain-network: var(--hsan);
  --domain-ai: var(--ask);
  --domain-languages: var(--diagram-l3);
  --domain-os: var(--diagram-l4);
  --domain-kernel: var(--diagram-l5);
  --domain-software-engineering: var(--diagram-l2);
  --domain-security-engineering: var(--danger);
  --domain-data-systems: var(--diagram-l1);
  --domain-distributed-systems: var(--diagram-l6);
  --domain-cloud-native: var(--success);
  --domain-computer-science-foundations: #2563EB;
  --domain-mathematics-for-computing: #4338CA;
  --domain-frontend-engineering: var(--link);
  --domain-ux-engineering: #7C3AED;
  --domain-backend-engineering: #0F766E;
  --domain-developer-productivity: #B45309;
  --domain-documentation-engineering: #0E7490;
  --domain-product-engineering: #A21CAF;
  --domain-mobile-engineering: #5E5CE6;
  --domain-reliability-engineering: var(--chart-warning);
  /* Domain colours are picked to identify a domain on a tile or a dot. Used as
     11-13px text on a 12% tint of themselves they measured 1.0-4.4:1 — 13 of
     20 failed on light themes and 18 of 20 on midnight. Text takes the same
     hue mixed most of the way to the theme's own text colour, which moves it toward
     the contrasting end on every theme and keeps the domain recognisable.
     Written inline at each use because a token declared here would resolve
     against :root's --domain-color, not the element's. */
  --domain-color-text-mix: 45%;
  --domain-neutral: var(--muted);
  --domain-accent: var(--domain-network);
  --focus-ring: color-mix(in srgb, var(--accent) 72%, white);
  --focus-ring-shadow: color-mix(in srgb, var(--accent) 24%, transparent);
  --focus-ring-width: 2px;
  --focus-ring-offset: 2px;
  --touch-target: 44px;
  --motion-duration-instant: 80ms;
  --motion-duration-fast: 160ms;
  --motion-duration-standard: 220ms;
  --motion-duration-slow: 360ms;
  --motion-ease-standard: cubic-bezier(.2, 0, .2, 1);
  --motion-ease-entrance: cubic-bezier(.16, 1, .3, 1);
  --motion-ease-exit: cubic-bezier(.7, 0, .84, 0);
  --motion-press-scale: .985;
  --shadow-hairline: 0 1px 2px rgba(0,0,0,.035);
  --shadow-subtle: 0 1px 2px rgba(0,0,0,.04);
  --shadow-soft: 0 1px 2px rgba(0,0,0,.05);
  --shadow-brand: 0 1px 2px rgba(0,0,0,.12);
  --shadow-card: 0 8px 24px rgba(0,0,0,.07);
  --shadow-seg: 0 1px 3px rgba(0,0,0,.12);
  --shadow-hover-card: 0 6px 20px rgba(0,0,0,.08);
  --shadow-modal: 0 24px 80px rgba(0,0,0,.28);
  --shadow-mobile-drawer: 0 0 40px rgba(0,0,0,.3);
  --shadow-drawer-panel: 0 18px 60px rgba(0,0,0,.24);
  --shadow-sticky-bar: 0 12px 36px rgba(0,0,0,.16);
  --shadow-sticky-composer: 0 -12px 28px rgba(0,0,0,.12);
  --shadow-graph-node: drop-shadow(0 6px 18px rgba(0,0,0,.08));
  --shadow-popover: 0 18px 48px rgba(0,0,0,.18);
  --shadow-spotlight: 0 28px 90px rgba(0,0,0,.28);
  --dur-fast: var(--motion-duration-fast) var(--motion-ease-standard);
  --motion-filter: filter var(--motion-duration-fast) var(--motion-ease-standard);
  --motion-drawer: transform var(--motion-duration-standard) var(--motion-ease-standard);
  --motion-ring: stroke-dasharray var(--motion-duration-slow) var(--motion-ease-entrance);
  --graph-grid-base: #ECEFF3;
  /* ---- 动效令牌（符号动效） ---- */
  --motion-ease-spring: cubic-bezier(.34, 1.56, .64, 1);
  --motion-duration-symbol: 560ms;
  /* ---- 动态玻璃令牌（glass-system.md §1）—— 三档厚度 + 材质调制 + 边缘光 ---- */
  --glass-blur-thin: 8px;
  --glass-blur: 18px;
  --glass-blur-thick: 28px;
  --glass-sat: 180%;
  --glass-bright: 1.05;
  --glass-contrast: 1.02;
  --glass-tint: 62%;
  --glass-sheen: rgba(255,255,255,.18);
  --glass-sheen-sweep: rgba(255,255,255,.34);
  --tile-rim: rgba(255,255,255,.3);
  --glass-rim-light: rgba(255,255,255,.30);
  --glass-rim-dark: rgba(0,0,0,.06);
  --glass-edge-grad: linear-gradient(135deg,
      rgba(255,255,255,.55), rgba(255,255,255,.05) 42%, rgba(0,0,0,.08));
}

:root[data-theme="system"],
:root[data-theme="light"] {
  --theme-spec-id: light;
  --theme-spec-name: "Light";
  --theme-spec-scheme: light;
}

@media (prefers-color-scheme: dark) {
  /* Graphite is a light theme that does not declare every token, so this block
     was leaking dark values into it whenever the OS was dark — visibly once
     P105 put the semantic ramp here (graphite rendered --danger #FF6B6B on
     #ECEFF3) and again once P108 added --on-accent-solid. */
  :root:not([data-theme="light"]):not([data-theme="graphite"]) {
    --bg: #1C1C1E; --card: #2C2C2E; --text: #F5F5F7; --muted: #A8A8AE;
    --link: #4DA3FF; --border: rgba(255,255,255,.1);
    --accent-text: #6FB7FF; --ask-text: #D9A6FF;
    --on-accent-solid: #1C1C1E;
    /* :root darkens these for white; on #2C2C2E they were 2.6-2.9:1. */
    --danger: #FF6B6B; --warning: #FFB340; --success: #30D158;
    --compare: #FF9F0A; --risk: #FF9F0A;
    --theme-spec-id: dark;
    --theme-spec-name: "System Dark";
    --theme-spec-scheme: dark;
    --glass-sat: 150%; --glass-bright: 1.12; --glass-tint: 58%;
    --glass-rim-light: rgba(255,255,255,.16); --glass-rim-dark: rgba(0,0,0,.24);
    --glass-edge-grad: linear-gradient(135deg, rgba(255,255,255,.28), rgba(255,255,255,.03) 44%, rgba(0,0,0,.30));
    --surface-well: color-mix(in srgb, var(--bg) 82%, black);
    --surface-elevated: color-mix(in srgb, var(--card) 88%, transparent);
    --surface-hover: color-mix(in srgb, var(--accent) 14%, transparent);
    --chart-ring-bg: var(--border);
    --chart-bar-bg: var(--border);
    --diagram-l1-fill: #17365F;
    --diagram-l1-text: #D8E9FF;
    --diagram-l2-fill: #35245F;
    --diagram-l2-text: #EFE7FF;
    --diagram-l3-fill: #143F42;
    --diagram-l3-text: #D9FAF7;
    --diagram-l4-fill: #4D3414;
    --diagram-l4-text: #FFF0D2;
    --diagram-l5-fill: #5A2037;
    --diagram-l5-text: #FFE2EC;
    --diagram-line: #8792A5;
    --diagram-surface: #202124;
    --focus-ring: color-mix(in srgb, var(--accent) 78%, white);
  }
}

:root[data-theme="dark"] {
  --bg: #1C1C1E; --card: #2C2C2E; --text: #F5F5F7; --muted: #A8A8AE;
  --link: #4DA3FF; --border: rgba(255,255,255,.1);
  --accent-text: #6FB7FF; --ask-text: #D9A6FF;
  --on-accent-solid: #1C1C1E;
  --danger: #FF6B6B; --warning: #FFB340; --success: #30D158;
  --compare: #FF9F0A; --risk: #FF9F0A;
  --theme-spec-id: dark;
  --theme-spec-name: "Dark";
  --theme-spec-scheme: dark;
  --glass-sat: 150%; --glass-bright: 1.12; --glass-tint: 58%;
  --glass-rim-light: rgba(255,255,255,.16); --glass-rim-dark: rgba(0,0,0,.24);
  --glass-edge-grad: linear-gradient(135deg, rgba(255,255,255,.28), rgba(255,255,255,.03) 44%, rgba(0,0,0,.30));
  --surface-well: color-mix(in srgb, var(--bg) 82%, black);
  --surface-elevated: color-mix(in srgb, var(--card) 88%, transparent);
  --surface-hover: color-mix(in srgb, var(--accent) 14%, transparent);
  --chart-ring-bg: var(--border);
  --chart-bar-bg: var(--border);
  --diagram-l1-fill: #17365F;
  --diagram-l1-text: #D8E9FF;
  --diagram-l2-fill: #35245F;
  --diagram-l2-text: #EFE7FF;
  --diagram-l3-fill: #143F42;
  --diagram-l3-text: #D9FAF7;
  --diagram-l4-fill: #4D3414;
  --diagram-l4-text: #FFF0D2;
  --diagram-l5-fill: #5A2037;
  --diagram-l5-text: #FFE2EC;
  --diagram-line: #8792A5;
  --diagram-surface: #202124;
  --focus-ring: color-mix(in srgb, var(--accent) 78%, white);
}

:root[data-theme="graphite"] {
  --bg: #ECEFF3; --card: rgba(255,255,255,.82); --text: #17191D;
  --muted: #5A616B; --link: #0B57C7; --accent: #1F6FEB;
  --accent-text: #0055B3; --ask-text: #8E2BC4;
  --hsan: #1F6FEB; --bcm: #34C759; --ask: #8E44AD; --ask-accent-strong: #9B59B6;
  --border: rgba(26,31,38,.10); --success: #147A3A; --warning: #8C4F00;
  --theme-spec-id: graphite;
  --theme-spec-name: "Graphite";
  --theme-spec-scheme: light;
  --glass-sat: 170%; --glass-bright: 1.04; --glass-tint: 66%;
  --glass-rim-light: rgba(255,255,255,.42); --glass-rim-dark: rgba(20,26,36,.08);
  --surface-well: #E2E6EC;
  --surface-elevated: rgba(255,255,255,.74);
  --surface-hover: color-mix(in srgb, var(--accent) 10%, transparent);
  --chart-line: #218B4B;
  --chart-line-alt: #1F6FEB;
  --focus-ring: color-mix(in srgb, var(--accent) 68%, white);
}

:root[data-theme="midnight"] {
  --bg: #080B12; --card: rgba(24,28,38,.86); --text: #F5F7FB;
  --muted: #BDBDC4; --link: #7FB6FF; --accent: #64A7FF;
  --accent-text: #8AC5FF; --ask-text: #D9A6FF;
  --on-accent-solid: #080B12;
  --hsan: #64A7FF; --bcm: #4CE070; --ask: #D18BFF; --ask-accent-strong: #B56BFF;
  --border: rgba(255,255,255,.13); --success: #4CE070; --warning: #FFB340;
  --danger: #FF8A8A; --compare: #FF9F0A; --risk: #FF9F0A;
  --theme-spec-id: midnight;
  --theme-spec-name: "Midnight";
  --theme-spec-scheme: dark;
  --glass-sat: 140%; --glass-bright: 1.15; --glass-tint: 60%;
  --glass-rim-light: rgba(120,160,255,.20); --glass-rim-dark: rgba(0,0,0,.34);
  --glass-edge-grad: linear-gradient(135deg, rgba(140,175,255,.30), rgba(140,175,255,.04) 46%, rgba(0,0,0,.36));
  --surface-well: #101723;
  --surface-elevated: rgba(28,35,50,.78);
  --surface-hover: color-mix(in srgb, var(--accent) 18%, transparent);
  --chart-line: #4CE070;
  --chart-line-alt: #64A7FF;
  --chart-fill: color-mix(in srgb, var(--accent) 22%, transparent);
  --focus-ring: color-mix(in srgb, var(--accent) 78%, white);
}

@media (max-width: 760px) {
  :root {
    --text-h1: 28px;
  }
}
