@tangle-network/brand 1.0.0 → 1.3.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.
@@ -1,29 +1,60 @@
1
- /* Tangle Sandbox UI design tokens */
2
- /* Brand: warm purple palette, #5B4ED4 primary */
1
+ /* Tangle design tokens — the single palette every Tangle surface renders from.
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.6: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.
8
+ *
9
+ * Apps consume this spine as-is. If a product needs its own accent identity,
10
+ * add a named theme in named-themes.css (`data-theme="<name>"`) — do NOT re-declare
11
+ * these tokens in the app, or its surfaces silently stop tracking the brand. */
3
12
 
4
13
  /* MD3 surface hierarchy */
5
14
  /* Dark is the default theme (`:root`/`[data-sandbox-ui]`). It also responds to
6
15
  * the standard `[data-theme="dark"]` / `.dark` switch so any consumer can pin or
7
16
  * 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 sits OFF pure black (#151523). A near-black page under near-white
26
+ * ink is the glare end of the range, and it is the reading surface for long
27
+ * sessions.
28
+ *
29
+ * Elevation is carried by surface lightness plus a hairline that can actually be
30
+ * seen — a black shadow on a dark canvas renders as nothing, so shadow does no
31
+ * work here. The hairline is 1.6:1 against the resting card — quiet by design,
32
+ * enough to state an edge without drawing a box around every panel.
33
+ */
8
34
  :root,
9
35
  [data-sandbox-ui],
10
36
  [data-theme="dark"],
11
37
  .dark {
12
- --md3-surface: #16161f;
13
- --md3-surface-dim: #111119;
14
- --md3-surface-bright: #2a2940;
15
- --md3-surface-container-lowest: #101017;
16
- --md3-surface-container-low: #16161f;
17
- --md3-surface-container: #1c1b27;
18
- --md3-surface-container-high: #232231;
19
- --md3-surface-container-highest: #2c2b3d;
20
- --md3-surface-variant: #2c2b3d;
21
- --md3-on-surface: #fdfdfd;
22
- --md3-on-surface-variant: #aaa8b8;
23
-
24
- --md3-primary: #818CF8;
25
- --md3-primary-dim: #6366F1;
26
- --md3-primary-container: #4F46E5;
38
+ --md3-surface: #151523; /* L0 page canvas */
39
+ --md3-surface-dim: #0f0f1a; /* recessed page */
40
+ --md3-surface-bright: #434365;
41
+ /* Same value as --md3-surface-dim by design, not by drift: both name the floor
42
+ * of the ladder, and dark has no plane below it to give them. They stay two
43
+ * tokens because the roles diverge in light, where the well is paper (#ffffff)
44
+ * and the recessed page is grey (#d9d9e6) — a consumer binding to the wrong one
45
+ * there gets the wrong surface. */
46
+ --md3-surface-container-lowest: #0f0f1a; /* L-1 terminal/code well */
47
+ --md3-surface-container-low: #222235; /* L1 chrome, field wells */
48
+ --md3-surface-container: #2d2e43; /* L2 resting card */
49
+ --md3-surface-container-high: #363654; /* L3 nested / hover */
50
+ --md3-surface-container-highest: #434365; /* L4 overlay */
51
+ --md3-surface-variant: #363654;
52
+ --md3-on-surface: #e3e6ed;
53
+ --md3-on-surface-variant: #a5abbb;
54
+
55
+ --md3-primary: #A5B4FC;
56
+ --md3-primary-dim: #7C6CF6;
57
+ --md3-primary-container: #4C3FC5;
27
58
  --md3-on-primary: #ffffff;
28
59
  --md3-on-primary-container: #E0E7FF;
29
60
  --md3-primary-fixed: #A5AAFC;
@@ -36,8 +67,8 @@
36
67
  --md3-on-secondary-container: #E8E5FF;
37
68
 
38
69
  --md3-tertiary: #10B981;
39
- --md3-outline: #4a4965;
40
- --md3-outline-variant: #34324a;
70
+ --md3-outline: #898da9;
71
+ --md3-outline-variant: #474a6b;
41
72
 
42
73
  --md3-error: #ff5277;
43
74
  --md3-error-dim: #eb2855;
@@ -46,30 +77,43 @@
46
77
  --md3-on-error-container: #ffbdf2;
47
78
 
48
79
  --md3-inverse-surface: #fdfdfd;
49
- --md3-inverse-primary: #4F46E5;
80
+ --md3-inverse-primary: #4C3FC5;
50
81
  --md3-inverse-on-surface: #000000;
51
- --md3-surface-tint: #818CF8;
52
-
53
- /* Semantic HSL bridge — flat neutral spine (Tangle Quiet) */
54
- --hsl-background: 240 7% 9%; /* #15151a canvas — lifted off pure black */
55
- --hsl-foreground: 240 6% 93%; /* #ececee */
56
- --hsl-card: 240 5% 13%; /* #1f1f23 surface, clearly above canvas */
57
- --hsl-card-foreground: 240 6% 93%;
58
- --hsl-popover: 240 5% 17%; /* #28282d elevated */
59
- --hsl-popover-foreground: 240 6% 93%;
60
- --hsl-primary: 239 84% 62%;
82
+ --md3-surface-tint: #A5B4FC;
83
+
84
+ /* Semantic HSL bridge — the neutral-grey surface ladder */
85
+ --hsl-background: 240 24% 11%; /* L0 canvas #151523 */
86
+ /* 92% L, not pure white: near-white ink on the canvas is the glare end of the
87
+ * range, and the tiers below it need room to separate. 91% holds 10.6:1 on the
88
+ * card — AAA with headroom — and leaves four ink steps the eye can tell apart. */
89
+ --hsl-foreground: 225 22% 91%; /* #e3e6ed */
90
+ --hsl-card: 238 20% 22%; /* L2 card #2d2e43 */
91
+ --hsl-card-foreground: 225 22% 91%;
92
+ --hsl-popover: 240 22% 27%; /* L4 overlay */
93
+ --hsl-popover-foreground: 225 22% 91%;
94
+ /* The fill indigo, NOT the bright one: `.btn-primary` puts white on this, and
95
+ * #6366F1 carries white at only 4.47:1. This clears the floor at 7.4:1. The
96
+ * bright indigo stays available for accent TEXT via --accent-text. */
97
+ --hsl-primary: 246 54% 51%; /* #4c3fc6 */
61
98
  --hsl-primary-foreground: 0 0% 100%;
62
- --hsl-secondary: 240 5% 15%; /* #242428 surface-2 */
63
- --hsl-secondary-foreground: 240 6% 93%;
64
- --hsl-muted: 240 5% 15%; /* #242428 — neutral hover well */
65
- --hsl-muted-foreground: 240 4% 64%; /* #9c9ca6 text-muted */
66
- --hsl-accent: 240 5% 15%; /* neutral hover (indigo lives in accent-surface-strong) */
67
- --hsl-accent-foreground: 240 6% 93%;
99
+ --hsl-secondary: 240 22% 27%; /* L3 #363654 */
100
+ --hsl-secondary-foreground: 225 22% 91%;
101
+ --hsl-muted: 240 22% 27%; /* L3 hover well */
102
+ --hsl-muted-foreground: 223 14% 69%; /* #a5abbb — high enough that the
103
+ reduced-opacity caption derivatives (/70, /60) still clear the large-text
104
+ contrast floor against the L2 card. */
105
+ --hsl-accent: 240 22% 27%; /* neutral hover (indigo lives in accent-surface-strong) */
106
+ --hsl-accent-foreground: 225 22% 91%;
68
107
  --hsl-destructive: 348 74% 48%;
69
108
  --hsl-destructive-foreground: 0 0% 100%;
70
- --hsl-border: 240 5% 16%; /* soft edge — fill carries separation, not the line */
71
- --hsl-input: 240 5% 15%;
72
- --hsl-ring: 239 84% 67%;
109
+ /* Quiet on purpose: 1.6:1 against the resting card. Shadow does no work on a
110
+ * dark canvas, so the hairline is what states where a plane ends. */
111
+ --hsl-border: 236 20% 35%; /* #474a6b = --md3-outline-variant */
112
+ --hsl-input: 240 24% 15%; /* recessed field well */
113
+ /* Brighter than --primary on purpose: the ring is NON-TEXT and has to clear
114
+ * 3:1 against BOTH canvas and card. The fill indigo is tuned to carry white
115
+ * text instead, so the two cannot be the same value. */
116
+ --hsl-ring: 233 100% 87%;
73
117
  --hsl-success: 160 84% 39%;
74
118
  --hsl-warning: 40 94% 56%;
75
119
  --hsl-info: 200 88% 56%;
@@ -92,6 +136,11 @@
92
136
  --destructive: var(--hsl-destructive);
93
137
  --destructive-foreground: var(--hsl-destructive-foreground);
94
138
  --border: var(--hsl-border);
139
+ /* shadcn's `--input` is the input BORDER and the Switch's off-track — a
140
+ * mid-tone that has to stay visible AGAINST a surface. It is deliberately NOT
141
+ * the field fill: pointing it at the recessed well would tint a border and a
142
+ * track the same color as the plane behind them, and both would vanish. The
143
+ * field fill is --hsl-input / --bg-input below. */
95
144
  --input: var(--hsl-muted-foreground);
96
145
  --ring: var(--hsl-ring);
97
146
  --success: var(--hsl-success);
@@ -99,25 +148,26 @@
99
148
  --error: var(--hsl-destructive);
100
149
  --info: var(--hsl-info);
101
150
 
102
- /* Sidebar tokens — flat surface + hairline */
103
- --sidebar-background: 240 6% 11%; /* #1a1a1f surface */
104
- --sidebar-foreground: 240 6% 93%;
105
- --sidebar-primary: 239 84% 67%;
151
+ /* Sidebar tokens — L1 chrome: one step BELOW the canvas here, so the nav reads
152
+ * as chrome the content sits in front of rather than as another card. */
153
+ --sidebar-background: 242 26% 8%; /* L1 chrome #0f0f1a */
154
+ --sidebar-foreground: 225 22% 91%;
155
+ --sidebar-primary: 246 54% 51%;
106
156
  --sidebar-primary-foreground: 0 0% 100%;
107
- --sidebar-accent: 240 5% 15%; /* neutral hover */
108
- --sidebar-accent-foreground: 240 6% 93%;
109
- --sidebar-border: 240 5% 16%; /* soft edge */
110
- --sidebar-ring: 239 84% 67%;
157
+ --sidebar-accent: 240 22% 27%; /* L3 hover well */
158
+ --sidebar-accent-foreground: 225 22% 91%;
159
+ --sidebar-border: 236 20% 35%;
160
+ --sidebar-ring: 233 100% 87%;
111
161
 
112
- /* Depth scale — flat neutral spine (Tangle Quiet) */
113
- --depth-1: #15151a;
114
- --depth-2: #1f1f23;
115
- --depth-3: #26262b;
116
- --depth-4: #2e2e34;
162
+ /* Depth scale — tracks the MD3 ladder (canvas / card / nested / overlay) */
163
+ --depth-1: #151523;
164
+ --depth-2: #2d2e43;
165
+ --depth-3: #363654;
166
+ --depth-4: #434365;
117
167
 
118
168
  /* Status colors */
119
169
  --status-running: #10B981;
120
- --status-creating: #9B93F0;
170
+ --status-creating: #A5B4FC;
121
171
  --status-stopped: #FFB800;
122
172
  --status-warm: #FF8A4C;
123
173
  --status-cold: #4AABFF;
@@ -157,38 +207,45 @@
157
207
  --glass-border-hover: var(--border-default);
158
208
  --glass-blur: 0px;
159
209
 
160
- /* Mesh glows — quieted to off; chrome is flat in Tangle Quiet */
210
+ /* Mesh glows — off; depth comes from the ladder, not from glow */
161
211
  --mesh-teal: transparent;
162
212
  --mesh-violet: transparent;
163
213
  --mesh-blue: transparent;
164
214
 
165
215
  /* Brand gradients — the indigo spark, kept ONLY for the logo mark + send button */
166
- --tangle-gradient: linear-gradient(135deg, #6366F1, #818CF8);
167
- --tangle-gradient-text: linear-gradient(135deg, #A5AAFC, #818CF8);
168
- --accent-gradient-strong: linear-gradient(135deg, #6366F1, #818CF8);
216
+ --tangle-gradient: linear-gradient(135deg, #4C3FC5, #A5B4FC);
217
+ --tangle-gradient-text: linear-gradient(135deg, #C7D2FE, #A5B4FC);
218
+ --accent-gradient-strong: linear-gradient(135deg, #4C3FC5, #7C6CF6);
169
219
  /* Accent wells — soft = hover neutral, strong = active/selected row (indigo) */
170
- --accent-surface-soft: rgba(255, 255, 255, 0.04);
171
- --accent-surface-strong: rgba(129, 140, 248, 0.14);
172
- --accent-text: #b9bffb;
173
-
174
- /* Surfaces — flat single-neutral (no radial mesh) */
175
- --bg-root: #15151a;
220
+ --accent-surface-soft: rgba(165, 180, 252, 0.07);
221
+ --accent-surface-strong: rgba(76, 63, 197, 0.30);
222
+ /* The BRIGHT indigo, and only ever as text: --primary is the fill (it carries
223
+ * white), and at 6.7:1 on the card this is the one that stays legible as ink. */
224
+ --accent-text: #A5B4FC;
225
+
226
+ /* Surfaces — solid fills off the ladder (no radial mesh) */
227
+ --bg-root: #151523;
176
228
  --bg-dark: var(--depth-1);
177
229
  --bg-card: var(--depth-2);
178
230
  --bg-elevated: var(--depth-4);
179
231
  --bg-section: var(--depth-1);
180
- --bg-input: var(--depth-3);
181
- --bg-hover: rgba(255, 255, 255, 0.04);
182
- --bg-selection: rgba(129, 140, 248, 0.14);
232
+ --bg-input: hsl(var(--hsl-input)); /* the field well — same color, hex-free */
233
+ --bg-hover: rgba(165, 180, 252, 0.07);
234
+ --bg-selection: rgba(124, 108, 246, 0.28);
183
235
 
184
236
  /* Text — neutral */
185
- --text-primary: #ececee;
186
- --text-secondary: #c8c8cd;
187
- --text-muted: #9a9aa2;
188
- --text-dim: #6b6b73;
237
+ /* Four steps the eye can actually separate: 10.6 / 7.5 / 5.8 / 4.7 against the
238
+ * L2 card. --text-dim is the faintest tier but it is still TEXT, so it clears
239
+ * the 4.5:1 body floor rather than only the 3:1 large-text one — asserted in
240
+ * tokens.test.ts against both the card and the canvas, since the tier is
241
+ * solved against one surface and rendered on both. */
242
+ --text-primary: #e3e6ed;
243
+ --text-secondary: #bec3d0;
244
+ --text-muted: #a5abbb;
245
+ --text-dim: #9299ab;
189
246
 
190
247
  /* Brand accents — Tangle */
191
- --brand-primary: #6366F1;
248
+ --brand-primary: #7C6CF6;
192
249
  --brand-strong: #2E2A5E;
193
250
  --brand-strong-text: #ffffff;
194
251
  --brand-strong-text-muted: rgba(255, 255, 255, 0.7);
@@ -200,17 +257,17 @@
200
257
  --brand-emerald: #10B981;
201
258
 
202
259
  /* Borders — neutral hairlines */
203
- --border-subtle: rgba(255, 255, 255, 0.05);
204
- --border-default: rgba(255, 255, 255, 0.07);
205
- --border-hover: rgba(255, 255, 255, 0.12);
206
- --border-accent: rgba(129, 140, 248, 0.20);
207
- --border-accent-hover: rgba(129, 140, 248, 0.35);
260
+ --border-subtle: rgba(165, 180, 252, 0.10);
261
+ --border-default: rgba(165, 180, 252, 0.16);
262
+ --border-hover: rgba(165, 180, 252, 0.28);
263
+ --border-accent: rgba(129, 140, 248, 0.35);
264
+ --border-accent-hover: rgba(129, 140, 248, 0.50);
208
265
 
209
266
  /* Buttons */
210
- --btn-primary-bg: #5B4ED4;
211
- --btn-primary-hover: #4F46E5;
267
+ --btn-primary-bg: #4C3FC5;
268
+ --btn-primary-hover: #5A50D6;
212
269
  --btn-primary-text: #ffffff;
213
- --btn-cta-bg: #4F46E5;
270
+ --btn-cta-bg: #4C3FC5;
214
271
  --btn-cta-text: #FFFFFF;
215
272
 
216
273
  /* Code / runtime syntax */
@@ -319,17 +376,23 @@
319
376
  /* Light theme — clean professional, warm purple accents */
320
377
 
321
378
  /* MD3 surface hierarchy */
322
- --md3-surface: #f8f9fb;
323
- --md3-surface-dim: #d9dae0;
324
- --md3-surface-bright: #f8f9fb;
325
- --md3-surface-container-lowest: #ffffff;
326
- --md3-surface-container-low: #f3f4f7;
327
- --md3-surface-container: #edeef2;
328
- --md3-surface-container-high: #e7e8ed;
329
- --md3-surface-container-highest: #e1e2e8;
330
- --md3-surface-variant: #e1e2e8;
331
- --md3-on-surface: #191c24;
332
- --md3-on-surface-variant: #464858;
379
+ /* The light ladder ALTERNATES; it is not monotonic, and that is deliberate.
380
+ * On a tinted canvas, a raised plane is PAPER (white) and a recessed one is a
381
+ * tinted WELL. So card and overlay are both white and separate from each other
382
+ * by shadow and ring, not by fill — which is how light-mode elevation actually
383
+ * reads. Forcing a strictly-darkening ladder here is what produced the
384
+ * white-on-white flatness this palette replaced. Pinned in tokens.test.ts. */
385
+ --md3-surface: #e9e9f1; /* L0 page canvas — neutral grey, BELOW the wells */
386
+ --md3-surface-dim: #d9d9e6;
387
+ --md3-surface-bright: #ffffff;
388
+ --md3-surface-container-lowest: #ffffff; /* insets read as paper */
389
+ --md3-surface-container-low: #f7f7fa; /* L1 chrome, field wells — well */
390
+ --md3-surface-container: #ffffff; /* L2 resting card — paper */
391
+ --md3-surface-container-high: #f2f2f7; /* L3 nested / hover — well */
392
+ --md3-surface-container-highest: #ffffff; /* L4 overlay — paper + shadow */
393
+ --md3-surface-variant: #f2f2f7;
394
+ --md3-on-surface: #2b2c3e;
395
+ --md3-on-surface-variant: #494b5d;
333
396
 
334
397
  --md3-primary: #3E349E;
335
398
  --md3-primary-dim: #352B88;
@@ -347,8 +410,8 @@
347
410
 
348
411
  --md3-tertiary: #065F46;
349
412
  --md3-tertiary-container: #D1FAE5;
350
- --md3-outline: #6B6E8A;
351
- --md3-outline-variant: #C7C6D6;
413
+ --md3-outline: #737696;
414
+ --md3-outline-variant: #cecfde;
352
415
 
353
416
  --md3-error: #ba1a1a;
354
417
  --md3-error-dim: #93000a;
@@ -361,34 +424,47 @@
361
424
  --md3-inverse-on-surface: #f0f1f4;
362
425
  --md3-surface-tint: #6366F1;
363
426
 
364
- /* Semantic HSL bridge — flat neutral light (Tangle Quiet) */
365
- --hsl-background: 0 0% 100%; /* #ffffff */
366
- --hsl-foreground: 0 0% 5%; /* #0d0d0d */
367
- --hsl-card: 240 7% 97%; /* #f7f7f8 surface */
368
- --hsl-card-foreground: 0 0% 5%;
427
+ /* Semantic HSL bridge — white paper on a neutral-grey canvas */
428
+ --hsl-background: 240 24% 93%; /* L0 canvas #e9e9f1 */
429
+ /* Ink, not black. Pure #0d0d0d on white paper measures 19.5:1 — a hair off the
430
+ * 21:1 ceiling — and that maximum is what reads as glare rather than as
431
+ * legibility. 20.5% L lands 13.7:1 on the card and 11.4:1 on the canvas: AAA with
432
+ * headroom in both places, and a step the eye reads as text instead of as a
433
+ * hole. */
434
+ --hsl-foreground: 235 18% 20.5%; /* #2b2c3e */
435
+ --hsl-card: 0 0% 100%; /* L2 card = white paper */
436
+ --hsl-card-foreground: 235 18% 20.5%;
369
437
  --hsl-popover: 0 0% 100%;
370
- --hsl-popover-foreground: 0 0% 5%;
371
- --hsl-primary: 245 62% 57%; /* #5B4ED4 light accent */
438
+ --hsl-popover-foreground: 235 18% 20.5%;
439
+ --hsl-primary: 246 54% 51%; /* #4c3fc6 — carries white at 7.4:1 */
372
440
  --hsl-primary-foreground: 0 0% 100%;
373
- --hsl-secondary: 240 6% 93%; /* #ececee surface-2 */
374
- --hsl-secondary-foreground: 0 0% 10%;
375
- --hsl-muted: 240 6% 93%; /* #ececee neutral hover */
376
- --hsl-muted-foreground: 240 5% 38%; /* #5d5d66 text-muted */
377
- --hsl-accent: 240 6% 93%; /* neutral hover */
378
- --hsl-accent-foreground: 0 0% 10%;
441
+ --hsl-secondary: 240 26% 96%; /* L3 #f2f2f7 */
442
+ --hsl-secondary-foreground: 235 18% 20.5%;
443
+ --hsl-muted: 240 26% 96%; /* L3 hover well */
444
+ /* 34% L: the shared components fade captions with /70 and /60, and a
445
+ * translucent foreground renders as the token COMPOSITED over the plane behind
446
+ * it. Against white paper that compositing is brutal — a lighter value puts the
447
+ * /60 caption under the 3:1 large-text floor. This holds 3.0:1 there and 8.6:1
448
+ * at full strength, still a clear step below the 13.7:1 body ink. */
449
+ --hsl-muted-foreground: 235 12% 32.5%; /* #494b5d text-muted */
450
+ --hsl-accent: 240 26% 96%; /* neutral hover */
451
+ --hsl-accent-foreground: 235 18% 20.5%;
379
452
  --hsl-destructive: 0 72% 41%;
380
453
  --hsl-destructive-foreground: 0 0% 100%;
381
- --hsl-border: 240 6% 89%; /* hairline ≈ rgba(0,0,0,0.08) */
382
- --hsl-input: 240 6% 89%;
383
- --hsl-ring: 245 62% 57%;
454
+ --hsl-border: 238 20% 84%; /* #cecfde = --md3-outline-variant */
455
+ --hsl-input: 240 30% 97%; /* field well */
456
+ --hsl-ring: 246 54% 51%;
384
457
  --hsl-success: 160 84% 30%;
385
458
  --hsl-warning: 41 96% 50%;
386
459
  --hsl-info: 212 80% 50%;
387
460
 
388
- --depth-1: #ffffff;
389
- --depth-2: #f7f7f8;
390
- --depth-3: #ececee;
391
- --depth-4: #e3e3e6;
461
+ /* depth-2 (card) and depth-4 (elevated) are BOTH paper, on purpose: in light,
462
+ * a popover does not out-lighten a white card — it lifts by shadow. See the
463
+ * ladder note above. */
464
+ --depth-1: #e9e9f1; /* canvas */
465
+ --depth-2: #ffffff; /* card — paper */
466
+ --depth-3: #f2f2f7; /* nested well */
467
+ --depth-4: #ffffff; /* elevated — paper, separated by shadow */
392
468
 
393
469
  --status-running: #059669;
394
470
  --status-creating: #3E349E;
@@ -430,33 +506,47 @@
430
506
  --surface-neutral-text: var(--text-muted);
431
507
 
432
508
  /* Sidebar — light, flat surface + hairline */
433
- --sidebar-background: 240 7% 97%; /* #f7f7f8 surface */
434
- --sidebar-foreground: 0 0% 5%;
435
- --sidebar-primary: 245 62% 57%; /* #5B4ED4 */
509
+ --sidebar-background: 0 0% 100%; /* white paper */
510
+ --sidebar-foreground: 235 18% 20.5%;
511
+ --sidebar-primary: 246 54% 51%; /* #4c3fc6 */
436
512
  --sidebar-primary-foreground: 0 0% 100%;
437
- --sidebar-accent: 240 6% 93%; /* neutral hover */
438
- --sidebar-accent-foreground: 0 0% 10%;
439
- --sidebar-border: 240 6% 89%; /* hairline */
440
- --sidebar-ring: 245 62% 57%;
513
+ --sidebar-accent: 240 26% 96%; /* L3 hover well */
514
+ --sidebar-accent-foreground: 235 18% 20.5%;
515
+ --sidebar-border: 238 20% 84%; /* = --md3-outline-variant */
516
+ --sidebar-ring: 246 54% 51%;
441
517
 
442
518
  --mesh-teal: transparent;
443
519
  --mesh-violet: transparent;
444
520
  --mesh-blue: transparent;
445
521
 
446
522
  /* Surfaces — flat single-neutral light */
447
- --bg-root: #ffffff;
448
- --bg-dark: #f7f7f8;
449
- --bg-card: #f7f7f8;
450
- --bg-elevated: #ffffff;
451
- --bg-section: #f7f7f8;
452
- --bg-input: #ececee;
453
- --bg-hover: rgba(0, 0, 0, 0.04);
454
- --bg-selection: rgba(91, 78, 212, 0.10);
455
-
456
- --text-primary: #0d0d0d;
457
- --text-secondary: #2a2a30;
458
- --text-muted: #5d5d66;
459
- --text-dim: #8e8e98;
523
+ --bg-root: #e9e9f1;
524
+ --bg-dark: var(--depth-1);
525
+ --bg-card: var(--depth-2);
526
+ /* Paper, exactly like --bg-card: in light you cannot raise a plane by making it
527
+ * lighter than white, so an overlay lifts by SHADOW. A consumer that fills with
528
+ * --bg-elevated and applies no shadow gets no separation from a card — that is
529
+ * the contract, not an oversight (--hsl-popover is white for the same reason).
530
+ * Pair it with --shadow-dropdown. */
531
+ --bg-elevated: var(--depth-4);
532
+ --bg-section: var(--depth-1);
533
+ --bg-input: hsl(var(--hsl-input)); /* the field well — same color, hex-free */
534
+ --bg-hover: rgba(76, 63, 198, 0.06);
535
+ --bg-selection: rgba(76, 63, 198, 0.16);
536
+
537
+ /* Mirrors the dark ramp's spacing: 13.7 / 10.5 / 8.6 / 5.6 against white paper.
538
+ * Every step carries the 240 cast so the ink belongs to the palette.
539
+ *
540
+ * --text-dim is pinned from the CANVAS (#e9e9f1), not the white card: it is
541
+ * the faintest tier, so the tinted L0 is the surface that binds it, and a
542
+ * value chosen against paper alone sits under the 4.5:1 body floor there.
543
+ * 4.6 on the canvas, 5.6 on the card — both floors are asserted in
544
+ * tokens.test.ts, because a ratio quoted in a comment drifts the moment a
545
+ * surface moves and nothing catches it. */
546
+ --text-primary: #2b2c3e;
547
+ --text-secondary: #3d3e52;
548
+ --text-muted: #494b5d;
549
+ --text-dim: #65677b;
460
550
 
461
551
  --brand-primary: #6366F1;
462
552
  --brand-strong: #2E2A5E;
@@ -469,16 +559,16 @@
469
559
  --brand-vibrant: #6366F1;
470
560
  --brand-emerald: #059669;
471
561
 
472
- --border-subtle: rgba(0, 0, 0, 0.06);
473
- --border-default: rgba(0, 0, 0, 0.08);
474
- --border-hover: rgba(0, 0, 0, 0.13);
562
+ --border-subtle: rgba(60, 52, 140, 0.10);
563
+ --border-default: rgba(60, 52, 140, 0.14);
564
+ --border-hover: rgba(60, 52, 140, 0.24);
475
565
  --border-accent: rgba(91, 78, 212, 0.18);
476
566
  --border-accent-hover: rgba(91, 78, 212, 0.35);
477
567
 
478
- --btn-primary-bg: #5B4ED4;
479
- --btn-primary-hover: #4F46E5;
568
+ --btn-primary-bg: #4C3FC5;
569
+ --btn-primary-hover: #5A50D6;
480
570
  --btn-primary-text: #ffffff;
481
- --btn-cta-bg: #5B4ED4;
571
+ --btn-cta-bg: #4C3FC5;
482
572
  --btn-cta-text: #ffffff;
483
573
 
484
574
  --code-keyword: #3E349E;
@@ -499,9 +589,9 @@
499
589
  --tangle-gradient: linear-gradient(135deg, #4F46E5, #5B4ED4);
500
590
  --tangle-gradient-text: linear-gradient(135deg, #4F46E5, #5B4ED4);
501
591
  --accent-gradient-strong: linear-gradient(135deg, #4F46E5, #5B4ED4);
502
- --accent-surface-soft: rgba(0, 0, 0, 0.04);
503
- --accent-surface-strong: rgba(91, 78, 212, 0.10);
504
- --accent-text: #4F46E5;
592
+ --accent-surface-soft: rgba(76, 63, 198, 0.05);
593
+ --accent-surface-strong: rgba(76, 63, 198, 0.13);
594
+ --accent-text: #5047eb;
505
595
 
506
596
  /* Typography — unified on Inter */
507
597
  --font-sans: "Inter", "Geist", ui-sans-serif, system-ui, -apple-system, sans-serif;