# Usage Pricing (Moonarc)

Usage-based pricing: a slider of monthly usage and every plan priced at the usage picked, each price rolling to its new figure, the cheapest plan for that usage lit and badged. A plan is a base price, what it includes, the rate for each block over it and an optional limit, past which it says so instead of a price. The slider is a group of radios drawn as a track with a thumb, so arrow keys and focus are native; every price at every stop is worked out at build and :has() shows the one picked, and a screen reader hears that price, not the reel. Zero JavaScript.

- Pro block. Install: `npx shadcn@latest add @moonarc-pro/pricing-usage` (license key required)
- Tier B · category section · trigger click
- Readout: `<PricingUsage stops={[…]} unit={{ one, many }} plans={[{ base, included, over, per }]} />`
- Browser support: widely (Chrome 105 · Firefox 121 · Safari 15.4); elsewhere: without :has() the first stop's prices stay
- Measured cost: 0 B JS (CSS 13.9 kB raw)
- Page: https://moonarc.dev/components/pricing-usage/

## Props

| Prop | Type | Default | Description |
|---|---|---|---|
| `plans` | `{ name; blurb?; base; included; over?; per?; max?; features?; action; featured? }[]` | none | One to four plans. The price at a usage is base plus, for every block of per units (1,000 when unset) over included, over. A plan without over, or past its max, prints the overLimit words instead of a price. |
| `stops` | `number[]` | none | Two to eight usage values, low to high: the slider's stops. |
| `start` | `number` | `0` | The stop picked at first, by index. |
| `unit` | `{ one: string; many: string }` | none | The unit, singular and plural. |
| `currency` | `string` | `'$'` | The currency symbol, printed before the figure. |
| `locale` | `string` | `'en-US'` | A BCP 47 locale for the numbers (the stops are compact: 50K, 1M). |
| `words` | `Partial<{ usage; perPeriod; includes; then; overLimit; best }>` | none | Every word the section prints around the figures. |
| `eyebrow` | `string` | none | A short line above the heading. |
| `headline` | `string` | none | The heading. Slot "headline" wins and may hold an <em>. |
| `copy` | `string` | none | A line under the heading. |
| `level` | `2 | 3` | `2` | Heading level of the heading. |
| `tone` | `'surface' | 'muted' | 'ink' | 'brand'` | `unset (paints nothing)` | The section's ground. |
| `density` | `'compact' | 'cozy' | 'roomy'` | `'cozy'` | Block padding ×0.8 / ×1 / ×1.25 through --ma-density. |

## Usage

```astro
---
import PricingUsage from '@/components/moonarc/pro/PricingUsage.astro';
---
<PricingUsage
  eyebrow="Pricing"
  unit={{ one: 'email', many: 'emails' }}
  stops={[3000, 50000, 100000, 500000, 1000000]}
  start={1}
  plans={[
    { name: 'Free', base: 0, included: 3000, max: 3000, features: ['One domain'], action: { href: '/signup/', label: 'Start free' } },
    { name: 'Pro', base: 20, included: 50000, over: 0.9, per: 1000, features: ['Ten domains', 'Webhooks'], action: { href: '/signup/', label: 'Start with Pro' }, featured: true },
    { name: 'Scale', base: 90, included: 250000, over: 0.6, per: 1000, features: ['Dedicated IP'], action: { href: '/contact/', label: 'Talk to us' } },
  ]}
>
  <span slot="headline">Pay for what you <em>send</em></span>
</PricingUsage>
```

## Reduced motion

The prices change in place without rolling and the thumb moves at once.

## With ClientRouter

CSS only; the stop picked resets on a fresh page.

## Craft

- Every price at every stop is worked out at build and printed in a reel; the stop picked moves the reel one line per stop, so the figure rolls and never computes in the browser.
- The cheapest plan at each stop is known at build too: each plan carries the stops it wins as a list, and [data-best~="3"] lights it when the third stop is picked.
- A slider made of radios: one checked at a time, arrow keys move between stops, each stop is labelled with the full number and the unit for a screen reader, and the compact label (50K) is for the eye.
- The reel is hidden from assistive technology; beside it, only the price at the stop picked is shown to a screen reader, so moving the slider never reads out every figure.
- A plan past its limit says so rather than showing a price nobody can pay.

## Replaces

- Pricing calculators with a usage slider
- Usage sliders on API pricing pages

## Source

Pro block. The source is served by the license-gated registry: `npx shadcn@latest add @moonarc-pro/pricing-usage` with a key in components.json (https://moonarc.dev/account/).
