# Build the "Variations" pattern: `VariationPicker`

## Stack (edit this block to match your project)
- React 18+ with TypeScript: one function component, typed props, no `any`.
- Tailwind CSS, using your design system's tokens if you have them.
- Presentational only: data comes in through props, decisions go out through callbacks. No fetching or global state inside.
- If your codebase already has conventions (naming, shared components, tokens), follow them over this spec.

## What the pattern is for
Several candidate outputs shown side by side so choosing replaces re-prompting.

Use it when: Subjective output — images, copy, layouts, names — where recognition beats specification.

## What to build
A grid of several candidate outputs from one request, each a deliberately different direction, so a person picks the closest and refines it instead of rewriting the prompt. Earlier sets stay one click away, so a good candidate is never lost. It renders inline in a chat or in a generation panel.

### Props
- `sets`: Array<{ id: string; label: string; meta?: string; candidates: Array<{ id: string; preview: ReactNode; label: string; direction: string; seed?: string | number; parentId?: string }> }>, every set generated so far, newest last. `direction` says how a candidate differs, e.g. "Type-led, bold"; `parentId` records which candidate a varied set came from.
- `activeSetId`: string and `onSetChange(id: string)`, controlled set navigation.
- `selectedId?`: string and `onSelect(id: string | null)`, the candidate currently chosen for the next action.
- `status`: "generating" | "ready" | "varying" | "error" | "chosen", controlled by the parent.
- `expectedCount?`: number, tiles to reserve while generating (default 4).
- `maxRounds?`: number, the cap on "More like this" rounds, with `roundsUsed`: number.
- `chosen?`: { candidateId: string; setId: string; at: Date }, shown once a candidate is used.
- `onVary(candidateId: string, strength: "subtle" | "strong")`, `onUse(candidateId: string)` and `onRetry()`: callbacks. The component never generates anything itself.

### States it must render
- Generating: `expectedCount` reserved tiles with skeletons and a line such as "Generating 4 directions"; no layout shift when results land.
- Ready: each tile shows its preview, a letter, its label and its `direction` note; Use and More like this are visible but disabled until a tile is selected.
- Selected: the chosen tile is outlined with a check mark and the actions name it ("Use B", "More like B").
- Varying: a new set tab appears with reserved tiles labelled "From B"; earlier sets remain browsable.
- Chosen: a one-line receipt ("Used B from set 2") with a link back to that set.
- Error: tiles that did arrive stay usable; missing ones say "Didn't generate" with Retry.

### Interactions
- Clicking a tile selects it; clicking the selected tile again clears the selection.
- More like this asks for subtle or strong variation, then calls `onVary` with the selected candidate.
- Use calls `onUse` with the selected candidate; selecting a tile in an older set acts on that candidate, not on the latest set.
- Arrow keys move between tiles (roving tabindex); Enter or Space selects.

### Edge cases
- Two candidates in one set share the same `direction`: show "Similar to A" on the later tile, so a weak set is visible instead of looking like four real choices.
- Round cap reached: More like this is disabled with the reason ("3 of 3 rounds used").
- Fewer candidates returned than expected: collapse the empty tiles and say "3 of 4 returned".
- Text candidates (names, copy): previews clamp to equal height with an expand control so comparison stays side by side.

## Must avoid
Four near-identical candidates that cost four runs and give the user no real decision.

## Engineering constraint
Diversify with different seeds or sampling, not repeated calls; cache all candidates for later recall.

## Accessibility
- Tiles are buttons with `aria-pressed` and labels like "Option B: Big date, type-led, bold"; set tabs follow the tablist pattern.
- Announce new sets once ("Set 2 ready, 4 options"), not each tile.

## House rules
- Real `<button>`/`<input>` elements, full keyboard use, visible focus; announce status changes with `aria-live="polite"`; respect `prefers-reduced-motion`.
- All user-facing text comes from props (or a `labels` prop) so it can be translated.
- One consistent accent marks anything the AI made or is doing; amber means "needs a person", red means risky, green means done. Never rely on colour alone.
- Fits a 360 px wide container without horizontal scrolling.

## Acceptance checks
- [ ] Every state above can be rendered by passing props, with no internal fake data.
- [ ] Every interaction and edge case above behaves as described.
- [ ] Nothing in "Must avoid" can happen with this component.

## Deliver
1. The component file with its exported prop types.
2. A short usage example that renders each state.
