# Build the "Content credentials panel" pattern: `ContentCredentials`

## 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
An expandable manifest: who made this, with which tools, and what changed since capture.

Use it when: Media pipelines, journalism, evidence, and EU AI Act Article 50 exposure from August 2026.

## What to build
An expandable provenance panel attached to an image or media file: who made it, with which tools, who signed it and what changed since capture, including any AI-edited region. When no credentials are present it says the origin is unknown, not that the file is fake. It renders as a pin on the media that opens a card, with a link to the full history.

### Props
- `manifest`: { status: "verified" | "invalid" | "none"; signer?: string; issuedAt?: Date; creator?: string; tools?: string[]; aiUsed?: { kind: "generated" | "edited"; region?: { x: number; y: number; w: number; h: number } }; history?: Array<{ action: string; tool?: string; at: Date }>; ingredients?: Array<{ title: string; status: "verified" | "none" }> } | null.
- `mediaLabel`: string, describes the asset, e.g. "Photo of a shop front".
- `watermark?`: { detected: boolean; provider?: string }, a durable signal checked separately.
- `open`: boolean and `onOpenChange(open)`, controlled.
- `showRegion`: boolean and `onShowRegionChange(show)`, overlay the AI-edited area on the media.
- `onInspect?()`: open a full verification view. The component only displays verification results; it never signs or verifies.

### States it must render
- Verified: a "CR" pin; the card lists creator, signer, date, tools and an AI line such as "AI-edited: background extended".
- Full history: an ordered list of actions since capture, with ingredients and their own credential status.
- AI region shown: the edited area outlined on the media with "AI-edited area" and a toggle to hide it.
- No credentials: neutral "No Content Credentials. Origin unknown" with a note that metadata is often stripped on upload or re-encode, and the watermark result if available.
- Invalid: amber "Credentials don't match this file. It may have been altered after signing", distinct from none.

### Interactions
- Clicking the pin calls `onOpenChange(true)`; Escape or clicking outside closes it and returns focus to the pin.
- "Show AI-edited area" toggles `onShowRegionChange`; it is only present when `aiUsed.region` exists.
- "Full history" expands the history list inside the card; "Inspect" calls `onInspect`.

### Edge cases
- `manifest` is null or status none: never use red, warning or "fake" language.
- Watermark detected but no manifest: say "No credentials, but a watermark from <provider> indicates AI generation".
- Ingredients with missing credentials: list them as "unknown origin" without downgrading the verified parent.
- Small thumbnails: the pin stays tappable (at least 24 px) and the card opens in a sheet at narrow widths.

## Must avoid
Treating an unsigned asset as proof of fakery — manifests strip on re-encode all the time.

## Engineering constraint
Pair signed manifests with watermarking; audit CDN and CMS so uploads do not strip metadata.

## Accessibility
- The pin is a button named "Content Credentials: verified" (or "none", "invalid"); the card is a dialog labelled with the media label.
- When the AI region is shown, describe it in text as well, e.g. "AI-edited area: right third of the image".

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