# Build the "Consent and data ownership" pattern: `CaptureConsentPanel`

## 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
Explicit control over what the system may retain, train on, and share, stated before capture.

Use it when: Anything recording people, and every enterprise or regulated deployment.

## What to build
A panel shown at the moment of sensitive capture (a recording, a transcript, an upload) that states what will be kept, for how long, who it is shared with and whether it is used for training, and collects an explicit answer from each person affected. Capture stays locked until everyone has answered, and each answer is recorded with the notice version. It renders before a call, recording or data collection starts, and as a join prompt for participants.

### Props
- `retention`: Array<{ id: string; label: string; detail: string; enabled: boolean; editable: boolean; policyNote?: string }>, e.g. transcript shared with attendees, audio deleted after 30 days, training off by workspace policy.
- `onToggleRetention?(id: string, enabled: boolean)`: for the host, before capture starts.
- `participants`: Array<{ id: string; name: string; role?: string; consent: "waiting" | "agreed" | "declined"; at?: Date }>.
- `viewer`: "host" | "participant", which view to render.
- `noticeVersion`: string, shown and recorded with each answer.
- `captureStatus`: "locked" | "capturing" | "not-capturing", controlled by the parent, which gates the actual pipeline.
- `onAnswer(participantId: string, allow: boolean)`: records a participant's choice. The component never starts capture itself.
- `onRevoke?(participantId: string)`: lets a participant withdraw during capture.

### States it must render
- Host, waiting: retention list with switches, the participant list with "Agreed 9:56" or "Waiting", and the line "Capture is locked until every attendee answers"; the badge reads "Not recording yet".
- Participant prompt: a plain list of what is kept (ticks) and what is never done (crosses, e.g. "Never used to train AI models"), with "Join and allow notes" and "Join without recording" as equal-weight buttons, and "You can change this at any time. Notice v3."
- Capturing: a red badge naming exactly what is being recorded, e.g. "Recording transcript + audio", and when capture started.
- Declined: capture stays off for the session, with "Not recording: one attendee declined".
- Consent record: who agreed to what, the basis ("Join prompt, notice v3") and when.

### Interactions
- Participant buttons call `onAnswer`; neither option is pre-selected or styled as the default.
- Host retention switches are disabled once capture has started; changes after that require a new notice.
- Revoke during capture calls `onRevoke` and the badge switches immediately to not capturing once the parent confirms.

### Edge cases
- A participant who joins late: capture shows as paused or excluded for them until they answer.
- Everyone agreed but the host turned every retention item off: say "Nothing is kept" rather than showing a recording badge.
- The notice changes between answers: earlier answers are marked as given to the older version and those participants are asked again.
- Training use governed by workspace policy: show the switch locked with the policy note.

## Must avoid
Consent buried at signup that never resurfaces when the sensitive capture actually happens.

## Engineering constraint
Consent state must gate the pipeline, not just the UI; log the basis and version of what was agreed.

## Accessibility
- The participant prompt is a dialog labelled by its heading, with focus on the heading, not on the allow button.
- Announce capture starting and stopping, and each participant's answer in the host view.

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