# Pricing Page Builder

## Objective

Publish a pricing page whose numbers come from the real plans, not a hand-edited table.

An editable public pricing page whose tiers, prices, and feature rows are driven by the app's actual plan records.

## Before You Begin

This feature is being added to an application that already exists and already
works. Do not scaffold a new project, and do not assume a blank slate.

Inspect the codebase first and establish:

- The existing application structure and where code of this kind already lives.
- The framework and version in use.
- The existing design system — colours, spacing, typography, and component conventions.
- Existing UI components you can reuse instead of writing new ones.
- The existing database structure, if this feature needs to persist anything.
- The existing authentication and authorization system, if this feature is user-scoped.
- Dependencies already installed, so you don't add a library that duplicates one.
- The existing test setup and conventions.

Only start writing code once you understand the above. If the application
already implements part of this feature, extend it rather than replacing it.

## Implementation Instructions

1. Read prices and plan names from the billing system rather than from copy typed into the page. The page should be laid out by hand and priced by the system.
2. If the app already has a plan comparison surface for signed-in users, extend it so both the public page and the in-app upgrade screen render from one set of plan and feature records.
3. Give each plan an explicit public flag. Default it to hidden so legacy, custom, and internal plans never appear on the page just because someone created them.
4. Build the feature comparison from a shared list of feature rows with a value per plan, so every tier has the same rows in the same order even when the value is simply absent.
5. Do not cache the published page indefinitely. A price change in billing must invalidate the page, because a stale price on a public page is a commitment you may have to honour.

## UI and UX Requirements

Match the application's existing design system exactly. Reuse its components,
spacing, and typography. This feature should look like it was always there.

## Responsive Requirements

Works on mobile, tablet, and desktop. Touch targets are large enough to hit on a
phone, and nothing overflows horizontally at 320px.

## Accessibility Requirements

- Fully keyboard navigable.
- Correct semantic elements and ARIA roles.
- Visible focus states.
- Meets WCAG AA contrast.
- Dynamic changes are announced to screen readers.
- Respects prefers-reduced-motion.

## Edge Cases

- A price changed in the billing system while the published page still shows the old number is the worst failure here. Invalidate the page on plan change and show authors when the page last rebuilt.
- Currency and tax display differ by visitor region. Decide whether prices are shown inclusive or exclusive of tax, say which on the page, and do not guess a currency the plan is not actually sold in.
- A monthly and annual toggle must compute the annual figure from the real annual price, not by multiplying the monthly one. Show both the per-month equivalent and the amount actually charged, and round them consistently.
- Legacy and hidden plans must never appear publicly even when an existing customer is still on one, and the page must not imply that customer's plan no longer exists.
- Feature comparison rows misalign as soon as one tier has more features than another. Render every row for every tier and mark absence explicitly rather than collapsing the row.
- A signed-in visitor should see their current plan marked as current, and the call to action should read as a change of plan rather than a fresh signup.
- Prices with a promotional or introductory rate need the standard rate visible too, or the page will be read as the ongoing price.
- The page must render sensibly when a plan has no price yet, such as an enterprise tier, without printing a zero.

## Testing

Exercise the feature end to end in the running application. Cover every edge case
above, then run the existing test suite and confirm nothing regressed.

## Acceptance Criteria

- [ ] Tier names and prices are read from the billing records rather than stored as page copy.
- [ ] A price change in billing is reflected on the published page without a manual edit.
- [ ] Only plans explicitly flagged public appear, and hidden or legacy plans never render.
- [ ] Every comparison row is present for every tier, with absence marked rather than omitted.
- [ ] The billing period toggle shows both the charged amount and the per-month equivalent.
- [ ] Currency and tax treatment are stated on the page.
- [ ] A signed-in visitor sees their current plan identified.
- [ ] The feature matches the existing design system.
- [ ] No existing functionality is broken.

## Adaptation Rules

- Match the existing design system. Do not introduce a new colour palette,
  spacing scale, or component library.
- Reuse existing components and utilities wherever they fit.
- Follow the naming, file layout, and code style already present.
- Do not upgrade, replace, or remove existing dependencies to make this
  feature fit. Adapt the feature to the app, not the app to the feature.
- Do not break existing functionality. If a change is genuinely required in
  existing code, make the smallest one that works and say so.
- If something in these instructions conflicts with how the application is
  built, follow the application and explain the deviation.

## Final Verification

Before you report the work as done:

1. Re-read the acceptance criteria above and check each one against what you
   actually built.
2. Run the application and exercise the feature end to end.
3. Run the existing test suite and confirm you have broken nothing.
4. Check the feature on mobile, tablet, and desktop widths.
5. Check keyboard navigation and focus handling.
6. Summarize what changed: files added, files modified, and anything you
   deliberately did differently because of how this application is built.

If any acceptance criterion is unmet, fix it before reporting completion.
