@tangle-network/sandbox-ui 0.139.0 → 0.140.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tokens.css CHANGED
@@ -1,35 +1,24 @@
1
1
  /* Tangle design tokens — the single palette every Tangle surface renders from.
2
2
  *
3
- * The surface ladder is NEUTRAL GREY and the indigo is trim — accent, never
4
- * field. It steps in even lightness increments so a card, a nested panel and an
5
- * overlay each read as a distinct plane by their own fill, and carries a crisp
6
- * hairline (1.5:1 against the card in dark) because shadow does no work on a dark
7
- * canvas. Light mirrors it: white paper on a grey canvas, never white-on-white.
3
+ * The surface ladder is near-neutral grey with a faint indigo cast; the indigo
4
+ * accent is trim, never field. Each plane steps at least 4.4 OKLCH L off the
5
+ * plane it sits on (surface-ladder.test.ts), so a card, a card in a card and a
6
+ * field each read by their own fill. Dark adds a 1.5:1 hairline and a top edge
7
+ * highlight, because a black shadow on a dark canvas renders as nothing; light
8
+ * is white paper on a grey canvas, separated by shadow.
9
+ *
10
+ * Each colour is declared ONCE, as a role (a plane, an ink tier, one of four
11
+ * indigos, one ink per status hue), and every other name of that colour is an
12
+ * alias of it, so a retune or a named theme moves one value and every family
13
+ * follows: hsl-*, md3-*, depth-*, bg-*, status-*, surface-*, code-* and brand-*.
8
14
  *
9
15
  * Apps consume this spine as-is. If a product needs its own accent identity,
10
16
  * add a named theme in named-themes.css (`data-theme="<name>"`) — do NOT re-declare
11
17
  * these tokens in the app, or its surfaces silently stop tracking the brand. */
12
18
 
13
- /* MD3 surface hierarchy */
14
19
  /* Dark is the default theme (`:root`/`[data-sandbox-ui]`). It also responds to
15
20
  * the standard `[data-theme="dark"]` / `.dark` switch so any consumer can pin or
16
21
  * toggle a theme without inventing an ad-hoc `data-sandbox-theme` name. */
17
- /*
18
- * Surfaces are NEUTRAL GREY and the indigo is trim.
19
- *
20
- * Colour in the surface competes with colour in the accent: on an indigo-cast
21
- * ladder the indigo nav pill, link and focus ring are one more indigo thing on
22
- * an indigo page, and the eye stops finding them. Neutral planes give the brand
23
- * a field to sit on, so a single accent does all the signalling.
24
- *
25
- * The canvas stays above pure black (#0a0a14, lifted toward indigo). Near-white ink on it is the
26
- * glare end of the range, so its luminance leaves room for distinct text tiers.
27
- *
28
- * Elevation is carried by surface lightness plus a hairline that can actually be
29
- * seen — a black shadow on a dark canvas renders as nothing, so shadow does no
30
- * work here. The hairline is about 1.5:1 against the resting card — quiet by design,
31
- * enough to state an edge without drawing a box around every panel.
32
- */
33
22
  /* Seed a complete baseline on every supported boundary BEFORE mode overrides.
34
23
  * Inherited var() aliases are already substituted at their parent; declaring
35
24
  * them here lets a nested light/dark/named scope resolve its own palette.
@@ -56,96 +45,181 @@
56
45
  .light,
57
46
  .dark {
58
47
  color-scheme: dark;
59
- --md3-surface: #0a0a14; /* L0 page canvas — indigo-lifted off black */
60
- --md3-surface-dim: #040408; /* recessed page: 4.1 L under the #0a0a14 panel (the inset workspace surface needs >= 3) */
61
- --md3-surface-bright: #232238;
62
- /* Same value as --md3-surface-dim by design, not by drift: both name the floor
63
- * of the ladder, and dark has no plane below it to give them. They stay two
64
- * tokens because the roles diverge in light, where the well is paper (#ffffff)
65
- * and the recessed page is grey (#dcdee6) — a consumer binding to the wrong one
66
- * there gets the wrong surface. */
67
- --md3-surface-container-lowest: #040408; /* L-1 terminal/code well */
68
- --md3-surface-container-low: #12111f; /* L1 chrome, field wells */
69
- --md3-surface-container: #191826; /* L2 resting card */
70
- --md3-surface-container-high: #221f33; /* L3 nested / hover */
71
- --md3-surface-container-highest: #2c2942; /* L4 overlay */
72
- --md3-surface-variant: #221f33;
73
- --md3-on-surface: #e6e6e6;
74
- --md3-on-surface-variant: #b5b5b5;
75
-
76
- --md3-primary: #A5B4FC;
77
- --md3-primary-dim: #7C6CF6;
78
- --md3-primary-container: #4C3FC5;
79
- --md3-on-primary: #ffffff;
80
- --md3-on-primary-container: #E0E7FF;
81
- --md3-primary-fixed: #A5AAFC;
82
- --md3-primary-fixed-dim: #818CF8;
83
-
84
- --md3-secondary: #C7D2FE;
85
- --md3-secondary-dim: #A5B4FC;
86
- --md3-secondary-container: #312E81;
87
- --md3-on-secondary: #E8E5FF;
88
- --md3-on-secondary-container: #E8E5FF;
89
-
90
- --md3-tertiary: #10B981;
91
- --md3-tertiary-container: var(--surface-success-bg);
92
- --md3-outline: #8e8e8e;
93
- --md3-outline-variant: #2a293d;
94
-
95
- --md3-error: #ff5277;
96
- --md3-error-dim: #eb2855;
97
- --md3-error-container: #82042b;
98
- --md3-on-error: #ffffff;
99
- --md3-on-error-container: #ffbdf2;
100
48
 
49
+ /* ── Planes ─────────────────────────────────────────────────────────────
50
+ * One ladder, authored once as HSL channels because the shadcn layer reads
51
+ * hsl(var(--hsl-*)); every hex plane below is derived from it, so a step
52
+ * moves in one place. OKLCH L x100: page 15.0, card 21.7, raised 26.6,
53
+ * raised-2 and overlay 31.6, each at least 4.4 over the plane it sits on.
54
+ * The page is lifted off black so near-white ink keeps room for its tiers. */
55
+ --hsl-background: 240 33% 6%; /* L0 page #0a0a14 */
56
+ --hsl-input: 244 29% 9%; /* L1 chrome (the sidebar) and the legacy field fill #11101e */
57
+ --hsl-card: 244 23% 12%; /* L2 card #191826 */
58
+ --hsl-secondary: 249 24% 17.5%; /* L3 raised: a card in a card, and the hover well #252237 */
59
+ --hsl-popover: 247 21% 23%; /* L4 raised-2 and overlay #312e47: a popover carries body text, so it sits on the top plane the ink ramp reaches */
60
+ --hsl-muted: var(--hsl-secondary);
61
+ --hsl-accent: var(--hsl-secondary); /* neutral hover; indigo lives in --accent-surface-strong */
62
+ /* The floor of the ladder: the recessed page and the terminal well share it
63
+ * because dark has no plane below L0 to give them. They stay two tokens
64
+ * because they diverge in light, where the well is paper and the page grey. */
65
+ --md3-surface-dim: #040408; /* 4.1 L under the L0 panel: the inset workspace surface needs >= 3 */
66
+ --md3-surface-container-lowest: var(--md3-surface-dim);
67
+ --md3-surface-bright: #232238;
68
+ --md3-surface: hsl(var(--hsl-background));
69
+ --md3-surface-container-low: hsl(var(--hsl-input));
70
+ --md3-surface-container: hsl(var(--hsl-card));
71
+ --md3-surface-container-high: hsl(var(--hsl-secondary));
72
+ --md3-surface-container-highest: hsl(var(--hsl-popover));
73
+ --md3-surface-variant: var(--md3-surface-container-high);
74
+ --depth-1: var(--md3-surface);
75
+ --depth-2: var(--md3-surface-container);
76
+ --depth-3: var(--md3-surface-container-high);
77
+ --depth-4: var(--md3-surface-container-highest);
78
+ --bg-root: var(--md3-surface);
79
+ --bg-dark: var(--depth-1);
80
+ --bg-card: var(--depth-2);
81
+ --bg-elevated: var(--depth-4);
82
+ --bg-section: var(--depth-1);
83
+ --bg-input: hsl(var(--hsl-input));
84
+ /* Sidebar: L1 chrome, one step BELOW the canvas, so the nav reads as what the
85
+ * content sits in front of rather than as another card. */
86
+ --sidebar-background: var(--hsl-input);
87
+ --sidebar-accent: var(--hsl-secondary);
101
88
  --md3-inverse-surface: #fdfdfd;
102
- --md3-inverse-primary: #4C3FC5;
103
89
  --md3-inverse-on-surface: #000000;
104
- --md3-surface-tint: #A5B4FC;
105
90
 
106
- /* Semantic HSL bridge — the indigo-lifted surface ladder (GTM's, now every product's) */
107
- --hsl-background: 240 33% 6%; /* L0 canvas #0a0a14 */
108
- /* Near-white ink on the canvas is the glare end of the range, and the tiers
109
- * below it need room to separate. This neutral ink keeps the original 10.6:1
110
- * contrast on the card and leaves four steps the eye can tell apart. */
91
+ /* ── Ink ────────────────────────────────────────────────────────────────
92
+ * Neutral on purpose: colour in the ink would compete with the one accent.
93
+ * Near-white on the canvas is the glare end of the range, so the tiers sit
94
+ * below it: 10.6 / 7.8 / 6.4 / 5.4 against the L2 card, solved against the
95
+ * WHOLE ladder so the faintest tier still clears the 4.5:1 body floor on the
96
+ * overlay plane. tokens.test.ts asserts every tier on every plane. */
111
97
  --hsl-foreground: 0 0% 90.1961%; /* #e6e6e6 */
112
- --hsl-card: 244 23% 12%; /* L2 card #191826 */
113
- --hsl-card-foreground: 0 0% 90.1961%;
114
- --hsl-popover: 247 23% 21%; /* #2c2942 — the NESTED plane despite
115
- the name; the overlay plane is --md3-surface-container-highest. A popover
116
- carries body text, and the ladder above L3 is reserved for planes that lift
117
- by shadow, so it sits on L3 and the ink ramp reaches it. */
118
- --hsl-popover-foreground: 0 0% 90.1961%;
119
- /* The fill indigo, NOT the bright one: `.btn-primary` puts white on this, and
120
- * #6366F1 carries white at only 4.47:1. This clears the floor at 7.4:1. The
121
- * bright indigo stays available for accent TEXT via --accent-text. */
122
- --hsl-primary: 246 54% 51%; /* #4c3fc6 */
98
+ --hsl-muted-foreground: 240 5% 68%; /* #a9a9b1: the /70 and /60 caption derivatives still clear the large-text floor on the card */
99
+ --text-primary: hsl(var(--hsl-foreground));
100
+ --text-secondary: #c7c7c7;
101
+ --text-muted: #b5b5b5;
102
+ --text-dim: #a5a5a5;
103
+ --hsl-card-foreground: var(--hsl-foreground);
104
+ --hsl-popover-foreground: var(--hsl-foreground);
105
+ --hsl-secondary-foreground: var(--hsl-foreground);
106
+ --hsl-accent-foreground: var(--hsl-foreground);
107
+ --sidebar-foreground: var(--hsl-foreground);
108
+ --sidebar-accent-foreground: var(--hsl-foreground);
109
+ --md3-on-surface: var(--text-primary);
110
+ --md3-on-surface-variant: var(--text-muted);
111
+ --md3-outline: #8e8e8e;
112
+
113
+ /* ── Hairline ───────────────────────────────────────────────────────────
114
+ * ONE role for every divider, card edge and table rule, so lines never
115
+ * disagree. 1.5:1 against the card and 1.3:1 on the raised plane: quiet by
116
+ * design, it states where a plane ends without boxing every panel.
117
+ * --border-subtle and --border-default are its older names. */
118
+ --hsl-border: 244 16% 25%; /* #37364a */
119
+ --md3-outline-variant: hsl(var(--hsl-border));
120
+ --sidebar-border: var(--hsl-border);
121
+ --border-subtle: hsl(var(--hsl-border));
122
+ --border-default: hsl(var(--hsl-border));
123
+ --border-hover: rgba(255, 255, 255, 0.28);
124
+
125
+ /* ── Accent: four indigos, each with one job ────────────────────────────
126
+ * fill --hsl-primary carries white at 7.4:1: buttons, the CTA, the selected row at 30%
127
+ * mark --brand-primary the brand indigo of the logo and marketing fills; never under text
128
+ * line --brand-cool edges, chart accents, the focus ring family
129
+ * ink --accent-text the bright indigo as TEXT and icon only: 6.7:1 on the card
130
+ * Every other indigo name is one of these four, so a named theme that retints
131
+ * the accent retints all of them. The ring is brighter than the fill on
132
+ * purpose: it is non-text and must clear 3:1 on the canvas AND the card,
133
+ * while the fill is tuned to carry white. */
134
+ --hsl-primary: 246 54% 51%; /* fill #4c3fc6 */
123
135
  --hsl-primary-foreground: 0 0% 100%;
124
- --hsl-secondary: 249 24% 16%; /* L3 #221f33 */
125
- --hsl-secondary-foreground: 0 0% 90.1961%;
126
- --hsl-muted: 249 24% 16%; /* L3 hover well */
127
- --hsl-muted-foreground: 240 5% 68%; /* #a9a9b1 — high enough that the
128
- reduced-opacity caption derivatives (/70, /60) still clear the large-text
129
- contrast floor against the L2 card. */
130
- --hsl-accent: 249 24% 16%; /* neutral hover (indigo lives in accent-surface-strong) */
131
- --hsl-accent-foreground: 0 0% 90.1961%;
132
- /* Light red carrying dark ink, as the dark primary does: it must read as
133
- * error TEXT on every dark plane (AA) and still carry its foreground as a fill. */
134
- --hsl-destructive: 0 90% 77%;
135
- --hsl-destructive-foreground: 0 60% 10%;
136
- /* Quiet on purpose: 1.5:1 against the resting card. Shadow does no work on a
137
- * dark canvas, so the hairline is what states where a plane ends. */
138
- --hsl-border: 243 20% 20%; /* #2a293d = --md3-outline-variant */
139
- --hsl-input: 244 29% 9%; /* recessed field well #11101e */
140
- /* Brighter than --primary on purpose: the ring is NON-TEXT and has to clear
141
- * 3:1 against BOTH canvas and card. The fill indigo is tuned to carry white
142
- * text instead, so the two cannot be the same value. */
143
136
  --hsl-ring: 233 100% 87%;
144
- --hsl-success: 160 84% 39%;
145
- --hsl-warning: 40 94% 56%;
146
- --hsl-info: 200 88% 56%;
147
-
148
- /* Tailwind/shadcn aliases */
137
+ --brand-primary: #7C6CF6;
138
+ --brand-cool: #818CF8;
139
+ --accent-text: #A5B4FC;
140
+ --brand-purple: #C7D2FE; /* the ink, softened: gradient text and ink on the deep field */
141
+ --brand-strong: #2E2A5E; /* the deep field the softened ink sits on */
142
+ --brand-strong-text: #ffffff;
143
+ --brand-strong-text-muted: rgba(255, 255, 255, 0.7);
144
+ --brand-strong-text-dim: rgba(255, 255, 255, 0.5);
145
+ --brand-glow: var(--accent-text);
146
+ --brand-vibrant: var(--brand-primary);
147
+ --btn-primary-bg: hsl(var(--hsl-primary));
148
+ --btn-primary-hover: #5a50d6;
149
+ --btn-primary-text: #ffffff;
150
+ --btn-cta-bg: var(--btn-primary-bg);
151
+ --btn-cta-text: var(--btn-primary-text);
152
+ --sidebar-primary: var(--hsl-primary);
153
+ --sidebar-primary-foreground: var(--hsl-primary-foreground);
154
+ --sidebar-ring: var(--hsl-ring);
155
+ --md3-primary: var(--accent-text);
156
+ --md3-primary-dim: var(--brand-primary);
157
+ --md3-primary-container: var(--btn-primary-bg);
158
+ --md3-on-primary: #ffffff;
159
+ --md3-on-primary-container: var(--brand-purple);
160
+ --md3-primary-fixed: var(--accent-text);
161
+ --md3-primary-fixed-dim: var(--brand-cool);
162
+ --md3-secondary: var(--brand-purple);
163
+ --md3-secondary-dim: var(--accent-text);
164
+ --md3-secondary-container: var(--brand-strong);
165
+ --md3-on-secondary: var(--brand-purple);
166
+ --md3-on-secondary-container: var(--brand-purple);
167
+ --md3-inverse-primary: var(--btn-primary-bg);
168
+ --md3-surface-tint: var(--accent-text);
169
+ /* Accent wells: soft is the hover neutral, strong the active or selected row. */
170
+ --accent-surface-soft: color-mix(in srgb, var(--accent-text) 7%, transparent);
171
+ --accent-surface-strong: color-mix(in srgb, var(--btn-primary-bg) 30%, transparent);
172
+ --bg-hover: var(--accent-surface-soft);
173
+ --bg-selection: color-mix(in srgb, var(--brand-primary) 28%, transparent);
174
+ --border-accent: color-mix(in srgb, var(--brand-cool) 35%, transparent);
175
+ --border-accent-hover: color-mix(in srgb, var(--brand-cool) 50%, transparent);
176
+ /* The indigo spark, kept ONLY for the logo mark and the send button. */
177
+ --tangle-gradient: linear-gradient(135deg, var(--btn-primary-bg), var(--accent-text));
178
+ --tangle-gradient-text: linear-gradient(135deg, var(--brand-purple), var(--accent-text));
179
+ --accent-gradient-strong: linear-gradient(135deg, var(--btn-primary-bg), var(--brand-primary));
180
+
181
+ /* ── Status: one ink, one wash, one border per hue ──────────────────────
182
+ * The ink is pastel on purpose. A saturated fill vibrates on a 6% canvas,
183
+ * and the ink already clears AA on every plane, so it is also the fill: a
184
+ * status fill carries dark text, as the destructive one always has. The
185
+ * shadcn channels are the source; the surface, status, md3, code and brand
186
+ * names of each hue alias them. */
187
+ --hsl-success: 154.7 60.2% 60.6%; /* #5ed7a4 */
188
+ --hsl-warning: 41.5 88.2% 63.3%; /* #f4c14f */
189
+ --hsl-destructive: 2.8 100% 83.1%; /* #ffada9 */
190
+ --hsl-destructive-foreground: 0 60% 10%;
191
+ --hsl-info: 199.7 100% 70.2%; /* #67cdff */
192
+ --surface-success-text: hsl(var(--hsl-success));
193
+ --surface-success-bg: #2f5146;
194
+ --surface-success-border: #3c7760;
195
+ --surface-warning-text: hsl(var(--hsl-warning));
196
+ --surface-warning-bg: #4b4232;
197
+ --surface-warning-border: #7a6236;
198
+ --surface-danger-text: hsl(var(--hsl-destructive));
199
+ --surface-danger-bg: #5a3b40;
200
+ --surface-danger-border: #7d4d53;
201
+ --surface-info-text: hsl(var(--hsl-info));
202
+ --surface-info-bg: #2d4e67;
203
+ --surface-info-border: #3d7292;
204
+ --status-running: var(--surface-success-text);
205
+ --status-creating: var(--accent-text);
206
+ --status-stopped: var(--surface-warning-text);
207
+ --status-warm: var(--surface-orange-text);
208
+ --status-cold: var(--surface-info-text);
209
+ --status-error: var(--surface-danger-text);
210
+ --status-deleted: var(--text-dim);
211
+ --brand-emerald: var(--surface-success-text);
212
+ --md3-tertiary: var(--surface-success-text);
213
+ --md3-tertiary-container: var(--surface-success-bg);
214
+ --md3-error: var(--surface-danger-text);
215
+ --md3-error-dim: color-mix(in srgb, var(--md3-error) 85%, black);
216
+ --md3-error-container: var(--surface-danger-bg);
217
+ --md3-on-error: hsl(var(--hsl-destructive-foreground));
218
+ --md3-on-error-container: var(--surface-danger-text);
219
+ /* ── shadcn aliases: the names @tangle-network/ui and Tailwind read ─────
220
+ * Declared once; they re-resolve per theme through the --hsl-* spine. --input
221
+ * is the input BORDER and the Switch off-track, a mid-tone that must stay
222
+ * visible against a surface, so it is NOT the field fill (--bg-input). */
149
223
  --background: var(--hsl-background);
150
224
  --foreground: var(--hsl-foreground);
151
225
  --card: var(--hsl-card);
@@ -163,11 +237,6 @@
163
237
  --destructive: var(--hsl-destructive);
164
238
  --destructive-foreground: var(--hsl-destructive-foreground);
165
239
  --border: var(--hsl-border);
166
- /* shadcn's `--input` is the input BORDER and the Switch's off-track — a
167
- * mid-tone that has to stay visible AGAINST a surface. It is deliberately NOT
168
- * the field fill: pointing it at the recessed well would tint a border and a
169
- * track the same color as the plane behind them, and both would vanish. The
170
- * field fill is --hsl-input / --bg-input below. */
171
240
  --input: var(--hsl-muted-foreground);
172
241
  --ring: var(--hsl-ring);
173
242
  --success: var(--hsl-success);
@@ -175,45 +244,6 @@
175
244
  --error: var(--hsl-destructive);
176
245
  --info: var(--hsl-info);
177
246
 
178
- /* Sidebar tokens — L1 chrome: one step BELOW the canvas here, so the nav reads
179
- * as chrome the content sits in front of rather than as another card. */
180
- --sidebar-background: 244 29% 9%; /* L1 chrome #11101e */
181
- --sidebar-foreground: 0 0% 90.1961%;
182
- --sidebar-primary: 246 54% 51%;
183
- --sidebar-primary-foreground: 0 0% 100%;
184
- --sidebar-accent: 0 0% 20.7843%; /* L3 hover well */
185
- --sidebar-accent-foreground: 0 0% 90.1961%;
186
- --sidebar-border: 0 0% 29.8039%;
187
- --sidebar-ring: 233 100% 87%;
188
-
189
- /* Depth scale — tracks the MD3 ladder (canvas / card / nested / overlay) */
190
- --depth-1: #0a0a14;
191
- --depth-2: #191826;
192
- --depth-3: #221f33;
193
- --depth-4: #2c2942;
194
-
195
- /* Status colors */
196
- --status-running: #10B981;
197
- --status-creating: #A5B4FC;
198
- --status-stopped: #FFB800;
199
- --status-warm: #FF8A4C;
200
- --status-cold: #4AABFF;
201
- --status-error: #FF4D6D;
202
- --status-deleted: #6B7094;
203
-
204
- /* Status surface tokens — bg / border / text for each semantic color */
205
- --surface-success-bg: #2f5146;
206
- --surface-success-border: #3c7760;
207
- --surface-success-text: #5ed7a4;
208
- --surface-warning-bg: #4b4232;
209
- --surface-warning-border: #7a6236;
210
- --surface-warning-text: #f4c14f;
211
- --surface-danger-bg: #5a3b40;
212
- --surface-danger-border: #7d4d53;
213
- --surface-danger-text: #ffada9;
214
- --surface-info-bg: #2d4e67;
215
- --surface-info-border: #3d7292;
216
- --surface-info-text: #67cdff;
217
247
  /* tones:begin — generated by scripts/gen-ladders.mjs; edit its CATEGORY map, not these lines. */
218
248
  --tone-violet-bg: #392751;
219
249
  --tone-violet-bg-hover: #432b60;
@@ -281,7 +311,6 @@
281
311
  --surface-teal-border: #207e73;
282
312
  --surface-teal-text: #0bd8b6;
283
313
  /* tones:end */
284
-
285
314
  /* Run-mix ramp — the fills of a proportional status bar.
286
315
  *
287
316
  * Separate from the `--surface-*-text` tones because a 6px bar is read
@@ -314,7 +343,7 @@
314
343
  --glass-bg: var(--depth-2);
315
344
  --glass-bg-strong: var(--depth-3);
316
345
  --glass-border: var(--border-subtle);
317
- --glass-border-hover: var(--border-default);
346
+ --glass-border-hover: var(--border-hover);
318
347
  --glass-blur: 0px;
319
348
 
320
349
  /* Mesh glows — off; depth comes from the ladder, not from glow */
@@ -322,79 +351,28 @@
322
351
  --mesh-violet: transparent;
323
352
  --mesh-blue: transparent;
324
353
 
325
- /* Brand gradients — the indigo spark, kept ONLY for the logo mark + send button */
326
- --tangle-gradient: linear-gradient(135deg, #4C3FC5, #A5B4FC);
327
- --tangle-gradient-text: linear-gradient(135deg, #C7D2FE, #A5B4FC);
328
- --accent-gradient-strong: linear-gradient(135deg, #4C3FC5, #7C6CF6);
329
- /* Accent wells — soft = hover neutral, strong = active/selected row (indigo) */
330
- --accent-surface-soft: rgba(165, 180, 252, 0.07);
331
- --accent-surface-strong: rgba(76, 63, 197, 0.30);
332
- /* The BRIGHT indigo, and only ever as text: --primary is the fill (it carries
333
- * white), and at 6.7:1 on the card this is the one that stays legible as ink. */
334
- --accent-text: #A5B4FC;
335
-
336
- /* Surfaces — solid fills off the ladder (no radial mesh) */
337
- --bg-root: #0a0a14;
338
- --bg-dark: var(--depth-1);
339
- --bg-card: var(--depth-2);
340
- --bg-elevated: var(--depth-4);
341
- --bg-section: var(--depth-1);
342
- --bg-input: hsl(var(--hsl-input)); /* the field well — same color, hex-free */
343
- --bg-hover: rgba(165, 180, 252, 0.07);
344
- --bg-selection: rgba(124, 108, 246, 0.28);
345
-
346
- /* Text — neutral */
347
- /* Four steps the eye can actually separate: 10.6 / 7.8 / 6.4 / 5.4 against the
348
- * L2 card. --text-dim is the faintest tier but it is still TEXT, so it clears
349
- * the 4.5:1 body floor rather than only the 3:1 large-text one.
350
- *
351
- * Solved against the WHOLE ladder, not against one plane. A faint tier tuned
352
- * on the card alone still has to clear the floor on the nested and overlay
353
- * planes above it, which carry the modals, the drawers and every dropdown
354
- * menu — so both ends meet in the middle: the faint tiers sit high enough for
355
- * the overlay, and the planes above the card sit low enough for them. The
356
- * card, the chrome and the canvas are fixed points. tokens.test.ts asserts
357
- * every tier on every plane, so a retune cannot open a gap on one plane while
358
- * looking correct on another. */
359
- --text-primary: #e6e6e6;
360
- --text-secondary: #c7c7c7;
361
- --text-muted: #b5b5b5;
362
- --text-dim: #a5a5a5;
363
-
364
- /* Brand accents — Tangle */
365
- --brand-primary: #7C6CF6;
366
- --brand-strong: #2E2A5E;
367
- --brand-strong-text: #ffffff;
368
- --brand-strong-text-muted: rgba(255, 255, 255, 0.7);
369
- --brand-strong-text-dim: rgba(255, 255, 255, 0.5);
370
- --brand-cool: #818CF8;
371
- --brand-glow: #A5AAFC;
372
- --brand-purple: #C7D2FE;
373
- --brand-vibrant: #6366F1;
374
- --brand-emerald: #10B981;
375
-
376
- /* Borders — neutral hairlines */
377
- --border-subtle: rgba(255, 255, 255, 0.10);
378
- --border-default: rgba(255, 255, 255, 0.16);
379
- --border-hover: rgba(255, 255, 255, 0.28);
380
- --border-accent: rgba(129, 140, 248, 0.35);
381
- --border-accent-hover: rgba(129, 140, 248, 0.50);
382
-
383
- /* Buttons */
384
- --btn-primary-bg: #4C3FC5;
385
- --btn-primary-hover: #5A50D6;
386
- --btn-primary-text: #ffffff;
387
- --btn-cta-bg: #4C3FC5;
388
- --btn-cta-text: #FFFFFF;
389
-
390
- /* Code / runtime syntax */
391
- --code-keyword: #A5AAFC;
392
- --code-string: #34D399;
393
- --code-function: #6D9FFF;
394
- --code-number: #FFB347;
395
- --code-success: #10B981;
396
- --code-comment: #606294;
397
- --code-error: #FF4D6D;
354
+ /* ── Code ───────────────────────────────────────────────────────────────
355
+ * The same inks as the status family and the accent, so a keyword, a
356
+ * string and a warning never carry three different purples or ambers.
357
+ * Keywords take the categorical violet, not the accent: a keyword is not a
358
+ * link. --code-* is the older name for the same roles. */
359
+ --code-header-bg: hsl(var(--hsl-background));
360
+ --syntax-foreground: var(--text-primary);
361
+ --syntax-variable: var(--text-secondary);
362
+ --syntax-comment: var(--text-dim);
363
+ --syntax-keyword: var(--tone-violet-icon);
364
+ --syntax-meta: var(--accent-text);
365
+ --syntax-function: var(--surface-info-text);
366
+ --syntax-string: var(--surface-success-text);
367
+ --syntax-number: var(--surface-warning-text);
368
+ --syntax-error: var(--surface-danger-text);
369
+ --code-keyword: var(--syntax-keyword);
370
+ --code-string: var(--syntax-string);
371
+ --code-function: var(--syntax-function);
372
+ --code-number: var(--syntax-number);
373
+ --code-success: var(--surface-success-text);
374
+ --code-comment: var(--syntax-comment);
375
+ --code-error: var(--syntax-error);
398
376
 
399
377
  /* Shadows — flat, no inner ring, no indigo glow */
400
378
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3);
@@ -469,12 +447,13 @@
469
447
  --transcript-font-size: 1rem; /* 16px: the chat reading size */
470
448
  --transcript-line-height: var(--line-height-base);
471
449
 
472
- /* Typography scale — override per-app for custom type hierarchy.
473
- Components reference these instead of hardcoded pixel values. */
474
- --font-size-xs: 0.6875rem; /* 11px — labels, timestamps */
475
- --font-size-sm: 0.75rem; /* 12px — captions, meta */
476
- --font-size-base: 0.9375rem; /* 15px — message body */
477
- --font-size-lg: 1rem; /* 16px — emphasized text */
450
+ /* Typography scale. Each --font-size-<step> is Tailwind's text-<step>, so a
451
+ step name means one size everywhere ("sm" is 14px); 12px is the floor.
452
+ Components reference these instead of pixel values; an app may override. */
453
+ --font-size-xs: 0.75rem; /* 12px — meta, timestamps, eyebrows */
454
+ --font-size-sm: 0.875rem; /* 14px — UI text */
455
+ --font-size-base: 1rem; /* 16px — reading text */
456
+ --font-size-lg: 1.125rem; /* 18px — emphasized text */
478
457
  --font-size-xl: 1.25rem; /* 20px — section title (h2) */
479
458
  /* Display tier — page titles, marketing section heads, and heroes.
480
459
  Hero sizes are fluid (clamp) so they scale with the viewport without
@@ -531,18 +510,6 @@
531
510
  --tool-card-py: 0.5rem;
532
511
  --tool-icon-size: 1.75rem;
533
512
 
534
- /* Syntax highlighting — the light palette is authored below. */
535
- --code-header-bg: hsl(var(--hsl-background));
536
- --syntax-comment: #9ea2cc;
537
- --syntax-keyword: #A78FFF;
538
- --syntax-string: #10b981;
539
- --syntax-function: #6D9FFF;
540
- --syntax-number: #FFB347;
541
- --syntax-meta: #8263FF;
542
- --syntax-error: #FF4D6D;
543
- --syntax-variable: #C4C0D8;
544
- --syntax-foreground: #E8E6F6;
545
-
546
513
  /* Shared motion vocabulary, previously authored by the light-default Agent
547
514
  * App consumer. These are tokens only, not automatic animation classes. */
548
515
  --duration-instant: 90ms;
@@ -599,130 +566,83 @@
599
566
  [data-theme="agents-light"],
600
567
  .light {
601
568
  color-scheme: light;
602
- /* Light theme — neutral surfaces with purple accents */
603
- --code-header-bg: color-mix(in srgb, hsl(var(--hsl-muted)) 50%, transparent);
604
- --syntax-foreground: #24243a;
605
- --syntax-comment: #6b7280;
606
- --syntax-keyword: #7c3aed;
607
- --syntax-string: #047857;
608
- --syntax-function: #2563eb;
609
- --syntax-number: #b45309;
610
- --syntax-meta: #6d28d9;
611
- --syntax-variable: #334155;
612
- --syntax-error: #dc2626;
613
569
 
614
- /* MD3 surface hierarchy */
615
- /* The light ladder ALTERNATES; it is not monotonic, and that is deliberate.
616
- * On a neutral canvas, a raised plane is PAPER (white) and a recessed one is a
617
- * neutral WELL. So card and overlay are both white and separate from each other
618
- * by shadow and ring, not by fill — which is how light-mode elevation actually
619
- * reads. Forcing a strictly-darkening ladder here is what produced the
620
- * white-on-white flatness this palette replaced. Pinned in tokens.test.ts. */
621
- --md3-surface: #eceef3; /* L0 page canvas — cool grey, BELOW the wells */
622
- --md3-surface-dim: #dcdee6;
570
+ /* ── Planes: white paper on a cool grey canvas ───────────────────────────
571
+ * The light ladder ALTERNATES, and that is deliberate: a raised plane is
572
+ * PAPER and a recessed one is a neutral WELL, so the card and the overlay
573
+ * are both white and separate by shadow and ring, not by fill. Forcing a
574
+ * strictly darkening ladder here is what produced the white-on-white
575
+ * flatness this palette replaced. Pinned in tokens.test.ts. */
576
+ --hsl-background: 223 23% 94%; /* L0 canvas #eceef3, BELOW the wells */
577
+ --hsl-input: 225 29% 97%; /* L1 chrome and field wells #f5f6fa */
578
+ --hsl-card: 0 0% 100%; /* L2 card: paper */
579
+ --hsl-secondary: 228 25% 94.5%; /* L3 raised: a card in a card #edeff4, 4.8 L under its paper */
580
+ --hsl-popover: var(--hsl-card); /* L4 overlay: paper, lifted by shadow, never out-lightening a card */
581
+ --md3-surface-dim: #dcdee6; /* the recessed page */
582
+ --md3-surface-container-lowest: hsl(var(--hsl-card)); /* insets read as paper */
623
583
  --md3-surface-bright: #ffffff;
624
- --md3-surface-container-lowest: #ffffff; /* insets read as paper */
625
- --md3-surface-container-low: #f6f7fa; /* L1 chrome, field wells — well */
626
- --md3-surface-container: #ffffff; /* L2 resting card — paper */
627
- --md3-surface-container-high: #f1f2f7; /* L3 nested / hover — well */
628
- --md3-surface-container-highest: #ffffff; /* L4 overlay — paper + shadow */
629
- --md3-surface-variant: #f1f2f7;
630
- --md3-on-surface: #2d2d2d;
631
- --md3-on-surface-variant: #4c4c4c;
632
-
633
- --md3-primary: #3E349E;
634
- --md3-primary-dim: #352B88;
635
- --md3-primary-container: #352B88;
636
- --md3-on-primary: #ffffff;
637
- --md3-on-primary-container: #9B93F0;
638
- --md3-primary-fixed: #E8E5FF;
639
- --md3-primary-fixed-dim: #C5C0F0;
640
-
641
- --md3-secondary: #524A78;
642
- --md3-secondary-dim: #3E3660;
643
- --md3-secondary-container: #E8E5FF;
644
- --md3-on-secondary: #ffffff;
645
- --md3-on-secondary-container: #524A78;
646
-
647
- --md3-tertiary: #065F46;
648
- --md3-tertiary-container: #D1FAE5;
649
- --md3-outline: #787878;
650
- --md3-outline-variant: #c7c6d6;
651
-
652
- --md3-error: #ba1a1a;
653
- --md3-error-dim: #93000a;
654
- --md3-error-container: #ffdad6;
655
- --md3-on-error: #ffffff;
656
- --md3-on-error-container: #93000a;
657
-
584
+ --sidebar-background: var(--hsl-card); /* paper, flat, with a hairline */
658
585
  --md3-inverse-surface: #2e2e3e;
659
- --md3-inverse-primary: #A5AAFC;
660
586
  --md3-inverse-on-surface: #f0f1f4;
661
- --md3-surface-tint: #6366F1;
662
587
 
663
- /* Semantic HSL bridge — white paper on an achromatic canvas */
664
- --hsl-background: 223 23% 94%; /* L0 canvas #eceef3 */
665
- /* Neutral ink rather than pure black keeps the original 13.7:1 contrast on
666
- * white paper and 11.4:1 on the canvas: AAA with headroom in both places. */
667
- --hsl-foreground: 0 0% 17.6471%; /* #2d2d2d */
668
- --hsl-card: 0 0% 100%; /* L2 card = white paper */
669
- --hsl-card-foreground: 0 0% 17.6471%;
670
- --hsl-popover: 0 0% 100%;
671
- --hsl-popover-foreground: 0 0% 17.6471%;
672
- --hsl-primary: 246 54% 51%; /* #4c3fc6 — carries white at 7.4:1 */
673
- --hsl-primary-foreground: 0 0% 100%;
674
- --hsl-secondary: 230 27% 96%; /* L3 #f2f3f8 */
675
- --hsl-secondary-foreground: 0 0% 17.6471%;
676
- --hsl-muted: 230 27% 96%; /* L3 hover well */
677
- /* The shared components fade captions with /70 and /60, so a translucent
678
- * foreground renders composited over the surface behind it. This neutral value
679
- * keeps 3.0:1 at /60 and 8.6:1 at full strength on paper. */
680
- --hsl-muted-foreground: 0 0% 29.8039%; /* #4c4c4c text-muted */
681
- --hsl-accent: 230 27% 96%; /* neutral hover */
682
- --hsl-accent-foreground: 0 0% 17.6471%;
683
- --hsl-destructive: 0 72% 41%;
684
- --hsl-destructive-foreground: 0 0% 100%;
685
- --hsl-border: 244 16% 81%; /* #c8c7d6, the --md3-outline-variant hairline */
686
- --hsl-input: 225 29% 97%; /* field well #f5f6fa */
687
- --hsl-ring: 246 54% 51%;
688
- --hsl-success: 160 84% 30%;
689
- --hsl-warning: 41 96% 50%;
690
- --hsl-info: 212 80% 50%;
691
-
692
- /* depth-2 (card) and depth-4 (elevated) are BOTH paper, on purpose: in light,
693
- * a popover does not out-lighten a white card — it lifts by shadow. See the
694
- * ladder note above. */
695
- --depth-1: #eceef3; /* canvas */
696
- --depth-2: #ffffff; /* card — paper */
697
- --depth-3: #f1f2f7; /* nested well */
698
- --depth-4: #ffffff; /* elevated — paper, separated by shadow */
699
-
700
- --status-running: #059669;
701
- --status-creating: #3E349E;
702
- --status-stopped: #B45309;
703
- --status-warm: #C05621;
704
- --status-cold: #1D6FA4;
705
- --status-error: #ba1a1a;
706
- --status-deleted: #6B6B82;
588
+ /* ── Ink: neutral grey, so purple stays reserved for interaction ─────────
589
+ * 13.7:1 on paper and 11.4:1 on the canvas, AAA with headroom. --text-dim is
590
+ * pinned from the CANVAS, the lighter plane that binds the faintest tier:
591
+ * 4.6:1 there and 5.6:1 on the card, asserted in tokens.test.ts. */
592
+ --hsl-foreground: 0 0% 17.6471%; /* #2d2d2d */
593
+ --hsl-muted-foreground: 0 0% 29.8039%; /* #4c4c4c: 3.0:1 at /60 and 8.6:1 at full strength on paper */
594
+ --text-secondary: #3f3f3f;
595
+ --text-muted: #4c4c4c;
596
+ --text-dim: #686868;
597
+ --md3-outline: #787878;
707
598
 
708
- --glass-bg: var(--depth-2);
709
- --glass-bg-strong: var(--depth-1);
710
- --glass-border: var(--border-subtle);
711
- --glass-border-hover: var(--border-default);
599
+ /* ── Hairline: 1.7:1 on paper, 1.4:1 on the canvas ──────────────────────── */
600
+ --hsl-border: 244 16% 81%; /* #c8c7d6 */
601
+ --border-hover: rgba(0, 0, 0, 0.24);
712
602
 
713
- /* Status surface overrides — light values */
603
+ /* ── Accent ─────────────────────────────────────────────────────────────
604
+ * The fill is the same indigo as dark (it carries white at 7.4:1) and is
605
+ * also the ring: on paper it clears 3:1 as a line. The ink is the bright
606
+ * indigo at 6.1:1 on paper; the mark is the website's indigo. */
607
+ --hsl-ring: var(--hsl-primary);
608
+ --brand-primary: #6366F1;
609
+ --brand-purple: var(--brand-primary);
610
+ --accent-text: #3c32e9;
611
+ --md3-primary: var(--accent-text);
612
+ --md3-primary-dim: var(--btn-primary-bg);
613
+ --md3-on-primary-container: #ffffff;
614
+ --md3-primary-fixed: #E8E5FF; /* the accent tint: a chip or a selected row on paper */
615
+ --md3-primary-fixed-dim: color-mix(in srgb, var(--md3-primary-fixed) 85%, var(--accent-text));
616
+ --md3-secondary: var(--accent-text);
617
+ --md3-secondary-dim: var(--btn-primary-bg);
618
+ --md3-secondary-container: var(--md3-primary-fixed);
619
+ --md3-on-secondary: #ffffff;
620
+ --md3-on-secondary-container: var(--accent-text);
621
+ --md3-inverse-primary: #A5B4FC;
622
+ --md3-surface-tint: var(--brand-primary);
623
+ --accent-surface-soft: color-mix(in srgb, var(--btn-primary-bg) 5%, transparent);
624
+ --accent-surface-strong: color-mix(in srgb, var(--btn-primary-bg) 13%, transparent);
625
+ --bg-selection: color-mix(in srgb, var(--btn-primary-bg) 16%, transparent);
626
+ --border-accent: color-mix(in srgb, var(--btn-primary-hover) 18%, transparent);
627
+ --border-accent-hover: color-mix(in srgb, var(--btn-primary-hover) 35%, transparent);
628
+ --tangle-gradient: linear-gradient(135deg, var(--btn-primary-bg), var(--btn-primary-hover));
629
+ --tangle-gradient-text: var(--tangle-gradient);
630
+ --accent-gradient-strong: var(--tangle-gradient);
631
+
632
+ /* ── Status: the deep inks carry white, so each is its own fill too ─────── */
633
+ --hsl-success: 162.9 93.5% 24.3%; /* #047857 */
634
+ --hsl-warning: 25.9 90% 35.3%; /* #ab4f09 */
635
+ --hsl-destructive: 0 73.7% 41.8%; /* #b91c1c */
636
+ --hsl-destructive-foreground: 0 0% 100%;
637
+ --hsl-info: 201.3 96.3% 32.2%; /* #0369a1 */
714
638
  --surface-success-bg: #ecfdf5;
715
639
  --surface-success-border: #a7f3d0;
716
- --surface-success-text: #047857;
717
640
  --surface-warning-bg: #fffbeb;
718
641
  --surface-warning-border: #fde68a;
719
- --surface-warning-text: #ab4f09;
720
642
  --surface-danger-bg: #fff1f2;
721
643
  --surface-danger-border: #fecdd3;
722
- --surface-danger-text: #b91c1c;
723
644
  --surface-info-bg: #f0f9ff;
724
645
  --surface-info-border: #bae6fd;
725
- --surface-info-text: #0369a1;
726
646
  /* tones:begin — generated by scripts/gen-ladders.mjs; edit its CATEGORY map, not these lines. */
727
647
  --tone-violet-bg: #f7edfe;
728
648
  --tone-violet-bg-hover: #f2e2fc;
@@ -790,7 +710,6 @@
790
710
  --surface-teal-border: #a1ded2;
791
711
  --surface-teal-text: #017d6d;
792
712
  /* tones:end */
793
-
794
713
  /* Run-mix ramp — the fills of a proportional status bar.
795
714
  *
796
715
  * Separate from the `--surface-*-text` tones because a 6px bar is read
@@ -815,119 +734,14 @@
815
734
  --run-mix-in-flight: #00679e;
816
735
  --run-mix-cancelled: #515367;
817
736
 
818
- --surface-neutral-bg: var(--depth-3);
819
- --surface-neutral-border: var(--border-subtle);
820
- --surface-neutral-text: var(--text-muted);
821
-
822
- /* Sidebar — light, flat surface + hairline */
823
- --sidebar-background: 0 0% 100%; /* white paper */
824
- --sidebar-foreground: 0 0% 17.6471%;
825
- --sidebar-primary: 246 54% 51%; /* #4c3fc6 */
826
- --sidebar-primary-foreground: 0 0% 100%;
827
- --sidebar-accent: 0 0% 94.902%; /* L3 hover well */
828
- --sidebar-accent-foreground: 0 0% 17.6471%;
829
- --sidebar-border: 0 0% 81.5686%; /* = --md3-outline-variant */
830
- --sidebar-ring: 246 54% 51%;
831
-
832
- --mesh-teal: transparent;
833
- --mesh-violet: transparent;
834
- --mesh-blue: transparent;
835
-
836
- /* Surfaces — flat single-neutral light */
837
- --bg-root: #eceef3;
838
- --bg-dark: var(--depth-1);
839
- --bg-card: var(--depth-2);
840
- /* Paper, exactly like --bg-card: in light you cannot raise a plane by making it
841
- * lighter than white, so an overlay lifts by SHADOW. A consumer that fills with
842
- * --bg-elevated and applies no shadow gets no separation from a card — that is
843
- * the contract, not an oversight (--hsl-popover is white for the same reason).
844
- * Pair it with --shadow-dropdown. */
845
- --bg-elevated: var(--depth-4);
846
- --bg-section: var(--depth-1);
847
- --bg-input: hsl(var(--hsl-input)); /* the field well — same color, hex-free */
848
- --bg-hover: rgba(76, 63, 198, 0.06);
849
- --bg-selection: rgba(76, 63, 198, 0.16);
850
-
851
- /* Mirrors the dark ramp's spacing against white paper. The ink uses neutral
852
- * grays so purple stays reserved for interaction accents.
853
- *
854
- * --text-dim is pinned from the CANVAS (#eceef3), not the white card: it is
855
- * the faintest tier, so the lighter L0 is the surface that binds it. Its 4.6:1
856
- * canvas and 5.6:1 card contrasts clear the body-text floor, as asserted in
857
- * tokens.test.ts, because a ratio quoted in a comment drifts the moment a
858
- * surface moves and nothing catches it. */
859
- --text-primary: #2d2d2d;
860
- --text-secondary: #3f3f3f;
861
- --text-muted: #4c4c4c;
862
- --text-dim: #686868;
863
-
864
- --brand-primary: #6366F1;
865
- --brand-strong: #2E2A5E;
866
- --brand-strong-text: #ffffff;
867
- --brand-strong-text-muted: rgba(255, 255, 255, 0.7);
868
- --brand-strong-text-dim: rgba(255, 255, 255, 0.5);
869
- --brand-cool: #818CF8;
870
- --brand-glow: #A5AAFC;
871
- --brand-purple: #6366F1;
872
- --brand-vibrant: #6366F1;
873
- --brand-emerald: #059669;
874
-
875
- --border-subtle: rgba(0, 0, 0, 0.10);
876
- --border-default: rgba(0, 0, 0, 0.14);
877
- --border-hover: rgba(0, 0, 0, 0.24);
878
- --border-accent: rgba(91, 78, 212, 0.18);
879
- --border-accent-hover: rgba(91, 78, 212, 0.35);
880
-
881
- --btn-primary-bg: #4C3FC5;
882
- --btn-primary-hover: #5A50D6;
883
- --btn-primary-text: #ffffff;
884
- --btn-cta-bg: #4C3FC5;
885
- --btn-cta-text: #ffffff;
886
-
887
- --code-keyword: #3E349E;
888
- --code-string: #059669;
889
- --code-function: #352B88;
890
- --code-number: #B45309;
891
- --code-success: #059669;
892
- --code-comment: #6B6E8A;
893
- --code-error: #ba1a1a;
737
+ --glass-bg-strong: var(--depth-1);
738
+ --code-header-bg: color-mix(in srgb, hsl(var(--hsl-muted)) 50%, transparent);
894
739
 
895
740
  /* Shadows — flat, no inner ring, no glow */
896
741
  --shadow-card: 0 1px 2px rgba(25, 28, 36, 0.06);
897
742
  --shadow-dropdown: 0 8px 30px rgba(25, 28, 36, 0.10);
898
743
  --shadow-glow: none;
899
744
  --shadow-accent: 0 1px 2px rgba(25, 28, 36, 0.06);
900
-
901
- /* Indigo spark kept for logo + send; accent-fill is the active row well */
902
- --tangle-gradient: linear-gradient(135deg, #4F46E5, #5B4ED4);
903
- --tangle-gradient-text: linear-gradient(135deg, #4F46E5, #5B4ED4);
904
- --accent-gradient-strong: linear-gradient(135deg, #4F46E5, #5B4ED4);
905
- --accent-surface-soft: rgba(76, 63, 198, 0.05);
906
- --accent-surface-strong: rgba(76, 63, 198, 0.13);
907
- --accent-text: #3c32e9;
908
-
909
- /* Typography — unified on Inter */
910
- --font-sans: "Inter Variable", "Inter", "Geist", ui-sans-serif, system-ui, -apple-system, sans-serif;
911
- --font-display: "Inter Variable", "Inter", "Geist", sans-serif;
912
- --font-mono: "Geist Mono", "JetBrains Mono", ui-monospace, monospace;
913
-
914
- /* Radius — unified chrome scale (matches dark; 8px md) */
915
- --radius: 0.5rem;
916
- --radius-sm: 6px;
917
- --radius-md: 8px;
918
- --radius-lg: 10px;
919
- --radius-xl: 12px;
920
- --radius-pill: 999px;
921
- --radius-full: 999px;
922
- --radius-composer: 26px;
923
- /* Radius by role. New surfaces pick the role they play; system.css repeats
924
- these for pages that load it on its own ladders. */
925
- --radius-tag: 4px;
926
- --radius-chip: 8px;
927
- --radius-field: 12px;
928
- --radius-panel: 16px;
929
- --radius-cover: 20px;
930
- --radius-sheet: 24px;
931
745
  }
932
746
 
933
747
 
@@ -978,15 +792,13 @@
978
792
  * level in, --hsl-raised-2 two levels in.
979
793
  * --well the field fill: --well-page on the page, --well-card in a card,
980
794
  * --well-raised in a card in a card, --well-raised-2 deeper still.
981
- * A theme that tunes its ladder (Hospitality) sets these per mode. Every other
982
- * theme gets wells derived from its own page and card, so a field always sits
983
- * at least 3 OKLCH L points off the plane it is on (agent-app-signoff
984
- * surface-ladder): dark wells recess below the page (page + 30% black: -4.6 L on
985
- * the page, -10.9 L in a card on the spine); light wells take the opposite plane
986
- * (the card on the page, the page in a card: 5 L either way). The old shared
987
- * --bg-input sat +3.1 above the dark page and -2.6 under the white card. Raised
988
- * levels still paint the card here: --hsl-raised is an HSL triplet and cannot
989
- * be derived per theme.
795
+ * The canonical ladder is the spine's own planes: raised is L3 (--hsl-secondary),
796
+ * raised-2 is L4 (--hsl-popover), and each well is a plane at least 4.4 OKLCH L
797
+ * off the level it fills. Dark wells always sit deeper (page + 30% black on the
798
+ * page and in a card, the card in a raised plane); light wells take the
799
+ * opposite plane (paper on a grey level, the canvas on paper). Dark raised
800
+ * planes also carry a 1px top highlight. Hospitality tunes its own ladder; the
801
+ * other named themes keep a flat one (below) until theirs is tuned.
990
802
  *
991
803
  * INTERACTION. Four states, all from the focus ring hue (--hsl-ring), so one
992
804
  * green or one indigo marks every interactive thing in a theme:
@@ -1004,20 +816,20 @@
1004
816
  [data-sandbox-ui],
1005
817
  [data-sandbox-theme],
1006
818
  [data-theme] {
1007
- --hsl-raised: var(--hsl-card);
1008
- --hsl-raised-2: var(--hsl-card);
819
+ --hsl-raised: var(--hsl-secondary);
820
+ --hsl-raised-2: var(--hsl-popover);
1009
821
  --well-page: color-mix(in oklab, hsl(var(--hsl-background)) 70%, black);
1010
822
  --well-card: var(--well-page);
1011
- --well-raised: var(--well-page);
1012
- --well-raised-2: var(--well-page);
823
+ --well-raised: hsl(var(--hsl-card));
824
+ --well-raised-2: var(--well-raised);
1013
825
  --well: var(--well-page);
1014
826
  /* The hairline is 1.5:1 by design; a field edge is non-text UI and needs 3:1.
1015
- * 45% toward the ink measures 4.0:1 on the spine card in both modes. */
827
+ * 45% toward the ink clears 4:1 on the spine card in both modes. */
1016
828
  --field-border: color-mix(in oklab, hsl(var(--hsl-border)), hsl(var(--hsl-foreground)) 45%);
1017
829
  --shadow-raised: var(--shadow-card);
1018
830
  /* A shadow token joins a comma list, so "none" would void the whole list. */
1019
831
  --shadow-well: 0 0 0 0 transparent;
1020
- --edge-highlight: 0 0 0 0 transparent;
832
+ --edge-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.06);
1021
833
  --ix-hover: 0 0 0 1px hsl(var(--hsl-ring) / 0.45), 0 0 0 4px hsl(var(--hsl-ring) / 0.16);
1022
834
  --ix-hover-border: hsl(var(--hsl-ring) / 0.7);
1023
835
  --ix-press: 0 0 0 1px hsl(var(--hsl-ring) / 0.95), 0 0 0 4px hsl(var(--hsl-ring) / 0.3);
@@ -1041,8 +853,20 @@
1041
853
  --ix-hover-bg: hsl(var(--hsl-ring) / 0.07);
1042
854
  --well-page: hsl(var(--hsl-card));
1043
855
  --well-card: hsl(var(--hsl-background));
1044
- --well-raised: hsl(var(--hsl-background));
1045
- --well-raised-2: hsl(var(--hsl-background));
856
+ --well-raised-2: var(--well-card);
857
+ --edge-highlight: 0 0 0 0 transparent;
858
+ }
859
+
860
+ /* Named themes with their own planes keep a flat ladder until each is tuned as
861
+ * Hospitality was: a nested card paints the card, and its field the card's well. */
862
+ [data-theme="aubergine"], [data-theme="aubergine-light"], [data-theme="arena"], [data-theme="arena-light"],
863
+ [data-theme="super"], [data-theme="super-light"], [data-theme="tangle-light"], [data-theme="agents"],
864
+ [data-theme="agents-light"], [data-theme="website"], [data-theme="intelligence"] {
865
+ --hsl-raised: var(--hsl-card);
866
+ --hsl-raised-2: var(--hsl-card);
867
+ --well-raised: var(--well-card);
868
+ --well-raised-2: var(--well-card);
869
+ --edge-highlight: 0 0 0 0 transparent;
1046
870
  }
1047
871
 
1048
872
  /* Surface context: keyed only on the Card primitive's data-slot, never on app