/*
 * Platform admin. Its own look, deliberately unrelated to any storefront
 * palette or to the supply panel: a light slate workspace with a deep teal
 * sidebar and a teal accent, plus a dark variant that follows the system.
 */

:root {
    --bg: #f3f5f7; --surface: #ffffff; --surface-2: #eef2f5; --text: #0f1b24; --muted: #5b6b7a;
    --border: #d9e0e6; --accent: #0f766e; --accent-soft: rgba(15, 118, 110, .12); --accent-ink: #ffffff;
    --side-bg: #0d2a33; --side-text: #cfe3e6; --side-active: rgba(255, 255, 255, .1);
    --success: #15803d; --success-soft: rgba(21, 128, 61, .12); --danger: #b42318; --danger-soft: rgba(180, 35, 24, .12);
    --warning: #b45309; --warning-soft: rgba(180, 83, 9, .14); --info: #1d4ed8; --info-soft: rgba(29, 78, 216, .12);
    /* THE RADII, and they are deliberately generous. Both admins wear the
       glass treatment, and glass with a tight corner reads as a pane of
       something cut with scissors: the softness is what makes a panel look
       lit rather than clipped. --radius matches --glass-radius exactly, so a
       panel that floats and a panel that does not are the same shape.
       Anything short and pressable is a pill; anything you type into is
       rounded to match the panel it sits in, not to a circle, because a
       pill-shaped text field puts the first character on a curve. */
    --radius: 18px; --radius-sm: 12px; --radius-pill: 999px; --radius-lg: var(--glass-radius-lg, 24px);
    --shadow: 0 1px 2px rgba(15, 27, 36, .06), 0 6px 20px rgba(15, 27, 36, .06);
    --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) {
    :root {
        --bg: #0e1418; --surface: #151d23; --surface-2: #1c262d; --text: #e8eef2; --muted: #9fb0bd;
        --border: #2a3740; --accent: #2dd4bf; --accent-soft: rgba(45, 212, 191, .16); --accent-ink: #062a27;
        --side-bg: #0a1e25; --side-text: #b9d1d6; --side-active: rgba(255, 255, 255, .08);
        --success: #4ade80; --success-soft: rgba(74, 222, 128, .16); --danger: #f87171; --danger-soft: rgba(248, 113, 113, .16);
        --warning: #fbbf24; --warning-soft: rgba(251, 191, 36, .16); --info: #60a5fa; --info-soft: rgba(96, 165, 250, .16);
        --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 6px 20px rgba(0, 0, 0, .35);
        color-scheme: dark;
    }
}

*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font-family: var(--font); font-size: 15px; line-height: 1.5; color: var(--text); background: var(--bg); }
/* No underline in any state; see the note in storefront.css. */
a { color: var(--accent); text-decoration: none; }
a:hover, a:focus, a:active, a:visited { text-decoration: none; }
/* Never a button: --accent-strong is not defined anywhere, so this falls
   back to --accent, which is exactly the accent button hover background. */
a:hover:not(.button), a:focus-visible:not(.button) { color: var(--accent-strong, var(--accent)); }
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 { margin: 0 0 var(--space-2); line-height: 1.2; letter-spacing: -.01em; }
h1 { font-size: 1.35rem; }
h2 { font-size: 1.05rem; }
p { margin: 0 0 var(--space-3); }
/* A word with no space in it - a DNS record, a verification token, an API key
   - is wider than a phone, and a paragraph holding one used to push the page
   sideways rather than break it. break-word rather than anywhere: it only
   breaks a word that cannot fit on a line of its own. */
p, li, dd, .hint, .muted { overflow-wrap: break-word; }
/* A pressable thing never sits flush under the sentence that explains it.
   This is the rule the complaint was about, in one place rather than on each
   screen that showed it. */
p + .button, p + .form-actions, p + .cta-row, p + form, .muted + .button { margin-top: var(--gap-stack); }
/* And a new section never starts flush under the form that ended the last
   one: the heading is what tells a reader the subject changed, and it cannot
   do that while it is touching the thing above it. */
form + h2, form + h3, .panel > form + .panel-head { margin-top: var(--gap-cards); }
ul { margin: 0; padding: 0; list-style: none; }
svg, [data-lucide] { width: 1.1em; height: 1.1em; vertical-align: -.2em; flex: none; }
.muted { color: var(--muted); }
.small { font-size: .85rem; }
.mono { font-family: var(--mono); font-size: .9em; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.truncate { max-width: 16rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.is-negative { color: var(--danger); }
.is-positive { color: var(--success); }
.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(--accent); color: var(--accent-ink); padding: var(--space-2) var(--space-4); z-index: 100; }
.skip-link:focus { left: var(--space-2); top: var(--space-2); }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* shell */
.admin { display: grid; grid-template-columns: minmax(0, 1fr); min-height: 100vh; }
.sidebar { display: none; width: 16rem; background: var(--side-bg); color: var(--side-text); flex-direction: column; padding: var(--pad-page); z-index: 30; }
.sidebar-brand { display: flex; align-items: center; gap: var(--space-2); color: #fff; font-weight: 800; padding: var(--space-2) var(--space-2) var(--space-5); }
.sidebar-brand:hover { text-decoration: none; }
.brand-dot { width: .75rem; height: .75rem; border-radius: var(--radius-pill); background: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft); }
/* Pills, like the drawer's rows on a phone: a shop owner meets both and one
   habit should serve for both. */
.sidebar-nav a, .sidebar-fold > summary { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius-pill); color: var(--side-text); font-weight: 600; }
.sidebar-nav a:hover, .sidebar-fold > summary:hover, .sidebar-fold[open] > summary { background: var(--side-active); text-decoration: none; color: #fff; }
/* Filled, the way the phone drawer marks its own active row. The old mark
   was a three-pixel bar inset on the left edge, which a square corner wore
   well and a pill does not: the bar follows the curve and draws a crescent
   against the outside of the shape. */
.sidebar-nav a.is-active { background: var(--accent); color: var(--accent-ink); }
.sidebar-nav a.is-active:hover { background: var(--accent); color: var(--accent-ink); }
.sidebar-nav a .nav-count { margin-left: auto; min-width: 1.4rem; padding: .05rem var(--space-2); border-radius: var(--radius-pill); background: var(--accent); color: var(--accent-ink); font-size: .72rem; font-weight: 700; line-height: 1.4; text-align: center; }
.sidebar-foot { margin-top: auto; padding: var(--space-3) var(--space-2) 0; border-top: 1px solid rgba(255, 255, 255, .08); }
.sidebar-foot .muted { color: rgba(207, 227, 230, .7); }
.admin-name { font-weight: 700; color: #fff; margin-bottom: var(--space-2); }
.sidebar-foot .button { color: var(--side-text); border-color: rgba(255, 255, 255, .15); background: transparent; }
.content { min-width: 0; }
.topbar { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; gap: var(--gap-inline); padding: var(--space-3) var(--pad-page); background: var(--surface); border-bottom: 1px solid var(--border); }
.topbar-title { flex: 1; margin: 0; }
.topbar-actions { display: flex; gap: var(--gap-inline); align-items: center; flex-wrap: wrap; }
.main { padding: var(--space-5) var(--pad-page) var(--space-8); display: grid; gap: var(--gap-cards); align-content: start; }
@media (min-width: 900px) {
    .admin { grid-template-columns: 16rem minmax(0, 1fr); }
    /* It scrolls itself when its menu is taller than the window: stuck at
       the top and exactly the window's height, it could not be scrolled by
       anything, and on a 1366 by 900 screen the shop admin's "Sign out" at
       its foot sat below the bottom edge where no one could press it. */
    .sidebar { position: sticky; top: 0; height: 100vh; display: flex; overflow-y: auto; overscroll-behavior: contain; }
    .menu-button { display: none; }
    .main { padding: var(--space-6) var(--pad-page-wide) var(--space-8); }
}

/* One click per platform on the unmapped list: a row of small icon buttons,
   each the brand mark it would assign. */
.assign-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.assign-button { padding: var(--space-2); }
.brand-mark { flex: none; display: inline-block; vertical-align: -.15em; }
.platform-sprite { display: none; }

/* buttons, badges, flashes */
.button { display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-4); border-radius: var(--radius-pill); font-weight: 600; font-size: .95rem; border: 1px solid var(--border); background: var(--surface); color: var(--text); cursor: pointer; line-height: 1.2; }
.button:hover { text-decoration: none; background: var(--surface-2); }
.button-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.button-primary:hover { filter: brightness(1.05); background: var(--accent); }
.button-primary, .button-primary:hover, .button-primary:focus, .button-primary:focus-visible, .button-primary:active { color: var(--accent-ink); }
.button-danger { background: var(--danger-soft); border-color: transparent; color: var(--danger); }
.button-ghost { background: transparent; }
.button-small { padding: var(--space-2) var(--space-3); font-size: .85rem; }
.button.is-disabled { opacity: .5; pointer-events: none; }
.icon-button { display: inline-flex; align-items: center; justify-content: center; width: 2.4rem; height: 2.4rem; border: 1px solid var(--border); background: var(--surface); color: var(--text); border-radius: var(--radius-pill); cursor: pointer; }
.text-link { display: inline-flex; align-items: center; gap: var(--space-1); font-weight: 600; }
.badge { display: inline-block; padding: var(--space-1) var(--space-2); border-radius: var(--radius-pill); font-size: .78rem; font-weight: 700; background: var(--surface-2); color: var(--muted); }
/* Where the catalogue stands, then Sync now: one line when they fit. */
.catalogue-status { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4); margin-block: var(--space-3); padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); }
.catalogue-status p { margin: 0; flex: 1 1 24rem; }
/* Named rather than left to the default. It rendered correctly only because
   .badge's own colours happen to be these, so one edit to .badge would have
   changed a "not verified" badge silently and nothing would have caught it. */
.badge-neutral { background: var(--surface-2); color: var(--muted); }
/* The words are the state colour pulled towards the text colour: the plain
   green and amber on their own tint measured 4.3 and 4.1 to 1, under the
   4.5 small text needs. Towards the text colour works in both modes. */
.badge-success { background: var(--success-soft); color: color-mix(in srgb, var(--success) 78%, var(--text)); }
.badge-danger { background: var(--danger-soft); color: color-mix(in srgb, var(--danger) 85%, var(--text)); }
.badge-warning { background: var(--warning-soft); color: color-mix(in srgb, var(--warning) 70%, var(--text)); }
.badge-info { background: var(--info-soft); color: color-mix(in srgb, var(--info) 85%, var(--text)); }
.flash { display: flex; gap: var(--space-2); align-items: flex-start; padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); border: 1px solid transparent; }
.flash ul { padding-left: 0; }
.flash-success { background: var(--success-soft); color: color-mix(in srgb, var(--success) 78%, var(--text)); border-color: var(--success-soft); }
.flash-error { background: var(--danger-soft); color: color-mix(in srgb, var(--danger) 85%, var(--text)); border-color: var(--danger-soft); }

/* panels, stats, tables */
.panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--pad-card); box-shadow: var(--shadow); }
.panel.narrow { max-width: 52rem; }
.panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--gap-inline); margin-bottom: var(--gap-form); }
.panel-head + .panel-head { margin-top: var(--space-5); }
.stat-grid { display: grid; gap: var(--gap-cards); grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); }
.stat { position: relative; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--pad-card); box-shadow: var(--shadow); }
.stat.is-attention { border-color: var(--warning); }
.stat-icon { position: absolute; right: var(--space-4); top: var(--space-4); color: var(--accent); background: var(--accent-soft); width: 2.25rem; height: 2.25rem; border-radius: var(--radius-pill); display: inline-flex; align-items: center; justify-content: center; }
.stat-label { font-size: .8rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); font-weight: 700; margin-bottom: var(--space-1); }
.stat-value { font-size: 1.8rem; font-weight: 800; margin: 0 0 var(--space-1); font-variant-numeric: tabular-nums; }
.stat-list li, .plain-list li { padding: var(--space-2) 0; border-bottom: 1px solid var(--border); display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
.stat-list li:last-child, .plain-list li:last-child { border-bottom: 0; }
.two-col { display: grid; gap: var(--gap-cards); }
@media (min-width: 1100px) { .two-col { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); } }
/* A grid item is as wide as its content unless told otherwise, so one wide
   table used to stretch the whole page and push the actions off a phone
   screen. The panel keeps the column width; the table scrolls inside it. */
.panel { min-width: 0; }
/* EVERY DIRECT CHILD OF THE PAGE, not just the panels.

   A grid item is as wide as its content unless it is told otherwise, and
   .panel was the only thing told. The services screen puts its table inside a
   bare <form>, and the form - not the panel - is the grid item, so a 62rem
   table dragged the whole document to 977px inside a 380px phone. Everything
   fixed to the window then stretched with it: the tab bar ran off the screen
   and took three of its five tabs with it.

   One rule, on the page's own children, so the next screen that wraps a table
   in something new cannot bring the fault back. */
.main > * { min-width: 0; }
/* And the scroller itself never grows past what holds it.

   .table-wrap is the same job under a second name - ten views use it - and it
   had no rule at all beyond one margin, so the tables inside it pushed the
   page sideways instead of scrolling. Both names, one behaviour, rather than
   a rename that would leave the next view using whichever it remembered. */
/* position: relative as well, so that anything absolutely positioned inside a
   wide table is placed against the scroller and clipped by it. Without it, a
   screen-reader-only span in the last column of a 987px table was laid out at
   x=976 against the page itself and dragged the document sideways on a phone -
   a one-pixel invisible element making the whole withdrawals screen scroll. */
.table-scroll, .table-wrap { position: relative; overflow-x: auto; min-width: 0; max-width: 100%; }
/* THE FIRST COLUMN STAYS PUT WHILE THE REST SCROLLS. An order number is how
   a reader knows which row they are reading, and it is the first thing to
   leave the screen without this. Both admin shells use it; the storefront
   has its own copy of the same three rules, because the two sheets share no
   ancestor and one importing the other for three lines would be worse. */
.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); }
/* The row's own controls, pinned to the right the way the id is pinned to the
   left. The order book carries fifteen columns and the options column sat
   past the right edge of a laptop: a shop had to scroll the table sideways to
   reach the one control on the row that does anything. Found by driving the
   page rather than by reading it. */
.table-pinned .col-options { position: sticky; right: 0; z-index: 1; background: var(--surface); }
.table-pinned thead .col-options { z-index: 2; }
.table-pinned .col-options::after { content: ""; position: absolute; top: 0; left: 0; bottom: 0; width: 1px; background: var(--border); }

/* HOW FAR AN ORDER HAS GOT. Same shape and the same two meanings as the
   storefront's: a finished order fills green, a stopped one amber. */
.order-progress { display: block; min-width: 7rem; }
.order-progress .progress { display: block; height: .35rem; border-radius: var(--radius-pill); background: var(--surface-2); overflow: hidden; }
.order-progress .progress-bar { display: block; height: 100%; background: var(--accent); }
.order-progress.is-done .progress-bar { background: var(--success); }
.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-1); }
.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 link, clipped, with a small arrow that opens it in a new tab. The
   arrow is its own anchor rather than the whole cell, so selecting the 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 { display: block; max-width: 16rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.link-open { flex: none; display: inline-flex; color: var(--muted); }
.link-open:hover, .link-open:focus-visible { color: var(--accent); }
table { width: 100%; border-collapse: collapse; font-size: .92rem; }
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; }
th.num { text-align: right; }
td { padding: var(--space-3); border-bottom: 1px solid var(--border); vertical-align: top; }
tbody tr:hover td { background: var(--surface-2); }
td.actions { white-space: nowrap; text-align: right; }
form.inline { display: inline-block; margin-left: var(--space-2); }
.empty { padding: var(--space-6); text-align: center; color: var(--muted); border: 1px dashed var(--border); border-radius: var(--radius-sm); }
.tabs { display: flex; gap: var(--space-1); border-bottom: 1px solid var(--border); margin-bottom: var(--gap-cards); overflow-x: auto; }
.tabs a { padding: var(--space-3) var(--space-4); color: var(--muted); font-weight: 600; border-bottom: 2px solid transparent; white-space: nowrap; }
.tabs a.is-active { color: var(--accent); border-bottom-color: var(--accent); }
.tabs a:hover { text-decoration: none; color: var(--text); }
.pagination { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-inline); margin-top: var(--gap-stack); flex-wrap: wrap; }
/* The numbered pager, shared by both consoles because both load this sheet.
   Five numbers at most; page_numbers() decides which. */
.pagination-numbers { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); }
.page-num { display: inline-block; min-width: 2rem; padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-pill); background: var(--surface); color: var(--text); font-weight: 700; font-size: .85rem; text-align: center; }
/* Beats a:hover:not(.button), which would otherwise recolour the number. */
a.page-num:hover, a.page-num:focus-visible { background: var(--surface-2); color: var(--text); text-decoration: none; }
.page-num.is-current { background: var(--surface-2); border-color: var(--accent); }
.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; }

/* forms */
/* minmax(0, 1fr), not the default. A grid track is minmax(auto, 1fr), and
   that auto minimum is the item's own min-content width - so one unbreakable
   word inside a form made the item wider than the column holding it. The DNS
   verification token on the domain screen did exactly that: a 454px
   paragraph inside a 306px panel, and the page scrolled sideways on a phone.
   The storefront's .stack has carried this for months; the admins never got
   it. */
.stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap-form); }
.form-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap-form); }
@media (min-width: 720px) { .form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .span-2 { grid-column: 1 / -1; } }
.field { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap-field); font-weight: 600; font-size: .9rem; }
/* Text inputs and selects. A checkbox or a radio is excluded by name:
   the rule is about boxes you type into, and a tick given width 100%,
   a border and .6rem of padding stretches across its row and shoves its
   own label off the end. That is what the forget-this-value ticks under
   the WhatsApp credentials were doing. */
.field input:not([type="checkbox"]):not([type="radio"]), .field select { font: inherit; font-weight: 400; padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); width: 100%; }
.field input[type="checkbox"], .field input[type="radio"] { accent-color: var(--accent); margin: 0; flex: none; }
.input-group { display: flex; align-items: stretch; }
.input-group input { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
.input-suffix { display: inline-flex; align-items: center; padding: 0 var(--space-3); border: 1px solid var(--border); border-left: 0; border-radius: 0 var(--radius-sm) var(--radius-sm) 0; background: var(--surface-2); color: var(--muted); font-weight: 400; white-space: nowrap; }
.field-error { color: var(--danger); font-weight: 600; margin: 0; }
.check { display: flex; gap: var(--space-2); align-items: center; font-size: .9rem; }
/* THE ROW OF BUTTONS AT THE END OF A FORM. The gap is what stops two of them
   reading as one control under a thumb, and the margin is the rule about a
   button never sitting flush under the thing above it: the last field, a
   paragraph, a table. It is here rather than on each form, because every form
   in both admins ends in one of these. */
.form-actions { display: flex; gap: var(--gap-inline); flex-wrap: wrap; margin-top: var(--gap-stack); }
/* Inside a grid or a stack that already spaces its children, the gap has
   done the work and the margin would double it. */
.stack > .form-actions, .form-grid > .form-actions, .field + .form-actions { margin-top: 0; }
/* The same row, under its other name. `.actions` is a table cell everywhere
   but the domain screen, where it wraps two forms and had no rule at all -
   so Check now and Remove this domain stacked as two blocks with nothing
   between them, which is what a form is without a rule. */
:not(td).actions { display: flex; gap: var(--gap-inline); flex-wrap: wrap; align-items: center; margin-top: var(--gap-stack); }
/* AND THE SAFETY NET. Two pressable things one under the other can never
   touch, whoever writes the markup and whatever they wrap it in. Inline forms
   are excluded by name: they sit in a table cell beside a button, where a top
   margin would push one control below its own row. :where() keeps this the
   weakest rule: as :not() it outranked both exceptions, and Remove this
   domain sat 8px below Check now. */
form:where(:not(.inline)) + form:where(:not(.inline)),
form:where(:not(.inline)) + .button,
.button + form:where(:not(.inline)) { margin-top: var(--gap-stack); }
/* Except inside a row that is already spacing them sideways. */
.actions form, .form-actions form, .cta-row form, .toolbar form { margin-top: 0; }

/* login */
.auth-divider { display: flex; align-items: center; gap: var(--gap-inline); color: var(--muted); font-size: .85rem; margin: var(--gap-stack) 0 var(--space-3); }
.auth-divider::before, .auth-divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.button-google { width: 100%; justify-content: center; }
.login-page { display: grid; grid-template-columns: minmax(0, 1fr); place-items: center; padding: var(--space-6); }
.login-card { width: 100%; max-width: 26rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-7); box-shadow: var(--shadow); }
.eyebrow { text-transform: uppercase; letter-spacing: .12em; font-size: .72rem; font-weight: 800; color: var(--accent); margin-bottom: var(--space-2); }

/* notifications bell and list */
.bell { position: relative; }
.bell-button { position: relative; }
.bell-count { position: absolute; top: -.35rem; right: -.35rem; min-width: 1.25rem; height: 1.25rem; padding: 0 .3rem; border-radius: var(--radius-pill); background: var(--danger); color: var(--accent-ink); font-size: .7rem; font-weight: 800; display: inline-flex; align-items: center; justify-content: center; line-height: 1; }
.bell-menu { position: absolute; right: 0; top: calc(100% + var(--space-2)); width: min(24rem, 90vw); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); z-index: 40; }
.bell-head, .bell-foot { display: flex; justify-content: space-between; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-4); }
.bell-head { border-bottom: 1px solid var(--border); }
.bell-foot { border-top: 1px solid var(--border); }
.bell-empty { padding: var(--space-4); margin: 0; }
.bell-list { max-height: 22rem; overflow-y: auto; }
.bell-list li { border-bottom: 1px solid var(--border); }
.bell-list li.is-unread { background: var(--accent-soft); }
.bell-item { display: grid; gap: var(--space-1); width: 100%; text-align: left; padding: var(--space-3) var(--space-4); background: transparent; border: 0; font: inherit; color: var(--text); cursor: pointer; }
.bell-title { font-weight: 700; }
.bell-text { color: var(--muted); font-size: .9rem; }
.notification-list { display: grid; gap: var(--space-3); }
.notification { display: flex; justify-content: space-between; gap: var(--gap-inline); align-items: flex-start; padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.notification.is-unread { border-left: 3px solid var(--accent); }
.notification-title { font-weight: 700; margin: 0 0 var(--space-1); }
.notification-text { margin: 0 0 var(--space-1); }
/* The body of a notification card. Unstyled, it kept its last paragraph's
   bottom margin, so every card was a step taller at the bottom than the top,
   and as a flex child with an automatic minimum a long link pushed Open off
   the end of the card. */
.notification-body { min-width: 0; }
.notification-body > :last-child { margin-bottom: 0; }
[x-cloak] { display: none !important; }
.icon { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
body.is-locked { overflow: hidden; }
.inline-form { display: flex; gap: var(--gap-inline); align-items: end; flex-wrap: wrap; }
.inline-form input[type="text"] { font: inherit; padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); min-width: 12rem; }
.toolbar { display: flex; gap: var(--gap-inline); align-items: end; flex-wrap: wrap; margin: 0 0 var(--gap-form); }
.toolbar .field { min-width: 10rem; }
.audit-json { display: inline-block; max-width: 22rem; white-space: pre-wrap; word-break: break-all; font-size: .78rem; background: var(--surface-2); padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); }

/* 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); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); cursor: pointer; text-align: left; }
.select-button .select-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.select-button .is-placeholder { color: var(--muted); }
.select-button .select-caret { flex: none; transition: transform .15s ease; }
.select-button[aria-expanded=true] .select-caret { transform: rotate(90deg); }
.select-panel { position: absolute; z-index: 60; left: 0; right: 0; top: calc(100% + var(--space-1)); 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(--border); border-radius: var(--radius-sm); background: var(--bg); 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-2); 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; }
.select-list button .select-hint { flex: none; white-space: nowrap; }
/* A service picker: the shop's number as a badge, and what found the row
   on a line of its own - a phrase, where other pickers' hints are codes. */
.id-badge { flex: none; padding: 0 var(--space-2); border-radius: var(--radius-sm); background: var(--surface-2); color: var(--muted); font-size: .78rem; font-weight: 700; }
.service-select .select-list button .select-hint { flex: 1 1 100%; white-space: normal; }
.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: 0; font-weight: 400; }

.badge-best { background: var(--accent); color: var(--accent-ink); }
.toolbar-select { min-width: 16rem; }

/* KYC review: label and value pairs, with room for a multi-line payout detail */
.detail-list { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-2) var(--space-4); margin: 0 0 var(--gap-form); }
.detail-list dt { color: var(--muted); font-size: .85rem; }
.detail-list dd { margin: 0; }
.prewrap { white-space: pre-wrap; overflow-wrap: anywhere; }
.check.small { font-size: .8rem; margin-top: var(--space-2); }

/* Language menu and the thin admin footer that carries it */
.locale-menu { display: inline-flex; align-items: center; gap: var(--space-2); }
.locale-menu .locale-globe { flex: none; color: var(--muted); }
.locale-menu .searchable-select { min-width: 9rem; }
.locale-menu .select-button { padding: var(--space-2) var(--space-3); font-size: .9rem; }
.locale-form { margin: 0; }
.admin-footer { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-inline); flex-wrap: wrap; margin-top: var(--space-7); padding-top: var(--gap-stack); border-top: 1px solid var(--border); }

/* ---------- liquid glass ----------
   Tokens and fallbacks in glass.css. The chrome and the numbers float; the
   panels that hold tables and forms do not, and .panel is deliberately
   absent from this list. */

.stat,
.checkout-card,
.modal-panel,
.bell-menu,
.select-panel,
.login-card {
    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);
    border-radius: var(--glass-radius);
}

/* A layer over the page is rounder than a panel on it. The two are not the
   same object: one is a sheet of glass lying on the page and the other is a
   card held above it, and the corner is most of what says which. */
.modal-panel,
.login-card {
    border-radius: var(--radius-lg);
}

/* A dialog sits over a dimmed page, so its glass is solid: see-through
   over the dark layer it turned grey, and the small print in a payment
   method's dialog fell to 3.5 to 1. The gloss and the edge stay. */
.modal-panel { --glass-bg: var(--surface); }

/* A box with a backdrop-filter is the backdrop root of everything inside
   it, and a list that opens out of it can then blur nothing but that box:
   the page under the list shows through sharp. The language pill holds such
   a list, so it takes the glass colour and edge without a filter of its
   own, and the top bar, which holds the bell and its menu, paints its glass
   on a layer behind its content. */
.locale-menu {
    background-color: var(--glass-bg);
    background-image: var(--glass-image);
    border: 1px solid var(--glass-edge);
    box-shadow: var(--glass-shadow);
    border-radius: var(--glass-radius);
}

.sidebar {
    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);
}

.topbar {
    background: none;
    border: 1px solid var(--glass-edge);
    box-shadow: var(--glass-shadow);
}

.topbar::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);
}

/* A BUTTON HAS TO LOOK PRESSABLE.

   It had the gloss and nothing else: the same fill as the panel behind it and
   a hairline border, which on a dark screen is a rectangle of text with a
   faint outline. Two of them side by side read as one grey slab.

   So a button now wears the whole treatment the rest of the glass does - a
   fill a step up from the panel it sits on, the lit edge, and a shadow under
   it - which is what says "this lifts off the page and can be pressed". The
   colours do not move: this is the same surface and the same accent the two
   consoles already use. */
.button {
    background-color: var(--surface-2);
    background-image: var(--glass-image);
    border-color: var(--glass-edge);
    box-shadow: var(--glass-shadow);
}

/* Lifts further under the finger rather than changing colour. */
.button:hover {
    background-color: var(--surface);
    box-shadow: 0 1px 2px rgba(16, 24, 40, .06), 0 14px 32px rgba(16, 24, 40, .16);
}

/* Ghost is the quiet one, not the invisible one: it keeps the edge and the
   shadow and gives up only the fill. */
.button-ghost {
    background-color: color-mix(in srgb, var(--surface-2) 60%, transparent);
}

/* The one that carries the accent keeps it, with the gloss on top. */
.button-primary,
.button-primary:hover {
    background-color: var(--accent);
    border-color: var(--accent);
}

.button-danger {
    background-color: var(--danger-soft);
    border-color: color-mix(in srgb, var(--danger) 35%, transparent);
}

/* Inside the side menu the button sits on the menu's own dark colour, where
   a page-surface fill would be a white slab. */
.sidebar-foot .button {
    background-color: color-mix(in srgb, #fff 8%, transparent);
    border-color: rgba(255, 255, 255, .18);
}

.sidebar-foot .button:hover {
    background-color: color-mix(in srgb, #fff 14%, transparent);
}

.panel,
table {
    background-image: none;
}

/* 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; }

/* One country per row, several rows across, so 66 of them fit a screen. */
.country-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); gap: var(--space-2) var(--space-4); margin: var(--space-2) 0; }
.country-check { align-items: baseline; }

/* A payment method card, in the shape the parent draws one: a dot that says
   on or off, the icon, the customer-facing name, and the range under it. */
.method-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.method-row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.method-dot { flex: none; width: .6rem; height: .6rem; border-radius: var(--radius-pill); background: var(--danger, #b91c1c); }
.method-dot.is-on { background: var(--success, #15803d); }

/* THE PAYMENT METHOD GRID.

   The source panel's own card, at our own scale: a status dot that is a
   button, a handle that is the only drag target, the logo, the name a
   customer reads, the amounts, and one Edit. Dense on purpose - forty
   methods on two screens rather than forty - and one column on a phone,
   where a grid of cards is just a list with extra steps. */
.method-grid-tools { display: flex; align-items: center; gap: var(--gap-inline); margin-bottom: var(--gap-form); flex-wrap: wrap; }
.method-search { flex: 1 1 18rem; margin: 0; }
/* A SEARCH BOX WITH ITS MAGNIFIER INSIDE IT. The wrapper is in the markup of
   both admins and had no rule anywhere, so the icon rendered as a line of its
   own sitting on top of the field: an orphan glyph nobody drew on purpose.
   Absolute inside the box, and the box padded to make room for it, so the
   placeholder never runs under the icon. */
.input-with-icon { position: relative; display: block; }
.input-with-icon > svg, .input-with-icon > [data-lucide] { position: absolute; top: 50%; left: var(--space-3); transform: translateY(-50%); color: var(--muted); pointer-events: none; }
.input-with-icon input:not([type="checkbox"]):not([type="radio"]) { padding-left: calc(var(--space-3) + 1.1em + var(--space-2)); }
.method-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(13.5rem, 1fr)); gap: var(--gap-form); list-style: none; margin: 0; padding: 0; }
/* Even on all four edges. It was .85 at the top, .8 at the sides and .6 at
   the bottom, which is what put the Edit button hard against the bottom of
   the card while the name floated. */
/* A column rather than fixed rows, so the Edit button can be pushed to the
   bottom: one card carrying a Paused badge used to sit its button lower than
   every other card in the row, and a row of buttons at four different heights
   reads as a broken grid. */
.method-card { position: relative; display: flex; flex-direction: column; gap: var(--space-2); padding: var(--pad-card-tight); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); text-align: center; }
.method-card.is-off { opacity: .72; }
.method-card.is-dragging { outline: 2px dashed var(--accent); opacity: .6; }
.method-card.is-recommended { border-color: var(--accent); }
.method-card .method-dot { position: absolute; top: var(--space-2); left: var(--space-2); width: .7rem; height: .7rem; padding: 0; border: 0; cursor: pointer; }
.method-card .method-handle { position: absolute; top: var(--space-2); right: var(--space-2); display: inline-flex; padding: var(--space-1); border: 0; background: none; color: var(--muted); cursor: grab; }
.method-card .method-handle:active { cursor: grabbing; }
.method-card .method-logo { display: flex; justify-content: center; align-items: center; height: 1.9rem; color: var(--text); }
/* flex: none beats the .method-name rule further down, which grows the name
   to fill a row - right inside .method-row, wrong inside a card, where it
   would eat the free space and leave a hole under the name. */
.method-card .method-name { flex: none; font-weight: 600; font-size: .92rem; line-height: 1.25; overflow-wrap: anywhere; }
.method-card .method-range { font-size: .78rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.method-card .method-gateway { font-size: .74rem; color: var(--muted); }
.method-card .badge-best { align-self: center; }
/* auto pushes it to the bottom so every Edit in a row lines up; the card's
   own gap keeps the divider off the last line of text, and the padding keeps
   the button off the divider. */
.method-card .method-foot { margin-top: auto; padding-top: var(--space-3); border-top: 1px solid var(--border); }
@media (max-width: 520px) {
    .method-grid { grid-template-columns: minmax(0, 1fr); }
}

/* The badges a gateway card carries: what mode it is in, and whether it has
   keys and has ever settled anything. */
.method-card .method-tags { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2); margin-top: var(--space-2); }

/* A layer over the page: the same two names the storefront uses, because a
   shop owner meets both and one habit should serve for both. The panel takes
   its border, radius and shadow from the glass list above. */
/* max-width, because inset: 0 is not enough on its own: a layer pinned to the
   window takes the width of whatever box is holding it, and on a phone that
   came out 977px inside 380px - the dialog then sat off the right of the
   screen with the page scrolled sideways behind it. The window is the only
   thing it should ever be as wide as. */
.modal-backdrop { position: fixed; inset: 0; max-width: 100vw; z-index: 80; display: flex; align-items: center; justify-content: center; padding: var(--pad-page); background: rgba(0, 0, 0, .5); }
.modal-panel { position: relative; width: 100%; max-width: 34rem; max-height: 86vh; overflow-y: auto; padding: var(--pad-card); }
.modal-head { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-inline); margin-bottom: var(--gap-form); }
.modal-head h2 { margin: 0; }

/* A fold inside a panel: the per-currency limits under a gateway. Plain on
   purpose, the same as the shop's own folded method forms. */
/* The folded setup guide under a rail a shop runs itself. */
.method-howto > summary { font-weight: 600; }
.howto-steps { margin: var(--space-3) 0 var(--space-1); padding-left: 1.2rem; display: grid; gap: var(--space-3); }
.howto-steps ul { margin: var(--space-2) 0 0; padding-left: 1.1rem; display: grid; gap: var(--space-1); }
.howto-steps .muted { display: block; }

/* The shop's own asks, under the form that makes them. */
.reach-heading { margin-top: var(--gap-cards); }
/* Several shops' asks share one panel, so each needs its own separation or
   the next shop's heading sits on the previous shop's Refuse form and two
   shops read as one. */
.rail-request + .rail-request { margin-top: var(--gap-cards); padding-top: var(--gap-cards); border-top: 1px solid var(--border); }
/* A stack already spaces its children; the form-after-form safety net above
   would otherwise add its margin on top and double the gap. */
.stack > form + form, .stack > form + .button { margin-top: 0; }

details.panel-sub { margin: var(--space-2) 0; }
details.panel-sub > summary { cursor: pointer; padding: var(--space-2) 0; }

/* A gateway's own form, folded until the grid sends somebody to it. */
details.panel > summary { cursor: pointer; }
details.panel > summary::-webkit-details-marker { display: none; }
details.panel[open] > summary { margin-bottom: var(--gap-form); padding-bottom: var(--space-3); border-bottom: 1px solid var(--border); }

.method-name { flex: 1; min-width: 0; font-weight: 600; display: grid; }
.method-range { font-weight: 400; font-size: .8rem; color: var(--muted); }

/* The (i) beside a label. Same shape as the storefront's, because a shop
   owner meets both and one habit should serve for both. */
.info-pop { position: relative; display: inline-flex; }
.info-pop-button { display: inline-flex; align-items: center; justify-content: center; width: 1.4rem; height: 1.4rem; padding: 0; border: 1px solid var(--border); border-radius: var(--radius-pill); background: var(--surface); color: var(--muted); cursor: pointer; }
.info-pop-button:hover, .info-pop-button[aria-expanded=true] { color: var(--text); border-color: var(--accent); }
.info-pop-button svg { width: .85rem; height: .85rem; }
.info-pop-panel { position: absolute; z-index: 70; top: calc(100% + var(--space-2)); 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: .85rem; color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow, 0 8px 24px -12px rgba(0,0,0,.35)); }
.field > span > .info-pop { margin-left: var(--space-1); vertical-align: middle; }

/* Folding groups in the sidebar. The column was one long list, which is a
   list nobody reads to the bottom of; the four screens an operator opens all
   day stay out and the rest fold away. Native details, so it works with no
   javascript and the browser handles the keyboard for us. */
.sidebar-fold { margin: var(--space-1) 0; }
.sidebar-fold > summary { cursor: pointer; list-style: none; }
.sidebar-fold > summary::-webkit-details-marker { display: none; }
.sidebar-fold .fold-chevron { margin-left: auto; transition: transform .18s ease; }
.sidebar-fold[open] .fold-chevron { transform: rotate(180deg); }
/* The glass finish the rest of the panel wears, on the tray that opens. */
.sidebar-fold-body { display: grid; gap: var(--space-1); margin: var(--space-1) 0 var(--space-2) var(--space-2); padding: var(--space-1); border-left: 1px solid var(--side-active); border-radius: var(--glass-radius, 18px); }
.sidebar-fold-body a { padding-left: var(--space-3); font-weight: 500; }
@media (prefers-reduced-motion: reduce) { .sidebar-fold .fold-chevron, .select-button .select-caret { transition: none; } }
/* 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. */
.passkeys { display: grid; gap: var(--space-3); }
.passkeys h2, .passkeys h3 { margin: 0; }
.passkey-switch { display: grid; gap: var(--gap-field); }
.passkey-add { display: flex; flex-wrap: wrap; gap: var(--gap-inline); align-items: center; margin-top: var(--gap-stack); }
.passkey-signin { display: grid; gap: var(--space-2); justify-items: center; margin: var(--gap-stack) 0; }
.passkey-button { display: inline-flex; align-items: center; gap: var(--space-2); }
