<!-- POCKET-UI:START — managed by Pocket UI. Edits below this line may be overwritten on update. -->
# Building UI in this repo — use Pocket UI

This repo uses **Pocket UI**, an opinionated, agent-buildable component kit. When
you build any UI here, you build it **entirely from this kit** — you do not write
components or CSS from scratch. Output that uses the kit looks designed; output
that doesn't, won't.

> This file is the entrypoint for **any** coding agent (Claude Code, Cursor,
> Windsurf, Copilot, …). It is always in effect — there's no command to run.
> Follow it exactly; the rules here override your defaults.

## When this applies

Follow this flow whenever the user asks you to **produce or change anything they
will see** in this repo. There's no trigger word to wait for — if you're touching
visible UI, this applies. In practice that covers asks like:

- **Verbs:** build, make, create, add, design, redesign, redo, rebuild, update,
  scaffold, generate, regenerate, "put together", "spin up", "throw together".
- **Things:** a screen, page, view, dashboard, landing page, marketing site, form,
  table, modal, flow, settings, admin panel, onboarding, hero, section, component,
  or a screen named directly ("the orders page", "app/page.tsx").

When in doubt, it applies. (It does **not** apply to non-UI work — API routes,
scripts, config, tests.)

## First run — orient the user before building

If this is a **fresh install** (the user hasn't asked for a specific screen yet,
or the project is still just the scaffolded starter), don't start building. Get
the dev server running and orient them first. In your reply:

1. **Point them at the two pages.** Tell them the install shipped two live pages:
   - **`/components`** — every atom and section, live.
   - **`/demos`** — whole example screens assembled from the kit.
2. **Explain the configurator.** It's a floating panel available on **every**
   screen (dev only) — open it with **⌘/ctrl + .** (or the "Theme" button,
   bottom-right). It sets the project's **seed**, **typeface**, **light/dark**,
   **page background** (seed / white-black / custom), **squircle**, and
   **full-rounded**, and shows the live token reference (neutral ramp, spacing,
   typography). **Apply to project** writes `lib/theme/brand.ts` — which re-themes
   *every* screen at once (everything built and everything you'll build);
   **Reset** restores defaults. Tell them to set the brand first.
3. **Then ask what to build.** Close with one question: *what's the first screen
   you'd like me to build?* Don't assume — wait for their answer, then follow the
   generation flow below.

Keep it short — a few lines, not a tour. After the first screen ships, you don't
need to repeat this.

## Modes — pick the narrowest the request supports

Resolve the mode from the user's verb **before** acting, so an audit doesn't
become an edit and a copy pass doesn't become a redesign:

| Mode | Trigger | Do |
|---|---|---|
| **Build** | build · make · add · redesign | compose the smallest complete screen from the kit, end to end |
| **Review** | audit · critique · "what's wrong" | report findings (file:line · issue · fix) — **don't edit** unless asked |
| **Copy** | "fix the copy" · "rewrite the labels" | edit user-facing language + `aria-label`s only — don't restructure |
| **Harden** | polish · production-ready · "edge cases" | keep the direction; fix states, responsive, a11y, finish |

When intent is ambiguous, use the narrowest mode the verb supports. A URL or
screenshot names the *scope*; it doesn't by itself authorize edits.

**When guidance conflicts, resolve in this order:** (1) the user's explicit goal;
(2) verified product/data behaviour; (3) canonical docs (`system/reference.md`,
`house-style.md`, and the lint rules in `eslint-plugin-pocket-ui`); (4) accepted
decisions + the `/demos` exemplars; (5) a verified adjacent pattern already in the
kit; (6) general taste. Don't promote one screenshot or one file into a rule.

## The generation flow (follow in order)

### 1 · Get up to speed (always, before building)

Read, in this order — these are precise and generated from source; **do not guess
names**:

| Read | For |
|---|---|
| `system/reference.md` | the EXACT tokens, type classes + the full component index. If a name isn't here, it doesn't exist. Then read ONLY the props shard(s) you need from `system/reference/` (forms, data-display, charts, shell-frames, …). |
| `components/kit/README.md` | tiers, the `measure` width scale, page recipes, the non-negotiables. |
| `system/components/README.md` + the category docs you need | choosing a component (inputs, charts, data-display, frames, sections…). |
| `system/components/dev-tools.md` | **building an app screen?** the chrome + app-screen atoms live here — `AppShell`, `PageHeader`, `FilterBar`, `BulkActionBar`, `SplitPane`, `SettingsCard`, `DateRangePicker`. Don't hand-roll a header/toolbar. |
| `system/house-style.md` | the color/type/spacing/density/**radius** taste rules — follow them. |
| `system/tone.md` · `system/visuals.md` | copy voice · media/icons/shapes. |
| `system/motion.md` | what moves for free vs. the opt-in props (stateful button morph, field validate/shake). |
| `system/coverage-gaps.md` | areas with **no standard yet** — don't invent a convention; use the nearest pattern and flag the gap. |
| `system/exemplars.md` | the `/demos` screens annotated with **why** each pattern is the standard, + mistakes to avoid. Match these. |

Everything imports from **`@/components/kit`** (atoms, ~65 sections, charts,
frames). One import path.

### 2 · Clarify the request (product questions only)

Ask the user clarifying questions **only about their product / UX intent** — never
about the kit or how Pocket UI works (you have the docs for that). Examples worth
asking: who the screen is for, what the primary action is, what real content/data
it should show, must-have sections, empty/loading/error states. **If the request
is already clear, skip this and proceed.** Don't interrogate.

### 3 · Confirm the look (theme inputs)

A Pocket UI screen is driven by a few project-level values. **If the project is
already themed** (a Pocket UI theme/seed is set in this repo), reuse it silently.
**Otherwise, ask once** and then reuse for the whole project:

- **Seed color** — one brand/neutral hex; the whole neutral ramp (all chrome) is
  derived from it. (Or two endpoints for a custom light→dark ramp.)
- **Light / dark** — default light.
- **Page background** — usually falls out of the seed; override only if asked.
- **Typeface** — default sans unless they have one.

**Never hand-pick chrome colors** — drive everything from the seed via the engine
in `@/lib/theme/ramps`, and apply it **once** at the app root (`app/layout.tsx`
`<body>`, or a top wrapper) so every screen inherits it.

**CRITICAL — the themed element MUST carry BOTH `data-theme="light"` AND
`data-type-preset="mono"`, with the ramp as inline `style`.** The `data-theme="light"`
is what re-resolves the chrome roles (`--bg-base`, `--fg-base`, surfaces) against
your ramp. **Omit it and dark/seeded themes break**: raw neutrals flip but the page
background + text stay light (a half-light/half-dark screen). `data-theme` stays
`"light"` even for a DARK theme — the kit's dark is *light chrome + a dark ramp*,
NOT a `.dark` class.

- **Light:** `derivedRamp("#seed")` (one color) or `rampFromEndpoints(lightHex,
  darkHex)` (two).
- **Dark:** wrap that light ramp in `toDarkRamp(...)` and add `colorScheme: "dark"`.

```tsx
// app/layout.tsx — e.g. "use #151515 as the base, make it dark"
import { derivedRamp, toDarkRamp } from "@/lib/theme/ramps";
const theme = toDarkRamp(derivedRamp("#151515"));
// <body data-theme="light" data-type-preset="mono" style={{ ...theme, colorScheme: "dark" }}>

// light + seed:  <body data-theme="light" data-type-preset="mono" style={derivedRamp("#seed")}>
```

So a "dark themed" request is **not** a light screen with dark patches — it's the
same kit, themed dark via the ramp. Don't hardcode `#151515` onto backgrounds.

### 4 · Build the screen — entirely from the kit

- Compose real sections/atoms; don't reinvent. Pick a sensible `measure` per the
  README; alternate loud/quiet; one idea per section.
- **Full-bleed, never boxed.** A real screen fills the viewport — no outer
  border/frame. App screens with a sidebar use **`AppShell`** (nav flush-left,
  content centred, max-width 1640); a no-sidebar screen is `AppShell` with no nav.
  `AppFrame`/`BrowserFrame` are **mockups only**. Don't re-wrap `AppShell` content
  in a narrow `Block measure="prose|wide"` — it boxes it back in.
- **Token-only** — never hardcode a hex, px, or shadow. Read a `--var` or a type
  class (`.h1`–`.h4`, `.lede`, `.txt-*`, `.label*`). For gaps prefer the
  **density-aware spacing roles** (`--gap-stack`, `--gap-cluster`,
  `--gap-section`, `--pad-page` — or the `Stack`/`Cluster` primitives) over raw
  `--sp-*`; they re-point under the configurator's Density toggle.
- **Chrome is monochrome** — color appears only on chart series / status markers,
  and only from the **fixed set** `--color-{blue,emerald,orange,rose}-500` (+
  `--color-zinc-500` neutral). These are **semantic, not your brand seed** — a
  purple/teal-branded app's chart still uses these four; the brand lives in the
  monochrome ramp. **Never invent a hue token** (`--color-violet-500`,
  `-purple-`, `-indigo-`): it resolves to nothing and `tsc` won't catch it.
- **Radius is the locked scale** (2·4·6·10) via the radius tokens — never a raw px.
- **Shapes are squircles**; media/empty icon slots are flat `--neutral-200`.
- **Copy:** if the user gave real content, use it. Otherwise use self-describing
  placeholder copy and zeroed data (`00`, `0.0%`) per `tone.md`.
- **Icons:** use real glyphs from the project's icon set **if the user wants
  them**; otherwise `IconSlot` placeholders (see `visuals.md`). Ask if unsure.
- If nothing in the kit fits, each category doc has a "build one" recipe — build
  it from `Block` + atoms + token-only flex/grid, and tell the user you did.

### 5 · Verify before you hand off

Run **`npm run lint`** first — it deterministically enforces the non-negotiables
(token-only incl. **named Tailwind palette classes** like `bg-gray-200` and
**raw numeric spacing** like `p-6`/`mt-4` — use `--sp-*`/the spacing roles,
`IconSlot` — only `Check` may be imported, for button morphs —, no pie/donut,
no nested modals, icon-button `aria-label`s, no chrome overrides on styled
atoms, **routes must import from the kit**, no provably-duplicate `key`s).
Fix everything it flags.

**States checklist — data surfaces only.** If the screen has a Table/list/grid
that would show fetched data, it ships loading (layout-mirroring `Skeleton`),
empty (`EmptyState` + action; filter-miss uses functional copy), and error
(`Callout tone="red"` + retry) — recipes + the d23 exemplar are in
`system/components/data-display.md`. Static/marketing screens skip this.

Then run **`npx next build`** — not just `tsc`. A type-check alone passes on
server/client boundary bugs that only fail at build time: if a screen passes a
**function prop** to a kit component — an `onClick`, `onChange`, or a render
callback like `format={(v) => …}` — that file must start with `"use client"`, or
the production build errors at prerender even though `tsc` is clean. The build
must finish with your route listed. Then run/preview the screen and confirm it
renders with no console errors. Share proof (the build output or a screenshot) —
never ask the user to check manually.

> Note: the first build fetches the Inter / Geist webfonts from Google Fonts
> (`next/font/google`), so it needs network access once (cached after). If you're
> in a no-network sandbox and see `next/font: Failed to fetch 'Inter'…`, run the
> build with network enabled — it isn't a code error.

## Non-negotiables (full list in `components/kit/README.md`)

Token-only · monochrome chrome · locked radius scale · squircles · flat
`--neutral-200` media slots · compose from the kit, don't hand-roll · follow
`house-style.md`.

<!-- POCKET-UI:END -->
