# Build the "Prompt template / fill-in form" pattern: `PromptTemplateForm`

## 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 structured form that produces a good prompt, trading expressive freedom for reliable output.

Use it when: Repeated jobs with known variables, and for users who freeze at a blank input.

## What to build
A short form that fills a versioned prompt template, with a live preview of the exact prompt that will run. It lets a person produce a reliable prompt for a repeated job by choosing a few values instead of writing from scratch. It renders in a side panel or modal beside the place the output will go.

### Props
- `template`: { id: string; version: number; title: string; body: string; fields: Array<{ key: string; label: string; type: "select" | "text" | "segmented"; options?: string[]; required?: boolean; allowCustom?: boolean }> }, where `body` contains `{key}` placeholders.
- `values`: Record<string, string> and `onChange(key: string, value: string)`, controlled field values.
- `fixedText?`: string[], any instructions the template always adds, shown in the preview so nothing is hidden.
- `meta?`: { editedBy?: string; editedAt?: Date }, shown as the template's provenance.
- `status`: "editing" | "generating" | "done" | "error".
- `onGenerate(compiled: { templateId: string; version: number; values: Record<string, string>; prompt: string })`: the component never runs the prompt itself.
- `onSwitchToFreeText?()`: leaves the form for a plain prompt box, carrying the compiled text.

### States it must render
- Editing: fields on one side, the compiled prompt below or beside them with each filled value highlighted and empty slots visibly marked.
- Incomplete: required fields empty; Generate is disabled and the missing slots are named.
- Generating: fields locked, progress on the button, preview still readable.
- Done: the result is handed to the parent; the form offers "Change inputs" to return to editing with values kept.
- Error: values stay as entered, with a message and retry.

### Interactions
- Changing any field updates the preview immediately, word for word.
- Generate calls `onGenerate` with template id, version, structured values and the compiled string.
- A select field with `allowCustom` accepts typed text as well as listed options.
- "Write it myself" calls `onSwitchToFreeText` with the current compiled prompt.

### Edge cases
- Templates with more than about 5 fields: show a field count and an estimated time beside Generate so length is visible; designers should split such templates.
- Every instruction that reaches the model appears in the preview; there is no hidden field or silent override.
- The template version changes while a person is editing: keep their values, show "Template updated to v5", and refresh the preview.
- A value containing braces or quotes: insert literally, never re-interpreted as a placeholder.

## Must avoid
Forms so long the user would have rather typed a sentence; hidden fields that silently override intent.

## Engineering constraint
Store templates as versioned prompt objects so you can improve wording without breaking saved runs.

## Accessibility
- Each field has a visible label; the preview is a labelled region updated with `aria-live="polite"` debounced to pauses in typing.
- Highlighted values in the preview are distinguished by underline or weight as well as colour.

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