# Design tokens — channels-manager

> **SPLIT 2026-07-31 (operator, grilled — `docs/DECISIONS.md`).** This file now
> holds **values that have a right answer**: colour, the spacing grid, radii,
> breakpoints, contrast floors, channel colours, assets. They are law, they are
> testable, and a wrong one is a bug.
>
> **Everything with a taste answer moved to `design/craft.md`** — the type scale
> and weights, casing, component chrome, the stat tile, the pill, content width.
> That sheet changes with a design round and needs no ledger entry. Five separate
> design calls collided with this file in round 4 because both kinds of rule were
> frozen behind "changes need an operator round"; that is the collision the split
> removes.

> Status: **APPROVED by the operator, 2026-07-09.** Source: tile B as
> converged through rounds 1 to 3 (`design/tiles/`). This file is now law:
> **no screen ships outside it.** Phase 1 encodes it into `tokens.css` (and
> DTCG JSON if tooling wants it); until then this document is the single
> reference. Changes need an operator round, not a commit.

## Color

Neutrals (zinc family), light theme first. Dark mode is a later toggle;
no screen may depend on one theme.

| token | value | use |
|---|---|---|
| `bg` | `#FAFAFA` | app canvas |
| `surface` | `#FFFFFF` | cards, tables, panels |
| `hover` | `#F4F4F5` | row/item hover |
| `ink` | `#18181B` | headings, primary text |
| `ink-2` | `#3F3F46` | body/secondary text |
| `muted` | `#71717A` | labels, meta, timestamps |
| `line` | `#E4E4E7` | hairline borders, row separators |
| `line-strong` | `#D4D4D8` | card/table outer borders, input borders |

One accent. Color carries meaning only; never decoration.

| token | value | use |
|---|---|---|
| `accent` | `#4F46E5` | primary buttons, active-tab underline, selection bars, count badges |
| `accent-ink` | `#4338CA` | links, active nav text, focus ring |
| `accent-soft` | `#EEF0FD` | active nav bg, selected row bg |
| `accent-contrast` | `#FFFFFF` | text on accent fills |

Status. Every status is text plus color, never color alone.

| token | value | soft | use |
|---|---|---|---|
| `ok` | `#15803D` | `#E9F5EE` | live, healthy, sent |
| `warn` | `#B45309` | `#FBF1E0` | shadow, queued, aging, expiring |
| `bad` | `#B91C1C` | `#FBEDED` | failed, error, hard rules |
| `info` | `#1D4ED8` | `#EAF0FC` | triggers, replies, neutral emphasis |

Brand gold (`#C9A96E`) appears inside the logo asset and in exactly one UI
place: the web-channel chip (below). Nowhere else.

Channel chips (operator-picked from the swatch page, 2026-07-24). White
text unless noted; ratios computed:

| token | value | use |
|---|---|---|
| `ch-whatsapp` | `#12824A` | WhatsApp chip (4.9:1) |
| `ch-instagram` | gradient `45deg #F58529 → #DD2A7B → #8134AF` | Instagram chip; build may deepen the orange end only as far as needed to hold 4.5:1 |
| `ch-facebook` | `#3B5998` | Facebook chip (6.8:1) |
| `ch-web` | `#C9A96E` + `ink` text | web channel (future) — brand gold, dark text (white fails) |

Contrast floors (headroom by construction): `ink` on `bg` about 15:1,
`ink-2` on `surface` about 10:1, `muted` at 11px+ about 4.6:1, `accent-ink`
on `surface` about 6.3:1. Nothing ships below 4.5:1 for body-size text.

## Type

| token | value |
|---|---|
| family (UI) | `Inter, system-ui, sans-serif` |
| family (brand wordmark only) | `'EB Garamond', 'Times New Roman', serif` (`--cm-font-brand`) |
| family (numbers/meta) | `ui-monospace, Menlo, Consolas, monospace` |
| scale | `12 / 13 / 15 / 18 / 24` px (`xs / sm / md / lg / xl`) |
| micro sizes | `11` px (`2xs`) — the small-caps label size (field labels, pills, chips, stat labels); `10` px (`3xs`) — micro annotations (slider ticks, foot lines). Below `xs`; used as labels/annotations, never body prose. |
| weights | 400 body · 500 nav/labels · 600 emphasis · 650 headings |
| field label | 11px, 600, uppercase, letter-spacing 0.07em, `muted` (the small-caps pattern) |
| line height | 1.45 body; headings tighter |
| numerals | `tabular-nums` in tables and stat tiles |

The brand wordmark is always the full name, "Silver Oak Properties", set in
EB Garamond next to the logo. Never "Silver Oak" alone.

**Serif is the wordmark's, nothing else's** (operator font ruling, 2026-07-17 —
`docs/DECISIONS.md`). The wordmark's serif is **EB Garamond** (`--cm-font-brand`),
used *only* for the "Silver Oak Properties" brand wordmark. Every heading, title,
dialog, and UI text uses the sans family (`Inter` in the token system; `DM Sans`
in the legacy `global.css` screens). No serif on headings. The legacy
`Cormorant Garamond` webfont was dropped from the `global.css` `@import` once the
sweep left it with zero usages — the wordmark never used it.

## Spacing, radius, layout

- 4/8px grid. Named steps used by the mocks: row padding 9px, card padding
  16px, gap 16px, section gap 26px, page padding 28px.
- Radius: **10px cards and tables, 7px inputs, pills, chips.**

> Amendment APPROVED by the operator, 2026-08-04 (review item 9, *"roundness —
> some personality, carefully"*). Was 6px / 4px. Raised once at the token, so
> every surface moves together and no screen carries its own radius; 10px also
> matches the flow canvas's own node radius, which was the only thing in the
> app already drawn round. Carefully means TWO values changed, not a sweep of
> per-component exceptions.
- Shell chrome: icon rail 56px, plus a 196px sub-nav panel where an area has
  more than one screen (operator ruling 2026-08-04, `docs/DECISIONS.md` § Design
  & UI). This replaced the 216px sidebar; `--cm-sidebar-w` survives because
  content screens still measure against it. Content max-width 1360px.
- Density comes from these values; no per-screen overrides.

## Breakpoints

> Amendment APPROVED by the operator, 2026-07-17 (appshell-scope-2 fork
> ruling): breakpoint values are tokens, never raw numbers scattered in CSS.

> Second amendment APPROVED by the operator, 2026-07-31 (`docs/DECISIONS.md`):
> a second breakpoint is added for a PANE. Two, and only two.

| token | value | use |
|---|---|---|
| `bp-shell` | `768px` | the shell boundary. Desktop layout (sidebar) AT 768px and above; the collapse pattern (top bar + overlay drawer, round-7 Tile A) only below (`max-width: 767.98px`). 768 is the industry-standard tablet boundary (Tailwind/Bootstrap `md`) — a standard value by ruling, not a project-invented one. |
| `bp-rail` | `1180px` | the pane boundary, never the shell. The width below which a three-pane screen drops its third pane (Inbox's lead-context rail). From the approved round-3 Inbox sheet. It regularizes a number three stylesheets already hard-coded raw; the shell still collapses at `bp-shell` alone. |

CSS custom properties cannot parameterize `@media` preludes or `matchMedia`
queries, so the `--cm-bp-shell` and `--cm-bp-rail` entries in `tokens.css` are
the documented sources that every 768/767.98 and 1180/1179.98 constant in code
must cite in a comment.
Screen content adapts intrinsically (wrapping, `auto-fit` grids, scrolling
strips) — per-screen viewport breakpoints stay a smell.

## Component chrome (one spec each, reused everywhere)

- **Card**: `surface`, 1px `line`, radius 6, padding 16.
- **Pill**: 11px/600, padding 4px 8px, radius 4, tinted soft bg + status
  text color, always carries a text label.
- **Input/field**: `surface`, 1px `line-strong`, radius 4, padding 10px
  12px; hover border `accent`; focus ring 2px `accent-ink`, offset 2.
- **Segmented control**: bordered group, radius 4; active segment `accent`
  fill with `accent-contrast` text.
- **Table**: header row of small-caps labels on `surface`, 1px hairline
  rows, row hover `hover`, selected row `accent-soft` with a 2px inset
  `accent` bar, cells 13px, numbers tabular.
- **Nav item**: 13px/500, radius 4; active is `accent-soft` bg +
  `accent-ink` text at 600.
- **Stat tile**: number 24px/650, small-caps label under it, tiles joined
  by hairlines inside one bordered container.
- **Focus**: every interactive element shows a visible 2px `accent-ink`
  outline on keyboard focus. Non-negotiable.
- **Empty values**: an em dash glyph ("—") is the null placeholder in data
  cells. This is the only place that character appears.

### Elevation, popover and side panel

> Amendment APPROVED by the operator, 2026-07-25 (agent-scope-13 fork
> ruling). The sheet specified Card, Pill, Input, Segmented control, Table,
> Nav item, Stat tile and Focus — and nothing that floats above the page. So
> a popover menu and a side panel had no legal chrome, and the alternative
> was for both to fall back to a flat hairline border and lose their lift.
> Two tokens, both derived from `ink` so the palette stays single-sourced.

| token | value | use |
|---|---|---|
| `shadow-raised` | `0 4px 16px` of `ink` at 12% | anything floating over page content: popover menus, side panels. One shadow serves both; split it only when a real design need proves they must differ. |
| `scrim` | `ink` at 28% | the dim layer between a side panel and the content behind it. |

- **Popover menu**: `surface`, 1px `line-strong`, radius 6, `shadow-raised`;
  rows are hairline-separated with `hover` on hover; a destructive item is
  separated by a hairline and set in `bad`.
- **Side panel**: enters from the right over a `scrim`,
  `width: min(440px, 100%)`, full height, `surface`, 1px `line-strong`,
  `shadow-raised`. It never replaces the page's own headers — the title,
  action row and tabs stay on screen beside it. Below ~440px it fills the
  window. Escape, the scrim and an explicit close all dismiss it. Only the
  panel's body scrolls.
- **Quiet button** (`cm-btn--quiet`): transparent, `muted` text, `fw-label`,
  no border, `hover` background on hover. For an action that must read as
  clearly secondary beside a primary — `cm-btn--ghost` is `accent-ink` and
  competes for attention at that distance.

## Copy rules

- All page copy passes the avoid-ai-writing check. No em dashes in prose,
  no AI-sounding phrasing. Middots (·) separate inline meta.
- Status and outcome labels are words, not codes ("Queued", "conf 0.58,
  below threshold").
- Arabic content renders with `dir` handling; translations sit on their own
  LTR line under the original.

## Assets

- Logo: `design/assets/logo.png` (gold SOP monogram over "UAE | INDIA").
  Sidebar use at 34px height.
- Logo mark: `design/assets/logo-mark.png` (monogram only, 270x512,
  operator-approved crop). Use for the favicon and any context under about
  28px, where the "UAE | INDIA" line would be unreadable.
