@actioneer/ads 0.4.0 → 0.5.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/llms.txt CHANGED
@@ -27,9 +27,9 @@ Reference semantic roles, never raw color/dimension literals (ads-lint rejects l
27
27
  - `muted-foreground`
28
28
  - `card`
29
29
  - `card-foreground`
30
- - `brand` — Brand (black) — used for solid fills, not body text
31
- - `brand-foreground` — Brand (black) — used for solid fills, not body text
32
- - `brand-hover` — Brand (black) — used for solid fills, not body text
30
+ - `brand` — Brand (Sage green) — used for solid fills / primary actions, not body text
31
+ - `brand-foreground` — Brand (Sage green) — used for solid fills / primary actions, not body text
32
+ - `brand-hover` — Brand (Sage green) — used for solid fills / primary actions, not body text
33
33
  - `fill` — Quiet fill — secondary buttons, avatars, chips
34
34
  - `fill-foreground` — Quiet fill — secondary buttons, avatars, chips
35
35
  - `accent`
@@ -252,7 +252,7 @@ When tabs and a search field control the same content, they share one responsive
252
252
  ### Overlays
253
253
 
254
254
  - [CommandPalette] — component [docs](https://ads.actioneer.dev/#/command-palette)
255
- - [Drawer] — Use the portaled Drawer for a focused, blocking task (form, details, settings) that slides in over the page with a backdrop and focus trap; use InlineDrawer instead for a contextual panel that must stay in-flow beside content the user still needs. (variants: side: right|left|bottom; size: sm|md|lg|xl) [docs](https://ads.actioneer.dev/#/drawer)
255
+ - [Drawer] — Use the portaled Drawer for a focused, blocking task (form, details, settings) that slides in over the page with a backdrop and focus trap; use InlineDrawer instead for a contextual panel that must stay in-flow beside content the user still needs. The inline Drawer is a flat white panel (bg-card, no shadow, no border) that reads ONLY by fill contrast, so it MUST sit as a sibling of its content inside a bg-surface flex row — on a white (bg-background) parent it is invisible. If it doesn't read, tint the ground; never add a border or shadow to force it. (variants: side: right|left|bottom; size: sm|md|lg|xl) [docs](https://ads.actioneer.dev/#/drawer)
256
256
  - [DrawerBody] — component (variants: side: right|left|bottom; size: sm|md|lg|xl) [docs](https://ads.actioneer.dev/#/drawer)
257
257
  - [DrawerFooter] — component (variants: side: right|left|bottom; size: sm|md|lg|xl) [docs](https://ads.actioneer.dev/#/drawer)
258
258
  - [DrawerHeader] — component (variants: side: right|left|bottom; size: sm|md|lg|xl) [docs](https://ads.actioneer.dev/#/drawer)
@@ -476,6 +476,10 @@ When tabs and a search field control the same content, they share one responsive
476
476
  - [BrandMark] — component [docs](https://ads.actioneer.dev/#/brand-loader)
477
477
  - [BrandTexture] — Place the ambient halftone wash behind a welcome or hero surface inside a relative container; set lifted when real content takes over so the field dissolves upward, and paused a beat later to stop the shader clock. [docs](https://ads.actioneer.dev/#/brand-texture)
478
478
  - [BrandTextureField] — component [docs](https://ads.actioneer.dev/#/brand-texture)
479
+ - [EmptyConnections] — component [docs](https://ads.actioneer.dev/#/empty-state)
480
+ - [EmptyDocuments] — component [docs](https://ads.actioneer.dev/#/empty-state)
481
+ - [EmptyInsights] — component [docs](https://ads.actioneer.dev/#/empty-state)
482
+ - [EmptySearch] — component [docs](https://ads.actioneer.dev/#/empty-state)
479
483
  - [EmptyState] — Use as the centered placeholder inside a list, table, or panel that currently has nothing to show. [docs](https://ads.actioneer.dev/#/empty-state)
480
484
  - [PixelBlastSVG] — component [docs](https://ads.actioneer.dev/#/pixel-blast-svg)
481
485
  - [Progress] — Use for a determinate or indeterminate bar showing task completion, driven by a value/max (default 100). [docs](https://ads.actioneer.dev/#/progress)
package/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actioneer/ads",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "importPath": "@actioneer/ads",
5
5
  "styleImport": "@import \"@actioneer/ads/styles.css\";",
6
6
  "families": [
@@ -16,8 +16,8 @@
16
16
  "Chat"
17
17
  ],
18
18
  "counts": {
19
- "total": 674,
20
- "component": 323,
19
+ "total": 678,
20
+ "component": 327,
21
21
  "type": 288,
22
22
  "hook": 10,
23
23
  "util": 53
@@ -148,7 +148,7 @@
148
148
  "text-brand",
149
149
  "border-brand"
150
150
  ],
151
- "description": "Brand (black) — used for solid fills, not body text"
151
+ "description": "Brand (Sage green) — used for solid fills / primary actions, not body text"
152
152
  },
153
153
  {
154
154
  "name": "--color-brand-foreground",
@@ -157,7 +157,7 @@
157
157
  "text-brand-foreground",
158
158
  "bg-brand-foreground"
159
159
  ],
160
- "description": "Brand (black) — used for solid fills, not body text"
160
+ "description": "Brand (Sage green) — used for solid fills / primary actions, not body text"
161
161
  },
162
162
  {
163
163
  "name": "--color-brand-hover",
@@ -167,7 +167,7 @@
167
167
  "text-brand-hover",
168
168
  "border-brand-hover"
169
169
  ],
170
- "description": "Brand (black) — used for solid fills, not body text"
170
+ "description": "Brand (Sage green) — used for solid fills / primary actions, not body text"
171
171
  },
172
172
  {
173
173
  "name": "--color-fill",
@@ -1318,7 +1318,7 @@
1318
1318
  "kind": "component",
1319
1319
  "importPath": "@actioneer/ads",
1320
1320
  "module": "drawer",
1321
- "description": "Use the portaled Drawer for a focused, blocking task (form, details, settings) that slides in over the page with a backdrop and focus trap; use InlineDrawer instead for a contextual panel that must stay in-flow beside content the user still needs.",
1321
+ "description": "Use the portaled Drawer for a focused, blocking task (form, details, settings) that slides in over the page with a backdrop and focus trap; use InlineDrawer instead for a contextual panel that must stay in-flow beside content the user still needs. The inline Drawer is a flat white panel (bg-card, no shadow, no border) that reads ONLY by fill contrast, so it MUST sit as a sibling of its content inside a bg-surface flex row — on a white (bg-background) parent it is invisible. If it doesn't read, tint the ground; never add a border or shadow to force it.",
1322
1322
  "showcaseUrl": "https://ads.actioneer.dev/#/drawer",
1323
1323
  "variants": {
1324
1324
  "side": {
@@ -3785,6 +3785,38 @@
3785
3785
  "module": "brand-texture",
3786
3786
  "showcaseUrl": "https://ads.actioneer.dev/#/brand-texture"
3787
3787
  },
3788
+ {
3789
+ "name": "EmptyConnections",
3790
+ "family": "Feedback",
3791
+ "kind": "component",
3792
+ "importPath": "@actioneer/ads",
3793
+ "module": "empty-state",
3794
+ "showcaseUrl": "https://ads.actioneer.dev/#/empty-state"
3795
+ },
3796
+ {
3797
+ "name": "EmptyDocuments",
3798
+ "family": "Feedback",
3799
+ "kind": "component",
3800
+ "importPath": "@actioneer/ads",
3801
+ "module": "empty-state",
3802
+ "showcaseUrl": "https://ads.actioneer.dev/#/empty-state"
3803
+ },
3804
+ {
3805
+ "name": "EmptyInsights",
3806
+ "family": "Feedback",
3807
+ "kind": "component",
3808
+ "importPath": "@actioneer/ads",
3809
+ "module": "empty-state",
3810
+ "showcaseUrl": "https://ads.actioneer.dev/#/empty-state"
3811
+ },
3812
+ {
3813
+ "name": "EmptySearch",
3814
+ "family": "Feedback",
3815
+ "kind": "component",
3816
+ "importPath": "@actioneer/ads",
3817
+ "module": "empty-state",
3818
+ "showcaseUrl": "https://ads.actioneer.dev/#/empty-state"
3819
+ },
3788
3820
  {
3789
3821
  "name": "EmptyState",
3790
3822
  "family": "Feedback",
@@ -3795,6 +3827,7 @@
3795
3827
  "showcaseUrl": "https://ads.actioneer.dev/#/empty-state",
3796
3828
  "props": [
3797
3829
  "icon",
3830
+ "illustration",
3798
3831
  "title",
3799
3832
  "description",
3800
3833
  "action"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actioneer/ads",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Actioneer Design System 1.0 — React 19 + Tailwind v4 component library.",
5
5
  "type": "module",
6
6
  "sideEffects": [
package/styles.css CHANGED
@@ -29,7 +29,7 @@
29
29
  /*
30
30
  * ============================================================
31
31
  * ADS 1.0 — Design Tokens
32
- * Brand: BLACK · Typeface: Aribau Grotesk (Regular + Bold)
32
+ * Brand: Sage (green) · Neutrals: warm greige · Typeface: Aribau Grotesk
33
33
  * ============================================================
34
34
  */
35
35
  @theme {
@@ -67,25 +67,44 @@
67
67
  --text-2xl: 1.625rem; /* 26px — display */
68
68
  --text-2xl--line-height: 2rem;
69
69
 
70
- /* ---- Ink scale (the black brand ramp) ---- */
71
- --color-ink-50: #f6f6f7;
72
- --color-ink-100: #ececed;
73
- --color-ink-200: #d9d9dc;
74
- --color-ink-300: #b8b8bd;
75
- --color-ink-400: #8f8f96;
76
- --color-ink-500: #6e6e75;
77
- --color-ink-600: #55555b;
78
- --color-ink-700: #3f3f44;
79
- --color-ink-800: #262629;
80
- --color-ink-900: #161618;
81
- --color-ink-950: #0a0a0b;
70
+ /* ---- Neutral ramp — warm greige (the workhorse: backgrounds, surfaces,
71
+ text, borders). Kept a touch warm so it sits with Sage without turning
72
+ green — one family, not colour plus grey. The `ink` name is retained so
73
+ every existing reference resolves; the values are now greige. ---- */
74
+ --color-ink-50: #f2f2ed;
75
+ --color-ink-100: #e7e7e1;
76
+ --color-ink-200: #d8d7cf;
77
+ --color-ink-300: #c7c6be;
78
+ --color-ink-400: #a8a79e;
79
+ --color-ink-500: #8c8c84;
80
+ --color-ink-600: #6b6b63;
81
+ --color-ink-700: #4a4a44;
82
+ --color-ink-800: #33322e;
83
+ --color-ink-900: #26251f;
84
+ --color-ink-950: #1a1a17;
85
+
86
+ /* ---- Brand ramp — Sage (one green hue: brand, primary actions, focal
87
+ data). 600 is the primary. On a white/greige ground, colour earns its
88
+ place — the eye always knows where to look. ---- */
89
+ --color-sage-50: #eff5ea;
90
+ --color-sage-100: #dde8cf;
91
+ --color-sage-200: #a9ce86;
92
+ --color-sage-300: #8abb6a;
93
+ --color-sage-400: #6da24e;
94
+ --color-sage-500: #548c41;
95
+ --color-sage-600: #3f7a34;
96
+ --color-sage-700: #2c5724;
97
+ --color-sage-800: #254a1d;
98
+ --color-sage-900: #1e3a16;
82
99
 
83
100
  /* ---- Semantic accent ramps ---- */
84
- --color-success-50: #f0fdf4;
85
- --color-success-100: #dcfce7;
86
- --color-success-500: #22c55e;
87
- --color-success-600: #16a34a;
88
- --color-success-700: #15803d;
101
+ /* Success — shifted cooler (emerald/teal) so positive states stay clearly
102
+ distinct from the warm Sage brand green. */
103
+ --color-success-50: #ecfdf5;
104
+ --color-success-100: #d1fae5;
105
+ --color-success-500: #10b981;
106
+ --color-success-600: #059669;
107
+ --color-success-700: #047857;
89
108
 
90
109
  --color-warning-50: #fffaeb;
91
110
  --color-warning-100: #fef0c7;
@@ -105,6 +124,19 @@
105
124
  --color-info-600: #1570ef;
106
125
  --color-info-700: #175cd3;
107
126
 
127
+ /* ---- Data-viz accents ---- three small ramps for charts & illustration.
128
+ deep = text/lines · base = fills · soft = backgrounds. Separate from the
129
+ semantic status ramps above; these are the "focal data" companions to Sage. */
130
+ --color-cyan-soft: #c7efe9; /* Cyan — vivid cool contrast; 2nd series, links */
131
+ --color-cyan-base: #12b5a5;
132
+ --color-cyan-deep: #0a7e82;
133
+ --color-lime-soft: #e4efcf; /* Lichen lime — energetic pop from the mark */
134
+ --color-lime-base: #a3c765;
135
+ --color-lime-deep: #6e8f30;
136
+ --color-amber-soft: #fce6c0; /* Amber — warm pop; highlights, "new" */
137
+ --color-amber-base: #f3a72e;
138
+ --color-amber-deep: #b57414;
139
+
108
140
  /* ---- Radii ---- */
109
141
  --radius-xs: 0.25rem;
110
142
  --radius-sm: 0.375rem;
@@ -201,12 +233,16 @@
201
233
  * every comparison series survives production builds.
202
234
  */
203
235
  :root {
204
- --color-chart-1: var(--color-ink-950);
205
- --color-chart-2: var(--color-ink-400);
206
- --color-chart-3: var(--color-ink-300);
207
- --color-chart-4: var(--color-info-500);
208
- --color-chart-5: var(--color-warning-500);
209
- --color-chart-6: var(--color-success-500);
236
+ /* Data-series palette — Sage (brand) leads as the focal/primary series,
237
+ then the three data-viz accents and two deep variants for extended
238
+ series. Explicit hexes (not var()) so every series survives @theme
239
+ pruning, since chart colours are referenced from SVG prop strings. */
240
+ --color-chart-1: #3f7a34; /* sage — primary / focal series */
241
+ --color-chart-2: #12b5a5; /* cyan base */
242
+ --color-chart-3: #f3a72e; /* amber base */
243
+ --color-chart-4: #a3c765; /* lichen lime base */
244
+ --color-chart-5: #0a7e82; /* cyan deep */
245
+ --color-chart-6: #b57414; /* amber deep */
210
246
  }
211
247
 
212
248
  /*
@@ -232,7 +268,7 @@
232
268
  --color-card: var(--card);
233
269
  --color-card-foreground: var(--card-foreground);
234
270
 
235
- /* Brand (black) — used for solid fills, not body text */
271
+ /* Brand (Sage green) — used for solid fills / primary actions, not body text */
236
272
  --color-brand: var(--brand);
237
273
  --color-brand-foreground: var(--brand-foreground);
238
274
  --color-brand-hover: var(--brand-hover);
@@ -274,41 +310,41 @@
274
310
 
275
311
  /* ---- Theme (light only) ---- */
276
312
  :root {
277
- --background: #ffffff;
278
- --foreground: #0a0a0b;
313
+ --background: #f7f7f3;
314
+ --foreground: #1a1a17;
279
315
 
280
- /* Text hierarchy — accessible on #ffffff */
281
- --text-primary: #0a0a0b; /* ~20:1 AAA */
282
- --text-secondary: #52525b; /* ~7.6:1 AAA */
283
- --text-tertiary: #6e6e75; /* ~5.2:1 AA */
316
+ /* Text hierarchy — accessible on the warm greige canvas */
317
+ --text-primary: #1a1a17; /* ~17:1 AAA */
318
+ --text-secondary: #4a4a44; /* ~8.5:1 AAA */
319
+ --text-tertiary: #6b6b63; /* ~4.8:1 AA */
284
320
 
285
- --surface: #f6f6f7;
286
- --surface-foreground: #262629;
321
+ --surface: #f2f2ed; /* warm greige — the tinted canvas */
322
+ --surface-foreground: #33322e;
287
323
  --elevated: #ffffff;
288
324
 
289
- --muted: #f6f6f7;
290
- --muted-foreground: #6e6e75;
325
+ --muted: #e7e7e1;
326
+ --muted-foreground: #6b6b63;
291
327
 
292
328
  --card: #ffffff;
293
- --card-foreground: #0a0a0b;
329
+ --card-foreground: #1a1a17;
294
330
 
295
- /* BLACK brand */
296
- --brand: #0a0a0b;
331
+ /* Sage brand (green) — primary actions, focal moments */
332
+ --brand: #3f7a34;
297
333
  --brand-foreground: #ffffff;
298
- --brand-hover: #262629;
334
+ --brand-hover: #2c5724;
299
335
 
300
336
  /* Quiet fill */
301
- --fill: #ececed;
302
- --fill-foreground: #161618;
337
+ --fill: #e7e7e1;
338
+ --fill-foreground: #1a1a17;
303
339
 
304
- --accent: #f6f6f7;
305
- --accent-foreground: #161618;
340
+ --accent: #edece6;
341
+ --accent-foreground: #1a1a17;
306
342
 
307
- --border: #ececed;
308
- --border-strong: #d9d9dc;
309
- --input: #d9d9dc;
310
- --ring: rgb(10 10 11 / 0.35);
311
- --overlay: rgb(10 10 11 / 0.5);
343
+ --border: #e7e7e1;
344
+ --border-strong: #c7c6be;
345
+ --input: #c7c6be;
346
+ --ring: rgb(63 122 52 / 0.5);
347
+ --overlay: rgb(26 26 23 / 0.5);
312
348
  }
313
349
  /* ---- Base, keyframes & component helpers ---- */
314
350
  /* Hide scrollbars app-wide but keep scrollability. The demo UI is filled
package/theme.css CHANGED
@@ -2,7 +2,7 @@
2
2
  /*
3
3
  * ============================================================
4
4
  * ADS 1.0 — Design Tokens
5
- * Brand: BLACK · Typeface: Aribau Grotesk (Regular + Bold)
5
+ * Brand: Sage (green) · Neutrals: warm greige · Typeface: Aribau Grotesk
6
6
  * ============================================================
7
7
  */
8
8
  @theme {
@@ -40,25 +40,44 @@
40
40
  --text-2xl: 1.625rem; /* 26px — display */
41
41
  --text-2xl--line-height: 2rem;
42
42
 
43
- /* ---- Ink scale (the black brand ramp) ---- */
44
- --color-ink-50: #f6f6f7;
45
- --color-ink-100: #ececed;
46
- --color-ink-200: #d9d9dc;
47
- --color-ink-300: #b8b8bd;
48
- --color-ink-400: #8f8f96;
49
- --color-ink-500: #6e6e75;
50
- --color-ink-600: #55555b;
51
- --color-ink-700: #3f3f44;
52
- --color-ink-800: #262629;
53
- --color-ink-900: #161618;
54
- --color-ink-950: #0a0a0b;
43
+ /* ---- Neutral ramp — warm greige (the workhorse: backgrounds, surfaces,
44
+ text, borders). Kept a touch warm so it sits with Sage without turning
45
+ green — one family, not colour plus grey. The `ink` name is retained so
46
+ every existing reference resolves; the values are now greige. ---- */
47
+ --color-ink-50: #f2f2ed;
48
+ --color-ink-100: #e7e7e1;
49
+ --color-ink-200: #d8d7cf;
50
+ --color-ink-300: #c7c6be;
51
+ --color-ink-400: #a8a79e;
52
+ --color-ink-500: #8c8c84;
53
+ --color-ink-600: #6b6b63;
54
+ --color-ink-700: #4a4a44;
55
+ --color-ink-800: #33322e;
56
+ --color-ink-900: #26251f;
57
+ --color-ink-950: #1a1a17;
58
+
59
+ /* ---- Brand ramp — Sage (one green hue: brand, primary actions, focal
60
+ data). 600 is the primary. On a white/greige ground, colour earns its
61
+ place — the eye always knows where to look. ---- */
62
+ --color-sage-50: #eff5ea;
63
+ --color-sage-100: #dde8cf;
64
+ --color-sage-200: #a9ce86;
65
+ --color-sage-300: #8abb6a;
66
+ --color-sage-400: #6da24e;
67
+ --color-sage-500: #548c41;
68
+ --color-sage-600: #3f7a34;
69
+ --color-sage-700: #2c5724;
70
+ --color-sage-800: #254a1d;
71
+ --color-sage-900: #1e3a16;
55
72
 
56
73
  /* ---- Semantic accent ramps ---- */
57
- --color-success-50: #f0fdf4;
58
- --color-success-100: #dcfce7;
59
- --color-success-500: #22c55e;
60
- --color-success-600: #16a34a;
61
- --color-success-700: #15803d;
74
+ /* Success — shifted cooler (emerald/teal) so positive states stay clearly
75
+ distinct from the warm Sage brand green. */
76
+ --color-success-50: #ecfdf5;
77
+ --color-success-100: #d1fae5;
78
+ --color-success-500: #10b981;
79
+ --color-success-600: #059669;
80
+ --color-success-700: #047857;
62
81
 
63
82
  --color-warning-50: #fffaeb;
64
83
  --color-warning-100: #fef0c7;
@@ -78,6 +97,19 @@
78
97
  --color-info-600: #1570ef;
79
98
  --color-info-700: #175cd3;
80
99
 
100
+ /* ---- Data-viz accents ---- three small ramps for charts & illustration.
101
+ deep = text/lines · base = fills · soft = backgrounds. Separate from the
102
+ semantic status ramps above; these are the "focal data" companions to Sage. */
103
+ --color-cyan-soft: #c7efe9; /* Cyan — vivid cool contrast; 2nd series, links */
104
+ --color-cyan-base: #12b5a5;
105
+ --color-cyan-deep: #0a7e82;
106
+ --color-lime-soft: #e4efcf; /* Lichen lime — energetic pop from the mark */
107
+ --color-lime-base: #a3c765;
108
+ --color-lime-deep: #6e8f30;
109
+ --color-amber-soft: #fce6c0; /* Amber — warm pop; highlights, "new" */
110
+ --color-amber-base: #f3a72e;
111
+ --color-amber-deep: #b57414;
112
+
81
113
  /* ---- Radii ---- */
82
114
  --radius-xs: 0.25rem;
83
115
  --radius-sm: 0.375rem;
@@ -174,12 +206,16 @@
174
206
  * every comparison series survives production builds.
175
207
  */
176
208
  :root {
177
- --color-chart-1: var(--color-ink-950);
178
- --color-chart-2: var(--color-ink-400);
179
- --color-chart-3: var(--color-ink-300);
180
- --color-chart-4: var(--color-info-500);
181
- --color-chart-5: var(--color-warning-500);
182
- --color-chart-6: var(--color-success-500);
209
+ /* Data-series palette — Sage (brand) leads as the focal/primary series,
210
+ then the three data-viz accents and two deep variants for extended
211
+ series. Explicit hexes (not var()) so every series survives @theme
212
+ pruning, since chart colours are referenced from SVG prop strings. */
213
+ --color-chart-1: #3f7a34; /* sage — primary / focal series */
214
+ --color-chart-2: #12b5a5; /* cyan base */
215
+ --color-chart-3: #f3a72e; /* amber base */
216
+ --color-chart-4: #a3c765; /* lichen lime base */
217
+ --color-chart-5: #0a7e82; /* cyan deep */
218
+ --color-chart-6: #b57414; /* amber deep */
183
219
  }
184
220
 
185
221
  /*
@@ -205,7 +241,7 @@
205
241
  --color-card: var(--card);
206
242
  --color-card-foreground: var(--card-foreground);
207
243
 
208
- /* Brand (black) — used for solid fills, not body text */
244
+ /* Brand (Sage green) — used for solid fills / primary actions, not body text */
209
245
  --color-brand: var(--brand);
210
246
  --color-brand-foreground: var(--brand-foreground);
211
247
  --color-brand-hover: var(--brand-hover);
@@ -247,39 +283,39 @@
247
283
 
248
284
  /* ---- Theme (light only) ---- */
249
285
  :root {
250
- --background: #ffffff;
251
- --foreground: #0a0a0b;
286
+ --background: #f7f7f3;
287
+ --foreground: #1a1a17;
252
288
 
253
- /* Text hierarchy — accessible on #ffffff */
254
- --text-primary: #0a0a0b; /* ~20:1 AAA */
255
- --text-secondary: #52525b; /* ~7.6:1 AAA */
256
- --text-tertiary: #6e6e75; /* ~5.2:1 AA */
289
+ /* Text hierarchy — accessible on the warm greige canvas */
290
+ --text-primary: #1a1a17; /* ~17:1 AAA */
291
+ --text-secondary: #4a4a44; /* ~8.5:1 AAA */
292
+ --text-tertiary: #6b6b63; /* ~4.8:1 AA */
257
293
 
258
- --surface: #f6f6f7;
259
- --surface-foreground: #262629;
294
+ --surface: #f2f2ed; /* warm greige — the tinted canvas */
295
+ --surface-foreground: #33322e;
260
296
  --elevated: #ffffff;
261
297
 
262
- --muted: #f6f6f7;
263
- --muted-foreground: #6e6e75;
298
+ --muted: #e7e7e1;
299
+ --muted-foreground: #6b6b63;
264
300
 
265
301
  --card: #ffffff;
266
- --card-foreground: #0a0a0b;
302
+ --card-foreground: #1a1a17;
267
303
 
268
- /* BLACK brand */
269
- --brand: #0a0a0b;
304
+ /* Sage brand (green) — primary actions, focal moments */
305
+ --brand: #3f7a34;
270
306
  --brand-foreground: #ffffff;
271
- --brand-hover: #262629;
307
+ --brand-hover: #2c5724;
272
308
 
273
309
  /* Quiet fill */
274
- --fill: #ececed;
275
- --fill-foreground: #161618;
310
+ --fill: #e7e7e1;
311
+ --fill-foreground: #1a1a17;
276
312
 
277
- --accent: #f6f6f7;
278
- --accent-foreground: #161618;
313
+ --accent: #edece6;
314
+ --accent-foreground: #1a1a17;
279
315
 
280
- --border: #ececed;
281
- --border-strong: #d9d9dc;
282
- --input: #d9d9dc;
283
- --ring: rgb(10 10 11 / 0.35);
284
- --overlay: rgb(10 10 11 / 0.5);
316
+ --border: #e7e7e1;
317
+ --border-strong: #c7c6be;
318
+ --input: #c7c6be;
319
+ --ring: rgb(63 122 52 / 0.5);
320
+ --overlay: rgb(26 26 23 / 0.5);
285
321
  }