/* The account — the chip, its menu, and the four billing dialogs behind it.
 *
 * Its own stylesheet because it is the one component every page carries.
 * The app, the policy pages and the landing page each have a stylesheet that
 * shares nothing with the others (see the heads of style.css, policy.css and
 * landing.css), and the account used to live in style.css alone, because it
 * lived on /app alone. account.js builds the same markup on every page now,
 * so the rules for it are here once and every page loads them.
 *
 * Load it BEFORE the page's own stylesheet. The four primitives the dialogs
 * are built from — `.key`, `.sr-only`, `.btn`, `.btn-secondary` — are defined
 * here, and style.css tunes them further down the cascade (`.btn.is-locked`,
 * `.rulebar .key`, `.download-btn`) exactly as it did when all of this was one
 * file. None of the other pages uses those names, which is what makes it safe
 * to define them unscoped.
 *
 * **The palette here is a fallback, not a fourth palette.** Every token is
 * declared inside `:where()`, which has no specificity, so the `:root` blocks
 * in style.css and policy.css win wherever they define the same name — and
 * they define the same values. What these defaults are for is the page that
 * defines none of them: the landing page, whose tokens are all `--lp-*`, and
 * the policy pages for the handful they never needed (`--overlay`,
 * `--err-bg`, `--pending-bg`). `test_account_css_falls_back_to_the_app_palette`
 * holds every value here to style.css's, so the copy cannot drift. */

:where(:root) {
  --terra: #C67B5C;
  --terra-dark: #A8624A;
  --terra-light: #E8C4B4;
  --paper: #F2EBE4;
  --ink: #241F1A;
  --ink-2: #3C352E;
  --ink-3: #564E44;
  --muted: #8A7F70;
  --faint: #B5A894;
  --field: #FCF9F4;
  --panel: #EFE8DF;
  --rule: #D3C7B7;
  --rule-soft: #E4DBCD;
  --rule-hair: #C6B9A8;
  --ok: #5E8452;
  --bad: #C4574E;
  --sel-bg: var(--ink);
  --sel-fg: var(--paper);
  --err-bg: rgba(196, 87, 78, 0.08);
  --err-fg: #8E332C;
  --danger-text: #A8423A;
  --danger: #B84A41;
  --danger-on: #FCF9F4;
  --danger-fill: #7F2B25;
  --danger-fill-on: #FCF9F4;
  --pending-bg: #EEE0D6;
  --overlay: rgba(36, 31, 26, 0.5);
  --sans: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
  --serif: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}

:where(:root[data-theme="dark"]) {
  --terra: #D9885F;
  --terra-dark: #E5A17C;
  --terra-light: #E8C4B4;
  --paper: #14100C;
  --ink: #F5EEE6;
  --ink-2: #E0D6C8;
  --ink-3: #C4B7A5;
  --muted: #A0927F;
  --faint: #8D8172;
  --field: #2E2619;
  --panel: #1C1712;
  --rule: #382E24;
  --rule-soft: #2A231B;
  --rule-hair: #4A3B2C;
  --ok: #8CAE78;
  --bad: #DE8474;
  --sel-bg: var(--terra);
  --sel-fg: #1B1512;
  --err-bg: #2C1E18;
  --err-fg: #F2AA9C;
  --danger-text: #DE8474;
  --danger: #DE8474;
  --danger-on: #1B1512;
  --danger-fill: #9E3F36;
  --danger-fill-on: #FCF9F4;
  --pending-bg: #3F2A1F;
  --overlay: rgba(0, 0, 0, 0.62);
}

/* A "key" is the sheet's smallest voice: mono, letterspaced, quiet. Every
   column header, unit and annotation is one. */
.key {
  font-family: var(--mono);
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.16em;
  line-height: 1.5;
  color: var(--muted);
  text-transform: uppercase;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The footer's withdrawal link, which billing.js hides for an account that
   provably has no right to withdraw. Both the policy pages' box and the
   landing page's set `display`, which beats the UA's [hidden] rule, so the
   hide is spelled out — here, because this is the one stylesheet every page
   with the link loads. Two attribute selectors outrank either class. */
[data-withdraw-footer][hidden] { display: none; }

/* ── Buttons ─────────────────────────────────────────────────────── */

.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 50px;
  border: 1px solid var(--ink);
  background: var(--terra);
  color: #1B1512;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.btn:hover:not(:disabled) { background: var(--terra-light); }
.btn:disabled { background: var(--field); border-color: var(--rule-hair); color: var(--faint); cursor: not-allowed; }
.btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.btn-secondary {
  height: 42px;
  background: transparent;
  border-color: var(--rule-hair);
  color: var(--ink-2);
  font-size: 14px;
  font-weight: 400;
}
.btn-secondary:hover:not(:disabled) { background: var(--field); border-color: var(--ink); }

/* ── Account ──────────────────────────────────────────────────────── */

/* Sits at the end of the rule bar. `position: relative` is what the menu
   below hangs off, and it is also why nothing else in this block may be
   positioned — the menu is anchored to the chip, not to the bar. */
.acct {
  align-self: center;   /* the group is baseline-aligned; a 26px box is not */
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
/* `display: flex` above beats the UA's `[hidden] { display: none }`. Without
   this, a deployment with no Clerk keys — which is every dev box, and every
   deployment before sign-in existed — showed a SIGN IN button that could only
   ever do nothing. */
.acct[hidden] { display: none; }

/* Square rather than the round avatar every other app draws: nothing on this
   sheet has a rounded corner, and one circle in the rule bar reads as a
   control borrowed from somewhere else. Same 26px box, same hairline, same
   fill-on-select as the theme cells beside it — it joins the bar's one
   bordered-control vocabulary instead of starting a second. */
.acct-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--rule-hair);
  background: var(--field);
  color: var(--ink-3);
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.acct-avatar:hover { background: var(--sel-bg); color: var(--sel-fg); }
.acct-avatar[aria-expanded="true"] { background: var(--sel-bg); color: var(--sel-fg); }
.acct-avatar:focus-visible { outline: 2px solid var(--terra); outline-offset: 2px; }
/* `display: flex` above beats the UA's `[hidden] { display: none }`, so the
   hide has to be spelled out — the same trap `.btn` carries. Signed in, the
   SIGN IN button is the one that disappears and this one that stands. */
.acct-avatar[hidden] { display: none; }

/* Right-aligned to the chip and hanging below the bar. Not a <dialog>: the
   plan picker is modal because it interrupts, and this is a panel you glance
   at and dismiss — a backdrop over the whole sheet for four facts would be
   the wrong weight entirely. */
.acct-menu {
  position: absolute;
  top: calc(100% + 9px);
  right: 0;
  z-index: 40;
  width: 258px;
  padding: 14px;
  border: 1px solid var(--rule-hair);
  background: var(--panel);
  box-shadow: 0 14px 34px var(--overlay);
  /* Same reason as `.plan-dialog`: the panel hangs off three different bars
     and inherits nothing from any of them. */
  color: var(--ink);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.55;
  text-align: left;
}

.acct-menu-email {
  margin: 0 0 12px;
  padding-bottom: 11px;
  border-bottom: 1px solid var(--rule-soft);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.35;
  /* An address is one unbroken token and routinely longer than the panel.
     Wrapped rather than truncated: this is the one place it is shown, so
     it has to be shown whole. */
  overflow-wrap: anywhere;
}

/* A two-column ledger, the same shape the manifest rows use — label at the
   left in the key tier, value hard right. */
.acct-menu-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 12px;
  margin: 0;
}
.acct-menu-facts dt { color: var(--muted); }
.acct-menu-facts dd { margin: 0; color: var(--ink); text-align: right; }
/* Nothing left this month. `--terra-dark` is the accent as text, which is the
   token that goes lighter in dark rather than darker — see the theme notes. */
.acct-menu-facts dd.is-low { color: var(--terra-dark); }

.acct-menu-note {
  margin: 10px 0 0;
  color: var(--muted);
  line-height: 1.45;
  letter-spacing: 0.04em;   /* the key tier's tracking is set for one word */
  text-transform: none;
}

.acct-menu-btn {
  width: 100%;
  height: 32px;
  margin-top: 10px;
  border: 1px solid var(--ink);
  background: var(--terra);
  color: #1B1512;
  font-family: var(--sans);
  font-size: 13px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.acct-menu-btn:hover { background: var(--terra-light); }
.acct-menu-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* Sign out is not the thing anyone came here to do. */
.acct-menu-quiet {
  border-color: var(--rule-hair);
  background: transparent;
  color: var(--ink-2);
}
.acct-menu-quiet:hover { background: var(--field); border-color: var(--ink); color: var(--ink); }

/* Deleting the account: 12px regular text in the danger red, under a hairline
   and below Sign out. A text control rather than a third button, because the
   panel's two buttons are the things people came to press and this is the
   way out — it should be findable by someone looking for it and not read as
   an action to anyone else. */
.acct-menu-delete {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 10px 0 0;
  border: 0;
  border-top: 1px solid var(--rule-soft);
  background: none;
  color: var(--danger-text);
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.01em;
  text-align: center;
  cursor: pointer;
}
.acct-menu-delete:hover { text-decoration: underline; text-underline-offset: 3px; }
.acct-menu-delete:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* Wears the theme toggle's dress rather than inventing a third button shape:
   the bar has one bordered-control vocabulary and this joins it. */
.acct-btn {
  height: 26px;
  padding: 0 11px;
  border: 1px solid var(--rule-hair);
  background: var(--field);
  color: var(--ink-3);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, color 0.15s;
}
.acct-btn:hover { background: var(--sel-bg); color: var(--sel-fg); }
.acct-btn:focus-visible { outline: 2px solid var(--terra); outline-offset: 2px; }

/* ── Allowance and plans ──────────────────────────────────────────── */

.plan-dialog {
  width: min(640px, calc(100vw - 32px));
  /* Its own type metrics rather than the body's. On /app those are the same
     thing; the policy pages set 16px/1.62 and the landing page sets cream on
     night, and a dialog that inherited either would be a different panel on
     every page that opened it. */
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.55;
  /* A modal <dialog> is centred by the UA's own `margin: auto`, which this
     sheet's reset zeroes along with everything else — without restoring it the
     panel sits in the top-left corner. */
  margin: auto;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 0;          /* the sheet has no rounded corners anywhere */
  background: var(--paper);
  color: var(--ink);

  /* Every dialog scrolls itself, and this is on the shared class rather than
     on the two that obviously need it. A dialog with no `overflow-y` does not
     merely clip on a short window — it has no scroll of its own, so the
     gesture goes straight past it to whatever is behind. The comparison table
     is the tall one, and on a phone it was both unreachable below the fold and
     the thing that scrolled the app underneath it.

     `overscroll-behavior: contain` is the other half: without it a scroll that
     reaches the dialog's own end **chains** to the document and carries on.
     Measured at 375x812 — six wheel notches past the foot of the billing panel
     moved the page 600px behind it. Together these two make a gesture that
     starts inside a dialog stay inside it. A gesture that starts on the
     *backdrop* is a different problem and is handled in `account.js`. */
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.plan-dialog::backdrop { background: var(--overlay); }

/* Written by account.js while any modal is open, on every page.
 *
 * `overscroll-behavior` on the dialog only governs a gesture that *started*
 * inside it. On a phone the backdrop is most of the screen, and a drag there
 * scrolls the document straight away — the page visibly moving behind a panel
 * that is supposed to have interrupted it.
 *
 * Only the overflow half lives here. The other half — `position: fixed` on
 * the body, because iOS Safari does not reliably honour `overflow: hidden` on
 * the body for touch — depends on whether the page scrolls at all, so each
 * page's own stylesheet decides it: style.css below its stacking breakpoint
 * only (above it the sheet is `100vh` and the body does not scroll), and
 * policy.css and landing.css at every width, because those are documents. The
 * scroll offset moves onto `top` and account.js puts it back on close. */
:root.is-modal-open,
:root.is-modal-open body { overflow: hidden; }

.plan-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--rule);
}

.plan-blurb {
  margin: 0;
  padding: 18px 20px 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-3);
}

/* The reason the picker opened — an exhausted allowance, or a mesh upload on
   the free tier. Wears the error strip's wash so it reads as a refusal being
   answered rather than as a marketing line. */
.plan-reason {
  margin: 0;
  padding: 12px 20px;
  background: var(--err-bg);
  color: var(--err-fg);
  font-size: 13px;
  line-height: 1.45;
  white-space: pre-line;
  border-bottom: 1px solid var(--rule-soft);
}

/* The two tiers side by side.
 *
 * A real <table>, not a grid of divs: it is tabular data, the row header is
 * the feature and the column header is the tier, so a screen reader can say
 * "Unlimited generations, free: no, 5 generations a month" without any of it
 * being spelled out in markup. The marks are decorative and carry an
 * `.sr-only` yes/no beside them for the same reason.
 *
 * One SUBSCRIPTION column rather than one per plan: every plan Stripe returns
 * carries the same three differences, so three identical tick columns would
 * be three ways of saying one thing — and at four columns the table stops
 * fitting a phone. The price cards below are what differ, and they differ
 * only on term. */
.plan-compare {
  width: 100%;
  border-collapse: collapse;
  margin-top: 4px;
  font-size: 13px;
  line-height: 1.4;
}

.plan-compare th,
.plan-compare td {
  padding: 10px 12px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--rule-soft);
}

.plan-compare thead th {
  padding-top: 16px;
  color: var(--muted);
  border-bottom-color: var(--rule);
}
/* Auto table layout gives the feature column whatever the other two leave,
   and the third row's paid answer is long enough to squeeze this header onto
   two lines while every row label under it is one word. Held to a line above
   the phone tier only — below it the 375px budget is real and a wrapped
   header is cheaper than a table that will not fit. */
@media (min-width: 621px) {
  .plan-compare thead th:first-child { white-space: nowrap; }
}

.plan-compare tbody th {
  font-weight: 400;
  color: var(--ink-2);
}

.plan-compare tfoot td { border-bottom: none; }

.plan-compare th:first-child,
.plan-compare td:first-child { padding-left: 20px; }
.plan-compare th:last-child,
.plan-compare td:last-child { padding-right: 20px; }

/* The paid column is the one being sold, so it is lifted off the sheet the
   way a chosen chip is — a wash rather than a border, because a border would
   need to be drawn on every cell of the column and would break the table's
   single set of horizontal rules. */
.plan-col-paid { background: var(--field); }
.plan-compare thead .plan-col-paid { color: var(--terra-dark); }

.plan-cell { color: var(--muted); }
/* The paid answers carry the weight: both columns state a value now, and at
   one colour the eye has no reason to land on the one being sold. */
.plan-cell.plan-col-paid { color: var(--ink-2); }

/* The mark sits on the text's own first line, which is why it is
   `vertical-align` against a line box rather than a flex row: the free
   column's cells wrap to two lines at a phone width and a flex row would
   centre the cross against the pair. */
.plan-mark {
  width: 14px;
  height: 14px;
  vertical-align: -2px;
  margin-right: 6px;
}
.plan-mark.is-yes { color: var(--ok); }
.plan-mark.is-no { color: var(--bad); }

/* The two columns' actions. */
.plan-foot-cell { padding-top: 12px; padding-bottom: 16px; }
.plan-current { color: var(--muted); }

/* Sizing scoped to the cell rather than hung on a class of each button's own,
   and that is not tidiness: `.btn-secondary`'s fixed `height: 42px` is
   declared further down this file, so a bare one-class rule setting
   `height: auto` loses to it on source order and the label spills straight
   through the border at a phone width. Descending from the cell wins on
   specificity instead, and covers both buttons at once — which is also why
   neither carries a class the stylesheet has never heard of.
   Column, because each label is a name over a qualifier — see `.plan-from`. */
.plan-foot-cell .btn {
  width: 100%;
  height: auto;
  min-height: 46px;
  flex-direction: column;
  gap: 1px;
  padding: 7px 8px;
  line-height: 1.25;
  font-size: 14px;
}

/* The qualifier under a label: "no card", "from $2.99/mo". Written as its own
   line rather than trailed after the label on one, because the pair has to
   hold at 375px in a column the free side already wraps three ways — a single
   run of words breaks wherever it happens to run out, and "Subscribe — from
   $2.99/mo" came apart across three lines with the amount orphaned on the
   last. Two deliberate lines survive every width. */
.plan-from {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  font-weight: 400;
  opacity: 0.75;
}

.plan-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;                  /* the container showing through, as elsewhere */
  background: var(--rule-soft);
  margin: 0;
  border-top: 1px solid var(--rule-soft);
  border-bottom: 1px solid var(--rule-soft);
}

.plan {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 18px 16px;
  background: var(--paper);
}

.plan-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  min-height: 14px;
}

.plan-name { color: var(--muted); }

.plan-flag { color: var(--terra-dark); }

.plan-price {
  font-family: var(--mono);
  font-size: 26px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.plan-unit { font-size: 13px; color: var(--muted); }

.plan-term { color: var(--muted); line-height: 1.4; }

/* When this term starts, or when it stops. One tier up from the term
   line in colour because it is the fact that changed, and directly under
   the price because it is the rest of what the price means. */
.plan-when { color: var(--ink-2); line-height: 1.4; }

.plan-btn { margin-top: auto; }

.plan-empty {
  grid-column: 1 / -1;
  margin: 0;
  padding: 24px 20px;
  background: var(--paper);
  color: var(--muted);
  font-size: 13px;
}

.plan-foot {
  margin: 0;
  padding: 14px 20px;
  color: var(--faint);
  line-height: 1.4;
}

@media (max-width: 620px) {
  .plan-list { grid-template-columns: 1fr; }
  /* The feature column is the one that can give: the two tier columns are set
     by their content and the free one already wraps. */
  .plan-compare { font-size: 12px; }
  .plan-compare th,
  .plan-compare td { padding: 9px 8px; }
  .plan-compare th:first-child,
  .plan-compare td:first-child { padding-left: 14px; }
  .plan-compare th:last-child,
  .plan-compare td:last-child { padding-right: 14px; }
}

/* ── Checkout, embedded ───────────────────────────────────────────── */
/*
 * Stripe's Checkout renders itself into `.checkout-mount` in its own iframe,
 * so nothing here styles the form — it styles the frame around it. The height
 * is Stripe's to set and it changes as the form does (a card error appears, a
 * tax id field opens), which is why the mount has no height of its own and
 * the dialog scrolls instead.
 */
.checkout-mount {
  padding: 16px 20px 20px;
  min-height: 320px;
}
.checkout-mount[hidden] { display: none; }

.checkout-wait {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 56px 24px;
  text-align: center;
}
.checkout-wait[hidden] { display: none; }

.checkout-wait-text {
  margin: 0;
  max-width: 42ch;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-2);
}

/* Motion is the whole content of this element: it is the only thing telling
   someone whose card has just been charged that the app is still working. It
   is dropped for a reduced-motion preference, where the wait text carries it
   alone — which is why that text is a sentence and not a label. */
.checkout-spin {
  width: 22px;
  height: 22px;
  border: 2px solid var(--rule);
  border-top-color: var(--terra);
  border-radius: 50%;
  animation: cp-spin 0.9s linear infinite;
}
@keyframes cp-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .checkout-spin { animation: none; }
}

/* ── Billing management ───────────────────────────────────────────── */

/* A scrolling panel has to keep CLOSE where it was put. Sticky rather than
   fixed: the header is the dialog's own, and a short panel that does not
   scroll must not have it floating over nothing. Applied to every dialog now
   that every dialog can scroll. */
.plan-dialog .plan-dialog-head {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--paper);
}

.bill-sec {
  padding: 16px 20px;
  border-bottom: 1px solid var(--rule-soft);
}
.bill-sec[hidden] { display: none; }

.bill-legend {
  margin: 0 0 10px;
  font-weight: 400;
  color: var(--faint);
}

.bill-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 16px;
  margin: 0;
}

.bill-fact-key { color: var(--muted); }

/* Right-aligned against the key's left edge, the way the account panel's
   facts are: the values are mono and tabular, so a common right edge is what
   makes an amount and a date read as a column rather than as prose. */
.bill-fact-val {
  margin: 0;
  color: var(--ink);
  text-align: right;
}

.bill-note {
  margin: 12px 0 0;
  color: var(--muted);
  line-height: 1.5;
  letter-spacing: 0.04em;
  text-transform: none;
  font-size: 11px;
}
.bill-note[hidden] { display: none; }

.bill-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 14px;
}
.bill-actions:empty { margin-top: 0; }

/* Cancelling is not a destructive-red button. It ends a subscription at the
   end of a period that is already paid for and can be undone from this same
   panel until that date arrives, so dressing it as a demolition overstates
   it; the outline says "not the primary action here" and that is all it
   needs to say. */
.bill-danger { border-color: var(--rule-hair); color: var(--muted); }
.bill-danger:hover:not(:disabled) { border-color: var(--bad); color: var(--bad); background: var(--err-bg); }

/* The confirm step. It replaces the panel's sections rather than sitting
   among them, so it carries no bottom rule — there is nothing under it but its
   own two buttons. */
.bill-confirm { border-bottom: none; }

.bill-confirm-note {
  margin: 14px 0 0;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.55;
}

.bill-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.bill-card {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--ink);
}

.bill-plans { border-bottom: none; }

.bill-current { color: var(--terra-dark); }

/* Same voice as CURRENT: both flags mark a card that is already in play, and
   which of the two it is, is what the word is for. A second colour here would
   be inventing a state the panel does not have. */
.bill-pending { color: var(--terra-dark); }

/* The plan already held is a statement, not an offer — no button, and a
   filled cell so the eye finds it before it reads the three names. */
.plan.is-current { background: var(--field); }

/* The plan being taken up at the renewal is filled for the same reason and by
   a *different* colour, because it is a different fact: --field is already
   spoken for by "what you are on", and two states sharing one surface put the
   whole weight of telling them apart on one word in 10px type. A terracotta
   wash reads as the one in motion — the accent this sheet uses for chosen —
   and leaves the unfilled card as the only offer on screen. */
.plan.is-pending { background: var(--pending-bg); }

.bill-fine {
  margin: 12px 0 0;
  color: var(--faint);
  line-height: 1.5;
  letter-spacing: 0.04em;
  text-transform: none;
  font-size: 11px;
}

.bill-invoices {
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--rule-soft);
}

.bill-invoice {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  gap: 14px;
  padding: 8px 0;
  background: var(--paper);
}

.bill-invoice-when { color: var(--ink); }
.bill-invoice-sum { color: var(--ink); }
.bill-invoice-status { color: var(--muted); }

.bill-invoice-link {
  color: var(--terra-dark);
  text-decoration: none;
  border-bottom: 1px solid currentColor;
}
.bill-invoice-link:hover { color: var(--ink); }

.bill-empty {
  margin: 0;
  padding: 8px 0;
  background: var(--paper);
  color: var(--muted);
}

.bill-foot { text-transform: none; letter-spacing: 0.04em; font-size: 11px; }

/* A button that reads as a link, because it is one in every way but the
   mechanism: opening the portal costs a signed session, so it is a POST and
   cannot be an <a href>. */
.bill-link {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--terra-dark);
  text-decoration: underline;
  cursor: pointer;
}
.bill-link:hover:not(:disabled) { color: var(--ink); }
.bill-link:disabled { color: var(--faint); cursor: not-allowed; }

@media (max-width: 620px) {
  .bill-invoice { grid-template-columns: 1fr auto auto; row-gap: 4px; }
  .bill-row { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* ── Deleting the account ─────────────────────────────────────────── */

/* Narrower than the billing panels: one question and two buttons. */
.delete-dialog { width: min(440px, calc(100vw - 32px)); }

.delete-body { padding: 22px 20px 6px; }
.delete-body[hidden] { display: none; }

.delete-title {
  margin: 0 0 10px;
  font-family: var(--serif);
  font-size: 30px;
  font-weight: 400;
  line-height: 1.1;
  color: var(--ink);
}

.delete-what { margin: 0; color: var(--ink-3); text-wrap: pretty; }

.delete-plan {
  margin: 12px 0 0;
  color: var(--err-fg);
  font-size: 13px;
  line-height: 1.5;
  text-wrap: pretty;
}
.delete-plan[hidden] { display: none; }

.delete-withdraw { margin: 6px 0 0; color: var(--muted); font-size: 12px; }
.delete-withdraw[hidden] { display: none; }
.delete-withdraw a { color: var(--danger-text); text-underline-offset: 2px; }
.delete-withdraw a:hover { color: var(--ink); }

.delete-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  padding: 20px 20px 10px;
}
.delete-actions[hidden] { display: none; }
.delete-actions .btn { height: 44px; }

/* Held, not clicked, for 3 seconds, and it fires on *release* over the button
   — see the gesture in clerk.js. Built the way the parameter column's reset
   is: two faces stacked in one grid cell, the upper one clipped from the
   bottom, so the label changes colour exactly where the fill has reached.
   clerk.js writes the clip itself rather than a `--charge` property: this
   sheet declares a fallback for every custom property it reads, and a
   per-frame value is not a token. The transition only drains it: while
   holding, the animation frame owns the fill and a transition would lag it.

   `touch-action: none` is load-bearing — without it a held finger scrolls the
   dialog and the pointer is cancelled before the charge completes. */
.delete-hold {
  display: grid;
  height: 44px;
  padding: 0;
  border: 1px solid var(--danger-fill);
  background: var(--danger);
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.delete-hold:disabled { cursor: not-allowed; opacity: 0.55; }
.delete-hold:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.delete-hold-face {
  grid-area: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--danger);
  color: var(--danger-on);
}
.delete-hold-fill {
  background: var(--danger-fill);
  color: var(--danger-fill-on);
  clip-path: inset(100% 0 0 0);
  transition: clip-path 0.3s ease-out;
}
.delete-hold.is-holding .delete-hold-fill { transition: none; }

.delete-hint { margin: 0; padding: 0 20px 18px; text-align: right; }
.delete-hint.is-alert { color: var(--danger-text); }
