/* Plan and billing (pages/billing.html) and the printable document (pages/receipt.html), 21 Sep 2026, M4.
   Linked from those two pages only. Everything scopes to main.billing or main.doc. The billing frame is the
   checkout's (css/plans.css .checkout-layout, .checkout-plan, .checkout-summary): content left, a sticky
   summary rail right from 1024px. The document reuses the invoice page's .inv-doc (css/engagement.css), so
   only the page wrapper and the print rule live here. No new tokens; every value maps to css/styles.css.

   STATES BY QUERY (app.js block 36): <main class="billing"> carries data-state in its markup, so the default
   state renders with no script; the script only overwrites it from the query string. Each view block lists
   the states it belongs to in data-state-view (space separated) and only the matching ones show. On
   receipt.html the same pattern runs on data-doc (the receipt or invoice number) and data-type.
   Sean renders one state per route from the record and deletes every view block but the live one. */

main.billing [data-state-view] { display: none; }
main.billing[data-state="active"]             [data-state-view~="active"],
main.billing[data-state="due"]                [data-state-view~="due"],
main.billing[data-state="grace"]              [data-state-view~="grace"],
main.billing[data-state="dunning"]            [data-state-view~="dunning"],
main.billing[data-state="expired"]            [data-state-view~="expired"],
main.billing[data-state="downgrade-pending"]  [data-state-view~="downgrade-pending"],
main.billing[data-state="paid"]               [data-state-view~="paid"],
main.billing[data-state="cancelled"]          [data-state-view~="cancelled"] { display: block; }
/* Inline holders (a state pill, a button) keep an inline display when shown; never put a view
   attribute on a word inside a sentence, wrap the whole line instead. */
main.billing[data-state="active"]            span[data-state-view~="active"],
main.billing[data-state="due"]               span[data-state-view~="due"],
main.billing[data-state="grace"]             span[data-state-view~="grace"],
main.billing[data-state="dunning"]           span[data-state-view~="dunning"],
main.billing[data-state="expired"]           span[data-state-view~="expired"],
main.billing[data-state="downgrade-pending"] span[data-state-view~="downgrade-pending"],
main.billing[data-state="paid"]              span[data-state-view~="paid"],
main.billing[data-state="cancelled"]         span[data-state-view~="cancelled"] { display: inline-flex; }

/* Two pending downgrades share one state. [data-to-view] narrows a downgrade-pending block to its own
   case: the default (no ?to=) is AT-13's Pro monthly to Growth, and ?to=starter is the Growth annual to
   Starter case the Move to Starter dialog confirms. app.js block 36 writes data-to from the query. */
main.billing[data-state="downgrade-pending"] [data-to-view="starter"] { display: none; }
main.billing[data-state="downgrade-pending"][data-to="starter"] [data-to-view="growth"] { display: none; }
main.billing[data-state="downgrade-pending"][data-to="starter"] [data-to-view="starter"] { display: block; }

/* Title row: the page name and the state pill on one line (HANDOFF 61 pattern). */
.billing-head       { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.billing-head h1    { margin: 0; }
.billing-notice     { margin-top: 1.25rem; }
/* Plan card: the checkout's card with a facts list under the price line. */
.billing-plan .kv   { margin-top: 1.25rem; font-size: var(--text-body-sm); gap: 0.375rem 1.25rem; }
.billing-plan__note { margin-top: 1.25rem; font-size: var(--text-body-sm); color: var(--muted-foreground); line-height: 1.55; }
/* Receipts: the site table, with the document link in the first column. */
.billing-receipts   { margin-top: 2.5rem; }
.billing-receipts .table-wrap { margin-top: 1rem; }
.billing-receipts .table td.num,
.billing-receipts .table th.num { font-variant-numeric: tabular-nums; }
.billing-receipts small { display: block; margin-top: 0.25rem; font-size: var(--text-caption); color: var(--muted-foreground); }
/* Rail: the next payment figure, then the actions for the state, then one help link. */
.billing-rail__label { font-size: var(--text-body-sm); color: var(--muted-foreground); }
.billing-rail__amount { margin-top: 0.25rem; display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; font-size: var(--text-subheading); font-weight: 600; }
.billing-rail__amount .mono { font-variant-numeric: tabular-nums; }
.billing-actions    { display: grid; gap: 0.625rem; margin-top: 1.25rem; }
.billing-actions .btn { width: 100%; }
.billing-help       { margin-top: 1rem; font-size: var(--text-body-sm); }
/* What each published programme becomes at renewal, on the pending Growth-to-Starter card. */
.billing-listings__title { margin: 1.5rem 0 0; font-size: var(--text-body-sm); }
.billing-listings   { list-style: none; margin: 0.625rem 0 0; padding: 0; display: grid; gap: 0.5rem; }
.billing-listings li { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; font-size: var(--text-body-sm); }
.billing-listings li span { color: var(--muted-foreground); font-size: var(--text-caption); white-space: nowrap; }
/* The over-cap chooser inside the downgrade dialog (AT-13). One row per programme, the cap on the count. */
.billing-pick       { margin-top: 1rem; display: grid; gap: 0.5rem; }
.billing-pick .check { padding: 0.625rem 0.875rem; border-radius: var(--radius-md); box-shadow: inset 0 0 0 1px var(--grey-200); }
.billing-pick .check:has(input:checked) { box-shadow: inset 0 0 0 1px var(--primary); background: var(--navy-50); }
.billing-pick__state { display: block; margin-top: 0.125rem; font-size: var(--text-caption); color: var(--muted-foreground); }
.billing-pick__count { margin: 0.25rem 0 0; font-size: var(--text-body-sm); color: var(--muted-foreground); }
.billing-pick__count.is-full { color: var(--primary); font-weight: 600; }

/* ---------- Receipt and invoice document (pages/receipt.html) ---------- */
/* The page wrapper only: the document itself is .inv-doc from css/engagement.css, so the invoice page's
   print rule (header, footer, actions and back link hidden) already applies to it. */
/* One document per route: the markup shows the first and marks the rest hidden, and app.js block 36
   picks the one that ?id= (or ?type=) names. No display rules, so the hidden attribute is the only switch. */
main.doc            { padding-bottom: 6rem; }
main.doc ::selection { background: var(--navy-100); color: var(--navy-900); }
/* The SST row carries a placeholder while the entity is under the registration threshold. */
.doc-sst            { color: var(--muted-foreground); }
/* An ?id= that names no document (app.js block 36). Hidden in the markup, shown by the script. */
.doc-missing        { margin: 2rem 0 0; }
@media print { main.doc { padding: 0; } }

/* Payment method block (CP 5 Oct 2026): stored cards, one row each; the "..." menu is the site dropdown.
   [data-cardrow] = the 4242 row's state (active-1: only card, active-2: after Add new card, cancelled);
   [data-cards-view="2"] = the 1745 row; [data-cards-text] = the card number in the rail. ?cards=2 is
   written on main as data-cards by app.js block 36. */
.billing-cards { margin-top: 1rem; }
.billing-card { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem 0.75rem; padding: 0.75rem 0; border-top: 1px solid var(--grey-100); }
.billing-card__num { font-size: var(--text-body-sm); letter-spacing: 0.04em; }
.billing-card__exp { font-size: var(--text-body-sm); color: var(--muted-foreground); }
.billing-card__menu { margin-left: auto; }
.billing-card__dots { padding: 0.25rem; border-radius: var(--radius-sm); color: var(--grey-500); }
.billing-card__dots svg { width: 1.25rem; height: 1.25rem; }
.billing-card__menu .nav-menu__panel { left: auto; right: 0; width: 15rem; }
.billing-card__menu .nav-menu__panel a[aria-disabled] { color: var(--grey-400); pointer-events: none; }
.billing-card__hint { font-size: var(--text-caption); color: var(--muted-foreground); white-space: normal; line-height: 1.4; }
.billing-cards__actions { margin-top: 0.75rem; }
main.billing [data-cardrow], main.billing [data-cards-view], main.billing [data-cards-text] { display: none; }
main.billing[data-state="active"]:not([data-cards="2"]) [data-cardrow="active-1"],
main.billing[data-state="active"][data-cards="2"]       [data-cardrow="active-2"],
main.billing[data-state="cancelled"]                    [data-cardrow="cancelled"],
main.billing[data-state="grace"][data-payer="card"]:not([data-cards="2"])   [data-cardrow="active-1"],
main.billing[data-state="dunning"][data-payer="card"]:not([data-cards="2"]) [data-cardrow="active-1"] { display: contents; }
main.billing[data-cards="2"] [data-cards-view="2"] { display: flex; }
main.billing:not([data-cards="2"]) [data-cards-text="1"],
main.billing[data-cards="2"]       [data-cards-text="2"] { display: inline; }

/* Card payer whose charge failed (CP 5 Oct 2026): ?pay=card is written on main as data-payer by app.js
   block 36; the grace and dunning notices carry one line per payer type and only hide, never restyle. */
main.billing:not([data-payer="card"]) [data-payer-view="card"],
main.billing[data-payer="card"]       [data-payer-view="fpx"] { display: none !important; }
/* #98 C3 (15 Oct 2026): the Payment method block shows in grace and dunning for the card payer only;
   adding a new card there charges the overdue renewal at once. */
main.billing[data-state="grace"]:not([data-payer="card"])   .billing-cards,
main.billing[data-state="dunning"]:not([data-payer="card"]) .billing-cards { display: none; }
/* #98 re-read C15 (15 Oct 2026): in grace and dunning the card payer's 4242 row shows the failed charge, not "Used for renewals". */
main.billing [data-cardpill="failed"] { display: none; }
main.billing[data-state="grace"][data-payer="card"]   [data-cardpill="renews"],
main.billing[data-state="dunning"][data-payer="card"] [data-cardpill="renews"] { display: none; }
main.billing[data-state="grace"][data-payer="card"]   [data-cardpill="failed"],
main.billing[data-state="dunning"][data-payer="card"] [data-cardpill="failed"] { display: inline-flex; }
