@nebutra/tokens 0.1.1 → 0.1.2

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.
Files changed (57) hide show
  1. package/.turbo/turbo-build.log +5 -2
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/AGENTS.md +5 -2
  4. package/CHANGELOG.md +23 -0
  5. package/DESIGN.md +30 -8
  6. package/LICENSE +21 -676
  7. package/README.md +41 -8
  8. package/brands/gsap/brand.json +180 -0
  9. package/brands/linear/brand.json +202 -0
  10. package/brands/notion/brand.json +189 -0
  11. package/brands/raycast/brand.json +167 -0
  12. package/brands/stripe/brand.json +183 -0
  13. package/brands/vanta/brand.json +198 -0
  14. package/brands/vercel/brand.json +207 -0
  15. package/package.json +17 -8
  16. package/recipe.css +114 -0
  17. package/scripts/compile-brand-run.ts +79 -0
  18. package/scripts/compile-brand.mjs +25 -0
  19. package/scripts/emit-skins-run.ts +62 -0
  20. package/scripts/emit-skins.mjs +31 -0
  21. package/scripts/sync-styles.mjs +29 -0
  22. package/skins/README.md +151 -0
  23. package/skins/gsap.css +247 -0
  24. package/skins/linear.css +282 -0
  25. package/skins/notion.css +248 -0
  26. package/skins/raycast.css +272 -0
  27. package/skins/stripe.css +248 -0
  28. package/skins/vanta.css +255 -0
  29. package/skins/vercel.css +283 -0
  30. package/src/brand-package/__tests__/compile-refero.test.ts +427 -0
  31. package/src/brand-package/__tests__/emit-css.test.ts +130 -0
  32. package/src/brand-package/apply-brand.ts +92 -0
  33. package/src/brand-package/compile-helpers.ts +170 -0
  34. package/src/brand-package/compile-refero.ts +69 -0
  35. package/src/brand-package/emit-css.ts +468 -0
  36. package/src/brand-package/hex-to-hsl.ts +100 -0
  37. package/src/brand-package/index.ts +71 -0
  38. package/src/brand-package/infer-recipe.ts +198 -0
  39. package/src/brand-package/normalize.ts +295 -0
  40. package/src/brand-package/presets/context.ts +17 -0
  41. package/src/brand-package/presets/generic.ts +214 -0
  42. package/src/brand-package/presets/gsap.ts +146 -0
  43. package/src/brand-package/presets/index.ts +31 -0
  44. package/src/brand-package/presets/linear.ts +183 -0
  45. package/src/brand-package/presets/notion.ts +208 -0
  46. package/src/brand-package/presets/raycast.ts +138 -0
  47. package/src/brand-package/presets/recipe.ts +61 -0
  48. package/src/brand-package/presets/stripe.ts +207 -0
  49. package/src/brand-package/presets/vanta.ts +220 -0
  50. package/src/brand-package/presets/vercel.ts +187 -0
  51. package/src/brand-package/types.ts +285 -0
  52. package/src/brand-package/use-brand.ts +207 -0
  53. package/src/brand-package/validate.ts +108 -0
  54. package/src/index.ts +31 -1
  55. package/src/theme-provider.tsx +5 -3
  56. package/styles.css +153 -69
  57. package/turbo.json +15 -0
@@ -0,0 +1,151 @@
1
+ # Design skins & Brand Packages
2
+
3
+ **Goal:** Create Center users swap a **design language** by applying one **Brand Package**.
4
+ This package is a *carrier* (roles + free elev/radii + zones), not a list of brand hacks.
5
+
6
+ ## Two layers
7
+
8
+ | Layer | What | Path |
9
+ |-------|------|------|
10
+ | **Recipe defaults** | Button default reads `--btn-default-*` | `@nebutra/tokens/recipe.css` (auto via `preset.css`) |
11
+ | **Brand JSON (SSOT)** | Machine contract for Create Center | `brands/<id>/brand.json` |
12
+ | **Brand skin (generated)** | Semantic colors + recipe overrides | `@nebutra/tokens/skins/<id>.css` only |
13
+
14
+ ## Product chrome contract
15
+
16
+ | Semantic | Tailwind / CSS | Role |
17
+ |----------|----------------|------|
18
+ | `--primary` + `--primary-foreground` | `bg-primary` | **Atomic pair** |
19
+ | `--background` / `--foreground` | canvas | App shell |
20
+ | `--card` / `--muted` / `--border` / `--input` / `--ring` | surfaces | Controls |
21
+ | `--btn-default-bg/fg/border/radius` | `Button` default | **Recipe** (solid vs outline) |
22
+
23
+ **Do not** paint product chrome with raw hex / `bg-blue-9` / VI `--brand-primary`.
24
+
25
+ ## Apply a brand (opt-in)
26
+
27
+ ```css
28
+ @import "@nebutra/ui/styles/preset.css";
29
+ /* or: tokens + recipe + sources */
30
+
31
+ @import "@nebutra/tokens/skins/linear.css"; /* solid acid-lime CTA */
32
+ /* @import "@nebutra/tokens/skins/gsap.css"; outline-first / gradient-stroke */
33
+ ```
34
+
35
+ Optional: `html data-brand="gsap"` for runtime targeting (skins also bind `:root`).
36
+
37
+ Default Nebutra shipping brand = **no skin import**.
38
+
39
+ ## Compile from Refero export (Create Center pipeline MVP)
40
+
41
+ ```bash
42
+ # Folder with tokens.json + DESIGN.md (Desktop/GSAP style)
43
+ pnpm --filter @nebutra/tokens exec node scripts/compile-brand.mjs ~/Desktop/GSAP --id gsap
44
+
45
+ # Writes brand.json (SSOT) + skins/<id>.css
46
+ # After hand-editing brand.json:
47
+ pnpm --filter @nebutra/tokens emit-skins
48
+ # Then refresh multi-language catalog:
49
+ pnpm --filter @nebutra/theme sync:skins
50
+ ```
51
+
52
+ Programmatic:
53
+
54
+ ```ts
55
+ import { compileReferoTokens } from "@nebutra/tokens/brand-package";
56
+
57
+ const { brand, css, warnings } = compileReferoTokens({
58
+ tokens: referoJson,
59
+ designMd,
60
+ id: "gsap",
61
+ });
62
+ ```
63
+
64
+ ## Fixtures
65
+
66
+ Cataloged as **design languages** on `@nebutra/theme` (`LANGUAGE_REGISTRY` / `skins.css`).
67
+
68
+ | Brand | Recipe | Notes |
69
+ |-------|--------|-------|
70
+ | **linear** | `solid`, 6px radius | Acid-lime filled CTA |
71
+ | **gsap** | `gradient-stroke` / outline, 100px pill | Green is **accent**, not solid fill |
72
+ | **raycast** | `solid` Mist/Iron, 8px, elevation=`key` | Coral is brand-only; CTA is neutral gray |
73
+ | **vercel** | `solid` Obsidian on paper, 6px, elevation=`hairline` | Light monochrome; no chromatic CTA |
74
+ | **vanta** | `solid` Vivid Violet, 999px pill, elevation=`none` | Light parchment; **indigo-ink** brand-mark ≠ CTA; carbon 1px borders |
75
+ | **stripe** | `solid` Indigo Ink, 4px, elevation=`none` | Light frost ledger; **midnight** brand-mark ≠ indigo CTA; tint-ladder depth |
76
+ | **notion** | `solid` Notion Blue, 8px/12px, elevation=`none` | Warm paper canvas ≠ white cards; ink brand-mark; accent hues decorative only |
77
+
78
+ ```ts
79
+ import { applyLanguage, LANGUAGE_REGISTRY } from "@nebutra/theme";
80
+ applyLanguage("vanta"); // built-in package resolved automatically
81
+ ```
82
+
83
+ Multi-language catalog (scoped `html[data-brand]` only):
84
+
85
+ ```css
86
+ @import "@nebutra/theme/skins.css";
87
+ /* then: document.documentElement.dataset.brand = "raycast" */
88
+ ```
89
+
90
+ ## Why recipe exists
91
+
92
+ Linear and GSAP both dark — but **button language differs**. Color-only skins fail Create Center.
93
+ `Button` / `Badge` defaults + Card/Input elevation/heights consume recipe vars so packages flip solid ↔ outline ↔ no-shadow without call-site edits.
94
+
95
+ Governed surface: `--btn-default-*`, `--badge-default-*`, `--control-height-*`, `--elevation-card|control|raised`, `--font-weight-medium`.
96
+ **Not governed (allowed):** VI logo hex, OAuth marks, decorative demos, trial/turbo special fills.
97
+
98
+ ## Runtime apply (Create Center preview)
99
+
100
+ ```ts
101
+ import {
102
+ compileReferoTokens,
103
+ applyBrandPackage,
104
+ clearBrand,
105
+ restorePersistedBrand,
106
+ validateBrandPackage,
107
+ useBrand,
108
+ useBrandIframePreview,
109
+ } from "@nebutra/tokens";
110
+
111
+ const { brand, warnings } = compileReferoTokens({ tokens, designMd, id: "gsap" });
112
+ const v = validateBrandPackage(brand);
113
+ if (!v.ok) throw new Error(v.errors.join(", "));
114
+
115
+ applyBrandPackage(brand, { persist: true });
116
+ clearBrand({ persist: true });
117
+ ```
118
+
119
+ ### React hooks
120
+
121
+ ```tsx
122
+ // Host app
123
+ const { brand, apply, clear } = useBrand({ autoRestore: true });
124
+
125
+ // Multi-tenant iframe preview
126
+ const { iframeRef, apply: preview, writePreviewDocument } = useBrandIframePreview({
127
+ baseStylesheetHrefs: ["/tokens-preset.css"],
128
+ });
129
+ // <iframe ref={iframeRef} />
130
+ // preview(brand) or writePreviewDocument(brand, "<button class='btn-brand-default'>CTA</button>")
131
+ ```
132
+
133
+ ### Fonts + zones
134
+
135
+ - `typography.faces[]` → emitted as `@font-face` (swap URL for Create Center CDN/upload).
136
+ - `zones.product` / `zones.marketing` → CSS vars + `[data-zone="…"]` / `.zone-…` consumers.
137
+ - Marketing may use 224px display; product stays compact.
138
+
139
+ ### design-sync
140
+
141
+ ```ts
142
+ import { getDesignSync, compileBrandFromTokenSets } from "@nebutra/design-sync";
143
+
144
+ const sync = await getDesignSync();
145
+ const { sets } = await sync.pull();
146
+ const { brand, css, warnings } = compileBrandFromTokenSets(sets, { id: "tenant-a" });
147
+ ```
148
+
149
+ ```bash
150
+ design-sync brand --json --id tenant-a
151
+ ```
package/skins/gsap.css ADDED
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Brand carrier skin: GSAP (gsap) v1.0.0
3
+ * darkDefault=true dualMode=false button=gradient-stroke
4
+ * fonts=2 zones=yes mode=global
5
+ * Contract: roles.action → --primary; roles.brand → --brand-mark (never default CTA)
6
+ */
7
+
8
+ /* Brand font faces */
9
+ @font-face {
10
+ font-family: "Mori";
11
+ src: url("/brand-assets/mori-regular.woff2") format("woff2");
12
+ font-weight: 400;
13
+ font-display: swap;
14
+ }
15
+
16
+ @font-face {
17
+ font-family: "Mori";
18
+ src: url("/brand-assets/mori-semibold.woff2") format("woff2");
19
+ font-weight: 600;
20
+ font-display: swap;
21
+ }
22
+
23
+ :root,
24
+ .dark,
25
+ html[data-brand="gsap"] {
26
+ /* ── Color roles (carrier) ── */
27
+ --role-canvas: 150 7% 6%;
28
+ --role-canvas-fg: 54 100% 94%;
29
+ --role-surface: 0 0% 10%;
30
+ --role-surface-fg: 54 100% 94%;
31
+ --role-action: 137 92% 47%;
32
+ --role-action-fg: 150 7% 6%;
33
+ --role-quiet: 70 5% 25%;
34
+ --role-quiet-fg: 54 100% 94%;
35
+ --role-muted: 0 0% 10%;
36
+ --role-muted-fg: 60 6% 46%;
37
+ --role-border: 70 5% 25%;
38
+ --role-input: 70 5% 25%;
39
+ --role-ring: 137 92% 47%;
40
+
41
+ /* ── shadcn bridge (primary = action CTA) ── */
42
+ --background: 150 7% 6%;
43
+ --foreground: 54 100% 94%;
44
+ --card: 0 0% 10%;
45
+ --card-foreground: 54 100% 94%;
46
+ --popover: 0 0% 10%;
47
+ --popover-foreground: 54 100% 94%;
48
+ --primary: 137 92% 47%;
49
+ --primary-foreground: 150 7% 6%;
50
+ --secondary: 70 5% 25%;
51
+ --secondary-foreground: 54 100% 94%;
52
+ --muted: 0 0% 10%;
53
+ --muted-foreground: 60 6% 46%;
54
+ --accent: 70 5% 25%;
55
+ --accent-foreground: 54 100% 94%;
56
+ --destructive: 309 100% 47%;
57
+ --destructive-foreground: 54 100% 94%;
58
+ --border: 70 5% 25%;
59
+ --input: 70 5% 25%;
60
+ --ring: 137 92% 47%;
61
+ --success: 137 92% 47%;
62
+ --success-foreground: 150 7% 6%;
63
+ --info: 191 100% 44%;
64
+ --info-foreground: 150 7% 6%;
65
+ --sidebar: 0 0% 10%;
66
+ --sidebar-foreground: 54 100% 94%;
67
+ --sidebar-primary: 137 92% 47%;
68
+ --sidebar-primary-foreground: 150 7% 6%;
69
+ --sidebar-accent: 70 5% 25%;
70
+ --sidebar-accent-foreground: 54 100% 94%;
71
+ --sidebar-border: 70 5% 25%;
72
+ --sidebar-ring: 137 92% 47%;
73
+ --brand-gradient: hsl(var(--primary));
74
+ --brand-gradient-reverse: hsl(var(--primary));
75
+ --brand-gradient-vertical: hsl(var(--primary));
76
+ --brand-gradient-radial: hsl(var(--primary));
77
+
78
+ /* Recipe (action language + free elev/radii) */
79
+ /* Shape slots */
80
+ --btn-default-radius: 100px;
81
+ --radius-button: 100px;
82
+ --radius-buttons: 100px;
83
+ --radius-md: 100px;
84
+ --radius-card: 8px;
85
+ --radius-lg: 8px;
86
+ --radius-badge: 9999px;
87
+ --badge-default-radius: 9999px;
88
+ --radius-inputs: 100px;
89
+ --input-radius: 100px;
90
+ --radius-pill: 9999px;
91
+
92
+ /* Free elevation (carrier-provided CSS shadows) */
93
+ --elevation-card: 0 0 #0000;
94
+ --elevation-control: 0 0 #0000;
95
+ --elevation-raised: 0 0 #0000;
96
+ --shadow-xs: 0 0 #0000;
97
+ --shadow-sm: 0 0 #0000;
98
+ --shadow-md: 0 0 #0000;
99
+ --shadow-lg: 0 0 #0000;
100
+ --btn-default-shadow: 0 0 #0000;
101
+ --btn-default-bg: transparent;
102
+ --btn-default-fg: hsl(var(--foreground));
103
+ --btn-default-border-width: 1.5px;
104
+ --btn-default-border: transparent;
105
+ --btn-default-stroke-gradient: linear-gradient(114.41deg, #0ae448 20.74%, #abff84 65.5%);
106
+ --btn-default-hover-bg: hsl(var(--primary) / 0.08);
107
+ --badge-default-bg: transparent;
108
+ --badge-default-fg: hsl(var(--foreground));
109
+ --badge-default-border: #fffce1;
110
+ --badge-default-hover-bg: hsl(var(--foreground) / 0.06);
111
+ --btn-default-padding-y: 0.5rem;
112
+ --btn-default-padding-x: 0.875rem;
113
+ --control-height-tiny: 1.25rem;
114
+ --control-height-sm: 1.75rem;
115
+ --control-height-md: 2rem;
116
+ --control-height-lg: 2.5rem;
117
+ --control-font-size-md: 0.8125rem;
118
+
119
+ /* Typography */
120
+ --font-sans: "Mori", "Inter Tight", "DM Sans", ui-sans-serif, system-ui, sans-serif;
121
+ --font-heading: "Mori", "Inter Tight", ui-sans-serif, system-ui, sans-serif;
122
+ --font-display: "Mori", "Inter Tight", ui-sans-serif, system-ui, sans-serif;
123
+ --font-weight-heading: 600;
124
+
125
+ /* Zone type scales */
126
+ --zone-product-caption-size: 14px;
127
+ --zone-product-caption-leading: 1.4;
128
+ --zone-product-caption-tracking: -0.14px;
129
+ --zone-product-caption-weight: 400;
130
+ --zone-product-body-sm-size: 16px;
131
+ --zone-product-body-sm-leading: 1.15;
132
+ --zone-product-body-sm-weight: 400;
133
+ --zone-product-body-size: 19px;
134
+ --zone-product-body-leading: 1.15;
135
+ --zone-product-body-weight: 400;
136
+ --zone-product-body-lg-size: 23px;
137
+ --zone-product-body-lg-leading: 1.38;
138
+ --zone-product-body-lg-tracking: -0.23px;
139
+ --zone-product-body-lg-weight: 400;
140
+ --zone-product-subheading-size: 34px;
141
+ --zone-product-subheading-leading: 1.2;
142
+ --zone-product-subheading-tracking: -0.34px;
143
+ --zone-product-subheading-weight: 400;
144
+ --zone-product-heading-size: 44px;
145
+ --zone-product-heading-leading: 1.2;
146
+ --zone-product-heading-tracking: -0.44px;
147
+ --zone-product-heading-weight: 600;
148
+ --zone-marketing-body-size: 19px;
149
+ --zone-marketing-body-leading: 1.15;
150
+ --zone-marketing-body-weight: 400;
151
+ --zone-marketing-heading-size: 66px;
152
+ --zone-marketing-heading-leading: 1.2;
153
+ --zone-marketing-heading-tracking: -0.66px;
154
+ --zone-marketing-heading-weight: 600;
155
+ --zone-marketing-heading-lg-size: 101px;
156
+ --zone-marketing-heading-lg-leading: 1;
157
+ --zone-marketing-heading-lg-tracking: -1.11px;
158
+ --zone-marketing-heading-lg-weight: 600;
159
+ --zone-marketing-display-size: 224px;
160
+ --zone-marketing-display-leading: 0.9;
161
+ --zone-marketing-display-tracking: -4.48px;
162
+ --zone-marketing-display-weight: 600;
163
+
164
+ /* Taxonomy / decorative (not product CTA) */
165
+ --brand-category-gsap: #0ae448;
166
+ --brand-category-scroll: #fec5fb;
167
+ --brand-category-svg: #ff8709;
168
+ --brand-category-text: #9d95ff;
169
+ --brand-category-ui: #00bae2;
170
+ --brand-category-other: #abff84;
171
+ }
172
+
173
+ /* Zone: product — product never inherits marketing display */
174
+ [data-zone="product"],
175
+ .zone-product {
176
+ font-size: var(--zone-product-body-size, 19px);
177
+ line-height: var(--zone-product-body-leading, 1.15);
178
+ font-weight: var(--zone-product-body-weight, 400);
179
+ --text-caption: var(--zone-product-caption-size, inherit);
180
+ --leading-caption: var(--zone-product-caption-leading, inherit);
181
+ --tracking-caption: var(--zone-product-caption-tracking, inherit);
182
+ --text-body-sm: var(--zone-product-body-sm-size, inherit);
183
+ --leading-body-sm: var(--zone-product-body-sm-leading, inherit);
184
+ --tracking-body-sm: var(--zone-product-body-sm-tracking, inherit);
185
+ --text-body: var(--zone-product-body-size, inherit);
186
+ --leading-body: var(--zone-product-body-leading, inherit);
187
+ --tracking-body: var(--zone-product-body-tracking, inherit);
188
+ --text-body-lg: var(--zone-product-body-lg-size, inherit);
189
+ --leading-body-lg: var(--zone-product-body-lg-leading, inherit);
190
+ --tracking-body-lg: var(--zone-product-body-lg-tracking, inherit);
191
+ --text-subheading: var(--zone-product-subheading-size, inherit);
192
+ --leading-subheading: var(--zone-product-subheading-leading, inherit);
193
+ --tracking-subheading: var(--zone-product-subheading-tracking, inherit);
194
+ --text-heading-sm: var(--zone-product-heading-sm-size, inherit);
195
+ --leading-heading-sm: var(--zone-product-heading-sm-leading, inherit);
196
+ --tracking-heading-sm: var(--zone-product-heading-sm-tracking, inherit);
197
+ --text-heading: var(--zone-product-heading-size, inherit);
198
+ --leading-heading: var(--zone-product-heading-leading, inherit);
199
+ --tracking-heading: var(--zone-product-heading-tracking, inherit);
200
+ --text-heading-lg: var(--zone-product-heading-lg-size, inherit);
201
+ --leading-heading-lg: var(--zone-product-heading-lg-leading, inherit);
202
+ --tracking-heading-lg: var(--zone-product-heading-lg-tracking, inherit);
203
+ --text-display: var(--zone-product-display-size, var(--zone-product-heading-size, inherit));
204
+ --leading-display: var(--zone-product-display-leading, inherit);
205
+ --tracking-display: var(--zone-product-display-tracking, inherit);
206
+ }
207
+
208
+ /* Zone: marketing — product never inherits marketing display */
209
+ [data-zone="marketing"],
210
+ .zone-marketing {
211
+ font-size: var(--zone-marketing-body-size, 19px);
212
+ line-height: var(--zone-marketing-body-leading, 1.15);
213
+ font-weight: var(--zone-marketing-body-weight, 400);
214
+ --text-caption: var(--zone-marketing-caption-size, inherit);
215
+ --leading-caption: var(--zone-marketing-caption-leading, inherit);
216
+ --tracking-caption: var(--zone-marketing-caption-tracking, inherit);
217
+ --text-body-sm: var(--zone-marketing-body-sm-size, inherit);
218
+ --leading-body-sm: var(--zone-marketing-body-sm-leading, inherit);
219
+ --tracking-body-sm: var(--zone-marketing-body-sm-tracking, inherit);
220
+ --text-body: var(--zone-marketing-body-size, inherit);
221
+ --leading-body: var(--zone-marketing-body-leading, inherit);
222
+ --tracking-body: var(--zone-marketing-body-tracking, inherit);
223
+ --text-body-lg: var(--zone-marketing-body-lg-size, inherit);
224
+ --leading-body-lg: var(--zone-marketing-body-lg-leading, inherit);
225
+ --tracking-body-lg: var(--zone-marketing-body-lg-tracking, inherit);
226
+ --text-subheading: var(--zone-marketing-subheading-size, inherit);
227
+ --leading-subheading: var(--zone-marketing-subheading-leading, inherit);
228
+ --tracking-subheading: var(--zone-marketing-subheading-tracking, inherit);
229
+ --text-heading-sm: var(--zone-marketing-heading-sm-size, inherit);
230
+ --leading-heading-sm: var(--zone-marketing-heading-sm-leading, inherit);
231
+ --tracking-heading-sm: var(--zone-marketing-heading-sm-tracking, inherit);
232
+ --text-heading: var(--zone-marketing-heading-size, inherit);
233
+ --leading-heading: var(--zone-marketing-heading-leading, inherit);
234
+ --tracking-heading: var(--zone-marketing-heading-tracking, inherit);
235
+ --text-heading-lg: var(--zone-marketing-heading-lg-size, inherit);
236
+ --leading-heading-lg: var(--zone-marketing-heading-lg-leading, inherit);
237
+ --tracking-heading-lg: var(--zone-marketing-heading-lg-tracking, inherit);
238
+ --text-display: var(--zone-marketing-display-size, inherit);
239
+ --leading-display: var(--zone-marketing-display-leading, inherit);
240
+ --tracking-display: var(--zone-marketing-display-tracking, inherit);
241
+ }
242
+
243
+ /* Product chrome must not use decorative gradients as action fills */
244
+ [data-zone="product"] .btn-brand-default,
245
+ .zone-product .btn-brand-default {
246
+ /* intentional no-op: documents contract; action uses --primary only */
247
+ }
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Brand carrier skin: Linear (linear) v1.0.0
3
+ * darkDefault=true dualMode=true button=solid
4
+ * fonts=1 zones=yes mode=global
5
+ * Contract: roles.action → --primary; roles.brand → --brand-mark (never default CTA)
6
+ */
7
+
8
+ /* Brand font faces */
9
+ @font-face {
10
+ font-family: "Inter Variable";
11
+ src: url("https://cdn.jsdelivr.net/fontsource/fonts/inter:vf@latest/latin-wght-normal.woff2")
12
+ format("woff2");
13
+ font-weight: 100 900;
14
+ font-display: swap;
15
+ }
16
+
17
+ :root,
18
+ html[data-brand="linear"] {
19
+ /* ── Color roles (carrier) ── */
20
+ --role-canvas: 0 0% 100%;
21
+ --role-canvas-fg: 210 11% 4%;
22
+ --role-surface: 0 0% 98%;
23
+ --role-surface-fg: 210 11% 4%;
24
+ --role-action: 64 89% 54%;
25
+ --role-action-fg: 210 11% 4%;
26
+ --role-quiet: 220 14% 96%;
27
+ --role-quiet-fg: 210 11% 4%;
28
+ --role-muted: 220 14% 96%;
29
+ --role-muted-fg: 218 5% 41%;
30
+ --role-border: 220 13% 91%;
31
+ --role-input: 0 0% 100%;
32
+ --role-ring: 64 89% 54%;
33
+
34
+ /* ── shadcn bridge (primary = action CTA) ── */
35
+ --background: 0 0% 100%;
36
+ --foreground: 210 11% 4%;
37
+ --card: 0 0% 98%;
38
+ --card-foreground: 210 11% 4%;
39
+ --popover: 0 0% 98%;
40
+ --popover-foreground: 210 11% 4%;
41
+ --primary: 64 89% 54%;
42
+ --primary-foreground: 210 11% 4%;
43
+ --secondary: 220 14% 96%;
44
+ --secondary-foreground: 210 11% 4%;
45
+ --muted: 220 14% 96%;
46
+ --muted-foreground: 218 5% 41%;
47
+ --accent: 220 14% 96%;
48
+ --accent-foreground: 210 11% 4%;
49
+ --destructive: 0 79% 63%;
50
+ --destructive-foreground: 0 0% 100%;
51
+ --border: 220 13% 91%;
52
+ --input: 0 0% 100%;
53
+ --ring: 64 89% 54%;
54
+ --success: 134 62% 40%;
55
+ --success-foreground: 0 0% 100%;
56
+ --info: 186 98% 40%;
57
+ --info-foreground: 0 0% 100%;
58
+ --sidebar: 0 0% 98%;
59
+ --sidebar-foreground: 210 11% 4%;
60
+ --sidebar-primary: 64 89% 54%;
61
+ --sidebar-primary-foreground: 210 11% 4%;
62
+ --sidebar-accent: 220 14% 96%;
63
+ --sidebar-accent-foreground: 210 11% 4%;
64
+ --sidebar-border: 220 13% 91%;
65
+ --sidebar-ring: 64 89% 54%;
66
+ --brand-gradient: hsl(var(--primary));
67
+ --brand-gradient-reverse: hsl(var(--primary));
68
+ --brand-gradient-vertical: hsl(var(--primary));
69
+ --brand-gradient-radial: hsl(var(--primary));
70
+
71
+ /* Recipe (action language + free elev/radii) */
72
+ /* Shape slots */
73
+ --btn-default-radius: 6px;
74
+ --radius-button: 6px;
75
+ --radius-buttons: 6px;
76
+ --radius-md: 6px;
77
+ --radius-card: 12px;
78
+ --radius-lg: 12px;
79
+ --radius-badge: 9999px;
80
+ --badge-default-radius: 9999px;
81
+ --radius-inputs: 6px;
82
+ --input-radius: 6px;
83
+ --radius-pill: 9999px;
84
+
85
+ /* Free elevation (carrier-provided CSS shadows) */
86
+ --elevation-card: var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05));
87
+ --elevation-control: var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.04));
88
+ --elevation-raised: var(--shadow-md, 0 4px 6px -1px rgb(0 0 0 / 0.1));
89
+ --shadow-xs: var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.04));
90
+ --shadow-sm: var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05));
91
+ --shadow-md: var(--shadow-md, 0 4px 6px -1px rgb(0 0 0 / 0.1));
92
+ --shadow-lg: var(--shadow-md, 0 4px 6px -1px rgb(0 0 0 / 0.1));
93
+ --btn-default-shadow: 0 0 #0000;
94
+ --btn-default-bg: hsl(var(--primary));
95
+ --btn-default-fg: hsl(var(--primary-foreground));
96
+ --btn-default-border-width: 0px;
97
+ --btn-default-border: transparent;
98
+ --btn-default-stroke-gradient: transparent;
99
+ --btn-default-hover-bg: color-mix(in srgb, hsl(var(--primary)) 90%, transparent);
100
+ --badge-default-bg: hsl(var(--primary));
101
+ --badge-default-fg: hsl(var(--primary-foreground));
102
+ --badge-default-border: transparent;
103
+ --badge-default-hover-bg: color-mix(in srgb, hsl(var(--primary)) 80%, transparent);
104
+ --btn-default-padding-y: 0.5rem;
105
+ --btn-default-padding-x: 0.875rem;
106
+ --control-height-tiny: 1.25rem;
107
+ --control-height-sm: 1.75rem;
108
+ --control-height-md: 2rem;
109
+ --control-height-lg: 2.5rem;
110
+ --control-font-size-md: 0.8125rem;
111
+
112
+ /* Typography */
113
+ --font-sans: "Inter Variable", "Inter", ui-sans-serif, system-ui, sans-serif;
114
+ --font-heading: "Inter Variable", "Inter", ui-sans-serif, system-ui, sans-serif;
115
+ --font-display: "Inter Variable", "Inter", ui-sans-serif, system-ui, sans-serif;
116
+ --font-mono: "Berkeley Mono", "JetBrains Mono", ui-monospace, monospace;
117
+ --font-weight-heading: 510;
118
+
119
+ /* Zone type scales */
120
+ --zone-product-caption-size: 13px;
121
+ --zone-product-caption-leading: 1.2;
122
+ --zone-product-caption-weight: 400;
123
+ --zone-product-body-sm-size: 14px;
124
+ --zone-product-body-sm-leading: 1.5;
125
+ --zone-product-body-sm-weight: 400;
126
+ --zone-product-body-size: 14px;
127
+ --zone-product-body-leading: 1.5;
128
+ --zone-product-body-weight: 400;
129
+ --zone-product-body-lg-size: 16px;
130
+ --zone-product-body-lg-leading: 1.5;
131
+ --zone-product-body-lg-weight: 400;
132
+ --zone-product-heading-size: 24px;
133
+ --zone-product-heading-leading: 1.25;
134
+ --zone-product-heading-tracking: -0.012em;
135
+ --zone-product-heading-weight: 510;
136
+ --zone-product-display-size: 32px;
137
+ --zone-product-display-leading: 1.15;
138
+ --zone-product-display-tracking: -0.022em;
139
+ --zone-product-display-weight: 510;
140
+ --zone-marketing-body-size: 15px;
141
+ --zone-marketing-body-leading: 1.6;
142
+ --zone-marketing-body-weight: 400;
143
+ --zone-marketing-heading-size: 48px;
144
+ --zone-marketing-heading-leading: 1;
145
+ --zone-marketing-heading-tracking: -0.022em;
146
+ --zone-marketing-heading-weight: 510;
147
+ --zone-marketing-display-size: 72px;
148
+ --zone-marketing-display-leading: 1;
149
+ --zone-marketing-display-tracking: -0.022em;
150
+ --zone-marketing-display-weight: 510;
151
+ }
152
+
153
+ .dark,
154
+ html.dark[data-brand="linear"] {
155
+ /* ── Color roles (carrier) ── */
156
+ --role-canvas: 210 11% 4%;
157
+ --role-canvas-fg: 0 0% 100%;
158
+ --role-surface: 210 6% 6%;
159
+ --role-surface-fg: 0 0% 100%;
160
+ --role-action: 64 89% 54%;
161
+ --role-action-fg: 210 11% 4%;
162
+ --role-quiet: 223 9% 15%;
163
+ --role-quiet-fg: 218 21% 85%;
164
+ --role-muted: 210 4% 9%;
165
+ --role-muted-fg: 218 5% 41%;
166
+ --role-border: 223 9% 15%;
167
+ --role-input: 223 9% 15%;
168
+ --role-ring: 64 89% 54%;
169
+
170
+ /* ── shadcn bridge (primary = action CTA) ── */
171
+ --background: 210 11% 4%;
172
+ --foreground: 0 0% 100%;
173
+ --card: 210 6% 6%;
174
+ --card-foreground: 0 0% 100%;
175
+ --popover: 210 6% 6%;
176
+ --popover-foreground: 0 0% 100%;
177
+ --primary: 64 89% 54%;
178
+ --primary-foreground: 210 11% 4%;
179
+ --secondary: 223 9% 15%;
180
+ --secondary-foreground: 218 21% 85%;
181
+ --muted: 210 4% 9%;
182
+ --muted-foreground: 218 5% 41%;
183
+ --accent: 223 9% 15%;
184
+ --accent-foreground: 218 21% 85%;
185
+ --destructive: 0 79% 63%;
186
+ --destructive-foreground: 0 0% 100%;
187
+ --border: 223 9% 15%;
188
+ --input: 223 9% 15%;
189
+ --ring: 64 89% 54%;
190
+ --success: 134 62% 40%;
191
+ --success-foreground: 0 0% 100%;
192
+ --info: 186 98% 40%;
193
+ --info-foreground: 0 0% 100%;
194
+ --sidebar: 210 6% 6%;
195
+ --sidebar-foreground: 0 0% 100%;
196
+ --sidebar-primary: 64 89% 54%;
197
+ --sidebar-primary-foreground: 210 11% 4%;
198
+ --sidebar-accent: 223 9% 15%;
199
+ --sidebar-accent-foreground: 218 21% 85%;
200
+ --sidebar-border: 223 9% 15%;
201
+ --sidebar-ring: 64 89% 54%;
202
+ --brand-gradient: hsl(var(--primary));
203
+ --brand-gradient-reverse: hsl(var(--primary));
204
+ --brand-gradient-vertical: hsl(var(--primary));
205
+ --brand-gradient-radial: hsl(var(--primary));
206
+ }
207
+
208
+ /* Zone: product — product never inherits marketing display */
209
+ [data-zone="product"],
210
+ .zone-product {
211
+ font-size: var(--zone-product-body-size, 14px);
212
+ line-height: var(--zone-product-body-leading, 1.5);
213
+ font-weight: var(--zone-product-body-weight, 400);
214
+ --text-caption: var(--zone-product-caption-size, inherit);
215
+ --leading-caption: var(--zone-product-caption-leading, inherit);
216
+ --tracking-caption: var(--zone-product-caption-tracking, inherit);
217
+ --text-body-sm: var(--zone-product-body-sm-size, inherit);
218
+ --leading-body-sm: var(--zone-product-body-sm-leading, inherit);
219
+ --tracking-body-sm: var(--zone-product-body-sm-tracking, inherit);
220
+ --text-body: var(--zone-product-body-size, inherit);
221
+ --leading-body: var(--zone-product-body-leading, inherit);
222
+ --tracking-body: var(--zone-product-body-tracking, inherit);
223
+ --text-body-lg: var(--zone-product-body-lg-size, inherit);
224
+ --leading-body-lg: var(--zone-product-body-lg-leading, inherit);
225
+ --tracking-body-lg: var(--zone-product-body-lg-tracking, inherit);
226
+ --text-subheading: var(--zone-product-subheading-size, inherit);
227
+ --leading-subheading: var(--zone-product-subheading-leading, inherit);
228
+ --tracking-subheading: var(--zone-product-subheading-tracking, inherit);
229
+ --text-heading-sm: var(--zone-product-heading-sm-size, inherit);
230
+ --leading-heading-sm: var(--zone-product-heading-sm-leading, inherit);
231
+ --tracking-heading-sm: var(--zone-product-heading-sm-tracking, inherit);
232
+ --text-heading: var(--zone-product-heading-size, inherit);
233
+ --leading-heading: var(--zone-product-heading-leading, inherit);
234
+ --tracking-heading: var(--zone-product-heading-tracking, inherit);
235
+ --text-heading-lg: var(--zone-product-heading-lg-size, inherit);
236
+ --leading-heading-lg: var(--zone-product-heading-lg-leading, inherit);
237
+ --tracking-heading-lg: var(--zone-product-heading-lg-tracking, inherit);
238
+ --text-display: var(--zone-product-display-size, var(--zone-product-heading-size, inherit));
239
+ --leading-display: var(--zone-product-display-leading, inherit);
240
+ --tracking-display: var(--zone-product-display-tracking, inherit);
241
+ }
242
+
243
+ /* Zone: marketing — product never inherits marketing display */
244
+ [data-zone="marketing"],
245
+ .zone-marketing {
246
+ font-size: var(--zone-marketing-body-size, 15px);
247
+ line-height: var(--zone-marketing-body-leading, 1.6);
248
+ font-weight: var(--zone-marketing-body-weight, 400);
249
+ --text-caption: var(--zone-marketing-caption-size, inherit);
250
+ --leading-caption: var(--zone-marketing-caption-leading, inherit);
251
+ --tracking-caption: var(--zone-marketing-caption-tracking, inherit);
252
+ --text-body-sm: var(--zone-marketing-body-sm-size, inherit);
253
+ --leading-body-sm: var(--zone-marketing-body-sm-leading, inherit);
254
+ --tracking-body-sm: var(--zone-marketing-body-sm-tracking, inherit);
255
+ --text-body: var(--zone-marketing-body-size, inherit);
256
+ --leading-body: var(--zone-marketing-body-leading, inherit);
257
+ --tracking-body: var(--zone-marketing-body-tracking, inherit);
258
+ --text-body-lg: var(--zone-marketing-body-lg-size, inherit);
259
+ --leading-body-lg: var(--zone-marketing-body-lg-leading, inherit);
260
+ --tracking-body-lg: var(--zone-marketing-body-lg-tracking, inherit);
261
+ --text-subheading: var(--zone-marketing-subheading-size, inherit);
262
+ --leading-subheading: var(--zone-marketing-subheading-leading, inherit);
263
+ --tracking-subheading: var(--zone-marketing-subheading-tracking, inherit);
264
+ --text-heading-sm: var(--zone-marketing-heading-sm-size, inherit);
265
+ --leading-heading-sm: var(--zone-marketing-heading-sm-leading, inherit);
266
+ --tracking-heading-sm: var(--zone-marketing-heading-sm-tracking, inherit);
267
+ --text-heading: var(--zone-marketing-heading-size, inherit);
268
+ --leading-heading: var(--zone-marketing-heading-leading, inherit);
269
+ --tracking-heading: var(--zone-marketing-heading-tracking, inherit);
270
+ --text-heading-lg: var(--zone-marketing-heading-lg-size, inherit);
271
+ --leading-heading-lg: var(--zone-marketing-heading-lg-leading, inherit);
272
+ --tracking-heading-lg: var(--zone-marketing-heading-lg-tracking, inherit);
273
+ --text-display: var(--zone-marketing-display-size, inherit);
274
+ --leading-display: var(--zone-marketing-display-leading, inherit);
275
+ --tracking-display: var(--zone-marketing-display-tracking, inherit);
276
+ }
277
+
278
+ /* Product chrome must not use decorative gradients as action fills */
279
+ [data-zone="product"] .btn-brand-default,
280
+ .zone-product .btn-brand-default {
281
+ /* intentional no-op: documents contract; action uses --primary only */
282
+ }