Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
e934a3ad8This file is pinned to the selected release. Download the original to open it in your design tool.
@import "tailwindcss";
@source "../";
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
/*
* The radius scale is 3 / 4 / 6 / 9 / 999 (DESIGN.md section 13). shadcn derives
* rounded-sm/md/lg/xl from one `--radius: 0.625rem`, which produced 6/8/10/14px:
* every primitive and every legacy component was a step or two off the product's
* scale, so an 8px control sitting on a 9px plate read as a different system.
* Pinning the derived steps here fixes all of them at the root.
*/
--radius-sm: 3px;
--radius-md: 4px;
--radius-lg: 6px;
--radius-xl: 9px;
--radius-2xl: 9px;
--radius-3xl: 9px;
}
/* ─── Light Theme Variables ──────────────────────────────────── */
:root {
/* ── Shell tokens (DESIGN.md section 11) ────────────────────────────────────
The floating shell must render in both themes. Everything the module system
uses resolves here so no screen invents a colour (section 22). */
/* the workspace field: one gradient per theme, never a hardcoded dark one */
--shell-canvas-grad: radial-gradient(ellipse 70% 62% at 50% 46%, #FBFAF7 0%, #F5F2ED 46%, #F0EDE6 78%, #EAE6DE 100%);
--shell-grid: rgba(13, 13, 14, 0.045);
--shell-tick: rgba(13, 13, 14, 0.22);
--shell-scrim: rgba(28, 24, 20, 0.35);
--shell-cube-border: rgba(13, 13, 14, 0.18);
--shell-cube-face-a: rgba(255, 255, 255, 0.97);
--shell-cube-face-b: rgba(246, 243, 238, 0.97);
--shell-cube-face-c: rgba(236, 231, 223, 0.97);
--shell-cube-shadow: inset 0 0 16px rgba(0, 0, 0, 0.06), inset 0 1px 0 rgba(255, 255, 255, 0.90);
/* Machined material for the 正在生成 part (DESIGN.md section 17). Same grey the
viewport uses for the part, so the loader and the result read as one material. */
--shell-part-face: #9AA5B1;
--shell-part-edge: #7C8794;
--shell-part-shadow: rgba(28, 24, 20, 0.20);
--shell-canvas: #F4F2EE;
/* The field the part sits on: one step deeper than the canvas, from the token map
(DESIGN.md colors.canvas-deep-dark) rather than the old cool #F4F6F8. */
--shell-canvas-deep: #EDE9E1;
/* Solid equivalents of the field grid (base + 5.5% ink / + 11%), because the WebGL
grid helper takes flat colours and cannot read an rgba token. */
--shell-field-grid: #E0DCD5;
--shell-field-grid-strong: #D5D2CB;
/* Scrollbars are a browser surface the shell owns (DESIGN.md section 22). */
--shell-scroll-thumb: #C7C0B4;
--shell-scroll-thumb-hover: #AAA294;
--shell-module: rgba(255, 255, 255, 0.92);
--shell-module-solid: #FFFFFF;
--shell-inner: #EDF1F5;
--shell-raise: #E4E1DA;
--shell-well: rgba(13, 13, 14, 0.045);
--shell-hover: rgba(13, 13, 14, 0.05);
--shell-border: rgba(13, 13, 14, 0.10);
--shell-border-strong: rgba(13, 13, 14, 0.20);
--shell-hairline: rgba(13, 13, 14, 0.06);
--shell-text: #1B1917;
--shell-text-muted: #5C564D;
/* Light theme ladder. DESIGN.md section 21 requires sufficient contrast "including
the 9px engraved labels and dim values", so every step clears 4.5:1 against the
module plate and the canvas: label 5.2:1, dim and placeholder 4.6:1. The previous
label/dim pair measured 4.24:1 and 3.11:1 and failed its own rule. */
--shell-text-label: #6B6459;
--shell-text-dim: #7C7468;
--shell-placeholder: #7C7468;
--shell-signal: #E0512A;
--shell-signal-soft: #B8471F;
--shell-ok: #2E9E5B;
--shell-warn: #B5821A;
--shell-fail: #C0392B;
--shell-shadow: 0 14px 30px -10px rgba(28, 24, 20, 0.16), 0 3px 8px rgba(28, 24, 20, 0.08);
--shell-shadow-lift: 0 22px 46px -12px rgba(28, 24, 20, 0.22), 0 0 0 1px rgba(224, 81, 42, 0.40);
--radius: 0.625rem;
/*
* shadcn's base tokens are the third alias layer (after --shell-* and --cad-*).
* Every primitive - Dialog, Sheet, Button, Input, Select, Tabs, Switch, Tooltip,
* DropdownMenu, Command, the toaster - reads its surface, text and border from
* here. Left on the stock oklch greys they render cool against a warm shell, and
* any child that does not set its own colour inherits a near-white that is not in
* the palette. They now resolve to the shell, in both themes (DESIGN.md section 22).
*/
--background: var(--shell-module-solid);
--foreground: var(--shell-text);
--card: var(--shell-module-solid);
--card-foreground: var(--shell-text);
--popover: var(--shell-module-solid);
--popover-foreground: var(--shell-text);
--primary: var(--shell-signal);
--primary-foreground: #1A0F08;
--secondary: var(--shell-well);
--secondary-foreground: var(--shell-text);
--muted: var(--shell-well);
--muted-foreground: var(--shell-text-label);
--accent: color-mix(in srgb, var(--shell-signal) 12%, transparent);
--accent-foreground: var(--shell-signal-soft);
--destructive: var(--shell-fail);
--border: var(--shell-border);
--input: var(--shell-border);
--ring: #E0512A;
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
/* AgentSCAD custom - Light (Direction 2: Shapr3D x VisionOS Precision Metrology) */
--app-bg: #F4F6F8;
--app-surface: #FFFFFF;
--app-surface-raised: #EDF1F5;
--app-surface-50: rgba(244, 246, 248, 0.5);
--app-surface-hover: rgba(17, 24, 32, 0.04);
--app-surface-95: rgba(244, 246, 248, 0.95);
--app-border: #D5DDE6;
--app-border-subtle: rgba(213, 221, 230, 0.6);
--app-border-strong: #B8C5D3;
--app-text-primary: #111820;
--app-text-secondary: #3B4B5B;
--app-text-muted: #607080;
--app-text-dim: #8FA0B2;
--app-text-bright: #05080C;
--app-scrollbar-thumb: #B8C5D3;
--app-scrollbar-thumb-hover: #8FA0B2;
--app-scrollbar-track: transparent;
--app-focus-ring: rgba(224, 81, 42, 0.45);
--app-interactive-hover: rgba(217, 119, 6, 0.08);
--app-shadow-sm: 0 1px 2px rgba(17, 24, 32, 0.04);
--app-shadow-md: 0 4px 12px rgba(17, 24, 32, 0.06);
--app-keyboard-bg: #EDF1F5;
--app-keyboard-border: #D5DDE6;
--app-keyboard-text: #607080;
--app-skeleton-bg: rgba(17, 24, 32, 0.06);
--app-notification-hover: rgba(17, 24, 32, 0.04);
--app-footer-separator: rgba(17, 24, 32, 0.10);
--app-footer-tooltip-bg: #FFFFFF;
--app-footer-tooltip-border: #D5DDE6;
--app-footer-tooltip-text: #3B4B5B;
--app-gradient-separator: linear-gradient(90deg, transparent, #D5DDE6, transparent);
--app-card-hover-shadow: 0 2px 8px rgba(17, 24, 32, 0.05), 0 0 0 1px #D5DDE6;
--app-pipeline-bg: rgba(17, 24, 32, 0.04);
--app-activity-hover: rgba(17, 24, 32, 0.03);
--app-selected-bg: rgba(217, 119, 6, 0.08);
--app-code-copy-hover: rgba(17, 24, 32, 0.06);
--app-empty-bg: rgba(17, 24, 32, 0.03);
--app-accent: #D97706;
--app-accent-hover: #B45309;
--app-accent-bg: rgba(217, 119, 6, 0.08);
--app-accent-border: rgba(217, 119, 6, 0.25);
--app-accent-text: #B45309;
--app-accent-text-secondary: rgba(180, 83, 9, 0.85);
--app-danger: #E25B5B;
--app-danger-bg: rgba(226, 91, 91, 0.08);
--app-warning: #D97706;
--app-warning-bg: rgba(217, 119, 6, 0.08);
--app-success: #10B981;
--app-success-bg: rgba(16, 185, 129, 0.08);
--app-info: #D97706;
--app-info-bg: rgba(217, 119, 6, 0.08);
/* Dual-Theme Status Semantics (WCAG AA >= 4.5:1 compliant in light mode) */
--app-status-success-text: #059669;
--app-status-success-bg: rgba(16, 185, 129, 0.12);
--app-status-success-border: rgba(16, 185, 129, 0.28);
--app-status-warning-text: #B45309;
--app-status-warning-bg: rgba(217, 119, 6, 0.12);
--app-status-warning-border: rgba(217, 119, 6, 0.28);
--app-status-danger-text: #C73838;
--app-status-danger-bg: rgba(226, 91, 91, 0.12);
--app-status-danger-border: rgba(226, 91, 91, 0.28);
--app-status-info-text: #B45309;
--app-status-info-bg: rgba(217, 119, 6, 0.10);
--app-status-info-border: rgba(217, 119, 6, 0.25);
--app-header-bg: rgba(244, 246, 248, 0.92);
--app-dialog-overlay: rgba(17, 24, 32, 0.35);
--app-dialog-bg: #FFFFFF;
--app-code-bg: #EDF1F5;
--app-code-border: #D5DDE6;
--app-tag-bg: rgba(17, 24, 32, 0.05);
--app-tag-border: #D5DDE6;
--app-batch-bar-bg: rgba(217, 119, 6, 0.08);
--app-batch-bar-text: #B45309;
--app-input-bg: #FFFFFF;
--app-input-border: #D5DDE6;
--app-input-focus-border: rgba(217, 119, 6, 0.45);
--app-hover-subtle: rgba(17, 24, 32, 0.04);
--app-border-separator: #D5DDE6;
--app-state-neutral-bg: rgba(96, 112, 128, 0.12);
--app-state-neutral-text: #607080;
--app-state-neutral-dot: #8FA0B2;
--app-state-neutral-border: rgba(96, 112, 128, 0.2);
/*
* The `--cad-*` layer is an ALIAS, not a second palette (DESIGN.md section 22, token
* ownership). Every legacy panel still names these tokens - the provider form, the
* theme panel, the SCAD editor, the stats and compare surfaces, the activity feed -
* and while they held the old cool blue-grey family those panels could never match
* the shell no matter how they were styled. Re-pointing the layer converts all of
* them at once: values now resolve to the shell tokens, in both themes.
*
* DESIGN.md section 11: one expressive accent (signal), no separate measurement
* hue - dimensional values carry meaning through mono type and units.
*/
--cad-bg: var(--shell-module-solid);
--cad-background: var(--shell-module-solid);
--cad-surface: var(--shell-well);
--cad-surface-raised: var(--shell-inner);
--cad-surface-muted: var(--shell-well);
--cad-border: var(--shell-border);
--cad-border-subtle: var(--shell-hairline);
--cad-border-strong: var(--shell-border-strong);
--cad-text: var(--shell-text);
--cad-text-primary: var(--shell-text);
--cad-text-secondary: var(--shell-text-muted);
--cad-text-muted: var(--shell-text-label);
--cad-text-dim: var(--shell-text-dim);
--cad-accent: var(--shell-signal);
--cad-accent-hover: var(--shell-signal);
--cad-accent-soft: color-mix(in srgb, var(--shell-signal) 10%, transparent);
--cad-accent-border: color-mix(in srgb, var(--shell-signal) 35%, transparent);
--cad-accent-bg: color-mix(in srgb, var(--shell-signal) 12%, transparent);
--cad-accent-text: var(--shell-signal-soft);
--cad-grid: var(--shell-grid);
--cad-measure: var(--shell-text);
--cad-success: var(--shell-ok);
--cad-warning: var(--shell-warn);
--cad-danger: var(--shell-fail);
--cad-input-bg: var(--shell-module-solid);
}
/* ─── Dark Theme Variables (Direction 2: Shapr3D x VisionOS Spatial Canvas) ─── */
.dark {
/* the workspace field: one gradient per theme, never a hardcoded dark one */
--shell-canvas-grad: radial-gradient(ellipse 70% 62% at 50% 46%, #322E29 0%, #242220 46%, #1A1917 78%, #141312 100%);
--shell-grid: rgba(255, 255, 255, 0.032);
--shell-tick: rgba(255, 255, 255, 0.22);
--shell-scrim: rgba(10, 9, 8, 0.62);
--shell-cube-border: rgba(255, 255, 255, 0.16);
--shell-cube-face-a: rgba(84, 78, 68, 0.95);
--shell-cube-face-b: rgba(72, 66, 57, 0.95);
--shell-cube-face-c: rgba(58, 54, 47, 0.95);
--shell-cube-shadow: inset 0 0 20px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.10);
/* Machined material for the 正在生成 part (DESIGN.md section 17). */
--shell-part-face: #9BA6B2;
--shell-part-edge: #5F6874;
/* A contact shadow, not a stain: on the dark canvas the vignette already grounds
the part, so this only needs to be a hint of weight. */
--shell-part-shadow: rgba(0, 0, 0, 0.34);
--shell-canvas: #141312;
/* Field tokens: same two steps as the light theme, warm not cool. */
--shell-canvas-deep: #0E0D0C;
--shell-field-grid: #191817;
--shell-field-grid-strong: #242322;
--shell-scroll-thumb: #3A362F;
--shell-scroll-thumb-hover: #565049;
--shell-module: rgba(34, 31, 28, 0.90);
--shell-module-solid: #26231F;
--shell-inner: #211F1C;
--shell-raise: #3A3631;
--shell-well: rgba(0, 0, 0, 0.28);
--shell-hover: rgba(255, 255, 255, 0.06);
--shell-border: rgba(255, 255, 255, 0.075);
--shell-border-strong: rgba(255, 255, 255, 0.17);
--shell-hairline: rgba(255, 255, 255, 0.045);
--shell-text: #EDE8E0;
--shell-text-muted: #BEB6AC;
/* Dark theme ladder, same rule: label 6.0:1, dim and placeholder 4.6:1 on the plate.
The previous pair measured 4.94:1 and 2.95:1; the dim step failed. */
--shell-text-label: #A79E92;
--shell-text-dim: #92897E;
--shell-placeholder: #92897E;
--shell-signal: #FF5A1F;
--shell-signal-soft: #FFB597;
--shell-ok: #7BD68A;
--shell-warn: #E8B84B;
--shell-fail: #E8583F;
--shell-shadow: 0 14px 30px -10px rgba(0, 0, 0, 0.62), 0 3px 8px rgba(0, 0, 0, 0.34);
--shell-shadow-lift: 0 22px 46px -12px rgba(0, 0, 0, 0.70), 0 0 0 1px rgba(255, 90, 31, 0.40);
/* Same alias layer as the light theme (see the comment there). */
--background: var(--shell-module-solid);
--foreground: var(--shell-text);
--card: var(--shell-module-solid);
--card-foreground: var(--shell-text);
--popover: var(--shell-module-solid);
--popover-foreground: var(--shell-text);
--primary: var(--shell-signal);
--primary-foreground: #1A0F08;
--secondary: var(--shell-well);
--secondary-foreground: var(--shell-text);
--muted: var(--shell-well);
--muted-foreground: var(--shell-text-label);
--accent: color-mix(in srgb, var(--shell-signal) 14%, transparent);
--accent-foreground: var(--shell-signal-soft);
--destructive: var(--shell-fail);
--border: var(--shell-border);
--input: var(--shell-border);
--ring: #FF5A1F;
--chart-1: oklch(0.769 0.188 70.08);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.488 0.243 264.376);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: var(--shell-module-solid);
--sidebar-foreground: var(--shell-text);
--sidebar-primary: var(--shell-signal);
--sidebar-primary-foreground: #1A0F08;
--sidebar-accent: var(--shell-well);
--sidebar-accent-foreground: #F8FAFC;
--sidebar-border: rgba(255, 255, 255, 0.09);
--sidebar-ring: #F59E0B;
/* Direction C2 — Floating Instrument Panel Design System Tokens (DESIGN.md section 11) */
--canvas-dark: #141312;
--canvas-deep-dark: #0E0D0C;
--module-dark: rgba(34, 31, 28, 0.90);
--module-solid-dark: #26231F;
--inner-dark: #211F1C;
--border-dark: rgba(255, 255, 255, 0.075);
--border-strong-dark: rgba(255, 255, 255, 0.17);
--foreground-dark: #EDE8E0;
--foreground-muted-dark: #BEB6AC;
--foreground-label-dark: #948C82;
--foreground-dim-dark: #6E6760;
--signal: #FF5A1F;
--signal-soft: #FFB597;
--pass: #7BD68A;
--warn: #E8B84B;
--fail: #E8583F;
--model-face: #9BA6B2;
--model-face-shaded: #77828E;
--mod: rgba(34, 31, 28, 0.90);
--mod-solid: #26231F;
--hair: rgba(255, 255, 255, 0.075);
--hair-2: rgba(255, 255, 255, 0.045);
--dim: #6E6760;
--label: #948C82;
--lift: inset 0 1px 0 rgba(255, 255, 255, 0.07), 0 14px 30px -10px rgba(0, 0, 0, 0.62), 0 3px 8px rgba(0, 0, 0, 0.34);
/* AgentSCAD custom - Dark (Direction C2: Warm Graphite & Signal Orange) */
--app-bg: #141312;
--app-surface: rgba(34, 31, 28, 0.90);
--app-surface-raised: #26231F;
--app-surface-50: rgba(20, 19, 18, 0.5);
--app-surface-hover: rgba(255, 255, 255, 0.04);
--app-surface-95: rgba(20, 19, 18, 0.95);
--app-border: rgba(255, 255, 255, 0.075);
--app-border-subtle: rgba(255, 255, 255, 0.045);
--app-border-strong: rgba(255, 255, 255, 0.17);
--app-text-primary: #EDE8E0;
--app-text-secondary: #BEB6AC;
--app-text-muted: #948C82;
--app-text-dim: #6E6760;
--app-text-bright: #FFFFFF;
--app-scrollbar-thumb: #33302B;
--app-scrollbar-thumb-hover: #4D4841;
--app-scrollbar-track: transparent;
--app-focus-ring: rgba(255, 90, 31, 0.45);
--app-interactive-hover: rgba(255, 90, 31, 0.12);
--app-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.5);
--app-shadow-md: 0 16px 40px -8px rgba(0, 0, 0, 0.6);
--app-keyboard-bg: #26231F;
--app-keyboard-border: rgba(255, 255, 255, 0.075);
--app-keyboard-text: #948C82;
--app-skeleton-bg: rgba(255, 255, 255, 0.04);
--app-notification-hover: rgba(255, 255, 255, 0.03);
--app-footer-separator: rgba(255, 255, 255, 0.08);
--app-footer-tooltip-bg: #26231F;
--app-footer-tooltip-border: rgba(255, 255, 255, 0.075);
--app-footer-tooltip-text: #BEB6AC;
--app-gradient-separator: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.075), transparent);
--app-card-hover-shadow: 0 4px 20px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 90, 31, 0.2);
--app-pipeline-bg: rgba(255, 255, 255, 0.04);
--app-activity-hover: rgba(255, 255, 255, 0.03);
--app-selected-bg: rgba(255, 90, 31, 0.12);
--app-code-copy-hover: rgba(255, 255, 255, 0.08);
--app-empty-bg: rgba(255, 255, 255, 0.02);
--app-accent: #FF5A1F;
--app-accent-hover: #FF6E36;
--app-accent-bg: rgba(255, 90, 31, 0.14);
--app-accent-border: rgba(255, 90, 31, 0.35);
--app-accent-text: #FF5A1F;
--app-accent-text-secondary: #FFB597;
--app-danger: #E8583F;
--app-danger-bg: rgba(232, 88, 63, 0.12);
--app-warning: #E8B84B;
--app-warning-bg: rgba(232, 184, 75, 0.12);
--app-success: #7BD68A;
--app-success-bg: rgba(123, 214, 138, 0.12);
--app-info: #FF5A1F;
--app-info-bg: rgba(255, 90, 31, 0.12);
/* Dual-Theme Status Semantics (WCAG AA >= 4.5:1 compliant in dark mode) */
--app-status-success-text: #7BD68A;
--app-status-success-bg: rgba(123, 214, 138, 0.14);
--app-status-success-border: rgba(123, 214, 138, 0.32);
--app-status-warning-text: #E8B84B;
--app-status-warning-bg: rgba(232, 184, 75, 0.14);
--app-status-warning-border: rgba(232, 184, 75, 0.32);
--app-status-danger-text: #E8583F;
--app-status-danger-bg: rgba(232, 88, 63, 0.14);
--app-status-danger-border: rgba(232, 88, 63, 0.32);
--app-status-info-text: #F59E0B;
--app-status-info-bg: rgba(245, 158, 11, 0.14);
--app-status-info-border: rgba(245, 158, 11, 0.32);
--app-header-bg: rgba(10, 13, 16, 0.85);
--app-dialog-overlay: rgba(10, 13, 16, 0.82);
--app-dialog-bg: #12161C;
--app-code-bg: #0A0D10;
--app-code-border: rgba(255, 255, 255, 0.09);
--app-tag-bg: rgba(255, 255, 255, 0.04);
--app-tag-border: rgba(255, 255, 255, 0.09);
--app-batch-bar-bg: rgba(245, 158, 11, 0.12);
--app-batch-bar-text: #F59E0B;
--app-input-bg: #0A0D10;
--app-input-border: rgba(255, 255, 255, 0.09);
--app-input-focus-border: rgba(245, 158, 11, 0.5);
--app-hover-subtle: rgba(255, 255, 255, 0.02);
--app-border-separator: rgba(255, 255, 255, 0.09);
--app-state-neutral-bg: rgba(148, 163, 184, 0.12);
--app-state-neutral-text: #94A3B8;
--app-state-neutral-dot: #525D6E;
--app-state-neutral-border: rgba(148, 163, 184, 0.22);
/* Same alias layer as the light theme: resolve to the shell, never to a second
palette. The dark values were already close, but `--cad-text` was the old near
white (#F8FAFC) and `--cad-surface` the old cool #12161C. */
--cad-bg: var(--shell-module-solid);
--cad-background: var(--shell-module-solid);
--cad-surface: var(--shell-well);
--cad-surface-raised: var(--shell-inner);
--cad-surface-muted: var(--shell-well);
--cad-border: var(--shell-border);
--cad-border-subtle: var(--shell-hairline);
--cad-border-strong: var(--shell-border-strong);
--cad-text: var(--shell-text);
--cad-text-primary: var(--shell-text);
--cad-text-secondary: var(--shell-text-muted);
--cad-text-muted: var(--shell-text-label);
--cad-text-dim: var(--shell-text-dim);
--cad-accent: var(--shell-signal);
--cad-accent-hover: var(--shell-signal);
--cad-accent-soft: color-mix(in srgb, var(--shell-signal) 15%, transparent);
--cad-accent-border: color-mix(in srgb, var(--shell-signal) 35%, transparent);
--cad-accent-bg: color-mix(in srgb, var(--shell-signal) 14%, transparent);
--cad-accent-text: var(--shell-signal-soft);
--cad-grid: var(--shell-grid);
--cad-measure: var(--shell-text);
--cad-success: var(--shell-ok);
--cad-warning: var(--shell-warn);
--cad-danger: var(--shell-fail);
--cad-input-bg: var(--shell-module-solid);
--cad-info: var(--shell-signal);
}
@layer base {
* {
@apply border-border outline-ring/50;
box-sizing: border-box;
}
html {
scrollbar-gutter: stable;
}
body {
@apply bg-background text-foreground;
transition: background-color 0.3s ease, color 0.2s ease;
overflow-x: hidden;
}
}
.stable-scrollbar {
scrollbar-gutter: stable;
}
.stable-scrollbar-both {
scrollbar-gutter: stable both-edges;
}
/* ─── AgentSCAD CAD Design System ────────────────────── */
/* Scrollbar - one owner (this file), one palette (the shell tokens). An injected
duplicate used to paint a zinc thumb with a violet hover on every scrollable
surface through `<body>`, which is not a colour this product uses. */
:root { scrollbar-color: var(--shell-scroll-thumb) transparent; }
::-webkit-scrollbar { width: 4px; height: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--shell-scroll-thumb); border-radius: 2px; }
::-webkit-scrollbar-thumb:hover { background: var(--shell-scroll-thumb-hover); }
/* Selection - the shell's own accent, not a legacy near-white that disappears on
the light canvas. */
::selection {
background: color-mix(in srgb, var(--shell-signal) 30%, transparent);
color: var(--shell-text);
}
/* Slider */
[data-radix-slider-root] {
position: relative;
display: flex;
align-items: center;
user-select: none;
touch-action: none;
}
[data-radix-slider-root] > span:first-child {
position: relative;
flex-grow: 1;
height: 3px;
border-radius: 2px;
background: var(--app-border);
}
[data-radix-slider-range] {
position: absolute;
height: 100%;
border-radius: 2px;
background: var(--app-accent);
}
[data-radix-slider-thumb] {
display: block;
width: 13px;
height: 13px;
border-radius: 50%;
background: var(--app-surface);
border: 2px solid var(--app-accent);
box-shadow: var(--app-shadow-sm);
transition: transform 0.1s ease;
outline: none;
}
[data-radix-slider-thumb]:hover { transform: scale(1.1); }
[data-radix-slider-thumb]:focus-visible { box-shadow: 0 0 0 3px var(--app-focus-ring); }
[data-radix-slider-thumb][data-disabled] { opacity: 0.4; cursor: not-allowed; }
/* Progress Bar */
[data-slot="progress-indicator"] {
background: var(--app-accent);
transition: width 0.3s ease;
}
/* Resizable Handle */
[data-panel-group-direction="horizontal"] > [data-resize-handle] { transition: background 0.15s ease; }
[data-resize-handle][data-resize-handle-active] { background: var(--app-accent) !important; }
/* Code Block */
pre code { font-variant-ligatures: none; }
/* Tab Indicator */
[data-state="active"][role="tab"] { transition: border-color 0.15s ease; }
/* Focus States - one focus colour for the whole product: the shell's signal
accent, which is what `--ring` (shadcn focus-visible) resolves to as well. */
input:focus, textarea:focus, select:focus {
outline: none;
box-shadow: 0 0 0 2px var(--ring);
}
/*
* Fields that carry their own focus treatment opt out of the generic ring.
* Inside the shell a field states focus by turning its own border amber
* (DESIGN.md section 5, "the action is a circular button inside the field");
* the generic ring drew a second amber rectangle just inside the first, which is
* what made the active composer read as a warning panel.
*/
.shell-field:focus,
.shell-field:focus-visible {
box-shadow: none;
}
/*
* The rail's create action: the signal hue in its soft form. DESIGN.md section 11
* allows one *solid* orange group per screen (the composer's action and its status
* line), and forbids a third competing orange - so this control carries the accent as
* a tint and the softer text, and only reaches full signal on hover.
*/
.rail-create {
color: var(--shell-signal-soft);
border-color: color-mix(in srgb, var(--shell-signal) 40%, transparent);
background: color-mix(in srgb, var(--shell-signal) 10%, transparent);
}
.rail-create:hover {
color: var(--shell-signal);
border-color: color-mix(in srgb, var(--shell-signal) 62%, transparent);
background: color-mix(in srgb, var(--shell-signal) 18%, transparent);
}
.rail-create .rail-create-key {
color: color-mix(in srgb, var(--shell-signal-soft) 72%, transparent);
}
/* ─── Animations ─────────────────────────────────────────── */
@keyframes status-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.animate-status-pulse { animation: status-pulse 2s ease-in-out infinite; }
@keyframes gentle-float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-4px); }
}
.empty-float { animation: gentle-float 4s ease-in-out infinite; }
@keyframes content-fade {
0% { opacity: 0; transform: translateY(4px); }
100% { opacity: 1; transform: translateY(0); }
}
.content-fade-in { animation: content-fade 0.2s ease-out; }
@keyframes badge-shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-2px); }
75% { transform: translateX(2px); }
}
.badge-shake { animation: badge-shake 0.4s ease-in-out; }
@keyframes typing-wave {
0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
30% { transform: translateY(-3px); opacity: 1; }
}
.typing-wave-dot {
display: inline-block;
width: 4px;
height: 4px;
border-radius: 50%;
background-color: var(--app-text-muted);
}
.typing-wave-dot:nth-child(1) { animation: typing-wave 1.2s ease-in-out infinite 0s; }
.typing-wave-dot:nth-child(2) { animation: typing-wave 1.2s ease-in-out infinite 0.15s; }
.typing-wave-dot:nth-child(3) { animation: typing-wave 1.2s ease-in-out infinite 0.3s; }
@keyframes stop-btn-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(244, 63, 94, 0.3); }
50% { box-shadow: 0 0 0 3px rgba(244, 63, 94, 0); }
}
.stop-btn-pulse { animation: stop-btn-pulse 1.5s ease-out infinite; }
@keyframes notification-slide {
0% { opacity: 0; transform: translateX(12px); }
100% { opacity: 1; transform: translateX(0); }
}
@keyframes tag-pop {
0% { transform: scale(0.85); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
@keyframes dialog-enter {
0% { opacity: 0; transform: translateY(4px); }
100% { opacity: 1; transform: translateY(0); }
}
.dialog-enter { animation: dialog-enter 0.15s ease-out; }
/* Direction 2: Shapr3D x VisionOS Spatial Glass Islands */
.glass-island {
background: var(--glass-island-bg, rgba(18, 22, 28, 0.78));
border: 1px solid var(--glass-island-border, rgba(255, 255, 255, 0.09));
backdrop-filter: blur(28px) saturate(160%);
-webkit-backdrop-filter: blur(28px) saturate(160%);
box-shadow: 0 16px 40px -8px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.12);
border-radius: 14px;
}
:root:not(.dark) .glass-island {
background: var(--glass-island-bg-light, rgba(255, 255, 255, 0.88));
border: 1px solid var(--glass-island-border-light, rgba(213, 221, 230, 0.8));
backdrop-filter: blur(20px) saturate(140%);
-webkit-backdrop-filter: blur(20px) saturate(140%);
box-shadow: 0 10px 30px -6px rgba(17, 24, 32, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.8);
border-radius: 14px;
}
/* Job card left border */
.job-card-left-border { position: relative; }
.job-card-left-border::before {
content: '';
position: absolute;
left: 0;
top: 4px;
bottom: 4px;
width: 2px;
border-radius: 0 1px 1px 0;
background-color: var(--border-color, var(--app-text-muted));
transition: background-color 0.15s ease;
}
/* Keyboard key */
.keyboard-key {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 24px;
height: 22px;
padding: 0 6px;
font-size: 11px;
font-family: var(--font-mono), monospace;
font-weight: 500;
color: var(--app-keyboard-text);
background: var(--app-keyboard-bg);
border: 1px solid var(--app-keyboard-border);
border-bottom-width: 1.5px;
border-radius: 4px;
white-space: nowrap;
line-height: 1;
}
.keyboard-key-lg { min-width: 30px; height: 26px; font-size: 12px; padding: 0 8px; }
/* Code copy button */
.code-copy-btn {
position: absolute;
top: 4px;
right: 4px;
opacity: 0;
transition: opacity 0.15s ease, background-color 0.15s ease;
z-index: 10;
}
.code-copy-btn:hover { background-color: var(--app-code-copy-hover); }
pre:hover .code-copy-btn { opacity: 1; }
/* Tab active underline */
.tab-active-glow { position: relative; }
.tab-active-glow::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
right: 0;
height: 1px;
background: var(--app-accent);
}
/* Notification item */
.notification-item { transition: background-color 0.15s ease; }
.notification-item:hover { background-color: var(--app-notification-hover); }
/* Tag badge */
.tag-badge { animation: tag-pop 0.15s ease-out both; line-height: 1.4; }
/* Footer separator */
.footer-separator {
width: 1px;
height: 10px;
background: var(--app-footer-separator);
display: inline-block;
vertical-align: middle;
}
/* Skeleton loading */
.skeleton-loading {
background: var(--app-skeleton-bg);
border-radius: 4px;
animation: status-pulse 2s ease-in-out infinite;
}
/* Online status dot (breathing) */
.online-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--app-success);
display: inline-block;
animation: status-pulse 2.5s ease-in-out infinite;
box-shadow: 0 0 0 2px var(--app-success-bg);
}
/* Compare card border */
.compare-border-left { position: relative; border-left: 2px solid transparent; }
/* ─── Enhanced Styling Classes ─────────────────────────── */
/* Job card hover - gradient + shadow (no layout shift) */
.job-card-hover {
position: relative;
overflow: hidden;
transition: box-shadow 0.2s ease, background-color 0.2s ease;
will-change: box-shadow;
}
.job-card-hover::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, var(--app-accent-bg) 0%, transparent 60%);
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
border-radius: inherit;
}
.job-card-hover:hover::after {
opacity: 1;
}
.job-card-hover:hover {
box-shadow: var(--app-card-hover-shadow);
}
/* Tab indicator - active tab left border */
.tab-indicator {
position: relative;
}
.tab-indicator::before {
content: '';
position: absolute;
left: 0;
top: 4px;
bottom: 4px;
width: 2px;
border-radius: 0 1px 1px 0;
background: var(--app-accent);
opacity: 0;
transition: opacity 0.15s ease;
}
.tab-indicator[data-state="active"]::before {
opacity: 1;
}
/* Skeleton pulse - legacy gradient shimmer for loading states */
.skeleton-pulse {
background: linear-gradient(90deg, var(--app-skeleton-bg) 25%, var(--app-border) 50%, var(--app-skeleton-bg) 75%);
background-size: 200% 100%;
animation: skeleton-shimmer 1.5s ease-in-out infinite;
border-radius: 4px;
}
@keyframes skeleton-shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* Skeleton shimmer: engineering scanline shimmer */
.skeleton-shimmer {
position: relative;
overflow: hidden;
background-color: var(--app-skeleton-bg);
border-radius: 6px;
}
.skeleton-shimmer::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.05) 20%,
rgba(255, 255, 255, 0.14) 60%,
rgba(255, 255, 255, 0)
);
animation: engineeringShimmer 1.8s infinite cubic-bezier(0.4, 0, 0.2, 1);
content: '';
}
:root:not(.dark) .skeleton-shimmer::after {
background-image: linear-gradient(
90deg,
rgba(15, 23, 42, 0) 0,
rgba(15, 23, 42, 0.04) 20%,
rgba(15, 23, 42, 0.10) 60%,
rgba(15, 23, 42, 0)
);
}
@keyframes engineeringShimmer {
100% {
transform: translateX(100%);
}
}
/* Slide in from right (tab forward) */
.slide-in-right {
animation: slideInRight 0.2s ease-out;
}
@keyframes slideInRight {
0% { opacity: 0; transform: translateX(20px); }
100% { opacity: 1; transform: translateX(0); }
}
/* Slide in from left (tab backward) */
.slide-in-left {
animation: slideInLeft 0.2s ease-out;
}
@keyframes slideInLeft {
0% { opacity: 0; transform: translateX(-20px); }
100% { opacity: 1; transform: translateX(0); }
}
/* Activity item - hover highlight */
.activity-item {
transition: background-color 0.15s ease;
border-radius: 6px;
}
.activity-item:hover {
background-color: var(--app-activity-hover);
}
/* Activity slide-in animation */
.activity-slide-in {
animation: activitySlideIn 0.25s ease-out;
}
@keyframes activitySlideIn {
0% { opacity: 0; transform: translateY(-8px); }
100% { opacity: 1; transform: translateY(0); }
}
/* State change bounce micro-animation */
.state-bounce {
animation: stateBounce 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes stateBounce {
0% { transform: scale(0.97); }
50% { transform: scale(1.02); }
100% { transform: scale(1); }
}
/* Gradient separator between header and content */
.gradient-separator {
height: 1px;
background: var(--app-gradient-separator);
border: none;
}
/* Progress indicator for pipeline states */
.pipeline-mini-progress {
height: 2px;
background: var(--app-pipeline-bg);
border-radius: 1px;
overflow: hidden;
position: relative;
}
.pipeline-mini-progress-fill {
height: 100%;
border-radius: 1px;
transition: width 0.5s ease;
}
/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
.job-card-hover::after,
.job-card-hover:hover {
transition: none;
transform: none;
}
.skeleton-pulse { animation: none; }
.skeleton-shimmer::after { animation: none; display: none; }
.slide-in-right, .slide-in-left { animation: none; }
.activity-slide-in { animation: none; }
.state-bounce { animation: none; }
.empty-float { animation: none; }
}
/* ─── 正在生成: the part tumbling on the bench ───────────────────────────────────
Two axes, so it reads as an object being turned over rather than a spinning
sprite: it nods on X while it comes round on Y. */
@keyframes partTumble {
0% { transform: rotateX(-22deg) rotateY(0deg); }
50% { transform: rotateX(16deg) rotateY(180deg); }
100% { transform: rotateX(-22deg) rotateY(360deg); }
}
.part-tumble {
animation: partTumble 5.2s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite;
}
/* Reduced motion: the part holds a readable three-quarter angle instead of turning.
This has to come after the rule above - the earlier reduced-motion block sits
higher in the file and lost the cascade, so the gear kept spinning. */
@media (prefers-reduced-motion: reduce) {
.part-tumble {
animation: none;
transform: rotateX(-20deg) rotateY(32deg);
}
}
/* Touch devices: expand hit area for icon-sized buttons to WCAG 44x44.
Visual size stays 36x36 on desktop IDE; coarse pointer (tablet/touch) gets a
transparent ::before expansion that doesn't affect layout. */
@media (pointer: coarse) {
.size-9 { position: relative; }
.size-9::before {
content: '';
position: absolute;
inset: -4px;
}
/*
* DESIGN.md section 20: hit targets are at least 44px on touch. Every control in
* the shell measured under 44px (module collapse 16x16, bell and gear 24x24,
* density segments 36x22 / 46x22, row utilities 16x20 / 20x20, viewport controls
* 28x28), so the hit area grows to 44px tall while the visible control keeps its
* instrument size. Vertical only: these controls sit shoulder to shoulder
* horizontally, and a wider invisible target would steal taps from its neighbour.
*/
.mod-h button,
#brand button,
#controls button,
#cmd button,
#view-cube button,
[data-testid="design-row"] button {
position: relative;
}
.mod-h button::after,
#brand button::after,
#controls button::after,
#cmd button::after,
#view-cube button::after,
[data-testid="design-row"] button::after {
content: '';
position: absolute;
left: 0;
right: 0;
top: 50%;
height: 44px;
transform: translateY(-50%);
}
/* Fields and chips cannot grow their hit area with a pseudo-element: an <input>
ignores ::after, and the empty-state chips are the whole target. These grow. */
#cmd input,
#slots input,
#slots button,
[data-testid="viewport-cold-start"] button {
min-height: 44px;
}
#slots button {
min-width: 44px;
}
}
/* Footer tooltip */
.footer-metric {
position: relative;
cursor: default;
}
/* ─── Linear Design Utilities ──────────────────────────────── */
.linear-surface { background: var(--app-surface); border: 1px solid var(--app-border); }
.linear-surface-hover:hover { background: var(--app-surface-hover); }
.linear-border { border: 1px solid var(--app-border); }
.linear-focus-ring:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--app-focus-ring); }
.linear-selected { border-left: 2px solid var(--app-accent); background: var(--app-selected-bg); }
.linear-transition { transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, opacity 0.15s ease; }
.linear-shadow-sm { box-shadow: var(--app-shadow-sm); }
.linear-shadow-md { box-shadow: var(--app-shadow-md); }
/* Typography tokens: section eyebrows (mono uppercase tracked) */
.text-eyebrow {
font-size: 11px;
font-family: var(--font-mono), ui-monospace, monospace;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.16em;
line-height: 1;
}
.cad-surface {
background: var(--cad-surface);
border: 1px solid var(--cad-border);
}
.cad-panel {
background: linear-gradient(180deg, color-mix(in srgb, var(--cad-surface) 96%, white 4%), var(--cad-surface));
border: 1px solid var(--cad-border);
border-radius: 8px;
}
.cad-chip {
display: inline-flex;
align-items: center;
gap: 0.35rem;
min-height: 24px;
padding: 0 0.55rem;
border: 1px solid var(--cad-border);
border-radius: 6px;
background: var(--cad-accent-soft);
color: var(--cad-text-secondary);
font-size: 10px;
font-family: var(--font-mono), monospace;
line-height: 1;
}
.cad-viewport-shell {
/*
* The viewport is the largest surface in the product and it was painted from the
* old palette: `--cad-bg` (#0A0D10 dark / #F4F6F8 light, a cool near-black and a
* cool grey) instead of the C2 field, `--cad-grid` for its straight grid, a 14%
* accent radial halo behind the part (a zero-offset glow: decoration, not depth)
* and a 1px legacy border with an 8px radius. The canvas is transparent, so that
* background colour is what the user actually sees around the part.
*
* Now: one field token, no halo, no inset frame. DESIGN.md section 3 makes the
* canvas full-bleed and section 4 puts the lighting on the camera, not on a
* decorative glow behind the part.
*/
background: var(--shell-canvas-deep);
}
.cad-viewport-glass {
background: color-mix(in srgb, var(--cad-surface) 82%, transparent);
border: 1px solid var(--cad-border);
backdrop-filter: blur(12px);
}
.cad-status-dot {
width: 6px;
height: 6px;
border-radius: 999px;
background: var(--cad-accent);
box-shadow: 0 0 0 3px var(--cad-accent-soft);
}
.cad-inspector-panel {
min-width: 0;
}
.cad-inspector-tabs {
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
.cad-inspector-tabs::-webkit-scrollbar {
display: none;
}
.cad-inspector-tabs [role="tab"] {
min-width: max-content;
}
@media (max-width: 1023px) {
/*
* The docked three-column layout these rules belonged to is gone (DESIGN.md
* section 3). Keeping them alive meant that at <=1023px `.cad-left-panel`
* still matched the slots rail inside its floating module and hid it with
* `display:none !important`: the plate stayed on screen, kept its count badge,
* and rendered an empty body, so no part could be selected.
*
* The classes survive as hooks (ViewerPanel: "styling/test hook"); the layout
* media queries do not. Responsive behaviour below 960px is still phase 6.
*/
.cad-viewer-panel,
.cad-inspector-panel {
min-width: 0;
}
}
@media (max-width: 767px) {
.cad-viewer-panel,
.cad-inspector-panel {
min-width: 0;
}
}
/* ─── Breadcrumb fade-in ──────────────────────────────── */
@keyframes breadcrumbFadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
.breadcrumb-fade-in { animation: breadcrumbFadeIn 0.2s ease-out; }
/* ─── Job Status Page Animations ──────────────────────── */
@keyframes statusPageEnter {
0% { opacity: 0; transform: translateY(12px); }
100% { opacity: 1; transform: translateY(0); }
}
.status-page-enter { animation: statusPageEnter 0.3s ease-out; }
/* Status icon pulse ring */
@keyframes statusIconPulse {
0% { box-shadow: 0 0 0 0 var(--app-accent-border); }
70% { box-shadow: 0 0 0 20px transparent; }
100% { box-shadow: 0 0 0 0 transparent; }
}
.status-icon-pulse { animation: statusIconPulse 2s ease-out infinite; }
/* Line clamp for 2 lines */
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* ─── Theme Transition ──────────────────────────────────── */
html.theme-transition,
html.theme-transition *,
html.theme-transition *::before,
html.theme-transition *::after {
transition: background-color 0.3s ease, border-color 0.3s ease, color 0.2s ease, box-shadow 0.3s ease, fill 0.2s ease, stroke 0.2s ease !important;
}
/*
* Direction C2 floating module plate.
*
* The plate is owned by `Module.tsx`, which writes every visual property from the
* shell tokens (DESIGN.md section 22: one writer per element). This file used to
* carry a second, hardcoded copy of the same plate - same background, same border,
* same shadow, literals instead of tokens - which made light mode and any token
* change silently disagree between the two definitions.
*
* Only the two affordances that are pure CSS are kept here.
*/
.mod-h {
cursor: grab;
}
.mod-h:active {
cursor: grabbing;
}