# Foundations · RJ DTC Admin design system

Generated by `phase-4/docs/foundations/src/build.py` from `phase-2/kit/tokens.css` (Kit v3), base.css, the family CSS and the 64 surface folders. Do not edit by hand.

**242 tokens documented · 13 pages · 33 surface values and 61 kit values with no token.**

Target stack: Next.js 15 + Tailwind 3.4 + shadcn/ui (new-york). HTML pages: `foundations/<slug>/` (desktop) and `foundations/<slug>/m/` (phone).

## Contents

- [Colour](#colour)
- [Typography](#typography)
- [Spacing and sizes](#spacing-and-sizes)
- [Layout and shell](#layout-and-shell)
- [Radius](#radius)
- [Elevation and shadows](#elevation-and-shadows)
- [Gradients and the sidebar light](#gradients-and-the-sidebar-light)
- [Motion](#motion)
- [Focus ring](#focus-ring)
- [Iconography](#iconography)
- [Z-index](#z-index)
- [Breakpoints and surface pairs](#breakpoints-and-surface-pairs)
- [Untokenised values](#untokenised-values)

## Colour

Every colour role as a shadcn-style HSL triple, with its hex, how to use it, its contrast, and where it may not go.

### Core roles

_The shadcn/ui roles. Values are bare HSL triples, used as hsl(var(--x)) or hsl(var(--x) / .12)._

A triple drops straight into a shadcn `globals.css`. Write `hsl(var(--primary))` for a solid and `hsl(var(--primary) / .12)` for a tint; the copy button on a token gives you `hsl(var(--token))`. Hex is for reference only: never write hex in product code. **Renders as** is what the browser paints from the triple; **Authored hex** is the board value where the triple rounds one step away from it. **Direct refs** counts the surface folders (of 64) whose own CSS, JS or HTML writes the token; every surface also uses it through the kit.

| Token | Value (HSL triple) | Renders as | Authored hex | Role | Direct refs |
|---|---|---|---|---|---|
| `--background` | `0 0% 100%` | `#FFFFFF` | same | page surface (clean white canvas) | 64 |
| `--foreground` | `222 43% 10%` | `#0F1524` | `#0F1626` | body text, headings | 64 |
| `--card` | `0 0% 100%` | `#FFFFFF` | same | — | 61 |
| `--card-foreground` | `222 43% 10%` | `#0F1524` | `#0F1626` | text and icons on `--card` | 0 |
| `--popover` | `0 0% 100%` | `#FFFFFF` | same | menus, sheets, dialogs, toasts | 10 |
| `--popover-foreground` | `222 43% 10%` | `#0F1524` | `#0F1626` | text and icons on `--popover` | 0 |
| `--primary` | `220 49% 11%` | `#0E172A` | `#0E1729` | RJ midnight navy (--rj-ink): every primary action | 61 |
| `--primary-foreground` | `60 17% 98%` | `#FBFBF9` | `#FAFAF8` | text and icons on `--primary` | 42 |
| `--primary-hover` | `223 42% 18%` | `#1B2641` | `#1A2540` | --rj-ink-hover | 0 |
| `--secondary` | `0 0% 100%` | `#FFFFFF` | same | outlined secondary button ground | 0 |
| `--secondary-foreground` | `220 49% 11%` | `#0E172A` | `#0E1729` | text and icons on `--secondary` | 0 |
| `--muted` | `225 22% 96%` | `#F3F4F7` | `#F4F5F8` | table header, wells, hover | 64 |
| `--muted-foreground` | `221 17% 38%` | `#505B71` | `#505A70` | quiet text (>= 5.9:1 on white) | 64 |
| `--accent` | `224 38% 94%` | `#EAEDF6` | `#EBEEF6` | selection, active filter | 55 |
| `--accent-foreground` | `220 49% 11%` | `#0E172A` | `#0E1729` | text and icons on `--accent` | 5 |
| `--destructive` | `353 53% 42%` | `#A43240` | `#A2323F` | — | 63 |
| `--destructive-foreground` | `0 0% 100%` | `#FFFFFF` | same | text and icons on `--destructive` | 0 |
| `--border` | `220 20% 91%` | `#E3E7ED` | `#E4E7ED` | hairlines | 64 |
| `--input` | `219 14% 59%` | `#8892A5` | same | control borders: 3.1:1 on white (WCAG 1.4.11); was #C6CCD7 at 1.6:1 | 63 |
| `--input-hover` | `223 17% 45%` | `#5F6A86` | same | field / checkbox / select border on hover | 19 |
| `--secondary-border` | `219 14% 79%` | `#C2C7D1` | same | outlined secondary button edge (board 3, text-labelled so exempt from 3:1) | 59 |
| `--secondary-border-hover` | `220 10% 70%` | `#ABB0BA` | same | — | 0 |
| `--ring` | `221 45% 45%` | `#3F60A6` | `#3F5FA6` | focus | 33 |


### Status roles

_Success, warning, info and neutral, plus the catalogue lifecycle names that alias them._

Each status has a `-foreground` for text on a solid fill. Chips use the soft fill: the status at `--status-soft-alpha` (.12) on white, with the text in the full status colour.

| Token | Value (HSL triple) | Renders as | Authored hex | Role | Direct refs |
|---|---|---|---|---|---|
| `--success` | `158 51% 26%` | `#20644B` | `#21654C` | — | 49 |
| `--success-foreground` | `0 0% 100%` | `#FFFFFF` | same | text and icons on `--success` | 0 |
| `--warning` | `36 79% 29%` | `#845610` | `#83550F` | also the STAGING badge | 52 |
| `--warning-foreground` | `0 0% 100%` | `#FFFFFF` | same | text and icons on `--warning` | 0 |
| `--info` | `218 49% 39%` | `#335694` | `#335794` | — | 38 |
| `--info-foreground` | `0 0% 100%` | `#FFFFFF` | same | text and icons on `--info` | 0 |
| `--neutral` | `222 13% 39%` | `#575E70` | `#575F72` | — | 1 |
| `--neutral-foreground` | `0 0% 100%` | `#FFFFFF` | same | text and icons on `--neutral` | 0 |
| `--draft` | `var(--info)` | `#335694` | — | Alias of `--info`. Draft = info blue | 1 |
| `--available` | `var(--success)` | `#20644B` | — | Alias of `--success`. Available = green | 2 |
| `--retired` | `var(--neutral)` | `#575E70` | — | Alias of `--neutral`. Retired = slate | 1 |
| `--locked` | `var(--neutral)` | `#575E70` | — | Alias of `--neutral`. "locked" banner + lock chip | 5 |
| `--not-wired` | `var(--warning)` | `#845610` | — | Alias of `--warning`. "not wired" banner (feature exists, backend not connected) | 5 |

| Token | Value | Role |
|---|---|---|
| `--status-soft-alpha` | `.12` | soft fills: status at 12% on white; text stays the status colour (all >= 4.5:1) |

- catalogue lifecycle — always use these names, never the generic role, in product UI
- soft fills: status at 12% on white; text stays the status colour (all >= 4.5:1)

Live specimen on the page: every chip colour on its soft fill.


### Sidebar

_The shadcn sidebar block. The satin sheen gradients live on the Gradients page._

| Token | Value (HSL triple) | Renders as | Authored hex | Role | Direct refs |
|---|---|---|---|---|---|
| `--sidebar` | `220 49% 11%` | `#0E172A` | `#0E1729` | — | 0 |
| `--sidebar-background` | `var(--sidebar)` | `#0E172A` | — | Alias of `--sidebar`. older shadcn name | 0 |
| `--sidebar-foreground` | `222 19% 82%` | `#C8CEDA` | `#C9CEDA` | --rj-mist | 10 |
| `--sidebar-primary` | `221 54% 75%` | `#9DB3E2` | `#9FB4E2` | active-item rail + icon tint | 0 |
| `--sidebar-primary-foreground` | `220 49% 11%` | `#0E172A` | same | text and icons on `--sidebar-primary` | 0 |
| `--sidebar-accent` | `222 43% 19%` | `#1C2845` | `#1B2744` | — | 0 |
| `--sidebar-accent-foreground` | `60 17% 98%` | `#FBFBF9` | `#FAFAF8` | text and icons on `--sidebar-accent` | 10 |
| `--sidebar-border` | `222 33% 22%` | `#26314B` | `#253049` | — | 10 |
| `--sidebar-ring` | `221 54% 75%` | `#9DB3E2` | same | focus ring on the navy sidebar | 0 |

The sheen tokens `--sidebar-sheen` and `--sidebar-sheen-light` are documented, layer by layer, on the Gradients page.


### Charts

_Five categorical series, assigned in order and never cycled, plus one muted comparison colour._

Categorical order, assigned in this order and never cycled. Validated with the dataviz validator on white: lightness band, chroma floor, adjacent CVD >= 10.6, normal-vision >= 15.9, all >= 3:1; slots 1-3 also pass --pairs all. None of them is a status colour. A single series uses --chart-1; a comparison / previous period uses --chart-muted (dotted).

| Token | Value (HSL triple) | Renders as | Authored hex | Role | Direct refs |
|---|---|---|---|---|---|
| `--chart-1` | `219 61% 47%` | `#2F62C1` | `#2F62C0` | blue | 7 |
| `--chart-2` | `166 76% 36%` | `#16A281` | `#16A07F` | teal | 0 |
| `--chart-3` | `20 70% 53%` | `#DB6B33` | `#DB6B35` | coral | 0 |
| `--chart-4` | `229 62% 68%` | `#7B8DE0` | `#7C8FE0` | periwinkle | 0 |
| `--chart-5` | `334 58% 58%` | `#D2568C` | `#D2588C` | rose | 0 |
| `--chart-muted` | `221 25% 58%` | `#798AAF` | `#7A8BB0` | steel: previous period, reference lines, never a category | 0 |

Live specimen on the page: the six chart colours in order.


### Presentation colours

_Kit and docs pages only. Never in product UI._

| Token | Value (HSL triple) | Renders as | Authored hex | Role | Direct refs |
|---|---|---|---|---|---|
| `--canvas` | `214 24% 94%` | `#ECEFF3` | `#EDF0F4` | page behind specimen cards | 1 |
| `--studio` | `radial-gradient(120% 90% at 50% 18%, #f7f8fa 0%, #e3e7ee 80%)` | — | — | garment photo ground | 15 |


### shadcn mapping

_The globals.css block, generated from tokens.css, and the Tailwind 3.4 colour key for every role._

**globals.css · :root (generated from tokens.css)**

```css
:root {
  --background: 0 0% 100%;
  --foreground: 222 43% 10%;
  --card: 0 0% 100%;
  --card-foreground: 222 43% 10%;
  --popover: 0 0% 100%;
  --popover-foreground: 222 43% 10%;
  --primary: 220 49% 11%;
  --primary-foreground: 60 17% 98%;
  --primary-hover: 223 42% 18%;
  --secondary: 0 0% 100%;
  --secondary-foreground: 220 49% 11%;
  --muted: 225 22% 96%;
  --muted-foreground: 221 17% 38%;
  --accent: 224 38% 94%;
  --accent-foreground: 220 49% 11%;
  --destructive: 353 53% 42%;
  --destructive-foreground: 0 0% 100%;
  --border: 220 20% 91%;
  --input: 219 14% 59%;
  --input-hover: 223 17% 45%;
  --secondary-border: 219 14% 79%;
  --secondary-border-hover: 220 10% 70%;
  --ring: 221 45% 45%;
  --radius: 0.375rem;
  --success: 158 51% 26%;
  --success-foreground: 0 0% 100%;
  --warning: 36 79% 29%;
  --warning-foreground: 0 0% 100%;
  --info: 218 49% 39%;
  --info-foreground: 0 0% 100%;
  --neutral: 222 13% 39%;
  --neutral-foreground: 0 0% 100%;
  --draft: var(--info);
  --available: var(--success);
  --retired: var(--neutral);
  --locked: var(--neutral);
  --not-wired: var(--warning);
  --sidebar: 220 49% 11%;
  --sidebar-background: var(--sidebar);
  --sidebar-foreground: 222 19% 82%;
  --sidebar-primary: 221 54% 75%;
  --sidebar-primary-foreground: 220 49% 11%;
  --sidebar-accent: 222 43% 19%;
  --sidebar-accent-foreground: 60 17% 98%;
  --sidebar-border: 222 33% 22%;
  --sidebar-ring: 221 54% 75%;
  --chart-1: 219 61% 47%;
  --chart-2: 166 76% 36%;
  --chart-3: 20 70% 53%;
  --chart-4: 229 62% 68%;
  --chart-5: 334 58% 58%;
  --chart-muted: 221 25% 58%;
}
```

| Token | Utility | tailwind.config.ts theme.extend.colors | Kind |
|---|---|---|---|
| `--background` | `bg-background` | `'background': 'hsl(var(--background) / <alpha-value>)',` | shadcn standard |
| `--foreground` | `text-foreground` | `'foreground': 'hsl(var(--foreground) / <alpha-value>)',` | shadcn standard |
| `--card` | `bg-card` | `'card': 'hsl(var(--card) / <alpha-value>)',` | shadcn standard |
| `--card-foreground` | `text-card-foreground` | `'card-foreground': 'hsl(var(--card-foreground) / <alpha-value>)',` | shadcn standard |
| `--popover` | `bg-popover` | `'popover': 'hsl(var(--popover) / <alpha-value>)',` | shadcn standard |
| `--popover-foreground` | `text-popover-foreground` | `'popover-foreground': 'hsl(var(--popover-foreground) / <alpha-value>)',` | shadcn standard |
| `--primary` | `bg-primary` | `'primary': 'hsl(var(--primary) / <alpha-value>)',` | shadcn standard |
| `--primary-foreground` | `text-primary-foreground` | `'primary-foreground': 'hsl(var(--primary-foreground) / <alpha-value>)',` | shadcn standard |
| `--primary-hover` | `bg-primary-hover` | `'primary-hover': 'hsl(var(--primary-hover) / <alpha-value>)',` | RJ extension |
| `--secondary` | `bg-secondary` | `'secondary': 'hsl(var(--secondary) / <alpha-value>)',` | shadcn standard |
| `--secondary-foreground` | `text-secondary-foreground` | `'secondary-foreground': 'hsl(var(--secondary-foreground) / <alpha-value>)',` | shadcn standard |
| `--muted` | `bg-muted` | `'muted': 'hsl(var(--muted) / <alpha-value>)',` | shadcn standard |
| `--muted-foreground` | `text-muted-foreground` | `'muted-foreground': 'hsl(var(--muted-foreground) / <alpha-value>)',` | shadcn standard |
| `--accent` | `bg-accent` | `'accent': 'hsl(var(--accent) / <alpha-value>)',` | shadcn standard |
| `--accent-foreground` | `text-accent-foreground` | `'accent-foreground': 'hsl(var(--accent-foreground) / <alpha-value>)',` | shadcn standard |
| `--destructive` | `bg-destructive` | `'destructive': 'hsl(var(--destructive) / <alpha-value>)',` | shadcn standard |
| `--destructive-foreground` | `text-destructive-foreground` | `'destructive-foreground': 'hsl(var(--destructive-foreground) / <alpha-value>)',` | shadcn standard |
| `--border` | `border-border` | `'border': 'hsl(var(--border) / <alpha-value>)',` | shadcn standard |
| `--input` | `border-input` | `'input': 'hsl(var(--input) / <alpha-value>)',` | shadcn standard |
| `--input-hover` | `border-input-hover` | `'input-hover': 'hsl(var(--input-hover) / <alpha-value>)',` | RJ extension |
| `--secondary-border` | `border-secondary-border` | `'secondary-border': 'hsl(var(--secondary-border) / <alpha-value>)',` | RJ extension |
| `--secondary-border-hover` | `border-secondary-border-hover` | `'secondary-border-hover': 'hsl(var(--secondary-border-hover) / <alpha-value>)',` | RJ extension |
| `--ring` | `ring-ring` | `'ring': 'hsl(var(--ring) / <alpha-value>)',` | shadcn standard |
| `--success` | `bg-success` | `'success': 'hsl(var(--success) / <alpha-value>)',` | RJ extension |
| `--success-foreground` | `text-success-foreground` | `'success-foreground': 'hsl(var(--success-foreground) / <alpha-value>)',` | RJ extension |
| `--warning` | `bg-warning` | `'warning': 'hsl(var(--warning) / <alpha-value>)',` | RJ extension |
| `--warning-foreground` | `text-warning-foreground` | `'warning-foreground': 'hsl(var(--warning-foreground) / <alpha-value>)',` | RJ extension |
| `--info` | `bg-info` | `'info': 'hsl(var(--info) / <alpha-value>)',` | RJ extension |
| `--info-foreground` | `text-info-foreground` | `'info-foreground': 'hsl(var(--info-foreground) / <alpha-value>)',` | RJ extension |
| `--neutral` | `bg-neutral` | `'neutral': 'hsl(var(--neutral) / <alpha-value>)',` | RJ extension |
| `--neutral-foreground` | `text-neutral-foreground` | `'neutral-foreground': 'hsl(var(--neutral-foreground) / <alpha-value>)',` | RJ extension |
| `--draft` | `bg-draft` | `'draft': 'hsl(var(--draft) / <alpha-value>)',` | RJ extension |
| `--available` | `bg-available` | `'available': 'hsl(var(--available) / <alpha-value>)',` | RJ extension |
| `--retired` | `bg-retired` | `'retired': 'hsl(var(--retired) / <alpha-value>)',` | RJ extension |
| `--locked` | `bg-locked` | `'locked': 'hsl(var(--locked) / <alpha-value>)',` | RJ extension |
| `--not-wired` | `bg-not-wired` | `'not-wired': 'hsl(var(--not-wired) / <alpha-value>)',` | RJ extension |
| `--sidebar` | `bg-sidebar` | `'sidebar': 'hsl(var(--sidebar) / <alpha-value>)',` | shadcn standard |
| `--sidebar-background` | `bg-sidebar-background` | `'sidebar-background': 'hsl(var(--sidebar-background) / <alpha-value>)',` | shadcn standard |
| `--sidebar-foreground` | `text-sidebar-foreground` | `'sidebar-foreground': 'hsl(var(--sidebar-foreground) / <alpha-value>)',` | shadcn standard |
| `--sidebar-primary` | `bg-sidebar-primary` | `'sidebar-primary': 'hsl(var(--sidebar-primary) / <alpha-value>)',` | shadcn standard |
| `--sidebar-primary-foreground` | `text-sidebar-primary-foreground` | `'sidebar-primary-foreground': 'hsl(var(--sidebar-primary-foreground) / <alpha-value>)',` | shadcn standard |
| `--sidebar-accent` | `bg-sidebar-accent` | `'sidebar-accent': 'hsl(var(--sidebar-accent) / <alpha-value>)',` | shadcn standard |
| `--sidebar-accent-foreground` | `text-sidebar-accent-foreground` | `'sidebar-accent-foreground': 'hsl(var(--sidebar-accent-foreground) / <alpha-value>)',` | shadcn standard |
| `--sidebar-border` | `border-sidebar-border` | `'sidebar-border': 'hsl(var(--sidebar-border) / <alpha-value>)',` | shadcn standard |
| `--sidebar-ring` | `ring-sidebar-ring` | `'sidebar-ring': 'hsl(var(--sidebar-ring) / <alpha-value>)',` | shadcn standard |
| `--chart-1` | `bg-chart-1` | `'chart-1': 'hsl(var(--chart-1) / <alpha-value>)',` | shadcn standard |
| `--chart-2` | `bg-chart-2` | `'chart-2': 'hsl(var(--chart-2) / <alpha-value>)',` | shadcn standard |
| `--chart-3` | `bg-chart-3` | `'chart-3': 'hsl(var(--chart-3) / <alpha-value>)',` | shadcn standard |
| `--chart-4` | `bg-chart-4` | `'chart-4': 'hsl(var(--chart-4) / <alpha-value>)',` | shadcn standard |
| `--chart-5` | `bg-chart-5` | `'chart-5': 'hsl(var(--chart-5) / <alpha-value>)',` | shadcn standard |
| `--chart-muted` | `bg-chart-muted` | `'chart-muted': 'hsl(var(--chart-muted) / <alpha-value>)',` | RJ extension |


### Contrast

_Every pairing the admin uses, computed from the tokens. Text needs 4.5:1; controls, focus and chart marks need 3:1._

| Pairing | Text / mark | On | Ratio | Result |
|---|---|---|---|---|
| Body text | `--foreground` | `--background` | 18.21:1 | Pass AA and AAA |
| Quiet text | `--muted-foreground` | `--background` | 6.83:1 | Pass AA |
| Quiet text on a well | `--muted-foreground` | `--muted` | 6.21:1 | Pass AA |
| Primary button | `--primary-foreground` | `--primary` | 17.25:1 | Pass AA and AAA |
| Primary button, hover | `--primary-foreground` | `--primary-hover` | 14.48:1 | Pass AA and AAA |
| Secondary button | `--secondary-foreground` | `--secondary` | 17.88:1 | Pass AA and AAA |
| Selected row, active filter | `--accent-foreground` | `--accent` | 15.27:1 | Pass AA and AAA |
| Danger button | `--destructive-foreground` | `--destructive` | 6.76:1 | Pass AA |
| Danger text | `--destructive` | `--background` | 6.76:1 | Pass AA |
| Success text | `--success` | `--background` | 7.03:1 | Pass AA and AAA |
| Warning text, STAGING badge | `--warning` | `--background` | 6.33:1 | Pass AA |
| Info text | `--info` | `--background` | 7.24:1 | Pass AA and AAA |
| Neutral text | `--neutral` | `--background` | 6.48:1 | Pass AA |
| Menu text | `--sidebar-foreground` | `--sidebar` | 11.32:1 | Pass AA and AAA |
| Active menu item | `--sidebar-accent-foreground` | `--sidebar-accent` | 14.09:1 | Pass AA and AAA |
| Active rail and icon | `--sidebar-primary` | `--sidebar` | 8.50:1 | Pass 3:1 |
| Focus ring | `--ring` | `--background` | 6.11:1 | Pass 3:1 |
| Control edge | `--input` | `--background` | 3.13:1 | Pass 3:1 |
| Control edge, hover | `--input-hover` | `--background` | 5.40:1 | Pass 3:1 |
| Secondary button edge (text-labelled, exempt) | `--secondary-border` | `--background` | 1.70:1 | Exempt: text-labelled |
| Hairline | `--border` | `--background` | 1.24:1 | Decorative, no minimum |
| Chart series 1 | `--chart-1` | `--background` | 5.77:1 | Pass 3:1 |
| Chart series 2 | `--chart-2` | `--background` | 3.22:1 | Pass 3:1 |
| Chart series 3 | `--chart-3` | `--background` | 3.41:1 | Pass 3:1 |
| Chart series 4 | `--chart-4` | `--background` | 3.12:1 | Pass 3:1 |
| Chart series 5 | `--chart-5` | `--background` | 3.85:1 | Pass 3:1 |
| Previous period | `--chart-muted` | `--background` | 3.46:1 | Pass 3:1 |
| Chip text on its 12% soft fill | `--draft` | `--status-soft-alpha` over `--background` = #E7EBF2 | 6.06:1 | Pass AA |
| Chip text on its 12% soft fill | `--available` | `--status-soft-alpha` over `--background` = #E4ECE9 | 5.85:1 | Pass AA |
| Chip text on its 12% soft fill | `--retired` | `--status-soft-alpha` over `--background` = #EBECEE | 5.48:1 | Pass AA |
| Chip text on its 12% soft fill | `--warning` | `--status-soft-alpha` over `--background` = #F0EBE2 | 5.33:1 | Pass AA |
| Chip text on its 12% soft fill | `--destructive` | `--status-soft-alpha` over `--background` = #F4E6E8 | 5.57:1 | Pass AA |
| Chip text on its 12% soft fill | `--not-wired` | `--status-soft-alpha` over `--background` = #F0EBE2 | 5.33:1 | Pass AA |


### Do and don't

_The colour rules from the kit contract._

**Do**

- Use `--primary` navy for every primary action, one per screen.
- Use the catalogue names `--draft`, `--available`, `--retired` in product UI, never the generic status role.
- Use `--input` (3.1:1 on white) for every control edge and `--secondary-border` only for text-labelled buttons and decorative edges.
- Assign chart colours in order, `--chart-1` first; a comparison series is `--chart-muted`, dotted.

**Don't**

- Write hex in product code: every colour is `hsl(var(--x))`.
- Use gold, brass, plum, purple, or a dark canvas. The brief bans them.
- Use a chart colour for a status, or a status colour for a chart series.
- Show Refunded in red or Shipped in green: Refunded is neutral and Shipped is info blue.


### Colour literals on surfaces

_Hex and rgba values written outside tokens.css, reconciled to the nearest token._

| Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|
| `rgba(0,0,0,.08)` | No token: no colour token has this RGB | — | 1 | 0 | base.css `.thumb` |


## Typography

Archivo for the voice, IBM Plex Sans for reading, IBM Plex Mono for codes. The full scale, weights, loading and the Archivo width rule.

### Families

_Three families, each with one job._

| Token | Stack | Used for | Weights loaded |
|---|---|---|---|
| `--font-title` | `'Archivo', system-ui, sans-serif` | titles, buttons, menu, labels, table headers | 400–700 (variable, wght axis only) |
| `--font-voice` | `var(--font-title)` | Alias of `--font-title`. | — |
| `--font-ui` | `'IBM Plex Sans', system-ui, sans-serif` | reading text, tables, prices | 400, 500, 600 |
| `--font-code` | `'IBM Plex Mono', ui-monospace, monospace` | cloth codes, handles, versions, ids | 400, 500 |

Live specimen on the page: one line in each family.


### Type scale

_Every size token. Line height and tracking where the kit names them; the rest are set per component._

Live specimen on the page: every step rendered in its family and weight.

Scale: size / line-height / tracking (tracking is never positive on Archivo).

| Token | Size | Line height | Tracking | Family | Weight | Use |
|---|---|---|---|---|---|---|
| `--text-display` | `40px` | `--lh-display` = 1.05 | `--tr-display` = -.02em | `--font-title` | `--w-semibold` = 600 | kit page H1 only |
| `--text-page` | `26px` | `--lh-page` = 1.15 | `--tr-page` = -.015em | `--font-title` | `--title-w` = 600 | page title in the app |
| `--text-section` | `16px` | `--lh-section` = 1.25 | `--tr-section` = -.005em | `--font-title` | `--w-semibold` = 600 | sheet / dialog / panel title |
| `--text-card` | `13.5px` | `--lh-card` = 1.3 | 0 | `--font-title` | `--w-semibold` = 600 | card title, panel h3 |
| `--text-body` | `13px` | `--lh-body` = 1.45 | 0 | `--font-ui` | `--w-regular` = 400 | default UI text (Plex) |
| `--text-read` | `15px` | `--lh-read` = 1.55 | 0 | `--font-ui` | `--w-regular` = 400 | help drawer, long copy |
| `--text-label` | `13px` | set per component | 0 | `--font-title` | `--w-semibold` = 600 | form label (Archivo 600) |
| `--text-small` | `12px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | meta, table secondary |
| `--text-kpi` | `28px` | set per component | 0 | `--font-title` | `--w-semibold` = 600 | KPI value / chart headline figure (raised by data-display) |
| `--text-hero` | `30px` | set per component | 0 | `--font-ui` | `--w-semibold` = 600 | KPI value / chart headline figure (raised by data-display) |
| `--text-step-m` | `17px` | set per component | 0 | `--font-title` | `--w-semibold` = 600 | phone compact step title (raised by wizard-stepper) |
| `--text-input-m` | `16px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | phone text fields: iOS never zooms at >= 16px |
| `--text-micro` | `11.5px` | set per component | 0 | `--font-title` | `--w-semibold` = 600 | chips, counts, th |
| `--text-code` | `12px` | set per component | 0 | `--font-code` | `--w-regular` = 400 | cloth codes, handles, versions, ids |
| `--text-btn` | `13.5px` | set per component | 0 | `--font-title` | `--w-semibold` = 600 | button label |
| `--text-btn-sm` | `12.5px` | set per component | 0 | `--font-title` | `--w-semibold` = 600 | small button label |
| `--text-btn-lg` | `15px` | set per component | 0 | `--font-title` | `--w-semibold` = 600 | large and phone primary button label |
| `--text-8` | `8px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | initials in the empty garment placeholder (.dt-ph.none) |
| `--text-8-5` | `8.5px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | initials in the empty garment placeholder (.dt-ph.none) |
| `--text-9` | `9px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | initials in the large empty garment placeholder (.dt-ph.lg.none) |
| `--text-9-5` | `9.5px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | small mono badge |
| `--text-10` | `10px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | tab bar badge, tiny avatar |
| `--text-10-5` | `10.5px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | STAGING / environment badge, tab bar label, filter counts |
| `--text-11` | `11px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | kbd, chip count, step mark |
| `--text-14` | `14px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | phone lede, phone list title |
| `--text-18` | `18px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | phone sheet and ticket title |
| `--text-19` | `19px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | surface-only literal |
| `--text-20` | `20px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | ticket head, mini error page title |
| `--text-22` | `22px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | stacked KPI value, error page title |
| `--text-24` | `24px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | phone page title |
| `--text-44` | `44px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | surface-only literal |
| `--text-52` | `52px` | set per component | 0 | `--font-ui` | `--w-regular` = 400 | surface-only literal |


### Weights

_Four weights. Archivo titles sit at --title-w._

| Token | Value | Note |
|---|---|---|
| `--w-regular` | `400` |  |
| `--w-medium` | `500` |  |
| `--w-semibold` | `600` |  |
| `--w-bold` | `700` |  |
| `--title-w` | `600` | Page title weight |


### Archivo stays at normal width

_Frank's rule, 2026-10-11: Archivo is never set wide._

| Token | Value | Rule |
|---|---|---|
| `--font-stretch-title` | `100%` | Frank 2026-10-11: Archivo is NEVER set wide |

`--font-stretch-title` is `100%`. The fonts load the wght axis only, so no wdth axis is available to the page. Tracking on Archivo is zero or negative, never positive: `--tr-display` -.02em, `--tr-page` -.015em, `--tr-section` -.005em.

**base.css · the Archivo voice selector (quoted)**

```css
.title,.page-title,.btn,.sb-item,.sb-group,.sb-brand b,.lbl,.th,.kit-h1,.kit-h2,.spec-h,.sec-title,.tab,.seg-i {
  font-family: var(--font-voice);
  font-stretch: var(--font-stretch-title);
  font-variation-settings: normal;
}
.title {
  font-weight: var(--title-w);
  letter-spacing: -.02em;
}
```

**Do**

- Set titles, buttons, the menu, labels, tabs and table headers in `--font-title` at 100% width.
- Use negative tracking only on display, page and section titles.

**Don't**

- Load Archivo's wdth axis, or set `font-stretch` above 100%.
- Add positive `letter-spacing` to any Archivo text, including uppercase labels.


### Numbers and money

_Money, counts and dates line up in columns._

GBP is the base currency, shown with EUR and USD. Lists show `£1,150`; edit fields show `£1,150.00`. Every number in a table or KPI uses tabular figures.

**base.css (quoted)**

```css
.num,.money {
  font-variant-numeric: tabular-nums;
}
body {
  background: hsl(var(--canvas));
  color: hsl(var(--foreground));
  font: var(--w-regular) var(--text-body)/var(--lh-body) var(--font-ui);
  -webkit-font-smoothing: antialiased;
  font-feature-settings: "cv11" 1;
}
```

Live specimen on the page: a price column with and without tabular figures.


### Font loading

_The kit loads from Google Fonts; the app loads the same files through next/font._

**Google Fonts URL (base.css @import)**

```text
https://fonts.googleapis.com/css2?family=Archivo:wght@400..700&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap
```

**next/font (generated from the same weights)**

```ts
// app/fonts.ts · next/font replaces Inter and JetBrains Mono
import { Archivo, IBM_Plex_Sans, IBM_Plex_Mono } from 'next/font/google';

// Archivo: 400–700 (variable, wght axis only). Never pass axes: ['wdth'].
export const archivo = Archivo({ subsets: ['latin'], display: 'swap', variable: '--font-archivo' });
export const plexSans = IBM_Plex_Sans({ subsets: ['latin'], display: 'swap', weight: ['400', '500', '600'], variable: '--font-plex-sans' });
export const plexMono = IBM_Plex_Mono({ subsets: ['latin'], display: 'swap', weight: ['400', '500'], variable: '--font-plex-mono' });

// globals.css then points the kit tokens at the next/font variables:
//   --font-title: var(--font-archivo), system-ui, sans-serif;
//   --font-ui:    var(--font-plex-sans), system-ui, sans-serif;
//   --font-code:  var(--font-plex-mono), ui-monospace, monospace;
```


### Phone type

_Phone-only steps. Text fields are 16px so iOS never zooms._

| Token | Value | Use |
|---|---|---|
| `--text-input-m` | `16px` | phone text fields: iOS never zooms at >= 16px |
| `--text-step-m` | `17px` | phone compact step title (raised by wizard-stepper) |


### Named type literals (kit v3)

_Sizes off the main scale, named so every use can be found and moved._

These hold the exact pixel values the kit and the surfaces already draw. They are not new steps in the scale: prefer a scale token, and reach for one of these only where the approved design uses that size.

| Token | Value | Use | Direct refs |
|---|---|---|---|
| `--text-8` | `8px` | initials in the empty garment placeholder (.dt-ph.none) | 0 |
| `--text-8-5` | `8.5px` | initials in the empty garment placeholder (.dt-ph.none) | 0 |
| `--text-9` | `9px` | initials in the large empty garment placeholder (.dt-ph.lg.none) | 0 |
| `--text-9-5` | `9.5px` | small mono badge | 0 |
| `--text-10` | `10px` | tab bar badge, tiny avatar | 0 |
| `--text-10-5` | `10.5px` | STAGING / environment badge, tab bar label, filter counts | 0 |
| `--text-11` | `11px` | kbd, chip count, step mark | 0 |
| `--text-14` | `14px` | phone lede, phone list title | 0 |
| `--text-18` | `18px` | phone sheet and ticket title | 0 |
| `--text-19` | `19px` | surface-only literal | 0 |
| `--text-20` | `20px` | ticket head, mini error page title | 0 |
| `--text-22` | `22px` | stacked KPI value, error page title | 0 |
| `--text-24` | `24px` | phone page title | 0 |
| `--text-44` | `44px` | surface-only literal | 0 |
| `--text-52` | `52px` | surface-only literal | 0 |


### Type literals on surfaces

_Font sizes and tracking written as raw values, reconciled to the scale._

| Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|
| `-.01em` | No token: tracking off the scale | — | 33 | 22 | family app-shell `.sh-mono`; family data-display `.dd-ticket-h h3`; family wizard-stepper `.wz-ci-t`; +22 more |
| `-.03em` | No token: tracking off the scale | — | 4 | 4 | page content-frame `.wb-big`; page releases `.wb-big`; page website `.wb-big`; +1 more |
| `.08em` | No token: positive tracking: allowed on Plex caps only, never on Archivo | — | 2 | 0 | base.css `.staging` |
| `-.003em` | No token: tracking off the scale | — | 1 | 0 | base.css `.btn` |
| `.02em` | No token: positive tracking: allowed on Plex caps only, never on Archivo | — | 1 | 0 | family app-shell `.p-bar .staging` |
| `22px` | Has a token: same px as --text-22 | `--text-22` | 39 | 30 | page analytics-funnel `.kit-m .dd-kpi-stack .dd-kpi-v`; page analytics-revenue `.kit-m .dd-kpi-stack .dd-kpi-v`; page analytics-system-status `.kit-m .dd-kpi-stack .dd-kpi-v`; +27 more |
| `-.02em` | Has a token: equals --tr-display | `--tr-display` | 29 | 26 | base.css `.title`; family data-display `.dd-kpi-v`; page campaign-detail `.pg-perf .big`; +25 more |
| `15px` | Has a token: same px as --text-read, --text-btn-lg | `--text-read` | 27 | 11 | base.css `.p-bar b`; family app-shell `.sh-pbar-place b`; family data-table `.dt-oc-b .dt-money b`; +13 more |
| `10.5px` | Has a token: same px as --text-10-5 | `--text-10-5` | 22 | 14 | page content-frame `.wb-addr .code`; page releases `.wb-addr .code`; page settings-api-keys `.st-mono`; +11 more |
| `20px` | Has a token: same px as --text-20 | `--text-20` | 17 | 17 | page appointments `.kit-m .cx-kpis .dd-kpi-v`; page campaign-detail `.pg-mail-b h3`; page campaigns `.pg-mail-b h3`; +14 more |
| `10px` | Has a token: same px as --text-10 | `--text-10` | 17 | 16 | page content-frame `.wb-pl-av`; page releases `.wb-pl-av`; page settings-api-keys `.st-si-who .avatar`; +13 more |
| `-.005em` | Has a token: equals --tr-section | `--tr-section` | 13 | 11 | family app-shell `.sh-sub-h`; family cards-gallery `.cg-title`; page hq-settings-audit-log `.kit-m .hq-li .t>b`; +10 more |
| `16px` | Has a token: same px as --text-section, --text-input-m | `--text-section` | 12 | 11 | family app-shell `.tabbar a .sh-tb`; page appointments `.kit-m .cx-ident .avatar.xl`; page content-frame `.kit-m .wb-rc-host`; +9 more |
| `28px` | Has a token: same px as --text-kpi | `--text-kpi` | 11 | 10 | family form-controls `.fc-cal-g span`; page settings-api-keys `.st-si.m .st-si-big`; page settings-audit-log `.st-si.m .st-si-big`; +8 more |
| `18px` | Has a token: same px as --text-18 | `--text-18` | 11 | 11 | page appointments `.cx-ident .avatar.xl`; page content-frame `.kit-m .wb-pl-g`; page customer-detail `.cx-ident .avatar.xl`; +8 more |
| `40px` | Has a token: same px as --text-display | `--text-display` | 10 | 10 | page settings-api-keys `.st-si-big`; page settings-audit-log `.st-si-big`; page settings-brand `.st-si-big`; +7 more |
| `19px` | Has a token: same px as --text-19 | `--text-19` | 9 | 9 | page content-frame `.wb-rc-host`; page releases `.wb-rc-host`; page support `.kit-m .su-th-t`; +6 more |
| `13px` | Has a token: same px as --text-body, --text-label | `--text-body` | 7 | 0 | base.css `.help-q`; family data-table `.dt-oc-no`; family form-controls `.kit-m .fc-tag`; +1 more |
| `14px` | Has a token: same px as --text-14 | `--text-14` | 7 | 7 | page appointments `.kit-m .cx-plist .dt-li-r b`; page customer-detail `.kit-m .cx-plist .dt-li-r b`; page customers `.kit-m .cx-plist .dt-li-r b`; +4 more |
| `11.5px` | Has a token: same px as --text-micro | `--text-micro` | 5 | 0 | base.css `.spec-foot code`; family app-shell `.sh-mono`; family data-table `.kit-m .dt-json` |
| `12.5px` | Has a token: same px as --text-btn-sm | `--text-btn-sm` | 4 | 0 | base.css `.p-head p`; family app-shell `.sh-jump`; family status-feedback `.sf-filter` |
| `24px` | Has a token: same px as --text-24 | `--text-24` | 4 | 4 | page content-frame `.wb-vn`; page releases `.wb-vn`; page website `.wb-vn`; +1 more |
| `-.015em` | Has a token: equals --tr-page | `--tr-page` | 4 | 4 | page content-frame `.wb-vn`; page releases `.wb-vn`; page website `.wb-vn`; +1 more |
| `52px` | Has a token: same px as --text-52 | `--text-52` | 4 | 4 | page content-frame `.wb-big`; page releases `.wb-big`; page website `.wb-big`; +1 more |
| `17px` | Has a token: same px as --text-step-m | `--text-step-m` | 4 | 4 | page content-frame `.kit-m .wb-host`; page releases `.kit-m .wb-host`; page website `.kit-m .wb-host`; +1 more |
| `44px` | Has a token: same px as --text-44 | `--text-44` | 4 | 4 | page content-frame `.kit-m .wb-big`; page releases `.kit-m .wb-big`; page website `.kit-m .wb-big`; +1 more |
| `12px` | Has a token: same px as --text-small, --text-code | `--text-small` | 2 | 0 | base.css `.brand-mark`; family status-feedback `.sf-filter .n` |
| `13.5px` | Has a token: same px as --text-card, --text-btn | `--text-card` | 2 | 0 | base.css `.kit-m .toast .act .btn`; family status-feedback `.kit-m .sf-filter` |
| `30px` | Has a token: same px as --text-hero | `--text-hero` | 1 | 0 | base.css `.kit-m .kit-h1` |
| `8.5px` | Has a token: same px as --text-8-5 | `--text-8-5` | 1 | 1 | page super-api-access `.ak-mono` |
| `9px` | Has a token: same px as --text-9 | `--text-9` | 1 | 1 | page super-api-access `.ak-who .avatar` |


## Spacing and sizes

The 4px grid, and every fixed size: controls, rows, chips, tabs and list rows.

### 4px grid

_Every gap, padding and margin is a step on this scale._

Write `var(--s-4)`, never `16px`. A negative step is `calc(var(--s-4) * -1)`. Tailwind's default spacing is also a 4px grid, so `p-4` equals `--s-4`; the token is the source.

| Token | Value | Direct refs |
|---|---|---|
| `--s-0` | `0` | 0 |
| `--s-1` | `4px` | 64 |
| `--s-2` | `8px` | 64 |
| `--s-3` | `12px` | 64 |
| `--s-4` | `16px` | 64 |
| `--s-5` | `20px` | 64 |
| `--s-6` | `24px` | 64 |
| `--s-7` | `28px` | 0 |
| `--s-8` | `32px` | 64 |
| `--s-10` | `40px` | 33 |
| `--s-12` | `48px` | 43 |
| `--s-16` | `64px` | 63 |
| `--s-18` | `72px` | 0 |


### Control and row sizes

_Fixed heights for controls, rows and chips, desktop and phone._

| Token | Value | Use | Direct refs |
|---|---|---|---|
| `--control-h` | `40px` | button, input, select (desktop) | 30 |
| `--control-h-sm` | `34px` | small button, row actions | 64 |
| `--control-h-m` | `44px` | phone minimum tap target | 53 |
| `--control-h-m-lg` | `48px` | phone primary / sheet buttons | 64 |
| `--row` | `44px` | table row | 42 |
| `--row-th` | `36px` | table header | 0 |
| `--row-compact` | `36px` | compact table density (raised by data-table) | 0 |
| `--control-h-xs` | `28px` | inline buttons inside photo slots, compact pills (raised by form-controls) | 9 |
| `--chip-h` | `22px` | status chip height | 0 |
| `--chip-h-lg` | `26px` | header-size chip (raised by status-feedback) | 0 |
| `--tab-h` | `40px` | page / section tab strip (.tabs) | 0 |
| `--tab-h-m` | `44px` | phone tab strip, thumb height | 0 |
| `--kv-label` | `120px` | key/value aside label column (.k-kv) | 0 |
| `--kv-label-m` | `104px` | phone key/value label column (.k-kv) | 0 |
| `--li-min-h` | `72px` | phone list row without a thumbnail (.k-li) | 0 |

Live specimen on the page: buttons and chips at each control height.


### Off-grid and hard-coded sizes

_Spacing literals off the 4px grid, and sizes written as px although a token exists._

| Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|
| `6px` | No token: not on the 4px grid | — | 296 | 37 | base.css `.sb-brand`; family app-shell `.sh-place`; family cards-gallery `.cg-why`; +44 more |
| `10px` | No token: not on the 4px grid | — | 123 | 63 | base.css `.sidebar`; family app-shell `.sh-place`; family cards-gallery `.kit-m .cg-body`; +68 more |
| `5px` | No token: not on the 4px grid | — | 24 | 7 | base.css `.staging`; family app-shell `.sh-pl-n span`; family cards-gallery `.cg-ghostgrid`; +13 more |
| `9px` | No token: not on the 4px grid | — | 9 | 0 | base.css `.sb-item.on::before`; family form-controls `.fc-ta`; family overlays `.ov-combo`; +2 more |
| `7px` | No token: not on the 4px grid | — | 8 | 0 | base.css `.lbl`; family form-controls `.fc-lrow`; family status-feedback `.sf-dl` |
| `18px` | No token: not on the 4px grid | — | 4 | 0 | base.css `.ph`; family app-shell `.sh-tree`; family wizard-stepper `.wz-line` |
| `22px` | No token: not on the 4px grid | — | 3 | 0 | base.css `.page`; family data-table `.dt-page`; family page-help `.ph-back` |
| `11px` | No token: not on the 4px grid | — | 3 | 0 | base.css `.toast`; family data-display `.dd-tl li::before`; family wizard-stepper `.wz-ri:not(:last-child)::after` |
| `14px` | No token: not on the 4px grid | — | 1 | 0 | base.css `.sidebar` |
| `26px` | No token: not on the 4px grid | — | 1 | 0 | base.css `.page` |
| `74px` | No token: not on the 4px grid | — | 1 | 0 | family page-help `.ph-guide` |
| `86px` | No token: not on the 4px grid | — | 1 | 0 | family status-feedback `.sf-screen .sf-toaster` |
| `28px` | Has a token: same px as --control-h-xs | `--control-h-xs` | 201 | 64 | base.css `.avatar`; family app-shell `.sh-notice`; family cards-gallery `.cg-selbar .cg-mini`; +69 more |
| `52px` | Has a token: same px as --pbar-h | `--pbar-h` | 124 | 54 | family app-shell `.sh-cmd-in`; family form-controls `.fc-bopt`; family overlays `.ov-fullbar`; +55 more |
| `48px` | Has a token: same px as --control-h-m-lg | `--control-h-m-lg` | 124 | 38 | family cards-gallery `.cg-li .cg-tthumb`; family form-controls `.fc-prow .fc-media`; family status-feedback `.sf-tile`; +38 more |
| `44px` | Has a token: same px as --control-h-m, --row, --tab-h-m | `--control-h-m` | 123 | 42 | base.css `.kit-m .switch`; family app-shell `.sh-pop-s`; family data-display `.dd-conf .m`; +45 more |
| `36px` | Has a token: same px as --row-th, --row-compact | `--row-th` | 109 | 44 | base.css `.sb-item`; family app-shell `.sh-frame-b.mid .ico`; family cards-gallery `.cg-ghostgrid i`; +50 more |
| `56px` | Has a token: same px as --topbar-h | `--topbar-h` | 107 | 45 | family app-shell `.sh-full .sh-cmd-in`; family cards-gallery `.cg-sh-head`; family data-table `.dt-ph.lg`; +49 more |
| `40px` | Has a token: same px as --control-h, --tab-h | `--control-h` | 104 | 42 | base.css `.thumb`; family app-shell `.sh-cmd .sh-row`; family cards-gallery `.cg-tthumb`; +45 more |
| `22px` | Has a token: same px as --chip-h | `--chip-h` | 84 | 25 | base.css `.chip`; family app-shell `.sh-mono.sm`; family cards-gallery `.cg-tick`; +31 more |
| `26px` | Has a token: same px as --chip-h-lg | `--chip-h-lg` | 35 | 20 | base.css `.kit-m .switch`; family form-controls `.fc-hue`; family overlays `.ov-presets span`; +21 more |
| `120px` | Has a token: same px as --kv-label | `--kv-label` | 32 | 32 | page analytics-funnel `.dd-skel`; page analytics-revenue `.dd-skel`; page analytics-system-status `.dd-skel`; +29 more |
| `72px` | Has a token: same px as --li-min-h | `--li-min-h` | 20 | 18 | base.css `.kit-m .state>.state-k`; family form-controls `.kit-m .fc-file .fc-meter`; page brand-global-store `.bg-th`; +17 more |
| `480px` | Has a token: same px as --dialog-w | `--dialog-w` | 9 | 9 | page design-pricing `.pg-gdl`; page pricing `.pg-gdl`; page pricing-bulk `.pg-gdl`; +6 more |
| `104px` | Has a token: same px as --kv-label-m | `--kv-label-m` | 6 | 6 | page hq-settings-audit-log `.hq-pcrumb .hq-proto b`; page hq-settings-users `.hq-pcrumb .hq-proto b`; page super-api-access `.hq-pcrumb .hq-proto b`; +3 more |
| `34px` | Has a token: same px as --control-h-sm | `--control-h-sm` | 5 | 0 | family app-shell `.sh-jump`; family data-display `.dd-kpi-row`; family data-table `.dt-fchip`; +1 more |
| `380px` | Has a token: same px as --help-w | `--help-w` | 3 | 2 | family data-table `.dt-emp`; page global-store-editor `.ge-toasts`; page product-prices `.pp-offers` |
| `232px` | Has a token: same px as --sidebar-w | `--sidebar-w` | 1 | 0 | base.css `.tip.wrap` |


## Layout and shell

Desktop at 1440 and phone at 390, the app shell dimensions, sheets and dialogs, and the column grid.

### Viewports

_Two dedicated compositions, never a responsive collapse._

| Token | Value | Note |
|---|---|---|
| `--vp-desktop` | `1440px` | desktop composition, served at / |
| `--vp-phone` | `390px` | phone composition, served at /m/ |


### Desktop shell

_Sidebar, top bar, content width, and the overlay widths._

| Token | Value | Use | Direct refs |
|---|---|---|---|
| `--sidebar-w` | `232px` | desktop sidebar width | 64 |
| `--topbar-h` | `56px` | desktop top bar height | 63 |
| `--content-max` | `1280px` | page content stops growing past this (raised by specimens; build lane) | 64 |
| `--sheet-w` | `404px` | side sheet (quick edit) width | 0 |
| `--sheet-w-md` | `532px` | medium side sheet: detail views (raised by overlays) | 2 |
| `--sheet-w-lg` | `856px` | large side sheet: create key, long forms (raised by overlays) | 0 |
| `--dialog-w` | `480px` | dialog and confirm width | 0 |
| `--help-w` | `380px` | help drawer width | 64 |

Schematic on the page: the desktop shell at half scale.

**base.css · the shell as shipped (quoted)**

```css
.frame {
  border-radius: var(--r-frame);
  overflow: clip;
  background: hsl(var(--background));
  position: relative;
  box-shadow: var(--e-frame);
}
.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0,1fr);
  min-height: 640px;
}
.main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  position: relative;
  overflow: clip;
}
.topbar {
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 0 var(--s-5);
  border-bottom: 1px solid hsl(var(--border));
  background: hsl(var(--background)/.86);
  backdrop-filter: blur(var(--blur-glass));
  position: relative;
  z-index: 2;
}
.page {
  padding: 22px var(--s-6) 26px;
}
.ph {
  display: flex;
  align-items: flex-end;
  gap: var(--s-4);
  margin-bottom: 18px;
}
.title,.page-title,.btn,.sb-item,.sb-group,.sb-brand b,.lbl,.th,.kit-h1,.kit-h2,.spec-h,.sec-title,.tab,.seg-i {
  font-family: var(--font-voice);
  font-stretch: var(--font-stretch-title);
  font-variation-settings: normal;
}
.page-title {
  font-weight: var(--title-w);
  font-size: var(--text-page);
  line-height: var(--lh-page);
  letter-spacing: var(--tr-page);
}
```

> **Flag.** `.page` padding `22px var(--s-6) 26px` and `.ph` margin-bottom `18px` carry px off the 4px grid with no token. They were kept so board 3 renders exactly as approved; move them to the nearest `--s-*` step in the build lane (KIT-CHANGELOG, Open).


### Phone shell

_Phone bar, sticky action bar and tab bar._

| Token | Value | Use | Direct refs |
|---|---|---|---|
| `--pbar-h` | `52px` | phone top bar (raised by specimens) | 4 |
| `--tabbar-h` | `62px` | phone tab bar incl. padding (raised by specimens) | 63 |
| `--control-h-m` | `44px` | phone minimum tap target | 53 |
| `--control-h-m-lg` | `48px` | phone primary / sheet buttons | 64 |

Schematic on the page: the phone shell at half scale.

**base.css · the phone shell as shipped (quoted)**

```css
.phone {
  background: hsl(var(--background));
  border: 1px solid hsl(var(--border));
  border-radius: var(--r-frame);
  overflow: clip;
  position: relative;
}
.p-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  height: var(--pbar-h);
  padding: 0 var(--s-3);
  border-bottom: 1px solid hsl(var(--border));
  background: hsl(var(--background));
}
.p-head {
  display: flex;
  align-items: flex-end;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-4) var(--s-3);
}
.p-sec {
  padding: var(--s-4);
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.tabbar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: grid;
  grid-template-columns: repeat(4,1fr);
  background: hsl(var(--background)/.96);
  backdrop-filter: blur(var(--actbar-blur));
  border-top: 1px solid hsl(var(--border));
  padding: 6px 0 10px;
}
.tabbar a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: 46px;
  font: var(--text-10-5)/1 var(--font-voice);
  color: hsl(var(--muted-foreground));
  text-decoration: none;
}
.k-actbar {
  position: sticky;
  bottom: var(--tabbar-h);
  z-index: calc(var(--z-sticky) * 2);
  display: flex;
  gap: var(--s-2);
  align-items: center;
  padding: var(--s-3) var(--s-4);
  background: hsl(var(--background)/.96);
  backdrop-filter: blur(var(--actbar-blur));
  border-top: 1px solid hsl(var(--border));
}
```

> **Flag.** `--pbar-h` (52px) and `--tabbar-h` (62px) are declared, but `.p-bar` and `.tabbar` in base.css still write the px. Use the tokens in the app.


### Column grid

_12 columns on desktop, 4 on phone._

The specimens family draws a 12-column grid with a `--s-4` gutter on desktop and a 4-column grid with a `--s-3` gutter on phone. Page layouts use CSS grid with `minmax(0,1fr)` tracks inside `--content-max`.

Specimen on the page: the column grid.

> **Flag.** No token names the column count or gutter (specimens raised `--grid-cols-d` / `--grid-cols-m`; not folded). Treat 12 / 4 as a convention.


## Radius

Chips 4px, controls 6px, cards 8px, frames 15px. The shadcn `--radius` is 0.375rem.

### Radius scale

_Every corner in the admin is one of these._

| Token | Value | Resolves to | Use | Direct refs |
|---|---|---|---|---|
| `--r-chip` | `4px` | — | square chips | 51 |
| `--r-control` | `6px` | — | buttons, inputs, menu items, filter chips | 64 |
| `--r-card` | `8px` | — | cards, table wrap, popovers (= radius + 2) | 64 |
| `--r-frame` | `15px` | — | panels, app frame, specimen cards | 63 |
| `--r-pill` | `999px` | — | switch, avatar | 63 |
| `--r-media` | `4px` | — | photos inside cards | 17 |
| `--r-card-inner` | `calc(var(--r-card) - 1px)` | `calc(8px - 1px)` | photo corners inside a 1px-bordered card (raised by cards-gallery) | 7 |
| `--radius` | `0.375rem` | — | 6px (shadcn --radius) | 0 |

Live specimen on the page: one box per radius.


### shadcn mapping

_How the scale maps to shadcn and Tailwind._

shadcn reads `--radius` (0.375rem). Cards use `calc(var(--radius) + 2px)`, which is `--r-card`. In `tailwind.config.ts` map `borderRadius` keys to the tokens (`chip`, `control`, `card`, `frame`, `pill`, `media`) rather than shadcn's `sm` / `md` / `lg` arithmetic, so a class names the role.

**Do**

- Use `--r-card-inner` for a photo inside a 1px-bordered card, so the corners nest.

**Don't**

- Round a status chip: chips are square (`--r-chip`).
- Use `--r-pill` for anything but switches, avatars and handles.


### Radius literals on surfaces

_Corner radii written as px._

| Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|
| `2px` | No token: radius off the scale | — | 11 | 5 | base.css `.sb-item.on::before`; family data-display `.dd-cur .sw`; family form-controls `.fc-opt mark`; +5 more |
| `1px` | No token: radius off the scale | — | 1 | 0 | family data-display `.dd-legend .key` |
| `4px` | Has a token: same px as --r-chip, --r-media | `--r-chip` | 1 | 0 | base.css `kbd` |


## Elevation and shadows

Every shadow, the scrim, the sticky edge and the glass bars, with when to use each.

### Shadow scale

_Navy-tinted, soft and low. Each one has one job._

Every shadow is tinted with the navy ink (RGB of `--primary`), never black. Use the token by name: `box-shadow: var(--e-pop)`.

| Token | Value | When | Direct refs |
|---|---|---|---|
| `--e-0` | `none` | flat: tables, rows, chips, inline content | 0 |
| `--e-1` | `0 1px 2px rgba(14,23,41,.04)` | card | 37 |
| `--e-btn` | `0 1px 2px rgba(14,23,41,.20)` | primary button | 0 |
| `--e-btn-2` | `0 1px 1px rgba(14,23,41,.05)` | secondary button | 0 |
| `--e-frame` | `0 0 0 1px rgba(14,23,41,.05), 0 16px 36px -24px rgba(14,23,41,.20)` | app frame | 0 |
| `--e-pop` | `0 12px 32px -10px rgba(14,23,41,.25)` | dropdown, combobox | 63 |
| `--e-toast` | `0 12px 28px -14px rgba(14,23,41,.35)` | toast | 0 |
| `--e-sheet` | `-24px 0 60px -20px rgba(14,23,41,.35)` | side sheet, help drawer | 0 |
| `--e-dialog` | `0 24px 64px -20px rgba(14,23,41,.40)` | dialog, confirm, guard | 0 |
| `--e-phone-sheet` | `0 -20px 50px -20px rgba(14,23,41,.50)` | phone bottom sheet | 0 |
| `--e-card-hover` | `0 10px 24px -14px rgba(14,23,41,.28)` | lifted card / kanban card (raised by cards-gallery + data-display) | 12 |
| `--e-rail` | `inset 2px 0 0 hsl(var(--primary))` | 2px primary rail: selected row (.fc-cp-tbl) | 0 |
| `--e-rail-warning` | `inset 2px 0 0 hsl(var(--warning))` | 2px warning rail (surfaces) | 0 |
| `--e-rail-strong` | `inset 3px 0 0 hsl(var(--primary))` | 3px primary rail (surfaces) | 0 |
| `--e-rail-strong-info` | `inset 3px 0 0 hsl(var(--info))` | — | 0 |
| `--e-rail-strong-warning` | `inset 3px 0 0 hsl(var(--warning))` | stale card | 0 |
| `--e-rail-strong-destructive` | `inset 3px 0 0 hsl(var(--destructive))` | blocked card | 0 |
| `--e-row-focus` | `inset 0 1px 0 hsl(var(--ring)),inset 0 -1px 0 hsl(var(--ring))` | keyboard-focused table row cell | 0 |
| `--e-row-focus-first` | `inset 2px 0 0 hsl(var(--ring)),inset 0 1px 0 hsl(var(--ring)),inset 0 -1px 0 hsl(var(--ring))` | its first cell | 0 |
| `--e-sidebar-edge` | `inset -1px 0 0 rgba(201,206,218,.07)` | the sidebar's inner right edge | 0 |
| `--e-sidebar-item-on` | `inset 0 0 0 1px rgba(201,206,218,.09),0 6px 18px -12px rgba(110,140,210,.22)` | active menu item edge + soft glow | 0 |
| `--e-brand-mark` | `0 0 12px -4px rgba(150,175,225,.22)` | brand mark glow | 0 |

Live specimen on the page: one card per shadow.


### Scrim, sticky edge, glass

_The rest of the depth system._

| Token | Value | Use | Direct refs |
|---|---|---|---|
| `--scrim` | `rgba(10,14,24,.38)` | dims the page under a sheet or dialog | 0 |
| `--sticky-edge` | `hsl(var(--foreground) / .09)` | frozen-column edge shade (raised by data-table) | 0 |
| `--focus-ring` | `0 0 0 1px hsl(var(--ring)), 0 0 0 4px hsl(var(--ring) / .14)` | field and control focus: a 1px edge plus a soft halo | 16 |
| `--actbar-blur` | `8px` | phone action bar / phone save bar glass | 0 |

**base.css · the glass bars (quoted)**

```css
.topbar {
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 0 var(--s-5);
  border-bottom: 1px solid hsl(var(--border));
  background: hsl(var(--background)/.86);
  backdrop-filter: blur(var(--blur-glass));
  position: relative;
  z-index: 2;
}
.tabbar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: grid;
  grid-template-columns: repeat(4,1fr);
  background: hsl(var(--background)/.96);
  backdrop-filter: blur(var(--actbar-blur));
  border-top: 1px solid hsl(var(--border));
  padding: 6px 0 10px;
}
.k-actbar {
  position: sticky;
  bottom: var(--tabbar-h);
  z-index: calc(var(--z-sticky) * 2);
  display: flex;
  gap: var(--s-2);
  align-items: center;
  padding: var(--s-3) var(--s-4);
  background: hsl(var(--background)/.96);
  backdrop-filter: blur(var(--actbar-blur));
  border-top: 1px solid hsl(var(--border));
}
```

> **Flag.** In base.css the top bar uses `blur(var(--blur-glass))` and the tab bar `blur(var(--actbar-blur))`; only `--actbar-blur` (8px) is a token. Use `--actbar-blur` for every glass bar.


### When to lift

**Do**

- Cards sit at `--e-1`; they lift to `--e-card-hover` on hover with `--lift-shift`.
- Menus, popovers and the sticky save bar use `--e-pop`.
- Side sheets use `--e-sheet`, dialogs `--e-dialog`, phone bottom sheets `--e-phone-sheet`.

**Don't**

- Put a shadow on a table row or a chip.
- Use black shadows or a hand-written shadow: if none fits, raise a token.
- Put a glow on a button (Gradients page).


### Selection rails and row focus (kit v3)

_Inset box-shadows that mark a selected, stale, blocked or focused row or card._

Combine with an elevation by listing both: `box-shadow: var(--e-rail-strong-warning), var(--e-1)`.

| Token | Value | Use | Direct refs |
|---|---|---|---|
| `--e-rail` | `inset 2px 0 0 hsl(var(--primary))` | 2px primary rail: selected row (.fc-cp-tbl) | 0 |
| `--e-rail-warning` | `inset 2px 0 0 hsl(var(--warning))` | 2px warning rail (surfaces) | 0 |
| `--e-rail-strong` | `inset 3px 0 0 hsl(var(--primary))` | 3px primary rail (surfaces) | 0 |
| `--e-rail-strong-info` | `inset 3px 0 0 hsl(var(--info))` |  | 0 |
| `--e-rail-strong-warning` | `inset 3px 0 0 hsl(var(--warning))` | stale card | 0 |
| `--e-rail-strong-destructive` | `inset 3px 0 0 hsl(var(--destructive))` | blocked card | 0 |
| `--e-row-focus` | `inset 0 1px 0 hsl(var(--ring)),inset 0 -1px 0 hsl(var(--ring))` | keyboard-focused table row cell | 0 |
| `--e-row-focus-first` | `inset 2px 0 0 hsl(var(--ring)),inset 0 1px 0 hsl(var(--ring)),inset 0 -1px 0 hsl(var(--ring))` | its first cell | 0 |


### Glass blur (kit v3)

_The backdrop blur behind translucent bars._

Use as `backdrop-filter: blur(var(--blur-glass))`. The phone action bar and save bar use `--actbar-blur` (8px).

| Token | Value | Use | Direct refs |
|---|---|---|---|
| `--blur-glass` | `10px` | desktop top bar glass | 0 |
| `--blur-soft` | `6px` | glass blur written on the surfaces | 0 |


### Shadows and blurs on surfaces

_Shadow and blur values written inline: the colour is usually a token, the geometry is not._

| Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|
| `inset 0 0 0 1px hsl(var(--foreground)/.12)` | No token: colour from tokens, geometry hand-set | — | 11 | 9 | family cards-gallery `.cg-selbar .cg-mini`; family data-table `.dt-sw`; page design-pricing `.pg-gsw`; +8 more |
| `inset 0 0 0 1px hsl(var(--foreground)/.08)` | No token: colour from tokens, geometry hand-set | — | 7 | 4 | family cards-gallery `.cg-tthumb`; family data-table `.dt-ph`; family form-controls `.fc-slot .fc-media`; +4 more |
| `0 0 0 1px hsl(var(--primary))` | No token: colour from tokens, geometry hand-set | — | 7 | 6 | family wizard-stepper `.wz-pk[aria-pressed=true]`; page brand-global-store `.bg-th.on`; page content-frame `.wb-ptile.on`; +4 more |
| `0 0 0 2px hsl(var(--ring)/.35)` | No token: colour from tokens, geometry hand-set | — | 6 | 6 | page campaign-detail `.pg-chi.moved`; page campaigns `.pg-chi.moved`; page campaigns-email `.pg-chi.moved`; +3 more |
| `inset 0 0 0 1px hsl(var(--primary))` | No token: colour from tokens, geometry hand-set | — | 5 | 4 | family form-controls `.fc-rcard:has(input:checked)`; page brand-global-store `.bp-fsel.on`; page create-product `.bp-fsel.on`; +2 more |
| `0 0 0 1px hsl(var(--primary)/.25)` | No token: colour from tokens, geometry hand-set | — | 5 | 5 | page support `.su-ladder li.cur`; page support-categories `.su-ladder li.cur`; page support-templates `.su-ladder li.cur`; +2 more |
| `0 -6px 16px -12px hsl(var(--foreground)/.25)` | No token: colour from tokens, geometry hand-set | — | 5 | 5 | page support `.kit-m .su-compose`; page support-categories `.kit-m .su-compose`; page support-templates `.kit-m .su-compose`; +2 more |
| `0 0 0 1px hsl(var(--primary)),var(--e-1)` | No token: colour from tokens, geometry hand-set | — | 4 | 3 | family cards-gallery `.cg-pick[aria-selected=true]`; page create-product `.cp-pick[aria-selected=true]`; page global-store `.gs-card.is-sel`; +1 more |
| `0 0 0 2px hsl(var(--ring)/.55),var(--e-1)` | No token: colour from tokens, geometry hand-set | — | 3 | 3 | page brand-global-store `@keyframes bg-flash 0%`; page global-store `@keyframes gs-flash 0%`; page products `@keyframes mp-flash 0%` |
| `inset 0 0 0 2px hsl(var(--ring))` | No token: colour from tokens, geometry hand-set | — | 2 | 0 | family data-table `.dt-li.is-focus`; family form-controls `.fc-slot.target .fc-media` |
| `inset 0 0 0 1px hsl(var(--foreground)/.1)` | No token: colour from tokens, geometry hand-set | — | 2 | 0 | family form-controls `.fc-sw8`; family overlays `.ov-sw` |
| `inset 0 0 0 1px rgba(0,0,0,.08)` | No token: fully hand-set | — | 1 | 0 | base.css `.thumb` |
| `inset 0 0 0 4px hsl(var(--primary))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `input[type=radio].cb:checked` |
| `inset 0 0 0 4px hsl(var(--primary)),var(--focus-ring)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `input[type=radio].cb:checked:focus-visible,input[type=radio]` |
| `inset 0 0 0 4px hsl(var(--muted-foreground)/.55)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `input[type=radio].cb:disabled:checked` |
| `var(--e-btn-2),0 0 0 1px hsl(var(--border))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `.seg>[aria-checked=true],.seg>[aria-pressed=true],.seg>.on` |
| `inset 0 0 0 1px hsl(var(--ring)/.55)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `.mi:focus-visible,.mi.is-focus` |
| `inset 0 0 0 1px hsl(var(--input)/.35)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `.k-grab` |
| `inset 0 0 0 1px hsl(var(--not-wired)/.55)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `.chip.unmapped` |
| `inset 0 0 0 1px hsl(var(--not-wired))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `.chip.unmapped::before` |
| `0 0 0 1px hsl(var(--primary)),var(--e-pop)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family cards-gallery `.cg-pick[aria-selected=true]:hover,.cg-pick[aria-selected=tr` |
| `inset 0 0 0 1px hsl(var(--ring)/.3)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family cards-gallery `.cg-tag.sibling` |
| `0 0 0 2px hsl(var(--background))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family cards-gallery `.cg-pick[aria-selected=true] .cg-tick,.cg-pick.is-anchor .cg` |
| `0 0 0 2px hsl(var(--primary-foreground)/.6)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family data-display `.dd-stab[aria-selected=true] .dot` |
| `inset 0 0 0 1.5px hsl(var(--neutral))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family data-table `.dt-off` |
| `inset 0 0 0 1px hsl(var(--primary)/.25)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family data-table `.dt-pg[aria-current=page]` |
| `0 0 0 1px hsl(var(--destructive)),0 0 0 4px hsl(var(--destructive)/.12)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-in.err:focus,.fc-in.err:focus-within,.fc-in.err.is-focus` |
| `0 0 0 1px hsl(var(--secondary-border))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-hue` |
| `0 0 0 1px hsl(var(--muted-foreground))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-hue:hover` |
| `0 0 0 2px hsl(var(--primary))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-hue.on` |
| `inset 0 0 0 1px hsl(var(--secondary-border))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-cal-g button.today` |
| `inset 0 0 0 1px hsl(var(--warning)/.28)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family overlays `.ov-note.warn` |
| `inset 0 0 0 1px hsl(var(--destructive)/.3)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family overlays `.ov-note.err` |
| `var(--e-sheet),inset 0 0 0 2px hsl(var(--ring)/.35)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family overlays `.ov-trap` |
| `inset 0 0 0 2px hsl(var(--ring) / .35)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family overlays `.k-sheet.ov-trap::after` |
| `inset 0 0 0 1px hsl(var(--border))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family wizard-stepper `.wz-img` |
| `0 0 0 1px hsl(var(--primary)),var(--e-card-hover)` | No token: colour from tokens, geometry hand-set | — | 1 | 1 | page global-store `.gs-card.is-sel:hover` |
| `inset 0 0 0 1px hsl(var(--foreground)/.1),var(--e-1)` | No token: colour from tokens, geometry hand-set | — | 1 | 1 | page global-store-editor `.ge-swatch` |
| `-1px 0 0 hsl(var(--border))` | No token: colour from tokens, geometry hand-set | — | 1 | 1 | page super-brands `.br-tbl .br-fzr` |
| `0 0 0 1px hsl(var(--warning)/.45)` | No token: colour from tokens, geometry hand-set | — | 1 | 1 | page super-mcp-connect `.mc-write` |
| `blur(8px)` | Has a token: equals --actbar-blur | `--actbar-blur` | 75 | 63 | page analytics-funnel `.kit-m .pg-actbar`; page analytics-revenue `.kit-m .pg-actbar`; page analytics-system-status `.kit-m .pg-actbar`; +60 more |
| `blur(6px)` | Has a token: equals --blur-soft | `--blur-soft` | 63 | 63 | page analytics-funnel `.pg-proto>.k-anchor>button`; page analytics-revenue `.pg-proto>.k-anchor>button`; page analytics-system-status `.pg-proto>.k-anchor>button`; +60 more |
| `inset 2px 0 0 hsl(var(--warning))` | Has a token: equals --e-rail-warning | `--e-rail-warning` | 13 | 13 | page analytics-funnel `.pg-fun .dd-fun-r.pg-leak`; page analytics-revenue `.pg-fun .dd-fun-r.pg-leak`; page analytics-system-status `.pg-fun .dd-fun-r.pg-leak`; +10 more |
| `inset 2px 0 0 hsl(var(--primary))` | Has a token: equals --e-rail | `--e-rail` | 10 | 10 | page design-pricing `.pg-gli.on`; page hq-settings-users `.us-b.on`; page pricing `.pg-gli.on`; +7 more |
| `inset 3px 0 0 hsl(var(--primary))` | Has a token: equals --e-rail-strong | `--e-rail-strong` | 7 | 7 | page campaign-detail `.pg-bi.on`; page campaigns `.pg-bi.on`; page campaigns-email `.pg-bi.on`; +4 more |
| `inset 3px 0 0 hsl(var(--info))` | Has a token: equals --e-rail-strong-info | `--e-rail-strong-info` | 2 | 1 | page product-prices `.pp-in.is-dirty` |
| `inset 3px 0 0 hsl(var(--destructive))` | Has a token: equals --e-rail-strong-destructive | `--e-rail-strong-destructive` | 1 | 1 | page product-prices `.pp-in.err` |


## Gradients and the sidebar light

The satin sheen on the navy menu, the studio ground behind garments, and the rule that keeps glow off buttons.

### Gradient tokens

_Three gradients exist. Nothing else in the admin is a gradient._

| Token | Value | Use |
|---|---|---|
| `--sidebar-sheen` | `linear-gradient(180deg, #131D33 0%, #0E1729 52%, #0C1425 100%)` | the satin sheen: about a third of Silk Facing's glow, sidebar only, never on buttons |
| `--sidebar-sheen-light` | `radial-gradient(closest-side, rgba(130,160,220,.11), transparent)` | the satin sheen: about a third of Silk Facing's glow, sidebar only, never on buttons |
| `--studio` | `radial-gradient(120% 90% at 50% 18%, #f7f8fa 0%, #e3e7ee 80%)` | garment photo ground |


### The sidebar light

_About a third of Silk Facing's glow, sidebar only._

Live specimen on the page: the kit sidebar with one active item.

#### Layer by layer (quoted from base.css)

| Selector | Property | Value | Source |
|---|---|---|---|
| `.sidebar` | `background` | `var(--sidebar-sheen)` | From tokens |
| `.sidebar` | `color` | `hsl(var(--sidebar-foreground))` | From tokens |
| `.sidebar` | `box-shadow` | `var(--e-sidebar-edge)` | From tokens |
| `.sidebar::before` | `background` | `var(--sidebar-sheen-light)` | From tokens |
| `.sb-item` | `color` | `inherit` | From tokens |
| `.sb-item:hover` | `background` | `hsl(var(--sidebar-foreground)/.06)` | From tokens |
| `.sb-item.on` | `background` | `var(--sidebar-item-on)` | From tokens |
| `.sb-item.on` | `color` | `hsl(var(--sidebar-accent-foreground))` | From tokens |
| `.sb-item.on` | `box-shadow` | `var(--e-sidebar-item-on)` | From tokens |
| `.sb-item.on::before` | `background` | `hsl(var(--sidebar-primary))` | From tokens |
| `.sb-item.on::before` | `opacity` | `.8` | From tokens |
| `.sb-item.on svg` | `color` | `var(--sidebar-item-on-icon)` | From tokens |
| `.sb-item.on svg` | `opacity` | `1` | From tokens |
| `.sb-item svg` | `opacity` | `.8` | From tokens |
| `.brand-mark` | `background` | `var(--brand-mark)` | From tokens |
| `.brand-mark` | `color` | `var(--brand-mark-foreground)` | From tokens |
| `.brand-mark` | `box-shadow` | `var(--e-brand-mark)` | From tokens |

**base.css · sidebar rules (quoted)**

```css
.sidebar {
  background: var(--sidebar-sheen);
  color: hsl(var(--sidebar-foreground));
  padding: 14px 10px;
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  position: relative;
  box-shadow: var(--e-sidebar-edge);
}
.sidebar::before {
  content: "";
  position: absolute;
  left: -40px;
  top: -60px;
  width: 300px;
  height: 260px;
  border-radius: 50%;
  background: var(--sidebar-sheen-light);
  pointer-events: none;
}
.sidebar>* {
  position: relative;
}
.title,.page-title,.btn,.sb-item,.sb-group,.sb-brand b,.lbl,.th,.kit-h1,.kit-h2,.spec-h,.sec-title,.tab,.seg-i {
  font-family: var(--font-voice);
  font-stretch: var(--font-stretch-title);
  font-variation-settings: normal;
}
.sb-item {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 36px;
  padding: 0 10px;
  border-radius: var(--r-control);
  font-size: var(--text-card);
  font-weight: var(--w-medium);
  color: inherit;
  text-decoration: none;
  position: relative;
  transition: background-color var(--motion-hover),color var(--motion-hover);
}
.sb-item:hover {
  background: hsl(var(--sidebar-foreground)/.06);
}
.sb-item.on {
  background: var(--sidebar-item-on);
  color: hsl(var(--sidebar-accent-foreground));
  box-shadow: var(--e-sidebar-item-on);
}
.sb-item.on::before {
  content: "";
  position: absolute;
  left: 0;
  top: 9px;
  bottom: 9px;
  width: 2px;
  border-radius: 2px;
  background: hsl(var(--sidebar-primary));
  opacity: .8;
}
.sb-item.on svg {
  color: var(--sidebar-item-on-icon);
  opacity: 1;
}
.sb-item svg {
  opacity: .8;
}
.brand-mark {
  width: 30px;
  height: 30px;
  border-radius: var(--r-control);
  display: grid;
  place-items: center;
  font: var(--w-bold) 12px/1 var(--font-voice);
  background: var(--brand-mark);
  color: var(--brand-mark-foreground);
  box-shadow: var(--e-brand-mark);
}
.sb-foot {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 8px 2px;
  font-size: var(--text-small);
  border-top: 1px solid hsl(var(--sidebar-foreground)/.12);
}
```

> **Flag.** The brand mark gradient, its glow, the active item's white fill and under-light, the active icon tint and the sidebar's inner edge are written as hex and rgba in base.css. They render board 3 exactly; port them as written, or raise tokens (`--sidebar-mark`, `--sidebar-item-on`) in the build lane.


### Studio ground

_The photo ground for garments and cloth._

Live specimen on the page: a thumb on the studio ground.


### Glow rules

**Do**

- Keep the sheen on the sidebar only.
- Use `--studio` behind every garment or cloth photo.

**Don't**

- Put a glow, inner glow or gradient on any button. The primary button is flat `--primary` with `--e-btn`.
- Use a gradient as a page or card background.


### Sidebar light and brand mark (kit v3)

_The exact board-3 values behind the active menu item and the RJ mark, now named._

These stay on the navy sidebar and the brand mark. They are never used on a button.

| Token | Value | Use | Direct refs |
|---|---|---|---|
| `--e-sidebar-edge` | `inset -1px 0 0 rgba(201,206,218,.07)` | the sidebar's inner right edge | 0 |
| `--sidebar-item-on` | `rgba(255,255,255,.07)` | active menu item ground | 0 |
| `--e-sidebar-item-on` | `inset 0 0 0 1px rgba(201,206,218,.09),0 6px 18px -12px rgba(110,140,210,.22)` | active menu item edge + soft glow | 0 |
| `--sidebar-item-on-icon` | `#B7C7EA` | active menu item icon | 0 |
| `--brand-mark` | `linear-gradient(145deg,#EEF1F8,#CBD5EA)` | RJ brand mark tile | 0 |
| `--brand-mark-foreground` | `#0E1729` | RJ initials on the brand mark | 0 |
| `--e-brand-mark` | `0 0 12px -4px rgba(150,175,225,.22)` | brand mark glow | 0 |


### Gradients on surfaces

_Inline gradients outside tokens.css._

| Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|
| `linear-gradient(180deg,hsl(var(--muted)/.55),hsl(var(--background)) 70%)` | No token: gradient written inline | — | 8 | 4 | page content-frame `.wb-site`; page releases `.wb-site`; page website `.wb-site`; +1 more |
| `repeating-linear-gradient(135deg,hsl(var(--warning)/.12) 0 6px,transparent 6px 12px)` | No token: gradient written inline | — | 1 | 0 | family data-display `.dd-fun-r.nw .t` |
| `linear-gradient(90deg,var(--sticky-edge),hsl(var(--foreground)/0))` | No token: gradient written inline | — | 1 | 0 | family data-table `.dt-frozen .fz-last::after` |
| `repeating-linear-gradient(135deg,hsl(var(--border)/.85) 0 1px,transparent 1px 5px),hsl(var(--muted))` | No token: gradient written inline | — | 1 | 0 | family status-feedback `.sf-tile` |


## Motion

The crisp Satin Lapel set: every duration and cubic-bezier, each interaction quoted from the kit, live demos, and reduced motion.

### Easing

_Three curves. The names describe direction, not the CSS keyword._

Plotted on the page: each cubic-bezier with its control points.

| Token | Value | Meaning |
|---|---|---|
| `--ease-standard` | `cubic-bezier(.2,0,0,1)` | Changes in place: hover, press, focus, colour. |
| `--ease-in` | `cubic-bezier(.22,1,.36,1)` | Entering: a soft landing (decelerates). Same curve as `--ease-enter`; prefer that name in new code. |
| `--ease-out` | `cubic-bezier(.4,0,1,1)` | Leaving: accelerates away. Same curve as `--ease-exit`; prefer that name in new code. |
| `--ease-enter` | `var(--ease-in)` | Kit v3 alias of `--ease-in`: the entering curve, named for what it does. |
| `--ease-exit` | `var(--ease-out)` | Kit v3 alias of `--ease-out`: the leaving curve, named for what it does. |
| `--ease-linear` | `linear` | Kit v3: spinners and rotations only. |
| `--ease-blink` | `steps(1)` | Kit v3: the command palette caret blink (a step, not a curve). |

> **Flag.** `--ease-in` is a decelerating curve used for things **entering**, and `--ease-out` an accelerating curve for things **leaving**. Do not map them to Tailwind's `ease-in` / `ease-out` by name: add `transitionTimingFunction` keys `standard`, `enter`, `exit` with these exact cubic-beziers.


### Durations

_Each interaction has its own duration and curve. Click a lane, or play them all._

| Token | Duration | Easing | Resolved (copy into CSS) | Use |
|---|---|---|---|---|
| `--motion-hover` | `120ms` | `--ease-standard` | `120ms cubic-bezier(.2,0,0,1)` | hover: colour, background, border |
| `--motion-press` | `80ms` | `--ease-standard` | `80ms cubic-bezier(.2,0,0,1)` | button press settle |
| `--motion-focus` | `140ms` | `--ease-standard` | `140ms cubic-bezier(.2,0,0,1)` | focus ring fade |
| `--motion-pop-in` | `160ms` | `--ease-in` | `160ms cubic-bezier(.22,1,.36,1)` | menu, popover, tooltip opening |
| `--motion-pop-out` | `110ms` | `--ease-out` | `110ms cubic-bezier(.4,0,1,1)` | menu, popover, tooltip, dialog closing |
| `--motion-sheet-in` | `260ms` | `--ease-in` | `260ms cubic-bezier(.22,1,.36,1)` | side sheet, bottom sheet and dialog opening; scrim in; step change |
| `--motion-sheet-out` | `180ms` | `--ease-out` | `180ms cubic-bezier(.4,0,1,1)` | side sheet and scrim closing |
| `--motion-toast-in` | `200ms` | `--ease-in` | `200ms cubic-bezier(.22,1,.36,1)` | toast entering |
| `--motion-toast-out` | `140ms` | `--ease-out` | `140ms cubic-bezier(.4,0,1,1)` | toast leaving |

Live on the page: one lane per duration token, played with its exact cubic-bezier.


### Dwell, delays and distances

_How long things stay, how long they wait, and how far they move._

| Token | Value | Use | Reduced motion |
|---|---|---|---|
| `--toast-dwell` | `2600ms` | how long a toast stays | unchanged |
| `--toast-dwell-undo` | `5200ms` | an Undo toast stays twice as long (raised by status-feedback) | unchanged |
| `--toast-gap` | `8px` | gap between stacked toasts (raised by status-feedback) | unchanged |
| `--tooltip-delay` | `400ms` | hover delay before a tooltip; keyboard focus shows it at once (raised by overlays + page-help) | `0ms` |
| `--hovercard-delay` | `500ms` | (raised by overlays) | `0ms` |
| `--motion-skeleton` | `1.6s var(--ease-standard) infinite alternate` | skeleton pulse (raised by data-table + cards-gallery) | `none` |
| `--press-shift` | `1px` | button settles 1px on press | `0px` |
| `--pop-shift` | `-4px` | dropdown drops 4px | `0px` |
| `--toast-shift` | `8px` | toast rises 8px | `0px` |
| `--lift-shift` | `-2px` | card hover lift (raised by cards-gallery) | `0px` |


### Per-interaction specs

_What moves, on which token, quoted from the kit CSS._

| Interaction | What happens | Tokens |
|---|---|---|
| Hover | Colour, background and border shift together; nothing moves. | `--motion-focus` `--motion-hover` `--motion-press` |
| Press | The button settles down by --press-shift. | `--press-shift` |
| Focus | The ring fades in on the box-shadow track; the field edge turns --ring. | `--motion-focus` `--motion-hover` |
| Dropdown and menu | Drops --pop-shift with a slight scale while fading in; leaves faster on the out curve. | `--motion-handoff` `--motion-pop-in` `--motion-pop-out` `--pop-shift` |
| Side sheet and scrim | Slides in from just off the right edge while the scrim fades in; a phone bottom sheet rises from below. | `--motion-handoff-sheet` `--motion-sheet-in` `--motion-sheet-out` |
| Dialog | Fades in and settles a hair downward from a slightly smaller scale. | `--motion-handoff` `--motion-pop-out` `--motion-sheet-in` |
| Toast | Rises --toast-shift and fades; dwells --toast-dwell (twice as long with Undo). | `--motion-toast-in` `--motion-toast-out` `--toast-shift` |
| Tooltip | Waits --tooltip-delay on hover, shows at once on keyboard focus. | `--motion-handoff` `--motion-pop-in` `--motion-pop-out` `--tooltip-delay` |
| Step change | The incoming step fades in and settles --toast-shift from the side it came from. | `--motion-sheet-in` |
| Skeleton | One slow opacity pulse on --motion-skeleton; off under reduced motion. | `--motion-skeleton` |
| Card lift | Rises --lift-shift onto --e-card-hover. | `--lift-shift` `--motion-hover` |

Live on the page: hover, press, focus, tooltip, dropdown, toast, side sheet (bottom sheet on phone), card lift, skeleton and step change, all on the kit's own classes.

#### Hover

**Hover · quoted from base.css**

```css
.btn {
  transition: background-color var(--motion-hover),
    border-color var(--motion-hover),
    color var(--motion-hover),
    box-shadow var(--motion-focus),
    transform var(--motion-press),
    filter var(--motion-hover);
}
.btn-primary:hover {
  background: hsl(var(--primary-hover));
}
.btn-secondary:hover {
  background: hsl(var(--muted));
  border-color: hsl(var(--secondary-border-hover));
}
.sb-item {
  color: inherit;
  transition: background-color var(--motion-hover),color var(--motion-hover);
}
```

#### Press

**Press · quoted from base.css**

```css
.btn:active {
  transform: translateY(var(--press-shift));
}
.help-q:active {
  transform: translateY(var(--press-shift));
}
```

#### Focus

**Focus · quoted from base.css**

```css
.field,.input {
  background: hsl(var(--background));
  outline: none;
  transition: border-color var(--motion-hover),box-shadow var(--motion-focus);
}
.field:focus-within,.input:focus {
  border-color: hsl(var(--ring));
  box-shadow: var(--focus-ring);
}
:focus-visible {
  outline: 2px solid hsl(var(--ring));
  outline-offset: 2px;
}
```

#### Dropdown and menu

**Dropdown and menu · quoted from base.css**

```css
.k-pop {
  opacity: 0;
  transform: translateY(var(--pop-shift)) scale(.985);
  visibility: hidden;
  transition: opacity var(--motion-pop-out),
    transform var(--motion-pop-out),
    visibility 0s linear var(--motion-handoff);
}
.open>.k-pop {
  opacity: 1;
  transform: none;
  visibility: visible;
  transition: opacity var(--motion-pop-in),
    transform var(--motion-pop-in),
    visibility 0s;
}
```

#### Side sheet and scrim

**Side sheet and scrim · quoted from base.css**

```css
.k-scrim {
  background: var(--scrim);
  opacity: 0;
  transition: opacity var(--motion-sheet-out);
}
.k-sheet {
  background: hsl(var(--popover));
  box-shadow: var(--e-sheet);
  transform: translateX(104%);
  visibility: hidden;
  transition: transform var(--motion-sheet-out),
    visibility 0s linear var(--motion-handoff-sheet);
}
.open>.k-scrim {
  opacity: 1;
  transition: opacity var(--motion-sheet-in);
}
.open>.k-sheet {
  transform: none;
  visibility: visible;
  transition: transform var(--motion-sheet-in),visibility 0s;
}
.k-sheet.k-bottom {
  box-shadow: var(--e-phone-sheet);
  transform: translateY(104%);
}
```

#### Dialog

**Dialog · quoted from base.css**

```css
.k-dialog {
  background: hsl(var(--popover));
  box-shadow: var(--e-dialog);
  opacity: 0;
  transform: translate(-50%,-48%) scale(.98);
  visibility: hidden;
  transition: opacity var(--motion-pop-out),
    transform var(--motion-pop-out),
    visibility 0s linear var(--motion-handoff);
}
.open>.k-dialog {
  opacity: 1;
  transform: translate(-50%,-50%);
  visibility: visible;
  transition: opacity var(--motion-sheet-in),
    transform var(--motion-sheet-in),
    visibility 0s;
}
```

#### Toast

**Toast · quoted from base.css**

```css
.k-toast {
  opacity: 0;
  transform: translateY(var(--toast-shift));
  transition: opacity var(--motion-toast-out),transform var(--motion-toast-out);
}
.k-toast.show {
  opacity: 1;
  transform: none;
  transition: opacity var(--motion-toast-in),transform var(--motion-toast-in);
}
```

#### Tooltip

**Tooltip · quoted from base.css**

```css
.tip {
  background: hsl(var(--primary));
  color: hsl(var(--primary-foreground));
  opacity: 0;
  transform: translate(-50%,2px);
  visibility: hidden;
  transition: opacity var(--motion-pop-out),
    transform var(--motion-pop-out),
    visibility 0s linear var(--motion-handoff);
}
.tt:hover>.tip,.tt:focus-within>.tip,.tip.show {
  opacity: 1;
  transform: translate(-50%,0);
  visibility: visible;
  transition: opacity var(--motion-pop-in),
    transform var(--motion-pop-in),
    visibility 0s;
  transition-delay: var(--tooltip-delay);
}
.tt:focus-within>.tip,.tip.show {
  transition-delay: 0s;
}
```

#### Step change

**Step change · quoted from families/wizard-stepper/family.css**

```css
.wz-pane.on {
  animation: wz-fwd var(--motion-sheet-in) both;
}
.wz-stage.back .wz-pane.on {
  animation-name: wz-back;
}
@keyframes wz-fwd { from{opacity:0;transform:translateX(var(--toast-shift))}to{opacity:1;transform:none} }
@keyframes wz-back { from{opacity:0;transform:translateX(calc(var(--toast-shift) * -1))}to{opacity:1;transform:none} }
```

#### Skeleton

**Skeleton · quoted from base.css**

```css
.skel {
  background: hsl(var(--foreground)/.075);
  animation: kit-skel var(--motion-skeleton);
}
```

#### Card lift

**Card lift · quoted from base.css**

```css
.lift {
  transition: transform var(--motion-hover),
    box-shadow var(--motion-hover),
    border-color var(--motion-hover);
}
.lift:hover,.lift.is-hover {
  transform: translateY(var(--lift-shift));
  box-shadow: var(--e-card-hover);
}
```


### Reduced motion

_Under prefers-reduced-motion every move becomes instant. The switch above previews it._

| Token | Normal | Reduced |
|---|---|---|
| `--motion-hover` | `120ms var(--ease-standard)` | `1ms linear` |
| `--motion-press` | `80ms var(--ease-standard)` | `1ms linear` |
| `--motion-focus` | `140ms var(--ease-standard)` | `1ms linear` |
| `--motion-pop-in` | `160ms var(--ease-in)` | `1ms linear` |
| `--motion-pop-out` | `110ms var(--ease-out)` | `1ms linear` |
| `--motion-sheet-in` | `260ms var(--ease-in)` | `1ms linear` |
| `--motion-sheet-out` | `180ms var(--ease-out)` | `1ms linear` |
| `--motion-toast-in` | `200ms var(--ease-in)` | `1ms linear` |
| `--motion-toast-out` | `140ms var(--ease-out)` | `1ms linear` |
| `--press-shift` | `1px` | `0px` |
| `--pop-shift` | `-4px` | `0px` |
| `--toast-shift` | `8px` | `0px` |
| `--lift-shift` | `-2px` | `0px` |
| `--tooltip-delay` | `400ms` | `0ms` |
| `--hovercard-delay` | `500ms` | `0ms` |
| `--motion-skeleton` | `1.6s var(--ease-standard) infinite alternate` | `none` |

Other reduced-motion rules in the kit:

| Selector | Reduced | File |
|---|---|---|
| `.spin` | `animation-duration:var(--motion-spin-reduced)` | phase-2/kit/base.css |
| `.sh-pop-s .caret` | `animation:none` | phase-2/kit/families/app-shell/family.css |
| `.sh-bar` | `animation:none;width:100%;opacity:.5` | phase-2/kit/families/app-shell/family.css |
| `.dd-flash` | `animation:none` | phase-2/kit/families/data-display/family.css |


### Keyframes

_Every @keyframes in the kit and on the surfaces, and how it is played._

| Name | Keyframes | Played as | Where |
|---|---|---|---|
| `kit-spin` | `to{transform:rotate(360deg)}` | `kit-spin var(--motion-spin) var(--ease-linear) infinite` | kit:base.css |
| `kit-skel` | `from{opacity:.55}to{opacity:1}` | `kit-skel var(--motion-skeleton)` | kit:base.css |
| `sh-blink` | `50%{opacity:0}` | `sh-blink var(--motion-caret) var(--ease-blink) infinite` | family:app-shell |
| `sh-load` | `0%{transform:translateX(-100%)}100%{transform:translateX(280%)}` | `sh-load var(--motion-loadbar) var(--ease-standard) infinite` | family:app-shell |
| `dd-flash` | `0%{background:hsl(var(--accent))}100%{background:transparent}` | `dd-flash var(--motion-flash) var(--ease-standard)` | family:data-display |
| `sf-slide` | `0%{transform:translateX(-100%)}100%{transform:translateX(280%)}` | `sf-slide var(--motion-indeterminate) var(--ease-standard) infinite` | family:status-feedback |
| `wz-fwd` | `from{opacity:0;transform:translateX(var(--toast-shift))}to{opacity:1;transform:none}` | `wz-fwd var(--motion-sheet-in) both` | family:wizard-stepper |
| `wz-back` | `from{opacity:0;transform:translateX(calc(var(--toast-shift) * -1))}to{opacity:1;transform:none}` | `wz-back` | family:wizard-stepper |
| `bg-flash` | `0%{box-shadow:0 0 0 2px hsl(var(--ring)/.55),var(--e-1)}100%{box-shadow:var(--e-1)}` | `bg-flash 900ms var(--ease-standard)` | page:brand-global-store |
| `wb-rot` | `to{transform:rotate(360deg)}` | `wb-rot 1.2s linear infinite` | page:content-frame, page:releases, page:website +1 |
| `wb-flash` | `0%{background:hsl(var(--accent))}100%{background:transparent}` | `wb-flash 900ms var(--ease-standard)` | page:content-frame, page:releases, page:website +1 |
| `pg-shake` | `0%,100%{transform:none}30%{transform:translateX(-3px)}60%{transform:translateX(3px)}` | `pg-shake 360ms var(--ease-standard)` | page:design-pricing, page:pricing, page:pricing-bulk +6 |
| `gs-flash` | `0%{box-shadow:0 0 0 2px hsl(var(--ring)/.55),var(--e-1)}100%{box-shadow:var(--e-1)}` | `gs-flash 900ms var(--ease-standard)` | page:global-store |
| `hq-pulse` | `from{opacity:1}to{opacity:.45}` | `hq-pulse var(--motion-skeleton)` | page:hq-settings-audit-log, page:hq-settings-users, page:super-api-access +3 |
| `mp-flash` | `0%{box-shadow:0 0 0 2px hsl(var(--ring)/.55),var(--e-1)}100%{box-shadow:var(--e-1)}` | `mp-flash 900ms var(--ease-standard)` | page:products |
| `su-in` | `from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}` | `su-in var(--motion-toast-in)` | page:support, page:support-categories, page:support-templates +2 |


### Animation and delay tokens (kit v3)

_The spinner, flash, shake, rotation, progress sweeps, caret and the visibility hand-off delays._

`--ease-enter` and `--ease-exit` are the clear names for `--ease-in` (entering, decelerates) and `--ease-out` (leaving, accelerates). They are the opposite of CSS's `ease-in` / `ease-out` keywords, which is why the aliases exist. The originals stay. The animation tokens are not collapsed by reduced motion here: each rule handles it (the spinner slows to `--motion-spin-reduced`, the flash and shake switch off).

| Token | Value | Use | Direct refs |
|---|---|---|---|
| `--ease-enter` | `var(--ease-in)` | clearer name: the ENTERING curve (decelerates into place). --ease-in stays for compatibility | 0 |
| `--ease-exit` | `var(--ease-out)` | clearer name: the LEAVING curve (accelerates away). --ease-out stays for compatibility | 0 |
| `--ease-linear` | `linear` | spinners and rotations only | 0 |
| `--ease-blink` | `steps(1)` | text caret blink | 0 |
| `--motion-spin` | `.8s` | spinner turn | 0 |
| `--motion-spin-reduced` | `2.4s` | spinner turn under prefers-reduced-motion | 0 |
| `--motion-flash` | `900ms` | row / card "just changed" flash | 0 |
| `--motion-shake` | `360ms` | shake animation on the surfaces (pg-shake) | 0 |
| `--motion-rotate` | `1.2s` | rotation animation on the surfaces (wb-rot) | 0 |
| `--motion-indeterminate` | `1.5s` | indeterminate progress bar sweep | 0 |
| `--motion-loadbar` | `1.4s` | top load bar sweep | 0 |
| `--motion-caret` | `1.1s` | command palette caret blink | 0 |
| `--motion-handoff` | `.2s` | visibility hand-off after a menu, tooltip or dialog closes | 0 |
| `--motion-handoff-sheet` | `.3s` | visibility hand-off after a side sheet closes | 0 |
| `--skeleton-stagger` | `.12s` | delay step between skeleton rows | 0 |


### Motion with no token

_Durations, easings and script timers written as raw values._

| Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|
| `60ms` | No token: a timer in script with no token | — | 57 | 16 | page analytics-funnel `setTimeout`; page analytics-revenue `setTimeout`; page analytics-system-status `setTimeout`; +13 more |
| `1300ms` | No token: a timer in script with no token | — | 25 | 25 | page brand-global-store `setTimeout`; page content-frame `setTimeout`; page design-pricing `setTimeout`; +22 more |
| `30ms` | No token: a timer in script with no token | — | 13 | 13 | page analytics-funnel `setTimeout`; page analytics-revenue `setTimeout`; page analytics-system-status `setTimeout`; +10 more |
| `700ms` | No token: a timer in script with no token | — | 13 | 13 | page campaign-detail `setTimeout`; page campaigns `setTimeout`; page campaigns-email `setTimeout`; +10 more |
| `linear` | No token: easing written inline | — | 6 | 5 | page content-frame `.wb-sync svg`; page releases `.wb-sync svg`; page super-api-access `.ak-fill`; +2 more |
| `320ms` | No token: a timer in script with no token | — | 5 | 5 | page order-detail `setTimeout`; page order-envelope `setTimeout`; page orders `setTimeout`; +2 more |
| `50ms` | No token: a timer in script with no token | — | 4 | 4 | page content-frame `setTimeout`; page releases `setTimeout`; page website `setTimeout`; +1 more |
| `.2s` | No token: delay | — | 2 | 1 | family status-feedback `.sf-skrow:nth-child(3n) .sf-sk`; page super-api-access `.ak-rowmenu` |
| `.15s` | No token: transition | — | 1 | 1 | page super-api-access `.ak-fill` |
| `650ms` | No token: a timer in script with no token | — | 1 | 0 | family wizard-stepper `setTimeout` |
| `30000ms` | No token: a timer in script with no token | — | 1 | 1 | page super-api-access `setTimeout` |
| `1000ms` | No token: a timer in script with no token | — | 1 | 1 | page super-cloths `setTimeout` |
| `300ms` | Has a token: same length as --motion-handoff-sheet | `--motion-handoff-sheet` | 57 | 57 | page appointments `setTimeout`; page brand-global-store `setTimeout`; page campaign-detail `setTimeout`; +54 more |
| `80ms` | Has a token: same length as --motion-press | `--motion-press` | 39 | 32 | page appointments `setTimeout`; page brand-global-store `setTimeout`; page campaign-detail `setTimeout`; +29 more |
| `900ms` | Has a token: same length as --motion-flash | `--motion-flash` | 20 | 20 | page design-pricing `setTimeout`; page pricing `setTimeout`; page pricing-bulk `setTimeout`; +17 more |
| `360ms` | Has a token: same length as --motion-shake (animation) | `--motion-shake` | 9 | 9 | page design-pricing `.pg-shake`; page pricing `.pg-shake`; page pricing-bulk `.pg-shake`; +6 more |
| `1100ms` | Has a token: same length as --motion-caret | `--motion-caret` | 9 | 9 | page analytics-funnel `setTimeout`; page analytics-revenue `setTimeout`; page analytics-system-status `setTimeout`; +6 more |
| `1200ms` | Has a token: same length as --motion-rotate | `--motion-rotate` | 9 | 8 | page content-frame `setTimeout`; page create-product `setTimeout`; page product-prices `setTimeout`; +5 more |
| `1600ms` | Has a token: same length as --motion-skeleton | `--motion-skeleton` | 9 | 9 | page design-pricing `setTimeout`; page pricing `setTimeout`; page pricing-bulk `setTimeout`; +6 more |
| `500ms` | Has a token: same length as --hovercard-delay | `--hovercard-delay` | 9 | 9 | page design-pricing `setTimeout`; page pricing `setTimeout`; page pricing-bulk `setTimeout`; +6 more |
| `900ms` | Has a token: same length as --motion-flash (animation) | `--motion-flash` | 7 | 7 | page brand-global-store `.bg-card.is-flash`; page content-frame `.wb-sectbl tr.is-flash td`; page global-store `.gs-card.is-flash`; +4 more |
| `400ms` | Has a token: same length as --tooltip-delay | `--tooltip-delay` | 6 | 6 | page analytics-funnel `setTimeout`; page analytics-revenue `setTimeout`; page analytics-system-status `setTimeout`; +3 more |
| `1.2s` | Has a token: same length as --motion-rotate (animation) | `--motion-rotate` | 5 | 5 | page content-frame `.wb-sync svg`; page releases `.wb-sync svg`; page super-api-access `.ak-hold.is-holding .ak-fill`; +2 more |
| `1500ms` | Has a token: same length as --motion-indeterminate | `--motion-indeterminate` | 4 | 4 | page content-frame `setTimeout`; page releases `setTimeout`; page website `setTimeout`; +1 more |
| `120ms` | Has a token: same length as --motion-hover | `--motion-hover` | 1 | 0 | family form-controls `setTimeout` |
| `1400ms` | Has a token: same length as --motion-loadbar | `--motion-loadbar` | 1 | 1 | page global-store `setTimeout` |
| `800ms` | Has a token: same length as --motion-spin | `--motion-spin` | 1 | 1 | page super-mcp-connect `setTimeout` |


## Focus ring

One ring colour for every focusable thing: a `2px solid hsl(var(--ring))` outline for buttons and links, and `--focus-ring` for fields.

### Focus tokens

| Token | Value | Resolves to | Use |
|---|---|---|---|
| `--ring` | `221 45% 45%` | — | focus |
| `--focus-ring` | `0 0 0 1px hsl(var(--ring)), 0 0 0 4px hsl(var(--ring) / .14)` | `0 0 0 1px hsl(221 45% 45%), 0 0 0 4px hsl(221 45% 45% / .14)` | field and control focus: a 1px edge plus a soft halo |
| `--sidebar-ring` | `221 54% 75%` | — | focus ring on the navy sidebar |


### Focus states

_Rings show for the keyboard (:focus-visible), not for the mouse._

Live specimen on the page: focused button, field, checkbox and switch.

> **Flag.** The global outline is `2px solid hsl(var(--ring))` with offset `2px`; neither width nor offset is a token. Keep them as written.


### Every focus rule in base.css

_Quoted, so each control's treatment is exact._

| Selector | Declarations |
|---|---|
| `:focus-visible` | `outline:2px solid hsl(var(--ring));outline-offset:2px` |
| `.is-focus` | `outline:2px solid hsl(var(--ring));outline-offset:2px` |
| `.field.is-focus,.input.is-focus` | `outline:0;border-color:hsl(var(--ring));box-shadow:var(--focus-ring)` |
| `.field:focus-within,.input:focus` | `border-color:hsl(var(--ring));box-shadow:var(--focus-ring)` |
| `.cb:focus-visible,.cb.is-focus` | `outline:0;box-shadow:var(--focus-ring)` |
| `input[type=radio].cb:checked:focus-visible,input[type=radio].cb.is-focus:checked` | `box-shadow:inset 0 0 0 4px hsl(var(--primary)),var(--focus-ring)` |
| `.switch.is-focus,.switch:focus-visible` | `outline:2px solid hsl(var(--ring));outline-offset:2px` |
| `.seg-i.is-focus,.seg>button:focus-visible` | `outline:2px solid hsl(var(--ring));outline-offset:2px` |
| `.mi:focus-visible,.mi.is-focus` | `outline:0;background:hsl(var(--accent));box-shadow:inset 0 0 0 1px hsl(var(--ring)/.55)` |
| `.tt:hover>.tip,.tt:focus-within>.tip,.tip.show` | `opacity:1;transform:translate(-50%,0);visibility:visible; transition:opacity var(--motion-pop-in),transform var(--motion-pop-in),visibility 0s;transition-delay:var(--tooltip-delay)` |
| `.tt:focus-within>.tip,.tip.show` | `transition-delay:0s` |
| `.tt.end:hover>.tip,.tt.end:focus-within>.tip,.tt.end>.tip.show` | `transform:none` |
| `.tabs .tab:focus-visible` | `outline:2px solid hsl(var(--ring));outline-offset:2px;border-radius:var(--r-chip)` |
| `.k-li:focus-visible` | `outline:2px solid hsl(var(--ring));outline-offset:-2px` |


### Keyboard rules

- Tab order follows the reading order; nothing gets a positive `tabindex`.
- An opened sheet or dialog takes focus (`[data-k-autofocus]` first), traps Tab, and returns focus to its opener on close (kit.js).
- Escape closes the top menu or layer first and returns focus.
- Arrow keys move through menu items, options and tabs.
- On the sidebar the ring uses `--sidebar-ring` so it stays visible on navy.


## Iconography

Lucide only (lucide-react@0.469.0), on a 24px grid at a 1.75 stroke, in currentColor. 54 icons named for this admin.

### Rules

- One set: **Lucide**, `lucide-react@0.469.0`, the version in the rj-dtc workspace. The hand-drawn `icons.py` set is retired.
- 24px grid, `stroke-width` 1.75, round caps and joins, `fill="none"`, `stroke="currentColor"`.
- 16px by default; 14px in chips and menus.
- An icon never stands alone: icon-only buttons carry an `aria-label` and a tooltip; status icons sit beside their word.

**React**

```tsx
import { LayoutDashboard } from 'lucide-react';

// lucide-react@0.469.0: 24px grid, strokeWidth 1.75, currentColor
<LayoutDashboard aria-hidden size={16} strokeWidth={1.75} />  // default
<LayoutDashboard aria-hidden size={14} strokeWidth={1.75} />  // chips and menus
```

Specimen on the page: the sizes in use.


### Sizes and strokes on the surfaces

_Counted across the 64 surface folders (desktop and phone)._

| Rendered size | Icons |
|---|---|
| `16px` | 3158 |
| `15px` | 2281 |
| `14px` | 1896 |
| `12px` | 429 |
| `18px` | 384 |
| `20px` | 237 |
| `13px` | 162 |
| `22px` | 71 |

| Stroke width | Icons |
|---|---|
| `1.75` | 8670 |
| `1.2` | 1 |

| Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|
| `15px` | Has a token: off the contract sizes (16 / 14px); named --icon-15 in kit v3 | `--icon-15` | 2092 | 52 | page analytics-funnel `svg`; page analytics-revenue `svg`; page analytics-system-status `svg`; +49 more |
| `12px` | Has a token: off the contract sizes (16 / 14px); named --icon-12 in kit v3 | `--icon-12` | 423 | 53 | page analytics-funnel `svg`; page analytics-revenue `svg`; page analytics-system-status `svg`; +50 more |
| `18px` | Has a token: off the contract sizes (16 / 14px); named --icon-18 in kit v3 | `--icon-18` | 384 | 51 | page analytics-funnel `svg`; page analytics-revenue `svg`; page analytics-system-status `svg`; +48 more |
| `20px` | Has a token: off the contract sizes (16 / 14px); named --icon-20 in kit v3 | `--icon-20` | 237 | 52 | page analytics-funnel `svg`; page analytics-revenue `svg`; page analytics-system-status `svg`; +49 more |
| `13px` | Has a token: off the contract sizes (16 / 14px); named --icon-13 in kit v3 | `--icon-13` | 162 | 13 | page campaign-detail `svg`; page order-detail `svg`; page order-envelope `svg`; +10 more |
| `22px` | Has a token: off the contract sizes (16 / 14px); named --icon-22 in kit v3 | `--icon-22` | 71 | 15 | page appointments `svg`; page customer-detail `svg`; page customers `svg`; +12 more |
| `28px` | Has a token: off the contract sizes (16 / 14px); named --icon-28 in kit v3 | `--icon-28` | 20 | 6 | page design-pricing `svg`; page pricing `svg`; page pricing-bulk `svg`; +3 more |
| `19px` | Has a token: off the contract sizes (16 / 14px); named --icon-19 in kit v3 | `--icon-19` | 6 | 6 | page analytics-funnel `svg`; page analytics-revenue `svg`; page analytics-system-status `svg`; +3 more |

> **Flag.** No token names the icon sizes or the stroke. The contract fixes them (16 / 14px, 1.75); keep them as constants in one icon wrapper.


### Icon list

_The icons this admin uses, by area. Copy gives the lucide-react component name._

#### Navigation (14)

| Icon | lucide-react |
|---|---|
| Dashboard | `LayoutDashboard` (`layout-dashboard`) |
| Products | `Shirt` (`shirt`) |
| Global Store | `Layers` (`layers`) |
| Cloths | `SwatchBook` (`swatch-book`) |
| Brands | `Store` (`store`) |
| Orders | `ShoppingBag` (`shopping-bag`) |
| Customers | `Users` (`users`) |
| Website | `Globe` (`globe`) |
| Pricing | `PoundSterling` (`pound-sterling`) |
| Payments | `CreditCard` (`credit-card`) |
| API keys | `KeyRound` (`key-round`) |
| Integrations | `Plug` (`plug`) |
| Audit log | `ScrollText` (`scroll-text`) |
| Settings | `Settings` (`settings`) |

#### Actions (12)

| Icon | lucide-react |
|---|---|
| Add | `Plus` (`plus`) |
| Edit | `Pencil` (`pencil`) |
| Duplicate | `Copy` (`copy`) |
| Delete | `Trash2` (`trash-2`) |
| Retire | `Archive` (`archive`) |
| Publish | `Send` (`send`) |
| Sync | `RefreshCw` (`refresh-cw`) |
| Upload | `Upload` (`upload`) |
| Export | `Download` (`download`) |
| Open in new tab | `ExternalLink` (`external-link`) |
| Copy link | `Link` (`link`) |
| Revert | `Undo2` (`undo-2`) |

#### Controls (17)

| Icon | lucide-react |
|---|---|
| Search | `Search` (`search`) |
| Open menu | `ChevronDown` (`chevron-down`) |
| Go to | `ChevronRight` (`chevron-right`) |
| Select | `ChevronsUpDown` (`chevrons-up-down`) |
| Sort | `ArrowUpDown` (`arrow-up-down`) |
| Filter | `ListFilter` (`list-filter`) |
| View options | `SlidersHorizontal` (`sliders-horizontal`) |
| More actions | `Ellipsis` (`ellipsis`) |
| Close | `X` (`x`) |
| Selected | `Check` (`check`) |
| Reorder | `GripVertical` (`grip-vertical`) |
| Collapse sidebar | `PanelLeft` (`panel-left`) |
| Command palette | `Command` (`command`) |
| Date | `Calendar` (`calendar`) |
| Photo | `Image` (`image`) |
| Show | `Eye` (`eye`) |
| Hide | `EyeOff` (`eye-off`) |

#### Status and help (11)

| Icon | lucide-react |
|---|---|
| Info | `Info` (`info`) |
| Warning | `TriangleAlert` (`triangle-alert`) |
| Done | `CircleCheck` (`circle-check`) |
| Failed | `CircleX` (`circle-x`) |
| Help | `CircleHelp` (`circle-help`) |
| Locked | `Lock` (`lock`) |
| Not wired | `Unlink` (`unlink`) |
| Loading | `LoaderCircle` (`loader-circle`) |
| History | `History` (`history`) |
| Notifications | `Bell` (`bell`) |
| Sign out | `LogOut` (`log-out`) |


### Icon size tokens (kit v3)

_Every icon size drawn on the 64 surfaces, named._

In React, pass the token's pixel value as the Lucide `size` prop, or size the svg with `width:var(--icon-16);height:var(--icon-16)`. The contract sizes are `--icon-16` (default), `--icon-14` (chips, menus, small buttons) and `--icon-20` (empty states).

| Token | Value | Use | Direct refs |
|---|---|---|---|
| `--icon-12` | `12px` | surface literal (svg width=12) | 0 |
| `--icon-13` | `13px` | surface literal (svg width=13) | 0 |
| `--icon-14` | `14px` | contract size inside chips, menus and small buttons | 0 |
| `--icon-15` | `15px` | surface literal (svg width=15), the most used off-contract size | 0 |
| `--icon-16` | `16px` | contract default | 0 |
| `--icon-18` | `18px` | surface literal (svg width=18) | 0 |
| `--icon-19` | `19px` | surface literal (svg width=19) | 0 |
| `--icon-20` | `20px` | contract size in empty states | 0 |
| `--icon-22` | `22px` | surface literal (svg width=22) | 0 |
| `--icon-28` | `28px` | surface literal (svg width=28) | 0 |


## Z-index

8 named layers. A guard dialog sits above an open sheet, and a toast above everything.

### Stacking scale

_Lowest first. Every overlay reads its level from this scale._

| Token | Value | Layer | Direct refs |
|---|---|---|---|
| `--z-sticky` | `3` | sticky header, frozen column | 63 |
| `--z-pop` | `20` | menu, popover, select | 1 |
| `--z-tip` | `25` | tooltip | 0 |
| `--z-scrim` | `30` | scrim under a sheet | 64 |
| `--z-sheet` | `31` | side sheet, dialog | 0 |
| `--z-guard-scrim` | `40` | scrim under a guard dialog | 0 |
| `--z-guard` | `41` | guard dialog above an open sheet | 0 |
| `--z-toast` | `50` | toast, always on top | 64 |

Diagram on the page: the eight layers stacked.


### Derived levels on surfaces

_Sticky bars stack above sticky headers by multiplying --z-sticky._

Surfaces write `calc(var(--z-sticky) * 2)` for a sticky action or save bar and `calc(var(--z-sticky) * 3)` for a bar above that. Name them in the app (`--z-sticky-bar`, `--z-sticky-top`) rather than repeating the arithmetic.

| Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|
| `2` | No token: local stacking level | — | 20 | 16 | base.css `.topbar`; family cards-gallery `.cg-media>.chip`; family data-table `.dt-frozen .fz`; +16 more |
| `4` | No token: local stacking level | — | 6 | 1 | family data-display `.dd-tip`; family data-table `.dt-frozen th.fz`; family overlays `.k-sheet.ov-trap::after`; +2 more |
| `1` | No token: local stacking level | — | 5 | 0 | family data-table `.dt-li-r`; family form-controls `.fc-cp-tbl th` |
| `5` | No token: local stacking level | — | 4 | 0 | base.css `.tabbar`; family cards-gallery `.cg-card:has(.open)`; family page-help `.ph-guide`; +1 more |
| `6` | No token: local stacking level | — | 1 | 0 | family data-table `.dt-mbulk` |
| `calc(var(--z-sticky) * 2)` | Derived: a level computed from a token, with no name of its own | `--z-sticky` | 131 | 63 | base.css `.k-actbar`; page analytics-funnel `.kit-m .pg-actbar`; page analytics-revenue `.kit-m .pg-actbar`; +61 more |
| `calc(var(--z-sticky) * 3)` | Derived: a level computed from a token, with no name of its own | `--z-sticky` | 126 | 63 | page analytics-funnel `.pg-top`; page analytics-revenue `.pg-top`; page analytics-system-status `.pg-top`; +60 more |
| `3` | Has a token: equals --z-sticky | `--z-sticky` | 5 | 2 | family cards-gallery `.cg-tick`; family data-table `.dt th`; family overlays `.ov-fo-n`; +2 more |
| `calc(var(--z-sheet) + 1)` | Derived: a level computed from a token, with no name of its own | `--z-sheet` | 1 | 0 | family page-help `.ph-stage>.topbar,.ph-stage>.ph-st-bar,.ph-st-bar` |


## Breakpoints and surface pairs

There are no breakpoints. Desktop and phone are two dedicated compositions on two endpoints, sharing tokens and data, never a layout.

### Two viewports

_Each surface ships at both._

| Token | Value | Composition |
|---|---|---|
| `--vp-desktop` | `1440px` | Desktop composition, `/` |
| `--vp-phone` | `390px` | Phone composition, `/m/` |


### The surface-pair rule

_Founder rule, 2026-09-21: dedicated endpoints._

- Every surface ships as `/` (desktop, 1440) and `/m/` (phone, 390). In Next.js the device is chosen server-side and the chosen tree is served at the same URL (a rewrite, never a redirect).
- No width or height `@media`, no `@container` size query, no user-agent sniffing in the page. `prefers-reduced-motion` and `print` are allowed.
- The phone is its own composition: tables become lists (`.k-li`), side sheets become bottom sheets (`.k-bottom`), the sidebar becomes `.p-bar` + `.tabbar`, and the primary action sits in the bottom 40% (`.k-actbar`).
- Phone tap targets are at least `--control-h-m` (44px); primary and sheet buttons `--control-h-m-lg` (48px); text fields `--text-input-m` (16px) so iOS never zooms.

**The gate**

```sh
python3 scripts/check-surface-pair.py <built-dir>   # PASS required before deploy
```


### Phone overrides in base.css

_Every .kit-m rule the kit applies to phone compositions, quoted._

| Selector | Declarations |
|---|---|
| `.kit-m .btn` | `height:var(--control-h-m)` |
| `.kit-m .btn.sm` | `height:var(--control-h-m)` |
| `.kit-m .btn.lg,.kit-m .p-btns .btn` | `height:var(--control-h-m-lg);font-size:var(--text-btn-lg)` |
| `.kit-m .field,.kit-m .input` | `height:var(--control-h-m)` |
| `.kit-m .icon-btn` | `width:var(--control-h-m);height:var(--control-h-m)` |
| `.kit-m .field.auto` | `height:auto;min-height:var(--control-h-m)` |
| `.kit-m input:not([type=checkbox]):not([type=radio]):not([type=range]),.kit-m textarea,.kit-m select` | `font-size:var(--text-input-m)` |
| `.kit-m .cb` | `width:20px;height:20px;background-size:16px 16px` |
| `.kit-m .switch` | `width:44px;height:26px` |
| `.kit-m .switch::after` | `width:22px;height:22px` |
| `.kit-m .switch[aria-checked=true]::after` | `transform:translateX(18px)` |
| `.kit-m .seg>button,.kit-m .seg-i` | `height:38px` |
| `.kit-m .mi` | `height:var(--control-h-m);font-size:var(--text-read)` |
| `.kit-m .toast .act .btn` | `height:var(--control-h-m);padding:0 var(--s-3);font-size:13.5px` |
| `.kit-m .toast .act .icon-btn` | `width:var(--control-h-m);height:var(--control-h-m)` |
| `.kit-m .help-q` | `width:36px;height:36px` |
| `.kit-m .help-q::after` | `inset:-4px` |
| `.kit-m .tabs` | `gap:var(--s-4);overflow-x:auto;scrollbar-width:none;margin:0 calc(var(--s-4) * -1);padding:0 var(--s-4)` |
| `.kit-m .tabs::-webkit-scrollbar` | `display:none` |
| `.kit-m .tabs .tab` | `height:var(--tab-h-m);flex:none` |
| `.kit-m .k-savebar` | `bottom:var(--tabbar-h);flex-wrap:wrap;margin:var(--s-6) calc(var(--s-4) * -1) 0;padding:var(--s-3) var(--s-4);border-width:1px 0 0;border-radius:0;box-shadow:none; background:hsl(var(--background)/.96);backdrop-filter:blur(var(--actbar-blur))` |
| `.kit-m .k-savebar .r` | `width:100%;margin-left:0` |
| `.kit-m .k-savebar .r .btn` | `flex:1;height:var(--control-h-m-lg);font-size:var(--text-btn-lg)` |
| `.kit-m .k-rec` | `flex-wrap:wrap;gap:var(--s-3)` |
| `.kit-m .k-rec>.avatar` | `width:40px;height:40px` |
| `.kit-m .k-rec-a` | `width:100%;margin-left:0` |
| `.kit-m .k-rec-a .btn` | `flex:1` |
| `.kit-m .k-kv>div` | `grid-template-columns:var(--kv-label-m) minmax(0,1fr)` |


## Untokenised values

Every literal value found in the kit and on the 64 surfaces, reconciled to the tokens. What has a token should use it; what has none is flagged.

### Summary

_Scanned: base.css, the 11 family.css files, and every surface folder's own CSS and JS (the _kit mirrors are not re-counted)._

| Kind | Distinct values | No token | Has a token | Derived | Uses |
|---|---|---|---|---|---|
| tracking | 8 | 5 | 3 | 0 | 87 |
| font size | 23 | 0 | 23 | 0 | 221 |
| size | 16 | 0 | 16 | 0 | 1087 |
| off-grid spacing | 12 | 12 | 0 | 0 | 474 |
| radius | 3 | 2 | 1 | 0 | 13 |
| z-index | 9 | 5 | 1 | 3 | 299 |
| colour | 1 | 1 | 0 | 0 | 1 |
| shadow | 45 | 40 | 5 | 0 | 119 |
| gradient | 4 | 4 | 0 | 0 | 11 |
| duration | 5 | 2 | 3 | 0 | 24 |
| blur | 2 | 0 | 2 | 0 | 138 |
| easing | 1 | 1 | 0 | 0 | 6 |
| script timing | 21 | 9 | 12 | 0 | 285 |
| icon size | 8 | 0 | 8 | 0 | 3395 |

| Source | Distinct values | No token | Uses |
|---|---|---|---|
| Kit base.css | 43 | 25 | 92 |
| Kit families (family.css) | 72 | 47 | 360 |
| Surface folders (page-local CSS and JS) | 99 | 33 | 5708 |


### On the surfaces

_Values in phase-3/pages/*/ local CSS and JS._

| Kind | Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|---|
| off-grid spacing | `6px` | No token: not on the 4px grid | — | 204 | 37 | page analytics-funnel `.pg-btn-n`; page analytics-revenue `.pg-btn-n`; page analytics-system-status `.pg-btn-n`; +34 more |
| off-grid spacing | `10px` | No token: not on the 4px grid | — | 77 | 63 | page analytics-funnel `.pg-pcrumb .pg-proto>.k-anchor>button`; page analytics-revenue `.pg-pcrumb .pg-proto>.k-anchor>button`; page analytics-system-status `.pg-pcrumb .pg-proto>.k-anchor>button`; +60 more |
| script timing | `60ms` | No token: a timer in script with no token | — | 57 | 16 | page analytics-funnel `setTimeout`; page analytics-revenue `setTimeout`; page analytics-system-status `setTimeout`; +13 more |
| tracking | `-.01em` | No token: tracking off the scale | — | 30 | 22 | page appointments `.cx-score b`; page campaign-detail `.pg-mail-b h3`; page campaigns `.pg-mail-b h3`; +19 more |
| script timing | `1300ms` | No token: a timer in script with no token | — | 25 | 25 | page brand-global-store `setTimeout`; page content-frame `setTimeout`; page design-pricing `setTimeout`; +22 more |
| z-index | `2` | No token: local stacking level | — | 16 | 16 | page appointments `.cx-photo .cx-plbl`; page brand-global-store `.bg-card .cg-media>span:first-child`; page content-frame `.wb-sitenav .wb-drop`; +13 more |
| script timing | `30ms` | No token: a timer in script with no token | — | 13 | 13 | page analytics-funnel `setTimeout`; page analytics-revenue `setTimeout`; page analytics-system-status `setTimeout`; +10 more |
| script timing | `700ms` | No token: a timer in script with no token | — | 13 | 13 | page campaign-detail `setTimeout`; page campaigns `setTimeout`; page campaigns-email `setTimeout`; +10 more |
| shadow | `inset 0 0 0 1px hsl(var(--foreground)/.12)` | No token: colour from tokens, geometry hand-set | — | 9 | 9 | page design-pricing `.pg-gsw`; page pricing `.pg-gsw`; page pricing-bulk `.pg-gsw`; +6 more |
| gradient | `linear-gradient(180deg,hsl(var(--muted)/.55),hsl(var(--background)) 70%)` | No token: gradient written inline | — | 8 | 4 | page content-frame `.wb-site`; page releases `.wb-site`; page website `.wb-site`; +1 more |
| off-grid spacing | `5px` | No token: not on the 4px grid | — | 7 | 7 | page campaign-detail `.pg-used`; page campaigns `.pg-used`; page campaigns-email `.pg-used`; +4 more |
| shadow | `0 0 0 1px hsl(var(--primary))` | No token: colour from tokens, geometry hand-set | — | 6 | 6 | page brand-global-store `.bg-th.on`; page content-frame `.wb-ptile.on`; page global-store-editor `.ge-opt.on`; +3 more |
| shadow | `0 0 0 2px hsl(var(--ring)/.35)` | No token: colour from tokens, geometry hand-set | — | 6 | 6 | page campaign-detail `.pg-chi.moved`; page campaigns `.pg-chi.moved`; page campaigns-email `.pg-chi.moved`; +3 more |
| easing | `linear` | No token: easing written inline | — | 6 | 5 | page content-frame `.wb-sync svg`; page releases `.wb-sync svg`; page super-api-access `.ak-fill`; +2 more |
| radius | `2px` | No token: radius off the scale | — | 5 | 5 | page order-detail `.od-legend i`; page order-envelope `.od-legend i`; page orders `.od-legend i`; +2 more |
| shadow | `0 0 0 1px hsl(var(--primary)/.25)` | No token: colour from tokens, geometry hand-set | — | 5 | 5 | page support `.su-ladder li.cur`; page support-categories `.su-ladder li.cur`; page support-templates `.su-ladder li.cur`; +2 more |
| shadow | `0 -6px 16px -12px hsl(var(--foreground)/.25)` | No token: colour from tokens, geometry hand-set | — | 5 | 5 | page support `.kit-m .su-compose`; page support-categories `.kit-m .su-compose`; page support-templates `.kit-m .su-compose`; +2 more |
| script timing | `320ms` | No token: a timer in script with no token | — | 5 | 5 | page order-detail `setTimeout`; page order-envelope `setTimeout`; page orders `setTimeout`; +2 more |
| shadow | `inset 0 0 0 1px hsl(var(--foreground)/.08)` | No token: colour from tokens, geometry hand-set | — | 4 | 4 | page brand-global-store `.bp-sw`; page create-product `.bp-sw`; page product-prices `.bp-sw`; +1 more |
| shadow | `inset 0 0 0 1px hsl(var(--primary))` | No token: colour from tokens, geometry hand-set | — | 4 | 4 | page brand-global-store `.bp-fsel.on`; page create-product `.bp-fsel.on`; page product-prices `.bp-fsel.on`; +1 more |
| tracking | `-.03em` | No token: tracking off the scale | — | 4 | 4 | page content-frame `.wb-big`; page releases `.wb-big`; page website `.wb-big`; +1 more |
| script timing | `50ms` | No token: a timer in script with no token | — | 4 | 4 | page content-frame `setTimeout`; page releases `setTimeout`; page website `setTimeout`; +1 more |
| shadow | `0 0 0 1px hsl(var(--primary)),var(--e-1)` | No token: colour from tokens, geometry hand-set | — | 3 | 3 | page create-product `.cp-pick[aria-selected=true]`; page global-store `.gs-card.is-sel`; page products `.mp-card.is-sel` |
| shadow | `0 0 0 2px hsl(var(--ring)/.55),var(--e-1)` | No token: colour from tokens, geometry hand-set | — | 3 | 3 | page brand-global-store `@keyframes bg-flash 0%`; page global-store `@keyframes gs-flash 0%`; page products `@keyframes mp-flash 0%` |
| z-index | `4` | No token: local stacking level | — | 1 | 1 | page super-brands `.br-tbl th.br-fzr` |
| duration | `.2s` | No token: delay | — | 1 | 1 | page super-api-access `.ak-rowmenu` |
| shadow | `0 0 0 1px hsl(var(--primary)),var(--e-card-hover)` | No token: colour from tokens, geometry hand-set | — | 1 | 1 | page global-store `.gs-card.is-sel:hover` |
| shadow | `inset 0 0 0 1px hsl(var(--foreground)/.1),var(--e-1)` | No token: colour from tokens, geometry hand-set | — | 1 | 1 | page global-store-editor `.ge-swatch` |
| duration | `.15s` | No token: transition | — | 1 | 1 | page super-api-access `.ak-fill` |
| shadow | `-1px 0 0 hsl(var(--border))` | No token: colour from tokens, geometry hand-set | — | 1 | 1 | page super-brands `.br-tbl .br-fzr` |
| shadow | `0 0 0 1px hsl(var(--warning)/.45)` | No token: colour from tokens, geometry hand-set | — | 1 | 1 | page super-mcp-connect `.mc-write` |
| script timing | `30000ms` | No token: a timer in script with no token | — | 1 | 1 | page super-api-access `setTimeout` |
| script timing | `1000ms` | No token: a timer in script with no token | — | 1 | 1 | page super-cloths `setTimeout` |
| icon size | `15px` | Has a token: off the contract sizes (16 / 14px); named --icon-15 in kit v3 | `--icon-15` | 2092 | 52 | page analytics-funnel `svg`; page analytics-revenue `svg`; page analytics-system-status `svg`; +49 more |
| icon size | `12px` | Has a token: off the contract sizes (16 / 14px); named --icon-12 in kit v3 | `--icon-12` | 423 | 53 | page analytics-funnel `svg`; page analytics-revenue `svg`; page analytics-system-status `svg`; +50 more |
| icon size | `18px` | Has a token: off the contract sizes (16 / 14px); named --icon-18 in kit v3 | `--icon-18` | 384 | 51 | page analytics-funnel `svg`; page analytics-revenue `svg`; page analytics-system-status `svg`; +48 more |
| icon size | `20px` | Has a token: off the contract sizes (16 / 14px); named --icon-20 in kit v3 | `--icon-20` | 237 | 52 | page analytics-funnel `svg`; page analytics-revenue `svg`; page analytics-system-status `svg`; +49 more |
| size | `28px` | Has a token: same px as --control-h-xs | `--control-h-xs` | 175 | 64 | page analytics-funnel `.pg-pcrumb .pg-proto>.k-anchor>button`; page analytics-revenue `.pg-pcrumb .pg-proto>.k-anchor>button`; page analytics-system-status `.pg-pcrumb .pg-proto>.k-anchor>button`; +61 more |
| icon size | `13px` | Has a token: off the contract sizes (16 / 14px); named --icon-13 in kit v3 | `--icon-13` | 162 | 13 | page campaign-detail `svg`; page order-detail `svg`; page order-envelope `svg`; +10 more |
| z-index | `calc(var(--z-sticky) * 2)` | Derived: a level computed from a token, with no name of its own | `--z-sticky` | 129 | 63 | page analytics-funnel `.kit-m .pg-actbar`; page analytics-revenue `.kit-m .pg-actbar`; page analytics-system-status `.kit-m .pg-actbar`; +60 more |
| z-index | `calc(var(--z-sticky) * 3)` | Derived: a level computed from a token, with no name of its own | `--z-sticky` | 126 | 63 | page analytics-funnel `.pg-top`; page analytics-revenue `.pg-top`; page analytics-system-status `.pg-top`; +60 more |
| size | `48px` | Has a token: same px as --control-h-m-lg | `--control-h-m-lg` | 118 | 38 | page appointments `.cx-empty .ico`; page content-frame `.wb-globe`; page customer-detail `.cx-empty .ico`; +35 more |
| size | `52px` | Has a token: same px as --pbar-h | `--pbar-h` | 116 | 54 | page analytics-funnel `.pg-chk .num`; page analytics-revenue `.pg-chk .num`; page analytics-system-status `.pg-chk .num`; +51 more |
| size | `44px` | Has a token: same px as --control-h-m, --row, --tab-h-m | `--control-h-m` | 105 | 42 | page analytics-funnel `.pg-empty-ic`; page analytics-revenue `.pg-empty-ic`; page analytics-system-status `.pg-empty-ic`; +39 more |
| size | `56px` | Has a token: same px as --topbar-h | `--topbar-h` | 99 | 45 | page analytics-funnel `.pg-orow`; page analytics-revenue `.pg-orow`; page analytics-system-status `.pg-orow`; +42 more |
| size | `40px` | Has a token: same px as --control-h, --tab-h | `--control-h` | 88 | 42 | page analytics-funnel `.pg-tabs a`; page analytics-revenue `.pg-tabs a`; page analytics-system-status `.pg-tabs a`; +39 more |
| size | `36px` | Has a token: same px as --row-th, --row-compact | `--row-th` | 85 | 44 | page analytics-funnel `.kit-m .pg-fun .dd-chart-f .btn`; page analytics-revenue `.kit-m .pg-fun .dd-chart-f .btn`; page analytics-system-status `.kit-m .pg-fun .dd-chart-f .btn`; +41 more |
| blur | `blur(8px)` | Has a token: equals --actbar-blur | `--actbar-blur` | 75 | 63 | page analytics-funnel `.kit-m .pg-actbar`; page analytics-revenue `.kit-m .pg-actbar`; page analytics-system-status `.kit-m .pg-actbar`; +60 more |
| icon size | `22px` | Has a token: off the contract sizes (16 / 14px); named --icon-22 in kit v3 | `--icon-22` | 71 | 15 | page appointments `svg`; page customer-detail `svg`; page customers `svg`; +12 more |
| blur | `blur(6px)` | Has a token: equals --blur-soft | `--blur-soft` | 63 | 63 | page analytics-funnel `.pg-proto>.k-anchor>button`; page analytics-revenue `.pg-proto>.k-anchor>button`; page analytics-system-status `.pg-proto>.k-anchor>button`; +60 more |
| size | `22px` | Has a token: same px as --chip-h | `--chip-h` | 62 | 25 | page brand-global-store `.bp-sw`; page content-frame `.wb-plc-add`; page create-product `.bp-sw`; +22 more |
| script timing | `300ms` | Has a token: same length as --motion-handoff-sheet | `--motion-handoff-sheet` | 57 | 57 | page appointments `setTimeout`; page brand-global-store `setTimeout`; page campaign-detail `setTimeout`; +54 more |
| font size | `22px` | Has a token: same px as --text-22 | `--text-22` | 39 | 30 | page analytics-funnel `.kit-m .dd-kpi-stack .dd-kpi-v`; page analytics-revenue `.kit-m .dd-kpi-stack .dd-kpi-v`; page analytics-system-status `.kit-m .dd-kpi-stack .dd-kpi-v`; +27 more |
| script timing | `80ms` | Has a token: same length as --motion-press | `--motion-press` | 39 | 32 | page appointments `setTimeout`; page brand-global-store `setTimeout`; page campaign-detail `setTimeout`; +29 more |
| size | `120px` | Has a token: same px as --kv-label | `--kv-label` | 32 | 32 | page analytics-funnel `.dd-skel`; page analytics-revenue `.dd-skel`; page analytics-system-status `.dd-skel`; +29 more |
| size | `26px` | Has a token: same px as --chip-h-lg | `--chip-h-lg` | 28 | 20 | page campaign-detail `.pg-chans .chip`; page campaigns `.pg-chans .chip`; page campaigns-email `.pg-chans .chip`; +17 more |
| tracking | `-.02em` | Has a token: equals --tr-display | `--tr-display` | 26 | 26 | page campaign-detail `.pg-perf .big`; page campaigns `.pg-perf .big`; page campaigns-email `.pg-perf .big`; +23 more |
| font size | `10.5px` | Has a token: same px as --text-10-5 | `--text-10-5` | 22 | 14 | page content-frame `.wb-addr .code`; page releases `.wb-addr .code`; page settings-api-keys `.st-mono`; +11 more |
| script timing | `900ms` | Has a token: same length as --motion-flash | `--motion-flash` | 20 | 20 | page design-pricing `setTimeout`; page pricing `setTimeout`; page pricing-bulk `setTimeout`; +17 more |
| icon size | `28px` | Has a token: off the contract sizes (16 / 14px); named --icon-28 in kit v3 | `--icon-28` | 20 | 6 | page design-pricing `svg`; page pricing `svg`; page pricing-bulk `svg`; +3 more |
| size | `72px` | Has a token: same px as --li-min-h | `--li-min-h` | 18 | 18 | page brand-global-store `.bg-th`; page campaign-detail `.kit-m .pg-mli`; page campaigns `.kit-m .pg-mli`; +15 more |
| font size | `15px` | Has a token: same px as --text-read, --text-btn-lg | `--text-read` | 17 | 11 | page campaign-detail `.pg-mail-w`; page campaigns `.pg-mail-w`; page campaigns-email `.pg-mail-w`; +8 more |
| font size | `20px` | Has a token: same px as --text-20 | `--text-20` | 17 | 17 | page appointments `.kit-m .cx-kpis .dd-kpi-v`; page campaign-detail `.pg-mail-b h3`; page campaigns `.pg-mail-b h3`; +14 more |
| font size | `10px` | Has a token: same px as --text-10 | `--text-10` | 17 | 16 | page content-frame `.wb-pl-av`; page releases `.wb-pl-av`; page settings-api-keys `.st-si-who .avatar`; +13 more |
| shadow | `inset 2px 0 0 hsl(var(--warning))` | Has a token: equals --e-rail-warning | `--e-rail-warning` | 13 | 13 | page analytics-funnel `.pg-fun .dd-fun-r.pg-leak`; page analytics-revenue `.pg-fun .dd-fun-r.pg-leak`; page analytics-system-status `.pg-fun .dd-fun-r.pg-leak`; +10 more |
| tracking | `-.005em` | Has a token: equals --tr-section | `--tr-section` | 11 | 11 | page hq-settings-audit-log `.kit-m .hq-li .t>b`; page hq-settings-users `.kit-m .hq-li .t>b`; page super-api-access `.kit-m .hq-li .t>b`; +8 more |
| font size | `16px` | Has a token: same px as --text-section, --text-input-m | `--text-section` | 11 | 11 | page appointments `.kit-m .cx-ident .avatar.xl`; page content-frame `.kit-m .wb-rc-host`; page customer-detail `.kit-m .cx-ident .avatar.xl`; +8 more |
| font size | `18px` | Has a token: same px as --text-18 | `--text-18` | 11 | 11 | page appointments `.cx-ident .avatar.xl`; page content-frame `.kit-m .wb-pl-g`; page customer-detail `.cx-ident .avatar.xl`; +8 more |
| font size | `28px` | Has a token: same px as --text-kpi | `--text-kpi` | 10 | 10 | page settings-api-keys `.st-si.m .st-si-big`; page settings-audit-log `.st-si.m .st-si-big`; page settings-brand `.st-si.m .st-si-big`; +7 more |
| shadow | `inset 2px 0 0 hsl(var(--primary))` | Has a token: equals --e-rail | `--e-rail` | 10 | 10 | page design-pricing `.pg-gli.on`; page hq-settings-users `.us-b.on`; page pricing `.pg-gli.on`; +7 more |
| font size | `40px` | Has a token: same px as --text-display | `--text-display` | 10 | 10 | page settings-api-keys `.st-si-big`; page settings-audit-log `.st-si-big`; page settings-brand `.st-si-big`; +7 more |
| font size | `19px` | Has a token: same px as --text-19 | `--text-19` | 9 | 9 | page content-frame `.wb-rc-host`; page releases `.wb-rc-host`; page support `.kit-m .su-th-t`; +6 more |
| duration | `360ms` | Has a token: same length as --motion-shake (animation) | `--motion-shake` | 9 | 9 | page design-pricing `.pg-shake`; page pricing `.pg-shake`; page pricing-bulk `.pg-shake`; +6 more |
| size | `480px` | Has a token: same px as --dialog-w | `--dialog-w` | 9 | 9 | page design-pricing `.pg-gdl`; page pricing `.pg-gdl`; page pricing-bulk `.pg-gdl`; +6 more |
| script timing | `1100ms` | Has a token: same length as --motion-caret | `--motion-caret` | 9 | 9 | page analytics-funnel `setTimeout`; page analytics-revenue `setTimeout`; page analytics-system-status `setTimeout`; +6 more |
| script timing | `1200ms` | Has a token: same length as --motion-rotate | `--motion-rotate` | 9 | 8 | page content-frame `setTimeout`; page create-product `setTimeout`; page product-prices `setTimeout`; +5 more |
| script timing | `1600ms` | Has a token: same length as --motion-skeleton | `--motion-skeleton` | 9 | 9 | page design-pricing `setTimeout`; page pricing `setTimeout`; page pricing-bulk `setTimeout`; +6 more |
| script timing | `500ms` | Has a token: same length as --hovercard-delay | `--hovercard-delay` | 9 | 9 | page design-pricing `setTimeout`; page pricing `setTimeout`; page pricing-bulk `setTimeout`; +6 more |
| font size | `14px` | Has a token: same px as --text-14 | `--text-14` | 7 | 7 | page appointments `.kit-m .cx-plist .dt-li-r b`; page customer-detail `.kit-m .cx-plist .dt-li-r b`; page customers `.kit-m .cx-plist .dt-li-r b`; +4 more |
| duration | `900ms` | Has a token: same length as --motion-flash (animation) | `--motion-flash` | 7 | 7 | page brand-global-store `.bg-card.is-flash`; page content-frame `.wb-sectbl tr.is-flash td`; page global-store `.gs-card.is-flash`; +4 more |
| shadow | `inset 3px 0 0 hsl(var(--primary))` | Has a token: equals --e-rail-strong | `--e-rail-strong` | 7 | 7 | page campaign-detail `.pg-bi.on`; page campaigns `.pg-bi.on`; page campaigns-email `.pg-bi.on`; +4 more |
| size | `104px` | Has a token: same px as --kv-label-m | `--kv-label-m` | 6 | 6 | page hq-settings-audit-log `.hq-pcrumb .hq-proto b`; page hq-settings-users `.hq-pcrumb .hq-proto b`; page super-api-access `.hq-pcrumb .hq-proto b`; +3 more |
| script timing | `400ms` | Has a token: same length as --tooltip-delay | `--tooltip-delay` | 6 | 6 | page analytics-funnel `setTimeout`; page analytics-revenue `setTimeout`; page analytics-system-status `setTimeout`; +3 more |
| icon size | `19px` | Has a token: off the contract sizes (16 / 14px); named --icon-19 in kit v3 | `--icon-19` | 6 | 6 | page analytics-funnel `svg`; page analytics-revenue `svg`; page analytics-system-status `svg`; +3 more |
| duration | `1.2s` | Has a token: same length as --motion-rotate (animation) | `--motion-rotate` | 5 | 5 | page content-frame `.wb-sync svg`; page releases `.wb-sync svg`; page super-api-access `.ak-hold.is-holding .ak-fill`; +2 more |
| font size | `24px` | Has a token: same px as --text-24 | `--text-24` | 4 | 4 | page content-frame `.wb-vn`; page releases `.wb-vn`; page website `.wb-vn`; +1 more |
| tracking | `-.015em` | Has a token: equals --tr-page | `--tr-page` | 4 | 4 | page content-frame `.wb-vn`; page releases `.wb-vn`; page website `.wb-vn`; +1 more |
| font size | `52px` | Has a token: same px as --text-52 | `--text-52` | 4 | 4 | page content-frame `.wb-big`; page releases `.wb-big`; page website `.wb-big`; +1 more |
| font size | `17px` | Has a token: same px as --text-step-m | `--text-step-m` | 4 | 4 | page content-frame `.kit-m .wb-host`; page releases `.kit-m .wb-host`; page website `.kit-m .wb-host`; +1 more |
| font size | `44px` | Has a token: same px as --text-44 | `--text-44` | 4 | 4 | page content-frame `.kit-m .wb-big`; page releases `.kit-m .wb-big`; page website `.kit-m .wb-big`; +1 more |
| script timing | `1500ms` | Has a token: same length as --motion-indeterminate | `--motion-indeterminate` | 4 | 4 | page content-frame `setTimeout`; page releases `setTimeout`; page website `setTimeout`; +1 more |
| z-index | `3` | Has a token: equals --z-sticky | `--z-sticky` | 2 | 2 | page global-store `.gs-tick`; page products `.mp-tick` |
| size | `380px` | Has a token: same px as --help-w | `--help-w` | 2 | 2 | page global-store-editor `.ge-toasts`; page product-prices `.pp-offers` |
| shadow | `inset 3px 0 0 hsl(var(--info))` | Has a token: equals --e-rail-strong-info | `--e-rail-strong-info` | 2 | 1 | page product-prices `.pp-in.is-dirty` |
| shadow | `inset 3px 0 0 hsl(var(--destructive))` | Has a token: equals --e-rail-strong-destructive | `--e-rail-strong-destructive` | 1 | 1 | page product-prices `.pp-in.err` |
| font size | `8.5px` | Has a token: same px as --text-8-5 | `--text-8-5` | 1 | 1 | page super-api-access `.ak-mono` |
| font size | `9px` | Has a token: same px as --text-9 | `--text-9` | 1 | 1 | page super-api-access `.ak-who .avatar` |
| script timing | `1400ms` | Has a token: same length as --motion-loadbar | `--motion-loadbar` | 1 | 1 | page global-store `setTimeout` |
| script timing | `800ms` | Has a token: same length as --motion-spin | `--motion-spin` | 1 | 1 | page super-mcp-connect `setTimeout` |


### In the kit

_Values in base.css and the family CSS that every surface also loads._

| Kind | Value | Reconciliation | Token | Uses | Surfaces | Where (first seen) |
|---|---|---|---|---|---|---|
| off-grid spacing | `6px` | No token: not on the 4px grid | — | 92 | 0 | base.css `.sb-brand`; family app-shell `.sh-place`; family cards-gallery `.cg-why`; +7 more |
| off-grid spacing | `10px` | No token: not on the 4px grid | — | 46 | 0 | base.css `.sidebar`; family app-shell `.sh-place`; family cards-gallery `.kit-m .cg-body`; +5 more |
| off-grid spacing | `5px` | No token: not on the 4px grid | — | 17 | 0 | base.css `.staging`; family app-shell `.sh-pl-n span`; family cards-gallery `.cg-ghostgrid`; +6 more |
| off-grid spacing | `9px` | No token: not on the 4px grid | — | 9 | 0 | base.css `.sb-item.on::before`; family form-controls `.fc-ta`; family overlays `.ov-combo`; +2 more |
| off-grid spacing | `7px` | No token: not on the 4px grid | — | 8 | 0 | base.css `.lbl`; family form-controls `.fc-lrow`; family status-feedback `.sf-dl` |
| radius | `2px` | No token: radius off the scale | — | 6 | 0 | base.css `.sb-item.on::before`; family data-display `.dd-cur .sw`; family form-controls `.fc-opt mark` |
| z-index | `4` | No token: local stacking level | — | 5 | 0 | family data-display `.dd-tip`; family data-table `.dt-frozen th.fz`; family overlays `.k-sheet.ov-trap::after`; +1 more |
| z-index | `1` | No token: local stacking level | — | 5 | 0 | family data-table `.dt-li-r`; family form-controls `.fc-cp-tbl th` |
| z-index | `2` | No token: local stacking level | — | 4 | 0 | base.css `.topbar`; family cards-gallery `.cg-media>.chip`; family data-table `.dt-frozen .fz` |
| off-grid spacing | `18px` | No token: not on the 4px grid | — | 4 | 0 | base.css `.ph`; family app-shell `.sh-tree`; family wizard-stepper `.wz-line` |
| z-index | `5` | No token: local stacking level | — | 4 | 0 | base.css `.tabbar`; family cards-gallery `.cg-card:has(.open)`; family page-help `.ph-guide`; +1 more |
| off-grid spacing | `22px` | No token: not on the 4px grid | — | 3 | 0 | base.css `.page`; family data-table `.dt-page`; family page-help `.ph-back` |
| off-grid spacing | `11px` | No token: not on the 4px grid | — | 3 | 0 | base.css `.toast`; family data-display `.dd-tl li::before`; family wizard-stepper `.wz-ri:not(:last-child)::after` |
| tracking | `-.01em` | No token: tracking off the scale | — | 3 | 0 | family app-shell `.sh-mono`; family data-display `.dd-ticket-h h3`; family wizard-stepper `.wz-ci-t` |
| shadow | `inset 0 0 0 1px hsl(var(--foreground)/.08)` | No token: colour from tokens, geometry hand-set | — | 3 | 0 | family cards-gallery `.cg-tthumb`; family data-table `.dt-ph`; family form-controls `.fc-slot .fc-media` |
| tracking | `.08em` | No token: positive tracking: allowed on Plex caps only, never on Archivo | — | 2 | 0 | base.css `.staging` |
| shadow | `inset 0 0 0 1px hsl(var(--foreground)/.12)` | No token: colour from tokens, geometry hand-set | — | 2 | 0 | family cards-gallery `.cg-selbar .cg-mini`; family data-table `.dt-sw` |
| shadow | `inset 0 0 0 2px hsl(var(--ring))` | No token: colour from tokens, geometry hand-set | — | 2 | 0 | family data-table `.dt-li.is-focus`; family form-controls `.fc-slot.target .fc-media` |
| shadow | `inset 0 0 0 1px hsl(var(--foreground)/.1)` | No token: colour from tokens, geometry hand-set | — | 2 | 0 | family form-controls `.fc-sw8`; family overlays `.ov-sw` |
| off-grid spacing | `14px` | No token: not on the 4px grid | — | 1 | 0 | base.css `.sidebar` |
| off-grid spacing | `26px` | No token: not on the 4px grid | — | 1 | 0 | base.css `.page` |
| tracking | `-.003em` | No token: tracking off the scale | — | 1 | 0 | base.css `.btn` |
| colour | `rgba(0,0,0,.08)` | No token: no colour token has this RGB | — | 1 | 0 | base.css `.thumb` |
| shadow | `inset 0 0 0 1px rgba(0,0,0,.08)` | No token: fully hand-set | — | 1 | 0 | base.css `.thumb` |
| shadow | `inset 0 0 0 4px hsl(var(--primary))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `input[type=radio].cb:checked` |
| shadow | `inset 0 0 0 4px hsl(var(--primary)),var(--focus-ring)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `input[type=radio].cb:checked:focus-visible,input[type=radio]` |
| shadow | `inset 0 0 0 4px hsl(var(--muted-foreground)/.55)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `input[type=radio].cb:disabled:checked` |
| shadow | `var(--e-btn-2),0 0 0 1px hsl(var(--border))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `.seg>[aria-checked=true],.seg>[aria-pressed=true],.seg>.on` |
| shadow | `inset 0 0 0 1px hsl(var(--ring)/.55)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `.mi:focus-visible,.mi.is-focus` |
| shadow | `inset 0 0 0 1px hsl(var(--input)/.35)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `.k-grab` |
| shadow | `inset 0 0 0 1px hsl(var(--not-wired)/.55)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `.chip.unmapped` |
| shadow | `inset 0 0 0 1px hsl(var(--not-wired))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | base.css `.chip.unmapped::before` |
| tracking | `.02em` | No token: positive tracking: allowed on Plex caps only, never on Archivo | — | 1 | 0 | family app-shell `.p-bar .staging` |
| shadow | `0 0 0 1px hsl(var(--primary)),var(--e-1)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family cards-gallery `.cg-pick[aria-selected=true]` |
| shadow | `0 0 0 1px hsl(var(--primary)),var(--e-pop)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family cards-gallery `.cg-pick[aria-selected=true]:hover,.cg-pick[aria-selected=tr` |
| shadow | `inset 0 0 0 1px hsl(var(--ring)/.3)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family cards-gallery `.cg-tag.sibling` |
| shadow | `0 0 0 2px hsl(var(--background))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family cards-gallery `.cg-pick[aria-selected=true] .cg-tick,.cg-pick.is-anchor .cg` |
| radius | `1px` | No token: radius off the scale | — | 1 | 0 | family data-display `.dd-legend .key` |
| gradient | `repeating-linear-gradient(135deg,hsl(var(--warning)/.12) 0 6px,transparent 6px 12px)` | No token: gradient written inline | — | 1 | 0 | family data-display `.dd-fun-r.nw .t` |
| shadow | `0 0 0 2px hsl(var(--primary-foreground)/.6)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family data-display `.dd-stab[aria-selected=true] .dot` |
| shadow | `inset 0 0 0 1.5px hsl(var(--neutral))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family data-table `.dt-off` |
| gradient | `linear-gradient(90deg,var(--sticky-edge),hsl(var(--foreground)/0))` | No token: gradient written inline | — | 1 | 0 | family data-table `.dt-frozen .fz-last::after` |
| shadow | `inset 0 0 0 1px hsl(var(--primary)/.25)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family data-table `.dt-pg[aria-current=page]` |
| z-index | `6` | No token: local stacking level | — | 1 | 0 | family data-table `.dt-mbulk` |
| shadow | `0 0 0 1px hsl(var(--destructive)),0 0 0 4px hsl(var(--destructive)/.12)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-in.err:focus,.fc-in.err:focus-within,.fc-in.err.is-focus` |
| shadow | `0 0 0 1px hsl(var(--secondary-border))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-hue` |
| shadow | `0 0 0 1px hsl(var(--muted-foreground))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-hue:hover` |
| shadow | `0 0 0 2px hsl(var(--primary))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-hue.on` |
| shadow | `inset 0 0 0 1px hsl(var(--primary))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-rcard:has(input:checked)` |
| shadow | `inset 0 0 0 1px hsl(var(--secondary-border))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family form-controls `.fc-cal-g button.today` |
| shadow | `inset 0 0 0 1px hsl(var(--warning)/.28)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family overlays `.ov-note.warn` |
| shadow | `inset 0 0 0 1px hsl(var(--destructive)/.3)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family overlays `.ov-note.err` |
| shadow | `var(--e-sheet),inset 0 0 0 2px hsl(var(--ring)/.35)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family overlays `.ov-trap` |
| shadow | `inset 0 0 0 2px hsl(var(--ring) / .35)` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family overlays `.k-sheet.ov-trap::after` |
| off-grid spacing | `74px` | No token: not on the 4px grid | — | 1 | 0 | family page-help `.ph-guide` |
| off-grid spacing | `86px` | No token: not on the 4px grid | — | 1 | 0 | family status-feedback `.sf-screen .sf-toaster` |
| gradient | `repeating-linear-gradient(135deg,hsl(var(--border)/.85) 0 1px,transparent 1px 5px),hsl(var(--muted))` | No token: gradient written inline | — | 1 | 0 | family status-feedback `.sf-tile` |
| duration | `.2s` | No token: delay | — | 1 | 0 | family status-feedback `.sf-skrow:nth-child(3n) .sf-sk` |
| shadow | `inset 0 0 0 1px hsl(var(--border))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family wizard-stepper `.wz-img` |
| shadow | `0 0 0 1px hsl(var(--primary))` | No token: colour from tokens, geometry hand-set | — | 1 | 0 | family wizard-stepper `.wz-pk[aria-pressed=true]` |
| script timing | `650ms` | No token: a timer in script with no token | — | 1 | 0 | family wizard-stepper `setTimeout` |
| size | `28px` | Has a token: same px as --control-h-xs | `--control-h-xs` | 26 | 0 | base.css `.avatar`; family app-shell `.sh-notice`; family cards-gallery `.cg-selbar .cg-mini`; +5 more |
| size | `36px` | Has a token: same px as --row-th, --row-compact | `--row-th` | 24 | 0 | base.css `.sb-item`; family app-shell `.sh-frame-b.mid .ico`; family cards-gallery `.cg-ghostgrid i`; +6 more |
| size | `22px` | Has a token: same px as --chip-h | `--chip-h` | 22 | 0 | base.css `.chip`; family app-shell `.sh-mono.sm`; family cards-gallery `.cg-tick`; +6 more |
| size | `44px` | Has a token: same px as --control-h-m, --row, --tab-h-m | `--control-h-m` | 18 | 0 | base.css `.kit-m .switch`; family app-shell `.sh-pop-s`; family data-display `.dd-conf .m`; +3 more |
| size | `40px` | Has a token: same px as --control-h, --tab-h | `--control-h` | 16 | 0 | base.css `.thumb`; family app-shell `.sh-cmd .sh-row`; family cards-gallery `.cg-tthumb`; +3 more |
| font size | `15px` | Has a token: same px as --text-read, --text-btn-lg | `--text-read` | 10 | 0 | base.css `.p-bar b`; family app-shell `.sh-pbar-place b`; family data-table `.dt-oc-b .dt-money b`; +2 more |
| size | `52px` | Has a token: same px as --pbar-h | `--pbar-h` | 8 | 0 | family app-shell `.sh-cmd-in`; family form-controls `.fc-bopt`; family overlays `.ov-fullbar`; +1 more |
| size | `56px` | Has a token: same px as --topbar-h | `--topbar-h` | 8 | 0 | family app-shell `.sh-full .sh-cmd-in`; family cards-gallery `.cg-sh-head`; family data-table `.dt-ph.lg`; +4 more |
| font size | `13px` | Has a token: same px as --text-body, --text-label | `--text-body` | 7 | 0 | base.css `.help-q`; family data-table `.dt-oc-no`; family form-controls `.kit-m .fc-tag`; +1 more |
| size | `26px` | Has a token: same px as --chip-h-lg | `--chip-h-lg` | 7 | 0 | base.css `.kit-m .switch`; family form-controls `.fc-hue`; family overlays `.ov-presets span`; +1 more |
| size | `48px` | Has a token: same px as --control-h-m-lg | `--control-h-m-lg` | 6 | 0 | family cards-gallery `.cg-li .cg-tthumb`; family form-controls `.fc-prow .fc-media`; family status-feedback `.sf-tile` |
| font size | `11.5px` | Has a token: same px as --text-micro | `--text-micro` | 5 | 0 | base.css `.spec-foot code`; family app-shell `.sh-mono`; family data-table `.kit-m .dt-json` |
| size | `34px` | Has a token: same px as --control-h-sm | `--control-h-sm` | 5 | 0 | family app-shell `.sh-jump`; family data-display `.dd-kpi-row`; family data-table `.dt-fchip`; +1 more |
| font size | `12.5px` | Has a token: same px as --text-btn-sm | `--text-btn-sm` | 4 | 0 | base.css `.p-head p`; family app-shell `.sh-jump`; family status-feedback `.sf-filter` |
| tracking | `-.02em` | Has a token: equals --tr-display | `--tr-display` | 3 | 0 | base.css `.title`; family data-display `.dd-kpi-v` |
| z-index | `3` | Has a token: equals --z-sticky | `--z-sticky` | 3 | 0 | family cards-gallery `.cg-tick`; family data-table `.dt th`; family overlays `.ov-fo-n` |
| size | `72px` | Has a token: same px as --li-min-h | `--li-min-h` | 2 | 0 | base.css `.kit-m .state>.state-k`; family form-controls `.kit-m .fc-file .fc-meter` |
| font size | `12px` | Has a token: same px as --text-small, --text-code | `--text-small` | 2 | 0 | base.css `.brand-mark`; family status-feedback `.sf-filter .n` |
| font size | `13.5px` | Has a token: same px as --text-card, --text-btn | `--text-card` | 2 | 0 | base.css `.kit-m .toast .act .btn`; family status-feedback `.kit-m .sf-filter` |
| z-index | `calc(var(--z-sticky) * 2)` | Derived: a level computed from a token, with no name of its own | `--z-sticky` | 2 | 0 | base.css `.k-actbar` |
| tracking | `-.005em` | Has a token: equals --tr-section | `--tr-section` | 2 | 0 | family app-shell `.sh-sub-h`; family cards-gallery `.cg-title` |
| font size | `30px` | Has a token: same px as --text-hero | `--text-hero` | 1 | 0 | base.css `.kit-m .kit-h1` |
| radius | `4px` | Has a token: same px as --r-chip, --r-media | `--r-chip` | 1 | 0 | base.css `kbd` |
| size | `232px` | Has a token: same px as --sidebar-w | `--sidebar-w` | 1 | 0 | base.css `.tip.wrap` |
| font size | `16px` | Has a token: same px as --text-section, --text-input-m | `--text-section` | 1 | 0 | family app-shell `.tabbar a .sh-tb` |
| size | `380px` | Has a token: same px as --help-w | `--help-w` | 1 | 0 | family data-table `.dt-emp` |
| font size | `28px` | Has a token: same px as --text-kpi | `--text-kpi` | 1 | 0 | family form-controls `.fc-cal-g span` |
| z-index | `calc(var(--z-sheet) + 1)` | Derived: a level computed from a token, with no name of its own | `--z-sheet` | 1 | 0 | family page-help `.ph-stage>.topbar,.ph-stage>.ph-st-bar,.ph-st-bar` |
| script timing | `120ms` | Has a token: same length as --motion-hover | `--motion-hover` | 1 | 0 | family form-controls `setTimeout` |
