/*
 * Storefront stylesheet. One file, four palette presets, each with a light
 * and a dark variant. The tenant picks the preset (html[data-theme]); the
 * visitor's system preference picks the mode unless they toggle it
 * (html[data-mode]). Mobile first, no webfonts, no framework.
 */

/* ---------- palettes: l- (light) and d- (dark) pairs per preset ---------- */

/*
 * noir reproduces the supply panel's Creon look: navy #0f1624 / #131c2e,
 * gold #f0b90b with #e0a800 hover and #1a1300 ink, text #f1f5f9 / #c0cad8,
 * Plus Jakarta Sans, 20px cards with a soft drop shadow, gold-glow 12px
 * buttons, dense 14px tables. The light variant keeps the same gold on a
 * navy-tinted off-white. The other three presets are original.
 */
[data-theme="noir"] {
    --l-bg: #f4f6fb; --l-surface: #ffffff; --l-surface-2: #e9edf5; --l-text: #0f1624; --l-muted: #4b5568;
    --l-border: #d7dce8; --l-primary: #8d6200; --l-primary-hover: #7d5700; --l-primary-contrast: #ffffff; --l-accent: #131c2e;
    --d-bg: #0f1624; --d-surface: #131c2e; --d-surface-2: #1a2438; --d-text: #f1f5f9; --d-muted: #c0cad8;
    --d-border: #1f2a40; --d-primary: #f0b90b; --d-primary-hover: #e0a800; --d-primary-contrast: #1a1300; --d-accent: #ffd45e;
    --l-field: #f4f6fb; --l-field-border: #79839a; --l-placeholder: #5b6577;
    --d-field: #0b1220; --d-field-border: #6d7a96; --d-placeholder: #98a6bd;
    --preset-font: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --preset-radius: 20px; --preset-radius-sm: 14px; --preset-button-radius: 12px;
    --preset-shadow: 0 4px 15px -2px rgba(0, 0, 0, .25); --preset-button-shadow: 0 3px 12px -3px #d8a333;
    --preset-table-pad: .45rem .6rem; --preset-table-size: .9rem; --preset-heading-weight: 800;
}

[data-theme="ivory"] {
    --l-bg: #fafafa; --l-surface: #ffffff; --l-surface-2: #f1f3f7; --l-text: #111827; --l-muted: #556070;
    --l-border: #e2e6ee; --l-primary: #4338ca; --l-primary-contrast: #ffffff; --l-accent: #1e293b;
    --d-bg: #0b1220; --d-surface: #111a2e; --d-surface-2: #182339; --d-text: #e6eaf2; --d-muted: #a3adc2;
    --d-border: #263353; --d-primary: #818cf8; --d-primary-contrast: #0b1220; --d-accent: #c7d2fe;
    --l-field: #f4f5fa; --l-field-border: #787f92; --l-placeholder: #5c6678;
    --d-field: #0a1020; --d-field-border: #6c7899; --d-placeholder: #9aa5bd;
}

[data-theme="savanna"] {
    --l-bg: #fbf6ee; --l-surface: #ffffff; --l-surface-2: #f3e9d8; --l-text: #2b1d12; --l-muted: #6b5644;
    --l-border: #e3d3bb; --l-primary: #a94e08; --l-primary-contrast: #ffffff; --l-accent: #7c4a1d;
    --d-bg: #1c1410; --d-surface: #261c15; --d-surface-2: #33261c; --d-text: #f6ede2; --d-muted: #d2bfa8;
    --d-border: #4a3829; --d-primary: #f59e0b; --d-primary-contrast: #1c1410; --d-accent: #fcd34d;
    --l-field: #fbf5eb; --l-field-border: #8d7355; --l-placeholder: #6b5644;
    --d-field: #171009; --d-field-border: #8a705a; --d-placeholder: #b8a68f;
}

[data-theme="mint"] {
    --l-bg: #f2fbf6; --l-surface: #ffffff; --l-surface-2: #e2f4ea; --l-text: #0b2e22; --l-muted: #3f6b5b;
    --l-border: #cfe6da; --l-primary: #047857; --l-primary-contrast: #ffffff; --l-accent: #064e3b;
    --d-bg: #06231a; --d-surface: #0a3126; --d-surface-2: #0f3f31; --d-text: #e6f7ef; --d-muted: #a7d3c1;
    --d-border: #1d5443; --d-primary: #34d399; --d-primary-contrast: #06231a; --d-accent: #6ee7b7;
    --l-field: #f0f9f4; --l-field-border: #6a8d7d; --l-placeholder: #41705f;
    --d-field: #041c14; --d-field-border: #5e9481; --d-placeholder: #8fc0ac;
}

/* ---------- mode switch: light by default, dark by preference or toggle ---------- */

:root {
    --bg: var(--l-bg); --surface: var(--l-surface); --surface-2: var(--l-surface-2); --text: var(--l-text);
    --muted: var(--l-muted); --border: var(--l-border); --primary: var(--l-primary);
    --primary-hover: var(--l-primary-hover, var(--l-primary));
    --primary-contrast: var(--l-primary-contrast); --accent: var(--l-accent);
    --field: var(--l-field, var(--l-surface)); --field-border: var(--l-field-border, var(--l-border));
    --placeholder: var(--l-placeholder, var(--l-muted));
    --success: #15803d; --danger: #b91c1c;
    --shadow: var(--preset-shadow, 0 1px 2px rgba(0, 0, 0, .06), 0 8px 24px rgba(0, 0, 0, .06));
    --radius: var(--preset-radius, 12px); --radius-sm: var(--preset-radius-sm, 8px);
    --button-radius: var(--preset-button-radius, 999px); --button-shadow: var(--preset-button-shadow, none);
    --table-pad: var(--preset-table-pad, .6rem); --table-size: var(--preset-table-size, .95rem);
    --heading-weight: var(--preset-heading-weight, 700);
    --font: var(--preset-font, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
    --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color-scheme: light;
}

@media (prefers-color-scheme: dark) {
    html:not([data-mode="light"]) {
        --bg: var(--d-bg); --surface: var(--d-surface); --surface-2: var(--d-surface-2); --text: var(--d-text);
        --muted: var(--d-muted); --border: var(--d-border); --primary: var(--d-primary);
        --primary-hover: var(--d-primary-hover, var(--d-primary));
        --primary-contrast: var(--d-primary-contrast); --accent: var(--d-accent);
        --field: var(--d-field, var(--d-surface)); --field-border: var(--d-field-border, var(--d-border));
        --placeholder: var(--d-placeholder, var(--d-muted));
        --success: #4ade80; --danger: #f87171;
        --shadow: var(--preset-shadow, 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .35));
        color-scheme: dark;
    }
}

html[data-mode="dark"] {
    --bg: var(--d-bg); --surface: var(--d-surface); --surface-2: var(--d-surface-2); --text: var(--d-text);
    --muted: var(--d-muted); --border: var(--d-border); --primary: var(--d-primary);
    --primary-hover: var(--d-primary-hover, var(--d-primary));
    --primary-contrast: var(--d-primary-contrast); --accent: var(--d-accent);
    --field: var(--d-field, var(--d-surface)); --field-border: var(--d-field-border, var(--d-border));
    --placeholder: var(--d-placeholder, var(--d-muted));
    --success: #4ade80; --danger: #f87171;
    --shadow: var(--preset-shadow, 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .35));
    color-scheme: dark;
}

/* ---------- base ---------- */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
    margin: 0; font-family: var(--font); font-size: 16px; line-height: 1.5;
    color: var(--text); background: var(--bg); min-height: 100vh; display: flex; flex-direction: column;
}
/* A link never underlines, in any state. Hover and focus shift the colour and
   lay a soft pill behind the text; keyboard focus keeps a visible ring. An
   underline on :active flashes on every tap, and one on :focus trails a mouse
   user around the page, which is why neither exists here. */
a { color: var(--primary); text-decoration: none; }
a:hover, a:focus, a:active, a:visited { text-decoration: none; }
/* Never a button: an accent button paints its hover background with this
   same token, so colouring the label with it makes the label disappear
   into the button. That is what emptied "New order" on the paid state. */
a:hover:not(.button), a:focus-visible:not(.button) { color: var(--primary-hover); }
/* The pill is for links that read as text in a sentence or a list; anything
   already carrying its own surface (buttons, tabs, menu rows, cards) opts out. */
a.text-link:hover, a.text-link:focus-visible, .back-link:hover, .back-link:focus-visible {
    background: var(--surface-2); border-radius: var(--radius-sm); box-shadow: 0 0 0 .2rem var(--surface-2);
}
h1, h2, h3 { line-height: 1.2; margin: 0 0 var(--space-2); letter-spacing: -.01em; font-weight: var(--heading-weight); }
h1 { font-size: clamp(1.75rem, 4vw, 2.5rem); }
h2 { font-size: clamp(1.25rem, 3vw, 1.6rem); }
h3 { font-size: 1.05rem; }
p { margin: 0 0 var(--space-3); }
/* A word with no space in it - a link, a handle, a reference - is wider than
   a phone, and the paragraph holding one pushed the page sideways rather than
   breaking it. A code sample is one of those by definition, so it breaks
   anywhere; ordinary text only breaks a word that cannot fit a line of its
   own. Measured on the order form: the example link was 446px wide and the
   page scrolled sideways at 380 and at 1366. */
p, li, dd, .hint, .muted { overflow-wrap: break-word; }
code { overflow-wrap: anywhere; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg { max-width: 100%; }
[x-cloak] { display: none !important; }
/* Inline icons carry their geometry; the drawing is the same for all of them. */
.icon { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.icon { width: 1.25em; height: 1.25em; vertical-align: -.25em; flex: none; }
.muted { color: var(--muted); }
.small { font-size: .875rem; }
.mono { font-family: var(--mono); font-size: .9em; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.skip-link { position: absolute; left: -999px; top: 0; background: var(--primary); color: var(--primary-contrast); padding: var(--space-2) var(--space-4); z-index: 100; }
.skip-link:focus { left: .5rem; top: .5rem; }
/* Three quarters of a wide screen rather than a fixed 72rem: a catalogue
   table is the widest thing on this storefront and it was being read down a
   column with four hundred empty pixels either side of it. Never narrower
   than it used to be, and capped so a line of prose cannot run the width of
   a 4K panel. Pages that are mostly words keep their own tighter caps:
   .narrow-page, .section.narrow, .lead. */
.container { width: 100%; max-width: clamp(72rem, 75vw, 100rem); margin: 0 auto; padding: 0 var(--pad-page); }
/* Prose is not a table. A page that is mostly reading keeps a comfortable
   measure whatever the container does around it. */
/* .post > .prose as well: a blog post wraps its body in an <article>, so the
   child combinator stopped matching and the post ran the full container width
   - about 180 characters a line on a desktop. */
.page > .prose, .post > .prose, .prose.measure { max-width: 46rem; }
.page { flex: 1; padding-top: var(--space-6); padding-bottom: var(--space-8); }

/* ---------- header ---------- */

.site-header { position: sticky; top: 0; z-index: 20; background: var(--surface); border-bottom: 1px solid var(--border); }
.bar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); min-height: 3.5rem; flex-wrap: wrap; }
.brand { font-weight: 800; font-size: 1.1rem; color: var(--text); letter-spacing: -.01em; }
.brand:hover { text-decoration: none; color: var(--primary); }
.site-nav { display: none; }
.site-nav a { color: var(--text); padding: var(--space-2) var(--space-1); border-radius: var(--radius-sm); font-weight: 500; }
.site-nav a.is-active { color: var(--primary); }
.site-nav a:hover { background: var(--surface-2); text-decoration: none; }
.bar-actions { display: flex; gap: var(--space-1); align-items: center; }
.icon-button {
    display: inline-flex; align-items: center; justify-content: center; width: 2.5rem; height: 2.5rem;
    border: 1px solid var(--border); background: var(--surface); color: var(--text); border-radius: 999px; cursor: pointer;
}
.icon-button:hover { background: var(--surface-2); }
.icon-button:focus-visible, .button:focus-visible, a:focus-visible, input:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

@media (min-width: 900px) {
    .site-nav { display: flex; gap: var(--space-1); }
    .site-nav a { padding: var(--space-2) var(--space-3); }
    .menu-button { display: none; }
}

/* ---------- buttons, badges, cards ---------- */

.button {
    display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-5); border-radius: var(--button-radius);
    font-weight: 600; border: 1px solid var(--border); background: var(--surface); color: var(--text); cursor: pointer; line-height: 1.1;
}
.button:hover { text-decoration: none; background: var(--surface-2); }
.button-primary { background: var(--primary); color: var(--primary-contrast); border-color: var(--primary); box-shadow: var(--button-shadow); }
.button-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
/* Hover moves the background and nothing else. The label is pinned to its
   contrast token in every state, so no later rule can colour it into the
   background again. */
.button-primary, .button-primary:hover, .button-primary:focus, .button-primary:focus-visible, .button-primary:active,
.skip-link:hover, .skip-link:focus-visible,
.balance-chip:hover, .balance-chip:focus-visible { color: var(--primary-contrast); }
.button-ghost { background: transparent; }
.button.is-disabled { opacity: .5; pointer-events: none; }
.text-link { display: inline-flex; align-items: center; gap: var(--space-1); font-weight: 600; }
.badge { display: inline-block; padding: .1rem var(--space-3); border-radius: 999px; font-size: .8rem; font-weight: 600; background: var(--surface-2); color: var(--text); }
.badge-soft { background: var(--surface-2); color: var(--muted); margin-left: var(--space-2); }
.card { display: block; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--pad-card-tight); box-shadow: var(--shadow); }
.card-link { color: var(--text); display: flex; flex-direction: column; gap: var(--space-2); height: 100%; }
.card-link:hover { text-decoration: none; border-color: var(--primary); }
.card-title { font-weight: 700; }
.card-grid { display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); }
.empty { padding: var(--space-6); text-align: center; color: var(--muted); background: var(--surface); border: 1px dashed var(--border); border-radius: var(--radius); }

/* ---------- sections ---------- */

.hero { padding: var(--space-6) 0 var(--space-7); }
.eyebrow { text-transform: uppercase; letter-spacing: .12em; font-size: .75rem; font-weight: 700; color: var(--primary); margin-bottom: var(--space-2); }
.lead { font-size: 1.1rem; color: var(--muted); max-width: 40rem; }
.cta-row { display: flex; gap: var(--gap-inline); flex-wrap: wrap; margin: var(--space-4) 0; }
.stats { display: flex; gap: var(--space-6); flex-wrap: wrap; margin-top: var(--space-4); }
.stats li { display: flex; flex-direction: column; }
.stats strong { font-size: 1.5rem; color: var(--text); }
.stats span { color: var(--muted); font-size: .9rem; }
.section { padding: var(--space-5) 0; }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-3); }
.page-head { padding: var(--space-2) 0 var(--space-4); }
.back-link { margin-bottom: var(--space-3); }
.steps-grid { display: grid; gap: var(--space-4); counter-reset: step; }
.steps-grid li { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--pad-card); }
.step-icon { display: inline-flex; width: 2.5rem; height: 2.5rem; align-items: center; justify-content: center; border-radius: 999px; background: var(--surface-2); color: var(--primary); margin-bottom: var(--space-3); }
@media (min-width: 720px) {
    .steps-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .hero { padding: var(--space-8) 0; }
}

/* ---------- search and filter ---------- */

.search { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-4); }
.search input, .filter input {
    flex: 1 1 14rem; min-width: 0; padding: var(--space-3) var(--space-4); border: 1px solid var(--field-border); border-radius: 999px;
    background: var(--field); color: var(--text); font: inherit;
}
.filter { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); color: var(--muted); }

/* ---------- services table: table on wide, cards on narrow ---------- */

/* The frame the catalogue sits in. Its background, edge and shadow come
   from the glass block near the foot of this file; what is left here is its
   shape and the room it gives the table. */
.table-block { border-radius: var(--radius); padding: var(--space-3); }
/* A table stays a table, at every width, and scrolls sideways inside its
   frame - which is what the panel this copies does, and what somebody
   comparing six prices actually needs. It used to fold into a stack of
   two-column cards under 720px: readable one row at a time, useless for the
   comparison the page exists to support, and it needed the column names
   repeated inside every cell to make sense at all. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Enough width that six columns are still six columns rather than six words
   stacked in a column each. Below it the frame scrolls. */
.services { min-width: 44rem; }
.services { width: 100%; border-collapse: collapse; font-size: var(--table-size); }
.services th { text-align: left; font-size: .75rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--border); }
.services td { padding: var(--table-pad); border-bottom: 1px solid var(--border); vertical-align: top; }
.services tbody tr:last-child td { border-bottom: 0; }
.service-name { display: inline; }
/* The bar is one cell across the whole table, so it must not be dragged out
   to the table's minimum width and leave the name scrolled off. */
.category-bar th, .category-tools td, .category-pager td { position: sticky; left: 0; }

/* ---------- pagination ---------- */

.pagination { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-inline); margin-top: var(--space-4); flex-wrap: wrap; }
/* THE WHOLE-CATALOGUE SEARCH RESULTS. The list had no rule, and the reset
   strips a <ul> of everything, so each result was four spans running together
   on one line with the next result flush underneath and nothing to aim a
   finger at. */
.search-result-list { display: grid; gap: var(--space-2); }
.search-result-list a { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-3); padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); }
.search-result-list a:hover { border-color: var(--primary); text-decoration: none; }
.search-result-list strong { margin-inline-start: auto; }
/* Two pending top-ups are two cards, and a card carries no margin of its own:
   they welded into one box. */
.topup-progress { margin-bottom: var(--gap-cards); }
/* The evidence block on an order: a heading, a line, the thumbnails and the
   upload form, which arrived with only a top margin between them. */
.panel-section { display: grid; gap: var(--gap-stack); margin-top: var(--gap-cards); }
.panel-section > h2, .panel-section > p { margin-bottom: 0; }

/* ---------- footer ---------- */

.site-footer { border-top: 1px solid var(--border); background: var(--surface); padding: var(--space-6) 0 var(--space-4); margin-top: auto; }
.footer-grid { display: grid; gap: var(--space-4); padding-bottom: var(--space-4); }
.footer-brand { font-weight: 800; margin-bottom: var(--space-1); }
.footer-nav { display: flex; gap: var(--space-4); flex-wrap: wrap; align-content: start; }
.footer-nav a { color: var(--text); }
@media (min-width: 720px) { .footer-grid { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr); } }
.footer-title { font-weight: 700; margin-bottom: var(--space-2); }

/* ---------- language switcher (form of buttons, no locale in the URL) ---------- */

.lang-switch { display: inline-flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; }
.lang-switch button {
    font: inherit; font-size: .85rem; font-weight: 600; padding: var(--space-2) var(--space-3); border-radius: 999px; cursor: pointer;
    border: 1px solid var(--border); background: var(--surface); color: var(--muted);
}
.lang-switch button:hover { color: var(--text); background: var(--surface-2); }
.lang-switch button.is-active { border-color: var(--primary); color: var(--primary); }
.site-nav .nav-lang { padding: var(--space-1); }
@media (min-width: 720px) { .site-nav .nav-lang { margin-left: var(--space-2); } }

/* ---------- right-to-left hooks (used when a locale declares dir: rtl) ---------- */

[dir="rtl"] .site-nav .nav-lang { margin-left: 0; margin-right: var(--space-2); }
[dir="rtl"] .services th, [dir="rtl"] .services td { text-align: right; }
[dir="rtl"] .text-link .icon, [dir="rtl"] .back-link .icon, [dir="rtl"] .button .icon { transform: scaleX(-1); }
[dir="rtl"] .badge-soft { margin-left: 0; margin-right: var(--space-2); }

/* ---------- tenant-editable chrome: announcement bar, logo, WhatsApp, prose ---------- */

.announcement { background: var(--primary); color: var(--primary-contrast); font-weight: 600; font-size: .95rem; text-align: center; padding: var(--space-2) 0; }
.brand { display: inline-flex; align-items: center; gap: var(--space-2); }
.brand-logo { display: block; height: 2.25rem; width: auto; max-width: 12rem; object-fit: contain; }
.prose p { margin: 0 0 var(--space-3); }
.prose p:last-child { margin-bottom: 0; }
.prose ul { list-style: disc; padding-left: var(--space-5); margin: 0 0 var(--space-3); }
.prose li { margin: .15rem 0; }
.prose a { text-decoration: underline; }
.prose.lead p { font-size: 1.1rem; color: var(--muted); }
.steps-grid .prose { color: var(--muted); }
.whatsapp-fab {
    position: fixed; right: 1rem; bottom: 1rem; z-index: 40; display: inline-flex; align-items: center; gap: var(--space-2);
    padding: var(--space-3) var(--space-5); border-radius: 999px; background: var(--success); color: var(--surface); font-weight: 700;
    box-shadow: 0 6px 20px rgba(0, 0, 0, .25);
}
.whatsapp-fab:hover { text-decoration: none; filter: brightness(1.05); }
[dir="rtl"] .whatsapp-fab { right: auto; left: 1rem; }
@media (max-width: 480px) { .whatsapp-fab span { display: none; } .whatsapp-fab { padding: var(--space-4); } }

/* ---------- customer auth and forms ---------- */

.section.narrow { max-width: 28rem; margin: 0 auto; }
.auth-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-6); box-shadow: var(--shadow); }
.auth-card h1 { margin: 0 0 var(--space-1); font-size: 1.5rem; }
.stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap-form); }
/* Single-column grids everywhere a form stacks. A grid item defaults to
   min-width auto, so one unbreakable row (a service name on a nowrap picker
   button) grows the implicit column and the whole form overflows its card,
   sideways, under the right-hand column. minmax(0, 1fr) is what stops it. */
.field { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap-field); font-weight: 600; font-size: .95rem; }
/* The line under a field. There was no rule for it at all outside the
   searchable select, so a hint inherited the label's weight and size and the
   "that name is free" confirmation was pixel-identical to the neutral hint it
   replaced - the check succeeded and said nothing. */
.field .hint { font-weight: 400; font-size: .85rem; color: var(--muted); }
.field .hint.field-ok, .field .field-ok { color: var(--success); font-weight: 600; }
/* A button that is a link in a sentence: the "use this one instead" beside a
   name that is taken. Unstyled it rendered as operating-system button chrome
   in the middle of a line of red text. */
/* No underline: the house rule is that only a link inside tenant prose wears
   one. It reads as pressable by weight, colour and the line under it drawn as
   a border, which also survives the rule above. */
.link-button { padding: 0; border: 0; border-bottom: 1px solid currentColor; background: none; font: inherit; font-weight: 600; color: var(--primary); cursor: pointer; }
.link-button:hover { color: var(--primary-hover); }
.field.checkbox { grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: var(--gap-field); font-weight: 400; }
/* A tick is not a box you type into: width 100% and .7rem of padding
   stretch it across its row and push its label off the end. .field.checkbox
   used to undo that one page at a time; the rule itself is correct now. */
.field.checkbox input { justify-self: start; }
.field input[type="checkbox"], .field input[type="radio"] { accent-color: var(--primary); margin: 0; }
.passkeys form > input:not([type="hidden"]), .field input:not([type="checkbox"]):not([type="radio"]), .field select, .field textarea {
    font: inherit; font-weight: 400; padding: var(--space-3) var(--space-4); border: 1px solid var(--field-border); border-radius: var(--radius-sm);
    background: var(--field); color: var(--text); width: 100%;
}
/* An input has to read as an input on every preset in both modes, so the
   fill, the boundary and the hint each carry their own token and each is
   held to a WCAG AA floor by ContrastTest. */
input::placeholder, textarea::placeholder { color: var(--placeholder); opacity: 1; }
.field textarea { resize: vertical; line-height: 1.5; }
.field-error { color: var(--danger); font-weight: 600; font-size: .9rem; margin: 0; }
.check { display: flex; gap: var(--space-3); align-items: flex-start; font-size: .95rem; }
.check input { margin-top: var(--space-2); }
.button-block { width: 100%; justify-content: center; }
.center { text-align: center; }
.flash { padding: var(--pad-card-tight); border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--surface); margin: 0 0 var(--space-4); }
.flash-success { border-color: var(--success); color: var(--success); }
.flash-error { border-color: var(--danger); color: var(--danger); }
.honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.auth-divider { display: flex; align-items: center; gap: var(--space-3); color: var(--muted); font-size: .85rem; margin: var(--space-1) 0; }
.auth-divider::before, .auth-divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.button-google { width: 100%; justify-content: center; }
.nav-form { display: inline; margin: 0; }
.site-nav .nav-cta { color: var(--primary); font-weight: 600; }

/* ---------- customer area and public pages ---------- */

/* THE PANEL SHELL. Signed in, the menu is a full-height column pinned to
   the left edge of the viewport from 900px, not a card inside the centred
   container: the parent puts it against the glass and everything else to
   its right, including the top bar and the footer. Below 900px the column
   is not drawn at all and the drawer and bottom bar carry the menu. */
.app-side { display: none; }
@media (min-width: 900px) {
    .app-shell { --side: 15rem; }
    .app-side { display: flex; flex-direction: column; gap: var(--space-1); position: fixed; inset-block: 0; inset-inline-start: 0; z-index: 30; width: var(--side); padding: var(--space-4) var(--space-3); overflow-y: auto; overscroll-behavior: contain; background: var(--surface); border-inline-end: 1px solid var(--border); }
    .app-body { margin-inline-start: var(--side); min-width: 0; display: flex; flex-direction: column; min-height: 100vh; }
    .app-body .site-header { inset-inline-start: var(--side); }
    .brand-mobile-only { display: none; }
}
.app-side-brand { display: flex; align-items: center; min-height: 2.75rem; padding: 0 var(--space-3); margin-bottom: var(--space-2); font-weight: 800; font-size: 1.05rem; color: var(--text); }
.app-side-brand:hover { text-decoration: none; }
.app-side-menu { display: grid; gap: .1rem; }
.app-side-menu a, .app-side-menu .nav-pending { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius-sm); color: var(--muted); font-weight: 600; }
.app-side-menu a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.app-side-menu a.is-active { background: var(--surface-2); color: var(--primary); box-shadow: inset 2px 0 0 var(--primary); }
.app-side-menu .nav-pending { opacity: .55; }

/* THE STATS ROW. Four cards under the top bar of every panel page, an
   illustration on the left of each and the figure on the right. One row on a
   desktop, stacked on a tablet and a phone, which is where the parent puts
   them and how it stacks them. */
.stat-row-wrap { padding-top: var(--space-4); }
.stat-row { display: grid; gap: var(--space-3); margin: 0; }
/* The parent stacks all four on a tablet and pairs them only once there is
   room for a pair to still read as two cards. */
@media (min-width: 900px) { .stat-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1200px) { .stat-row { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.stat-card { display: flex; align-items: center; gap: var(--space-3); min-width: 0; padding: var(--pad-card-tight); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }
.stat-art { flex: none; display: grid; place-items: center; width: 3rem; height: 3rem; border-radius: var(--radius-sm); background: var(--surface-2); color: var(--muted); overflow: hidden; }
.stat-art img { width: 100%; height: 100%; object-fit: cover; }
.stat-initials { font-weight: 800; letter-spacing: .02em; color: var(--primary); }
.stat-text { min-width: 0; border-inline-start: 1px solid var(--border); padding-inline-start: var(--space-3); }
.stat-title { margin: 0; font-size: 1.05rem; font-weight: 800; overflow-wrap: anywhere; }
.stat-label { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin: .1rem 0 0; font-size: .85rem; color: var(--muted); }
.stat-note { margin: var(--space-2) 0 0; font-size: .74rem; color: var(--muted); }
.stat-verified { color: var(--primary); vertical-align: -.15em; }
.stat-card-warning .stat-title { color: var(--primary); }
.stat-card-success .stat-title { color: var(--success); }
.stat-deposit { padding: .2rem var(--space-3); font-size: .78rem; }
.page-title { margin: var(--space-5) 0 var(--space-3); }
/* Top bar right: the balance pill, then Account and Logout as their own
   buttons, pushed to the far edge the way the parent lays them out. */
.top-account, .top-logout { white-space: nowrap; }
@media (min-width: 900px) { .app-body .site-nav { margin-inline-start: auto; } }
/* The balance in the top bar opens Account and Sign out; the panel menu
   itself is never repeated up here. */
.balance-menu { position: relative; }
.balance-chip { display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-4); font: inherit; font-weight: 700; border: 1px solid var(--primary); border-radius: 999px; background: var(--primary); color: var(--primary-contrast); cursor: pointer; }
.balance-chip[aria-expanded=true] .select-caret { transform: rotate(90deg); }
.balance-menu-panel a { display: block; padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); color: var(--text); }
.balance-menu-panel a:hover { background: var(--surface-2); text-decoration: none; }
/* The currency list, on the same panel the parent hangs it on. Codes in a
   row rather than a column: three or four of them read at a glance and
   never push the two links above them off the screen. */
/* Paying from your own wallet: what to send, where, and the one thing we
   need back. Wide targets and code that wraps, because the thing being
   copied is long and is being read off a handset. */
.pay-crypto { display: grid; gap: var(--space-3); margin: 0 0 var(--space-4); padding: var(--pad-card-tight); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2); text-align: left; }
.pay-crypto-amount { display: grid; gap: .1rem; margin: 0; }
.pay-crypto-amount strong { font-size: 1.35rem; }
.pay-crypto-copy { display: grid; gap: var(--space-1); }
.pay-crypto-copy code { padding: var(--space-2) var(--space-3); font-size: .95rem; word-break: break-all; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.pay-crypto-copy .button { justify-self: start; }
.pay-crypto-qr { margin: 0; text-align: center; }
.pay-crypto-qr img { width: 220px; height: 220px; border-radius: var(--radius-sm); }
.pay-crypto-form { display: grid; gap: var(--space-2); margin-top: var(--space-2); padding-top: var(--space-3); border-top: 1px solid var(--border); }
.currency-switch { margin: var(--space-2) 0 0; padding-top: var(--space-2); border-top: 1px solid var(--border); }
.currency-switch-label { margin: 0 0 var(--space-2); padding: 0 var(--space-3); font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.currency-switch-row { display: flex; flex-wrap: wrap; gap: var(--space-1); padding: 0 var(--space-2) .1rem; max-height: 11rem; overflow-y: auto; }
.currency-switch-option { display: inline-flex; align-items: baseline; gap: var(--space-2); padding: var(--space-2); font: inherit; font-size: .82rem; font-weight: 600; color: var(--text); background: none; border: 1px solid var(--border); border-radius: 999px; cursor: pointer; }
.currency-switch-option:hover { background: var(--surface-2); }
.currency-switch-option.is-current { color: var(--primary-contrast); background: var(--primary); border-color: var(--primary); }
.currency-switch-option small { font-size: inherit; color: var(--muted); font-weight: 400; }
.currency-switch-option.is-current small { color: inherit; opacity: .75; }
.balance-menu-panel { position: absolute; z-index: 70; inset-inline-end: 0; top: calc(100% + .35rem); min-width: 13.5rem; display: grid; gap: .1rem; padding: var(--space-2); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow); }
/* The account sub-nav is gone; the tabs rule it shared lives on. */
.filter-tabs { display: flex; gap: var(--space-1); overflow-x: auto; border-bottom: 1px solid var(--border); margin-bottom: var(--space-4); }
.filter-tabs a { padding: var(--space-3) var(--space-4); color: var(--muted); font-weight: 600; white-space: nowrap; border-bottom: 2px solid transparent; }
.filter-tabs a.is-active { color: var(--primary); border-bottom-color: var(--primary); }
.filter-tabs a:hover { text-decoration: none; color: var(--text); }
.data-table { width: 100%; border-collapse: collapse; font-size: .95rem; }
.data-table th { text-align: left; font-size: .75rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--border); white-space: nowrap; }
.data-table td { padding: var(--space-3); border-bottom: 1px solid var(--border); vertical-align: top; }
.data-table tbody tr:last-child td { border-bottom: 0; }
.link-text { font-family: var(--mono); font-size: .85em; word-break: break-all; }
code.link-text { background: var(--surface-2); padding: .2rem var(--space-2); border-radius: 4px; }
.is-negative { color: var(--danger); }
.is-positive { color: var(--success); }
.badge-success { background: var(--surface-2); color: color-mix(in srgb, var(--success) 80%, var(--text)); }
.badge-danger { background: var(--surface-2); color: var(--danger); }
.badge-info { background: var(--surface-2); color: var(--primary); }
.badge-warning { background: var(--surface-2); color: var(--text); }
.kv { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-2) var(--space-5); margin: 0 0 var(--space-4); }
.kv dt { color: var(--muted); font-size: .85rem; font-weight: 600; padding-top: .15rem; }
.kv dd { margin: 0; }
.two-col { display: grid; gap: var(--space-4); }
@media (min-width: 900px) { .two-col { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
.card h2 { margin-top: 0; font-size: 1.15rem; }
.narrow-card { max-width: 40rem; }
.narrow-page { max-width: 46rem; }
.thread { display: grid; gap: var(--space-3); margin: var(--space-4) 0 var(--space-5); padding: 0; }
.bubble { padding: var(--space-4); border-radius: var(--radius); background: var(--surface-2); max-width: 40rem; }
.bubble-mine { justify-self: end; background: var(--surface-2); border: 1px solid var(--primary); }
.bubble-meta { margin: 0 0 var(--space-1); }
.bubble-body { margin: 0; white-space: pre-wrap; word-break: break-word; }
.faq { display: grid; gap: var(--space-2); }
.faq-item { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--space-1) var(--space-4); }
.faq-item summary { cursor: pointer; font-weight: 600; padding: var(--space-3) 0; }
.faq-item .prose { padding: 0 0 var(--space-3); color: var(--muted); }
/* The same answer on the blog, where it is raw text rather than markdown and
   so had none of the padding the .prose version gets: the words sat on the
   card's own border. */
.faq-item .faq-answer { padding-bottom: var(--space-3); color: var(--muted); }
.faq-item .faq-answer .prose { padding-bottom: 0; }
.prose.legal p, .prose.legal li { color: var(--text); }
.prose.legal strong { display: block; font-size: 1.15rem; margin: var(--space-4) 0 var(--space-1); }
.prose.legal > p:first-child > strong:first-child { margin-top: 0; }
/* The refund rule every shop's policy opens with, set apart from the shop's
   own words so it reads as the rule rather than as one more paragraph. */
.legal-summary { padding: var(--pad-card-tight); border: 1px solid var(--border); border-left: 4px solid var(--primary); border-radius: var(--radius-sm); background: var(--surface-2); margin: 0 0 var(--space-5); }
.legal-summary-title { font-weight: 700; margin: 0 0 var(--space-2); }
.legal-summary ul { margin: 0; padding-left: 1.25rem; list-style: disc; }
.legal-summary li + li { margin-top: var(--space-1); }
.legal-summary-foot { margin: var(--space-3) 0 0; }
.balance-pill { display: inline-block; margin-left: var(--space-2); padding: .05rem var(--space-2); border-radius: 999px; background: var(--surface-2); color: var(--primary); font-size: .8rem; font-weight: 700; }

/* ---------- order form ---------- */

.price-preview { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; padding: var(--pad-card-tight); background: var(--surface-2); border-radius: var(--radius-sm); }
.price-preview strong { font-size: 1.35rem; }
.warning-box { border: 1px solid var(--primary); background: var(--surface-2); border-radius: var(--radius-sm); padding: var(--pad-card-tight); margin-bottom: var(--space-4); }
.warning-box p { margin: 0 0 var(--space-3); font-weight: 600; }
.button-small { padding: var(--space-2) var(--space-3); font-size: .85rem; }
.services td .button-small { white-space: nowrap; }

/* ---------- notifications ---------- */
.notification-list { display: grid; gap: var(--space-2); padding: 0; }
.notification { display: flex; justify-content: space-between; gap: var(--space-4); align-items: flex-start; padding: var(--pad-card-tight); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.notification.is-unread { border-left: 3px solid var(--primary); }
.notification-title { font-weight: 700; margin: 0 0 .15rem; }
.notification-text { margin: 0 0 var(--space-1); color: var(--muted); }

/* ---------- demo banner ---------- */
.demo-banner { background: var(--surface-2); border-bottom: 1px solid var(--border); font-size: .9rem; padding: var(--space-2) 0; color: var(--text); }
.demo-credentials { display: block; color: var(--muted); }

/* Searchable select: replaces every native select (button + search + listbox). */
.searchable-select { position: relative; }
.select-button { display: flex; justify-content: space-between; align-items: center; gap: var(--space-2); width: 100%; min-height: 2.9rem; font: inherit; font-weight: 400; padding: var(--space-3) var(--space-4); border: 1px solid var(--field-border); border-radius: var(--radius-sm); background: var(--field); color: var(--text); cursor: pointer; text-align: left; }
.select-button .select-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.select-button .is-placeholder { color: var(--placeholder); }
.select-button .select-caret { flex: none; transition: transform .15s ease; }
@media (prefers-reduced-motion: reduce) { .select-button .select-caret { transition: none; } }
.select-button[aria-expanded=true] .select-caret { transform: rotate(90deg); }
.select-panel { position: absolute; z-index: 60; left: 0; right: 0; top: calc(100% + .25rem); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow); padding: var(--space-2); }
.select-panel input[type=search] { width: 100%; font: inherit; font-weight: 400; padding: var(--space-3); border: 1px solid var(--field-border); border-radius: var(--radius-sm); background: var(--field); color: var(--text); margin-bottom: var(--space-2); }
.select-note { margin: 0 var(--space-1) var(--space-2); font-weight: 400; }
.select-list { max-height: 16rem; overflow-y: auto; overscroll-behavior: contain; }
.select-group-title { font-size: .72rem; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); font-weight: 700; margin: var(--space-2) var(--space-2) var(--space-1); }
.select-list button { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: baseline; justify-content: space-between; width: 100%; text-align: left; font: inherit; font-weight: 400; padding: var(--space-3); border: 0; border-bottom: 1px solid var(--row-divider); border-radius: var(--radius-sm); background: transparent; color: var(--text); cursor: pointer; }
/* break-word, never anywhere: both break an overlong word, but anywhere
   also lets the min-content width fall to one character, which is how a
   squeezed row printed the name vertically. */
.select-list button .select-label { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
/* A picker row reads the way the parent writes one: platform mark, the id
   the customer quotes in a ticket, the name, then the rate. The id is its
   own badge because a service whose name opens with digits is otherwise
   indistinguishable from its id. Icons come from one sprite. */
/* The sprite is a definitions block, never drawn. The hidden attribute is
   not enough: an svg with no width or height still takes the replaced
   element default of 300x150 and left a gap above the form. */
.platform-sprite { display: none; }
/* A brand mark carries its own colour from the sprite, so nothing here
   sets fill or stroke on it; the .icon rule would override both. */
.brand-mark { flex: none; display: inline-block; vertical-align: -.15em; }
.row-icon { flex: none; }
.id-badge { flex: none; padding: .05rem var(--space-2); border-radius: var(--radius-sm); background: var(--surface-2); color: var(--muted); font-size: .78rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.select-button .id-badge, .select-button .row-icon { margin-right: .1rem; }
.select-rate { flex: none; white-space: nowrap; }
/* Below this the row is too narrow to hold a name and a rate side by
   side, so the rate takes a line of its own underneath. */
@media (max-width: 480px) { .select-rate { flex: 1 0 100%; } }
.select-list button .select-hint { flex: none; white-space: nowrap; }
.select-list button:hover, .select-list button.is-focused { background: var(--surface-2); }
.select-list button.is-active { background: var(--surface-2); font-weight: 600; }
.select-empty { margin: var(--space-1) var(--space-2) var(--space-2); color: var(--muted); font-weight: 400; }
.searchable-select .hint { font-weight: 400; }
.service-facts { margin: -.25rem 0 0; font-weight: 400; }

/* Best-in-category badge and description lines in service lists */
.badge-best { background: var(--primary); color: var(--primary-contrast); cursor: help; margin-left: var(--space-2); }
.service-desc { display: inline; }

/* What the shop says about a service: Hot, New, Cheapest. Quieter than the
   badge above on purpose - that one is measured, these are claims. Each tone
   only names a theme colour; one rule draws all four. */
.badge-mark { margin-left: var(--space-2); font-weight: 600; background: color-mix(in srgb, var(--mark) 17%, transparent); color: color-mix(in srgb, var(--mark) 80%, var(--text)); }
.mark-accent { --mark: var(--accent); }
.mark-warm { --mark: var(--danger); }
.mark-cool { --mark: var(--primary); }
.mark-good { --mark: var(--success); }

/* Order page: form on the left, selected-service details on the right (stacked on phones) */
.order-layout { display: grid; gap: var(--space-4); align-items: start; }
/* Two columns only once there is room for both: the account rail takes 13rem
   from 900px, so splitting the form there as well left a right column near
   280px and the service card ran under the form. Both columns declare
   min-width 0 so a long service name shrinks its own column instead of
   widening the grid, and the right column is a fixed 22rem so the form
   keeps every pixel the window gains. */
@media (min-width: 1100px) {
    .order-layout { grid-template-columns: minmax(0, 1fr) 22rem; }
    .order-aside { position: sticky; top: 4.5rem; }
}
.order-aside { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); min-width: 0; }
.order-form-card { min-width: 0; }
.service-card-name { font-size: 1.1rem; margin: 0 0 var(--space-2); overflow-wrap: anywhere; }
.service-card-badge { margin: 0 0 var(--space-3); }
.service-card-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3) var(--space-4); margin: 0 0 var(--space-3); }
.service-card-facts dt { font-size: .75rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.service-card-facts dd { margin: .1rem 0 0; overflow-wrap: anywhere; }
.service-card-desc h3 { font-size: .9rem; margin: var(--space-2) 0 var(--space-1); }
.service-desc-full { white-space: pre-line; margin: 0; font-size: .95rem; overflow-wrap: anywhere; }
.service-card.is-empty { border-style: dashed; }
.balance-mini { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.balance-mini strong { font-size: 1.15rem; }
.price-preview.is-over strong { color: var(--danger); }
.order-search-all .select-button { border-color: var(--primary); }
/* Field guidance lives behind an (i) beside the label. Permanent help text
   under every input pushed the form down a phone screen and read as an
   error; the note is still in the markup and still referenced by
   aria-describedby, it is just not always drawn. */
.field-label { display: flex; align-items: center; gap: var(--space-2); }
.field-label label { font-weight: inherit; }
.info-pop { position: relative; display: inline-flex; }
.info-pop-button { display: inline-flex; align-items: center; justify-content: center; width: 1.5rem; height: 1.5rem; padding: 0; border: 1px solid var(--field-border); border-radius: 999px; background: var(--field); color: var(--muted); cursor: pointer; }
.info-pop-button:hover, .info-pop-button[aria-expanded=true] { color: var(--text); border-color: var(--primary); }
.info-pop-panel { position: absolute; z-index: 70; top: calc(100% + .35rem); left: 0; display: grid; gap: var(--space-1); width: max-content; max-width: min(20rem, 78vw); padding: var(--space-3); font-weight: 400; font-size: .875rem; color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow); }
.info-pop-panel code { overflow-wrap: anywhere; }

/* Pay online: the gateway card on Add funds and the payment watch page */
.pay-card { margin-bottom: var(--space-4); border-color: var(--primary); }
.payment-amount { font-size: 2rem; margin: var(--space-1) 0; }
.payment-state { font-weight: 600; margin: var(--space-3) 0; }

/* tenant pages, phone bar, low balance, progress, receipt */
.review-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); }
.review-stars { margin: 0 0 var(--space-2); letter-spacing: .1em; color: var(--muted); }
.review-stars .is-on { color: var(--primary); }
.review-body { margin: 0 0 var(--space-3); }
.review-author { margin: 0; font-weight: 700; }
.link-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.link-list a { font-weight: 600; }
.breadcrumb { display: flex; gap: var(--space-2); align-items: center; margin-bottom: var(--space-3); font-size: .9rem; color: var(--muted); }
.app-banner, .low-balance { position: sticky; bottom: 0; z-index: 20; border-top: 1px solid var(--border); padding: var(--space-3) 0; }
.app-banner { background: var(--surface); }
.low-balance { background: var(--surface-2); z-index: 21; }
.app-banner-inner, .low-balance-inner { display: flex; align-items: center; gap: var(--space-3); font-weight: 600; }
.app-banner-text, .low-balance-inner span { margin: 0; flex: 1 1 auto; }
/* Link and service get a fifth of the table each, as the parent gives them,
   so neither squeezes the numbers a customer came to read. Both clip, with
   the whole value in the title. A percentage on a td is only a hint once the
   table sizes to content, so the cap that binds is the max-width below. */
.nowrap { white-space: nowrap; }
.orders-table { width: 100%; min-width: 54rem; }
.orders-table td { vertical-align: top; }
.orders-table .col-link, .orders-table .col-service { width: 20%; }
.orders-table th, .orders-table .actions, .orders-table .badge { white-space: nowrap; }
.orders-table .link-text { display: block; max-width: 16rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.orders-table .service-clamp { display: -webkit-box; max-width: 14rem; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* The reorder link under it on one line, or a two-line service name and a
   wrapped two-word link make every row four lines tall. */
.orders-table .col-service .text-link { white-space: nowrap; }
.copy-button { padding: .1rem; border: 0; background: none; color: var(--muted); cursor: pointer; vertical-align: middle; }
.copy-button:hover, .copy-button:focus-visible { color: var(--text); }
.copy-button.is-copied { color: var(--success); }
.progress { display: block; height: .35rem; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.progress-bar { display: block; height: 100%; background: var(--primary); }
/* How far an order has got. The bar is decoration; the figure beside it is
   the answer, so the column reads without it. Done green, stopped amber -
   the same two meanings the stage badge two cells left already uses. */
.order-progress { display: block; min-width: 7rem; }
.order-progress.is-done .progress-bar { background: var(--success, #16a34a); }
.order-progress.is-stopped .progress-bar { background: var(--warning, #d97706); }
.progress-figure { display: block; margin-top: var(--space-1); font-size: .78rem; font-variant-numeric: tabular-nums; }
.progress-figure .muted { margin-left: var(--space-2); }
.order-progress.is-waiting .progress { position: relative; }
.order-progress.is-waiting .progress-bar { position: absolute; width: 40%; animation: order-progress-slide 1.5s ease-in-out infinite; }
@keyframes order-progress-slide { from { left: -40%; } to { left: 100%; } }
@media (prefers-reduced-motion: reduce) { .order-progress.is-waiting .progress-bar { animation: none; left: 0; } }


/* The arrow is its own anchor, not the whole cell, so selecting the link
   text to copy it does not navigate. */
.order-link { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.order-link .link-text { min-width: 0; }
.link-open { flex: none; display: inline-flex; color: var(--muted); }
.link-open:hover, .link-open:focus-visible { color: var(--primary); }

/* The first column stays put while the rest scrolls: an order number is how
   a reader knows which row they are on, and it is the first thing off the
   screen without this. Opaque, or the scrolled cells show through. */
.table-pinned { position: relative; }
.table-pinned .col-pin { position: sticky; left: 0; z-index: 1; background: var(--surface); }
.table-pinned thead .col-pin { z-index: 2; }
.table-pinned .col-pin::after { content: ""; position: absolute; top: 0; right: 0; bottom: 0; width: 1px; background: var(--border); }
.home-signin { max-width: 26rem; margin: var(--space-4) auto 0 }
/* The headline and the sign-in card, side by side once there is room
   for both. Below that they stack and the card centres itself, which
   is what it did everywhere before. */
.hero-row { display: grid; gap: var(--space-6); align-items: start; }
@media (min-width: 1100px) {
  .hero-row { grid-template-columns: minmax(0, 1.15fr) minmax(24rem, .85fr); align-items: center; }
  .hero-row .home-signin { margin-top: 0; max-width: none; }
  .hero-row .lead { max-width: none; }
}
.receipt-head { display: flex; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-4); }
.receipt-title h1 { margin: 0; font-size: 1.2rem }
@media print { .site-header, .site-footer, .no-print, .whatsapp-fab, .app-banner, .low-balance { display: none !important } }

/* ---------- the price calculator ----------
   Laid out to match the panel this copies: platform, service and quantity
   read left to right on one line, the quantity chips are one wrapping row
   under them, and the answer sits in a panel of its own with its figures
   set apart. Under 720px they stack, which is the only thing that fits. */

.calculator { margin: var(--space-4) 0 var(--space-5); padding: var(--pad-card); border-radius: var(--radius); }
.calc-eyebrow { margin: 0 0 .15rem; font-size: .72rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--primary); }
.calculator > h2 { margin: 0 0 var(--space-1); }
.calc-controls { display: grid; gap: var(--space-3); margin-top: var(--space-4); }
/* The label of each control, whichever kind it is: a plain label wraps its
   own span, the searchable select puts one first inside itself. Not
   .select-label, which names a row inside the open list. */
.calc-controls > .field > span:first-child { font-size: .72rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); }
@media (min-width: 720px) { .calc-controls { grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: end; } }
/* One wrapping row, not a list stacked one chip to a line. */
.calc-presets { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-3) 0 0; padding: 0; list-style: none; }
.chip { padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--button-radius); background: var(--surface-2); color: var(--text); font: inherit; font-size: .82rem; font-weight: 700; cursor: pointer; }
.chip:hover { border-color: var(--primary); }
.chip.is-chosen { background: var(--primary); border-color: var(--primary); color: var(--primary-contrast); }
.calc-pointer { margin: var(--space-4) 0 var(--space-2); font-size: .85rem; color: var(--muted); }
.calc-pointer::before { content: "↓ "; font-weight: 800; color: var(--primary); }
.calc-answer { margin: 0; padding: var(--pad-card-tight); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); font-weight: 600; }
.calc-answer .amount { color: var(--primary); font-weight: 800; }

/* ---------- the answers card ----------
   A pill per platform and, under the chosen one, its questions as rows that
   open. Everything is in the document; only a browser running scripts hides
   what it is not showing, which is why the group rule is behind .js. */

.answers { margin: 0 0 var(--space-5); padding: var(--pad-card); border-radius: var(--radius); }
.answers > h2 { margin: 0 0 var(--space-1); }
.answer-pills { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-4) 0; padding: 0; list-style: none; }
.answer-pill {
    display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-2) var(--space-4);
    border-radius: var(--button-radius); font-size: .85rem; font-weight: 700; color: var(--text);
}
.answer-pill:hover { text-decoration: none; }
.answer-pill.is-chosen { background: var(--primary); border-color: var(--primary); color: var(--primary-contrast); }
.answer-pill .brand-mark { flex: 0 0 auto; }
.answer-group details { border-radius: var(--radius-sm); margin-bottom: var(--space-2); }
.answer-group summary { display: flex; gap: var(--space-3); align-items: flex-start; justify-content: space-between; padding: var(--space-3) var(--space-4); cursor: pointer; font-weight: 700; list-style: none; }
.answer-group summary::-webkit-details-marker { display: none; }
.answer-group summary::after { content: "+"; flex: 0 0 auto; font-weight: 800; color: var(--muted); transform-origin: 50% 50%; transition: transform .15s ease; }
@media (prefers-reduced-motion: reduce) { .answer-group summary::after { transition: none; } }
.answer-group details[open] summary::after { transform: rotate(45deg); color: var(--primary); }
.answer-group details p { margin: 0; padding: 0 var(--space-4) var(--space-3); }
.answer-group b { color: var(--primary); font-weight: 800; }
/* Only where scripts are running. With none, every platform's questions are
   simply there, which is what a crawler and a reader without them get. */
.js .answer-group:not(.is-chosen) { display: none; }

/* services: platform tabs, category bars, one details dialog for the table */
/* Icon only, and small enough that twenty platforms are two or three rows
   on a phone and one or two on a desktop. The name and the count live in the
   tooltip and the accessible name. */
.platform-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-4); }
.platform-tab { display: grid; place-items: center; width: 2.75rem; height: 2.75rem; padding: 0; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--muted); }
.platform-tab:hover { text-decoration: none; border-color: var(--primary); }
.platform-tab.is-active { border-color: var(--primary); background: var(--surface-2); color: var(--text); }
button.platform-tab { font: inherit; cursor: pointer; }
.pay-timeline { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: grid; gap: var(--space-2); }
.pay-timeline li { position: relative; padding-left: var(--space-6); color: var(--muted); }
.pay-timeline li::before { content: ""; position: absolute; left: 0; top: .45rem; width: .6rem; height: .6rem; border-radius: 999px; border: 1px solid var(--border); }
.pay-timeline .is-done { color: var(--text); }
.pay-timeline .is-done::before { background: var(--success); border-color: var(--success); }
.pay-timeline .is-current { color: var(--text); font-weight: 700; }
.pay-timeline .is-current::before { background: var(--primary); border-color: var(--primary); }
.service-desc-box { padding: var(--pad-card-tight); background: var(--surface-2); border-radius: var(--radius-sm); }
.service-desc-box p { margin: 0; font-weight: 400; white-space: pre-line; overflow-wrap: anywhere; }
.field input[readonly] { background: var(--surface-2); color: var(--muted); }
/* Filter and search on one line, the platform row under them. */
.services-toolbar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin: var(--space-4) 0 var(--space-3); }
.services-toolbar .search { flex: 1 1 18rem; margin: 0; }
.services-filter { flex: none; }
/* A band across the table, as the panel this copies has it. The shape is
   theirs; the colour is not. This preset's own surface, in both modes, so a
   shop that chose mint is not handed somebody else's gold. */
/* No padding of its own: the link inside carries it, so the padding is
   part of what you press rather than a dead margin around it. */
.category-bar th { background: var(--surface-2); font-weight: 700; text-align: left; padding: 0; }
.services thead th { background: var(--surface-2); }
.category-bar .brand-mark { margin-inline-end: var(--space-2); vertical-align: -.2em; }
.category-bar a { color: inherit; }
/* A bar is a band across the table, not a control sitting in one, and the
   whole band is the control: it fills its cell so anywhere on the row
   presses it, which is where a hand goes. */
/* A th, so it arrives wearing the header row's small tracked-out capitals.
   A category name is neither a heading nor an abbreviation. */
.category-toggle {
    display: block; width: 100%; padding: var(--table-pad); cursor: pointer; text-align: left;
    font-size: var(--table-size); font-weight: 700; text-transform: none; letter-spacing: normal; color: var(--text);
    background: none; border: 0;
}
.category-toggle:hover { text-decoration: none; }
.category-bar:hover th { background: var(--border); }
/* Rate and range as badges rather than as numbers loose in a cell, which is
   how the panel this copies prints them and how somebody scanning a long
   table finds the two figures they came for. Its green and red are its own;
   these take the preset's surfaces, and the rate is the one that carries
   weight because the rate is what somebody came to read. */
.pill { display: inline-block; padding: .12rem var(--space-2); border-radius: var(--radius-sm); font-size: .82rem; font-weight: 700; white-space: nowrap; }
.pill-rate { background: var(--surface-2); color: var(--text); border: 1px solid var(--border); }
.pill-unit { font-weight: 600; color: var(--muted); text-transform: uppercase; font-size: .72rem; }
.pill-range { background: transparent; color: var(--muted); border: 1px solid var(--border); font-weight: 600; }
/* Numbered pages, as the panel this copies has them. Twenty-three of them
   is too many to print, so it shows a window around where somebody is. */
.pagination-numbers { justify-content: flex-start; gap: var(--space-2); }
button.page-num, button.chip { font: inherit; font-weight: 700; cursor: pointer; }
.page-num { display: inline-block; min-width: 2rem; padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); font-weight: 700; font-size: .85rem; text-align: center; }
.page-num:hover { background: var(--surface-2); text-decoration: none; }
.page-num.is-current { background: var(--surface-2); border-color: var(--primary); }
.page-gap { color: var(--muted); padding: 0 .1rem; }
/* Drawn at the ends rather than removed, so the control keeps its shape
   under a finger walking it. */
.page-num.is-disabled { opacity: .45; cursor: default; }
.page-step { font-weight: 600; }
/* The list while it is being replaced: dimmed rather than emptied, so the
   page does not jump to nothing and back. */
[data-list].is-busy { opacity: .55; transition: opacity .12s ease; }
/* The order control and the pager of one open category. They sit inside its
   tbody because a page can have two categories open and they are not asking
   the same question. */
.category-tools td, .category-pager td { padding: var(--space-2) var(--space-3); }
.category-tools .chip { padding: .2rem var(--space-3); font-size: .78rem; margin-inline-start: var(--space-2); }
.category-tools .chip:hover { text-decoration: none; }
.category-pager .pagination { margin: 0; }
.list-error { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; padding: var(--pad-card-tight); margin-top: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); }
/* max-width as well as inset: a layer pinned to the window takes the width of
   whatever holds it, which on a phone measured 977px inside 380px. */
.modal-backdrop { position: fixed; inset: 0; max-width: 100vw; z-index: 80; display: flex; align-items: center; justify-content: center; padding: var(--space-4); background: rgba(0, 0, 0, .5); }
.modal-panel { position: relative; width: 100%; max-width: 32rem; max-height: 85vh; overflow-y: auto; padding: var(--space-6); border-radius: var(--radius); background: var(--surface); }
/* A dialog sits over a dimmed page, so its glass is solid: a see-through
   panel over the dark layer turns grey, and the small print on it fell to
   a contrast nobody could read. The gloss and the edge stay. */
.modal-panel { --glass-bg: var(--surface); }
/* The close button is pinned in the corner; a long service name ran under it. */
.modal-close ~ .stack > h2:first-child { padding-right: var(--space-8); }
.modal-close { position: absolute; top: .5rem; right: .5rem; }

/* Account: two tabs above the cards, and the small marks the cards use. */
.account-tabs { display: flex; gap: var(--space-2); margin: 0 0 var(--space-4); border-bottom: 1px solid var(--border); }
.account-tabs a { padding: var(--space-3) var(--space-4); font-weight: 700; color: var(--muted); border-bottom: 2px solid transparent; }
.account-tabs a:hover, .account-tabs a:focus-visible { color: var(--text); background: var(--surface-2); border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
.account-tabs a.is-active { color: var(--primary); border-bottom-color: var(--primary); }
.badge-ok { background: var(--surface-2); color: var(--success); }

/* The waiting page: one spinner, one green confirmation, one refusal. */
.pay-waiting, .pay-done { display: grid; gap: var(--space-3); justify-items: center; text-align: center; margin: var(--space-4) 0; }
.pay-spinner span { display: block; width: 2.5rem; height: 2.5rem; border-radius: 50%; border: 3px solid var(--surface-2); border-top-color: var(--primary); animation: pay-spin 1s linear infinite; }
@keyframes pay-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .pay-spinner span { animation-duration: 3s; } }
.pay-done-ok .pay-tick { color: var(--success); line-height: 1; }
.pay-amount-line { font-size: 1.6rem; font-weight: 800; margin: 0; }
.pay-warn { padding: var(--pad-card-tight); border-radius: var(--radius-sm); background: var(--surface-2); font-weight: 600; }
.pay-done-bad .payment-state { color: var(--danger); }
/* The code a wallet texts before it rings: one short field, read at a glance.
   And the steps for approving on the handset by hand, in the same column. */
.pay-code, .pay-steps { display: grid; gap: var(--space-3); width: min(100%, 20rem); text-align: left; }
.pay-code input { text-align: center; font-size: 1.25rem; letter-spacing: .2em; }
.pay-code .button, .pay-steps .button { justify-content: center; }
.pay-steps p, .pay-steps ol { margin: 0; }
.pay-steps ol { padding: 0; display: grid; gap: var(--space-1); }

/* ---------- liquid glass ----------
   Tokens and the three fallbacks are in glass.css; this is only what floats
   on a storefront. Content stays flat on purpose: .card, .service-card, the
   tables themselves and the form panels are the page rather than something
   over it, and glass under a paragraph costs more than it gives. A test
   holds that line.

   A panel that holds a table is not a table. .table-block and .calculator
   are the frames the catalogue sits in - the edge, the corner and the
   shadow are what somebody means by the glass feel - and the rows inside
   them keep their own solid ground so a price is read against a flat
   surface rather than through one. */

.stat-card, .pay-card, .modal-panel, .balance-menu-panel, .info-pop-panel,
.select-panel, .auth-card, .flash, .table-block, .calculator, .answers,
/* The pills and the rows inside the card float on it, which is the glossy
   edge the shop asked for; the answer text under an open row does not. */
.answer-pill, .answer-group details,
/* Fixed, so the page already moves underneath it, which is what makes the
   blur read as glass rather than as a tint. */
.app-side {
    background-color: var(--glass-bg);
    background-image: var(--glass-image);
    -webkit-backdrop-filter: var(--glass-filter);
    backdrop-filter: var(--glass-filter);
    border: 1px solid var(--glass-edge);
    box-shadow: var(--glass-shadow);
}

/* The top bar is the same glass, painted on a layer behind its content
   rather than on the bar itself. A box with a backdrop-filter becomes the
   backdrop root of everything inside it, so the balance menu hanging below
   the bar could blur nothing but the bar: the page under the menu showed
   through sharp at a third strength and the menu could not be read over
   it. With the filter one layer down the bar is an ordinary box, and the
   menu blurs the page it opens over, as every other dropdown does. */
.site-header {
    background: none;
    border: 1px solid var(--glass-edge);
    box-shadow: var(--glass-shadow);
}

.site-header::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-color: var(--glass-bg);
    background-image: var(--glass-image);
    -webkit-backdrop-filter: var(--glass-filter);
    backdrop-filter: var(--glass-filter);
}

/* Bars that run to an edge keep the shape they had. */
.stat-card, .pay-card, .modal-panel, .balance-menu-panel, .info-pop-panel,
.select-panel, .auth-card, .flash { border-radius: var(--glass-radius); }

/* Controls keep their accent fill; the glass here is the gloss and the edge
   over it, not the background. */
.balance-chip, .balance-pill {
    background-image: var(--glass-image);
    border: 1px solid var(--glass-edge);
    border-radius: 999px;
    box-shadow: var(--glass-shadow);
}

.button { background-image: var(--glass-image); }

/* The active row reads as a pill lying on the menu. */
.app-side-menu a.is-active {
    background-image: var(--glass-image);
    border-radius: var(--glass-radius);
    box-shadow: inset 2px 0 0 var(--primary), var(--glass-shadow);
}

/* ---------- ways to pay ----------
   A row of named methods, each the shop's own word for it. No gateway brand
   appears here or anywhere a customer can see. */

.pay-methods { border: 0; padding: 0; margin: 0 0 var(--space-1); display: grid; gap: var(--space-2); grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); }
.field-legend { font-weight: 600; font-size: .95rem; margin-bottom: var(--space-2); }
.pay-method {
    display: flex; align-items: center; gap: var(--space-2); padding: var(--pad-card-tight); cursor: pointer;
    border: 1px solid var(--border); border-radius: var(--glass-radius); background-color: var(--glass-bg);
    background-image: var(--glass-image); font-weight: 600; line-height: 1.2;
}
.pay-method:hover { border-color: var(--primary); }
.pay-method.is-chosen { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); }
.pay-method input { margin: 0; accent-color: var(--primary); flex: none; }
.pay-method-name { min-width: 0; overflow-wrap: anywhere; }
/* Where the payer is, said once above the list, and the networks a method
   that is not itself one network covers. Both are recognition rather than
   instruction, so they sit quietly. */
.pay-from { margin: 0 0 var(--space-2); }
/* What will actually leave their wallet. Said louder than the fee above it,
   because it is the number they are agreeing to. */
.pay-total { margin: 0; font-weight: 600; }
/* The shop's offer: an invitation before they type and a figure after, so it
   reads as good news rather than as another rule. */
.pay-bonus { margin: .1rem 0 0; font-size: .85rem; color: var(--success, #15803d); font-weight: 600; }
.pay-method-networks { display: block; margin-top: .15rem; font-weight: 400; font-size: .78rem; color: var(--muted); }
.pay-instructions { padding: var(--pad-card-tight); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2); }
.pay-instructions p:last-child { margin-bottom: 0; }
.pay-instructions-image { display: block; max-width: min(100%, 22rem); height: auto; margin-top: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); }

/* A hairline between rows, none under the last, and the rate hard right
   on its own line once the row is too narrow to share one. */
.select-list button:last-child, .select-list .select-group:last-child button:last-child { border-bottom: 0; }
.select-rate { text-align: right; margin-left: auto; }


/* The table stays a table here. A stack of cards used to replace it, which
   reads well and is the wrong shape: comparing two orders wants rows, and
   the source panel keeps its table at every width. The edges come off
   instead - tighter cells, type at its normal size, the id column pinned by
   .table-pinned. Nothing meaningful is truncated: link and service clip with
   the whole value in their title, every number prints in full. */
@media (max-width: 899px) {
    .orders-table { min-width: 46rem; }
    .orders-table th, .orders-table td { padding-inline: var(--space-2); }
    .orders-table .link-text { max-width: 11rem; }
    .orders-table .service-clamp { max-width: 10rem; }
    .order-progress { min-width: 5.5rem; }
}

/* The home page, section by section. Glass finish throughout, taking its
   colour from the theme tokens so every preset and both modes are handled by
   the palette rather than by a second stylesheet. */
.trust-grid li, .platform-tile, .quote, .faq-item { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md, 1rem); }
.hero-band, .why-section { display: grid; gap: var(--space-6); align-items: center; }
.hero-band-copy h2 { margin-top: 0; }
.hero-art { display: flex; justify-content: center; color: var(--accent); }
.hero-art img, .hero-default, .why-art img { width: 100%; height: auto; border-radius: var(--radius-lg, 1.2rem); }
.hero-art img, .hero-default { max-width: 26rem; }
.trust-grid, .platform-tiles, .quote-grid, .why-list, .faq-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.trust-grid { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); text-align: center; }
.trust-grid li { padding: var(--pad-card-tight); }
.trust-grid strong { display: block; font-size: 1.5rem; line-height: 1.1; }
.trust-grid span { color: var(--muted); font-size: .85rem; }
.platform-tiles { grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); }
.platform-tile { display: flex; align-items: center; gap: var(--space-2); padding: var(--pad-card-tight); font-weight: 600; text-decoration: none; color: inherit; }
.platform-tile:hover, .platform-tile:focus-visible { border-color: var(--accent); }
.pay-names { list-style: none; margin: var(--space-3) 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.pay-names li { padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--button-radius); background: var(--surface-2); font-size: .85rem; font-weight: 700; }
.why-list { gap: var(--space-4); }
.why-list h3 { margin: 0 0 .2rem; }
.quote-grid { grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: var(--space-4); }
.quote { margin: 0; padding: var(--pad-card); height: 100%; display: flex; flex-direction: column; gap: var(--space-3); }
.quote blockquote { margin: 0; flex: 1; }
.quote figcaption { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: baseline; font-size: .85rem; }
.faq-list { gap: var(--space-2); }
.faq-item { padding: var(--pad-card-tight); }
.faq-item > summary { cursor: pointer; font-weight: 600; }
.faq-item[open] > summary { margin-bottom: var(--space-2); }
.final-cta { text-align: center; }
.final-cta .cta-row { justify-content: center; }
@media (min-width: 48rem) {
  .hero-band { grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); }
  .why-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.post-answer { font-size: 1.08rem; font-weight: 600; margin: 0 0 var(--space-4); }
.post-hero { width: 100%; height: auto; border-radius: var(--radius-lg, 1.2rem); margin-bottom: var(--space-4); }
/* Where a post sends a reader next. Set apart from the prose so it
   reads as a signpost rather than as another paragraph. */
.post-next { margin: var(--space-5) 0 0; font-weight: 600; }
.post-siblings { margin-top: var(--space-6); }
.post-siblings h2 { margin: 0 0 var(--space-2); font-size: 1.05rem; }
.post-siblings ul { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--space-2); }
/* PASSKEYS. One switch, the devices under it, and the button beside the
   password form. Nothing here renders at all on a browser that cannot do
   the ceremony, so none of it needs a disabled state. One column no wider
   than the card: sized to the device list, it ran off a phone. Its password
   boxes take the .field rule. */
.passkeys { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
.passkeys h3 { margin: 0; }
.passkey-switch { display: grid; gap: var(--gap-field); }
.passkey-add { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; margin-top: var(--space-2); }
.passkeys td form { display: grid; gap: var(--space-2); justify-items: start; min-width: 10rem; }
.passkey-signin { display: grid; gap: var(--space-2); justify-items: center; margin: var(--space-3) 0; }
.passkey-button { display: inline-flex; align-items: center; gap: var(--space-2); }
