# Build the "Randomize" pattern: `RandomizeControl`

## 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
A surprise-me action that produces a valid result with no input, lowering the cost of the first try.

Use it when: Exploratory creative tools, and any surface where a blank input is the drop-off point.

## What to build
A "Surprise me" control that produces a valid result with no input, then shows the seed and the full prompt it chose so a lucky result can be reproduced and steered. It keeps a short history of rolls in the session. It renders beside an empty input or generation canvas.

### Props
- `dimensions`: Array<{ key: string; label: string; options: string[]; locked?: string }>, the things a roll can vary (palette, layout, length); a locked value is held fixed across rolls.
- `current?`: { seed: string; picks: Record<string, string>; prompt: string; preview?: ReactNode }, the latest roll.
- `history`: Array<{ seed: string; picks: Record<string, string>; preview?: ReactNode }>, rolls from this session, newest first.
- `status`: "empty" | "rolling" | "ready" | "error".
- `onRoll(locks: Record<string, string>)`: requests a new roll with current locks. The component never generates itself.
- `onLock(key: string, value: string | null)`: pins or releases one dimension.
- `onRestore(seed: string)` and `onUse(seed: string)`: return to an earlier roll, or commit the current one.

### States it must render
- Empty: a short invitation and the Surprise me button; the prompt area says nothing has been picked yet.
- Rolling: button busy, previous result kept visible until the new one arrives.
- Ready: the result, its seed with a copy control, each picked value with a lock toggle, and the full prompt that was used.
- History: earlier rolls as small thumbnails with their seeds; the current one is marked.
- Error: the roll failed; the previous result and history stay.

### Interactions
- Surprise me calls `onRoll` with the current locks, so a person can keep the palette they liked and vary the rest.
- Locking a pick calls `onLock`; locked picks show a lock state and are not changed by the next roll.
- Selecting a history item calls `onRestore(seed)`; Copy seed copies it and confirms briefly.
- Use this calls `onUse` with the current seed.

### Edge cases
- Every result, including the first, must show its seed and prompt; no roll is ever unrecoverable.
- All dimensions locked: the button changes to "Re-run this seed" since nothing is left to vary.
- History beyond about 12 items: keep the newest and say how many older rolls were dropped.
- Restoring a seed whose options no longer exist: show the stored picks and flag the missing ones.

## Must avoid
Randomness with no path back — the user gets something interesting and cannot steer toward it again.

## Engineering constraint
Seed the randomiser and surface the seed so a lucky result is reproducible.

## Accessibility
- Lock toggles are buttons with `aria-pressed` and labels like "Lock palette: dusk".
- Announce a new roll by its key picks, not by repeating the full prompt.

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