# BeFair Lab — Design

> A small studio. Honest software. We use AI in everything we build, and we say so out loud.

This file is the source of truth for how BeFair Lab looks, sounds, and feels. It precedes the code. Everything in `globals.css` and the live components on the [/design](/design) page is downstream of what's written here.

When the design changes: update this file first, then the swatches and live components on `/design`, then `globals.css`, then the pages. In that order.

---

## 1. Voice

We write the way we'd talk to a colleague we respect. Plain. First-person plural ("we"). Honest about uncertainty. We don't dress up shipping a feature in launch language, and we don't dress up an unfinished thing as roadmap material.

**Do**

- Say what we did, in past tense.
- Name the model when AI was involved.
- Admit what didn't work.
- Use ordinary words. "Honest" not "transparent." "Wrote" not "authored."

**Don't**

- "Revolutionary," "powered by AI," "best-in-class," "delight," "magical."
- Em-dash padding inside short headlines. Reserve em-dashes for prose.
- Exclamation marks. Ever.

**Tone scale.** Headlines are *declarative + a little dry.* Body is *plain + specific.* Mono labels are *clinical* ("§ 02 · Blog", not "Recent posts ✨").

**Recurring rhetorical move.** Sans + italic-serif accent in the same sentence. The sans says the thing, the italic underlines the part that matters.

> Things we've built — and *shipped.*
> A small *studio.* Honest *software.*

---

## 2. Brand

- **Name:** BeFair Lab
- **Tagline:** Let's be fair. We use AI.
- **One-liner:** A small studio. Honest software.
- **What we ship:** Apps, tools, websites, plugins, and the occasional record.

The "Lab" in the name is a posture, not a product. It means we work in public, we keep notes, and we're willing to be wrong out loud. There is no separate Lab section on the site — the whole site is the lab.

---

## 3. Principles

These are the rules we work to. They live on the About page; they belong here too because they steer design decisions, not just product ones.

1. **Ship the thing first.** Projects are what exists in the world. We don't talk about ideas — we point at the thing running and say "that."
2. **Write down what we learned.** Every project leaves a trail. The blog is where we say what worked, what didn't, and what we abandoned.
3. **Name the model.** We use AI in everything we build. We say which model, where it helped, where it got in the way, and which decisions stayed human.

---

## 4. Type

| Role | Family | Why |
|---|---|---|
| Display + UI | **Inter Tight** (400 / 500 / 600 / 700) | Tight, modern grotesk. Reads as "a studio that ships." |
| Editorial accent | **Instrument Serif** (italic) | The single italic word that adds warmth + opinion. |
| Mono | **JetBrains Mono** (400 / 500) | Section markers, eyebrows, labels, captions, numerics. |

**Type rules**

- One serif word per phrase, max. The italic is a spotlight; spotlights stop working when there are five of them.
- Display sizes use `letter-spacing: -0.035em` (sans) and `-0.015em` (serif italic). The italic sits **looser** than the sans on purpose.
- Mono labels are always uppercase, `letter-spacing: 0.06–0.08em`, and prefixed with `§ NN ·` when they tag a section.
- Body copy: 16–18px, `line-height: 1.5–1.65`. Headlines: `text-wrap: balance`.

**Display scale**

| Token | Size (clamp) | Use |
|---|---|---|
| `display-xl` | 44–116 px | Home + landing hero |
| `display-lg` | 36–72 px | Project / post hero |
| `display-md` | 28–44 px | Section heads, prose h1 |
| `h3` | 22–36 px | Tile titles, post titles |
| `lede` | 17–20 px | First paragraph after a hero |
| `body` | 16–18 px | Default running text |
| `mono-eyebrow` | 11–13 px | Section markers, captions, metadata |

---

## 5. Color

The system is intentionally near-monochrome. Color enters through five **atmospheric gradient stops**, never as flat fills. One token — `--danger` — exists for destructive admin actions and form validation.

### Neutrals (the system)

| Token | Hex | Use |
|---|---|---|
| `--ink` | `#0c0a09` | Primary text, CTA backgrounds, the live core |
| `--ink-2` | `#292524` | Secondary text, hover-darken |
| `--muted` | `#4e4e4e` | Mono labels, eyebrows |
| `--muted-2` | `#777169` | Tertiary metadata |
| `--muted-3` | `#a8a29e` | Disabled, placeholders |
| `--line` | `#e7e5e4` | Default 1px hairlines |
| `--line-2` | `#f0efed` | Lower-contrast dividers |
| `--line-3` | `#d6d3d1` | Form controls, dot states |
| `--paper` | `#f5f5f5` | Page background |
| `--paper-2` | `#fafafa` | Sub-surfaces, code blocks |
| `--paper-3` | `#ffffff` | Highest surface, inputs |
| `--surface-strong` | `#f0efed` | Image placeholders before media loads |

### Atmospheric stops (the soul)

| Token | Hex | Use |
|---|---|---|
| `--grad-mint` | `#a7e5d3` | Live status. Affirmation. |
| `--grad-peach` | `#f4c5a8` | The headline accent glow. Warmth. |
| `--grad-lavender` | `#c8b8e0` | Process / lab placeholders. |
| `--grad-sky` | `#a8c8e8` | Default media gradient pair. |
| `--grad-rose` | `#e8b8c4` | Paired with peach for emphasis. |

### Functional

| Token | Hex | Use |
|---|---|---|
| `--danger` | `#c8351f` | Destructive actions, form errors, "hidden" flag |
| `--danger-bg` | `#fff3f0` | Background for danger notices |

**Color rules**

- Never use a gradient stop as a flat fill on type. Always inside a gradient, blur, or aspect-ratio media tile.
- One accent per screen. The home hero has the peach glow; nothing else on the page competes.
- The "live" pip is the only place mint appears as solid color, and it pulses (see Motion).
- Borders before fills. When in doubt, use `--line` over a tinted background.
- Danger is for action only. We do not paint red where we just want emphasis.

---

## 6. Space, grid, layout

- **Shell width:** `max-width: 1440px` with a fluid gutter `clamp(20px, 4vw, 56px)`.
- **Section rhythm:** vertical padding `clamp(60px, 9vw, 120px)`, single 1px `--line` between sections. No double rules.
- **Section head pattern:** § label on top (mono, uppercase, muted), h2 below (sans + one italic word). Always stacked, never inline columned.
- **Project mosaic:** 12-column grid; tiles span 4 / 8 / 12 columns. Mosaic is divided by `--line`, not gaps.
- **Density:** spacious. We'd rather have one item per row than three crammed.

---

## 7. Components

These exist in code; the [/design](/design) page renders each one live.

**Public-facing**

- **CTA button (`.cta`)** — ink fill, paper text, no radius. Arrow translates 3px on hover. Hover dims to `--ink-2`.
- **CTA variants (`.cta-sm`, `.cta-secondary`, `.cta-danger`)** — same shape, smaller / outlined / red-outlined for secondary and destructive uses. `.chip on` is a *filter*, never a primary action.
- **Tile (`.tile`)** — paper surface, 1px line cell wall, status pip top-left, mono metadata top-right, sans h3 + small description bottom. Hover lifts to `--paper-2`.
- **Status pip (`.status .pip`)** — 6px circle. `live` = mint with pulsing 10px ring. `beta` = ink. `soon` = muted-2. `archived` = ring only.
- **Post row (`.post-row`)** — 3-column: date column (mono with big sans day), body (no., title, excerpt, topic), arrow. Whole row clickable.
- **Principle card (`.principle`)** — № NN on top, h3, body. Three across on About.
- **Filter bar (`.filterbar` + `.chip` + `.search`)** — filter chips toggle on the left, mono count + search field on the right. Used identically on Blog and Projects browsers.
- **Form field (`.field`)** — label-above pattern, mono label, line-bordered (underline) input, error in `--danger` on the right of the label.
- **Code block (`.codeblock`)** — paper-2 background, mono lang tag + copy button on a paper-3 bar.
- **Carousel / media (`.media`, `.carousel`)** — 16:9 placeholder gradient until real media exists. Mono caption below.
- **Marquee (`.marquee`)** — site-footer scrolling line. Single accent: italic serif word with peach glow behind it.
- **Empty state (`.empty-state`)** — mono `§ Label` eyebrow, serif italic h3, muted lede, optional `.cta-sm` action. Used in admin lists, browsers with no matches, and the dashboard.

**Admin chrome**

- **Sidebar (`.admin-sidebar`)** — sticky 240px column, brand + nav with active-state border-left, footer with sign-out chip + "Open public site" link.
- **Page header (`.admin-page-header`)** — `§ crumb` + 32px h1 + optional lede + actions. One chrome for every list / setup page.
- **Sticky form bar (`.admin-sticky-stack`)** — used by `ProjectForm` for edit screens. Sticky breadcrumb + h1 + save status + section nav.
- **Boxed input (`.admin-input`, `.admin-textarea`, `.admin-select`)** — paper-3 fill with 1px line. Distinct from the public underline `.field` pattern; admin density warrants the visual frame.
- **Admin table (`.admin-table`)** — single source for all admin list views (`Th`/`Td` components in `components/admin/AdminTable.tsx`).
- **Stats grid (`.admin-stats`)** — dashboard stat cards. Mono label + 36px sans value.
- **Notice (`.admin-notice`, `.is-danger`)** — single-line ink-bordered banner; danger variant uses `--danger-bg`.

---

## 8. Motion

Motion is a **statement of presence**, not a transition library. Things either drift quietly or pulse to indicate something is alive. Nothing slides, swooshes, or springs.

| Pattern | Where | Detail |
|---|---|---|
| Atmospheric drift | Hero blobs (mint + lavender) | 22–28s `ease-in-out` `alternate`. Translate + scale ±10%. |
| Headline glow pulse | `.ac::before` peach glow | 6s `ease-in-out infinite`. Opacity 0.85 ↔ 1.0, scale 1 ↔ 1.06. |
| Live pip pulse | `.status.live .pip` | 2.2s `ease-out infinite`. Box-shadow ring expands 0 → 10px and fades. |
| Eyebrow pulse | `.hero .eyebrow .pulse` | 2s `ease-in-out infinite`. Opacity + scale on an 8px ink dot. |
| CTA glow drift | `.cta.cta-glow` (Contact) | 28s `ease-in-out infinite`. Background-position drift across 5 atmospheric stops. |
| Hover lift | Tiles, post rows | `transition: background .2s ease`. No transform. |
| CTA arrow | `.cta:hover .arrow` | `translateX(3px)` over 200ms. |
| Marquee scroll | Footer marquee | 40s linear infinite. |
| Brand bulb | Header brand `.dot` | Static; the live pip carries the "alive" signal. |

**Motion rules**

- Long durations (4s+) for ambient. Short (150–200ms) for input. Nothing in the 600ms–1.5s "page transition" range.
- All ambient animation is wrapped in `@media (prefers-reduced-motion: reduce) { animation: none !important; }`.
- No layout-affecting transitions. Hover changes color/background only.

---

## 9. Imagery

We don't generate decorative SVG illustrations and we don't use stock photography. Until a real screenshot or asset exists, we use a **gradient placeholder** in the right aspect ratio with a mono caption underneath. The placeholder *is* the design — it tells the reader "real thing pending."

Three placeholder variants ship today: `default` (mint→sky), `peach` (peach→rose), `lavender` (lavender→sky). Pick the one that doesn't compete with the section's accent.

---

## 10. Anti-patterns

Things we have actively decided not to do, in case it's tempting later:

- Rounded corners on UI surfaces. Inputs, buttons, tiles, code blocks — all square. The only round things are the live pip and the brand dot.
- Drop shadows on UI. The whole system is hairlines. (The exception is the *stuck* sticky bar in admin, which gets a subtle elevation shadow only when scrolled.)
- Emoji in headlines or marketing copy.
- Decorative SVG illustrations of "AI" — circuits, brains, sparkles, robots, glowing orbs.
- Aggressive gradient backgrounds. Atmosphere only.
- Hero copy with three clauses and an em-dash holding it together.
- Statistic bars on a landing page ("10x faster, 50% less, ∞ better").
- `.chip on` repurposed as a primary button. Filter chips and primary actions must stay visually distinct.
- More than one italic word per phrase. Spotlights stop working at five.
- Hardcoded hex literals in components. If a color shows up in two places it earns a token.

---

## 11. File map

| File | What it owns |
|---|---|
| `src/content/design.md` | This document. The why. The source of truth. |
| `src/app/(public)/design/page.tsx` | Live renders of every token + component. The what, visually. |
| `src/app/globals.css` | The system in CSS. The how. |
| `src/components/admin/*` | Admin chrome (`AdminShell`, `AdminPageHeader`, `AdminTable`). |
| `src/components/{Header,Footer,Marquee,...}.tsx` | Public chrome and shared components. |
| `src/lib/queries.ts` | Data access for public + admin pages. |

---

*v 1.1 · Last touched May 2026*
