# Build the "Caveat" pattern: `Caveat`

## 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 plain statement of a known limitation, placed where it changes the user's next action.

Use it when: Domains where the model is reliably weak: recent events, arithmetic, legal specifics, rare languages.

## What to build
An inline notice attached to a specific part of an AI answer that states one known limitation, why it applies here, and what to do about it. It appears only when a condition is detected (stale data, unsupported language, unchecked arithmetic) and sits next to the affected value, not in a footer. It renders inside a chat answer, report or generated document.

### Props
- `condition`: { kind: string; detectedAt?: Date }, the detected trigger, e.g. "stale-sync"; the component renders nothing without one.
- `title`: string, the limitation in plain words, e.g. "The seat count may be out of date".
- `detail`: string, the specific reason, e.g. "It comes from the billing sync on 19 Sep, 12 days ago".
- `appliesTo`: string, a label or id for the value or passage affected, used to link the caveat to it visually.
- `actions?`: Array<{ id: string; label: string; kind: "fix" | "carry" }>, where "fix" removes the cause (re-sync, recalculate) and "carry" keeps the caveat attached when the content leaves (copy with note).
- `onAction(id: string)`: callback. The component never re-syncs or recalculates itself.
- `status`: "shown" | "resolving" | "resolved" | "dismissed", controlled.
- `resolution?`: string, what changed once fixed, e.g. "Up to date. 6 seats were added, so the total is $2,304 higher".

### States it must render
- Shown: an amber notice directly under or beside the affected value, which is itself marked, with title, detail and actions.
- Resolving: the notice is replaced by progress, e.g. "Syncing seats from billing", and the affected value dims.
- Resolved: the caveat disappears and a green line explains what changed; any updated value is highlighted briefly.
- Carried: after a carry action, a confirmation shows exactly the text that went with the content, including the note.
- Not shown: when no condition is passed, nothing renders, so there is no default disclaimer.

### Interactions
- Each action calls `onAction(id)`; the fix action is listed first.
- Hovering or focusing the caveat highlights the value it applies to, and the reverse.
- Dismiss, when offered, records that the person saw it and keeps a small marker on the value.

### Edge cases
- Several caveats in one answer: each sits next to its own value; never merge them into one generic block at the bottom.
- The condition clears while the caveat is shown (data synced elsewhere): switch to Resolved with the reason.
- The fix action fails: keep the caveat, add the error, and leave the carry action available.
- A caveat whose value is off-screen in a long answer: keep it adjacent to the value, not pinned to the viewport.

## Must avoid
A blanket footer disclaimer on every screen, which users learn to read as decoration.

## Engineering constraint
Trigger caveats from detected conditions — stale index, unsupported language — not from a static string.

## Accessibility
- The caveat is a `note` linked to the affected value with `aria-describedby`, so screen readers hear it when they reach the value.
- Announce Resolved with what changed.

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