# Build the "Cost estimate" pattern: `CostEstimate`

## 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
The price of an action — credits, tokens, minutes, dollars — shown before the user commits to it.

Use it when: Metered products and long agent runs where a careless click is expensive.

## What to build
A panel that shows what an action will cost (credits, tokens, minutes or money) before a person commits, with the range, the remaining balance after the run, and any cap it would exceed. Changing scope or depth updates the figure immediately. It renders next to the Run button of a metered action.

### Props
- `estimate`: { amount: number; unit: string; low?: number; high?: number; moneyEquivalent?: string; durationLabel?: string; basis: string }, where `basis` explains the source, e.g. "Estimated from 2,140 runs of the same shape".
- `balance?`: { remaining: number; usedThisPeriod?: number; allowance?: number }, used to draw the before and after bar.
- `cap?`: { amount: number; label: string; owner?: string }, a hard per-run ceiling enforced by the server; the component only displays it.
- `options?`: Array<{ id: string; label: string; choices: Array<{ id: string; label: string; hint?: string }>; selectedId: string }>, scope and depth controls that change the estimate.
- `onOptionChange(optionId: string, choiceId: string)`: the parent recalculates and passes a new `estimate`.
- `status`: "estimating" | "ready" | "over-budget" | "running" | "done" | "error", controlled by the parent.
- `actual?`: { amount: number }, the final spend once done.
- `onRun()`: callback for the commit button, whose label repeats the cost. The component never starts the run itself.

### States it must render
- Estimating: the previous figure dims with a quiet indicator; Run is disabled until the new figure arrives.
- Ready: "This run will cost about 1,248 credits" with "about $12.48, about 6 min, +/-6%", a bar showing used, this run and left after, and the basis line.
- Over budget: the amount exceeds balance or cap; the bar segment and text turn red, the reason is stated ("Over the team's 2,000-credit cap per run") with a suggestion to narrow scope, and Run reads "Over budget" and is disabled.
- Running: Run is replaced by a progress line; options are locked.
- Done: actual next to estimate, e.g. "Spent 1,231 of an estimated 1,248 credits".
- Error: "Couldn't estimate this run" with Retry; Run stays disabled rather than running blind.

### Interactions
- Changing a scope or depth choice calls `onOptionChange`; the figure updates without a page reload and a brief highlight marks the change.
- The Run button label always includes the amount, e.g. "Run on 312 tickets, 1,248 credits", so the commit point and the price are the same element.
- A "Try on 10 first" choice, when provided in `options`, is shown first as the cheapest way to check.

### Edge cases
- No historical runs of this shape: show a wider range and the basis "Rough estimate, no past runs to compare", never a falsely precise number.
- Actual exceeds the estimate by more than the stated range: show the difference plainly in Done.
- Free actions: show "Free" rather than hiding the panel, so people learn which buttons cost nothing.
- Balance unknown: hide the bar and keep the amount.

## Must avoid
Revealing spend only in a monthly invoice, teaching users to fear every button.

## Engineering constraint
Estimate from historical runs of the same shape; enforce a hard budget ceiling server-side.

## Accessibility
- The figure sits in an `aria-live="polite"` region that announces the new total after options change, debounced so rapid changes announce once.
- The bar has a text equivalent, e.g. "7,200 used, this run 1,248, 3,552 left after".

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