Source of truth

DESIGN.md

The file an agent reads before it chooses anything. Front matter carries the values, prose carries the rules. The copy below is embedded by the sync script so this page works even when opened straight from disk.

Never hand-edit this copy Change DESIGN.md, then run sync-design-md-embed.ps1 (or node sync-design-md-embed.mjs). The block below is generated output.
---
version: 1.0.0
name: WP Product Talk
description: >-
  Core design system contract for WP Product Talk. Normative for the podcast site, episode pages, marketing surfaces, and related product chrome. Prefer the CSS custom properties in css/tokens.css (--cds-*) when implementing.
colors:
  primary: "#5e53c0"
  primary-deep: "#38307f"
  primary-soft: "#e8e6ff"
  accent: "#fed000"
  accent-soft: "#fff6c2"
  paper: "#f5f5f5"
  paper-deep: "#e6e6e6"
  surface: "#ffffff"
  panel: "#e8e6ff"
  panel-deep: "#d4d0f5"
  panel-soft: "#f4f3ff"
  ink: "#394158"
  ink-deep: "#38307f"
  ink-muted: "#4a5568"
  ink-soft: "#4c5667"
  rule: "rgba(56, 48, 127, 0.14)"
  success: "#13612e"
  success-soft: "#d1fae5"
  warning: "#f5a524"
  warning-soft: "#ffedd5"
  danger: "#b82105"
  danger-soft: "#fee2e2"
  info: "#5e53c0"
  info-soft: "#e8e6ff"
  surface-dark: "#38307f"
  on-primary: "#ffffff"
  on-accent: "#38307f"
  on-dark: "#ffffff"
  on-dark-muted: "rgba(255, 255, 255, 0.78)"
typography:
  display-xl:
    fontFamily: "Noto Sans, system-ui, -apple-system, Segoe UI, sans-serif"
    fontSize: 3.5rem
    fontWeight: 700
    lineHeight: 1.1
    letterSpacing: -0.015em
  display-lg:
    fontFamily: "Noto Sans, system-ui, -apple-system, Segoe UI, sans-serif"
    fontSize: 3rem
    fontWeight: 700
    lineHeight: 1.1
    letterSpacing: -0.015em
  display-md:
    fontFamily: "Noto Sans, system-ui, -apple-system, Segoe UI, sans-serif"
    fontSize: 2.25rem
    fontWeight: 700
    lineHeight: 1.1
    letterSpacing: -0.015em
  display-sm:
    fontFamily: "Noto Sans, system-ui, -apple-system, Segoe UI, sans-serif"
    fontSize: 1.75rem
    fontWeight: 700
    lineHeight: 1.15
    letterSpacing: -0.015em
  body-lg:
    fontFamily: "Noto Sans, system-ui, -apple-system, Segoe UI, sans-serif"
    fontSize: 1.375rem
    fontWeight: 400
    lineHeight: 1.5
  body-md:
    fontFamily: "Noto Sans, system-ui, -apple-system, Segoe UI, sans-serif"
    fontSize: 1.125rem
    fontWeight: 400
    lineHeight: 1.6
  body-sm:
    fontFamily: "Noto Sans, system-ui, -apple-system, Segoe UI, sans-serif"
    fontSize: 1rem
    fontWeight: 400
    lineHeight: 1.35
  label:
    fontFamily: "Noto Sans, system-ui, -apple-system, Segoe UI, sans-serif"
    fontSize: 1rem
    fontWeight: 700
    lineHeight: 1.35
  meta:
    fontFamily: "Noto Sans, system-ui, -apple-system, Segoe UI, sans-serif"
    fontSize: 1rem
    fontWeight: 700
    lineHeight: 1.35
    letterSpacing: 0.06em
  code:
    fontFamily: "JetBrains Mono, ui-monospace, SFMono-Regular, Consolas, monospace"
    fontSize: 1rem
    fontWeight: 400
    lineHeight: 1.55
rounded:
  sm: 0.5rem
  md: 0.75rem
  lg: 1.125rem
  pill: 999px
spacing:
  1: 0.25rem
  2: 0.5rem
  3: 0.75rem
  4: 1rem
  5: 1.5rem
  6: 2.25rem
  7: 3.375rem
  8: 5.0625rem
components:
  page:
    backgroundColor: "{colors.paper}"
    textColor: "{colors.ink}"
    typography: "{typography.body-md}"
  heading:
    textColor: "{colors.ink-deep}"
    typography: "{typography.display-md}"
  subhead:
    textColor: "{colors.primary}"
    typography: "{typography.display-sm}"
  body:
    textColor: "{colors.ink}"
    typography: "{typography.body-md}"
  muted:
    textColor: "{colors.ink-muted}"
    typography: "{typography.body-sm}"
  link:
    textColor: "{colors.primary}"
    typography: "{typography.body-md}"
  button-primary:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    rounded: "{rounded.pill}"
    padding: 0.7rem 1.15rem
    typography: "{typography.label}"
  button-secondary:
    backgroundColor: "{colors.panel}"
    textColor: "{colors.ink}"
    rounded: "{rounded.pill}"
    padding: 0.7rem 1.15rem
  button-outline:
    backgroundColor: transparent
    textColor: "{colors.primary-deep}"
    rounded: "{rounded.pill}"
    padding: 0.7rem 1.15rem
  button-accent:
    backgroundColor: "{colors.accent}"
    textColor: "{colors.on-accent}"
    rounded: "{rounded.pill}"
    padding: 0.7rem 1.15rem
  button-ghost:
    backgroundColor: transparent
    textColor: "{colors.ink-muted}"
    rounded: "{rounded.pill}"
    padding: 0.7rem 0.65rem
  input:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    rounded: "{rounded.sm}"
    padding: 0.6rem 0.8rem
    typography: "{typography.body-md}"
  surface:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    rounded: "{rounded.md}"
    padding: 2.25rem
  surface-panel:
    backgroundColor: "{colors.panel}"
    textColor: "{colors.ink}"
    rounded: "{rounded.md}"
    padding: 2.25rem
  surface-soft:
    backgroundColor: "{colors.paper-deep}"
    textColor: "{colors.ink}"
    rounded: "{rounded.md}"
    padding: 2.25rem
  surface-dark:
    backgroundColor: "{colors.surface-dark}"
    textColor: "{colors.on-dark}"
    rounded: "{rounded.md}"
    padding: 2.25rem
  lede:
    textColor: "{colors.ink-muted}"
    typography: "{typography.body-lg}"
  alert-info:
    backgroundColor: "{colors.info-soft}"
    textColor: "{colors.ink}"
    rounded: "{rounded.md}"
    padding: 0.75rem 1rem
  alert-success:
    backgroundColor: "{colors.success-soft}"
    textColor: "{colors.ink}"
    rounded: "{rounded.md}"
    padding: 0.75rem 1rem
  alert-warning:
    backgroundColor: "{colors.warning-soft}"
    textColor: "{colors.ink}"
    rounded: "{rounded.md}"
    padding: 0.75rem 1rem
  alert-danger:
    backgroundColor: "{colors.danger-soft}"
    textColor: "{colors.ink}"
    rounded: "{rounded.md}"
    padding: 0.75rem 1rem
  alert-note:
    backgroundColor: "{colors.panel-soft}"
    textColor: "{colors.ink-muted}"
    rounded: "{rounded.sm}"
    padding: 1.5rem
  pill:
    backgroundColor: "{colors.panel}"
    textColor: "{colors.ink}"
    rounded: "{rounded.pill}"
    padding: 0.25rem 0.65rem
    typography: "{typography.label}"
  pill-active:
    backgroundColor: "{colors.primary-soft}"
    textColor: "{colors.primary-deep}"
    rounded: "{rounded.pill}"
    padding: 0.25rem 0.65rem
  pill-success:
    backgroundColor: "{colors.success-soft}"
    textColor: "{colors.success}"
    rounded: "{rounded.pill}"
    padding: 0.25rem 0.65rem
  pill-warning:
    backgroundColor: "{colors.warning-soft}"
    textColor: "{colors.ink-deep}"
    rounded: "{rounded.pill}"
    padding: 0.25rem 0.65rem
  pill-danger:
    backgroundColor: "{colors.danger-soft}"
    textColor: "{colors.danger}"
    rounded: "{rounded.pill}"
    padding: 0.25rem 0.65rem
  table-header:
    backgroundColor: "{colors.panel-soft}"
    textColor: "{colors.ink}"
    typography: "{typography.label}"
  details:
    backgroundColor: "{colors.surface}"
    rounded: "{rounded.md}"
  details-summary:
    backgroundColor: "{colors.panel-soft}"
    textColor: "{colors.ink-deep}"
    typography: "{typography.label}"
    padding: 0.7rem 1rem
  code-inline:
    backgroundColor: "{colors.panel-soft}"
    textColor: "{colors.primary-deep}"
    rounded: "{rounded.sm}"
    padding: 0.08em 0.35em
  code-block:
    backgroundColor: "{colors.panel-soft}"
    textColor: "{colors.ink}"
    rounded: "{rounded.md}"
    padding: 1rem
    typography: "{typography.code}"
layout:
  measure: 62ch
  content-size: 40rem
  wide-size: 60rem
  gutter: "{spacing.5}"
  inset: "{spacing.6}"
elevation:
  sm: "0 2px 14px rgba(15, 23, 42, 0.06)"
  md: "0 10px 32px rgba(15, 23, 42, 0.1)"
focus:
  ring: "0 0 0 3px color-mix(in srgb, {colors.primary} 38%, transparent)"
---

## Overview

WP Product Talk is a weekly podcast for WordPress product owners: founders,
marketers, and operators who want practical insight from people who have run
real plugin and SaaS businesses. The brand should feel confident, energetic, and
community-led. It must never look generic “AI purple,” corporate grey, or like a
status-colour carnival.

The identity is violet-led with a bright gold accent: soft lavender paper and
panels, deep Minsk purple for ink on emphasis, Blue Violet for primary actions,
and gold used sparingly for high-stakes moments. Type is Noto Sans throughout
for clarity on screens and episode pages.

This file is normative. Agents and generators must read it before choosing
colour, type, radius, or components, and must map values to the CSS custom
properties in `css/tokens.css` (`--cds-*`) rather than inventing parallel ones.
The human reference site starts at `index.html` and walks Color → Type →
Layout → Pieces → Pages → The site. Assets and Design.md sit under that walk.

Brand feel:
- Vibrant and clear; purple carries the brand, gold punches through
- One primary interactive colour; the accent is rare and intentional
- Friendly authority for product owners, not hype or enterprise coldness

Surfaces this system must serve equally well:
1. Marketing and website pages (wpproducttalk.com)
2. Episode and show surfaces
3. Compact admin or plugin-adjacent chrome
4. Documents, surveys, and reports

## How this system is built

Three layers, in order. The reference walk follows them.

1. **Design tokens** — primitive, semantic, and component. Color and type are this layer. See Token layers.
2. **Every Layout** — intrinsic primitives in `css/layouts.css` (Stack, Cluster, Center, Grid, Split) plus Copy. Mixed marketing copy uses `.cds-copy` (Gestalt proximity), not an even stack. See Layout.
3. **Compose** — Pieces are those tokens on those primitives. Pages are Pieces composed into landing jobs. The site is one page from that list.

Walk: Start → Color → Type → Layout → Pieces → Pages → The site. Assets and Design.md sit under that walk. Do not treat Pieces and Pages as two catalogs. A control’s look lives under Components; a page job may include that control.

## Token layers

Tokens come in three layers. Keep these names. Only values change.

1. **Primitives** in this kit are the `--cds-primary-1`…`--cds-primary-5` ramp (and `--cds-chart-1`…`4`, which alias brand jobs). There is no separate hue-named scale file. A fork of the template may add private primitives in CSS. Authors do not pick from this layer in a theme.
2. **Semantic tokens** are jobs: `primary`, `ink`, `ink-deep`, `paper`, `accent`. The `colors:` map in this file is that layer. Pairing colours (`on-primary`, `on-accent`, `on-dark`) sit here too. Compilers and WordPress presets use these names.
3. **Component tokens** are the `components:` recipes in this file (`button-primary`, `surface`). Hierarchy words (`primary`, `secondary`) belong on components, not on leftover brand colours.

Do not put a hue in the semantic map (`green`, `cream`, `orange`). Authors, `theme.json` slugs, and other surfaces only see the job names.

YAML names in this file are not always the CSS custom-property names. Map them before grepping `tokens.css`:

| This file | CSS |
| --- | --- |
| `success-soft` / `warning-soft` / `danger-soft` / `info-soft` | `--cds-success-bg` (and `warning-bg`, `danger-bg`, `info-bg`) |
| `rounded.md` | `--cds-radius` (no `-md` suffix). `sm`, `lg`, and `pill` keep their suffix |
| `elevation.sm` / `elevation.md` | `--cds-shadow` / `--cds-shadow-md` |
| `focus.ring` | `--cds-focus` |
| `layout.wide-size` | `--cds-max` |
| `layout.inset` | `--cds-inset` (equals `--cds-space-6`) |
| `layout.content-size` | WordPress `theme.json` `contentSize` only. Not a `--cds-*` variable |

`ink-deep` is a rank of ink, not a colour. Do not use `secondary` or `tertiary` as colour names — they collide with `button-secondary`. Muted text is `ink-muted`. A rare second punch is `accent`.

## WordPress

WordPress is a projection of this file, not a second system. The contract is
[`WORDPRESS.md`](WORDPRESS.md). The compiler writes `wordpress/cds/`. Keep the
`layout`, `elevation`, and `focus` front-matter groups — customize does not
own them. `layout.content-size` (40rem) is the article `contentSize`. Landing
columns use `wide-size` / `--cds-max` (60rem). Neither is a reading `ch`.

## Fork status

This folder is the public WP Product Talk design system, forked from the
Design System Template. Palette and fonts live in `brand.json`. Marks are in
`assets/`. Re-run `npm run customize` only after editing `brand.json`. Leave
**Token layers** and **WordPress** as-is. Do not hand-edit the generated colour
/ typography / rounded front matter — customize overwrites those blocks.

**Asset gap:** there is no confirmed light-background wordmark
(`assets/logos/wordmark.svg`) and no mono lockup (`wordmark-mono.svg`). On light
paper use the square mark. On `surface-dark` bands use
`assets/logos/wordmark-white.svg`. Do not invent, recolour, or stretch a
substitute.

## Colors

Semantic roles. Use these names in reasoning; hex values live in front matter.

| Role | Token | Use |
| --- | --- | --- |
| Page wash | `paper` | Default background |
| Wash deep | `paper-deep` | Alternating page bands |
| Surface | `surface` | Cards, inputs, lifted modules |
| Panel | `panel` | Grouping without a card shadow |
| Panel deep | `panel-deep` | Stronger panel, secondary button border and hover |
| Panel soft | `panel-soft` | Quietest fill: notes, code paper, table headers |
| Rule | `rule` | Hairline borders |
| Ink | `ink` | Body text |
| Ink deep | `ink-deep` | Headings, text on light accent fills |
| Ink muted | `ink-muted` | Captions, helper text, secondary copy |
| Ink soft | `ink-soft` | Meta only, never body copy |
| Primary | `primary` | Links, primary action, accent subheads |
| Primary deep | `primary-deep` | Primary hover and pressed |
| Primary soft | `primary-soft` | Soft fills, chips, selected states |
| Accent | `accent` | Rare high-stakes CTA, emphasis rules |
| Accent soft | `accent-soft` | Soft accent fills, never a page wash |
| On primary / on accent / on dark | `on-*` | Text on those fills. `on-dark-muted` is body on `surface-dark`. Gold CTAs use `on-accent` (deep purple), not white |
| Surface dark | `surface-dark` | Header chrome, kit sidebar, dark callout cards, hero-adjacent bands |
| Brand stripe | `--cds-brand-stripe` | 5px gold → magenta → blue rule under dark chrome. Not a wash |
| Success / Warning / Danger / Info | semantic | Status only, not decoration |

**How primary and accent relate.** Brand colours need a deliberate relationship,
not a second random favourite. For this kit, **accent must contrast with
primary** so a rare high-stakes CTA can cut through a violet-led page. The
common options:

- **Complementary / near-complementary** (opposites or near-opposites): maximum
  pop. WP Product Talk uses this: Blue Violet primary + gold accent. Warm amber
  stays on `warning`, and red stays on `danger`, so the brand CTA does not look
  like a status colour. Gold labels use `on-accent` (deep purple), not white.
- **Analogous** (neighbours on the wheel): calm and cohesive, but a weak choice
  for accent if the job of accent is to stand out.
- **Split-complementary / triadic**: more energy than analogous, slightly less
  clash than a straight complement. Useful when you need a third brand hue.

Product systems also separate **brand roles** (primary, secondary/accent) from
**semantic roles** (success, warning, danger, info). Do not let a brand accent
double as a status colour. Material Design 3's colour roles are a clear,
current reference for that split:
[m3.material.io/styles/color/system/roles](https://m3.material.io/styles/color/system/roles).
For the classic harmony types themselves, Adobe Color's wheel is a solid
interactive primer:
[color.adobe.com/create/color-wheel](https://color.adobe.com/create/color-wheel).

Rules:
- Default stack is `paper` background, `ink` text, `primary` for interaction
- `accent` must read as a contrast to `primary` (complementary or near-
  complementary). Do not pick a neighbour hue and call it accent
- Text on a filled surface uses its paired `on-*` token: `on-primary` on primary
  fills, `on-accent` on accent fills, `on-dark` on `surface-dark`. Recheck AA
  whenever you change an accent hue
- Soft brand fills use `primary-soft` with `ink` (not `ink-muted`). Solid
  `primary` plus `on-primary` is for buttons, not card bodies, in this demo
- Never promote `accent` into a second theme colour or a large background fill
- Semantic colours describe state; do not borrow `danger` or `warning` for brand
  emphasis, and do not alias `accent` to `warning`
- Check WCAG AA (4.5:1) for body text and AA Large (3:1) for display sizes
  before shipping any text-on-colour pair. The Color page has a live
  accessibility matrix scored from these tokens; button labels need AA, not
  AA Large. Include hover (`primary-deep` + `on-primary`)
- The `primary-1`…`primary-5` ramp is for charts, illustrations, and
  progressive emphasis, not random decoration. `--cds-primary-4` aliases
  `primary`. `--cds-chart-1`…`4` alias `primary`, `accent`, `success`, and
  `ink-muted`.
- `info` is an alias of `primary` (Blue Violet). That is intentional: the brand
  primary already reads informational. Do not invent a second cool hue.
- The brand stripe is the thin rule under the live site header (gold, magenta,
  blue). Use `.cds-brand-stripe` or `border-image` at `--cds-brand-stripe-width`
  (5px). Never as a hero wash, button fill, or page background.

## Typography

Three families, one job each:
- **Display** (`Noto Sans`): titles, section headings, accent subheads.
  Tracking `--cds-tracking-display`. This brand uses the same sans for display
  and body. Customize still stamps the family name.
- **Body sans** (`Noto Sans`): body copy, UI labels, tables, forms, meta.
  Semibold maps to 700 (Noto Sans 400/700 on Google Fonts for this kit).
- **Mono** (`JetBrains Mono`): code only, never marketing body copy.

Size ladder (`--cds-text-*`):

| Token | Size | Role |
| --- | --- | --- |
| `sm` | 16px | Compact UI, labels, table cells |
| `md` | 18px | Default body and long-form |
| `lg` | 22px | Short lead or intro emphasis |
| `xl` | 28px | Small display, card titles |
| `2xl` | 36px | Medium display |
| `3xl` | 48px | Large display |
| `4xl` | 56px | Hero display |

`sm` is the demo floor. If your product genuinely needs denser chrome, lower the
floor in `tokens.css` once rather than sprinkling one-off sizes in components.

`css/base.css` is the only file that styles bare tags. Default headings: `h1`
is `3xl`, `h2` is `2xl`, `h3` is `xl`, `h4` is `lg`. Hero `display-xl` (`4xl`)
is opt-in, not the default `h1`. Skip `base.css` when dropping the kit into a
host that already owns element styles; import tokens, layouts, and components
instead.

`.cds-lede` is short intro emphasis: `lg` size, callout leading, `ink-muted`,
capped at the measure. `.cds-prose` is the article column: measure, `md` body,
stack rhythm on nested paragraphs and headings. `.cds-meta` is the uppercase
eyebrow (tracking `--cds-tracking-meta`).

Line-height is optical, per role: `--cds-leading-display` (1.1) for large
display, `--cds-leading-snug` (1.15) for short titles, `--cds-leading-body`
(1.6) for reading, `--cds-leading-callout` (1.5) for contained copy, and
`--cds-leading-ui` (1.35) for compact UI.

Weights are 400 and 700 (Noto Sans on Google Fonts for this kit). Prefer the
emphasis weight for UI chrome before inventing a third weight, and do not
introduce 500 or 800 without adding a token for it.

Rules:
- Set reading width with `--cds-measure` in `ch`, never a fixed `px` width
- Display and body are the same Noto Sans family; do not add a second display face
- Sentence case by default; uppercase is for short meta eyebrows with
  `--cds-tracking-meta`
- One display family and one body family. A third display font is a redesign,
  not a variation
- Do not rely on fluid viewport type alone to fix readability

## Layout

Composable primitives beat a breakpoint for every block. The approach follows
the intrinsic-layout ideas in *Every Layout* (Pickering & Bell). **Proximity**
(Gestalt): things that belong together sit closer than things that do not.
A uniform gap is not proximity. It is a list.

Axioms:
- **Measure:** no reading line exceeds `--cds-measure`. Flow text is capped;
  structural shells are not.
- **Suggestion over prescription:** prefer `max-inline-size`, `min-block-size`,
  `gap`, and `flex-basis` over fixed `width` and `height`.
- **One space ratio:** `--cds-ratio` (1.5) seeds space from `1rem` (step 4).
  Steps 5 to 8 multiply by the ratio; steps 1 to 3 are chrome-only.
- **Boxes:** global `border-box`, logical properties, and `content-box` on
  reading columns so `ch` measures text rather than text plus padding.
- **Inset:** framed modules (`.cds-surface`, specimen frames) pad with
  `--cds-inset` (step 6). Do not sit copy against the inner edge.

Primitives in `css/layouts.css`:

| Class | Job |
| --- | --- |
| `.cds-stack` | Even vertical gap. Only when every child is the same kind of thing (stats, tiles). Variants `--sm`, `--md`, `--lg` |
| `.cds-copy` | Mixed marketing column. Eyebrow hugs the title; title hugs the lede; the button gets more air; proof meta tucks under the action |
| `.cds-center` | Centred reading column at `--cds-measure`. `.cds-center--gutter` adds `--cds-space-5` inline padding |
| `.cds-cluster` | Wrapping horizontal group for buttons, pills, tags. `--center` and `--between` for alignment |
| `.cds-grid` | Auto-fitting tiles that wrap at `--cds-grid-min`, no media query. `--tight` uses a smaller min and gap |
| `.cds-grid--center` | Center each tile in its track. Proof stats. Not cards. |
| `.cds-grid--five` | Five even tracks. Proof. Stacks to one column when the row cannot hold five. Not auto-fit. |
| `.cds-split` | Two tracks that wrap. Copy plus media. `align-items: start`. No breakpoint soup |

Hierarchy:
- One job per section: a heading, a short supporting line, then content
- Space major blocks with `.cds-stack` or steps 5 to 7
- Do not mix ad-hoc child margins with a parent `.cds-stack` gap
- Hero, Split, CTA, and Dark band copy columns use `.cds-copy`, not a bare `.cds-stack`
- `--cds-max` may wrap shells and specimen grids; paragraphs still obey measure
- `layout.content-size` is the WordPress article column (40rem). Do not look
  for `--cds-content-size` in CSS

## Pages

Marketing pages are stacks of these jobs, composed from Components (Pieces in
the walk). Specimens: `sections.html`. WordPress projects the same names as
patterns in `wordpress/cds/patterns/`. Do not invent a ninth job. A path chooser
is a three-tile card grid. Header and footer are template parts, not page jobs.

| Section | Job | Wash | CTA | Not |
| --- | --- | --- | --- | --- |
| Hero | Pill, display title, lede, one primary. Optional proof cluster. Media on the right via `.cds-split`. Copy column is `.cds-copy`. A text stack without media is still legal | `paper` | One primary | A whole page, a Split section (wrong wash), a hero in a bare `.cds-stack` |
| Proof | Three or five stats, centered in even tracks, or a logo cluster. No leftover tile | `paper-deep` (viewport wash; numbers sit on `wide`) | None | A feature grid, four tiles wrapping to three, five auto-fitting to four-plus-one, left-aligned stats |
| Card grid | Three paired fills in `.cds-grid` (surface, `primary-soft` tint, dark). Title, one line, optional meta and a link | `paper` | Outline or text link, not a second primary | A dashboard, three identical whites |
| Split | Copy plus media via `.cds-split` (WordPress: `core/columns`) | `paper-deep` (viewport wash; split sits on `wide`) | One primary, optional | A blog layout |
| Quote | One attribution. Compose `.cds-surface` plus `.cds-lede` plus `.cds-meta`. There is no `.cds-quote` class | `paper` | None | A reviews carousel, a blockquote restyle |
| Dark band | Callout strip. `on-dark` title | `surface-dark` (viewport wash; copy on the measure) | One accent max | A second theme |
| CTA | Short `display-sm` plus one button | `panel` | Primary or accent, not both | Two buttons fighting |
| Alert | One status note on a landing. Look and tones live under Components (`.cds-alert`). Never a hero | soft status or `primary-soft` | None | A hero, a restated alert system |

Page recipes compose sections. `landing` is hero, optional proof, and one card
grid or split. The hero holds the primary. Use the CTA section when the page
has no hero button. `article` is measured prose on paper, not this catalog.

If a request needs a section that is not in this table, say so and compose from
what is here. Do not add a class or a pattern to cover a one-off.

Washes (`paper-deep`, `surface-dark`) are viewport bands. Inner marketing
copy sits on `wide` (the same column as the header). Article prose stays on
the measure / `content`. Do not wrap `post-content` in a second constrained
group; `alignfull` cannot escape it. Do not mix `content` and `wide` on one
landing. Do not leave a leftover tile in a proof strip or card grid. Three
items, three tracks. Five items, five tracks (`.cds-grid--five`). Four items only as two by two or four across on `wide`. Do not auto-fit five into four-plus-one.

## Elevation & depth

Depth is quiet.
- Default surfaces: 1px `rule` border with an optional light shadow
- Stronger lift only when something genuinely floats (`--cds-shadow-md`)
- Prefer a border plus a background shift (`surface`, `panel`, `paper-deep`)
  over stacked shadows
- No neon glow, no multi-layer coloured shadows, no glass blur as a brand device

## Shapes

Radii: `rounded.sm` for inputs and small controls, `rounded.md` for surfaces and
alerts (`--cds-radius` in CSS, no `-md` suffix), `rounded.lg` for large media
frames, `rounded.pill` for buttons and chips.

Button shape is a single knob: `--cds-button-radius`. Set it to
`var(--cds-radius)` for a squarer system. Do not mix sharp chrome and pill
buttons in the same view unless the exception is documented here.

Focus ring: a soft primary halo (`--cds-focus`). Restyle it if you must, but
never remove it.

## Components

Pieces in the reference walk. Class names live in `css/components.css`, all prefixed `cds-`.

**Buttons**
- `button-primary` (`.cds-btn--primary`): the main action. One per section.
- `button-secondary` (`.cds-btn--secondary`): panel fill for secondary actions.
- `button-outline` (`.cds-btn--outline`): low-emphasis action, transparent fill
  with a `primary-deep` border and label so body-size type meets WCAG AA on
  `paper` and `surface`. Hover fills `primary-soft`. That hover is invisible on
  a `primary-soft` tile, so `.cds-surface--tint` inverts to `primary-deep` /
  `on-primary`. On `surface-dark`, rest is `on-dark` outline; hover fills
  `surface` with a `primary-deep` label (same pairing as the dark primary
  button). Hover fill must read as a different colour from the tile.
- `button-accent` (`.cds-btn--accent`): at most one high-stakes CTA per view
  (Buy, Book, Submit final). Use `on-accent` for the label. Hover deepens the
  fill so a light label keeps its contrast.
- `button-ghost` (`.cds-btn--ghost`): text-like dismissal or tertiary nav.
  Hover fills `primary-soft` with a `primary-deep` label.
- `button-sm` (`.cds-btn--sm`): compact control size for cards, toolbars, and
  download rows. Compose with a colour variant (e.g. `.cds-btn--outline.cds-btn--sm`).
  Keeps the button radius knob; uses denser padding and a 14px control label
  (not a new body type-scale step).
- Disabled reduces opacity (0.45). Do not invent a separate grey system.
- Buttons fade colour on hover (`0.15s`). That is CSS, not a motion token. The
  spinner respects `prefers-reduced-motion` by slowing, not stopping.

**Links**
- Default `a` in `base.css` is `primary` at body weight, with `primary-deep`
  on hover. Not semibold. Label weight is for buttons, field labels, and meta.

**Prose**
- `.cds-lede`: intro line. `lg`, muted, measure.
- `.cds-prose`: article column. Measure, `md` body, heading and paragraph rhythm.
- `.cds-meta`: uppercase eyebrow. Use it for kicker lines, not body copy.
- `.cds-text-muted` / `--primary` / `--accent` / `--danger`: colour utilities
  when a class is enough and a new component is not.

**Forms**
- Labels sit above fields; hints and errors sit below
- Inputs: surface fill, soft ink border, `rounded.sm`, primary focus ring
- Errors pair a `danger` border with a text message, never colour alone
- Selected chips use `pill-active` (soft fill, deep text), not solid `primary`
  with `ink`

**Surfaces**
- `surface`: default module on the page wash. Padding is `--cds-inset` (step 6),
  not the 1.5rem recipe that used to sit in this file
- `surface-panel`: grouping without a card shadow; body stays `ink` (muted fails AA)
- `surface-soft` (`.cds-surface--soft`): `paper-deep` fill, no shadow. Alternating
  bands inside a long document
- `surface-tint` (`.cds-surface--tint`): `primary-soft` fill, `ink` body. Soft brand paint, not a second theme
- `surface-dark`: one dark callout per section at most; muted white body copy.
  Header chrome and the kit sidebar also use this fill.
- Do not wrap every block in a shadowed card
- Do not fill a card with solid `primary` or `accent`; those fills fail AA with `on-*` at body size in this demo

**Pills**
- `pill`: default panel fill, for compact status, tags, and the hero eyebrow
- `pill-active` (`.cds-pill--active` or `.cds-pill.is-active`): selected state
- `pill-success` / `pill-warning` / `pill-danger`: soft semantic fills, state only. Not the hero eyebrow.

**Feedback**
- Pills for compact status, alerts for full-sentence messages
- `alert-note` (`.cds-alert--note`) is guidance, not status
- Spinner for indeterminate waits, progress bar only when percent is known

**Tables**
- `.cds-table-wrap` is the frame: surface fill, `rule` border, `rounded.md`
- Header row is `table-header`: `panel-soft`, heading family, left-aligned
- Cells use `body-sm` and muted ink. Hairline row rules only, no grid of
  cell borders. Last row drops the bottom rule so it does not double the frame

**Details**
- `.cds-details` is a disclosure, not a card stack. Same frame as the table
- Summary is the control: label weight, `ink-deep`, a kit chevron (not the
  browser triangle). Open state paints the summary `panel-soft` and a hairline
- Body copy inside stays `ink`. Do not nest a second surface

**Code**
- Inline `code` stays quiet: `panel-soft` paper, `primary-deep` text, no border
- Block `.cds-code` has a `rule` border, soft-wraps, and never hides content
  behind a scrollbar

**Brand assets**
- Marks live in `assets/`. Use the white wordmark when naming the brand on a
  dark field, and the square icon for favicons, avatars, tight badges, and
  light paper until a light wordmark exists
- Do not add shadows, outlines, recolours, or stretch transforms to mark files
- Prefer SVG on screen; keep raster exports for hosts that require them

## Do's and Don'ts

**Do**
- Start from `paper` plus `ink` plus `primary`
- Keep the accent rare and singular
- Use the brand stripe as a 5px rule under dark chrome, matching the live header
- Use the space, size, and radius scales exactly
- Check contrast for body text before shipping
- Reuse `css/cds.css` (or tokens plus components) instead of new one-off CSS
- Add a token when you need a new value
- Compose marketing pages from the page jobs, not from a new layout
- Put mixed copy (eyebrow, title, lede, action) in `.cds-copy`. Use `.cds-stack` when every child is the same kind of thing

**Don't**
- Don't use one even stack gap for a hero, split, or CTA copy column
- Don't invent hex values, font stacks, or spacing that bypass the tokens
- Don't make the accent a theme colour or a page background
- Don't use the brand stripe as a wash, a button, or a large fill
- Don't ship more than one primary button, or more than one accent CTA, per view
- Don't use pure black or pure white as the default page wash
- Don't use semantic status colours as decoration
- Don't remove focus indicators
- Don't set paragraph measure in `px`
- Don't introduce a third display family or a fourth type role
- Don't create a dark-mode palette unless it is asked for and tokenised here
- Don't leave a generic kit name on a WP Product Talk surface

## Agent directives

When generating or editing UI for this brand:

1. Read this file before choosing colour, type, radius, or components.
2. Prefer token names and `{colors.*}` references in reasoning; emit `--cds-*`
   custom properties in code. Semantic names (`paper`, `primary`, `accent`)
   are the public layer — see Token layers. Do not expose hue names as
   author-facing slugs.
3. If a request conflicts with Do's and Don'ts, follow this file and say so
   briefly rather than silently deviating.
4. Validate text and background pairs against WCAG AA for body copy.
5. Self-check after UI work: correct page wash, display family on titles, one
   primary action, accent used at most once, focus rings intact.
6. Use the mark files in `assets/`; never regenerate a lookalike.
7. Patterns not covered here (modals, tabs, toasts, dark mode) are out of scope
   until they are added. Improvise with existing tokens and keep the same
   constraints instead of inventing a new visual language.
8. This fork is WP Product Talk. If a file still reads like the generic
   template placeholder, treat that as a leftover and fix it before shipping.
9. For a WordPress theme, follow [`WORDPRESS.md`](WORDPRESS.md). Do not invent
   palette slugs, picker unlocks, or recipes the compiler does not emit.
10. Marketing pages start from the page jobs above. Compose those jobs.
    Hero, Split, CTA, and Dark band copy columns use `.cds-copy`. Do not put
    mixed copy in a bare `.cds-stack`. Do not invent a layout, a card class, or
    a ninth page job. Do not wrap
    `post-content` in a second constrained group. Do not leave a leftover tile.
    Do not mix `content` and `wide` on one landing. Proof stats are centered.
    Proof is three or five even tracks, never a leftover tile. Marketing hero is
    a split: pill, title, lede, one primary, optional proof cluster, media.
    Card-grid tiles use three paired fills. Body and outline labels must meet
    WCAG AA against the tile. Outline hover on tint and dark must fill a colour
    that is not the tile. Do not ship three identical whites.