# 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`; `` 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.