/* ============================================================
   Meridian — shared components
   ------------------------------------------------------------
   Loaded on every page, after styles.css and before
   admin-common.css.

   Everything here was previously declared in page-local <style>
   blocks, several of them on two, three or four pages at once
   with values that had already drifted apart. A component that
   more than one page needs belongs here, not on the page.
   ============================================================ */

/* ============================================================
   Buttons
   ------------------------------------------------------------
   .btn was defined on the admin pages and again, with different
   padding, on subscription-success. Several pages also invented
   their own name for the same thing — .subscription-button,
   .plan-button, .checkout-button, .modal-button — and a modifier
   used without .btn got a colour but no padding or radius, which
   is why .btn-primary and friends carry the base rule too.
   Every alias resolves to the same component here.
   ============================================================ */
.btn,
.subscription-button,
.plan-button,
.checkout-button,
.modal-button,
.btn-primary,
.btn-secondary,
.btn-success,
.btn-danger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-5);
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    cursor: pointer;
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: 600;
    line-height: 1.25;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color .15s ease, border-color .15s ease;
}

/* Primary is the default for the page-specific aliases: each of them
   was the page's one call to action. */
.btn-primary,
.subscription-button,
.plan-button,
.checkout-button,
.modal-button-confirm {
    background-color: var(--accent);
    color: var(--accent-ink);
}

.btn-primary:hover,
.subscription-button:hover,
.plan-button:hover,
.checkout-button:hover,
.modal-button-confirm:hover {
    background-color: var(--accent-deep);
    color: var(--accent-ink);
}

.btn-secondary,
.modal-button-cancel {
    background-color: var(--surface);
    color: var(--ink-2);
    border-color: var(--line);
}

.btn-secondary:hover,
.modal-button-cancel:hover {
    background-color: var(--surface-2);
    color: var(--ink);
}

.btn-success { background-color: var(--ok); color: var(--on-fill); }
.btn-success:hover { background-color: var(--ok-ink); color: var(--on-fill); }
.btn-danger { background-color: var(--danger); color: var(--on-fill); }
.btn-danger:hover { background-color: var(--danger-ink); color: var(--on-fill); }

.btn-full,
.subscription-button,
.checkout-button,
.plan-button { width: 100%; }

/* A call to action that is the point of its section - the landing
   page's. Everywhere else the standard size is the right one. */
.btn-lg {
    padding: var(--sp-3) var(--sp-6);
    font-size: var(--fs-base);
}

/* Disabled, including the "current plan" button on pricing. */
.btn:disabled,
.plan-button:disabled,
.plan-button.current,
.plan-button.disabled,
.subscription-button:disabled,
.checkout-button:disabled {
    background-color: var(--surface-3);
    color: var(--ink-3);
    border-color: transparent;
    cursor: not-allowed;
}

/* Rows of actions. Each of these was its own one-off flex rule. */
.action-buttons,
.success-actions,
.plan-actions,
.modal-buttons,
.payment-modal-buttons,
.range-buttons {
    display: flex;
    gap: var(--sp-3);
    flex-wrap: wrap;
}

.action-buttons,
.success-actions {
    justify-content: center;
}

/* ============================================================
   Forms — .form-group was on four pages.
   ============================================================ */
.form-group {
    margin-bottom: var(--sp-4);
}

.form-group label {
    display: block;
    margin-bottom: var(--sp-1);
    font-weight: 600;
    font-size: var(--fs-sm);
    color: var(--ink-2);
}

.form-group input:not([type="checkbox"]):not([type="radio"]),
.form-group select,
.form-group textarea {
    width: 100%;
    padding: var(--sp-3);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    font-family: inherit;
    font-size: var(--fs-base);
    box-sizing: border-box;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--ring);
}

/* A form's actions. A form is a flex column, which stretches its children,
   so a bare button ends up as wide as the page instead of as wide as its
   label. This row keeps them at their natural size. */
.form-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    align-items: center;
}

.form-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
    gap: var(--sp-4);
}

/* ============================================================
   Auth pages — the .container-login card on login, register,
   forgot-password and the reset-password route.
   ============================================================ */

/* The line under the heading that explains what the page wants.
   h1 is centred inside this card, so the lead follows it and sits
   tight underneath rather than a full step below. */
.container-login .lead {
    margin: calc(var(--sp-4) * -1) 0 var(--sp-5);
    color: var(--ink-2);
    font-size: var(--fs-sm);
    text-align: center;
    text-wrap: pretty;
}

/* The secondary way out: "Back to login" under the primary action. */
.container-login .auth-alt {
    margin: var(--sp-5) 0 0;
    padding-top: var(--sp-4);
    border-top: 1px solid var(--line-2);
    text-align: center;
    font-size: var(--fs-sm);
}

.container-login .form-note {
    margin-top: var(--sp-3);
    text-align: center;
}

.container-login .form-note:empty {
    margin-top: 0;
}

/* ============================================================
   Modals — declared on admin.html, admin-subscriptions.html and
   pricing.html; pricing is not an admin page, so it could not
   even reach the admin copy.
   ============================================================ */
.modal {
    display: none;
    position: fixed;
    z-index: var(--z-modal);
    inset: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(15, 17, 21, .55);
}

.modal.open {
    display: block;
}

/* admin-subscriptions and pricing toggle a `hidden` class rather than
   `open`. Both spellings must hide the overlay, and this has to beat any
   page rule that sets `display: flex` on .modal. */
.modal.hidden,
.modal[hidden] {
    display: none !important;
}

.modal-content {
    background-color: var(--surface);
    margin: 5vh auto;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-lg);
    width: min(540px, calc(100% - var(--sp-8)));
    max-height: 86vh;
    overflow-y: auto;
    position: relative;
}

/* A dialog with a body keeps its header and its actions in place and scrolls
   only the body. Without this the whole .modal-content scrolls, so on a tall
   dialog - the plan switch preview is 774px - Cancel and Confirm sit below the
   fold, reachable only by scrolling past the thing being confirmed. Modals with
   no .modal-body are untouched and still scroll as a whole. */
.modal-content:has(.modal-body) {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* A form between the dialog and its body has to pass the flex context along,
   or the body is not the part that scrolls. */
.modal-content:has(.modal-body) > form {
    display: flex;
    flex-direction: column;
    min-height: 0;
    flex: 1;
}

.modal-content:has(.modal-body) .modal-body {
    overflow-y: auto;
    min-height: 0;
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    border-bottom: 1px solid var(--line-2);
    background-color: var(--surface);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
}

.modal-header h3,
.modal-title {
    margin: 0;
    color: var(--ink);
    font-size: var(--fs-md);
    font-weight: 600;
}

.modal-body {
    padding: var(--sp-5);
}

.modal-footer,
.modal-actions,
.modal-buttons {
    display: flex;
    gap: var(--sp-2);
    flex-wrap: wrap;
    justify-content: flex-end;
    padding: var(--sp-4) var(--sp-5);
    border-top: 1px solid var(--line-2);
    background-color: var(--surface-2);
    border-radius: 0 0 var(--r-lg) var(--r-lg);
}

.close,
.modal-close,
.modal-close-btn {
    color: var(--ink-3);
    font-size: 26px;
    font-weight: 400;
    cursor: pointer;
    line-height: 1;
    background: none;
    border: 0;
    padding: 0 var(--sp-1);
    min-width: 0;
}

.close:hover,
.modal-close:hover,
.modal-close-btn:hover {
    color: var(--ink);
    background: none;
}

/* A grouped block inside a modal body — a before/after pair, a summary.
   The tinted variants say which side of a change you are looking at. */
.modal-section {
    padding: var(--sp-4);
    margin-bottom: var(--sp-4);
    background: var(--surface-2);
    border: 1px solid var(--line-2);
    border-radius: var(--r-md);
}

.modal-section:last-child { margin-bottom: 0; }

/* A value quoted back as it was entered, set apart from the surrounding form. */
.modal-section .quoted {
    padding: var(--sp-3);
    background: var(--surface);
    border-radius: var(--r-sm);
    font-style: italic;
    color: var(--ink-2);
}

.modal-section h4 {
    margin: 0 0 var(--sp-2);
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--ink-2);
    text-transform: uppercase;
    letter-spacing: .04em;
}

.modal-section.current-plan-section {
    background: var(--surface-2);
    border-color: var(--line);
}

.modal-section.new-plan-section {
    background: var(--accent-soft);
    border-color: var(--accent-soft);
}

/* Tinted by the state of the thing being shown — a completed payment, one
   still pending, one that failed. The soft step is the fill and the ink step
   is the text; admin-payments used to pair `background: var(--danger)` with
   `color: var(--ink)`, which is near-black on dark red. */
.modal-section-ok     { background: var(--ok-soft);     border-color: var(--ok-soft); }
.modal-section-warn   { background: var(--warn-soft);   border-color: var(--warn-soft); }
.modal-section-danger { background: var(--danger-soft); border-color: var(--danger-soft); }

.modal-section-ok h4     { color: var(--ok-ink); }
.modal-section-warn h4   { color: var(--warn-ink); }
.modal-section-danger h4 { color: var(--danger-ink); }

/* The money the customer gets back out of a switch. */
.credit-highlight {
    color: var(--ok-ink);
    font-weight: 600;
}

.modal-info-row,
.payment-info-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--sp-4);
    padding: var(--sp-2) 0;
    border-bottom: 1px solid var(--line-2);
    font-size: var(--fs-sm);
}

.modal-info-row:last-child,
.payment-info-row:last-child { border-bottom: 0; }

/* These rows are built as two bare <span>s on several pages, so the
   label/value distinction has to come from position. */
.modal-info-row > span:first-child,
.payment-info-row > span:first-child { color: var(--ink-2); font-weight: 500; }

.modal-info-row > span:last-child,
.payment-info-row > span:last-child { color: var(--ink); text-align: right; overflow-wrap: anywhere; }

/* ============================================================
   Checkout and subscription payment forms — checkout.html and
   create-subscription.html carried near-identical copies.
   ============================================================ */
.checkout-container,
.subscription-container,
.pricing-container {
    max-width: 960px;
    margin: 0 auto;
    padding: var(--sp-10) var(--page-pad);
}

.checkout-header,
.subscription-header,
.pricing-header {
    text-align: center;
    margin-bottom: var(--sp-10);
}

.checkout-header h1,
.subscription-header h1,
.pricing-header h1 {
    font-size: var(--fs-2xl);
    font-weight: 800;
    letter-spacing: -0.03em;
}

.checkout-header p,
.subscription-header p,
.pricing-header p {
    color: var(--ink-2);
    margin-top: var(--sp-2);
}

.checkout-content,
.subscription-content {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-8);
    align-items: start;
}

.order-summary {
    background: var(--surface-2);
    padding: var(--sp-6);
    border-radius: var(--r-lg);
    border: 1px solid var(--line);
}

.order-summary h3 {
    margin-top: 0;
    margin-bottom: var(--sp-4);
    color: var(--ink);
}

.order-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--sp-4);
    padding: var(--sp-3) 0;
    border-bottom: 1px solid var(--line-2);
    font-size: var(--fs-sm);
}

/* The figure the customer is agreeing to. .order-item:last-child emphasises
   the total of a summary that ends with it, but create-subscription puts a
   billing-cycle note after its total, so that row was never the last child and
   the amount looked like every other line. .total says it by name. */
.order-item.total {
    margin-top: var(--sp-1);
    padding-top: var(--sp-3);
    border-top: 1px solid var(--line);
    font-size: var(--fs-md);
    font-weight: 700;
    color: var(--ink);
}

.order-item:last-child {
    border-bottom: none;
    font-weight: 700;
    font-size: var(--fs-md);
    color: var(--accent);
}

/* Stripe Elements mount points. Stripe draws its own field inside an iframe,
   so the box around it has to be matched to the site's inputs by hand -
   checkout.html did that locally and reached for
   `box-shadow: 0 0 0 2px rgba(0, 123, 255, .25)`, which is the old Bootstrap
   blue rather than the accent, on a page that sits next to real inputs using
   --ring. */
.stripe-field {
    padding: var(--sp-3);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
}

.stripe-field.StripeElement--focus {
    border-color: var(--accent);
    box-shadow: var(--ring);
}

.stripe-field.StripeElement--invalid {
    border-color: var(--danger);
}

/* A tinted block inside the order summary: what is charged now, what will be
   charged later. These were built out of eleven style="" attributes each,
   down to a hand-written `border-radius: 0 0 6px 6px` and negative margins
   to bleed the total row to the block's edges. */
.summary-block {
    padding: var(--sp-4);
    margin-bottom: var(--sp-4);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
}

.summary-block h4 {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin: 0 0 var(--sp-2);
    font-size: var(--fs-sm);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* .order-item:last-child styles the grand total of a plain summary. Inside a
   block the total is marked with .total instead, so the positional rule is
   reset here. */
.summary-block .order-item {
    padding: var(--sp-2) 0;
    border-bottom: 0;
    font-size: var(--fs-sm);
    font-weight: 400;
    color: var(--ink-2);
}

.summary-block .order-item > span:last-child {
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

.summary-block .order-item.total {
    margin-top: var(--sp-1);
    padding-top: var(--sp-3);
    border-top: 1px solid var(--line);
    font-size: var(--fs-md);
    font-weight: 700;
    color: var(--ink);
}

.summary-block-ok      { background: var(--ok-soft);   border-color: var(--ok-soft); }
.summary-block-ok h4   { color: var(--ok-ink); }
.summary-block-ok .order-item.total,
.summary-block-ok .order-item.total > span:last-child { color: var(--ok-ink); }
.summary-block-ok .order-item.total { border-top-color: var(--ok); }

.summary-block-warn    { background: var(--warn-soft); border-color: var(--warn-soft); }
.summary-block-warn h4 { color: var(--warn-ink); }

/* The credit taken off today's charge, in a block summary and in a plain one. */
.summary-block .credit > span:last-child,
.credit-applied { color: var(--ok-ink); font-weight: 600; }

/* What the plan includes, under the summary. */
.summary-includes {
    margin-top: var(--sp-5);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--line-2);
}

.summary-includes h4 {
    margin: 0 0 var(--sp-3);
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--ink);
}

.payment-form {
    background: var(--surface);
    padding: var(--sp-6);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-md);
}

.payment-form h3 {
    margin-top: 0;
    margin-bottom: var(--sp-5);
    color: var(--ink);
}

#card-element {
    padding: var(--sp-3);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
}

#card-element.StripeElement--focus {
    border-color: var(--accent);
    box-shadow: var(--ring);
}

#card-errors {
    color: var(--danger);
    margin-top: var(--sp-2);
    font-size: var(--fs-sm);
}

.payment-options {
    margin-bottom: var(--sp-5);
    display: grid;
    gap: var(--sp-2);
}

.payment-option {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-4);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}

.payment-option:hover { background: var(--surface-2); }

.payment-option.selected {
    border-color: var(--accent);
    background: var(--accent-soft);
}

.security-info {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin-top: var(--sp-4);
    font-size: var(--fs-xs);
    color: var(--ink-3);
}

.billing-cycle-info {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-2);
    background: var(--accent-soft);
    color: var(--info-ink);
    border-radius: var(--r-sm);
    padding: var(--sp-3);
    font-size: var(--fs-sm);
    margin-bottom: var(--sp-4);
}

/* ============================================================
   Plans — pricing.html and admin-subscriptions.html.
   ============================================================ */
.pricing-grid,
.subscription-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
    gap: var(--sp-4);
    align-items: stretch;
}

.plan-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: var(--sp-6);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    box-shadow: var(--sh-xs);
    position: relative;
}

.plan-card.featured,
.plan-card.selected {
    border-color: var(--accent);
    box-shadow: var(--sh-md);
}

/* An event plan and a withdrawn one, as the admin plan list shows them.
   The local sheet reached for a linear-gradient on the event card; a border
   and a badge say the same thing without inventing a surface. */
.plan-card.event-plan { border-color: var(--warn); background: var(--warn-soft); }
.plan-card .plan-event-dates { color: var(--warn-ink); font-weight: 600; font-size: var(--fs-sm); }
.plan-card.inactive   { border-color: var(--danger); opacity: .7; }

/* Already subscribed: stated in the plan's own colour rather than by
   making the card bigger than its neighbours. */
.plan-card.current-plan {
    border-color: var(--ok);
    box-shadow: var(--sh-xs);
}

/* The badge used to be CSS `content` on .featured::before, hung outside the
   card at top: -10px - which is why the featured card had to be scaled up to
   make room for it. It is a real element in the header now, carrying whatever
   subscription_plans.marketing_badge says. */
.plan-header .plan-badge { justify-self: start; }

.plan-header { display: grid; gap: var(--sp-1); }

.plan-name,
.plan-title {
    font-size: var(--fs-md);
    font-weight: 600;
    letter-spacing: -0.015em;
    color: var(--ink);
}

.plan-price {
    font-size: var(--fs-3xl);
    font-weight: 800;
    letter-spacing: -0.04em;
    line-height: 1;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

.plan-period,
.plan-price small {
    font-size: var(--fs-sm);
    font-weight: 500;
    color: var(--ink-3);
    letter-spacing: 0;
}

.plan-description {
    color: var(--ink-2);
    font-size: var(--fs-sm);
}

.plan-features {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--sp-2);
    font-size: var(--fs-sm);
    color: var(--ink-2);
}

.plan-features li {
    position: relative;
    padding-left: var(--sp-5);
}

.plan-features li::before {
    content: "\2713";
    position: absolute;
    left: 0;
    top: 0;
    color: var(--ok);
    font-weight: 700;
}

.plan-price .currency {
    font-size: 0.6em;
    vertical-align: top;
}

.form-group.full-width {
    grid-column: 1 / -1;
}

.payment-option input[type="radio"] {
    width: auto;
    margin: 0;
    flex: none;
}

/* A checkbox with its label beside it — consent, opt-ins. Inputs are 100%
   wide by default, which turns a checkbox into a bar, so register.html was
   carrying the whole row as a style attribute. */
.check {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-3);
    margin: var(--sp-4) 0;
    font-size: var(--fs-sm);
    line-height: var(--lh-body);
    color: var(--ink-2);
    text-align: left;
}

.check input[type="checkbox"],
.check input[type="radio"] {
    width: auto;
    flex: none;
    margin: 2px 0 0;
    accent-color: var(--accent);
}

/* A row of choices, each its own .check. The label wraps the input, so the
   whole line is a hit target rather than just the 13px control. */
.check-group {
    display: grid;
    gap: var(--sp-2);
    margin: 0;
    padding: 0;
    border: 0;
}

.check-group > legend {
    padding: 0;
    margin-bottom: var(--sp-2);
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--ink-2);
}

.check-group .check { margin: 0; }
.check strong { color: var(--ink); font-weight: 600; }

/* The rule a field expects, said before the form is submitted rather than
   after. Sits directly under its input, inside the .form-group. */
.form-group .form-note {
    display: block;
    margin-top: var(--sp-1);
    color: var(--ink-3);
}

.plan-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    font-size: var(--fs-xs);
    color: var(--ink-2);
}

.plan-meta-item {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    background: var(--surface-2);
    border: 1px solid var(--line-2);
    border-radius: var(--r-pill);
    padding: 3px var(--sp-2);
    white-space: nowrap;
}

.pricing-note {
    margin-top: var(--sp-10);
    text-align: center;
    color: var(--ink-2);
    font-size: var(--fs-sm);
    line-height: var(--lh-body);
}

.plan-actions {
    display: flex;
    gap: var(--sp-2);
    flex-wrap: wrap;
    margin-top: auto;
}

.plan-badge,
.subscription-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    background: var(--accent);
    color: var(--accent-ink);
    font-size: var(--fs-xs);
    font-weight: 600;
    padding: 3px var(--sp-2);
    border-radius: var(--r-pill);
    white-space: nowrap;
}

/* A badge trailing a value in a summary row needs air. .plan-header places
   its badge with justify-self instead, so this is scoped to the row. */
.order-item .subscription-badge { margin-left: var(--sp-2); }

.plan-badge.event    { background: var(--warn);   color: var(--on-fill); }
.plan-badge.inactive { background: var(--danger); color: var(--on-fill); }

/* Not signed in yet: a different call to action from "choose this plan". */
.plan-button.signup-required        { background-color: var(--info); color: var(--accent-ink); }
.plan-button.signup-required:hover  { background-color: var(--info-ink); }

/* ============================================================
   Success pages — payment-success.html and
   subscription-success.html. One centred column; the cards in it
   are left-aligned, and every block spaces itself from the next
   with margin-bottom only. .next-steps used to take margin-top
   instead, so nothing separated it from the buttons below.
   ============================================================ */
.success-container {
    max-width: 640px;
    margin: 0 auto;
    padding: var(--sp-10) var(--page-pad);
    text-align: center;
}

.success-icon {
    width: 64px;
    height: 64px;
    margin: 0 auto var(--sp-5);
    border-radius: 50%;
    background: var(--ok-soft);
    color: var(--ok);
    display: grid;
    place-items: center;
}

/* Strokes with currentColor, so the mark is --ok. It was an emoji on
   one page, which no colour reaches, and a text glyph on the other. */
.success-icon svg {
    width: 30px;
    height: 30px;
}

.success-title {
    font-size: var(--fs-2xl);
    font-weight: 800;
    letter-spacing: -0.03em;
    margin-bottom: var(--sp-3);
}

.success-message {
    color: var(--ink-2);
    margin-bottom: var(--sp-6);
}

.success-details,
.payment-summary-section {
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: var(--sp-5);
    text-align: left;
    margin-bottom: var(--sp-5);
}

.next-steps {
    text-align: left;
    background: var(--accent-soft);
    border-radius: var(--r-md);
    padding: var(--sp-5);
    margin-bottom: var(--sp-6);
}

.success-details h2,
.next-steps h2 {
    font-size: var(--fs-base);
    margin-bottom: var(--sp-2);
}

.next-steps ul {
    margin-left: var(--sp-5);
    color: var(--ink-2);
    font-size: var(--fs-sm);
    line-height: var(--lh-body);
}

.next-steps li + li { margin-top: var(--sp-2); }

/* ============================================================
   Charts — qr-info.html and stats.html.
   ============================================================ */
.chart-container,
.country-chart-container {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: var(--sp-4);
    margin-bottom: var(--sp-5);
    min-height: 240px;
    overflow: hidden;
}

.chart-container canvas,
.country-chart-container canvas {
    max-width: 100%;
}

/* ============================================================
   Feedback — .loading was on four pages, .error on three.
   ============================================================ */
.loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    padding: var(--sp-10);
    color: var(--ink-3);
    font-size: var(--fs-sm);
}

.error:empty { display: none; }

.error {
    background: var(--danger-soft);
    color: var(--danger-ink);
    border: 1px solid var(--danger);
    border-radius: var(--r-sm);
    padding: var(--sp-3) var(--sp-4);
    font-size: var(--fs-sm);
    margin: var(--sp-3) 0;
}

.spinner {
    width: 18px;
    height: 18px;
    border: 2px solid var(--line);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin .7s linear infinite;
    flex: none;
}

@keyframes spin { to { transform: rotate(360deg); } }

.empty-icon {
    font-size: 34px;
    color: var(--ink-3);
    margin-bottom: var(--sp-2);
}

.note {
    background: var(--warn-soft);
    border: 1px solid var(--warn);
    color: var(--warn-ink);
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-md);
    font-size: var(--fs-sm);
}

/* ============================================================
   Legal documents — privacy, refund and terms carried three
   identical copies of this.
   ============================================================ */
.legal {
    max-width: 68ch;
    margin: 0 auto;
    padding: var(--sp-10) var(--page-pad);
    color: var(--ink);
    line-height: var(--lh-body);
}

.legal h1 {
    font-size: var(--fs-2xl);
    font-weight: 800;
    letter-spacing: -0.03em;
    text-align: left;
    margin-bottom: var(--sp-2);
}

.legal h2 {
    font-size: var(--fs-lg);
    font-weight: 700;
    margin-top: var(--sp-8);
    margin-bottom: var(--sp-2);
}

.legal p,
.legal li { color: var(--ink-2); }
.legal p { margin-bottom: var(--sp-3); }
.legal ul { margin-bottom: var(--sp-3); }

.legal .updated {
    color: var(--ink-3);
    font-size: var(--fs-sm);
    margin-bottom: var(--sp-6);
}

.legal .note { margin-bottom: var(--sp-6); }

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 860px) {
    .checkout-content,
    .subscription-content {
        grid-template-columns: 1fr;
        gap: var(--sp-5);
    }

    .checkout-container,
    .subscription-container,
    .pricing-container,
    .success-container {
        padding: var(--sp-6) var(--page-pad);
    }

    .checkout-header,
    .subscription-header,
    .pricing-header {
        margin-bottom: var(--sp-6);
    }

    .order-summary,
    .payment-form {
        padding: var(--sp-5);
    }

    .modal-content {
        width: calc(100% - var(--sp-4));
        margin: var(--sp-3) auto;
        max-height: 90vh;
    }

    .success-actions > *,
    .action-buttons > * { width: 100%; }
}

@media (max-width: 560px) {
    .plan-card { padding: var(--sp-5); }
    .legal { padding: var(--sp-6) var(--page-pad); }
}

/* ============================================================
   Page shell
   ------------------------------------------------------------
   A full-width page on the tinted ground, as opposed to the
   white .container card. The dashboard uses it: wrapping a grid
   of cards inside another card gave two nested borders and cost
   a whole gutter of width on a phone.
   ============================================================ */
.page {
    max-width: var(--page-max);
    margin: 0 auto;
    padding: var(--sp-8) var(--page-pad) var(--sp-14);
    display: grid;
    gap: var(--sp-6);
    align-content: start;
}

/* A page that is mostly reading wants a measure, not the full 1200px:
   past roughly 75 characters the eye loses the start of the next line. */
.page-narrow {
    max-width: 820px;
}

.pagehead {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--sp-4);
    flex-wrap: wrap;
}

.pagehead h1 {
    /* The global h1 is centred; a page heading is not. */
    text-align: left;
    font-size: var(--fs-xl);
    font-weight: 700;
    letter-spacing: -0.025em;
    margin-bottom: 0;
}

.pagehead .sub {
    color: var(--ink-2);
    font-size: var(--fs-sm);
    margin: var(--sp-1) 0 0;
}

.headtools {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    flex-wrap: wrap;
}

/* Plan usage: the bar carries "how full am I" at a glance, the
   number carries the detail. */
.meter {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    padding: var(--sp-2) var(--sp-3);
    font-size: var(--fs-sm);
    background: var(--surface);
    color: var(--ink-2);
    white-space: nowrap;
}

.meter-bar {
    width: 58px;
    height: 5px;
    border-radius: var(--r-pill);
    background: var(--surface-3);
    overflow: hidden;
    flex: none;
}

.meter-bar i {
    display: block;
    height: 100%;
    background: var(--accent);
    border-radius: var(--r-pill);
    transition: width .3s ease;
}

.meter.full { border-color: var(--danger); color: var(--danger-ink); }
.meter.full .meter-bar i { background: var(--danger); }

/* ---------- toolbar ---------- */
.toolbar {
    display: flex;
    gap: var(--sp-2);
    align-items: center;
    flex-wrap: wrap;
}

.search {
    flex: 1 1 220px;
    min-width: 0;
    max-width: 340px;
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: var(--sp-2) var(--sp-3);
    background: var(--surface);
    color: var(--ink-3);
}

.search:focus-within {
    border-color: var(--accent);
    box-shadow: var(--ring);
}

.search svg { width: 15px; height: 15px; flex: none; }

.search input {
    border: 0;
    padding: 0;
    background: none;
    font-size: var(--fs-sm);
    color: var(--ink);
    width: 100%;
    min-width: 0;
}

.search input:focus { outline: none; box-shadow: none; }

.chips {
    display: flex;
    gap: var(--sp-1);
    flex-wrap: wrap;
}

.chip-f {
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    padding: var(--sp-2) var(--sp-3);
    font-family: inherit;
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--ink-2);
    background: var(--surface);
    cursor: pointer;
    min-width: 0;
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
}

.chip-f:hover { background: var(--surface-2); color: var(--ink); }

.chip-f.on {
    background: var(--accent);
    color: var(--accent-ink);
    border-color: var(--accent);
}

.chip-f .n {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    opacity: .7;
    font-weight: 500;
}

/* ============================================================
   QR tiles
   ------------------------------------------------------------
   Thumbnail and identity on one line, the scan trend under it,
   actions in a footer bar. The old tile put a 290px QR image on
   top of a centred title, so a screen of codes showed six big
   pictures of squares and almost no information.
   ============================================================ */
.qr-grid {
    display: grid;
    /* Wide enough for a 150px preview plus a readable title beside it. */
    grid-template-columns: repeat(auto-fill, minmax(min(360px, 100%), 1fr));
    gap: var(--sp-4);
    /* Stretch, not start: every tile in a row shares the row's height, so the
       "create" tile is the same size as the codes next to it. */
    align-items: stretch;
}

.qr-tile {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: var(--sh-xs);
    overflow: hidden;
    display: grid;
    grid-template-rows: auto 1fr auto;
    transition: border-color .15s ease, box-shadow .15s ease;
}

.qr-tile:hover {
    border-color: var(--ink-3);
    box-shadow: var(--sh-md);
}

/* The identity area is one big button so the whole card is clickable
   and reachable by keyboard, without nesting controls. */
.qr-tile-open {
    display: flex;
    gap: var(--sp-3);
    align-items: flex-start;
    text-align: left;
    padding: var(--sp-4) var(--sp-4) var(--sp-3);
    background: none;
    border: 0;
    border-radius: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    width: 100%;
    min-width: 0;
}

.qr-tile-open:hover { background: var(--surface-2); }

.qr-tile .qrbox {
    flex: none;
    width: 150px;
    height: 150px;
    border-radius: var(--r-sm);
    background: var(--surface);
    border: 1px solid var(--line-2);
    display: grid;
    place-items: center;
    padding: var(--sp-2);
}

.qr-tile .qrbox img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

.qr-tile .meta {
    min-width: 0;
    display: grid;
    gap: 3px;
}

.qr-tile .qr-title {
    font-size: var(--fs-base);
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.3;
    color: var(--ink);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.qr-tile .dest {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    color: var(--ink-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.qr-tile .state {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
    font-size: var(--fs-xs);
    color: var(--ink-2);
    margin-top: 2px;
}

.qr-tile .state .dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--ok);
    flex: none;
}

.qr-tile .state.direct .dot { background: var(--ink-3); }

/* ---------- scan trend ---------- */
.qr-tile .spark {
    padding: 0 var(--sp-4) var(--sp-3);
    display: flex;
    align-items: flex-end;
    gap: var(--sp-3);
}

.qr-tile .spark > svg {
    flex: 1 1 auto;
    width: auto;
    height: 30px;
    min-width: 0;
}

.qr-tile .spark .n { text-align: right; line-height: 1.15; }

.qr-tile .spark .n b {
    font-size: var(--fs-lg);
    font-weight: 700;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    display: block;
    color: var(--ink);
}

.qr-tile .spark .n span {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.qr-tile .spark.untracked {
    font-size: var(--fs-xs);
    color: var(--ink-3);
    align-items: center;
    min-height: 30px;
}

.spark-area { fill: color-mix(in srgb, var(--accent) 12%, transparent); stroke: none; }
.spark-line { fill: none; stroke: var(--accent); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
.spark-dot  { fill: var(--accent); stroke: none; }

/* ---------- footer actions ---------- */
.qr-tile .acts {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-4);
    border-top: 1px solid var(--line-2);
    background: var(--surface-2);
}

.qr-tile .acts .id {
    font-size: var(--fs-xs);
    color: var(--ink-3);
}

.qr-tile .acts .ib { display: flex; gap: 2px; }

.iconbtn {
    width: 30px;
    height: 30px;
    min-width: 0;
    padding: 0;
    border-radius: var(--r-sm);
    display: inline-grid;
    place-items: center;
    color: var(--ink-2);
    background: none;
    border: 1px solid transparent;
    cursor: pointer;
}

.iconbtn svg { width: 15px; height: 15px; }
.iconbtn:hover { background: var(--surface-3); color: var(--ink); }
.iconbtn.danger:hover { background: var(--danger-soft); color: var(--danger); }

/* ---------- add / empty ---------- */
.qr-tile.add {
    /* One centred stack rather than the three-row tile grid, but the same
       box: the grid's stretch alignment gives it the row's height. */
    display: grid;
    grid-template-rows: none;
    place-items: center;
    align-content: center;
    gap: var(--sp-2);
    padding: var(--sp-6) var(--sp-5);
    border-style: dashed;
    background: none;
    box-shadow: none;
    color: var(--ink-3);
    text-align: center;
    text-decoration: none;
}

.qr-tile.add:hover { border-color: var(--accent); color: var(--accent); box-shadow: none; }

.qr-tile.add .plus {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    background: var(--accent-soft);
    color: var(--accent);
    display: grid;
    place-items: center;
    font-size: 21px;
    line-height: 1;
}

.qr-tile.add strong { font-size: var(--fs-sm); color: var(--ink-2); font-weight: 600; }
.qr-tile.add span { font-size: var(--fs-xs); }

.qr-blank {
    grid-column: 1 / -1;
    text-align: center;
    padding: var(--sp-14) var(--sp-5);
    border: 1px dashed var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
    display: grid;
    justify-items: center;
    gap: var(--sp-3);
}

.qr-blank h2 { font-size: var(--fs-lg); }
.qr-blank p { color: var(--ink-2); font-size: var(--fs-sm); max-width: 44ch; }

.qr-empty {
    text-align: center;
    color: var(--ink-3);
    font-size: var(--fs-sm);
    padding: var(--sp-8) 0;
}

@media (max-width: 640px) {
    .page { padding: var(--sp-5) var(--page-pad) var(--sp-10); gap: var(--sp-4); }
    .pagehead { align-items: flex-start; }
    .headtools { width: 100%; justify-content: space-between; }
    .search { max-width: none; }
    .qr-grid { gap: var(--sp-3); }
}

/* ============================================================
   Status pills
   ------------------------------------------------------------
   State as shape and colour, not just a coloured word, so what
   needs attention reads at a glance. Semantic colours only —
   these never borrow the accent.
   ============================================================ */
.pill {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    padding: 3px var(--sp-2);
    border-radius: var(--r-pill);
    font-size: var(--fs-xs);
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
    border: 1px solid transparent;
}

.pill::before {
    content: "";
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

.pill-ok     { color: var(--ok-ink);     background: var(--ok-soft); }
.pill-warn   { color: var(--warn-ink);   background: var(--warn-soft); }
.pill-danger { color: var(--danger-ink); background: var(--danger-soft); }
.pill-accent { color: var(--accent-deep); background: var(--accent-soft); }
.pill-muted  { color: var(--ink-2);      background: var(--surface-3); }

/* ============================================================
   News
   ------------------------------------------------------------
   A dated feed. The date takes its own column on a wide screen
   so the timeline reads straight down the left edge, and moves
   above the entry on a narrow one rather than squeezing the
   text into half the width.
   ============================================================ */
.news {
    display: grid;
    gap: var(--sp-4);
}

.news-item {
    display: grid;
    /* minmax(0, 1fr), not the default minmax(auto, 1fr): a grid item's
       automatic minimum is its content's min-content width, so one long
       URL in an entry would otherwise widen the track and push the page. */
    /* 100px: the date is the widest thing in this column at 82px, and a
       wider track just opens a gap between the date and the headline. */
    grid-template-columns: 100px minmax(0, 1fr);
    gap: var(--sp-6);
    padding: var(--sp-6);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-xs);
}

.news-when {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-2);
}

/* Mono, like every other timestamp on the site. */
.news-date {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    color: var(--ink-3);
    letter-spacing: .02em;
    white-space: nowrap;
}

.news-item h2 {
    margin: 0 0 var(--sp-2);
    font-size: var(--fs-md);
}

.news-item p {
    color: var(--ink-2);
    line-height: var(--lh-body);
}

.news-item ul {
    margin: var(--sp-3) 0 0;
    padding-left: var(--sp-5);
    color: var(--ink-2);
    line-height: var(--lh-body);
}

.news-item li + li {
    margin-top: var(--sp-1);
}

.news-item > * {
    min-width: 0;
}

@media (max-width: 680px) {
    .news-item {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--sp-3);
        padding: var(--sp-5);
    }

    /* Date and tag share one line once they are above the entry. */
    .news-when {
        flex-direction: row;
        align-items: center;
        gap: var(--sp-3);
    }
}

/* ============================================================
   Content pages
   ------------------------------------------------------------
   The pieces a page of explanation needs: a plain section card,
   a two-up that collapses, numbered steps, and a pointer box.
   Nothing here knows about the tutorial specifically.
   ============================================================ */
.panel {
    padding: var(--sp-6);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-xs);
}

.panel > :first-child { margin-top: 0; }
.panel > :last-child { margin-bottom: 0; }

.panel h2 {
    font-size: var(--fs-md);
    margin: 0 0 var(--sp-3);
}

/* Sub-headings inside a panel: the label for the chart or table beneath. */
.panel h3 {
    margin: var(--sp-6) 0 var(--sp-3);
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--ink-2);
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* ------------------------------------------------------------
   Stat tiles — a number worth reading on its own.
   ------------------------------------------------------------ */
.stat-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
    gap: var(--sp-4);
}

/* Four tiles should be 4-up or 2x2, never three and an orphan, so the
   columns are stated rather than left to auto-fit to the container. */
.stat-row-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

@media (max-width: 900px) {
    .stat-row-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 460px) {
    .stat-row-4 { grid-template-columns: minmax(0, 1fr); }
}

.stat-tile {
    padding: var(--sp-4) var(--sp-5);
    background: var(--surface-2);
    border: 1px solid var(--line-2);
    border-radius: var(--r-md);
}

.stat-tile .k {
    display: block;
    font-size: var(--fs-sm);
    color: var(--ink-2);
}

/* Proportional figures, deliberately not tabular-nums: tabular gives every
   digit the width of a zero, which reads loose at display sizes. Columns of
   numbers that must line up vertically still use tabular. */
.stat-tile .v {
    display: block;
    margin-top: var(--sp-1);
    font-size: var(--fs-2xl);
    font-weight: 600;
    line-height: var(--lh-tight);
    letter-spacing: -0.02em;
    color: var(--ink);
}

/* ------------------------------------------------------------
   The rendered code, centred under its details.
   ------------------------------------------------------------ */
.qr-figure {
    display: grid;
    justify-items: center;
    gap: var(--sp-4);
    margin-top: var(--sp-6);
    padding-top: var(--sp-6);
    border-top: 1px solid var(--line-2);
}

.qr-figure img {
    width: 100%;
    max-width: 240px;
    height: auto;
    padding: var(--sp-4);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
}

.panel p,
.panel li {
    color: var(--ink-2);
    line-height: var(--lh-body);
}

/* Two side by side, one under the other once there is no room.
   min(260px, 100%) rather than a bare 260px: a fixed minimum cannot
   shrink below itself and overflows a narrow phone. */
.duo {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
    gap: var(--sp-4);
    align-items: stretch;
}

/* Numbered steps. The number is a counter rather than markup, so
   inserting a step in the middle cannot leave the list mis-numbered. */
.steps {
    display: grid;
    gap: var(--sp-3);
    counter-reset: step;
    padding: 0;
    margin: 0;
    list-style: none;
}

.step {
    display: grid;
    grid-template-columns: 30px minmax(0, 1fr);
    gap: var(--sp-4);
    align-items: start;
    padding: var(--sp-5) var(--sp-6);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-xs);
}

.step::before {
    counter-increment: step;
    content: counter(step);
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--accent-soft);
    color: var(--accent-deep);
    font-family: var(--font-mono);
    font-size: var(--fs-sm);
    font-weight: 600;
}

.step h3 {
    margin: var(--sp-1) 0 var(--sp-2);
    font-size: var(--fs-base);
}

.step p,
.step li {
    color: var(--ink-2);
    line-height: var(--lh-body);
    margin-bottom: 0;
}

.step > * { min-width: 0; }

/* A pointer to somewhere else, tinted so it does not read as body text. */
.callout {
    padding: var(--sp-4) var(--sp-5);
    background: var(--accent-soft);
    border: 1px solid var(--accent-soft);
    border-left: 3px solid var(--accent);
    border-radius: var(--r-md);
    color: var(--ink-2);
    font-size: var(--fs-sm);
    line-height: var(--lh-body);
}

.callout > :last-child { margin-bottom: 0; }

@media (max-width: 560px) {
    .panel { padding: var(--sp-5); }

    .step {
        padding: var(--sp-4) var(--sp-5);
        gap: var(--sp-3);
    }
}

/* ============================================================
   Landing page — index.html
   ------------------------------------------------------------
   Built from the shared parts where one exists: .btn, .panel,
   the .plan-card family from pricing and the counter-numbered
   .steps from the tutorial. It used to declare a private .lp-*
   system in a <style> block - its own buttons, cards, plan cards
   and a step number typed into the markup under a counter-reset
   nothing read - and its headline was centred by the global h1
   while the paragraph and buttons beside it were left-aligned.
   ============================================================ */
.landing {
    max-width: var(--page-max);
    margin: 0 auto;
    padding: 0 var(--page-pad) var(--sp-10);
}

.landing-section { padding-top: var(--sp-14); }

.section-head {
    text-align: center;
    margin-bottom: var(--sp-8);
}

.section-head h2,
.landing-cta h2 {
    font-size: var(--fs-2xl);
    font-weight: 700;
    letter-spacing: -0.025em;
}

.section-head p,
.landing-cta p {
    margin: var(--sp-2) 0 0;
    color: var(--ink-2);
}

.hero {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-10);
    align-items: center;
    padding-top: var(--sp-14);
}

.hero h1 {
    text-align: left;
    font-size: var(--fs-3xl);
    font-weight: 800;
    line-height: 1.1;
    letter-spacing: -0.035em;
    margin-bottom: var(--sp-4);
}

.hero-sub {
    font-size: var(--fs-md);
    color: var(--ink-2);
    line-height: var(--lh-body);
}

.hero-actions {
    display: flex;
    gap: var(--sp-3);
    flex-wrap: wrap;
    margin-top: var(--sp-6);
}

/* The live demo: a panel lifted off the ground, because it is the one
   thing on the page that does something. */
.demo { box-shadow: var(--sh-md); }

.demo-row {
    display: flex;
    gap: var(--sp-2);
    flex-wrap: wrap;
}

.demo-row input {
    flex: 1 1 180px;
    width: auto;
    min-width: 0;
}

.demo-colors {
    display: flex;
    gap: var(--sp-4);
    flex-wrap: wrap;
    margin-top: var(--sp-3);
    font-size: var(--fs-sm);
    color: var(--ink-2);
}

.demo-colors label {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}

.demo-out {
    display: grid;
    place-items: center;
    min-height: 216px;
    margin: var(--sp-4) 0 var(--sp-2);
}

.demo-out img {
    width: 200px;
    height: 200px;
}

/* Six cards: three across, then two, then one - never four and two. */
.feature-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--sp-4);
}

/* .panel h3 is the small uppercase label above a chart; here the h3 is
   the card's title. */
.panel.feature h3 {
    margin: var(--sp-3) 0 var(--sp-1);
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--ink);
    text-transform: none;
    letter-spacing: -0.015em;
}

.panel.feature p { font-size: var(--fs-sm); }

.feature-ico {
    width: 38px;
    height: 38px;
    display: grid;
    place-items: center;
    border-radius: var(--r-sm);
    background: var(--accent-soft);
    color: var(--accent);
}

.feature-ico svg {
    width: 19px;
    height: 19px;
}

/* The tutorial's steps, side by side. */
.steps-row {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--sp-4);
}

.landing-more {
    text-align: center;
    margin-top: var(--sp-6);
}

.faq {
    display: grid;
    gap: var(--sp-3);
    max-width: 820px;
    margin: 0 auto;
}

.faq details {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: var(--sp-4) var(--sp-5);
}

.faq summary {
    font-weight: 600;
    cursor: pointer;
}

.faq details p {
    margin: var(--sp-3) 0 0;
    color: var(--ink-2);
    font-size: var(--fs-sm);
    line-height: var(--lh-body);
}

.landing-cta { text-align: center; }
.landing-cta p { margin-bottom: var(--sp-6); }

.site-footer {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--sp-5);
    margin-top: var(--sp-14);
    padding-top: var(--sp-8);
    border-top: 1px solid var(--line);
    font-size: var(--fs-sm);
}

.site-footer p {
    margin: var(--sp-1) 0 0;
    color: var(--ink-2);
}

.site-footer nav {
    display: flex;
    flex-wrap: wrap;
    align-content: start;
    gap: var(--sp-2) var(--sp-4);
}

.site-footer a {
    color: var(--ink-2);
    text-decoration: none;
}

.site-footer a:hover { color: var(--accent); }

@media (max-width: 900px) {
    .hero {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--sp-8);
        padding-top: var(--sp-10);
    }

    .feature-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .steps-row { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 560px) {
    .feature-grid { grid-template-columns: minmax(0, 1fr); }
    .landing-section { padding-top: var(--sp-10); }
}

/* ============================================================
   Settings
   ------------------------------------------------------------
   One card per concern rather than one long card divided by
   bare rules, so each section has its own heading, its own
   actions and its own edge.
   ============================================================ */
.settings-grid {
    display: grid;
    gap: var(--sp-5);
    align-content: start;
}

.settings-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-xs);
    overflow: hidden;
}

.settings-card > header,
.settings-card-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--sp-4);
    flex-wrap: wrap;
    padding: var(--sp-5) var(--sp-6);
    border-bottom: 1px solid var(--line-2);
    /* This sits inside a card, not at the top of the page: the site header
       rules must not apply. */
    position: static;
    height: auto;
    background: none;
    z-index: auto;
}

.settings-card h2 {
    font-size: var(--fs-md);
    font-weight: 600;
    letter-spacing: -0.015em;
    text-align: left;
    margin: 0;
}

.settings-card .hint {
    color: var(--ink-2);
    font-size: var(--fs-sm);
    margin: var(--sp-1) 0 0;
    max-width: 62ch;
}

.settings-card-body {
    padding: var(--sp-5) var(--sp-6);
    display: grid;
    gap: var(--sp-4);
}

/* A password box stretched to 995px on a wide screen. Inputs get a
   readable measure; lists and fact rows still use the full width. */
.settings-card .form-group input,
.settings-card .form-group select,
.settings-card .form-group textarea,
.settings-card #billing-address-element {
    max-width: 460px;
}

.settings-card-foot {
    display: flex;
    gap: var(--sp-2);
    flex-wrap: wrap;
    padding: var(--sp-4) var(--sp-6);
    border-top: 1px solid var(--line-2);
    background: var(--surface-2);
}

/* Label/value facts, as a <dl> of .fact rows: the subscription summary
   in account settings and on the success page, and qr-info's details. */
.fact-list {
    display: grid;
    gap: 0;
}

.fact {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: var(--sp-3) 0;
    border-bottom: 1px solid var(--line-2);
    font-size: var(--fs-sm);
}

.fact:last-child { border-bottom: 0; }
.fact > dt,
.fact > .k { color: var(--ink-2); }
.fact > dd,
.fact > .v {
    margin: 0;
    color: var(--ink);
    font-weight: 600;
    text-align: right;
    overflow-wrap: anywhere;
}

.fact .v.mono { font-family: var(--font-mono); font-weight: 500; }

/* The share that goes with a count — present, but not competing with it. */
.fact .pct {
    margin-left: var(--sp-2);
    color: var(--ink-3);
    font-family: var(--font-sans);
    font-weight: 500;
}

/* A column heading inside a card, above a fact list. */
.fact-head {
    margin: 0 0 var(--sp-2);
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--ink);
}

/* ---------- invoices ---------- */
.invoice-list {
    display: grid;
    gap: var(--sp-3);
}

.invoice {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--sp-4);
    flex-wrap: wrap;
    padding: var(--sp-4);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface);
}

.invoice:hover { border-color: var(--ink-3); }

.invoice-main { min-width: 0; display: grid; gap: var(--sp-1); flex: 1 1 260px; }

.invoice-top {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex-wrap: wrap;
}

.invoice-top h3 {
    font-size: var(--fs-sm);
    font-weight: 600;
    margin: 0;
    color: var(--ink);
}

.invoice-meta {
    display: flex;
    gap: var(--sp-4);
    flex-wrap: wrap;
    font-size: var(--fs-xs);
    color: var(--ink-2);
}

.invoice-meta .amount {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-weight: 500;
    color: var(--ink);
}

.invoice-desc { font-size: var(--fs-xs); color: var(--ink-3); }

.invoice-links {
    display: flex;
    gap: var(--sp-2);
    flex-wrap: wrap;
    align-items: center;
}

.invoice-links a {
    font-size: var(--fs-xs);
    font-weight: 600;
    text-decoration: none;
    color: var(--accent);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: var(--sp-1) var(--sp-2);
    white-space: nowrap;
}

.invoice-links a:hover { background: var(--accent-soft); border-color: var(--accent); }

/* ---------- inline form feedback ---------- */
.form-note { font-size: var(--fs-sm); }
.form-note.ok    { color: var(--ok-ink); }
.form-note.bad   { color: var(--danger); }
.field-error {
    display: block;
    font-size: var(--fs-xs);
    color: var(--danger);
    margin-top: var(--sp-1);
}

.state-line {
    color: var(--ink-3);
    font-size: var(--fs-sm);
    padding: var(--sp-4) 0;
    text-align: center;
}

@media (max-width: 640px) {
    .settings-card > header,
    .settings-card-header,
    .settings-card-body,
    .settings-card-foot {
        padding-left: var(--sp-4);
        padding-right: var(--sp-4);
    }

    .settings-card-foot > .btn { width: 100%; }
    .invoice-links { width: 100%; }
    .invoice-links a { flex: 1; text-align: center; }
}
