/* =====================================================================
   Alldebrid Payment — design system (v2 redesign)
   Rebuilt from design_handoff_payment_flow/ (README.md tokens).
   Themes: "Alldebrid Gold" (Light, default) + Dark (required).
   Theme is set via <html data-theme="light|dark">; falls back to
   prefers-color-scheme. Icons are our own SVGs (assets/img).
   ===================================================================== */

/* ---- Fonts ----------------------------------------------------------------------------------------
   local() only: the woff2 files were never added, so the url() sources 404'd on EVERY page load — twice,
   visible in any browser console on the payment form. The face is used when the visitor happens to have it
   installed and the system stack in --font takes over otherwise, which is what was already happening.
   To self-host properly, drop the two woff2 into public/assets/fonts/ and restore the url() sources:
       url('/assets/fonts/HankenGrotesk-Variable.woff2') format('woff2')
   A CDN is not an option here — the CSP on these pages is font-src 'self'. */
@font-face{font-family:'Hanken Grotesk';font-weight:400 800;font-display:swap;
  src:local('Hanken Grotesk');}
@font-face{font-family:'Spline Sans Mono';font-weight:400 600;font-display:swap;
  src:local('Spline Sans Mono');}

/* ---- Tokens: neutral (Light) + accent (Alldebrid Gold) ---- */
:root{
  --bg:#e9edf2; --card:#ffffff; --card2:#fbfcfd; --track:#f1f3f7; --seg-on:#ffffff;
  --ink:#182130; --muted:#5c6675; --line:#e4e8ee; --field:#ffffff;
  --danger:#c0362c; --danger-bg:#fbeae9; --ok:#1c7f54; --ok-ink:#ffffff;
  --cta:#e8a400; --cta-h:#cc9000; --cta-ink:#3a2b00;
  --panel:#fbf6ea; --panel-line:#f1e6cf; --accent:#e8a400; --focus:#cc9000;
  /* --accent is the FILL gold. As TEXT on a light background it measures 2.16:1 — nowhere near AA — so
     anything gold that has to be READ uses --accent-ink instead (5.70:1 on white, 5.29:1 on the panel). */
  --accent-ink:#8a5e00;
  --font:'Hanken Grotesk',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
  --mono:'Spline Sans Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  --shadow:0 1px 3px rgba(16,24,40,.05),0 14px 44px rgba(16,24,40,.12);
}
:root[data-theme="dark"]{
  --bg:#0f141b; --card:#161d27; --card2:#1b2330; --track:#222b39; --seg-on:#2c3645;
  --ink:#eef2f7; --muted:#95a1b1; --line:#2a3440; --field:#1d2632;
  --danger:#f0857b; --danger-bg:#38211f; --ok:#3fb37e; --ok-ink:#0f141b;
  --cta:#e8a400; --cta-h:#f0b93a; --cta-ink:#241a00;
  --panel:#1e2732; --panel-line:#2f3a49; --accent:#f0b93a; --focus:#e8a400;
  --accent-ink:#f0b93a;   /* on the dark card this already measures 9.45:1 */
  --shadow:0 1px 3px rgba(0,0,0,.3),0 14px 44px rgba(0,0,0,.45);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --bg:#0f141b; --card:#161d27; --card2:#1b2330; --track:#222b39; --seg-on:#2c3645;
    --ink:#eef2f7; --muted:#95a1b1; --line:#2a3440; --field:#1d2632;
    --danger:#f0857b; --danger-bg:#38211f; --ok:#3fb37e; --ok-ink:#0f141b;
    --cta:#e8a400; --cta-h:#f0b93a; --cta-ink:#241a00;
    --panel:#1e2732; --panel-line:#2f3a49; --accent:#f0b93a; --focus:#e8a400;
  --accent-ink:#f0b93a;   /* on the dark card this already measures 9.45:1 */
    --shadow:0 1px 3px rgba(0,0,0,.3),0 14px 44px rgba(0,0,0,.45);
  }
}

*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  font-family:var(--font); background:var(--bg); color:var(--ink);
  -webkit-font-smoothing:antialiased; line-height:1.45;
  min-height:100vh; display:flex; align-items:flex-start; justify-content:center;
  padding:32px 16px;
}
a{color:var(--cta)}

/* ---- Card shell ---- */
.pay-card{
  width:100%; max-width:620px; background:var(--card);
  border:1px solid var(--line); border-radius:18px; overflow:hidden; box-shadow:var(--shadow);
}
.pay-head{display:flex;align-items:center;justify-content:space-between;padding:20px 24px 0}
.pay-brand{display:flex;align-items:center;gap:10px;font-weight:800;font-size:18px}
.pay-brand img{height:26px;display:block}
.pay-secure{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:700;
  color:var(--ok);letter-spacing:.04em}
.pay-secure svg{width:14px;height:14px}
.pay-title{padding:14px 24px 4px}
.pay-title h1{margin:0;font-size:21px;font-weight:700}
.pay-title p{margin:2px 0 0;font-size:13px;color:var(--muted)}

/* ---- Order summary (persistent) ---- */
/*
 * The 24px sides inset it from the card edge, matching .pay-title and .stepper — needed because header.php
 * renders this box as a DIRECT CHILD of the card, outside .pay-body.
 */
.order{margin:12px 24px 0;padding:15px 17px;background:var(--panel);
  border:1px solid var(--panel-line);border-radius:14px}
/*
 * ...but manage-subscription.php renders one INSIDE .pay-body, which already carries padding:14px 24px. The
 * margin was landing on top of that padding, so the box sat 48px in while the .receipt blocks beneath it sat
 * at 24 — two panels on one screen with different left edges. Zero it here and take .receipt's own 14px
 * rhythm instead.
 */
.pay-body .order{margin:14px 0}
.order-top{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
.order-plan{font-size:12px;color:var(--muted)}
.order-plan strong{display:block;font-weight:700;text-transform:uppercase;letter-spacing:.03em}
.order-price{text-align:right}
.order-price b{font-family:var(--mono);font-size:23px;font-weight:600}
.order-price span{display:block;font-size:11px;color:var(--muted)}
.order-rows{margin-top:12px;border-top:1px solid var(--panel-line);padding-top:10px;
  display:grid;gap:6px}
.order-row{display:flex;justify-content:space-between;gap:10px;font-size:12px}
.order-row .k{color:var(--muted)}
.order-row .v{text-align:right}
.order-row .mono{font-family:var(--mono)}
/* Separates the money rows from the identifiers. Same hairline as the block's own top border, with the
   grid gap doing the spacing, so it reads as a grouping rather than a second box. */
.order-sep{height:1px;background:var(--panel-line);margin:2px 0}

/* ---- Stepper ---- */
.stepper{display:flex;align-items:center;padding:16px 24px 4px}
.step{display:flex;align-items:center;gap:8px}
/*
 * Measured, not eyeballed. White on the gold dot was 2.16:1 and white on the grey one 1.55:1 — the step
 * NUMBER, which is the whole point of the dot, was the least legible thing in the stepper. Dark ink on gold
 * is 6.39:1; the inactive dot gets a darker grey for the same reason.
 * The inactive label at #98a1af was 2.61:1; --muted is 5.81:1 and still reads as secondary.
 */
.step .dot{width:24px;height:24px;border-radius:50%;display:grid;place-items:center;
  font-size:11px;font-weight:700;color:#fff;background:#6b7585;transition:background .3s}
.step .lbl{font-size:12px;font-weight:600;color:var(--muted);transition:color .3s}
.step.on .dot{background:var(--cta);color:var(--cta-ink)} .step.on .lbl{color:var(--ink)}
/* --ok flips lightness between themes (dark green on light, light green on dark), so the tick cannot be
   one fixed colour: white reads at 4.98:1 on the light-theme green and 2.64:1 on the dark-theme one. */
.step.done .dot{background:var(--ok);color:var(--ok-ink)} .step.done .lbl{color:var(--ink)}
.step.err .dot{background:var(--danger)} .step.err .lbl{color:var(--danger)}
.step-line{flex:1;height:2px;background:var(--line);margin:0 8px}

/* ---- Body ---- */
.pay-body{padding:14px 24px 4px}
.screen{animation:fadeUp .3s ease}
@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* ---- Express wallets ---- */
.express{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.wallet-btn{height:48px;border-radius:12px;border:1px solid transparent;cursor:pointer;
  display:flex;align-items:center;justify-content:center;gap:8px;font-family:var(--font);
  font-weight:600;font-size:15px}
.wallet-btn:disabled{opacity:.5;cursor:not-allowed}
/* Wallet marks (M4 — UI only, the providers are not enabled on the Axepta side yet).
   These are the official badge assets: each carries its own background and border (Apple Pay is a dark mark
   on white, Google Pay a white mark on black — opposite polarities), and both brands' guidelines forbid
   recolouring or re-backgrounding them. So the button is a transparent frame and the badge IS the button;
   no background, no border, no filter. */
.wallet-btn{background:none;border:none;padding:0}
.wallet-btn img{height:100%;width:auto;display:block;border-radius:12px}
.wallet-btn:hover img{opacity:.88}
.express-note{margin:8px 0 0;text-align:center;font-size:11px;color:var(--muted)}

/* ---- Divider ---- */
.or{display:flex;align-items:center;gap:12px;margin:16px 0;color:var(--muted);
  font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em}
.or::before,.or::after{content:"";flex:1;height:1px;background:var(--line)}

/* ---- Form ---- */
.field-label{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;
  color:var(--muted);margin:0 0 8px}
.field{position:relative;margin-bottom:4px}
/* Persistent field labels. A placeholder disappears the moment the field is filled, which leaves a row of
   digits with nothing naming them — and placeholder text is not a label to a screen reader either. */
.field-lbl{display:block;font-size:12px;font-weight:600;color:var(--muted);margin:0 0 5px}

.field input{width:100%;height:54px;border:1.6px solid var(--line);border-radius:12px;
  background:var(--field);color:var(--ink);font-family:var(--font);font-size:16px;
  padding:0 14px;outline:none;transition:border-color .15s,box-shadow .15s}
.field.icon input{padding-left:46px}
.field .ic{position:absolute;left:14px;top:50%;transform:translateY(-50%);
  width:22px;height:22px;color:var(--muted);pointer-events:none}
.field input:focus{border-color:var(--focus);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--focus) 16%,transparent)}
.field.invalid input{border-color:var(--danger);background:var(--danger-bg)}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.net-chip{position:absolute;right:12px;top:50%;transform:translateY(-50%);
  font-size:11px;font-weight:700;padding:3px 8px;border:none;
  border-radius:8px;background:var(--field);color:var(--ink)}
/* Showing the mark instead of the word: tighter padding, and a fixed height so a square mark (Amex) and a
   wide one (Visa/CB) occupy the same vertical space rather than resizing the field as you type. */
.net-chip.has-mark{padding:2px 5px;line-height:0}
.net-chip.has-mark img{height:25px;width:auto;display:block}
.field-foot{display:flex;justify-content:space-between;align-items:center;min-height:15px;
  margin:6px 2px 12px}
.field-err{display:none;align-items:center;gap:6px;color:var(--danger);font-size:12px}
.field-err.show{display:flex}
.field-err svg{width:14px;height:14px;flex:none}
/* margin-left:auto, not just the parent's space-between: .field-err is display:none until there is an
   error, so with a single flex child the switch was falling to the LEFT edge in the normal case. This keeps
   it under the brand mark either way. */
.net-switch{font-size:11.5px;font-weight:600;color:var(--cta);text-decoration:underline;
  background:none;border:none;cursor:pointer;font-family:var(--font);display:none;
  margin-left:auto;text-align:right}
.net-switch.show{display:inline}

/* ---- Subscription terms block --------------------------------------------------------------------
   Shown above the card fields so the billing schedule is read BEFORE the card is typed, not after. */
.sub-terms{border:1px solid var(--line);border-radius:12px;padding:12px 14px;margin:0 0 14px;
  background:var(--card2)}
.sub-terms p{margin:0 0 6px;font-size:12.5px;color:var(--muted);line-height:1.5}
.sub-terms p:last-child{margin-bottom:0}
.sub-terms .sub-terms-head{font-size:13.5px;font-weight:600;color:var(--ink);margin-bottom:8px}
.sub-terms .sub-terms-free{color:var(--ok);font-weight:600}

/* ---- Consent (subscription) ---- */
/* 13.5px, a touch above the surrounding copy rather than below it. This one sentence is the legal
   authorisation for every future charge; it should not be the smallest thing on the screen, and it is
   already in full-contrast --ink rather than the muted grey used for supporting text. */
.consent{display:flex;gap:10px;padding:13px 14px;border:1.6px solid var(--line);
  border-radius:12px;background:var(--card2);margin:4px 0 14px;font-size:13.5px;line-height:1.5}
.consent.invalid{border-color:var(--danger)}
.consent input{accent-color:var(--cta);width:18px;height:18px;margin-top:1px;flex:none}
.consent label{color:var(--ink)}

/* ---- Pay button ---- */
.pay-btn{width:100%;height:54px;border-radius:13px;border:none;cursor:pointer;
  background:var(--cta);color:var(--cta-ink);font-family:var(--font);font-weight:700;
  font-size:16px;display:flex;align-items:center;justify-content:center;gap:8px;
  transition:background .15s;text-decoration:none}   /* <a class="pay-btn"> must not underline */
.pay-btn:hover{background:var(--cta-h)} .pay-btn:active{transform:translateY(1px)}
/*
 * Equal-weight alternative to .pay-btn. Same height, same type size, same hit area — a real button, never a
 * de-emphasised one.
 *
 * An earlier version of this file made "Cancel your subscription" a hairline text link next to a solid gold
 * "Change card", and on the confirmation screen made "Keep my subscription" the loud primary with the
 * confirming action reduced to a link. That is a dark pattern: it puts a thumb on the scale of a decision
 * the customer has already made, on the one page they reached deliberately in order to make it. Someone who
 * came to cancel gets the same size button as someone who came to stay.
 *
 * .danger marks a destructive action rather than shouting it: danger-coloured text and border on the normal
 * surface, so it is unmistakably the irreversible one and unmistakably still a button.
 */
.btn-alt{width:100%;height:54px;border-radius:13px;border:1px solid var(--line);cursor:pointer;
  background:var(--card);color:var(--ink);font:inherit;font-size:15px;font-weight:600;
  display:inline-flex;align-items:center;justify-content:center;gap:8px;text-decoration:none}
.btn-alt:hover{border-color:var(--focus);background:var(--card2)}
.btn-alt:active{transform:translateY(1px)}
.btn-alt svg{width:18px;height:18px}
.btn-alt.danger{color:var(--danger);border-color:var(--danger)}
.btn-alt.danger:hover{background:var(--danger-bg);border-color:var(--danger)}
/* The positive alternative reads as one, without outshouting the action the customer actually came for.
   Tinted like .btn-alt.danger and exactly the same size — a choice offered, not a choice pushed. */
.btn-alt.positive{color:var(--ok);border-color:var(--ok)}
.btn-alt.positive:hover{background:color-mix(in srgb,var(--ok) 12%,var(--card));border-color:var(--ok)}
.pay-btn svg{width:18px;height:18px}

.legal{margin:12px 0 4px;text-align:center;font-size:11.5px;color:var(--muted)}
/* Regulatory notice for recurring payments — sits above the terms line rather than inside the consent
   sentence, so the checkbox reads as plain language and the legal basis is still stated. */
.legal-mandate{margin-bottom:2px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;font-size:10.5px}
.marks{display:flex;justify-content:center;align-items:center;gap:10px;margin:14px 0 4px}
.marks img{height:26px;width:auto;display:block}

/* ---- State screens: processing / redirect / challenge ---- */
.state{text-align:center;padding:34px 24px 40px}
.spinner{width:52px;height:52px;border:4px solid var(--track);border-top-color:var(--cta);
  border-radius:50%;margin:0 auto 18px;animation:spin .8s linear infinite}
.spinner.sm{width:44px;height:44px;border-width:4px}
@keyframes spin{to{transform:rotate(360deg)}}
.state h2{margin:0 0 6px;font-size:16px;font-weight:600}
.state p{margin:0;font-size:13px;color:var(--muted)}
.pill{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:999px;
  background:var(--panel);font-size:12px;font-weight:700;margin-bottom:14px}
.pill svg{width:14px;height:14px}
.tds-frame{border:1px solid var(--line);border-radius:12px;overflow:hidden;margin:8px 0}
.tds-frame iframe{width:100%;min-height:420px;border:0;display:block;background:#fff}

/*
 * Success screens (.state-done) breathe more than the processing ones they share .state with.
 * A spinner screen is deliberately tight — it is transient and nobody reads it. A success screen is the last
 * thing a customer sees, is read properly, and had its message running straight into the CTA with no gap at
 * all: .state p is margin:0 and .pay-btn has no margin-top. The measure is capped too, so a two-line message
 * breaks somewhere sensible instead of spanning the full card width.
 */
.state-done h2{margin-bottom:10px}
.state-done p{line-height:1.5;max-width:44ch;margin-left:auto;margin-right:auto}
.state-done .pay-btn{margin-top:22px}
.state-done .legal{margin-top:14px}
.state-done .receipt{margin-top:20px}

/* ---- Success / receipt ---- */
.badge-circle{width:60px;height:60px;border-radius:50%;display:grid;place-items:center;
  margin:0 auto 12px}
.badge-ok{background:color-mix(in srgb,var(--ok) 16%,var(--card));color:var(--ok)}
.badge-err{background:var(--danger-bg);color:var(--danger)}
.badge-circle svg{width:30px;height:30px}
.receipt{border:1px solid var(--line);border-radius:13px;overflow:hidden;margin:14px 0;text-align:left}
/* A live subscription, tinted so it separates from the ended ones at a glance in a list of several.
   color-mix against --card rather than a fixed green, so it follows the dark theme without a second rule,
   and stays a wash — the text on top keeps the same contrast it has everywhere else. */
.receipt.is-active{background:color-mix(in srgb,var(--ok) 6%,var(--card));
  border-color:color-mix(in srgb,var(--ok) 28%,var(--line))}
.receipt.is-active .receipt-head{background:color-mix(in srgb,var(--ok) 10%,var(--card))}
.receipt.is-active .receipt-row{border-top-color:color-mix(in srgb,var(--ok) 16%,var(--line))}
.receipt-head{display:flex;justify-content:space-between;align-items:center;
  padding:11px 14px;background:var(--card2);font-size:12px;font-weight:700;
  text-transform:uppercase;letter-spacing:.03em;color:var(--muted)}
.receipt-head .approved{color:var(--ok);display:inline-flex;align-items:center;gap:5px}
.receipt-row{display:flex;justify-content:space-between;gap:10px;padding:9px 14px;font-size:12.5px;
  border-top:1px solid var(--line)}
.receipt-row .k{color:var(--muted)} .receipt-row .mono{font-family:var(--mono)}
/* A whole subscription card acting as one control, on the payment history page.
   A full-width .btn-alt under each card turned the list into a stack of 54px white slabs that outweighed
   the data they belonged to. The card IS the thing being opened, so the card is the target. */
.receipt.is-open{cursor:pointer;transition:border-color .15s,box-shadow .15s}
.receipt.is-open:hover{border-color:var(--focus);box-shadow:0 1px 6px rgba(24,33,48,.07)}
.receipt.is-open:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.receipt-go{display:flex;justify-content:flex-end;align-items:center;gap:5px;
  padding:9px 14px;border-top:1px solid var(--line);background:var(--card2);
  font-size:12px;font-weight:600;color:var(--accent-ink)}
.receipt-go svg{width:14px;height:14px}
.receipt.is-open:hover .receipt-go{text-decoration:underline}
/* The one you arrived from. A footer inside the card, not a line of coloured text floating under it —
   that read as a warning about the subscription rather than a note about the page. Inside the card it is
   plainly about the card, and the header stays free to keep showing ACTIVE / CANCELLED.
   Left-aligned with the arrow first, because it goes BACK: mirroring the layout is what distinguishes it
   from the forward action at a glance, before anyone reads either label. */
.receipt-go.is-back{justify-content:flex-start;color:var(--muted);font-weight:500}

/* Language switcher, in the header. Quiet by default: it is a correction for the minority whose browser
   language is not the one they read in, not a decision the page is asking anyone to make.
   <details> so it needs no script — the CSP has no 'unsafe-inline' for a select's change handler. */
.langsel{position:relative;margin-left:auto;margin-right:14px}
.langsel summary{display:inline-flex;align-items:center;gap:6px;cursor:pointer;list-style:none;
  padding:4px 8px;border:1px solid var(--line);border-radius:9px;background:var(--card);
  font-size:11.5px;font-weight:700;letter-spacing:.03em;color:var(--muted)}
.langsel summary::-webkit-details-marker{display:none}
.langsel summary:hover{color:var(--ink);border-color:var(--focus)}
.langsel summary:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.langsel summary img{width:18px;height:auto;display:block;border-radius:2px}
.langsel summary svg{width:11px;height:11px;transition:transform .15s}
.langsel[open] summary svg{transform:rotate(180deg)}
.langsel-menu{position:absolute;top:calc(100% + 5px);right:0;z-index:20;min-width:168px;
  display:flex;flex-direction:column;padding:5px;
  background:var(--card);border:1px solid var(--line);border-radius:11px;
  box-shadow:0 6px 22px rgba(20,28,40,.13)}
.langsel-opt{display:flex;align-items:center;gap:9px;width:100%;padding:7px 9px;
  border:0;border-radius:7px;background:none;cursor:pointer;
  font:inherit;font-size:13px;color:var(--ink);text-align:left;white-space:nowrap}
.langsel-opt img{width:20px;height:auto;display:block;border-radius:2px}
.langsel-opt:hover{background:var(--card2)}
.langsel-opt:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.langsel-opt.is-on{color:var(--muted);cursor:default;background:var(--card2)}

.note{display:inline-block;padding:9px 13px;border-radius:10px;font-size:12.5px}
.note-err{background:var(--danger-bg);color:var(--danger);border:1px solid var(--danger)}
/* Neutral, informational sibling of .note-err — states a fact rather than reporting a problem, so it takes
   the panel tint instead of the danger colours. Block, because it carries a couple of lines. */
.note-info{display:block;background:var(--panel);border:1px solid var(--panel-line);
  border-radius:10px;padding:11px 13px;margin:0 0 14px;line-height:1.5}
/* The positive sibling: reports that something WORKED, where .note-err reports that it did not. Tinted
   with color-mix against --card exactly as .receipt.is-active and .btn-alt.positive are, so it follows the
   dark theme without needing a second rule. */
.note-ok{display:block;background:color-mix(in srgb,var(--ok) 9%,var(--card));color:var(--ok);
  border:1px solid color-mix(in srgb,var(--ok) 42%,var(--line));
  border-radius:10px;padding:11px 13px;margin:0 0 14px;line-height:1.5}
.note-ok b{color:var(--ok)}
.note-info b{color:var(--ink)}
.note-info ul{margin:7px 0 0;padding-left:17px}
.note-info li{margin:3px 0}

/* ---- Error state ----------------------------------------------------------------------------------
   The badge and heading already say "this failed", so the reason below them does not need a second red
   outline shouting it again: inside .state the alert keeps its tint and drops the border. The reassurance
   sits closer to the reason it qualifies than to the button, and the action gets room to breathe. */
/* Same 10px the success and error screens give their heading — .state's default 6px is tuned for the
   transient processing screens, where the heading and its line are one unit. */
.state-confirm h2{margin-bottom:10px}

/*
 * The one fact that answers "do I lose access now?" — full-contrast ink, not the muted grey of the
 * supporting copy above it, because it is the reason someone can click cancel without hesitating.
 *
 * Scoped `.state .cancel-access`, NOT a bare `.cancel-access`: `.state p` is a class plus an element
 * (0,0,1,1) and outranks a lone class (0,0,1,0), so the bare selector lost both the margin AND the colour
 * while looking perfectly correct in the file. Two classes (0,0,2,0) wins.
 */
.state .cancel-access{margin-top:18px;color:var(--ink);font-weight:600}

.state-error{padding-top:20px}
.state-error h2{margin-bottom:10px}
/* 46ch, not 38: at 38 a two-clause message ("…for now. You can contact our support, or wait 23h 56min.")
   broke into three ragged lines in a box narrower than the button under it. */
.state-error .note-err{border:none;display:block;max-width:46ch;margin:0 auto;
  font-size:13px;line-height:1.55}
/*
 * The gap before the CTA belongs to the BUTTON, not to the reassurance line above it.
 * It used to live on .reassure (margin-bottom 18px) with .pay-btn at margin-top:2px — but .reassure only
 * renders when a card was actually submitted. On every other error (a throttle, an expired token) it is
 * absent, and the message ran straight into the button 2px below. Which is exactly the screen people see
 * when something went wrong and they are least inclined to squint at it.
 */
.state-error .reassure{margin:10px 0 0;text-align:center;font-size:12px;color:var(--muted)}
.state-error .pay-btn{margin-top:22px}
.state-error .legal{margin-top:14px}
.state-error .legal a{color:var(--muted);text-decoration:underline;text-underline-offset:2px}
.state-error .legal a:hover{color:var(--ink)}

/* ---- Footer ---- */
.pay-foot{padding:15px 24px 18px;border-top:1px solid var(--line);background:var(--card2)}
/* Links inside the legal/footer microcopy: readable, but never louder than the copy around them. */
.legal a,.mor a{color:inherit;text-decoration:underline;text-underline-offset:2px}
.legal a:hover,.mor a:hover{color:var(--ink)}
.pay-foot .mor{font-size:11.5px;color:var(--muted);text-align:center;margin:0 0 8px}
.trust{display:flex;justify-content:center;align-items:center;gap:14px;flex-wrap:wrap}
.trust span{display:inline-flex;align-items:center;gap:5px;font-size:10.5px;font-weight:700;
  color:var(--muted);letter-spacing:.03em}
.trust img,.trust svg{height:15px;width:15px;flex:none}

/* ---- Responsive ---- */
@media (max-width:520px){
  body{padding:16px 12px}
  .pay-head,.pay-title,.pay-body{padding-left:18px;padding-right:18px}
  .order{margin-left:18px;margin-right:18px}
  .stepper{padding-left:18px;padding-right:18px}
  .express{grid-template-columns:1fr}
}

/* ---- Dev-area PSP-mode banner (staging only; prod sets psp_mode_switchable=false) ---------------- */
.env-banner{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:10px;padding:8px 10px;
  border:1px solid var(--line);border-radius:8px;background:var(--track);color:var(--muted);
  font-size:12px;line-height:1.45}
.env-banner .env-tag{font-weight:700;letter-spacing:.04em;color:var(--ink)}
.env-banner a{color:inherit;text-decoration:underline;margin-left:auto;white-space:nowrap}
.env-banner.env-live{background:var(--danger-bg);border-color:var(--danger);color:var(--danger)}
.env-banner.env-live .env-tag,.env-banner.env-live strong{color:var(--danger)}

/* ---- Staging test-card picker (views/test-cards.php) -----------------------------------------------
   Reads as a dev tool, not part of the checkout: dashed border, uppercase header, collapsed by default. It
   renders only where $debug does, but it sits on a screen that also takes real payments in prod PSP mode, so
   it should never be mistakable for something a customer is meant to touch. */
.devcards{margin-top:14px;border:1px dashed var(--line);border-radius:12px;overflow:hidden}
.devcards-toggle{width:100%;display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:10px 13px;border:none;background:var(--card2);cursor:pointer;font:inherit;font-size:12px;
  font-weight:700;color:var(--muted);letter-spacing:.04em;text-transform:uppercase}
.devcards-toggle:hover{color:var(--ink)}
.devcards-mode{font-size:10.5px;padding:2px 7px;border-radius:999px}
.devcards-mode.is-sandbox{background:var(--panel);color:var(--accent-ink)}
.devcards-mode.is-live{background:var(--danger-bg);color:var(--danger)}
.devcards-body{padding:6px 6px 8px}

/* One row per SCENARIO. The outcome is the heading and carries the colour, because that is what the row is
   chosen by; the providers are chips on the right, so a scenario with two of them reads as one line and not
   as two entries. */
.devrow{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;
  padding:7px 8px;border-radius:9px}
.devrow:hover{background:var(--card2)}
.devrow-main{display:flex;flex-direction:column;gap:2px;min-width:0}
.devrow-label{font-size:12.5px;font-weight:600;line-height:1.35}
.devrow-note{font-size:11px;line-height:1.45;color:var(--muted)}
.devrow-actions{display:flex;gap:5px;flex:0 0 auto;padding-top:1px}
.devchip{display:inline-flex;align-items:center;gap:4px;padding:3px 9px;border:1px solid var(--line);
  border-radius:999px;background:var(--card);cursor:pointer;font:inherit;font-size:11px;font-weight:600;
  color:var(--muted);white-space:nowrap}
.devchip:hover{border-color:var(--focus);color:var(--ink)}
.devchip-otp{font-family:var(--mono);font-size:10px;padding:0 4px;border-radius:4px;
  background:var(--panel);color:var(--accent-ink)}

.tag-ok{color:var(--ok)} .tag-challenge{color:var(--accent-ink)} .tag-fail{color:var(--danger)}

.devmore{margin-top:4px}
.devmore>summary{padding:7px 8px;cursor:pointer;font-size:11px;font-weight:700;color:var(--muted);
  letter-spacing:.04em;text-transform:uppercase;list-style:none}
.devmore>summary::-webkit-details-marker{display:none}
.devmore>summary:before{content:"+ ";font-family:var(--mono)}
.devmore[open]>summary:before{content:"- "}
.devmore>summary:hover{color:var(--ink)}

.devotps{margin:8px 8px 2px;padding:9px 11px;border-radius:9px;background:var(--card2);
  display:grid;gap:4px;font-size:11px;color:var(--muted);line-height:1.5}
.devotps-head{font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}
.devotp code{font-family:var(--mono);font-weight:700;margin-right:5px}
.devotps-foot{margin-top:3px;font-style:italic}

/* The live-PSP warning, moved to the top of the form when a test card is filled in prod mode. */
.devcards-alert{margin:0 0 14px;padding:11px 13px;border-radius:10px;border:1px solid var(--danger);
  background:var(--danger-bg);color:var(--danger);font-size:12.5px;line-height:1.5}
