@nadicodeai/design-system 9.2.1 → 10.0.1

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 (63) hide show
  1. package/AGENTS.md +1 -1
  2. package/DESIGN.md +153 -298
  3. package/GLOSSARY.md +4 -4
  4. package/README.md +32 -27
  5. package/dist/assets/app-icon.png +0 -0
  6. package/dist/assets/app-icon.svg +1 -0
  7. package/dist/assets/logo-one-ink-white.png +0 -0
  8. package/dist/assets/logo-one-ink-white.svg +1 -0
  9. package/dist/assets/logo-one-ink.png +0 -0
  10. package/dist/assets/logo-one-ink.svg +1 -0
  11. package/dist/assets/logo-white.png +0 -0
  12. package/dist/assets/logo-white.svg +1 -0
  13. package/dist/assets/logo.png +0 -0
  14. package/dist/assets/logo.svg +1 -0
  15. package/dist/assets/mark-white.png +0 -0
  16. package/dist/assets/mark-white.svg +1 -0
  17. package/dist/assets/mark.png +0 -0
  18. package/dist/assets/mark.svg +1 -0
  19. package/dist/css/host.css +45 -150
  20. package/dist/css/index.css +112 -922
  21. package/dist/css/tokens.css +44 -149
  22. package/dist/favicon/apple-touch-icon.png +0 -0
  23. package/dist/favicon/favicon.ico +0 -0
  24. package/dist/favicon/favicon.svg +1 -7
  25. package/dist/favicon/icon-192.png +0 -0
  26. package/dist/favicon/icon-512.png +0 -0
  27. package/dist/favicon/site.webmanifest +4 -4
  28. package/dist/icons/agent.svg +1 -1
  29. package/dist/icons/approvals.svg +1 -1
  30. package/dist/icons/arch.svg +1 -1
  31. package/dist/tailwind/nadicode.tailwind.json +23 -91
  32. package/dist/tokens/nadicode.dtcg-resolved.json +56 -426
  33. package/dist/tokens/nadicode.dtcg.json +56 -426
  34. package/package.json +43 -83
  35. package/src/assets/logo-geometry.d.ts +10 -7
  36. package/src/assets/logo-geometry.js +119 -172
  37. package/dist/assets/logo-lockup-inverse.png +0 -0
  38. package/dist/assets/logo-lockup-inverse.svg +0 -4
  39. package/dist/assets/logo-lockup-mono.png +0 -0
  40. package/dist/assets/logo-lockup-mono.svg +0 -4
  41. package/dist/assets/logo-lockup-on-verde.png +0 -0
  42. package/dist/assets/logo-lockup-on-verde.svg +0 -4
  43. package/dist/assets/logo-lockup.png +0 -0
  44. package/dist/assets/logo-lockup.svg +0 -4
  45. package/dist/assets/logo-mark-inverse.png +0 -0
  46. package/dist/assets/logo-mark-inverse.svg +0 -6
  47. package/dist/assets/logo-mark-mono.png +0 -0
  48. package/dist/assets/logo-mark-mono.svg +0 -6
  49. package/dist/assets/logo-mark-on-verde.png +0 -0
  50. package/dist/assets/logo-mark-on-verde.svg +0 -6
  51. package/dist/assets/logo-mark.png +0 -0
  52. package/dist/assets/logo-mark.svg +0 -6
  53. package/dist/assets/logo-wordmark-inverse.png +0 -0
  54. package/dist/assets/logo-wordmark-inverse.svg +0 -4
  55. package/dist/assets/logo-wordmark-mono.png +0 -0
  56. package/dist/assets/logo-wordmark-mono.svg +0 -4
  57. package/dist/assets/logo-wordmark-on-verde.png +0 -0
  58. package/dist/assets/logo-wordmark-on-verde.svg +0 -4
  59. package/dist/assets/logo-wordmark.png +0 -0
  60. package/dist/assets/logo-wordmark.svg +0 -4
  61. package/src/assets/logo-mark.d.ts +0 -2
  62. package/src/assets/logo-wordmark.d.ts +0 -2
  63. /package/src/assets/{logo-lockup.d.ts → logo-asset.d.ts} +0 -0
package/DESIGN.md CHANGED
@@ -1,14 +1,13 @@
1
1
  ---
2
2
  version: alpha
3
- name: NadicodeAI Design System
3
+ name: nadicode Design System
4
4
  description: >-
5
- Reusable design-system contract for NadicodeAI: one foundation of colour,
5
+ Reusable design-system contract for nadicode: one foundation of colour,
6
6
  type, spacing, radius, material, elevation, and motion, applied by three
7
7
  registers, the website, the Portal, and the Nadia desktop app, plus the
8
8
  package token, CSS, and asset exports they consume.
9
9
 
10
10
  colors:
11
- primary: "#007a3c"
12
11
  on-primary: "#ffffff"
13
12
  action: "#111111"
14
13
  action-foreground: "#ffffff"
@@ -44,7 +43,6 @@ colors:
44
43
  warning: "#ffc220"
45
44
  warning-soft: "#fff3d1"
46
45
  warning-deep: "#7a5800"
47
- verde-vivo: "#008c45"
48
46
  selection-bg: "#111111"
49
47
  selection-fg: "#fafafa"
50
48
  navigation-selection-bg: "#ededed"
@@ -63,6 +61,7 @@ colors:
63
61
  identity-azzurro: "#0f6ee8"
64
62
  identity-ink: "#000000"
65
63
  identity-white: "#ffffff"
64
+ logo-dot: "#1e3cff"
66
65
  chart-1: "#1e3cff"
67
66
  chart-2: "#00a85a"
68
67
  chart-3: "#ddaa00"
@@ -88,7 +87,6 @@ colors:
88
87
  dark-line: "#262626"
89
88
  dark-input: "#696969"
90
89
  dark-seam: "#1a1a1a"
91
- dark-primary: "#2fd584"
92
90
  dark-on-primary: "#0a0a0a"
93
91
  dark-action: "#ffffff"
94
92
  dark-action-foreground: "#0a0a0a"
@@ -116,13 +114,13 @@ colors:
116
114
  dark-state-review: "#ffc220"
117
115
  dark-state-blocked: "#d3302f"
118
116
  dark-state-complete: "#00a85a"
117
+ dark-logo-dot: "#4d6bff"
119
118
  dark-selection-bg: "#ededed"
120
119
  dark-selection-fg: "#0a0a0a"
121
120
  dark-navigation-selection-bg: "#1a1a1a"
122
121
  dark-navigation-selection-fg: "#ededed"
123
122
  dark-navigation-selection-hover: "#262626"
124
123
  dark-cross: "#555555"
125
- dark-verde-vivo: "#62d494"
126
124
  dark-chart-1: "#4d6bff"
127
125
  dark-chart-2: "#00a85a"
128
126
  dark-chart-3: "#ffd866"
@@ -144,42 +142,36 @@ typography:
144
142
  fontSize: 72px
145
143
  fontWeight: 600
146
144
  lineHeight: 74px
147
- letterSpacing: -1.5px
145
+ letterSpacing: -2.52px
148
146
  display-lg:
149
147
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
150
148
  fontSize: 52px
151
149
  fontWeight: 600
152
150
  lineHeight: 54px
153
- letterSpacing: -1px
151
+ letterSpacing: -1.5px
154
152
  display-md:
155
153
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
156
- fontSize: 24px
157
- fontWeight: 600
158
- lineHeight: 32px
159
- letterSpacing: 0px
160
- display-sm:
161
- fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
162
- fontSize: 20px
154
+ fontSize: 40px
163
155
  fontWeight: 600
164
- lineHeight: 28px
165
- letterSpacing: 0px
166
- title-lg:
156
+ lineHeight: 42px
157
+ letterSpacing: -1px
158
+ heading-lg:
167
159
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
168
- fontSize: 40px
160
+ fontSize: 30px
169
161
  fontWeight: 600
170
- lineHeight: 41px
162
+ lineHeight: 32px
171
163
  letterSpacing: 0px
172
- title-md:
164
+ heading-md:
173
165
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
174
- fontSize: 30px
166
+ fontSize: 24px
175
167
  fontWeight: 600
176
168
  lineHeight: 32px
177
169
  letterSpacing: 0px
178
- title-sm:
170
+ heading-sm:
179
171
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
180
- fontSize: 22px
172
+ fontSize: 20px
181
173
  fontWeight: 600
182
- lineHeight: 25px
174
+ lineHeight: 28px
183
175
  letterSpacing: 0px
184
176
  body-lg:
185
177
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
@@ -193,59 +185,29 @@ typography:
193
185
  fontWeight: 400
194
186
  lineHeight: 25px
195
187
  letterSpacing: 0px
196
- body-md-strong:
197
- fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
198
- fontSize: 16px
199
- fontWeight: 500
200
- lineHeight: 24px
201
- letterSpacing: 0px
202
188
  body-sm:
203
189
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
204
190
  fontSize: 14px
205
191
  fontWeight: 400
206
192
  lineHeight: 20px
207
193
  letterSpacing: 0px
208
- body-sm-strong:
209
- fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
210
- fontSize: 14px
211
- fontWeight: 500
212
- lineHeight: 20px
213
- letterSpacing: 0px
214
194
  caption:
215
195
  fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
216
196
  fontSize: 12px
217
197
  fontWeight: 400
218
198
  lineHeight: 16px
219
199
  letterSpacing: 0px
220
- caption-strong:
221
- fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
222
- fontSize: 12px
223
- fontWeight: 500
224
- lineHeight: 16px
225
- letterSpacing: 0px
226
- caption-mono:
227
- fontFamily: Geist Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, monospace
228
- fontSize: 12px
229
- fontWeight: 500
230
- lineHeight: 16px
231
- letterSpacing: 0px
232
200
  code:
233
201
  fontFamily: Geist Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, monospace
234
202
  fontSize: 13px
235
203
  fontWeight: 400
236
204
  lineHeight: 20px
237
205
  letterSpacing: 0px
238
- button-md:
239
- fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
240
- fontSize: 14px
241
- fontWeight: 500
242
- lineHeight: 18px
243
- letterSpacing: 0px
244
- button-lg:
245
- fontFamily: Geist, Inter, system-ui, -apple-system, sans-serif
246
- fontSize: 16px
206
+ label-mono:
207
+ fontFamily: Geist Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, monospace
208
+ fontSize: 12px
247
209
  fontWeight: 500
248
- lineHeight: 24px
210
+ lineHeight: 16px
249
211
  letterSpacing: 0px
250
212
 
251
213
  rounded:
@@ -286,7 +248,7 @@ spacing:
286
248
 
287
249
  ## Overview
288
250
 
289
- nadicode is an AI agency for SMBs. The design system makes the website and the shipped product read as one family: a calm, high-contrast monochrome architecture in which the identity layer — the Agent orbs, Nadia's striped ribbon among them, and the verde brand accent in its scoped duties — is the expressive element. Structure, chrome, and controls are neutral; a desaturated screen loses nothing structural. The interface helps a pragmatic buyer understand the work: repeated workflows, agents working beside people, inspectable outputs, human approval.
251
+ nadicode is an AI agency for SMBs. The design system makes the website and the shipped product read as one family: a calm, high-contrast monochrome architecture in which the identity layer — the Agent orbs and Nadia's striped ribbon among them — is the expressive element. Structure, chrome, and controls are neutral; a desaturated screen loses nothing structural. The interface helps a pragmatic buyer understand the work: repeated workflows, agents working beside people, inspectable outputs, human approval.
290
252
 
291
253
  One foundation, three registers. The foundation is every `core` value in this contract: the colour roles and their dark pairs, the two type families and the type scale, the spacing ladder and the control geometry, the radius scale, the material values, the four-rung elevation ladder, the motion durations and easings, the icon render, and the brand marks. It is owned by no single product and available to every product; a value stays foundation whether one product renders it or three. A register is one product's way of applying it: its direction, its density, the rungs and steps it reaches for, its dark-mode stance, its structural grammar, its motion budget. The three registers are the website, the Portal, and the Nadia desktop app; `## Registers` names each one's rulebook, its own token families, and its dark-mode stance.
292
254
 
@@ -298,9 +260,9 @@ The settled target state of this contract is [the nadicode design system ADR](..
298
260
 
299
261
  ## Colors
300
262
 
301
- **Target state** ([ADR](../../docs/adr/2026-09-23-the-nadicode-design-system.md), #2266): two greens. `link` #00713d, dark #4ecf8c, is the only green in text; identity verde #00a85a is the only green mark. The brand-mark role of `primary` #007a3c and `verde-vivo` #008c45, with their dark pairs, retire. Neutrals, true black and the identity palette are unchanged.
263
+ **Two greens, and only two.** `{colors.link}` #00713d, dark `{colors.dark-link}` #4ecf8c, is the green that carries text: links, navigational accents, approval states, and the deep and soft steps the `success` pair reuses. Identity verde `{colors.identity-verde}` #00a85a is the green that carries marks. Nothing else in this contract is green. A third green always turned out to be one of those two at a slightly different lightness, which is why `primary` #007a3c and `verde-vivo` #008c45, with their dark pairs `dark-primary` and `dark-verde-vivo`, no longer exist: the logo is ink with a cobalt dot and needs no brand verde, and the presence mark `verde-vivo` painted is retired with the portrait it sat on. The shadcn `primary` role is unaffected and still maps to the neutral action family.
302
264
 
303
- Neutrals carry the interface: grounds, chrome, hierarchy, and every control come from the achromatic spine, and the strongest contrast on any surface lands on the primary action. Chromatic colour appears where it says something a reader already knows how to read: the semantic feedback hues, the verde brand accent in its scoped duties (links, success, presence, identity marks), the chart palettes on data, and the Agent identity palette inside the Agent visuals. Chrome, sections, and controls stay unpainted.
265
+ Neutrals carry the interface: grounds, chrome, hierarchy, and every control come from the achromatic spine, and the strongest contrast on any surface lands on the primary action. Chromatic colour appears where it says something a reader already knows how to read: the semantic feedback hues, the two greens in their scoped duties, the chart palettes on data, and the Agent identity palette inside the Agent visuals. Chrome, sections, and controls stay unpainted.
304
266
 
305
267
  Interaction states are material, not meaning: focus, hover, and selection read as contrast steps on the neutral spine. A hover ground is a neutral step; the only change a control hover carries is a step inside its own family, as `{colors.action-hover}` does on the action control. Cobalto stays on info and running.
306
268
 
@@ -323,8 +285,7 @@ Every value a role needs is carried by the role token itself; there is no numeri
323
285
 
324
286
  ### Semantic roles (functional tier)
325
287
 
326
- - **Action** (`{colors.action}` = ink `#111111`, `{colors.action-foreground}` = white, `{colors.action-hover}`, `{colors.action-active}`): the primary-control family is neutral — a white label on the ink field in light shells, and in dark shells the `dark-action-*` roles, a white control carrying a near-black label. Maximum contrast belongs to the thing that acts. The shadcn `primary` roles map here.
327
- - **Brand accent — verde** (`{colors.primary}` = deep verde `#007a3c`, white `{colors.on-primary}` text on its field): identity marks, logo contexts, and brand accents. Verde's working duties are exactly the scoped roles below — links, success, and Nadia's presence — and the brighter verde `#00a85a` stays the imagery-tier anchor, never carrying normal-size white text.
288
+ - **Action** (`{colors.action}` = ink `#111111`, `{colors.action-foreground}` = white, `{colors.action-hover}`, `{colors.action-active}`): the primary-control family is neutral — a white label on the ink field in light shells, and in dark shells the `dark-action-*` roles, a white control carrying a near-black label. Maximum contrast belongs to the thing that acts. The shadcn `primary` roles map here, and `{colors.on-primary}` / `{colors.dark-on-primary}` are the foregrounds that role carries.
328
289
  - **Canvas / Line / Input / Cross / Seam** (`{colors.canvas}`, `{colors.line}`, `{colors.input}`, `{colors.cross}`, `{colors.seam}`): white content cells, the universal decorative 1 px divider, the resting-control boundary, the stronger mark/crosshair gray, and the dashed connector guide. `{colors.line}` owns ordinary seams and is deliberately quiet — a decorative hairline, not held to a non-text contrast floor. `{colors.input}` owns fields and other interactive boundaries and clears the 3:1 non-text floor against its canvas. A labeled filter or search control already identified by readable text and its icon may use the quiet line role for an optional perimeter, configured by its composition owner. Form fields whose boundary identifies the input keep the input role. Focus remains a separate full-contrast state. Never darken a decorative border locally to make it behave like a control.
329
290
  - **Body / Muted** (`{colors.body}`, `{colors.muted}`): `{colors.body}` is readable supporting copy and clears AA on canvas; `{colors.muted}` is restricted to disabled, inactive, and placeholder content. Do not use muted text for ordinary descriptions, labels, or help text.
330
291
  - **Focus ring** (`{colors.focus-ring}` / `{colors.dark-focus-ring}`): the ink keyboard-focus indicator, a full-opacity `outline` of `{spacing.focus-outline-width}` at `{spacing.focus-outline-offset}` offset, never a box-shadow. Focus is a state of material, so the indicator is the maximum-contrast neutral on each ground rather than a hue; the offset keeps it independent of the resting border, and it clears the 3:1 non-text floor against the adjacent canvas.
@@ -333,7 +294,6 @@ Every value a role needs is carried by the role token itself; there is no numeri
333
294
  - **Navigation selection** (`{colors.navigation-selection-bg}`, `{colors.navigation-selection-fg}`, `{colors.navigation-selection-hover}`): the selected destination in persistent product navigation, a neutral selection field. Selection is a place, not a meaning, so it holds by contrast on the neutral spine; cobalto belongs to the semantic info and running roles only. The dark counterparts are tuned independently for the black console. Resting and hover pairs clear AA for text, and the offset focus outline stays visible against both the selection fill and the adjacent canvas.
334
295
  - **Semantic feedback** (`{colors.success}` = verde with `{colors.success-soft}`/`{colors.success-deep}`; `{colors.error}` = rosso funzionale `#d3302f` with `{colors.error-soft}`/`{colors.error-deep}`; `{colors.warning}` = giallo `#ffc220` with `{colors.warning-soft}`/`{colors.warning-deep}`; `{colors.info}` = cobalto `#1e3cff` with `{colors.info-soft}`/`{colors.info-deep}`): validation, caution, approval, and operational feedback. In each pair the `-soft` value is the tinted tag/banner ground and the `-deep` value is the AA-clearing text on it. On a solid `error` fill (the shadcn `destructive` role) text is white; on a solid `warning` or `success` fill text is ink. The success pair intentionally reuses the link pair's AA-safe verde values; its feedback meaning remains distinct from inline navigation and entity-status semantics.
335
296
  - **Accent** (`{colors.accent}` = arancio `#ff5a1f`): a small emphasis mark for exception flags and highlights; it appears as a mark, carries no text, and paints no fill or background. The identically-named shadcn surface role is a different thing — a neutral (`{colors.canvas-soft-2}` light, `{colors.dark-canvas-soft-2}` dark) with no hue.
336
- - **Verde vivo** (`{colors.verde-vivo}` = `#008c45`, dark `{colors.dark-verde-vivo}` = `#62d494`): Nadia's presence accent. It paints the marks that say *Nadia herself is here*, such as the success beat over her silhouette on Nadia's entry screen. It is a mark colour, never a control fill, a text colour, a link, or a status; `action`, `link`, and `success` keep their jobs. It clears the 3:1 non-text floor on `{colors.canvas}` (4.3:1) and `{colors.dark-canvas}` (11.4:1) and carries no text of its own. White on it is 4.34:1, below AA for normal text, so a label never sits on a `verde-vivo` fill; the ring, halo, or portrait sits on it and the words sit on the canvas.
337
297
  - **Workflow States** (`{colors.state-ready}` neutral, `{colors.state-running}` cobalto, `{colors.state-review}` giallo, `{colors.state-blocked}` rosso, `{colors.state-complete}` verde): the left-border/state vocabulary for agentic work surfaces — cobalto means in motion, verde means done. The dark counterparts (`{colors.dark-state-ready}`, `{colors.dark-state-running}`, `{colors.dark-state-review}`, `{colors.dark-state-blocked}`, `{colors.dark-state-complete}`) keep the same functional identity. These colours are carried by non-text state accents only — meter fills, delta marks, and the brand book's own rails; label text stays on the neutral foreground roles for contrast in both themes. Status is not one of their jobs: every status the product renders is the Badge status vocabulary (see `## Status Vocabulary`), which reads from the semantic feedback and link pairs, and the two token sets never cross.
338
298
 
339
299
  ### Chart categoricals (functional tier)
@@ -347,7 +307,7 @@ Five full-strength hues are the system's expressive chromatic range: verde `{col
347
307
 
348
308
  ### Dark primitives
349
309
 
350
- **Dark Primitives** (`{colors.dark-canvas}`, `{colors.dark-ink}`, `{colors.dark-line}`, and siblings) are the source tokens for the generated shadcn `.dark` role map — mode-bearing primitives, not a separate palette; consuming apps opt in by applying a `.dark` ancestor through their runtime theme provider. `dark-canvas` is true black: the only base plane for dark product shells. The remaining dark neutrals (`dark-canvas-soft`, `dark-canvas-soft-2`, `dark-ink`, `dark-body`, `dark-muted`, `dark-line`, `dark-input`, `dark-seam`, `dark-on-primary`, `dark-scrim`, and the neutral `dark-selection`/`dark-cross` marks) are exact equal-channel grays (OKLCH chroma 0), so no hue paints a surface. The soft canvas steps belong only to bounded interaction states such as hover, selection, keycaps, and inline controls; they never replace true black as a page, section, card, popover, sidebar, or large-panel ground. `dark-input` is the resting-control boundary, held brighter than the decorative `dark-line` border so a field edge clears the non-text floor before `{colors.dark-focus-ring}` appears. The `dark-action` family is a white control stepping through light grays, ink-labelled — the primary control stays neutral on black. Chroma lives only in the intentional accents: `dark-primary` is the brightened brand verde for identity marks, `dark-link`/`dark-link-deep` a lighter verde for links, and `dark-error`/`dark-error-deep`, `dark-warning`/`dark-warning-deep`, `dark-info`/`dark-info-deep`, `dark-success`/`dark-success-deep` the brightened feedback hues, each `*-soft` a deep tinted ground. `dark-navigation-selection-*` and `dark-focus-ring` sit on the neutral spine: selection and focus are material states, not meanings. The `dark-state-*` workflow family is not a text role; it preserves the matching ready/running/review/blocked/complete rail colours for 3 px state accents while text stays on neutral foreground roles. `dark-selection-bg`/`dark-selection-fg` invert the light selection, `dark-scrim` supplies the modal foreground, and `dark-cross` is the visible mark gray, brighter than the decorative `dark-line`. Never reintroduce chroma into the neutral set.
310
+ **Dark Primitives** (`{colors.dark-canvas}`, `{colors.dark-ink}`, `{colors.dark-line}`, and siblings) are the source tokens for the generated shadcn `.dark` role map — mode-bearing primitives, not a separate palette; consuming apps opt in by applying a `.dark` ancestor through their runtime theme provider. `dark-canvas` is true black: the only base plane for dark product shells. The remaining dark neutrals (`dark-canvas-soft`, `dark-canvas-soft-2`, `dark-ink`, `dark-body`, `dark-muted`, `dark-line`, `dark-input`, `dark-seam`, `dark-on-primary`, `dark-scrim`, and the neutral `dark-selection`/`dark-cross` marks) are exact equal-channel grays (OKLCH chroma 0), so no hue paints a surface. The soft canvas steps belong only to bounded interaction states such as hover, selection, keycaps, and inline controls; they never replace true black as a page, section, card, popover, sidebar, or large-panel ground. `dark-input` is the resting-control boundary, held brighter than the decorative `dark-line` border so a field edge clears the non-text floor before `{colors.dark-focus-ring}` appears. The `dark-action` family is a white control stepping through light grays, ink-labelled — the primary control stays neutral on black. Chroma lives only in the intentional accents: `dark-link`/`dark-link-deep` a lighter verde for links, and `dark-error`/`dark-error-deep`, `dark-warning`/`dark-warning-deep`, `dark-info`/`dark-info-deep`, `dark-success`/`dark-success-deep` the brightened feedback hues, each `*-soft` a deep tinted ground. `dark-navigation-selection-*` and `dark-focus-ring` sit on the neutral spine: selection and focus are material states, not meanings. The `dark-state-*` workflow family is not a text role; it preserves the matching ready/running/review/blocked/complete rail colours for 3 px state accents while text stays on neutral foreground roles. `dark-selection-bg`/`dark-selection-fg` invert the light selection, `dark-scrim` supplies the modal foreground, and `dark-cross` is the visible mark gray, brighter than the decorative `dark-line`. Never reintroduce chroma into the neutral set.
351
311
 
352
312
  This achromatic-neutral discipline is not dark-specific: it governs the generated shadcn surface-role bridge in both the light `:root` and dark `.dark` maps. The neutral surface family — `background`, `card`, `popover`, `muted`, `secondary`, `sidebar`, `sidebar-accent`, their neutral foreground/border variants, plus `border` and `input` — resolves only to exact equal-channel grays (OKLCH chroma 0) in both themes. Hue in the role bridge is reserved for the explicit semantic roles `destructive`, `success`, and `warning`; `primary` resolves to the neutral action family. The focus rings (`ring`/`sidebar-ring`) and the `sidebar-primary*` navigation-selection family resolve to equal-channel grays like the surface family: focus and selection are material states, and no interaction state carries a hue. In dark product shells, `background`, `card`, `popover`, and `sidebar` share `{colors.dark-canvas}` so page chrome, cards, tables, overlays, and nav rails read as one console. `muted`, `secondary`, and `accent` retain the `dark-canvas-soft` steps for small interaction states such as hover, selected rows, keycaps, and inline controls. Large product surfaces never take a tinted or soft-panel fill by default.
353
313
 
@@ -411,40 +371,34 @@ is never the only explanation.
411
371
 
412
372
  ## Typography
413
373
 
414
- **Target state** ([ADR](../../docs/adr/2026-09-23-the-nadicode-design-system.md), #2267): Geist and Geist Mono stay the only families. Twelve sizes named by role replace the tokens below: `display-xl` 72/74, `display-lg` 52/54, `display-md` 40/42, `heading-lg` 30/32, `heading-md` 24/32, `heading-sm` 20/28, `body-lg` 18/27, `body-md` 16/25, `body-sm` 14/20, `caption` 12/16, `code` 13/20 and `label-mono` 12/16; strong becomes a weight, not a token. The ADR holds the old-to-new mapping.
415
-
416
- Geist Sans and Geist Mono are the two families. Each token below carries its own — `caption-mono` and `code` are Geist Mono, every other token is Geist Sans — and its Use column says what that token is for. The register law that decides which family a given string takes, and what a card, panel, or section title therefore takes, is stated once in `## Components` → **`label-mono`**.
417
-
418
- All body, title, caption, and code typography uses `letterSpacing: 0px`. Do not use viewport-scaled type (no `vw`/`vh` font sizing): use fixed token sizes stepped by breakpoint-specific rules when a headline must grow. The two large display tiers carry a small negative tracking — `display-xl` at `-1.5px` and `display-lg` at `-1px` — so large type reads optically tight; this is a display-heading-only exception and every non-display tier stays at `0px`.
419
-
420
- | Token | Size | Weight | Line Height | Use |
421
- | ----------------------------- | ---- | ------ | ----------- | -------------------------------------------------------------------------- |
422
- | `{typography.display-xl}` | 72px | 600 | 74px | Hero headline at the widest breakpoint. |
423
- | `{typography.display-lg}` | 52px | 600 | 54px | Compact hero and major landing headlines. |
424
- | `{typography.display-md}` | 24px | 600 | 32px | Compact display tier: action bands, plan names, an entry screen's sentence. |
425
- | `{typography.display-sm}` | 20px | 600 | 28px | Small display tier for inline display headings. |
426
- | `{typography.title-lg}` | 40px | 600 | 41px | Major section headings. |
427
- | `{typography.title-md}` | 30px | 600 | 32px | Standard section title. |
428
- | `{typography.title-sm}` | 22px | 600 | 25px | Cell and artifact titles. |
429
- | `{typography.body-lg}` | 18px | 400 | 27px | Hero/lead paragraphs. |
430
- | `{typography.body-md}` | 16px | 400 | 25px | Default body copy. |
431
- | `{typography.body-md-strong}` | 16px | 500 | 24px | Emphasised body copy at the default size. |
432
- | `{typography.body-sm}` | 14px | 400 | 20px | Secondary copy, nav, compact cell copy. |
433
- | `{typography.body-sm-strong}` | 14px | 500 | 20px | Emphasised secondary copy and compact labels. |
434
- | `{typography.caption}` | 12px | 400 | 16px | Footer secondary lines and badge labels. |
435
- | `{typography.caption-strong}` | 12px | 500 | 16px | Compact emphasised labels on chrome and chips. |
436
- | `{typography.caption-mono}` | 12px | 500 | 16px | Eyebrows, identifiers and code. |
437
- | `{typography.code}` | 13px | 400 | 20px | Tool calls, snippets, traces. |
438
- | `{typography.button-lg}` | 16px | 500 | 24px | Large button labels. |
439
- | `{typography.button-md}` | 14px | 500 | 18px | Standard button and control labels. |
374
+ **Twelve sizes, named by the role they play.** A token says what a string is — a display line, a heading, body copy, a caption, code, a mono label — never how big it is, so a page that changes its mind about size changes one token and not every call site. Strong is a weight, not a token: a 500 run of body copy is `body-md` with `font-weight: 500`, which is why `body-md-strong`, `body-sm-strong`, `caption-strong` and the two `button-*` tokens are gone.
375
+
376
+ Geist Sans and Geist Mono are the two families, and there is no third. Each token below carries its own — `code` and `label-mono` are Geist Mono, every other token is Geist Sans — and its Use column says what that token is for. The register law that decides which family a given string takes, and what a card, panel, or section title therefore takes, is stated once in `## Components` → **`label-mono`**.
377
+
378
+ Do not use viewport-scaled type (no `vw`/`vh` font sizing): use fixed token sizes stepped by breakpoint-specific rules when a headline must grow. Tracking is a display-tier exception and nothing else: the three display sizes tighten from -0.035em at 72px to -0.025em at 40px so large type reads optically tight, and every heading, body, caption, code and label tier stays at `0px`. The contract authors the result in px, because DTCG dimensions are px or rem.
379
+
380
+ | Token | Size | Weight | Line Height | Tracking | Use |
381
+ | --------------------------- | ---- | ------ | ----------- | ---------- | ---------------------------------------------------------------------- |
382
+ | `{typography.display-xl}` | 72px | 600 | 74px | `-2.52px` | Hero headline at the widest breakpoint. |
383
+ | `{typography.display-lg}` | 52px | 600 | 54px | `-1.5px` | Compact hero and major landing headlines. |
384
+ | `{typography.display-md}` | 40px | 600 | 42px | `-1px` | Major section headings and the smallest display line. |
385
+ | `{typography.heading-lg}` | 30px | 600 | 32px | `0px` | Standard section title. |
386
+ | `{typography.heading-md}` | 24px | 600 | 32px | `0px` | Cell, artifact and panel titles; action bands and plan names. |
387
+ | `{typography.heading-sm}` | 20px | 600 | 28px | `0px` | The smallest heading, inline above a group. |
388
+ | `{typography.body-lg}` | 18px | 400 | 27px | `0px` | Hero and lead paragraphs. |
389
+ | `{typography.body-md}` | 16px | 400 | 25px | `0px` | Default body copy, and large control labels at weight 500. |
390
+ | `{typography.body-sm}` | 14px | 400 | 20px | `0px` | Secondary copy, nav, compact cell copy, control labels at weight 500. |
391
+ | `{typography.caption}` | 12px | 400 | 16px | `0px` | Footer secondary lines, badge labels, compact chrome labels. |
392
+ | `{typography.code}` | 13px | 400 | 20px | `0px` | Tool calls, snippets, traces. Geist Mono. |
393
+ | `{typography.label-mono}` | 12px | 500 | 16px | `0px` | Mono eyebrows, identifiers and low-priority metadata. Geist Mono. |
440
394
 
441
395
  ## Layout
442
396
 
443
- **Target state** ([ADR](../../docs/adr/2026-09-23-the-nadicode-design-system.md), #2270): the website parts of this section retire with the website rebuild: the Frame System, Seam Ownership, Delivery Tiers, the Page Grammar with its markers, side-fills, guide strips, hatched rails and dotted field, and the `register.website.layout` tokens. The website keeps one content max-width with side gutters that grow from phone to desktop, full-bleed white, black and colour sections, and one spacing step between them. Spacing, responsive rules and control geometry that the products use stay. The Portal's dotted workspace belongs to the material ladder and is unaffected.
397
+ Layout is mathematical and single-owner in every register: one owner per visible line, every gap from the tokens below, never eyeballed.
444
398
 
445
- Layout is mathematical and single-owner in every register: one owner per visible line, every gap and height from the tokens below, never eyeballed.
399
+ The website's structure is three kinds of full-bleed section and nothing else: white for reading, black for a scene, and at most one flat, full-strength colour section per page for its single statement. Every section runs edge to edge and centres its content in the one column, `.nc-page`: a single content max-width with side gutters that grow from phone to desktop. Inside a section the content is a single column or a picture-and-text split, and between two sections there is one spacing step. Nothing else on the website sets a page width, and a section adds no frame, no row grid and no gutter texture.
446
400
 
447
- The page grammar this section documents, the 12-column frame with its rows, its centered 10-column active lane, its stage and its markers, is the website register's structure. The Portal and the Nadia desktop app build their own structure by their rulebooks (`## Registers`) and reach for the spacing ladder and the control geometry, not for the frame.
401
+ The Portal and the Nadia desktop app build their own structure by their rulebooks (`## Registers`) and reach for the spacing ladder and the control geometry, not for the website's column.
448
402
 
449
403
  ### Spacing
450
404
 
@@ -468,7 +422,7 @@ Control geometry is foundation, so a control has the same physical size and the
468
422
 
469
423
  | Token | Value | Owns |
470
424
  | --- | --- | --- |
471
- | `{spacing.guide}` | 1 px | Every ordinary seam, and the crosshair weight |
425
+ | `{spacing.guide}` | 1 px | Every ordinary seam |
472
426
  | `{spacing.icon-stroke}` | 1.5 px | The house icon line, on screen, at 16, 20 and 24 px |
473
427
  | `{spacing.icon-stroke-sm}` | 1.25 px | The same line at 12 px |
474
428
  | `{spacing.focus-outline-width}` | 2 px | The one keyboard focus outline |
@@ -477,109 +431,29 @@ Control geometry is foundation, so a control has the same physical size and the
477
431
  | `{spacing.control-padding-inline}` | 10 px | Inline padding of a control |
478
432
  | `{spacing.touch-target}` | 44 px | Minimum target on coarse or non-hover input |
479
433
 
480
- Page-grammar geometry and the fixed component heights belong to the website register and are authored in `register.website.layout`; the filter-chip sizes belong to the Portal register and are authored in `register.portal.density`. Both emit today's `--nc-*` names, and the values live in the one `json design-tokens` fence ("CSS Architecture & Token Pipeline").
434
+ The website's column and its section rhythm belong to the website register and are authored in `register.website.layout`; the filter-chip sizes belong to the Portal register and are authored in `register.portal.density`. Both emit today's `--nc-*` names, and the values live in the one `json design-tokens` fence ("CSS Architecture & Token Pipeline"). Six tokens are the whole of the website's geometry; a page that needs a seventh is asking for a section kind the register does not have.
481
435
 
482
- | Family | Tokens | Register |
436
+ | Token | Owns | Register |
483
437
  | --- | --- | --- |
484
- | Band rhythm | `section` | Website |
485
- | Page shell | `page-pad`, `page-max-mobile`, `page-max-tablet`, `page-max-desktop` | Website |
486
- | Frame | `frame-max`, `frame-gap`, `cell-padding`, `section-gap` | Website |
487
- | Crosshair | `cross-size-mobile`, `cross-size-tablet`, `cross-size-desktop` | Website |
488
- | Cell and strip heights | `h-cell-sm`, `h-cell-md-*`, `h-cell-lg-*`, `h-strip-*` | Website |
489
- | Media and stage heights | `h-visual-*`, `h-precision-*`, `h-hero-*` | Website |
490
- | Band and feature heights | `h-action-strip-*`, `h-action-body-*`, `h-action-band`, `h-feature-*`, `h-editorial-feature-*` | Website |
491
- | Bento heights | `h-bento-primary`, `h-bento-secondary`, `h-bento-mobile-copy`, `h-bento-mobile-main`, `h-bento-strip` | Website |
492
- | Filter chip | `filter-chip-gap`, `filter-chip-height`, `filter-chip-icon` | Portal |
493
-
494
- ### Frame System
495
-
496
- **Target state** (#2270): retires from the website with the rebuild; see `## Layout`.
497
-
498
- - `page-system` adds `--nc-page-pad` horizontal page padding and `{spacing.4xl}` vertical padding, so the frame floats on clear canvas between the sticky nav chrome and the footer instead of stacking against their hairlines.
499
- - `page-frame` is centered and capped at `--nc-frame-max`. It owns the outer left/right/top borders.
500
- - `page-row` is the only direct section unit inside `page-frame`. It owns the bottom border for each row.
501
- - Desktop rows use 12 equal columns with `--nc-frame-gap` gutters.
502
- - `active-lane` spans columns 2 through 11 on desktop and all columns on smaller viewports.
503
- - Full-lane rows span all 12 columns only when the content owns columns 1 and 12 explicitly through the `stage` geometry (`nc-stage-frame`/`nc-stage-fill`/`nc-stage-grid`/`nc-stage-active`).
504
- - Columns 1 and 12 are the page's quiet margin: empty canvas by default. Their presence is the whitespace itself — the grid sizes them whether or not anything paints there, matching the reference container model where structure lives inside bounded modules and never as page-length gutter texture. The diagonal `side-fill` is a scarce structural accent a row opts into with `data-rails="hatch"`; reserve it for the page's anchor rows (the opening hero, the closing conversion row), never as a default on every row. When present, the fill is bounded on both sides — the frame perimeter outside, the fill's own inner rail inside — so the texture reads as a deliberate panel, not an unbounded wash.
505
-
506
- ### Seam Ownership
507
-
508
- **Target state** (#2270): retires from the website with the rebuild; see `## Layout`.
509
-
510
- Every visible line has one owner. Do not draw borders on both adjacent children at the same coordinate. Use a line-colored parent background plus `{spacing.guide}` gaps, or use a single explicit border owned by the parent row/component.
511
-
512
- On a `blueprint-field` surface the model is draw-all-then-occlude: the texture layer owns every line uniformly and opaque content (`cell-solid` or a content slot) occludes the lines it covers. This is distinct from the parent-plus-gap model used by `cell-grid`; both keep exactly one owner per visible line.
513
-
514
- The universal guide weight is `{spacing.guide}`. Crosshairs use the same physical weight as the line they mark; they are darker through `{colors.cross}`, not thicker.
438
+ | `content-max` | 1200 px: the widest the reading column ever grows | Website |
439
+ | `gutter-mobile` | 20 px: the side gutter beside that column below 600 px | Website |
440
+ | `gutter-tablet` | 40 px: the same gutter from 600 px | Website |
441
+ | `gutter-desktop` | 64 px: the same gutter from 960 px | Website |
442
+ | `section-step-mobile` | 64 px: the one gap between two sections below 960 px | Website |
443
+ | `section-step-desktop` | 96 px: the same gap from 960 px | Website |
444
+ | `filter-chip-gap`, `filter-chip-height`, `filter-chip-icon` | The Portal's filter chip | Portal |
515
445
 
516
446
  ### Responsive Rules
517
447
 
518
- Mobile collapses the 12-column frame to a single content column while preserving the outer frame. Tablet may use 2 or 3 internal tracks for repeated cell grids. Desktop restores the 12-column frame and centered 10-column active lane.
519
-
520
- Height tokens are fixed by component family and breakpoint. Components may grow when real content requires it, but the default specimen height comes from the exported height tokens so repeated rows align predictably.
521
-
522
- Page shells must prevent horizontal scrollbars when a mathematically aligned full-bleed surface uses viewport width. Clip horizontal overflow at the shell/root level; do not hide overflow on structural cells to mask bad seam math.
523
-
524
- Compositions arrange approved primitives for a content role. They must not redefine page-frame math, row seams, marker placement, side-fill ownership, active-lane spans, or global guide primitives. If a composition needs new structural geometry, promote that geometry into the page grammar or a real component first.
525
-
526
- ### Delivery Tiers
527
-
528
- **Target state** (#2270): retires from the website with the rebuild; see `## Layout`.
529
-
530
- Each reusable concern has one home and one authoring surface, so a member is documented and shipped in exactly one place:
531
-
532
- - **Tokens** — exact reusable values live in this contract's `colors`, `spacing`, `rounded`, and `typography` front matter, and in the one `json design-tokens` fence for material, elevation, motion, and the register groups; they export to the generated token layer. No component or section may re-encode a token value.
533
- - **Framework-agnostic CSS components** — the brand/display CSS components that remain in this package (agent-work artifacts, `image`, and the small label/badge/link metadata components). Their names and semantics live once in `## Components`; design-system CSS realises that catalog without a second recipe or component-token hierarchy.
534
- - **Reusable compositions and sections** — patterns that arrange the page grammar and lower tiers for a content role. They ship as React shadcn compositions from `@nadicodeai/ui`, composed over the kept page grammar; they are not a pure-CSS section layer and do not add a second component catalog here.
535
- - **Layout grammar** — the page-frame, row, lane, seam, stage, and marker system documented across this `## Layout` section, carried by the layout CSS layer.
536
- - **Specimens** — static demonstrations of already-defined primitives. They invent no geometry, seams, heights, or responsive behavior; they only assemble shipped tiers for inspection.
537
-
538
- The page grammar below is the `## Layout` home for the structural names; the section-level compositions that arrange them ship from `@nadicodeai/ui`.
539
-
540
- ### Page Grammar
541
-
542
- **Target state** (#2270): retires from the website with the rebuild; see `## Layout`.
543
-
544
- Page grammar is the NadicodeAI structural system. It defines how pages are built before any content surface appears. These names live in this layout section, not in the component catalog. Exact reusable values are exported through `colors`, `spacing`, `rounded`, and `typography`; the structural semantics live here as layout doctrine, not as interface components.
545
-
546
- **`page-frame`** (`nc-page-frame`) — centered bordered frame capped by `--nc-frame-max`. Owns the outer left/right/top seams.
448
+ The website has two breakpoints, 600 px and 960 px. They live in the application CSS layer, never in the generated token layer: `foundation.css` collapses `gutter-*` into `--nc-gutter` and `section-step-*` into `--nc-section-step` at those widths, so a section reads one name and resolves the value for the viewport it is on. No register adds a third breakpoint to this column.
547
449
 
548
- **`page-row`** (`nc-page-row`) — one continuous section row inside `page-frame`. Owns the bottom seam and default row background.
450
+ Page shells must prevent horizontal scrollbars when a full-bleed section uses viewport width. Clip horizontal overflow at the shell or root level; do not hide overflow on an inner surface to mask bad math.
549
451
 
550
- **`active-lane`** — the centered 10-column lane. On desktop it spans columns 2 / 12; on smaller viewports it spans the row.
551
-
552
- **`side-fill`** (`nc-side-fill`) — the opt-in owner for columns 1 and 12 when a row uses the active lane. Hidden by default (the quiet-gutter doctrine: the empty margin column is the default treatment); a row enables it with `data-rails="hatch"` on the `page-row`, reserved for the page's anchor rows. When shown, it paints the diagonal filler, expands into the frame gutter so the texture touches the active content edge, and owns its single inner boundary rail (`{colors.line}` on its lane-facing edge) so the hatch is bounded on both sides — frame perimeter outside, fill rail inside.
553
-
554
- **`cell-grid`** (`nc-row-grid`) — repeated cell math. Uses `{colors.line}` parent background and `{spacing.guide}` gaps so the seam has one owner. Supports 2, 3, 4, 5, and 6 column desktop variants with responsive collapse.
555
-
556
- **`panel-cell`** — a square structural content cell inside `cell-grid`. Uses `--nc-cell-padding` and no local border.
557
-
558
- **`marker`** — sparse crosshair marker for major section/frame corners. It marks intersections; it is not decorative chrome for every card. It is placed on a grid-field track intersection and derives its position from the grid, not from a frame-relative offset.
559
-
560
- **`diagonal-fill`** — structural filler for outer columns and empty side lanes.
561
-
562
- **`dotted-field`** — artifact/canvas background for conversations, outputs, and diagrams. It indicates a work surface, not generic decoration.
563
-
564
- **`blueprint-field`** — a feathered structural-pitch background texture that visualizes a grid as graph-paper rather than as load-bearing DOM. Column and row pitch track the meaningful column count so it stays legible; the field is always feathered so it reads as texture, not as paper. On the blueprint surface the lines are drawn once underneath and occluded by opaque content (`cell-solid` or the content slot) — the draw-all-then-occlude model. It is incidental brand texture: if the blueprint field is the first thing you notice, it is too strong.
565
-
566
- **`rule-svg`** — bounded dashed connector rule. Uses `{colors.seam}` and never overlaps a cell seam.
567
-
568
- Reusable section compositions ship as React components from `@nadicodeai/ui`, composed over the page grammar above; the page grammar is the framework-agnostic foundation they assemble.
569
-
570
- ### Related Layout Helpers
571
-
572
- This live helper is part of the kept page-grammar layout layer. It supports index-style page assemblies rather than naming an interface component.
573
-
574
- **`index-grid`** — layout helper for index-style page assemblies (blog, learn, and case-study rows whose guide layer owns the dividers).
575
-
576
- `index-card` is the repeated item class inside `index-grid`, not a standalone contract key.
452
+ Compositions arrange approved primitives for a content role. They do not redefine the content max-width, the gutters, or the step between sections. If a composition needs structural geometry this section does not name, promote that geometry here first.
577
453
 
578
454
  ## Elevation & Depth
579
455
 
580
- **Target state** ([ADR](../../docs/adr/2026-09-23-the-nadicode-design-system.md), #2268): in dark mode the four rungs step their ring, 12, 16, 22 and 28% white, in place of one 18% ring on every rung. Light mode and the shadows are unchanged.
581
-
582
- Product UI uses one shared material ladder. The workspace is one continuous ground (`{colors.material-workspace}` / `{colors.dark-material-workspace}`); every solid object uses the same fill (`{colors.material-solid}` / `{colors.dark-material-solid}`). In dark mode, both are true black. The workspace remains legible because it carries the existing `dotted-field` texture and solid objects occlude it. Elevation never means progressively greyer cards. Physical distance changes only the falloff of the rung, and the four rungs are the four owned utilities: `nc-elevation-resting` for resting content, then `nc-elevation-raised`, `nc-elevation-floating`, and `nc-elevation-modal` when an object is physically raised, floating, or modal. Each rung is authored per mode, light and dark, and carries its own ring as the recipe's final layer, so an elevated root never adds a border, a second ring, a local ring tint, or an opacity override. Product code chooses only the semantic role.
456
+ Product UI uses one shared material ladder. The workspace is one continuous ground (`{colors.material-workspace}` / `{colors.dark-material-workspace}`); every solid object uses the same fill (`{colors.material-solid}` / `{colors.dark-material-solid}`). In dark mode, both are true black. The workspace remains legible because it carries the existing `dotted-field` texture and solid objects occlude it. Elevation never means progressively greyer cards. Physical distance changes only the falloff of the rung, and the four rungs are the four owned utilities: `nc-elevation-resting` for resting content, then `nc-elevation-raised`, `nc-elevation-floating`, and `nc-elevation-modal` when an object is physically raised, floating, or modal. Each rung is authored per mode, light and dark, and carries its own ring as the recipe's final layer, so an elevated root never adds a border, a second ring, a local ring tint, or an opacity override. In light mode every rung's ring is black at 5%, because the shadow carries the height. On true black the shadows vanish, so the dark ring carries it instead and steps with the rung: resting 12%, raised 16%, floating 22%, modal 28% white. The falloff layers are identical in both modes; only the ring differs. Product code chooses only the semantic role.
583
457
 
584
458
  Glass is a material variant, not an elevation step. Floating chrome may be glass: menus, popovers, selects, comboboxes, the command palette, and bars, the surfaces that stand over the room and show it through themselves. Content surfaces stay solid: cards, KPIs, tables, inspectors, and dialog, sheet, and drawer bodies, because glass floats but never carries content. Inside the window, glass composites only over our own content; a product whose window the operating system composites states its window material in its register. Glass keeps exactly one separator, the integrated edge ring of its elevation falloff, never ring plus shadow plus border stacked. Reduced-transparency mode replaces glass with the same solid material at the same elevation.
585
459
 
@@ -609,34 +483,44 @@ On coarse or non-hover input capability, every interactive target is at least 44
609
483
  One radius rounds every surface. The reference is the search field: `{rounded.md}` (6 px). A surface never earns a larger corner by sitting higher on the elevation ladder, and no register may reintroduce a second surface radius.
610
484
 
611
485
  - Use `{rounded.md}` (6 px) for every surface and every control: resting, raised, outlined, floating, and modal surfaces; cards, KPI cards, chart cards, tables, menus, selects, popovers, dropdowns, dialogs, sheets, drawers, takeovers, tooltips, toasts, inspectors; and buttons, inputs, chips, status tags, and small artifacts.
612
- - Use `{rounded.none}` for structural page rows, frame edges, grid cells, and structural modules.
486
+ - Use `{rounded.none}` for full-bleed sections, structural cells, and structural modules.
613
487
  - Use `{rounded.full}` only for intrinsically circular identity/avatar chrome and compact control marks whose geometry is inherently round or pill-shaped. It never turns a content surface, button, badge, or navigation item into a pill by default.
614
488
 
615
489
  The radius scale itself is unchanged: `{rounded.xs}` through `{rounded.3xl}` stay exported for inner marks and for geometry a component owns, such as a progress track or a bar end. Only the rung that surfaces take is fixed.
616
490
 
617
- Public page-grammar cells are not cards and remain square because their parent frame supplies the visual system. Product cards use the resting material role. A details composition may divide its header and bound individual facts with the outlined role; each edge has one owner and fact cells add no elevation. Grouping does not justify another ring around the same container or a stronger shadow.
491
+ Structural cells on the website are not cards and remain square, because the section around them supplies the visual system. Product cards use the resting material role. A details composition may divide its header and bound individual facts with the outlined role; each edge has one owner and fact cells add no elevation. Grouping does not justify another ring around the same container or a stronger shadow.
618
492
 
619
493
  ## Motion
620
494
 
621
- **Target state** ([ADR](../../docs/adr/2026-09-23-the-nadicode-design-system.md), #2264, #2265, #2260): only Agents move on their own; the interface answers a person's action briefly and stops. `pulse` becomes `wait`, 1.6 s, for the waiting line only; `typing`, `shimmer` and `spin` retire. One default transition in every product: `confirm`, out-strong. Reduced motion is one policy: movement off, opacity and colour kept, a thinking or working orb breathes in opacity. Opening an Agent is a view transition in which the orb travels from the row into the page, on `transition` with in-out-strong. An idle orb holds still.
495
+ **One motion law.** Only Agents move on their own. The interface answers a person's action, briefly, and stops: motion follows the action, stays under 300 ms, grows from the element that was pressed, and settles. While a person waits for something they asked for, one waiting line moves and nothing else.
496
+
497
+ Only Agents move on their own. An idle orb holds still, and under reduced
498
+ motion a thinking or working orb breathes in opacity, 1.6 s ease-in-out from
499
+ full ink to 0.45, so busy still reads as busy while every other orb is still
500
+ ([agent orbs](../ui/docs/agent-orb.md)). Opening an Agent in the Portal is the
501
+ one view transition: the orb travels from the list row into the Agent's page
502
+ on `transition` with `in-out-strong`, over a root crossfade on `close`, and
503
+ reduced motion navigates instantly; the Portal's `portal.css` owns those rules
504
+ because that one navigation is the only place the product moves a page.
622
505
 
623
- Motion reports state: things move when something changes, briefly, and settle. Continuous, ambient motion is scarce and purposeful — the Agent identity visuals are its natural carrier — and repeated interactions stay fast and quiet. Motion preserves meaning when movement is reduced.
506
+ **One default transition in every product**: `confirm`, 180 ms, `out-strong`. It is set once, on `--default-transition-duration` and `--default-transition-timing-function` in `src/css/motion.css`, so a bounded Tailwind transition utility inherits the cadence and no product declares a default of its own.
624
507
 
625
- The durations and easings live in `core.motion` of the authored token extension ("CSS Architecture & Token Pipeline"). The build emits every duration as `--nc-duration-*` and every easing as `--nc-ease-*`. `src/css/motion.css` owns the keyframes, the `nc-anim-*` and `nc-transition-*` classes, and the reduced-motion law, and references those names; it declares no duration, easing, or alias of its own. A register decides where motion appears; it never picks a duration outside this list.
508
+ **One reduced-motion policy**, stated once in `motion.css` and repeated nowhere: under `prefers-reduced-motion: reduce`, movement stops and paint continues. Transforms and keyframed movement go to their end state; opacity and colour transitions are kept, because a fade or a colour change is the state change itself rather than decoration around it, and an Agent that is thinking or working still has to read as busy. A component never answers reduced motion by turning every transition off.
509
+
510
+ The durations and easings live in `core.motion` of the authored token extension ("CSS Architecture & Token Pipeline"). The build emits every duration as `--nc-duration-*` and every easing as `--nc-ease-*`. `src/css/motion.css` owns the keyframes, the `nc-anim-*` and `nc-transition-*` classes, and the reduced-motion law, and references those names; it declares no duration, easing, or alias of its own. A register decides where motion appears; it never picks a duration outside this list, and `tests/guards/motion-durations.test.ts` fails on a time literal anywhere in the motion layer.
626
511
 
627
512
  | Token | Use |
628
513
  | --- | --- |
629
- | `control` | Hover and press on a control in a dense register |
630
- | `confirm` | Confirmation feedback and instant state flips |
631
- | `close` / `open` | Floating chrome and overlays leaving and arriving |
632
- | `transition` | Property transitions on a working surface |
633
- | `msg-in` | A transcript message arriving |
634
- | `drawer` / `drawer-swipe` | Sheets and drawers, programmatic and pointer-driven |
635
- | `stagger` | The step between siblings in one arrival |
636
- | `spin` | One revolution of a busy spinner |
637
- | `typing` / `pulse` / `shimmer` | Ambient loops; each has a reduced-motion fallback in `motion.css` |
638
-
639
- The easings are `emphasized`, `out-strong`, `in-out-strong`, and `drawer`.
514
+ | `stagger` 60 ms | The step between siblings in one arrival |
515
+ | `control` 100 ms | Hover and press on a control in a dense register |
516
+ | `close` 140 ms / `open` 220 ms | Floating chrome and overlays leaving and arriving |
517
+ | `confirm` 180 ms | Confirmation feedback, instant state flips, and the default transition |
518
+ | `msg-in` 240 ms | A transcript message arriving |
519
+ | `transition` 240 ms | Property transitions on a working surface, and the Agent view transition |
520
+ | `drawer-swipe` 400 ms / `drawer` 450 ms | Sheets and drawers, pointer-driven and programmatic |
521
+ | `wait` 1.6 s | The one waiting line, and nothing else |
522
+
523
+ The easings are `emphasized`, `out-strong`, `in-out-strong`, and `drawer`. Every remaining duration has a job these rules allow, which is why `spin`, `typing` and `shimmer` retired with the spinner, the typewriter line and the three shimmer systems: a busy spinner is not an Agent, and ambient decoration is not an answer to anything a person did.
640
524
 
641
525
  ## Registers
642
526
 
@@ -646,23 +530,29 @@ A register adds rules. It never redefines a foundation value. A value one regist
646
530
 
647
531
  | Register | Rulebook | Its own token families | Dark mode |
648
532
  | --- | --- | --- | --- |
649
- | Website | `apps/website/docs/design-doctrine.md` over `## Layout` here | `register.website.layout`: `section`, `page-*`, `frame-*`, `cell-padding`, `section-gap`, `cross-size-*`, every `h-*` height. It speaks `display-xl`, `display-lg`, and `title-lg` | Light only |
533
+ | Website | `apps/website/docs/design-doctrine.md` over `## Layout` here | `register.website.layout`: `content-max`, `gutter-*`, `section-step-*`. It speaks `display-xl`, `display-lg`, and `title-lg` | Light only |
650
534
  | Portal | `apps/portal/docs/information-architecture.md` for direction; `apps/portal/docs/design-doctrine.md` for implementation | `register.portal.density`: `filter-chip-gap`, `filter-chip-height`, `filter-chip-icon` | System, light, dark, on the `.dark` class |
651
535
  | Nadia desktop app | `apps/nadia/docs/design-doctrine.md` | `register.nadia.*`: colour-role and radius aliases; six deprecated elevation recipes and the legacy overlay border remain as compatibility data. New surfaces consume the foundation's material roles | Follows the operating system, on the `.dark` class |
652
536
 
653
- **Target state** ([ADR](../../docs/adr/2026-09-23-the-nadicode-design-system.md), #2270): the website is rebuilt from three kinds of full-bleed section only: white for reading, black for a scene with a marble figure and the live orb, and at most one flat, full-strength colour section per page for its single statement. The 12-column page grammar and the `register.website.layout` family retire from the website entirely; one content max-width with growing side gutters, a single column or a picture-and-text split inside a section, and one spacing step between sections replace them. The rows below describe the current draft until that rebuild.
537
+ The website is built from three kinds of full-bleed section and nothing else: white for reading, black for a scene with a marble figure and the live orb, and at most one flat, full-strength colour section per page for its single statement, in the hue of that page's picture. One content max-width with growing side gutters centres the reading content, a section holds a single column or a picture-and-text split, and one spacing step separates two sections (`## Layout`).
654
538
 
655
- The website's depth is flat and architectural: hairlines, contrast, dotted fields, diagonal fills, and dark action bands make hierarchy, and the page frame and row grid stay dominant. It uses no rung of the material ladder and no floating card stack as page structure. Marketing bands may use `register.website.layout.section`, emitted as `--nc-section`, when they need the broader inherited hero rhythm. The website uses no negative letter spacing outside the two display tiers.
539
+ The website's depth is flat and architectural: the three section grounds, contrast, large Geist headlines and the approved pictures make hierarchy. It has no page frame, no row grid, no diagonal fill and no dotted field; the dotted field is the Portal's work surface. It uses no rung of the material ladder and no floating card stack as page structure, and no negative letter spacing outside the two display tiers.
656
540
 
657
541
  The Portal and the desktop are product shells on the material ladder above, each by its rulebook. The desktop's window material is the operating system's where the OS composites the window; inside the window the foundation's glass law holds.
658
542
 
659
543
  ## Components
660
544
 
661
- This section is the semantic catalog for framework-agnostic components. Exact reusable values stay in the token maps above; CSS owns component composition. Page grammar and composition semantics live in `## Layout`, while React components and reusable sections live in `@nadicodeai/ui`.
545
+ This section is the semantic catalog for framework-agnostic components. Exact reusable values stay in the token maps above; CSS owns component composition. The section grammar and the content column live in `## Layout`, while React components and reusable sections live in `@nadicodeai/ui`.
546
+
547
+ ### Structural Surfaces
548
+
549
+ One surface carries structure rather than content. It is square (`{rounded.none}`), takes no rung of the elevation ladder, and draws no border of its own.
550
+
551
+ **`dotted-field`** — the Portal's work surface: the dotted ground behind conversations, outputs, and diagrams, on the material ladder's workspace role ("Elevation & Depth"). It indicates a work surface, never decoration, and the website does not use it.
662
552
 
663
553
  ### Interface Atoms
664
554
 
665
- Interface CSS components provide small metadata, label, and link chrome. They inherit NadicodeAI tokens and stay subordinate to the page grammar; they must not create independent layout systems or repair structural seams. The generic interactive controls (navigation, buttons, inputs, footer chrome, the marketing card/code/pricing/proof surfaces, the standard sections) ship as React shadcn components from `@nadicodeai/ui`; this contract keeps only the brand/display CSS components and primitives that belong in the framework-agnostic package.
555
+ Interface CSS components provide small metadata, label, and link chrome. They inherit nadicode tokens and stay subordinate to the layout; they must not create independent layout systems or repair structural seams. The generic interactive controls (navigation, buttons, inputs, footer chrome, the marketing card/code/pricing/proof surfaces, the standard sections) ship as React shadcn components from `@nadicodeai/ui`; this contract keeps only the brand/display CSS components and primitives that belong in the framework-agnostic package.
666
556
 
667
557
  Keyboard focus has one owner: the kit's foundation layer draws the full-opacity `{spacing.focus-outline-width}` `{colors.focus-ring}` / `{colors.dark-focus-ring}` outline at `{spacing.focus-outline-offset}` offset on every `:focus-visible` element. A component never repaints focus with its own ring, hue, or width, and never removes the outline without a contrast-safe equivalent.
668
558
 
@@ -676,13 +566,13 @@ Keyboard focus has one owner: the kit's foundation layer draws the full-opacity
676
566
 
677
567
  ### Content Surfaces
678
568
 
679
- Content surfaces display specific content types inside the grammar. They are valid when the content exists; they do not replace the page frame, row model, or seam rules.
569
+ Content surfaces display specific content types inside a section. They are valid when the content exists; they do not replace the section, the content column, or the seam rules.
680
570
 
681
- **`image`** — editorial media mount, the single home for art-directed imagery inside the grammar. A bounded media fill (img, picture, video, or inline SVG) with an optional mono tag chip overlaid on the media and an optional body-tone caption bar below it, both typed by the generated caption utilities applied in markup. Square-cornered (`{rounded.none}`): a mounted print, not a floating card — the parent frame supplies the visual system. With `data-frame="true"` it draws exactly one hairline (its own `{colors.line}` border), so it sits inside a borderless `panel-cell` or a visual slot without repairing or stacking onto any grid seam. The aspect ratio is the per-instance `--nc-image-ratio` knob (default 4 / 3) — set the prop on the element, never re-derive geometry inline. Variant: `data-strip="true"` is the full-width divider form for thin editorial bands between page rows (thread, texture, panorama) — the ratio unlocks and the height resolves from the responsive `--nc-h-strip-*` tokens through the layout-layer collapsed alias. Use `image` for every illustrated image, photographic surface, or image divider. Conversation artifacts render through assistant-ui rather than a design-system CSS family.
571
+ **`image`** — editorial media mount, the single home for art-directed imagery inside a section. A bounded media fill (img, picture, video, or inline SVG) with an optional mono tag chip overlaid on the media and an optional body-tone caption bar below it, both typed by the generated caption utilities applied in markup. Square-cornered (`{rounded.none}`): a mounted print, not a floating card — the parent frame supplies the visual system. With `data-frame="true"` it draws exactly one hairline (its own `{colors.line}` border), so it sits inside a borderless cell or a visual slot without repairing or stacking onto any grid seam. The aspect ratio is the per-instance `--nc-image-ratio` knob (default 4 / 3) — set the prop on the element, never re-derive geometry inline. It has one form: there is no divider-strip variant. Use `image` for every illustrated image and every photographic surface. Conversation artifacts render through assistant-ui rather than a design-system CSS family.
682
572
 
683
573
  ### Agentic Work Surfaces
684
574
 
685
- Agentic work surfaces are the NadicodeAI-specific display layer outside the conversation renderer. They show concrete work output or workflow state such as memory, state, run progress, identity, and handoffs. Conversation messages, tools, artifacts, approvals, and pending indicators belong to assistant-ui.
575
+ Agentic work surfaces are the nadicode-specific display layer outside the conversation renderer. They show concrete work output or workflow state such as memory, state, run progress, identity, and handoffs. Conversation messages, tools, artifacts, approvals, and pending indicators belong to assistant-ui.
686
576
 
687
577
  Agent identity is decorative product identity, not a work artifact or a status signal: the Agent orbs, owned by [`packages/ui/docs/agent-orb.md`](../ui/docs/agent-orb.md).
688
578
 
@@ -719,32 +609,41 @@ whole map in one module.
719
609
 
720
610
  ## System activity
721
611
 
722
- **Target state** ([ADR](../../docs/adr/2026-09-23-the-nadicode-design-system.md), #2261, #2262, #2263): who does the work decides how it looks. An Agent's work shows that Agent's orb in its state and a sentence; Nadia connecting is Nadia's ribbon, working. System work under about a second is a still pending label on the control; beyond a second one 2 px ink waiting line moves linearly on `wait`; measurable work is a progress line with a written count; page loads show still placeholders. `ActivityOrb`, the spinners and the shimmer text retire.
723
-
724
- System activity, measured progress and settled status share one visual
725
- language. Activity is transient: it communicates ongoing work, then gives way
726
- to content or outcome feedback. The activity family uses neutral ink for its
727
- structure and the cobalto info role for moving foreground particles; success,
728
- warning and error keep their meanings. Measurable completion uses progress,
729
- and waiting for a person or a failed operation uses the feedback treatment.
730
-
731
- The renderer is `ActivityOrb` in `@nadicodeai/ui`, with three activities:
732
- loading, connecting and processing. Its motion recipes, operation mapping,
733
- sizes and runtime behaviour are owned by
734
- [`packages/ui/docs/activity.md`](../ui/docs/activity.md); a consuming app
735
- chooses the activity and supplies its readable meaning.
612
+ Who does the work decides how it looks. An Agent's work shows that Agent's orb
613
+ in its state and a sentence saying what it is doing; Nadia connecting is
614
+ Nadia's ribbon, working. Everything below is the system's own work, and an orb
615
+ means an Agent, so no system wait takes one.
616
+
617
+ System work under about a second is a still pending label on the control: its
618
+ own word changes, it holds its place and its icon, and it carries `disabled`
619
+ and `aria-busy`. Past a second, one 2 px waiting line of ink moves beside the
620
+ work, a 32% segment crossing its track linearly on `wait`; nothing else on the
621
+ screen moves while it does. Measurable work is a progress line with a written
622
+ count. A page load shows still placeholders in the shape of the content.
623
+ Reduced motion drops the travel and breathes the whole line in opacity.
624
+
625
+ The colour of a wait is ink: the cobalto info role belonged to the retired
626
+ activity particles and marks no system wait now. Success, warning and error
627
+ keep their meanings; waiting for a person or a failed operation uses the
628
+ feedback treatment, not an activity one.
629
+
630
+ The renderers are `PendingLabel`, `WaitingLine`, `Progress` and `Skeleton` in
631
+ `@nadicodeai/ui`. The one-second delay, the geometry, the ink and the motion
632
+ are owned by [`packages/ui/docs/activity.md`](../ui/docs/activity.md); the
633
+ motion vocabulary owns `.nc-anim-wait` and its reduced-motion law; a consuming
634
+ app chooses the wait and supplies its readable words.
736
635
 
737
636
  ## Do's and Don'ts
738
637
 
739
638
  This section states the correct form; a complete positive definition is the whole rule.
740
639
 
741
- - Website pages are continuous `page-row` sequences inside one `page-frame`; centered content sits in the 10-column `active-lane` with columns 1 and 12 visibly owned — by empty canvas, `side-fill`, guide strips, or component-owned fillers.
742
- - Every visible line has one owner: ordinary seams are `{spacing.guide}` at `{colors.line}`, tuned only at the token contract; required control boundaries are `{colors.input}` / `{colors.dark-input}`; named collection controls may use the optional-perimeter rule above; keyboard focus is the dedicated focus-ring roles; crosshairs are `{colors.cross}` at `{spacing.guide}` weight.
743
- - Page structure is architectural: hairlines, luminance, spacing, and the grammar above. One material role per product container; structural page-grammar cells are border-only; the four `nc-elevation-*` rungs are the complete shadow vocabulary; modal scrims stay unblurred.
744
- - Typography uses fixed token sizes stepped by breakpoint; components take their default heights from the exported height tokens and grow only when real content requires it.
745
- - Agent orbs identify an Agent; conversations, artifacts, review states, and work output belong to assistant-ui and the agentic work surfaces. Text beside `{colors.verde-vivo}` sits on the canvas.
746
- - Components are named as NadicodeAI-owned primitives, and this file carries contract only: tokens, semantics, and application rules — component prop APIs, state matrices, screenshots, and page-specific material live with their owners.
747
- - Each register keeps its own structure and chrome: the page grammar on the website, the material ladder in the Portal and the desktop, the desktop window following the operating system.
640
+ - A website page is a sequence of full-bleed sections in three kinds only: white for reading, black for a scene, and at most one flat colour section for one statement. Each one centres its content in the same column, capped at `content-max` with the gutter its viewport gives it, and the sections sit one spacing step apart.
641
+ - Every visible line has one owner: ordinary seams are `{spacing.guide}` at `{colors.line}`, tuned only at the token contract; required control boundaries are `{colors.input}` / `{colors.dark-input}`; named collection controls may use the optional-perimeter rule above; keyboard focus is the dedicated focus-ring roles.
642
+ - Page structure is architectural: hairlines, luminance, spacing, and the section grammar above. One material role per product container; structural cells carry no elevation; the four `nc-elevation-*` rungs are the complete shadow vocabulary; modal scrims stay unblurred.
643
+ - Typography uses fixed token sizes stepped by breakpoint; a component's height comes from its content and the spacing ladder, never from a fixed height of its own.
644
+ - Agent orbs identify an Agent; conversations, artifacts, review states, and work output belong to assistant-ui and the agentic work surfaces. Text sits on the canvas beside an identity mark, never on it.
645
+ - Components are named as nadicode-owned primitives, and this file carries contract only: tokens, semantics, and application rules — component prop APIs, state matrices, screenshots, and page-specific material live with their owners.
646
+ - Each register keeps its own structure and chrome: the three section kinds on the website, the material ladder in the Portal and the desktop, the desktop window following the operating system.
748
647
 
749
648
  ## Iconography
750
649
 
@@ -753,7 +652,7 @@ discipline below, the sizes, the stroke, and the decorative-versus-named rule,
753
652
  is foundation and holds in every register.
754
653
 
755
654
  The website, the Portal, and `@nadicodeai/ui` draw from Lucide (ISC). Lucide's
756
- shapes are never edited; the NadicodeAI house render changes only how they are
655
+ shapes are never edited; the nadicode house render changes only how they are
757
656
  drawn: a line of `{spacing.icon-stroke}` on screen at every size, held there by
758
657
  `vector-effect: non-scaling-stroke` on each shape, round caps, round joins, no
759
658
  fill, and `currentColor` inheritance. A 12 px icon takes
@@ -792,9 +691,9 @@ Other companies' marks are not nadicode company logos and are not governed by an
792
691
 
793
692
  Agent orbs, Nadia's included, and generated imagery are not nadicode company logos. The orbs live in [`packages/ui/docs/agent-orb.md`](../ui/docs/agent-orb.md); editorial imagery routes through [`skills/marketing/blog-imagegen/references/editorial-register.md`](../../skills/marketing/blog-imagegen/references/editorial-register.md). This contract owns only the package delivery interface.
794
693
 
795
- All forms derive from one authored, framework-neutral geometry source exported as `@nadicodeai/design-system/assets/logo-geometry`: on a 100-unit x-height, an 18-unit stroke with flat ends, bowls and the n's arch of radius 41, and the dot of radius 11.5 over the i's stem. No SVG, React component, favicon, document, or app re-authors, traces, typesets, or rearranges those shapes. The eight reference files beside the ADR are the target each generated output matches.
694
+ All forms derive from one authored, framework-neutral geometry source exported as `@nadicodeai/design-system/assets/logo-geometry`: on a 100-unit x-height, an 18-unit stroke with flat ends, bowls and the n's arch of radius 41, and the dot of radius 14 centred 79 units above the baseline over the i's stem. No SVG, React component, favicon, document, or app re-authors, traces, typesets, or rearranges those shapes. The eight reference files beside the ADR are the target each generated output matches, byte for byte, and the package guard fails on any output that drifts from them.
796
695
 
797
- On `{colors.canvas}` and the soft neutral canvases, the letters are ink and the dot is `{colors.identity-cobalto}`. On `{colors.identity-ink}` and other dark grounds, the letters are `{colors.identity-white}` and the dot is the brighter cobalt #4d6bff, so it does not sink into black. A one-ink form, letters and dot in one colour, exists only where a single ink prints: embossing, stamps, one-colour print. On photography, place the logo in a quiet area or over a scrim that keeps every part legible; when neither is available, mount it on an approved solid field. One surface uses one treatment.
696
+ On `{colors.canvas}` and the soft neutral canvases, the letters are ink and the dot is `{colors.logo-dot}`. On `{colors.identity-ink}` and other dark grounds, the letters are `{colors.identity-white}` and the dot is `{colors.dark-logo-dot}`, a brighter cobalt, so it does not sink into black. `{colors.logo-dot}` is the logo's own colour role and the only token a consumer paints the dot with; the generated mode layer flips it under any dark ancestor, so React draws one form on both grounds. A one-ink form, letters and dot in one colour, exists only where a single ink prints: embossing, stamps, one-colour print. On photography, place the logo in a quiet area or over a scrim that keeps every part legible; when neither is available, mount it on an approved solid field. One surface uses one treatment.
798
697
 
799
698
  The clear-space unit is one stroke width of the rendered wordmark or mark. Keep at least one unit free on every side, with no text, rule, image, or container edge inside it. Minimum rendered sizes are 72 CSS pixels wide for the wordmark, 12 CSS pixels for the mark, and 16 CSS pixels for the favicon. Browser and operating-system icon slots use the supplied favicon or app-icon files at the platform's required size. Never compress, crop, or distort a logo to fit.
800
699
 
@@ -802,13 +701,11 @@ For a meaningful standalone image, use the accessible name `nadicode`; the acces
802
701
 
803
702
  Do not redraw, retype, reorder, stretch, crop, rotate, recolor, outline, shadow, animate, or add effects or gradients to a logo form. Do not add a dot or any shape to the mark, move the dot off the i, or substitute Nadia imagery for the logo.
804
703
 
805
- The package build renders and publicly exports the positive, reversed and one-ink SVG and transparent PNG files for the wordmark and mark, and the icon tile, from the geometry and the current `colors.*` contract. React consumers use `BrandLockup`, `BrandMark`, or `BrandWordmark` according to the roles above. The operator-only favicon generator wraps the same mark geometry in the platform tile; it does not substitute another mark. Private geometry templates do not ship. A contract or geometry change is completed by regeneration, never by repainting an output or updating a fallback by hand.
806
-
807
- **Target state** (#2272): the geometry source and every generated output, favicon and React component still draw the previous interlocked-link NadicodeAI lockup with its verde AI suffix; #2272 replaces them with the forms above and regenerates everything.
704
+ The package build renders and publicly exports seven files as SVG and transparent PNG from the geometry and the current `colors.*` contract: `assets/logo` and `assets/logo-white`, `assets/logo-one-ink` and `assets/logo-one-ink-white`, `assets/mark` and `assets/mark-white`, and `assets/app-icon`. React consumers use `BrandMark` or `BrandWordmark` according to the roles above; there is no lockup adapter, because the wordmark is the logo and the mark is already inside it, so no surface places the two together. The operator-only favicon generator draws the same icon tile at the sizes a browser and a platform ask for; it does not substitute another mark. Private geometry templates do not ship. A contract or geometry change is completed by regeneration, never by repainting an output or updating a fallback by hand.
808
705
 
809
706
  ## CSS Architecture & Token Pipeline
810
707
 
811
- How this contract becomes shipped CSS. The private CSS authoring graph lives in `src/css/`; the build flattens that graph and inlines its component icon dependencies into the single public `dist/css/index.css` artifact exported as `@nadicodeai/design-system/css`. Generated files and partials never ship as package subpaths. The cascade is `@layer tokens, theme, reset, foundation, motion, layout, primitives, components;` with no `sections` layer. The bundle ships only framework-agnostic brand and display CSS components; generic marketing and SaaS components ship as React components from `@nadicodeai/ui`.
708
+ How this contract becomes shipped CSS. The private CSS authoring graph lives in `src/css/`; the build flattens that graph and inlines its component icon dependencies into the single public `dist/css/index.css` artifact exported as `@nadicodeai/design-system/css`. Generated files and partials never ship as package subpaths. The cascade is `@layer tokens, theme, reset, foundation, motion, primitives, components;` with no `layout` and no `sections` layer. The bundle ships only framework-agnostic brand and display CSS components; generic marketing and SaaS components ship as React components from `@nadicodeai/ui`.
812
709
 
813
710
  ### Public style interfaces
814
711
 
@@ -821,17 +718,17 @@ Callers never import generated files, CSS partials, or framework adapters direct
821
718
 
822
719
  ### Authoring homes
823
720
 
824
- Each delivery tier (see Layout, "Delivery Tiers") has exactly one authoring file:
721
+ Each part of the shipped CSS has exactly one authoring file:
825
722
 
826
- - Tokens: `src/css/tokens.generated.css`, built from this contract and never hand-edited. Runtime composition belongs to the owning layer, such as responsive switching in `layout.css`; there is no hand-authored token partial.
723
+ - Tokens: `src/css/tokens.generated.css`, built from this contract and never hand-edited. Runtime composition belongs to the owning layer, such as the website's responsive switching in `foundation.css`; there is no hand-authored token partial.
827
724
  - Type utilities: the `.nc-type-*` classes live in `foundation.css`.
828
- - Page grammar: `layout.css`, which also owns all responsive `@media` switching.
725
+ - The website's content column, `.nc-page`, and the responsive `@media` switching behind it: `foundation.css`.
829
726
  - CSS primitives and components: `primitives.css` plus `components/*`. Every shipped component is named once in `## Components`; the two-way guard derives the CSS relationship from that catalog.
830
727
  - Specimens: `examples/`, which only assemble already-defined tiers.
831
728
 
832
729
  ### Authored token extension
833
730
 
834
- The YAML front matter carries the four groups the Google design schema defines: colors, typography, rounded, spacing. Everything else this contract owns is authored in the one `json design-tokens` fence below, as DTCG 2025.10. It holds `core.material`, `core.elevation` (four rungs per mode, each recipe ending in its own ring), `core.motion.duration` and `core.motion.ease`, and the register groups: `register.website.layout` (the page-grammar geometry), `register.portal.density` (the filter-chip sizes), and `register.nadia` (the desktop's colour and radius aliases onto `core`, plus deprecated elevation data retained for older consumers).
731
+ The YAML front matter carries the four groups the Google design schema defines: colors, typography, rounded, spacing. Everything else this contract owns is authored in the one `json design-tokens` fence below, as DTCG 2025.10. It holds `core.material`, `core.elevation` (four rungs per mode, each recipe ending in its own ring), `core.motion.duration` and `core.motion.ease`, and the register groups: `register.website.layout` (the website's column and section rhythm), `register.portal.density` (the filter-chip sizes), and `register.nadia` (the desktop's colour and radius aliases onto `core`, plus deprecated elevation data retained for older consumers).
835
732
 
836
733
  An alias is written in the short `{core.color.canvas}` form here; the build rewrites it to the public rooted form `{nadicode.core.color.canvas}` in `dist/tokens/nadicode.dtcg.json`. `$extensions."ai.nadicode.targets"` on a register group lists where its values go: `css` puts them in the generated stylesheets, `data` keeps them in the token files only. A register group without that key fails the build. `core` carries no key and always reaches both.
837
734
 
@@ -897,7 +794,7 @@ An alias is written in the short `{core.color.canvas}` form here; the build rewr
897
794
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":5.25,"unit":"px"},"blur":{"value":7,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.02}},
898
795
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":2.79,"unit":"px"},"blur":{"value":3.72,"unit":"px"},"spread":{"value":-2,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.01}},
899
796
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":1.16,"unit":"px"},"blur":{"value":1.5,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.01}},
900
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.18}}
797
+ {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.12}}
901
798
  ]
902
799
  },
903
800
  "raised": {
@@ -907,7 +804,7 @@ An alias is written in the short `{core.color.canvas}` form here; the build rewr
907
804
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":6,"unit":"px"},"blur":{"value":12,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.03}},
908
805
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":3,"unit":"px"},"blur":{"value":6,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.02}},
909
806
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":1.5,"unit":"px"},"blur":{"value":3,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.02}},
910
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.18}}
807
+ {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.16}}
911
808
  ]
912
809
  },
913
810
  "floating": {
@@ -917,7 +814,7 @@ An alias is written in the short `{core.color.canvas}` form here; the build rewr
917
814
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":10,"unit":"px"},"blur":{"value":20,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.04}},
918
815
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":5,"unit":"px"},"blur":{"value":10,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.03}},
919
816
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":2,"unit":"px"},"blur":{"value":4,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.02}},
920
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.18}}
817
+ {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.22}}
921
818
  ]
922
819
  },
923
820
  "modal": {
@@ -927,7 +824,7 @@ An alias is written in the short `{core.color.canvas}` form here; the build rewr
927
824
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":15,"unit":"px"},"blur":{"value":30,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.05}},
928
825
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":7.5,"unit":"px"},"blur":{"value":15,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.04}},
929
826
  {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":3,"unit":"px"},"blur":{"value":6,"unit":"px"},"spread":{"value":0,"unit":"px"},"color":{"colorSpace":"srgb","components":[0,0,0],"alpha":0.03}},
930
- {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.18}}
827
+ {"offsetX":{"value":0,"unit":"px"},"offsetY":{"value":0,"unit":"px"},"blur":{"value":0,"unit":"px"},"spread":{"value":1,"unit":"px"},"color":{"colorSpace":"srgb","components":[1,1,1],"alpha":0.28}}
931
828
  ]
932
829
  }
933
830
  }
@@ -944,10 +841,7 @@ An alias is written in the short `{core.color.canvas}` form here; the build rewr
944
841
  "transition": {"$value":{"value":240,"unit":"ms"}},
945
842
  "drawer-swipe": {"$value":{"value":400,"unit":"ms"}},
946
843
  "drawer": {"$value":{"value":450,"unit":"ms"}},
947
- "spin": {"$value":{"value":600,"unit":"ms"}},
948
- "typing": {"$value":{"value":1,"unit":"s"}},
949
- "pulse": {"$value":{"value":1.6,"unit":"s"}},
950
- "shimmer": {"$value":{"value":2,"unit":"s"}}
844
+ "wait": {"$value":{"value":1.6,"unit":"s"}}
951
845
  },
952
846
  "ease": {
953
847
  "$type": "cubicBezier",
@@ -963,51 +857,12 @@ An alias is written in the short `{core.color.canvas}` form here; the build rewr
963
857
  "$extensions": {"ai.nadicode.targets":["css","data"]},
964
858
  "layout": {
965
859
  "$type": "dimension",
966
- "section": {"$value":{"value":192,"unit":"px"}},
967
- "page-pad": {"$value":{"value":24,"unit":"px"}},
968
- "page-max-mobile": {"$value":{"value":448,"unit":"px"}},
969
- "page-max-tablet": {"$value":{"value":624,"unit":"px"}},
970
- "page-max-desktop": {"$value":{"value":1440,"unit":"px"}},
971
- "frame-max": {"$value":{"value":1392,"unit":"px"}},
972
- "frame-gap": {"$value":{"value":24,"unit":"px"}},
973
- "cell-padding": {"$value":{"value":24,"unit":"px"}},
974
- "section-gap": {"$value":{"value":48,"unit":"px"}},
975
- "cross-size-mobile": {"$value":{"value":11,"unit":"px"}},
976
- "cross-size-tablet": {"$value":{"value":15,"unit":"px"}},
977
- "cross-size-desktop": {"$value":{"value":21,"unit":"px"}},
978
- "h-strip-mobile": {"$value":{"value":72,"unit":"px"}},
979
- "h-strip-tablet": {"$value":{"value":80,"unit":"px"}},
980
- "h-strip-desktop": {"$value":{"value":88,"unit":"px"}},
981
- "h-cell-sm": {"$value":{"value":120,"unit":"px"}},
982
- "h-cell-md-mobile": {"$value":{"value":144,"unit":"px"}},
983
- "h-cell-md-tablet": {"$value":{"value":160,"unit":"px"}},
984
- "h-cell-md-desktop": {"$value":{"value":168,"unit":"px"}},
985
- "h-cell-lg-mobile": {"$value":{"value":220,"unit":"px"}},
986
- "h-cell-lg-desktop": {"$value":{"value":240,"unit":"px"}},
987
- "h-visual-mobile": {"$value":{"value":276,"unit":"px"}},
988
- "h-visual-tablet": {"$value":{"value":318,"unit":"px"}},
989
- "h-visual-desktop": {"$value":{"value":360,"unit":"px"}},
990
- "h-precision-mobile": {"$value":{"value":276,"unit":"px"}},
991
- "h-precision-tablet": {"$value":{"value":480,"unit":"px"}},
992
- "h-precision-desktop": {"$value":{"value":720,"unit":"px"}},
993
- "h-hero-mobile": {"$value":{"value":560,"unit":"px"}},
994
- "h-hero-desktop": {"$value":{"value":720,"unit":"px"}},
995
- "h-action-strip-mobile": {"$value":{"value":20,"unit":"px"}},
996
- "h-action-strip-desktop": {"$value":{"value":32,"unit":"px"}},
997
- "h-action-body-mobile": {"$value":{"value":320,"unit":"px"}},
998
- "h-action-body-tablet": {"$value":{"value":338,"unit":"px"}},
999
- "h-action-band": {"$value":{"value":260,"unit":"px"}},
1000
- "h-feature-mobile": {"$value":{"value":360,"unit":"px"}},
1001
- "h-feature-tablet": {"$value":{"value":400,"unit":"px"}},
1002
- "h-feature-desktop": {"$value":{"value":456,"unit":"px"}},
1003
- "h-editorial-feature-mobile": {"$value":{"value":360,"unit":"px"}},
1004
- "h-editorial-feature-tablet": {"$value":{"value":440,"unit":"px"}},
1005
- "h-editorial-feature-desktop": {"$value":{"value":560,"unit":"px"}},
1006
- "h-bento-primary": {"$value":{"value":384,"unit":"px"}},
1007
- "h-bento-secondary": {"$value":{"value":240,"unit":"px"}},
1008
- "h-bento-mobile-copy": {"$value":{"value":248,"unit":"px"}},
1009
- "h-bento-mobile-main": {"$value":{"value":537,"unit":"px"}},
1010
- "h-bento-strip": {"$value":{"value":20,"unit":"px"}}
860
+ "content-max": {"$value":{"value":1200,"unit":"px"}},
861
+ "gutter-mobile": {"$value":{"value":20,"unit":"px"}},
862
+ "gutter-tablet": {"$value":{"value":40,"unit":"px"}},
863
+ "gutter-desktop": {"$value":{"value":64,"unit":"px"}},
864
+ "section-step-mobile": {"$value":{"value":64,"unit":"px"}},
865
+ "section-step-desktop": {"$value":{"value":96,"unit":"px"}}
1011
866
  }
1012
867
  },
1013
868
  "portal": {