# Build the "Result filter and facets" pattern: `ResultFacets`

## 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
Post-generation filters that narrow a set of candidates rather than regenerating it.

Use it when: Retrieval, search and multi-candidate generation where the good answer is already in the set.

## What to build
A set of filters that narrows a list of results the agent already returned, without running it again. Each facet option shows how many results it keeps, and clearing filters brings back the full set instantly. It renders above or beside a result list or table.

### Props
- `facets`: Array<{ key: string; label: string; options: Array<{ value: string; label: string }>; multi?: boolean }>, e.g. a "Renews" facet with months.
- `items`: Array<{ id: string; facets: Record<string, string | string[]> }>, the full returned set; the component counts and filters over it in memory.
- `selected`: Record<string, string[]> and `onChange(selected: Record<string, string[]>)`, controlled filter state.
- `onFiltered?(ids: string[])`: called with the visible ids whenever the selection changes, so the parent renders the list.
- `runInfo?`: string, e.g. "Ran once, 38 credits", shown so a person sees filtering costs nothing more.
- `onRefine?()`: an explicit, separate action for a new run when the answer is not in the set. The component never triggers a run from a filter.

### States it must render
- Unfiltered: all facets with counts per option and "Showing 12 of 12".
- Filtered: active options pressed, "Showing 4 of 12", active filters as removable chips, and Clear filters.
- No matches: "No results match these filters" with the filters that caused it and Clear filters; the full set is still held.
- Zero-count option: shown disabled with "0" rather than hidden, so the person knows the value exists.

### Interactions
- Toggling an option recomputes counts and visible ids instantly from `items`, then calls `onChange` and `onFiltered`.
- Within a facet, options combine with OR; across facets, with AND. Counts reflect the other active facets.
- Removing a chip or pressing Clear filters restores the previous view without any network call.
- "Search again" (if `onRefine` is provided) is visually separate and labelled with its cost.

### Edge cases
- An item with multiple values for a facet (e.g. several risk signals): it counts under each value.
- An item missing a facet value: grouped under "Not set" so it is never silently excluded.
- The parent replaces `items` with a new run: keep selections whose options still exist and drop the rest with a notice.
- Large sets (thousands of items): counting must stay fast; memoise per selection.

## Must avoid
Filters that trigger a fresh expensive run and throw away results the user was still comparing.

## Engineering constraint
Filter client-side over the returned candidate set where possible; keep the unfiltered set in memory.

## Accessibility
- Each facet is a group with a legend; options are checkboxes or pressed buttons whose names include the count.
- Announce the result count after each change, e.g. "Showing 4 of 12".

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