Brand kit

Rendered by the product, from the product's own stylesheet. If a swatch here is wrong, the interface is wrong the same way.

The mark

A container with one route leaving it — a diagram of the product rather than a metaphor for it. The wordmark is constructed on a stem grid rather than set in a typeface: stem 11, x-height 44, round letters drawn as true circles, sidebearing 12 measured from each letter's widest part. Never re-set it in a font, never stretch it, and keep the mark in --brand with the wordmark in --ink.

Colour

Every value is a custom property and nothing below the token block names a colour directly, which is what makes both themes real rather than one being an inversion of the other.

GROUND

--paper

The page

--raise

Cards, inputs — a step toward the reader

--sunk

Code, console output — a step away

--rule

Borders that divide

--hair

Borders inside a thing

INK AND ACCENT

--ink

Body text

--dim

Secondary — cool, biased toward the accent

--brand

Links, primary controls, the mark

STATE

Three colours meaning one thing each, kept separate from the accent so that "brand" and "good" are never confused.

--permit

Allowed, running, verified

--hold

Waiting on a person or a machine

--refuse

Refused, failed, destructive

Type

Two faces, two jobs. A grotesque for anything a person wrote; a monospace for anything a machine did — hostnames, policy, sequence numbers, states, keys. This product's content is machine output, and setting it in the same face as the prose is what made every page read as undifferentiated grey. No webfont: a font from a CDN is a third party who can stop your sign-in page from rendering.

Agents you can leave running

17px lede — the sentence under a heading

15px body, 1.6 line height, measure capped near 66 characters

13px mono — allow gmail.googleapis.com read

11PX LABEL, .13EM TRACKED

Components

One primary per view. Destructive is never filled — a red button large enough to be the obvious target is a trap.

running starting failed openclaw on w-1abc

The dot on a waiting pill breathes while something is genuinely in progress, and only then. Motion that never stops stops meaning anything.

A shape where the content will be, rather than the word "Loading" — so the page does not jump when the data lands.

Motion

One curve and three durations. A system with five of each reads as five different products. The curve leaves fast and arrives slowly, which is what makes an interface feel answerable rather than springy — nothing here should look like it is enjoying itself.

TokenValueFor
--easecubic-bezier(.2,.7,.2,1)Everything
--fast110msPressed, focused, hovered
--base200msA message arriving, a card lifting
--slow340msContent entering the page

All of it is decoration, so all of it stops under prefers-reduced-motion. Someone who asked their system to stop moving things asked for a reason; the interface still works, it simply arrives already there.

Voice

Say what happened and what happens next. Never announce a state without saying whether the reader should wait or act — "stopped", in red, next to nothing else, was accurate and useless. Name things the way a person would: an agent is waiting on you, not blocked pending approval. Errors say what went wrong and what to do. No exclamation marks, no apologies, no congratulating anyone for filling in a form.