# Build the "Report and escalate" pattern: `ReportResponse`

## 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 clear route for harmful or high-stakes failures that reaches a human with the full context.

Use it when: Regulated domains, safety-relevant output, and anything with reputational blast radius.

## What to build
A report control attached to each AI message that opens a short form (reason, severity, optional note), shows who will receive it and what context is attached, and confirms the hand-off to a human. Reporting never requires leaving the conversation or digging into settings. It renders in the message action menu, with the form as a popover or sheet.

### Props
- `reasons`: Array<{ id: string; label: string; severity: "standard" | "urgent" }>, e.g. "Asked for sensitive data", "Harmful advice", "Wrong and costly".
- `selectedReasonId?`: string and `note`: string, controlled.
- `attachedContext`: Array<{ label: string; value: string }>, e.g. run id, model version, retrieved sources, conversation excerpt, shown before sending.
- `recipient`: { name: string; responseTime?: string }, who handles it, e.g. "Trust and safety team, replies within 4 hours".
- `status`: "closed" | "open" | "submitting" | "submitted" | "error".
- `receipt?`: { id: string; at: Date; nextStep: string }, shown after submitting.
- `onOpen()`, `onReasonChange(id)`, `onNoteChange(text)`, `onSubmit()`, `onCancel()` and `onHideMessage?()`: callbacks. The component never sends reports itself.

### States it must render
- Closed: a visible "Report" action (flag icon plus text) on every AI message, not hidden in an overflow menu only.
- Open: reason list, optional note, "Goes to <recipient>" and a "Sent with your report" list of attached context.
- Urgent reason selected: the form shows that it will be escalated immediately and offers to hide the message now.
- Submitted: a receipt with report id, time, who has it and what happens next, e.g. "A person will review this within 4 hours"; the message shows "Reported".
- Error: "Report not sent" with Retry, keeping the reason and note.

### Interactions
- Report calls `onOpen`; the form opens next to the message without navigating away.
- Selecting a reason calls `onReasonChange`; Send is enabled once a reason is chosen and calls `onSubmit`.
- Hide message calls `onHideMessage` and replaces the content with "Hidden: you reported this" and a show option.
- Cancel or Escape closes the form and returns focus to the Report button.

### Edge cases
- Missing run id or model version in `attachedContext`: show "Some details couldn't be attached" so triage knows, rather than failing silently.
- Reporting the same message twice: show the existing receipt and offer to add a note.
- The conversation contains sensitive data: the attached-context list states that the excerpt is included so the person can decide.
- A high-stakes report while the AI conversation continues: the message stays reported and hidden state persists if the person scrolls away or reloads, driven by props.

## Must avoid
Burying reporting in settings so the only visible option is abandoning the product.

## Engineering constraint
Attach the run ID, model version and retrieval set to the report or triage cannot reproduce it.

## Accessibility
- The form is a dialog labelled "Report this response"; reasons are a radio group.
- On submit, focus moves to the receipt and its next step is announced.

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