# Plan picker

Choose a plan and seats. Prices are per seat and flat: the total is the per-seat price times seats, shown before anything is charged. The current plan is marked; the recommended one is called out but not pre-selected. What every plan includes, and how billing works, is the app's to say (`terms`), once, plainly, under the cards.

- Registry name: `plan-picker` (`@candor/plan-picker`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/plan-picker

## Install

```bash
npx candor-ui add plan-picker
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/plan-picker`.
It brings in: button, locale, ops, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import { PlanPicker } from "@/components/plan-picker"
import type { PlanTier } from "@/lib/ops"

const plans: PlanTier[] = [
  { id: "team", name: "Team", perSeat: 249, tagline: "The whole product for an in-house team", includes: ["Chat, agents and reports", "Approvals and audit log", "SSO sign-in"] },
  { id: "enterprise", name: "Enterprise", perSeat: 499, tagline: "Everything in Team, plus people behind it", includes: ["Everything in Team", "Security and deployment controls", "A named engineer on your account"], recommended: true },
  { id: "consultancy", name: "Agency", perSeat: 599, tagline: "For agencies running many clients’ apps", includes: ["Everything in Enterprise", "Clients’ apps under one account", "Per-client separation"] },
]

export default function Example() {
  return <PlanPicker className="max-w-4xl" plans={plans} current="team" seats={6} minSeats={6} terms="Prices are per seat, billed monthly. Changes take effect on your next invoice." onChoose={() => {}} />
}
```

## <PlanPicker>

Props:

- `plans: PlanTier[]`: The plans to choose from, one card each.
- `current?: PlanTier["id"] | undefined`: The account's plan, marked Current and selected at first. Undefined means no plan yet.
- `seats?: number | undefined` (default `1`): The starting seat count. Defaults to 1, and never below minSeats.
- `minSeats?: number | undefined` (default `1`): Seats can't go below members already in the account.
- `terms?: string | undefined`: What every plan includes and how billing works, e.g. "No trial: billing starts when you choose a plan."
- `onChoose: (plan: PlanTier["id"], seats: number) => void`: Called with the chosen plan and seat count; nothing is charged before this.
- `className?: string | undefined`

## Strings (`planPicker`)

The fixed words, English by default. Override any of them under `planPicker` in `<LocaleProvider strings={{ planPicker: { … } }}>`; the table is exported as `planPickerStrings`.

- `label`: Plans
- `seats`: Seats
- `fewerSeats`: Fewer seats
- `moreSeats`: More seats
- `atLeast`: At least {count}: one per member.
- `plan`: Plan
- `recommended`: Recommended
- `current`: Current
- `price`: ${amount}
- `perSeatMonth`: / seat / month
- `monthFor`: { one: "a month for {count} seat", other: "a month for {count} seats" }
- `updateSeats`: Update seats
- `choose`: Choose {plan}
- `chooseAPlan`: Choose a plan

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
