# Proof of Work — Design Brief

The durable brand brief for Proof of Work's user-facing UI. Read this and
[`tokens.tokens.json`](./tokens.tokens.json) before building or changing any UI, and conform
to them. Machine-readable values live in the tokens file and
[`brand/palette.css`](./brand/palette.css); this file is the *why* and the rules.

> **Provenance.** Established 2026-06-19 by a **document-as-is extract** (Case B) of the
> as-built UI — there were no prior brand guidelines; the look grew in the code. Values were
> harvested by frequency census of the Tailwind classes in `proof-of-work/app` +
> `proof-of-work/components` and reconciled with the owner. The only owner-driven *elevations*
> over the literal as-built state: red promoted from error-only to a **brand accent**
> (canonical `#DC2626`), and a documented two-tier radius rule. See
> [`inspiration/README.md`](./inspiration/README.md) for what served as the reference.

---

## 1. Visual theme

**Monochrome gym-brutalist.** A near-black canvas, a single cool-gray (zinc) neutral ramp,
white as the primary voice, and **red as the one accent** — the heat in an otherwise
black-and-white system. The feeling is a piece of gym equipment: heavy, blunt, high-contrast,
no ornament. The product's voice ("Track. Lift. Dominate.") shows up visually as
**UPPERCASE, wide-tracked, condensed display type** and flat, edge-defined surfaces.

It is **not** soft, pastel, glassy, or playful. No gradients-as-decoration, no drop-shadow
depth, no rounded-pill friendliness, no multi-hue palette. Restraint is the brand: color is
rare and earns its place.

## 2. Color palette

The system is **one neutral ramp (zinc) + white/black + four semantic hues**, on a near-black
canvas. Red is both the **brand accent** and the **error/destructive** hue, told apart by
*treatment*, never by a second red (see §7).

**Canvas & surfaces** (depth is built by stepping *up* in lightness, not by shadow):

| Role | Token | Hex | Notes |
|------|-------|-----|-------|
| Canvas (app background) | `color.bg.canvas` | `#0A0A0A` | The PWA `theme_color`; `<body>` bg. The anchor. |
| Surface (cards, panels) | `color.bg.surface` | `#18181B` | zinc-900 — the default raised surface. |
| Surface raised (inputs, chips, hover) | `color.bg.raised` | `#27272A` | zinc-800 — controls and the next step up. |
| Surface inset (rare deep wells) | `color.bg.inset` | `#09090B` | zinc-950 — slightly below canvas. |

**Borders** (the primary depth cue alongside bg layering):

| Role | Token | Hex |
|------|-------|-----|
| Subtle (default hairline) | `color.border.subtle` | `#27272A` (zinc-800) |
| Default | `color.border.default` | `#3F3F46` (zinc-700) |
| Strong (emphasis/hover) | `color.border.strong` | `#52525B` (zinc-600) |

**Text** (on the dark canvas):

| Role | Token | Hex |
|------|-------|-----|
| Primary | `color.text.primary` | `#FFFFFF` |
| Secondary | `color.text.secondary` | `#A1A1AA` (zinc-400) |
| Muted | `color.text.muted` | `#71717A` (zinc-500) |
| Subtle / disabled | `color.text.subtle` | `#52525B` (zinc-600) |
| Inverted (on white surfaces) | `color.text.inverted` | `#000000` |

**Accent / semantic.** The accent and error share `#DC2626`; success/warning/info round out
the state palette. On the dark canvas the *text/icon* tint is the lighter -400 step; fills and
edges use the -600 step; washes use a translucent dark step.

| Role | Token | Hex | Use |
|------|-------|-----|-----|
| **Accent / error (canonical red)** | `color.accent.red` | `#DC2626` (red-600) | Brand emphasis fills/edges **and** destructive intent. |
| Accent hover/pressed | `color.accent.red-strong` | `#B91C1C` (red-700) | |
| Red text/icon on dark | `color.accent.red-text` | `#F87171` (red-400) | Error text, destructive links, accent labels. |
| Red border | `color.accent.red-border` | `#991B1B` (red-800) | Error/destructive outlines. |
| Red wash (bg) | `color.accent.red-wash` | `rgba(127,29,29,.30)` (red-900/30) | Error banners, destructive hover. |
| Success | `color.state.success` | `#34D399` (emerald-400) text / `#059669` (emerald-600) fill | PRs, saved, positive deltas. |
| Warning | `color.state.warning` | `#FBBF24` (amber-400) text / `#78350F` (amber-900) edge | Cautions, cost/limit notices. |
| Info | `color.state.info` | `#60A5FA` (blue-400) text / `#172554` (blue-950) wash | Neutral notices (used sparingly). |

**Primary action color is not a hue — it's white.** The primary button is `#FFFFFF` bg /
`#000000` text (see §4). White, not red, is the loudest thing on screen.

## 3. Typography

Two families, both already wired as CSS variables in `app/layout.tsx`:

- **Display — Bebas Neue** (`var(--font-display)`): condensed, all-caps by nature. Used for
  **all headings (h1–h3), buttons, and labels**, always `text-transform: uppercase` with
  `letter-spacing: 0.05em` (Tailwind `tracking-wider`). This UPPERCASE + tracking pairing is
  the single strongest brand signal — ~115 uses in the as-built UI. Don't set body copy in it.
- **Body — Space Grotesk** (`var(--font-sans)`): all running text, form values, data, numbers.
  Use `tabular-nums` for stat/metric columns.

**Type scale** (Tailwind rem, the app is data-dense so the small end dominates):

| Token | Size | Typical use |
|-------|------|-------------|
| `font.size.xs` | 12px | Labels, meta, table cells (the workhorse — ~240 uses) |
| `font.size.sm` | 14px | Default body / form text (~175 uses) |
| `font.size.base` | 16px | Comfortable body |
| `font.size.lg` | 18px | Sub-headings |
| `font.size.xl` | 20px | |
| `font.size.2xl` | 24px | Section headings (Bebas) |
| `font.size.3xl` | 30px | Page headings (Bebas) |
| `font.size.4xl` | 36px | Hero / display (Bebas) |

**Weights:** 400 normal, 500 medium (default for emphasis), 600 semibold, 700 bold. Bebas
ships a single weight; weight contrast lives in the body font.

## 4. Component styling

- **Primary button** — `bg-white text-black`, `font-bold uppercase tracking-wider`,
  `hover:bg-zinc-200`, `disabled:bg-zinc-700 disabled:text-zinc-500`. Radius 4px (`rounded`),
  padding ≈ `px-5 py-2` (inline) / `py-3` (full-width). **This is the signature; keep it white.**
- **Secondary / accent (ghost) button** — transparent bg, `border` + text in the accent red
  (`#DC2626` border, `text-red-400`), uppercase tracking. For secondary emphasis (e.g.
  "Refine"). Never a *solid* red fill (see §7).
- **Destructive button** — same ghost treatment in red (`text-red-400`/`text-red-500`,
  `hover:bg-red-950/30`) or a red-wash block. Destructive is red-as-*outline/wash*, so it never
  competes with the white primary.
- **Cards / panels** — `bg-zinc-900`, `border border-zinc-800`, radius 8px (`rounded-lg`),
  padding `p-4`. Accent a card by adding a **left edge** `border-l-4` in `#DC2626`.
- **Inputs / selects** — `bg-zinc-800` (or `bg-zinc-900`), `border border-zinc-700`, radius 4px,
  white text, `placeholder` in zinc-500, focus ring `ring-white/20`–`ring-white/30`.
- **Badges / chips** — uppercase, `text-xs`, tracked; semantic ones use the wash pattern
  (tinted text + matching translucent bg + matching border at ~45% — e.g. a red "PR" badge:
  `text-red-400 bg-red-900/30 border border-red-800`).
- **Active nav / selected state** — accent red: `text-red-400` + a `border-b-2` underline or a
  left indicator in `#DC2626`; inactive items in `text-zinc-500`.
- **Error / alert banners** — `bg-red-900/30 border border-red-800 text-red-400`, radius 8px.

## 5. Layout

- **Mobile-first PWA**, portrait-primary, installable. Most styling is unprefixed (mobile);
  `sm:` is the dominant breakpoint, `md:` introduces the desktop sidebar.
- **App shell:** a fixed **240px sidebar** on `md+` (`md:pl-[var(--sidebar-width)]` via the
  `.app-content` utility) and a **64px bottom nav** on mobile. Top nav height 64px. These three
  dimensions are CSS vars in `globals.css` (`--sidebar-width`, `--nav-height`,
  `--bottom-nav-height`) — reuse them, don't hardcode.
- **Spacing** follows Tailwind's 4px scale. Dense by intent: `p-4` cards, `px-4 py-3` controls,
  `gap-2`/`gap-3` between elements.
- **Content** is the focus; chrome is minimal. Single-column on mobile; the sidebar is the only
  persistent chrome on desktop.

## 6. Depth & elevation

**Flat by design — depth comes from background layering + 1px borders, not shadows.** The
surface ladder (`#0A0A0A` canvas → `zinc-900` → `zinc-800`) plus zinc-700/800 hairlines *is*
the elevation system. Shadows are reserved for **truly floating overlays only** (modals,
popovers, dropdowns) — `shadow-lg`/`shadow-xl`/`shadow-2xl`. Never put a shadow on a static
card; raise it with bg + border instead.

## 7. Do's and don'ts

**Do**
- Keep the **primary button white** (`bg-white text-black`). It's the brand's loudest element.
- Use **UPPERCASE + `tracking-wider` Bebas** for headings, buttons, and labels.
- Reach for **zinc** for every neutral — backgrounds, borders, secondary text.
- Use **`#DC2626` red as the single accent**: active states, emphasis edges, key deltas, links,
  the destructive intent. Make it rare and deliberate.
- Build depth with **bg steps + borders**; keep surfaces flat.
- Use the **wash pattern** for semantic blocks (tinted text + translucent bg + matching border).

**Don't**
- ❌ Don't make a **solid red button** — it collides with the white primary *and* with the red
  destructive meaning. Red buttons are ghost/outline/wash only. (Red as a solid *fill* is for
  small accents — nav indicators, edges, badges — not full buttons.)
- ❌ Don't introduce a **second neutral** (gray/slate/stone) — zinc only. (`gray-100` strays
  exist in the code; they're cleanup, not precedent.)
- ❌ Don't introduce a **second red, second green, or second yellow** — `#DC2626` red, `emerald`
  success, `amber` warning. (`green-*`/`yellow-*` strays are cleanup.)
- ❌ Don't add **decorative shadows or gradients**. Flat only.
- ❌ Don't set **body text in Bebas Neue**, or leave headings/labels lowercase.
- ❌ Don't add a **new arbitrary radius** — controls are 4px, containers 8px (see §8 below).

## 8. Responsive behavior

- **Mobile-first**: author the mobile layout unprefixed, layer desktop with `sm:`/`md:`.
- The **bottom nav** is the mobile primary navigation; the **240px sidebar** replaces it at
  `md+`. The main content reserves the sidebar via `md:pl-[var(--sidebar-width)]`.
- Touch targets stay ≥ 44px tall on mobile; the dense `text-xs`/`text-sm` scale is for
  *information density*, not for shrinking tap targets.
- Viewport is locked (`maximum-scale=1, user-scalable=no`) — this is an app, not a document.

**Radius scale (canonical):**

| Token | Value | Use |
|-------|-------|-----|
| `radius.control` | 4px (`rounded`) | Buttons, inputs, chips, small elements |
| `radius.container` | 8px (`rounded-lg`) | Cards, panels, modals, banners |
| `radius.full` | 9999px (`rounded-full`) | Pills, avatars, dots |

(`rounded-md`/6px exists in the code as a third value; treat it as drift toward one of the two.)

## 9. Agent prompt guide

When building or editing UI in `proof-of-work/`:

> Build it **monochrome-first**: near-black canvas (`#0A0A0A`), **zinc** for every neutral,
> **white** for primary text and the primary button (`bg-white text-black font-bold uppercase
> tracking-wider`). Headings/labels/buttons are **Bebas Neue, UPPERCASE, `tracking-wider`**;
> body is **Space Grotesk**, dense (`text-xs`/`text-sm`). The **only accent is red `#DC2626`** —
> use it sparingly for active states, emphasis edges (`border-l-4`), key deltas, links, and
> destructive intent; **never as a solid button fill**. Build depth with **background layering +
> 1px zinc borders**, not shadows (shadows are for floating overlays only). Radius: **4px**
> controls, **8px** containers. Pull exact values from `design/tokens.tokens.json` /
> `design/brand/palette.css` rather than re-deriving hexes; reuse the layout CSS vars
> (`--sidebar-width`, `--nav-height`, `--bottom-nav-height`). Stay in the system — no second
> neutral, no second red/green/yellow, no decorative gradients or shadows.
