Skip to content

Design system

The parts.

Every part Marquee ships, rendered once with real content: 18 sections, 16 primitives, 67 tokens per mode. Four of the primitives are tt-ui parts — the library owns Escape, the focus return and the submit flow; this theme owns every class string, glyph and word in them.

18 sections16 primitives67 tokens2 modes

src/components · src/components/ui · src/components/ui/_tt · src/theme.css · src/fonts.ts

Tokens

66 variables per mode in src/theme.css: 37 colour, 25 type, four shape, weight and motion. These chips read the active mode tokens.

Surfaces and pairs

  • Aa

    --background

    --foreground

  • Aa

    --card

    --card-foreground

  • Aa

    --primary

    --primary-foreground

  • Aa

    --secondary

    --secondary-foreground

  • Aa

    --muted

    --muted-foreground

  • Aa

    --accent

    --accent-foreground

State ramp

  • --primary

  • --primary-hover

  • --primary-active

  • --surface-tint

Type · ratio 1.333

--font-display · Bricolage Grotesque

Aa Bb Cc 0123

--font-text · Schibsted Grotesk

Aa Bb Cc 0123

--font-mono · Spline Sans Mono

Aa Bb Cc 0123

  • d4 · 3.556rem · home h1Revenue, reconciled.
  • d3 · 2.667rem · page h1Wire. Match. Close.
  • d2 · 2rem · section h2Metrics that survive an audit
  • d1 · 1.5rem · card titleNet revenue retention
  • lead · 1.125remThe section lede, one sentence, twenty words at most.
  • body · 1remRunning prose: every figure tied to a matched invoice.
  • small · 0.875remCard body, nav links, buttons, the match log.
  • caption · 0.75remTicker items, chips, table cells, footnotes.
  • label · caption + 0.08emThe close

Radius, lines and the drift

--radius 0.875rem · --radius-inner 0.625rem

--border · hairlines never carry structure alone

--duration-ticker 90s · the one moving part

The marquee drift. Everything else is placed.

Sections

Each section rendered once, at the width and with the content it ships with.

site-header

src/components/site-header.tsx

Sticky nav: wordmark, four links, the mode toggle and the primary action. Below md it collapses into a JS-free details menu.

hero

src/components/hero.tsx

The centered manuscript and the reconciliation surface: MRR at display size, twelve months of bars, the wire, and the match verdict.

Subscription analytics

Revenue, reconciled.

Recur reads your billing feeds, matches every invoice to the ledger, and publishes metrics your accountant will sign.

No card · free to $2,500 MRR · export everything, any time

recur · the close · March

Δ $0.00

Monthly recurring revenue

$128,410

+6.1% vs February

The wire

  • 14:02invoice 8841 matched$429.00
  • 14:02new subscriber · Growth$49.00
  • 14:05upgrade · Seed → Growth+$49.00
  • 14:11refund posted$99.00
  • 14:19dunning recovered$147.00

2,412 of 2,412 normalized records matched

Δ $0.00

ticker-rail

src/components/ticker-rail.tsx

THE SIGNATURE — a full-bleed strip of timestamped events drifting past. Three feeds: wire, match, wins. A Pause switch in the feed cell; pauses on hover and focus too; static under reduced motion. Shown here on the wire feed.

The wire

  • 14:02 UTC · invoice 8841 matched · $429.00 · Δ $0.00
  • 14:02 UTC · new subscriber · Growth · $49.00 MRR
  • 14:05 UTC · upgrade · Seed → Growth · +$49.00 MRR
  • 14:07 UTC · invoice 8842 matched · $1,188.00 · Δ $0.00
  • 14:11 UTC · refund posted · $99.00 · ledger updated
  • 14:19 UTC · invoice 8843 · Δ $84.00 · flagged for review
  • 14:19 UTC · dunning recovered · $147.00 · 2nd retry
  • 14:23 UTC · invoice 8844 matched · $588.00 · Δ $0.00

sources-band

src/components/sources-band.tsx

Eight available sources on a hairline grid, each derived from the canonical fixture.

Sources

Eight feeds, one ledger

Review the eight sample adapters, then inspect objects, permissions and mapping evidence on the integrations page.

  • Stripe

    Billing

  • Paddle

    Merchant of record

  • Chargebee

    Billing

  • Lemon Squeezy

    Merchant of record

  • App Store

    Mobile

  • Play Billing

    Mobile

  • Braintree

    Billing

  • Manual invoices

    Manual

Inspect sample access

integration-catalog

src/components/integration-catalog.tsx

Local search and category filtering with available, sample-authorized, partial, planned and recoverable no-result states. No request is simulated.

Source catalog

Eight adapters are available in this sample; planned sources stay visibly unavailable.

Category

9 sources

Stripe

Billing

Sample authorized

Invoices, subscriptions, customers and balance transactions

4 read-only object families

Inspect sample

Paddle

Merchant of record

Available

Checkout invoices, adjustments and subscription changes

Merchant-of-record normalization

Connect provider

Chargebee

Billing

Available

Subscriptions, invoices, credit notes and customer records

Recurring billing objects

Connect provider

Lemon Squeezy

Merchant of record

Available

Orders, subscriptions, refunds and tax-inclusive totals

Merchant-of-record normalization

Connect provider

App Store

Mobile

Available

In-app renewals, proceeds and subscription status changes

Mobile subscription receipts

Connect provider

Play Billing

Mobile

Available

Google Play subscription purchases, renewals and reversals

Mobile subscription purchases

Connect provider

Braintree

Billing

Available

Card and PayPal transactions, disputes and settlements

Payment and settlement objects

Connect provider

Manual invoices

Manual

Partial sample

CSV or email-in invoices with explicit ledger mapping

77 sample records, 1 variance

Start setup

RevenueCat

Mobile

Planned

No adapter or sample connection is included in this release

Planned, not available

No adapter or action included

State coverage

Stripe

Billing

Sample authorized

Invoices, subscriptions, customers and balance transactions

4 read-only object families

Inspect sample

Manual invoices

Manual

Partial sample

CSV or email-in invoices with explicit ledger mapping

77 sample records, 1 variance

Start setup

RevenueCat

Mobile

Planned

No adapter or sample connection is included in this release

Planned, not available

No adapter or action included

No source matches “warehouse ledger mirror”

Query and category stay visible; Clear filters returns focus to search.

connection-detail

src/components/connection-detail.tsx

The Stripe sample expanded through read-only objects, sync health, run stages, exact mappings and a frontend-only setup checklist.

Sample connection

Stripe close evidence

Inspect what the sample reads, when it ran, and what blocks publication.

Sample authorizedRead-only access

Healthy sample

Stripe is sample-authorized and 5 minutes old at evaluation.

1,904

Retained sample records

31 Mar 2026, 14:24 UTC

Last sample sync

As of 31 Mar 2026, 14:29 UTC · 30-minute rule · 1,904 new records accepted

0 identity gaps; the March statement published after variance resolution

Run 442 in order

Automated identity match 3m 41s · 14 currencies · ECB daily rates

  1. Identity match

    2,412 of 2,412

    Every source record has an invoice identity; this does not clear value differences.

  2. Variance review

    Invoice 8843 · $84.00

    Manual invoices is linked, but its ledger value needs review.

  3. Resolution

    31 Mar 2026, 14:23 UTC

    The ledger mapping is corrected before publication.

  4. Published

    Δ $0.00

    March 2026 closes after the exception is resolved.

Field mapping

Long source paths remain exact; ignored fields never become zero values.

Stripe sample field mapping
Source fieldinvoice.idRecur conceptInvoice identityStatusMappedRuleExact source identifier
Source fieldcustomer.idRecur conceptSubscriber identityStatusMappedRuleStable customer join
Source fieldsubscription.current_period_endRecur conceptService period endStatusMappedRuleUTC timestamp
Source fieldbalance_transaction.exchange_rateRecur conceptExchange rateStatusMappedRuleCompared with ECB daily rate
Source fieldinvoice.metadata.ledger_account_codeRecur conceptLedger accountStatusMappedRuleMapped to SaaS subscription revenue
Source fieldcustomer.descriptionRecur conceptNoneStatusIgnoredRuleNot used in reconciliation

Health states

Delayed sample

The sample is 55 minutes old, beyond the 30-minute rule.

1,904

Retained sample records

31 Mar 2026, 13:34 UTC

Last sample sync

As of 31 Mar 2026, 14:29 UTC · 30-minute rule · 0 new records accepted

Keep the prior sample visible; your backend should retry the sync

Partial sample

Manual invoices are identity-matched, but one value needs review.

77

Retained sample records

31 Mar 2026, 14:19 UTC

Last sample sync

As of 31 Mar 2026, 14:29 UTC · 30-minute rule · 77 new records accepted

Invoice 8843 carries an $84 variance until the mapping is resolved

Failed sample

The sample request failed before new records were accepted.

1,904

Retained sample records

31 Mar 2026, 13:34 UTC

Last sample sync

As of 31 Mar 2026, 14:29 UTC · 30-minute rule · 0 new records accepted

Retain the prior sample and wire retry behavior in your backend

Read-only boundary

Illustrative Recur permission labels are visible; authorization and retries belong to your backend.

Partial mapping

Manual invoice 8843 stays blocked until its ledger account is mapped.

Partial manual invoice field mapping
Source fieldmanual_invoice.invoice_numberRecur conceptInvoice identityStatusMappedRule77 sample records are identity-linked
Source fieldmanual_invoice.ledger_account_codeRecur conceptLedger accountStatusNeeds mappingRuleMap invoice 8843 before publication
Source fieldmanual_invoice.internal_review_noteRecur conceptNoneStatusIgnoredRuleNot used in reconciliation

Blocked prerequisite

The recovery instruction and frontend boundary remain visible together.

  • Provider objects reviewed

    Complete

    Manual invoice columns are documented

  • Ledger account missing

    Blocked

    Map manual_invoice.ledger_account_code before publication

  • Backend connection added

    Frontend only

    Upload, retries and persistence belong to your backend

metrics-grid

src/components/metrics-grid.tsx

Six metric cards, each opening with a real sample figure; the delta card carries the chip.

The close

Metrics that survive an audit

Every number below is computed from matched invoices, so the board pack and the books agree.

  • MRR + movements

    $128,410

    New, expansion, contraction and churn split out, month by month.

  • Net revenue retention

    100.6%

    Opening MRR carried through expansion, contraction and churn, with the customers driving it named.

  • Revenue churn

    1.4%

    Churned MRR against the opening balance; early-warning flags on usage cliffs.

  • Records matched

    2,412

    Three sources, one invoice identity per line before any value is reviewed.

  • Forecast

    $1.54M ARR

    A twelve-month run-rate from matched invoices, not from optimism.

  • The delta

    Δ $0.00

    Every metric ties to the ledger; anything that will not match gets flagged.

reconcile

src/components/reconcile.tsx

Wire, match, close as a ruled 7/5: the three steps beside a match run with one flagged row.

How it works

Wire, match, close

Three steps between a charge landing and a statement you can sign.

  1. The wire

    Every charge, refund and credit note arrives from your billing feeds within minutes.

  2. The match

    Each event is paired with its invoice and its ledger entry. Anything that will not pair gets flagged, not smoothed over.

  3. The close

    When the delta reads zero, the month's statement publishes itself. Your accountant gets a trail, not a spreadsheet.

Match run 442

Identity match · 3m 41s

5 sample invoices from match run 442 with their reconciliation deltas
InvoiceAmountDelta
8839$1,240.00Δ $0.00
8840$88.00Δ $0.00
8841$429.00Δ $0.00
8842$1,188.00Δ $0.00
8843$536.00Δ $84.00

1 value variance · identity matched · publication waits

proof

src/components/proof.tsx

Named outcome quotes: the lead quote at display size, two supporting, each beside the figure it claims.

Customers

Numbers other people sign

Three teams, three closes, outcomes they put their names on.

“Our close was nine days of spreadsheet archaeology. With Recur it is one afternoon, and the auditors take the statement as-is.”

9 days → 1 afternoon

monthly close

Maren Ilves

Finance lead, Standfirst

“Recur flagged a cohort going quiet 41 days before the cancellations landed. We called them; most stayed.”

Dele Akande

Founder, Loam

41 days

earlier churn warning

“The board pack is now an export of the close. Nobody re-derives MRR in a slide the night before.”

Sofia Brandt

COO, Tidemark

0

hand-built board slides

product-tour

src/components/product-tour.tsx

Three surfaces in alternating 7/5 splits, wire to match to close, ending in the Δ $0.00 band. Shown without its page lead; /product hands one in.

Wire. Match. Close.

01 · The wire

See money as it moves

Charges, refunds, upgrades and credit notes land within minutes of the processor, normalized across eight sources and fourteen currencies.

The wire · today

  • 14:02invoice 8841 matched$429.00
  • 14:02new subscriber · Growth$49.00
  • 14:05upgrade · Seed → Growth+$49.00
  • 14:07invoice 8842 matched$1,188.00
  • 14:11refund posted$99.00
  • 14:19dunning recovered$147.00

02 · The match

Nothing smoothed over

Every event pairs with its invoice and ledger entry. A row that will not pair is flagged and assigned, so exceptions get owners instead of burial.

Match run 442

Δ $84.00
  • stripe · 1,904 records0 identity gaps
  • paddle · 431 records0 identity gaps
  • manual · 77 records1 value variance

2,412 identity-matched · 1 value variance under review

03 · The close

A statement, not a spreadsheet

When the delta reads zero the statement publishes: MRR movements, retention, cohorts and forecast, each figure tied to matched invoices.

The close · March 2026 statement

  • Opening MRR$121,020
  • New business+$6,640
  • Expansion+$3,610
  • Contraction−$1,180
  • Churn−$1,680
  • Closing MRR$128,410

Δ $0.00

The state your month ends in. Anything else is a flag with an owner.

pricing-plans

src/components/pricing-plans.tsx

A client island: the billing switch does the monthly/annual arithmetic. One meter, three plans, risk reversal at the price.

One meter: MRR tracked

Seed

For a product with its first revenue.

$0forever

  • To $2,500 MRR tracked
  • 1 billing source
  • Daily match runs
  • MRR, movements and churn
  • 2 seats
Start on Seed

Growth

Most teams

For a team that closes every month.

$49/month

  • To $50,000 MRR tracked
  • 4 billing sources
  • Hourly match runs
  • Full metric set, cohorts included
  • 10 seats
  • CSV and Sheets export
Start free for 30 days

Scale

For finance teams with auditors.

$199/month

  • Unlimited MRR tracked
  • 8 billing sources
  • Continuous matching
  • Forecast and API access
  • SSO and unlimited seats
  • Audit-trail export
Start on Scale

30 days of Growth free, no card · cancel any month · export everything, any time

MRR tracked is the meter. Seats never are.

faq

src/components/faq.tsx

Six pricing objections as native details disclosures on a 5/7 split. The +/− swap is instant and shape-based.

Questions

Asked before buying

The six questions every finance lead asks; answers a salesperson would give you anyway.

What counts as MRR tracked?

The closing MRR of your last published statement. Cross a plan boundary and Recur asks before it upgrades you; nothing changes silently.

Is Seed a trial in disguise?

No. Seed is free to $2,500 MRR tracked, one billing source, daily match runs. No card and no time limit.

How does the 30-day trial work?

Every new account gets Growth free for 30 days, no card. When it ends you pick a plan or drop to Seed. Your data stays either way.

What does annual billing change?

You pay ten months for twelve. Cancel any month on monthly billing; annual refunds the unused whole months.

Which sources can Recur read?

8: Stripe, Paddle, Chargebee, Lemon Squeezy, App Store, Play Billing, Braintree, Manual invoices. The integrations page shows sample objects and access boundaries.

Does Recur replace my accountant?

No. It hands them a matched trail instead of a spreadsheet. Every figure in a statement links to the invoices behind it.

case-study

src/components/case-study.tsx

One close told in full at prose measure, figures in a narrow rail, the pull quote signed. Shown without its page lead.

Case study

Standfirst closes in an afternoon

Standfirst sells newsroom analytics to regional publishers. Sixty-one people, London and Tallinn, and revenue arriving three ways: Stripe for self-serve, Chargebee for the annual contracts, and invoices the sales team still sends by hand.

The close took nine days. Finance exported each source to its own spreadsheet, matched rows by eye, and every board pack carried an MRR figure somebody had re-derived the night before. Two of those figures disagreed in the same quarter. The auditors noticed.

Recur read all three feeds on a Tuesday. The first match run finished in 54 minutes and surfaced 17 rows that would not pair; twelve were credit notes booked against the wrong month, five were duplicate manual invoices nobody had caught. Finance cleared the queue by Thursday.

Since then the statement publishes when the delta reads zero. The board pack is an export of the close, the auditors take the trail as-is, and the March figure everyone quotes is the March figure the ledger holds.

“Our close was nine days of spreadsheet archaeology. With Recur it is one afternoon, and the auditors take the statement as-is.”

Maren Ilves

Finance lead, Standfirst

customer-wall

src/components/customer-wall.tsx

The go-live wall: the five companies the wins rail announces, plus the action cell.

Go-lives

Recent closes

What each team runs, and what changed after the first clean match.

  • Standfirst

    newsroom analytics

    close now one afternoon

  • Loam

    sensor platform for growers

    churn flagged 41 days early

  • Tidemark

    marina bookings

    board pack from the close

  • Bellwether Labs

    clinical trial software

    3 sources, one ledger

  • Quartermaster

    fleet maintenance

    first clean close in 2 years

  • Your close next

    30 days of Growth free, no card

    Start the match

signup-panel

src/components/signup-panel.tsx

A client island: validation, focus recovery and a designed success state. Specimen mode renders h2; /signup keeps the default h1.

Start free

Start the match.

30 days of Growth free, no card. Free to $2,500 MRR after that.

More sources connect after the first match

Demo form: validation and the submitting state run, the POST to /api/signups does not. Wire it to your backend in signup-panel.tsx.

source-request

src/components/source-request.tsx

The capture surface, put where the reader needs it: the integrations no-result state links here. Three controls, one of them optional, on ui/form — so a refusal can reach the summary rather than always landing on one control.

Sources

Ask for a source

Name the source that bills you; we answer with where it sits in the queue.

Reading now
8 adapters, 1 more in build
Answered by
the integrations desk, in writing
Kept
the source and the address, nothing else
Costs
nothing, and buys no obligation

The platform that issues the invoice

Proration, multi-currency, a private plan

Demo form: validation and the receipt run, the POST to /api/source-requests does not. Wire it to your backend in source-request.tsx.

cta

src/components/cta.tsx

The closing band: one primary action, cost, risk and effort answered beneath it.

The books happened. Match them.

Connect a billing source and watch the first match run before the coffee is done.

  • Free to $2,500 MRR, then from $49 a month
  • 30 days of Growth free, no card
  • First close in about an hour
  • Export everything, any time

Primitives

The ui/ parts every section is built from, in one frame.

ui/button · size + icon trim

Three heights, 36 / 44 / 48px. The icon side trims 2px of padding because a glyph carries its own optical margin; the last control shows the trim against the one above it.

ui/button · disabled + pending

Disabled carries the DOM attribute, so it leaves the tab order and the press ramp is gone rather than dimmed. Pending takes the independent info family, reports aria-busy and says what it is doing; it never spins, because the ticker rail holds this theme’s whole ambient budget. Keyboard: neither is reachable by Tab, so a second submit is impossible from the keyboard as well as from the pointer.

ui/badge · ui/delta · ui/mode-toggle

Most teams1 flaggedΔ $0.00Δ $84.00

Keyboard: the mode control is a real toggle button — Enter or Space flips it and aria-pressed reports the setting, not the next state. Badge and the delta chip carry their words as well as their tone, so a flagged figure survives a greyscale capture.

ui/logo · ui/section-heading

Recur

The close

Metrics that survive an audit

Every number computed from matched invoices, so the board pack and the books agree.

ui/container · three measures

3xl · 768px — the case study and long-form answers

6xl · 1152px — every marketing section

7xl · 1280px — the rails and the widest tables

ui/eyebrow · ui/type

Subscription analytics

Sizes come from ui/type.ts only: caption 12 · small 14 · body 16 · lead 18 · d1 24 · d2 32 · d3 42.7 · d4 56.9. There is no size between the rungs.

ui/integration-card · availability states

Stripe

Billing

Sample authorized

Invoices, subscriptions, customers and balance transactions

4 read-only object families

Inspect sample

Paddle

Merchant of record

Available

Checkout invoices, adjustments and subscription changes

Merchant-of-record normalization

Connect provider

Manual invoices

Manual

Partial sample

CSV or email-in invoices with explicit ledger mapping

77 sample records, 1 variance

Start setup

RevenueCat

Mobile

Planned

No adapter or sample connection is included in this release

Planned, not available

No adapter or action included

ui/sync-health · health states

Healthy sample

Stripe is sample-authorized and 5 minutes old at evaluation.

1,904

Retained sample records

31 Mar 2026, 14:24 UTC

Last sample sync

As of 31 Mar 2026, 14:29 UTC · 30-minute rule · 1,904 new records accepted

0 identity gaps; the March statement published after variance resolution

Delayed sample

The sample is 55 minutes old, beyond the 30-minute rule.

1,904

Retained sample records

31 Mar 2026, 13:34 UTC

Last sample sync

As of 31 Mar 2026, 14:29 UTC · 30-minute rule · 0 new records accepted

Keep the prior sample visible; your backend should retry the sync

Partial sample

Manual invoices are identity-matched, but one value needs review.

77

Retained sample records

31 Mar 2026, 14:19 UTC

Last sample sync

As of 31 Mar 2026, 14:29 UTC · 30-minute rule · 77 new records accepted

Invoice 8843 carries an $84 variance until the mapping is resolved

Failed sample

The sample request failed before new records were accepted.

1,904

Retained sample records

31 Mar 2026, 13:34 UTC

Last sample sync

As of 31 Mar 2026, 14:29 UTC · 30-minute rule · 0 new records accepted

Retain the prior sample and wire retry behavior in your backend

ui/field-mapping-table · complete, unmapped, ignored

Stripe sample field mapping
Source fieldinvoice.idRecur conceptInvoice identityStatusMappedRuleExact source identifier
Source fieldcustomer.idRecur conceptSubscriber identityStatusMappedRuleStable customer join
Source fieldsubscription.current_period_endRecur conceptService period endStatusMappedRuleUTC timestamp
Source fieldbalance_transaction.exchange_rateRecur conceptExchange rateStatusMappedRuleCompared with ECB daily rate
Source fieldinvoice.metadata.ledger_account_codeRecur conceptLedger accountStatusMappedRuleMapped to SaaS subscription revenue
Source fieldcustomer.descriptionRecur conceptNoneStatusIgnoredRuleNot used in reconciliation
Source fieldmanual_invoice.ledger_account_codeRecur conceptLedger accountStatusNeeds mappingRuleMap invoice 8843 before publication

ui/connection-checklist · complete, blocked, frontend-only

  • Provider objects reviewed

    Complete

    Manual invoice columns are documented

  • Ledger account missing

    Blocked

    Map manual_invoice.ledger_account_code before publication

  • Backend connection added

    Frontend only

    Upload, retries and persistence belong to your backend

Library parts

Four parts whose behaviour and structure come from tt-ui and whose every class string, glyph and word are decided here.

ui/accordion-disclosure

src/components/ui/accordion-disclosure.tsx

One graph, two recipes: the FAQ's ruled row and the header's plate. Both are native details elements, so the answers are in the page with no script; both close on Escape and hand focus back to the summary, which the element alone never did.

closed · the FAQ row at rest, two rows for the rhythm

What happens to a flagged invoice?

It stays out of the close until the variance resolves. The statement publishes at Δ $0.00 or it does not publish.

Who can republish a statement?

2 roles
The owner and the finance lead. Every republish keeps the old one, with the run that produced it.

open · the second row open, and the header plate beside it

What happens to a flagged invoice?

It stays out of the close until the variance resolves. The statement publishes at Δ $0.00 or it does not publish.

Who can republish a statement?

2 roles
The owner and the finance lead. Every republish keeps the old one, with the run that produced it.
Menu

Keyboard: Enter and Space open a row, and Escape from inside the answer closes it and puts focus back on the question — stopped there, so a row inside a row closes only the inner one. Under prefers-reduced-motion the 240ms height tween is off and the answer is simply there.

ui/field

src/components/ui/field.tsx

The shell around a native control: a mono label, the optional marker in words, the read-only sentence, and one message line whose id never changes as it carries the description, the error or the receipt. Each state is drawn for the input, the select and the textarea, because the three used to disagree.

rest · the description, and the optional marker in words

The invite goes here

More sources connect after the first match

Published with the statement, plain text

invalid · the error replaces the description on the same id

That address is missing an @ or a domain. Check it and retry.

Pick the source that bills most of your revenue. More can follow.

Say what changed. One sentence is enough for the audit trail.

valid · a checked value, and a durable receipt under it

Domain answers, inbox accepts.

Stripe is authorized for read-only sample access.

Long enough to publish with the statement.

read-only · the value still matters, so it stays legible and selectable

Taken from the March run. Change it at the source.

A native select has no read-only, so it is out of play instead.

The statement is published; the note travels with it.

disabled · out of play — the neutral register, never the resting one faded

Connect a source before inviting anyone

Nothing to choose until a source authorizes

The March close has not run

One line at a time, error before receipt before description, on one id — so a reader who tabs in hears whichever of the three the field is carrying and never two of them. The critical edge and the focus ring are separate roles, so invalid and focused hold both meanings at once.

ui/form-error-summary

src/components/ui/form-error-summary.tsx

The panel a refused submit lands on: one row per failed field, each a real link to the control it names. It is inserted on the refusal rather than revealed, so it is announced once and on the submit that matters — and only when more than one answer failed, because a summary of one is a detour.

Keyboard: focus lands on the panel one render after the refusal, when it exists; each row is a link, so Enter jumps to the control it names. The four states of ui/form around it — idle, invalid, submitting, success — run for real on /signup and on /integrations.

src/components/site-footer.tsx

Brand block, three link columns and the legal row. Every link resolves to a page or an anchor the theme ships. Rendered in place as the page shell.