@nebutra/design-tokens 2.0.0 → 3.0.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.
@@ -26,17 +26,17 @@
26
26
  --nebutra-cyan-800: #057963;
27
27
  --nebutra-cyan-900: #035143;
28
28
  --nebutra-cyan-950: #012923;
29
- --nebutra-neutral-50: #f8fafc;
30
- --nebutra-neutral-100: #f1f5f9;
31
- --nebutra-neutral-200: #e2e8f0;
32
- --nebutra-neutral-300: #cbd5e1;
33
- --nebutra-neutral-400: #94a3b8;
34
- --nebutra-neutral-500: #64748b;
35
- --nebutra-neutral-600: #475569;
36
- --nebutra-neutral-700: #334155;
37
- --nebutra-neutral-800: #1e293b;
38
- --nebutra-neutral-900: #0f172a;
39
- --nebutra-neutral-950: #020617;
29
+ --nebutra-neutral-50: #f9fafb;
30
+ --nebutra-neutral-100: #f3f4f6;
31
+ --nebutra-neutral-200: #e4e5e8;
32
+ --nebutra-neutral-300: #d2d4d8;
33
+ --nebutra-neutral-400: #9fa1a5;
34
+ --nebutra-neutral-500: #717377;
35
+ --nebutra-neutral-600: #515357;
36
+ --nebutra-neutral-700: #3f4044;
37
+ --nebutra-neutral-800: #26272a;
38
+ --nebutra-neutral-900: #18191c;
39
+ --nebutra-neutral-950: #0b0c0d;
40
40
  --nebutra-gray-50: #fafafa;
41
41
  --nebutra-gray-100: #f5f5f5;
42
42
  --nebutra-gray-200: #e5e5e5;
@@ -75,14 +75,85 @@
75
75
  --tracking-display: -0.04em; /** Hero H1 */
76
76
  --tracking-heading: -0.03em; /** Section H2/H3 */
77
77
  --tracking-tight: -0.015em; /** Body emphasis */
78
+ --tracking-label: 0.07em; /** Uppercase micro-labels. 0.07em is Vercel's measured eyebrow value (0.071em); open tracking keeps small caps legible. */
78
79
  --leading-display: 1.05;
79
80
  --leading-heading: 1.15;
81
+ --type-2xs-size: 0.6875rem;
82
+ --type-2xs-leading: 1.45;
83
+ --type-2xs-tracking: 0.01em;
84
+ --type-xs-size: 0.75rem;
85
+ --type-xs-leading: 1.4;
86
+ --type-xs-tracking: 0.005em;
87
+ --type-ui-size: 0.8125rem;
88
+ --type-ui-leading: 1.45;
89
+ --type-ui-tracking: 0em;
90
+ --type-sm-size: 0.875rem;
91
+ --type-sm-leading: 1.5;
92
+ --type-sm-tracking: 0em;
93
+ --type-base-size: 1rem;
94
+ --type-base-leading: 1.6;
95
+ --type-base-tracking: -0.005em;
96
+ --type-lg-size: 1.125rem;
97
+ --type-lg-leading: 1.55;
98
+ --type-lg-tracking: -0.01em;
99
+ --type-xl-size: 1.25rem;
100
+ --type-xl-leading: 1.45;
101
+ --type-xl-tracking: -0.012em;
102
+ --type-2xl-size: 1.5rem;
103
+ --type-2xl-leading: 1.33;
104
+ --type-2xl-tracking: -0.015em;
105
+ --type-3xl-size: 1.875rem;
106
+ --type-3xl-leading: 1.25;
107
+ --type-3xl-tracking: -0.02em;
108
+ --type-4xl-size: 2.25rem;
109
+ --type-4xl-leading: 1.15;
110
+ --type-4xl-tracking: -0.022em;
111
+ --type-5xl-size: 3rem;
112
+ --type-5xl-leading: 1.08;
113
+ --type-5xl-tracking: -0.028em;
114
+ --type-6xl-size: 3.75rem;
115
+ --type-6xl-leading: 1.04;
116
+ --type-6xl-tracking: -0.032em;
117
+ --type-7xl-size: 4.5rem;
118
+ --type-7xl-leading: 1;
119
+ --type-7xl-tracking: -0.035em;
120
+ --type-8xl-size: 6rem;
121
+ --type-8xl-leading: 1;
122
+ --type-8xl-tracking: -0.04em;
123
+ --type-9xl-size: 8rem;
124
+ --type-9xl-leading: 1;
125
+ --type-9xl-tracking: -0.045em;
126
+ --type-display-size: 1.25rem;
127
+ --type-display-leading: 1.45;
128
+ --type-display-tracking: -0.012em;
129
+ --type-body-size: 0.875rem;
130
+ --type-body-leading: 1.5;
131
+ --type-body-tracking: 0em;
132
+ --type-label-size: 0.75rem;
133
+ --type-label-leading: 1.4;
134
+ --type-label-tracking: 0.005em;
135
+ --type-meta-size: 0.6875rem;
136
+ --type-meta-leading: 1.45;
137
+ --type-meta-tracking: 0.01em;
138
+ --layer-raised: 10; /** Within a component: an element lifted over its siblings. */
139
+ --layer-sticky: 20; /** Sticky headers and table heads. */
140
+ --layer-floating: 30; /** Floating toolbars and canvas chrome anchored to content. */
141
+ --layer-nav: 40; /** App navigation and the shell's top bar. */
142
+ --layer-drawer: 60; /** Mobile navigation drawers and their toggles. */
143
+ --layer-banner: 90; /** Page-level banners: cookie consent, announcements. */
144
+ --layer-skip-link: 100; /** The skip link — above page chrome whenever it is focused. */
145
+ --layer-panel: 200; /** App-owned floating panels that are not overlays (locale switcher, search flyouts). */
146
+ --layer-backdrop: 1040; /** Overlay tier. Mirrors overlayZIndex in @nebutra/ui; a test keeps them equal. */
147
+ --layer-modal: 1050; /** Dialogs and sheets. */
148
+ --layer-popover: 1060; /** Popovers, menus, selects. */
149
+ --layer-tooltip: 1070; /** Tooltips. */
150
+ --layer-devtools: 9999; /** Developer and debug surfaces only — never product UI. */
80
151
  --ring-hairline: inset 0 0 0 1px rgb(255 255 255 / 0.06); /** Hairline override — more visible on dark background */
81
152
  --overlay-faint: rgb(255 255 255 / 0.06); /** Dark-mode faint overlay (6% white) — inverted from light */
82
- --font-sans: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
83
- --font-cn: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", "Microsoft YaHei", sans-serif; /** CJK-locale stack — same order as sans, minus the Latin-only system faces */
84
- --font-display: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif;
85
- --font-heading: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif;
153
+ --font-sans: var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
154
+ --font-cn: var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei", sans-serif; /** CJK-locale stack — same order as sans, minus the Latin-only system faces */
155
+ --font-display: var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
156
+ --font-heading: var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
86
157
  --font-mono: var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
87
158
  --brand-primary: #5c7cfa; /** Dark-mode brand primary — the identity blue lifted to nebutra-blue-400 so it stays visible on dark surfaces. This is the identity alias, not the functional fill: the dark solid fill is --blue-9 (#396ae2, white label 4.85:1). The two are deliberately separate now that the VI hex is no longer pinned into ramp slot 9. */
88
159
  --brand-accent: #1af7c8; /** Dark-mode brand accent — nebutra-cyan-400 (L* 87.1). The functional bright fill is --cyan-9 (#0bf1c3, L* 85.41), the same hex in both modes; dark ink for it is --cyan-contrast at 10.35:1. */
@@ -108,18 +179,18 @@
108
179
  --motion-duration-reveal: 300ms;
109
180
  --motion-duration-cinematic: 500ms;
110
181
  --focus-ring: 0 0 0 2px hsl(var(--ring) / 0.4); /** Single-layer translucent focus ring (2026 pattern). Legacy box-shadow consumers stay compatible; modern surfaces should prefer the global :focus-visible outline rule in static/base.css. */
111
- --neutral-1: #020617;
112
- --neutral-2: #0f172a;
113
- --neutral-3: #1e293b;
114
- --neutral-4: #334155;
115
- --neutral-5: #475569;
182
+ --neutral-1: #0b0c0d;
183
+ --neutral-2: #18191c;
184
+ --neutral-3: #26272a;
185
+ --neutral-4: #3f4044;
186
+ --neutral-5: #515357;
116
187
  --neutral-6: #666666; /** subtle border — DERIVED: OKLab lerp step5 → step9 at t=0.25, chroma forced to 0. The achromatic result is deliberate and preserves the documented intent of color.nebutra-gray ('used where blue undertone clashes — primarily dark-mode borders, dividers, elevation hairlines'). What was broken was the LIGHTNESS: gray-900/800/700 are darker than the slate background tier they sit above, so the border read as a hole rather than a hairline. */
117
- --neutral-7: #7a7a7a; /** default border — DERIVED: OKLab lerp step5 → step9 at t=0.50, chroma 0. */
188
+ --neutral-7: #797979; /** default border — DERIVED: OKLab lerp step5 → step9 at t=0.50, chroma 0. */
118
189
  --neutral-8: #8d8d8d; /** hovered border — DERIVED: OKLab lerp step5 → step9 at t=0.75, chroma 0. */
119
- --neutral-9: #94a3b8;
120
- --neutral-10: #cbd5e1;
121
- --neutral-11: #e2e8f0;
122
- --neutral-12: #f8fafc;
190
+ --neutral-9: #9fa1a5;
191
+ --neutral-10: #d2d4d8;
192
+ --neutral-11: #e4e5e8;
193
+ --neutral-12: #f9fafb;
123
194
  --blue-1: #030815; /** app tint — L* 13.59, C 0.031, hue 264. */
124
195
  --blue-2: #060f25; /** subtle background — L* 17.43, C 0.048. */
125
196
  --blue-3: #09183b; /** component background default — L* 22.04, C 0.071. */
@@ -146,20 +217,20 @@
146
217
  --cyan-11: #90fbdb; /** secondary text — L* 91.46, C 0.110. 15.68:1 on step 1. */
147
218
  --cyan-12: #bdfde8; /** primary text — L* 94.54, C 0.070. 16.95:1 on step 1. */
148
219
  --cyan-contrast: #0d2b25; /** Label ink for the bright fill tier. Absolute near-black, identical in both modes. 10.35:1 on step 9, 11.34:1 on step 10. */
149
- --background: 222 14% 9%;
150
- --foreground: 210 18% 96%;
151
- --card: 222 12% 13%;
152
- --card-foreground: 210 18% 96%;
153
- --popover: 222 12% 13%;
154
- --popover-foreground: 210 18% 96%;
155
- --primary: 222.6 74.4% 55.5%; /** Dark-mode action blue, paired with the near-black --primary-foreground, not white. #396ae2 keeps the VI hue and sits at L*55.9 C0.191. */
156
- --primary-foreground: 0 0% 100%; /** White, matching light mode and matching --blue-contrast. It was near-black (222 14% 9%) when --primary was a LIGHT fill at L* 68.3, where dark ink read 6.20:1. Once --primary became a deep fill at L* 55.9 the pairing inverted and measured 3.74:1, below AA — the fill moved and the ink did not. The deep-fill tier takes white in both modes; --blue-contrast says the same thing, and they must agree because --primary and --blue-9 are now the same value. */
157
- --secondary: 222 10% 18%;
158
- --secondary-foreground: 210 18% 96%;
159
- --muted: 222 10% 17%;
160
- --muted-foreground: 220 8% 65%;
161
- --accent: 228 38% 22%;
162
- --accent-foreground: 228 95% 80%;
220
+ --background: 220 11.1% 5.3%; /** House canvas. Light is gray-50 under white cards (two-surface layering, as Vercel #fafafa/#fff); dark is OKLCH L 0.16, inside the near-black band every reference measures (Linear L 0.14, Raycast #040506). */
221
+ --foreground: 228 23.8% 95.9%;
222
+ --card: 220 7.3% 8%;
223
+ --card-foreground: 228 23.8% 95.9%;
224
+ --popover: 225 7.7% 10.2%;
225
+ --popover-foreground: 228 23.8% 95.9%;
226
+ --primary: 228 23.8% 95.9%; /** House action fill = ink, not blue (owner decision 2026-09-24). Every measured reference ships a solid near-black primary (Vercel #171717, Figma #000) and keeps its one saturated hue for a single signal. Blue now lives on --ring (focus, selection) only. */
227
+ --primary-foreground: 220 11.1% 5.3%; /** White, matching light mode and matching --blue-contrast. It was near-black (222 14% 9%) when --primary was a LIGHT fill at L* 68.3, where dark ink read 6.20:1. Once --primary became a deep fill at L* 55.9 the pairing inverted and measured 3.74:1, below AA — the fill moved and the ink did not. The deep-fill tier takes white in both modes; --blue-contrast says the same thing, and they must agree because --primary and --blue-9 are now the same value. */
228
+ --secondary: 225 6.2% 12.5%;
229
+ --secondary-foreground: 228 23.8% 95.9%;
230
+ --muted: 225 6.2% 12.5%;
231
+ --muted-foreground: 228 2.6% 62.5%;
232
+ --accent: 225 6.2% 12.5%;
233
+ --accent-foreground: 228 23.8% 95.9%;
163
234
  --destructive: 0 63% 38%;
164
235
  --destructive-foreground: 210 18% 96%;
165
236
  --destructive-strong: 0 80% 66%; /** Red as FOREGROUND on a dark surface. --destructive (0 63% 38%) is 2.15:1 on --card here — a fill only. Lightened to 5.18:1 on --card / 5.61:1 on --neutral-1, which sits alongside the light-mode 5.42:1 so red reads with the same weight in both themes. Use --destructive for fills with --destructive-foreground on top; use this for red text and icons. */
@@ -171,17 +242,17 @@
171
242
  --warning-strong: 38 80% 52%; /** Amber as foreground on a dark surface. Unlike light mode this needs no adjustment — 38 80% 52% is already 7.98:1 against --neutral-1 — so it deliberately equals --warning. The token exists in both themes so consumers can reference one name. */
172
243
  --info: 228 95% 72%;
173
244
  --info-foreground: 222 14% 9%;
174
- --border: 220 8% 22%;
175
- --input: 220 8% 22%;
176
- --ring: 0 0% 88%; /** Focus ring (dark mode) — neutral light gray. Mirrors light-mode neutral foreground choice. */
177
- --sidebar: 222 16% 8%;
178
- --sidebar-foreground: 210 18% 94%;
179
- --sidebar-primary: 228 95% 72%;
180
- --sidebar-primary-foreground: 222 14% 9%;
181
- --sidebar-accent: 228 38% 22%;
182
- --sidebar-accent-foreground: 228 95% 80%;
183
- --sidebar-border: 220 8% 18%;
184
- --sidebar-ring: 228 95% 72%;
245
+ --border: 220 3.6% 16.3%;
246
+ --input: 220 3.6% 16.3%;
247
+ --ring: 222.6 87.3% 69%; /** The House blue. Focus rings, selection and links — the one saturated hue on a product screen. #2e65ee light / #6b93f5 dark. */
248
+ --sidebar: 220 11.1% 5.3%;
249
+ --sidebar-foreground: 228 23.8% 95.9%;
250
+ --sidebar-primary: 228 23.8% 95.9%; /** Current-page accent in the sidebar. = blue-11 (#89aaee, L* 74.0, C 0.106), the ramp's measured secondary-text tier on dark (8.62:1 on step 1). Was 228 95% 72% — 95% chroma sat outside every measured band and matched no ramp step. */
251
+ --sidebar-primary-foreground: 220 11.1% 5.3%;
252
+ --sidebar-accent: 225 6.2% 12.5%;
253
+ --sidebar-accent-foreground: 228 23.8% 95.9%;
254
+ --sidebar-border: 225 6.2% 12.5%;
255
+ --sidebar-ring: 222.6 87.3% 69%;
185
256
  --chart-1: 228 95% 67%;
186
257
  --chart-2: 168 96% 54%;
187
258
  --chart-3: 228 100% 82%;
@@ -26,17 +26,17 @@
26
26
  --nebutra-cyan-800: #057963;
27
27
  --nebutra-cyan-900: #035143;
28
28
  --nebutra-cyan-950: #012923;
29
- --nebutra-neutral-50: #f8fafc;
30
- --nebutra-neutral-100: #f1f5f9;
31
- --nebutra-neutral-200: #e2e8f0;
32
- --nebutra-neutral-300: #cbd5e1;
33
- --nebutra-neutral-400: #94a3b8;
34
- --nebutra-neutral-500: #64748b;
35
- --nebutra-neutral-600: #475569;
36
- --nebutra-neutral-700: #334155;
37
- --nebutra-neutral-800: #1e293b;
38
- --nebutra-neutral-900: #0f172a;
39
- --nebutra-neutral-950: #020617;
29
+ --nebutra-neutral-50: #f9fafb;
30
+ --nebutra-neutral-100: #f3f4f6;
31
+ --nebutra-neutral-200: #e4e5e8;
32
+ --nebutra-neutral-300: #d2d4d8;
33
+ --nebutra-neutral-400: #9fa1a5;
34
+ --nebutra-neutral-500: #717377;
35
+ --nebutra-neutral-600: #515357;
36
+ --nebutra-neutral-700: #3f4044;
37
+ --nebutra-neutral-800: #26272a;
38
+ --nebutra-neutral-900: #18191c;
39
+ --nebutra-neutral-950: #0b0c0d;
40
40
  --nebutra-gray-50: #fafafa;
41
41
  --nebutra-gray-100: #f5f5f5;
42
42
  --nebutra-gray-200: #e5e5e5;
@@ -75,14 +75,85 @@
75
75
  --tracking-display: -0.04em; /** Hero H1 */
76
76
  --tracking-heading: -0.03em; /** Section H2/H3 */
77
77
  --tracking-tight: -0.015em; /** Body emphasis */
78
+ --tracking-label: 0.07em; /** Uppercase micro-labels. 0.07em is Vercel's measured eyebrow value (0.071em); open tracking keeps small caps legible. */
78
79
  --leading-display: 1.05;
79
80
  --leading-heading: 1.15;
81
+ --type-2xs-size: 0.6875rem;
82
+ --type-2xs-leading: 1.45;
83
+ --type-2xs-tracking: 0.01em;
84
+ --type-xs-size: 0.75rem;
85
+ --type-xs-leading: 1.4;
86
+ --type-xs-tracking: 0.005em;
87
+ --type-ui-size: 0.8125rem;
88
+ --type-ui-leading: 1.45;
89
+ --type-ui-tracking: 0em;
90
+ --type-sm-size: 0.875rem;
91
+ --type-sm-leading: 1.5;
92
+ --type-sm-tracking: 0em;
93
+ --type-base-size: 1rem;
94
+ --type-base-leading: 1.6;
95
+ --type-base-tracking: -0.005em;
96
+ --type-lg-size: 1.125rem;
97
+ --type-lg-leading: 1.55;
98
+ --type-lg-tracking: -0.01em;
99
+ --type-xl-size: 1.25rem;
100
+ --type-xl-leading: 1.45;
101
+ --type-xl-tracking: -0.012em;
102
+ --type-2xl-size: 1.5rem;
103
+ --type-2xl-leading: 1.33;
104
+ --type-2xl-tracking: -0.015em;
105
+ --type-3xl-size: 1.875rem;
106
+ --type-3xl-leading: 1.25;
107
+ --type-3xl-tracking: -0.02em;
108
+ --type-4xl-size: 2.25rem;
109
+ --type-4xl-leading: 1.15;
110
+ --type-4xl-tracking: -0.022em;
111
+ --type-5xl-size: 3rem;
112
+ --type-5xl-leading: 1.08;
113
+ --type-5xl-tracking: -0.028em;
114
+ --type-6xl-size: 3.75rem;
115
+ --type-6xl-leading: 1.04;
116
+ --type-6xl-tracking: -0.032em;
117
+ --type-7xl-size: 4.5rem;
118
+ --type-7xl-leading: 1;
119
+ --type-7xl-tracking: -0.035em;
120
+ --type-8xl-size: 6rem;
121
+ --type-8xl-leading: 1;
122
+ --type-8xl-tracking: -0.04em;
123
+ --type-9xl-size: 8rem;
124
+ --type-9xl-leading: 1;
125
+ --type-9xl-tracking: -0.045em;
126
+ --type-display-size: 1.25rem;
127
+ --type-display-leading: 1.45;
128
+ --type-display-tracking: -0.012em;
129
+ --type-body-size: 0.875rem;
130
+ --type-body-leading: 1.5;
131
+ --type-body-tracking: 0em;
132
+ --type-label-size: 0.75rem;
133
+ --type-label-leading: 1.4;
134
+ --type-label-tracking: 0.005em;
135
+ --type-meta-size: 0.6875rem;
136
+ --type-meta-leading: 1.45;
137
+ --type-meta-tracking: 0.01em;
138
+ --layer-raised: 10; /** Within a component: an element lifted over its siblings. */
139
+ --layer-sticky: 20; /** Sticky headers and table heads. */
140
+ --layer-floating: 30; /** Floating toolbars and canvas chrome anchored to content. */
141
+ --layer-nav: 40; /** App navigation and the shell's top bar. */
142
+ --layer-drawer: 60; /** Mobile navigation drawers and their toggles. */
143
+ --layer-banner: 90; /** Page-level banners: cookie consent, announcements. */
144
+ --layer-skip-link: 100; /** The skip link — above page chrome whenever it is focused. */
145
+ --layer-panel: 200; /** App-owned floating panels that are not overlays (locale switcher, search flyouts). */
146
+ --layer-backdrop: 1040; /** Overlay tier. Mirrors overlayZIndex in @nebutra/ui; a test keeps them equal. */
147
+ --layer-modal: 1050; /** Dialogs and sheets. */
148
+ --layer-popover: 1060; /** Popovers, menus, selects. */
149
+ --layer-tooltip: 1070; /** Tooltips. */
150
+ --layer-devtools: 9999; /** Developer and debug surfaces only — never product UI. */
80
151
  --ring-hairline: inset 0 0 0 1px rgb(255 255 255 / 0.04); /** Etched-glass hairline (light mode value, dark mode override in themes/dark.json) */
81
152
  --overlay-faint: rgb(0 0 0 / 0.06); /** Light-mode faint overlay (6% black) */
82
- --font-sans: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
83
- --font-cn: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", "Microsoft YaHei", sans-serif; /** CJK-locale stack — same order as sans, minus the Latin-only system faces */
84
- --font-display: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif;
85
- --font-heading: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif;
153
+ --font-sans: var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
154
+ --font-cn: var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei", sans-serif; /** CJK-locale stack — same order as sans, minus the Latin-only system faces */
155
+ --font-display: var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
156
+ --font-heading: var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
86
157
  --font-mono: var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
87
158
  --brand-primary: #0033fe; /** Brand primary — 云毓蓝 */
88
159
  --brand-accent: #0bf1c3; /** Brand accent — 云毓青 */
@@ -109,17 +180,17 @@
109
180
  --motion-duration-cinematic: 500ms;
110
181
  --focus-ring: 0 0 0 2px hsl(var(--ring) / 0.4); /** Single-layer translucent focus ring (2026 pattern). Legacy box-shadow consumers stay compatible; modern surfaces should prefer the global :focus-visible outline rule in static/base.css. */
111
182
  --neutral-1: #ffffff; /** app background */
112
- --neutral-2: #f8fafc; /** subtle background */
113
- --neutral-3: #f1f5f9; /** component bg default */
114
- --neutral-4: #e2e8f0; /** component bg hover */
115
- --neutral-5: #cbd5e1; /** component bg active */
116
- --neutral-6: #b0bccb; /** subtle border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp step5 → step9 at t=0.25. The $value here is a placeholder anchor only; the preprocessor overwrites it. */
117
- --neutral-7: #96a3b5; /** default border — DERIVED: OKLab lerp step5 → step9 at t=0.50. */
118
- --neutral-8: #7d8ba0; /** hovered border — DERIVED: OKLab lerp step5 → step9 at t=0.75. */
119
- --neutral-9: #64748b; /** solid fill */
120
- --neutral-10: #475569; /** solid fill hover */
121
- --neutral-11: #334155; /** secondary text */
122
- --neutral-12: #0f172a; /** primary text */
183
+ --neutral-2: #f9fafb; /** subtle background */
184
+ --neutral-3: #f3f4f6; /** component bg default */
185
+ --neutral-4: #e4e5e8; /** component bg hover */
186
+ --neutral-5: #d2d4d8; /** component bg active */
187
+ --neutral-6: #b9bbbf; /** subtle border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp step5 → step9 at t=0.25. The $value here is a placeholder anchor only; the preprocessor overwrites it. */
188
+ --neutral-7: #a0a2a6; /** default border — DERIVED: OKLab lerp step5 → step9 at t=0.50. */
189
+ --neutral-8: #888a8e; /** hovered border — DERIVED: OKLab lerp step5 → step9 at t=0.75. */
190
+ --neutral-9: #717377; /** solid fill */
191
+ --neutral-10: #515357; /** solid fill hover */
192
+ --neutral-11: #3f4044; /** secondary text */
193
+ --neutral-12: #18191c; /** primary text */
123
194
  --blue-1: #fafcfe; /** app tint — L* 99.01, C 0.003, hue 264. Radix blue-1 sits at L* 99.31 C 0.003. */
124
195
  --blue-2: #f2f6fd; /** subtle background — L* 97.21, C 0.010. */
125
196
  --blue-3: #e7eefc; /** component background default — L* 94.79, C 0.020. */
@@ -146,20 +217,20 @@
146
217
  --cyan-11: #027a61; /** secondary text — L* 51.64, C 0.100. 5.17:1 on step 1. */
147
218
  --cyan-12: #004133; /** primary text — L* 33.45, C 0.065. 11.35:1 on step 1. */
148
219
  --cyan-contrast: #0d2b25; /** Label ink for the bright fill tier. Absolute near-black, identical in both modes, carrying a trace of the scale hue. A bright fill needs an explicit dark ink; comparing white against the scale's own step 12 picks white in dark mode, where step 12 is light, and measures 1.52:1. 10.35:1 on step 9, 9.40:1 on step 10. */
149
- --background: 0 0% 100%; /** HSL channel triple — consume via hsl(var(--background)) */
150
- --foreground: 222 47% 11%;
220
+ --background: 210 20% 98%; /** House canvas. Light is gray-50 under white cards (two-surface layering, as Vercel #fafafa/#fff); dark is OKLCH L 0.16, inside the near-black band every reference measures (Linear L 0.14, Raycast #040506). */
221
+ --foreground: 225 7.7% 10.2%;
151
222
  --card: 0 0% 100%;
152
- --card-foreground: 222 47% 11%;
223
+ --card-foreground: 225 7.7% 10.2%;
153
224
  --popover: 0 0% 100%;
154
- --popover-foreground: 222 47% 11%;
155
- --primary: 222.8 85% 55.7%; /** Product action blue. Derived, not borrowed: hue 264 is the VI hue, L* and C were chosen inside the band the industry actually ships (measured OKLCH C 0.167-0.235, L* 54-67 across Vercel, Stripe, Linear, GitHub, Tailwind, Radix, Figma, Notion). #2e65ee = L*55.5 C0.215, white label 4.99:1. The previous 228 85% 56% (#2f56ee, C0.232) also passed at 5.70:1 — this trims it to the middle of the band rather than its top edge. The VI lock --blue-9 (#0033FE, C0.290) is 23% above anything in that band and must never surface on a component. */
225
+ --popover-foreground: 225 7.7% 10.2%;
226
+ --primary: 225 7.7% 10.2%; /** House action fill = ink, not blue (owner decision 2026-09-24). Every measured reference ships a solid near-black primary (Vercel #171717, Figma #000) and keeps its one saturated hue for a single signal. Blue now lives on --ring (focus, selection) only. */
156
227
  --primary-foreground: 0 0% 100%;
157
- --secondary: 210 40% 96%;
158
- --secondary-foreground: 222 47% 11%;
159
- --muted: 210 40% 96%;
160
- --muted-foreground: 215 25% 45%;
161
- --accent: 228 100% 97%;
162
- --accent-foreground: 228 85% 56%; /** Softer brand blue — desaturated to match --primary, avoids 100% saturation glare on focus/accent */
228
+ --secondary: 220 14.3% 95.9%;
229
+ --secondary-foreground: 225 7.7% 10.2%;
230
+ --muted: 220 14.3% 95.9%;
231
+ --muted-foreground: 220 2.6% 45.5%;
232
+ --accent: 220 14.3% 95.9%;
233
+ --accent-foreground: 225 7.7% 10.2%; /** Softer brand blue — desaturated to match --primary, avoids 100% saturation glare on focus/accent */
163
234
  --destructive: 0 84% 45%;
164
235
  --destructive-foreground: 0 0% 100%;
165
236
  --destructive-strong: 0 84% 45%; /** Red as FOREGROUND. In light mode --destructive already passes (5.42:1 on white), so this equals it; the token exists so consumers reference one name across themes. In DARK, --destructive is 0 63% 38% = 2.15:1 on --card, unreadable as text — see dark.json. Mirrors --warning-strong. */
@@ -171,17 +242,17 @@
171
242
  --warning-strong: 38 92% 30%; /** Amber as FOREGROUND on a light surface. --warning is a fill: at 38 92% 50% it is 2.04:1 against --neutral-2, so it is unreadable as text or as an icon stroke. This darkened step is 5.17:1, which sits between --success (4.92) and --destructive (5.19) so the three status colours read with the same weight. Use --warning for fills with --warning-foreground on top; use this for amber text and icons. Destructive and success need no equivalent — they already pass AA as foreground. */
172
243
  --info: 228 85% 56%;
173
244
  --info-foreground: 0 0% 100%;
174
- --border: 240 5.9% 90%;
175
- --input: 240 5.9% 90%;
176
- --ring: 222 47% 11%; /** Focus ring — neutral foreground (Vercel/Linear/Cursor 2026 pattern). Brand blue is reserved for selected/active states; focus indication is chrome and stays neutral. Consumed by global :focus-visible outline as hsl(var(--ring) / 0.5). */
177
- --sidebar: 210 40% 98%;
178
- --sidebar-foreground: 215 25% 27%;
179
- --sidebar-primary: 228 85% 56%;
245
+ --border: 225 8% 90.2%;
246
+ --input: 225 8% 90.2%;
247
+ --ring: 222.8 85% 55.7%; /** The House blue. Focus rings, selection and links — the one saturated hue on a product screen. #2e65ee light / #6b93f5 dark. */
248
+ --sidebar: 210 20% 98%;
249
+ --sidebar-foreground: 225 7.7% 10.2%;
250
+ --sidebar-primary: 225 7.7% 10.2%; /** Current-page accent in the sidebar (icon tint + edge). Same value as --primary: the sidebar carried the superseded 228 85% 56% (#2f56ee, C 0.232), which the action-blue measurement trimmed to C 0.215. */
180
251
  --sidebar-primary-foreground: 0 0% 100%;
181
- --sidebar-accent: 228 100% 97%;
182
- --sidebar-accent-foreground: 228 85% 56%;
183
- --sidebar-border: 214 32% 91%;
184
- --sidebar-ring: 228 85% 56%;
252
+ --sidebar-accent: 220 14.3% 95.9%;
253
+ --sidebar-accent-foreground: 225 7.7% 10.2%;
254
+ --sidebar-border: 225 8% 90.2%;
255
+ --sidebar-ring: 222.8 85% 55.7%;
185
256
  --chart-1: 228 100% 50%;
186
257
  --chart-2: 168 91% 49%;
187
258
  --chart-3: 263 70% 66%;