

.prov-row { display: grid; grid-template-columns: 1.2fr 1.2fr .7fr 2fr .7fr auto; gap: 8px; align-items: center;
    margin: 0 18px 8px; }
.prov-row input, .prov-row select { padding: 9px 10px; border: 1px solid var(--border-2); border-radius: 8px;
    background: var(--bg-card); color: var(--text); font-family: inherit; font-size: 13px; min-width: 0; }
.prov-row input:focus, .prov-row select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.prov-row .prov-del { padding: 8px 12px; color: var(--danger); }
@media (max-width: 720px) { .prov-row { grid-template-columns: 1fr 1fr; } .prov-row input[name="prov_url[]"] { grid-column: 1 / -1; } }

html:has(body.auth-body) { height:auto; min-height:100vh; overflow:auto; width:auto; }
.auth-body { display:grid; place-items:center; min-height:100vh; height:auto; width:auto; background:var(--bg-root); overflow:auto; padding:24px 0; }
.auth-card { width:420px; max-width:calc(100vw - 32px); background:var(--bg-panel); border:1px solid var(--border);
    border-radius:16px; padding:30px; box-shadow:var(--sh); }

.auth-body.auth-split { display:grid; grid-template-columns:minmax(340px,42%) 1fr; place-items:stretch; padding:0; overflow:hidden; min-height:100dvh; }
.auth-form-side { display:flex; align-items:center; justify-content:center; padding:24px 18px; overflow:auto; min-height:100dvh; }
.auth-split .auth-card { border:none; box-shadow:none; background:transparent; padding:8px; }
.auth-visual { position:relative; overflow:hidden; display:flex; flex-direction:column; padding:40px 44px; color:#fff;
    background:radial-gradient(circle at 82% 62%, color-mix(in srgb, var(--accent) 42%, transparent), transparent 30%),
               linear-gradient(150deg, #0b1220, #0a0f1a); }
.auth-visual:before { content:""; position:absolute; inset-inline-end:-260px; top:-220px; width:480px; height:480px; border-radius:50%;
    border:1px solid rgba(255,255,255,.10); box-shadow:0 0 0 70px rgba(255,255,255,.03), 0 0 0 150px rgba(255,255,255,.02); }
.av-logo { display:flex; align-items:center; gap:12px; text-decoration:none; color:#fff; position:relative; z-index:2; }
.av-logo-ic { width:40px; height:40px; display:inline-flex; align-items:center; justify-content:center; color:#fff; }
.av-logo-ic svg, .av-logo-ic img { width:100%; height:100%; }
.av-logo-tx { display:grid; gap:2px; }
.av-logo-tx b { font-size:15px; } .av-logo-tx small { color:rgba(255,255,255,.5); font-size:10px; }
.av-copy { margin:auto 0 18px; position:relative; z-index:2; max-width:440px; }
.av-secure { display:inline-flex; align-items:center; gap:8px; color:#5fd0a8; font-size:11px; font-weight:800; letter-spacing:.06em; }
.av-secure i { width:8px; height:8px; border-radius:50%; background:#54d0a7; box-shadow:0 0 0 5px rgba(84,208,167,.12); }
.av-copy h1 { margin:18px 0 14px; font-size:clamp(26px,3vw,38px); line-height:1.15; letter-spacing:-.03em; }
.av-copy > p { color:rgba(255,255,255,.62); font-size:13px; line-height:1.8; margin:0 0 22px; }
.av-points { list-style:none; padding:0; margin:0; display:grid; gap:13px; }
.av-points li { display:flex; align-items:center; gap:11px; color:rgba(255,255,255,.86); font-size:13.5px; }
.av-points .av-ic { flex-shrink:0; font-size:18px; }
.av-foot { position:relative; z-index:2; padding-top:20px; border-top:1px solid rgba(255,255,255,.08); color:rgba(255,255,255,.4); font-size:11px; }
@media (max-width:900px) {
  .auth-body.auth-split { grid-template-columns:1fr; }
  .auth-visual { display:none; }
  .auth-split .auth-card { border:1px solid var(--border); box-shadow:var(--sh); background:var(--bg-panel); padding:30px; }
}
.auth-brand { display:flex; gap:14px; align-items:center; margin-bottom:22px; color:var(--accent); }
.auth-title { font-size:17px; font-weight:800; color:var(--text); }
.auth-sub { font-size:11.5px; color:var(--text-mute); margin-top:2px; }
.auth-form label { display:block; font-size:12px; font-weight:700; color:var(--text-dim); margin:14px 0 6px; }
.auth-form input { width:100%; padding:12px; border:1px solid var(--border-2); border-radius:9px; background:var(--bg-card);
    color:var(--text); font-family:inherit; font-size:14px; }
.auth-form input:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.auth-btn { width:100%; margin-top:20px; padding:13px; border:none; border-radius:9px; background:var(--accent); color:#fff;
    font-weight:800; font-size:14px; cursor:pointer; font-family:inherit; transition:.15s; }
.auth-btn:hover { filter:brightness(1.06); }

.auth-actions { display:flex; gap:10px; margin-top:20px; }
.auth-actions .auth-btn { margin-top:0; flex:1; width:auto; }
.auth-btn.ghost { background:var(--bg-card); color:var(--text-dim); border:1px solid var(--border-2); }
.auth-btn.ghost:hover { filter:none; border-color:var(--accent); color:var(--accent); background:var(--accent-soft); }
.auth-link { background:none; border:none; color:var(--accent); font-weight:700; font-size:12.5px; cursor:pointer;
    font-family:inherit; text-decoration:underline; padding:0; }
.auth-error { background:rgba(239,68,68,.12); color:var(--danger); padding:11px 13px; border-radius:9px; font-size:13px; margin-bottom:6px; }
/* complete.php: a sent code, said plainly; the code field; the inline secondary buttons */
.auth-note { background:var(--accent-soft); color:var(--text); padding:11px 13px; border-radius:9px; font-size:13px; line-height:1.8; margin-bottom:6px; }
.auth-code { text-align:center; font-size:22px !important; font-weight:800; letter-spacing:.35em; }
.auth-inline { flex:1; display:flex; }
.auth-inline .auth-btn { margin-top:0; }
.auth-help { display:block; margin-top:22px; line-height:1.9; }

.cap-math { margin:16px 0 4px; }
.cap-math-row { display:flex; align-items:stretch; gap:8px; }
.cap-math-img { flex:0 0 auto; height:46px; display:flex; align-items:center; justify-content:center;
    padding:0 6px; border:1px solid var(--border-2); border-radius:10px; overflow:hidden; background:#f1f5f9; }
.cap-math-img img, .cap-math-img svg { display:block; height:38px; width:auto; }
.cap-math-input { flex:1 1 auto; min-width:0; height:46px; padding:0 12px; border:1px solid var(--border-2); border-radius:10px;
    background:var(--bg-card); color:var(--text); font-family:inherit; font-size:16px; font-weight:700; text-align:center; letter-spacing:3px; }
.cap-math-input::placeholder { font-size:12px; font-weight:500; letter-spacing:0; color:var(--text-mute); }
.cap-math-input:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.cap-math-refresh { flex:0 0 auto; width:46px; height:46px; border:1px solid var(--border-2); border-radius:10px;
    background:var(--bg-card); color:var(--text-dim); font-size:20px; line-height:1; cursor:pointer; transition:.15s; }
.cap-math-refresh:hover { border-color:var(--accent); color:var(--accent); background:var(--accent-soft); }
.cap-math-refresh:active { transform:rotate(180deg); }
.auth-foot { display:flex; justify-content:space-between; margin-top:20px; font-size:12px; }
.auth-foot a { color:var(--text-mute); text-decoration:none; }
.auth-foot a:hover { color:var(--accent); }

.auth-top { display:flex; justify-content:space-between; align-items:center; gap:8px; margin-bottom:6px; }
.auth-langbtn { padding:7px 12px; border-radius:20px; border:1px solid var(--border-2); background:var(--bg-card);
    color:var(--text-dim); font-family:inherit; font-size:11.5px; font-weight:800; cursor:pointer; transition:.15s; }
.auth-langbtn:hover { border-color:var(--accent); color:var(--accent); background:var(--accent-soft); }

.auth-alt { margin-top:16px; text-align:center; font-size:12.5px; color:var(--text-mute); }
.auth-register {
    display:inline-flex; align-items:center; gap:8px; margin-top:10px; width:100%; justify-content:center;
    padding:11px; border-radius:9px; border:1px solid rgba(16,185,129,.5);
    background:rgba(16,185,129,.10); color:var(--success); font-weight:800; font-size:13px; cursor:pointer;
    font-family:inherit; transition:.15s; text-decoration:none;
}
.auth-register:hover { background:var(--success); color:#fff; }

.reg-overlay { position:fixed; inset:0; background:rgba(0,0,0,.55); z-index:100000; display:flex;
    align-items:center; justify-content:center; opacity:0; pointer-events:none; transition:opacity .2s; padding:16px; }
.reg-overlay.show { opacity:1; pointer-events:auto; }
.reg-box { width:440px; max-width:100%; background:var(--bg-panel); border:1px solid var(--border);
    border-radius:16px; padding:26px; box-shadow:0 24px 48px rgba(0,0,0,.35); position:relative;
    transform:translateY(16px); transition:transform .2s; }
.reg-overlay.show .reg-box { transform:none; }
.reg-box h3 { margin:0 0 6px; font-size:18px; color:var(--text); }
.reg-box .reg-sub { color:var(--text-mute); font-size:12.5px; margin-bottom:16px; }
.reg-steps { list-style:none; padding:0; margin:0 0 18px; display:flex; flex-direction:column; gap:12px; }
.reg-steps li { display:flex; gap:12px; align-items:flex-start; font-size:13px; color:var(--text-dim); line-height:1.7; }
.reg-steps .n { flex-shrink:0; width:24px; height:24px; border-radius:50%; background:var(--accent); color:#fff;
    display:grid; place-items:center; font-weight:800; font-size:12px; }
.reg-steps code { background:var(--bg-inset); padding:1px 7px; border-radius:5px; font-family:var(--mono); font-size:11.5px; color:var(--text); }
.reg-actions { display:flex; gap:10px; }
.reg-go { flex:1; text-align:center; padding:12px; border-radius:9px; background:var(--accent); color:#fff;
    font-weight:800; font-size:13px; text-decoration:none; border:none; cursor:pointer; font-family:inherit; }
.reg-go:hover { filter:brightness(1.07); }
.reg-close { padding:12px 16px; border-radius:9px; background:var(--bg-card); border:1px solid var(--border-2);
    color:var(--text-dim); font-weight:700; font-size:13px; cursor:pointer; font-family:inherit; }
.reg-close:hover { border-color:var(--danger); color:var(--danger); }

.reg-badge { display:inline-block; font-size:11px; font-weight:800; letter-spacing:.2px; color:var(--accent);
    background:var(--accent-soft); border:1px solid color-mix(in srgb, var(--accent) 30%, transparent);
    padding:5px 11px; border-radius:999px; margin-bottom:14px; }
.reg-points { list-style:none; padding:0; margin:0 0 18px; display:flex; flex-direction:column; gap:11px; }
.reg-points li { display:flex; gap:10px; align-items:flex-start; font-size:13px; color:var(--text-dim); line-height:1.7; }
.reg-points .reg-ic { flex-shrink:0; font-size:16px; line-height:1.4; }
.reg-points b { color:var(--text); }
.reg-signin { padding:12px 18px; border-radius:9px; background:var(--bg-card); border:1px solid var(--border-2);
    color:var(--text); font-weight:800; font-size:13px; cursor:pointer; font-family:inherit; transition:.15s; }
.reg-signin:hover { border-color:var(--accent); color:var(--accent); background:var(--accent-soft); }
.reg-x { position:absolute; top:12px; inset-inline-end:14px; background:none; border:none; color:var(--text-mute);
    font-size:16px; cursor:pointer; line-height:1; padding:4px; }
.reg-x:hover { color:var(--danger); }

/* =====================================================================
   THE ADMIN SHELL AND ITS COMPONENTS.

   Rebuilt from the design the operator supplied: a permanently dark rail
   on one side, a light working surface on the other, and everything in
   between built from four decisions rather than ad-hoc numbers --

     surface   white panel, 1px border, 12px radius, one soft shadow
     rhythm    a 4px scale; 24px inside a card, 32px around the page
     ink       one weight for labels (600), one for numbers (800)
     accent    used for exactly two things: the active nav item and the
               primary action. Everything else is grey until it has a
               state to report.

   WHY THE RAIL IS DARK IN BOTH THEMES. It is navigation, not content:
   keeping it constant means the eye learns one shape, and the light
   surface beside it always reads as "the thing you are working on".
   That is also why --text-mute is re-declared for .adm-main further down
   this file and NOT here -- those tokens are tuned for the light surface
   and would make the rail's own labels unreadable.
   ===================================================================== */

/* The rhythm, declared once at the root and NOT on .adm.
   .panel, .tbl, .btn and .card2 are shared with the reseller panel and the
   customer dashboard, and neither of those pages has an .adm ancestor: with
   the tokens scoped to the shell, every `padding: 20px var(--pad-card)` on
   those two pages resolved to an invalid value and the declaration was
   dropped entirely -- panels with no padding at all. */
:root {
    --pad-page: 32px;
    --pad-card: 24px;
    --radius-lg: 12px;
    --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);
    --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .08), 0 2px 4px -2px rgba(0, 0, 0, .05);
}

.adm {
    display: flex;
    height: 100vh;
    overflow: hidden;
    background: var(--bg-root);
    /* Rail colours stay on the shell: nothing else has a rail. */
    --rail-bg: #0f172a;
    --rail-deep: #0b1120;
    --rail-line: #1e293b;
    --rail-text: #94a3b8;
    --rail-label: #64748b;
}

/* In the dark theme the page background IS #0f172a, so a rail painted the
   same colour stops being a separate surface and the whole screen reads as
   one flat sheet. It goes a step deeper instead, which keeps the same
   "navigation is darker than work" relationship the light theme has. */
[data-theme="dark"] .adm {
    --rail-bg: #0b1120;
    --rail-deep: #070c16;
    --rail-line: #1e293b;
}
[data-theme="dark"] .adm-side { border-inline-end: 1px solid #1e293b; }

/* ------------------------------------------------------------- rail -- */

.adm-side {
    width: 280px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    height: 100vh;
    background: var(--rail-bg);
    border-inline-end: none;
    /* THE RAIL ITSELF DOES NOT SCROLL -- only the nav between the logo and
       the footer does. When the whole column scrolled, the footer had to be
       sticky to stay reachable, and a sticky footer over a scrolling column
       sits ON TOP of the last nav item: at 900px tall "Audit Log" was
       permanently hidden behind the sign-out button. */
    overflow: hidden;
}

.adm-side .logo {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 24px;
    background: var(--rail-bg);
    border-bottom: 1px solid var(--rail-line);
    color: var(--accent);
}
.adm-side .logo-ic { width: 32px; height: 32px; flex-shrink: 0; display: inline-flex; }
.adm-side .logo-ic svg { width: 100%; height: 100%; }
.adm-side .logo b {
    color: #fff;
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -.02em;
    white-space: nowrap;
}

.adm-nav { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 8px 12px 16px; display: block;
    scrollbar-width: thin; scrollbar-color: #334155 transparent; }
.adm-nav::-webkit-scrollbar { width: 6px; }
.adm-nav::-webkit-scrollbar-thumb { background: #334155; border-radius: 4px; }

.adm-nav-g {
    padding: 16px 12px 8px;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--rail-label);
}
.adm-nav-g:first-child { padding-top: 8px; }

.adm-nav a {
    display: flex;
    align-items: center;
    gap: 12px;
    /* 44px tall: a pointer target that a tired thumb can hit on a tablet. */
    padding: 11px 12px;
    margin-bottom: 4px;
    border-radius: 8px;
    color: var(--rail-text);
    text-decoration: none;
    font-size: .95rem;
    font-weight: 500;
    transition: background .15s, color .15s;
}
.adm-nav a .nav-ic { display: inline-flex; flex-shrink: 0; }
.adm-nav a .nav-ic svg { width: 20px; height: 20px; display: block; }

.adm-nav a:hover { background: var(--rail-line); color: #f8fafc; }
.adm-nav a.active {
    background: var(--accent);
    color: #fff;
    font-weight: 600;
    box-shadow: none;
}
.adm-nav a.active::before { content: none; }   /* the old accent bar; the fill says it now */
.adm-nav a:focus-visible { outline: 2px solid #fff; outline-offset: -2px; }

/* The signed-in staff member, and the one destructive control on the rail. */
.adm-side .foot {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 18px 24px;
    background: var(--rail-deep);
    border-top: 1px solid var(--rail-line);
    font-size: .75rem;
    color: var(--rail-text);
}
.adm-who { display: flex; flex-direction: column; min-width: 0; }
.adm-who b { color: #fff; font-size: .9rem; font-weight: 600; }
.adm-who span { font-size: .75rem; color: var(--rail-text); text-transform: capitalize; }
.adm-side .foot-acts { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.adm-side .foot-acts a {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 8px;
    color: var(--rail-text);
    text-decoration: none;
    transition: background .15s, color .15s;
}
.adm-side .foot-acts a svg { width: 18px; height: 18px; }
.adm-side .foot-acts a:hover { background: var(--rail-line); color: #fff; }
.adm-side .foot-acts a.signout { color: #f87171; }
.adm-side .foot-acts a.signout:hover { background: rgba(239, 68, 68, .12); color: #fca5a5; }

/* ------------------------------------------------------- work surface -- */

.adm-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    height: 100vh;
}

.adm-top {
    height: 72px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 0 var(--pad-page);
    background: var(--bg-panel);
    border-bottom: 1px solid var(--border);
    z-index: 5;
}
.adm-top h1 {
    margin: 0;
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--text);
}
.adm-top-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.adm-body {
    flex: 1 1 auto;
    min-height: 0;
    padding: var(--pad-page);
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--border-2) transparent;
    scroll-behavior: smooth;
}
.adm-body::-webkit-scrollbar { width: 10px; }
.adm-body::-webkit-scrollbar-thumb {
    background: var(--border-2); border-radius: 8px;
    border: 3px solid transparent; background-clip: content-box;
}

/* ------------------------------------------------------------ flash -- */

.flash {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 15px 20px;
    border-radius: var(--radius-lg);
    margin-bottom: 24px;
    font-size: .95rem;
    font-weight: 600;
    border: 1px solid transparent;
    box-shadow: var(--shadow-sm);
}
.flash.ok   { background: rgba(16, 185, 129, .10); border-color: rgba(16, 185, 129, .28); color: var(--ind-ok-ink, var(--success)); }
.flash.err  { background: rgba(239, 68, 68, .10);  border-color: rgba(239, 68, 68, .28);  color: var(--ind-bad-ink, var(--danger)); }
.flash.info { background: var(--accent-soft);      border-color: color-mix(in srgb, var(--accent) 26%, transparent); color: var(--ind-accent-ink, var(--accent)); }

/* ------------------------------------------------------------ cards -- */

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 24px;
    margin-bottom: 32px;
}

/* One figure, told in three lines: what it is, how big, which way it moved. */
.card2 {
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--pad-card);
    box-shadow: var(--shadow-sm);
    transition: box-shadow .15s, border-color .15s;
}
.card2::after { content: none; }      /* the old accent stripe */
.card2:hover { transform: none; box-shadow: var(--shadow-md); border-color: var(--border-2); }
.card2 .n {
    font-size: 2rem;
    font-weight: 800;
    line-height: 1.1;
    letter-spacing: -.02em;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}
.card2 .n small { font-size: .9rem; font-weight: 700; color: var(--text-mute); margin-inline-start: 4px; }
.card2 .l {
    margin-top: 8px;
    font-size: .875rem;
    font-weight: 600;
    color: var(--text-mute);
    text-transform: none;
    letter-spacing: 0;
}

/* ------------------------------------------------------------ panel -- */

.panel {
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    margin-bottom: 24px;
}
.panel-h {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    padding: 20px var(--pad-card);
    border-bottom: 1px solid var(--border);
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: -.01em;
    color: var(--text);
}
/* The count an admin panel header carries after its title -- "Customers (12)".
   SCOPED TO .adm-main: the reseller panel wraps its panel TITLES in a bare
   <span data-t="..."> for the translator to fill, and an unscoped rule
   shrank those titles to caption size on every panel of that page. */
.adm-main .panel-h > span:not([class]) { font-size: .8rem; font-weight: 600; color: var(--text-mute); }
.p-desc {
    padding: 16px var(--pad-card);
    margin: 0;
    font-size: .875rem;
    line-height: 1.7;
    color: var(--text-dim);
}

/* ------------------------------------------------------------ table -- */

.tbl-wrap { overflow-x: auto; }
table.tbl { width: 100%; border-collapse: collapse; text-align: start; font-size: .9rem; }
table.tbl th {
    padding: 14px var(--pad-card);
    background: var(--bg-card);
    border-bottom: 1px solid var(--border);
    color: var(--text-mute);
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    text-align: start;
    white-space: nowrap;
}
table.tbl td {
    padding: 16px var(--pad-card);
    border-bottom: 1px solid var(--border);
    color: var(--text);
    vertical-align: middle;
}
table.tbl tr:last-child td { border-bottom: none; }
table.tbl tbody tr { transition: background .12s; }
table.tbl tbody tr:hover td { background: var(--bg-card); }
table.tbl tr.clickable { cursor: pointer; }

.tbl.compact th { padding: 10px 16px; }
.tbl.compact td { padding: 11px 16px; }
.tbl .num { text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mini { font-size: .8rem; color: var(--text-mute); margin-top: 3px; }

/* ----------------------------------------------------------- badges -- */
/* A pill is a filled wash with dark ink on it, never ink on the page
   colour: at 0.75rem the fill is what makes the state readable at a
   glance, and the ink values come from the contrast block further down. */

.badge {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border-radius: 99px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .01em;
    white-space: nowrap;
}
.pill2 {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border-radius: 99px;
    background: #f1f5f9;
    color: #475569;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .01em;
    text-transform: capitalize;
    white-space: nowrap;
}
.pill2.success, .pill2.active   { background: rgba(16, 185, 129, .14); }
.pill2.failed, .pill2.blocked,
.pill2.disabled                 { background: rgba(239, 68, 68, .14); }
.pill2.running                  { background: rgba(245, 158, 11, .16); }
.pill2.transfer                 { background: var(--accent-soft); }
[data-theme="dark"] .pill2      { background: rgba(148, 163, 184, .16); color: #cbd5e1; }
[data-theme="dark"] .pill2.success, [data-theme="dark"] .pill2.active { background: rgba(16, 185, 129, .18); }
[data-theme="dark"] .pill2.failed, [data-theme="dark"] .pill2.blocked,
[data-theme="dark"] .pill2.disabled { background: rgba(239, 68, 68, .18); }
[data-theme="dark"] .pill2.running  { background: rgba(245, 158, 11, .20); }

/* ------------------------------------------------------------ forms -- */

.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 20px 24px;
    padding: var(--pad-card);
}
.fld { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.fld label { font-size: .875rem; font-weight: 600; color: var(--text); }
.fld .hint { font-size: .8rem; line-height: 1.6; color: var(--text-mute); }
.fld input, .fld select, .fld textarea,
.searchbar input, .searchbar select {
    width: 100%;
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-panel);
    color: var(--text);
    font-family: inherit;
    font-size: .95rem;
    transition: border-color .15s, box-shadow .15s;
}
.fld select { cursor: pointer; }
.fld input:focus, .fld select:focus, .fld textarea:focus,
.searchbar input:focus, .searchbar select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.form-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 16px var(--pad-card);
    border-top: 1px solid var(--border);
    background: var(--bg-card);
}

.searchbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.searchbar input, .searchbar select { width: auto; padding: 8px 12px; font-size: .875rem; }

/* ---------------------------------------------------------- buttons -- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 18px;
    border: 1px solid transparent;
    border-radius: 8px;
    font-family: inherit;
    font-size: .9rem;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    text-decoration: none;
    transition: background .15s, color .15s, border-color .15s;
}
.btn:hover { transform: none; }         /* the old 1px lift; the colour change is the feedback */
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn.primary { background: var(--btn-primary-bg, var(--accent)); color: #fff; box-shadow: none; }
.btn.primary:hover { filter: brightness(1.06); }
.btn.ghost { background: transparent; color: var(--text-dim); border-color: var(--border); }
.btn.ghost:hover { background: var(--bg-card); color: var(--text); border-color: var(--border-2); }
.btn.danger { background: transparent; border-color: color-mix(in srgb, var(--danger) 36%, transparent); }
.btn.mini { padding: 7px 12px; font-size: .8rem; }
.inline-form { display: inline; }

/* The header's own controls: same height, quieter than a page action. */
.auth-langbtn, .adm-top-actions .btn.ghost {
    padding: 9px 14px;
    border-radius: 8px;
    font-size: .85rem;
    font-weight: 600;
}
.auth-langbtn {
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text-dim);
    font-family: inherit;
    cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}
.auth-langbtn:hover { background: var(--bg-card); color: var(--text); border-color: var(--border-2); }

/* ---------------------------------------------------------- toggles -- */

.switch { display: inline-flex; align-items: center; gap: 12px; cursor: pointer; user-select: none; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .switch-track {
    position: relative; width: 44px; height: 24px; border-radius: 99px;
    background: var(--border-2); flex-shrink: 0; transition: background .2s;
}
.switch .switch-knob {
    position: absolute; top: 3px; inset-inline-start: 3px;
    width: 18px; height: 18px; border-radius: 50%;
    background: #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
    transition: inset-inline-start .2s;
}
.switch input:checked + .switch-track { background: var(--success); }
.switch input:checked + .switch-track .switch-knob { inset-inline-start: 23px; }
.switch input:focus-visible + .switch-track { box-shadow: 0 0 0 3px var(--accent-soft); }
.switch .sw-hint { font-size: .85rem; font-weight: 500; color: var(--text-dim); }

/* ------------------------------------------------------- responsive -- */
/*
 * Below 1100px the rail collapses to icons. It is NOT hidden: the nav is
 * the only way between fifteen screens, and a hamburger that needs
 * JavaScript would strand anyone whose script failed to load.
 */
@media (max-width: 1100px) {
    .adm-side { width: 76px; }
    .adm-side .logo { padding: 20px 0; justify-content: center; }
    .adm-side .logo b { display: none; }
    .adm-nav { padding: 8px; }
    .adm-nav-g { display: none; }
    .adm-nav a { justify-content: center; padding: 12px 0; }
    .adm-nav a span:last-child { display: none; }
    .adm-side .foot { padding: 14px 8px; justify-content: center; }
    .adm-who { display: none; }
}

@media (max-width: 820px) {
    :root { --pad-page: 16px; --pad-card: 16px; }
    .adm-top { height: auto; padding: 12px 16px; flex-wrap: wrap; gap: 8px; }
    .adm-top h1 { font-size: 1.25rem; }
    .adm-top-actions { width: 100%; justify-content: space-between; }
    .form-grid { grid-template-columns: 1fr; }
    .cards { grid-template-columns: 1fr 1fr; gap: 16px; }
    table.tbl th, table.tbl td { padding-inline: 16px; }
}

@media (max-width: 560px) {
    .adm-side { width: 60px; }
    .cards { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
    .adm-body { scroll-behavior: auto; }
    .btn, .card2, .adm-nav a, .switch .switch-track, .switch .switch-knob,
    table.tbl tbody tr, .fld input, .fld select, .fld textarea { transition: none; }
}


.op-detail { display:grid; grid-template-columns:1fr 1fr; gap:8px 18px; margin-bottom:6px; }
.op-detail .op-kv { display:flex; flex-direction:column; gap:2px; min-width:0; }
.op-detail .op-kv.wide { grid-column:1 / -1; }
.op-detail .op-kv .k { font-size:10px; text-transform:uppercase; letter-spacing:.4px; color:var(--text-mute); font-weight:800; }
.op-detail .op-kv .v { font-size:12.5px; color:var(--text); word-break:break-all; }
.op-detail .op-kv .v.code { font-family:"LatinNum","Cascadia Mono",Consolas,ui-monospace,monospace; font-size:11.5px; background:var(--bg-inset); padding:6px 8px; border-radius:6px; }
@media(max-width:640px){ .op-detail { grid-template-columns:1fr; } }

.dash-cols { display:grid; grid-template-columns:1fr 1fr; gap:16px; align-items:start; }
.dash-cols .panel { margin:0; }
.tbl.compact th, .tbl.compact td { padding:7px 12px; }
.status-dot { display:inline-block; width:9px; height:9px; border-radius:50%; margin-inline-end:7px; background:var(--text-mute); }
.status-dot.on { background:var(--success); box-shadow:0 0 7px var(--success); }
.status-dot.off { background:var(--danger); }
@media(max-width:820px){ .dash-cols { grid-template-columns:1fr; } }

/* The "v2" refinement layer that used to sit here -- a second pass over the
   rail, the cards, the panel and the buttons -- is gone. It only existed to
   correct the first pass, and the block above now states each of those
   decisions once. Three stacked layers meant a change had to be made in
   whichever one happened to win, which is how the sidebar ended up defined
   in two places with different widths. */


.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 20px; }
.tabs .tab {
    appearance: none; border: 0; background: transparent; cursor: pointer;
    font-family: inherit; font-size: 13px; font-weight: 700; color: var(--text-mute);
    padding: 12px 18px; border-bottom: 2px solid transparent; margin-bottom: -1px; transition: .15s;
}
.tabs .tab:hover { color: var(--text); }
.tabs .tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.tab-pane { display: none; }
.tab-pane.active { display: block; }
.dash { display: flex; flex-direction: column; gap: 16px; }
.dash .num { text-align: end; font-variant-numeric: tabular-nums; }
.dash th.num, .dash td.num { white-space: nowrap; }

.d-alerts { display: flex; flex-direction: column; gap: 8px; }
.d-alert { display: flex; gap: 10px; align-items: flex-start; padding: 11px 14px; border-radius: 10px;
    border: 1px solid var(--border); border-inline-start: 4px solid var(--text-mute);
    background: var(--bg-panel); font-size: .88rem; line-height: 1.6; }
.d-alert.high { border-inline-start-color: var(--danger); }
.d-alert.med { border-inline-start-color: var(--warn); }
.d-alert-ic { flex: 0 0 auto; width: 18px; height: 18px; margin-top: 1px; }
.d-alert.high .d-alert-ic { color: var(--danger); }
.d-alert.med .d-alert-ic { color: var(--warn); }
.d-alert-ic svg { width: 100%; height: 100%; display: block; }

.d-kpis { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; }
/* The figure card: label + icon, then the number, then the detail line.
   Same surface vocabulary as .card2 -- white, 1px border, 12px radius, one
   soft shadow -- because they are the same kind of object and sitting them
   side by side with different treatments is what made the dashboard look
   assembled rather than designed. */
.kpi { display: flex; flex-direction: column; padding: var(--pad-card, 24px); border-radius: var(--radius-lg, 12px);
    border: 1px solid var(--border); background: var(--bg-panel); min-width: 0;
    box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.05)); transition: box-shadow .15s, border-color .15s; }
.kpi:hover { box-shadow: var(--shadow-md, 0 4px 6px -1px rgba(0,0,0,.08)); border-color: var(--border-2); }
.kpi-h { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
.kpi-ic { flex: 0 0 auto; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 10px;
    background: var(--bg-card); color: var(--text-dim); }
.kpi-ic svg { width: 20px; height: 20px; display: block; }
.kpi.accent .kpi-ic { background: var(--accent-soft); color: var(--ind-accent-ink, var(--accent)); }
.kpi.good .kpi-ic { background: color-mix(in srgb, var(--success) 14%, transparent); color: var(--ind-ok-ink, var(--success)); }
.kpi.warn .kpi-ic { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--ind-warn-ink, var(--warn)); }
.kpi.bad .kpi-ic { background: color-mix(in srgb, var(--danger) 14%, transparent); color: var(--ind-bad-ink, var(--danger)); }
.kpi-l { font-size: .875rem; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--text-mute); }
.kpi-v { font-size: 2rem; font-weight: 800; line-height: 1.1; letter-spacing: -.02em; color: var(--text);
    font-variant-numeric: tabular-nums; overflow-wrap: anywhere; margin-bottom: 8px; }
/* .62em of a .kpi-v that itself shrinks at the 430px breakpoint landed the
   currency suffix at 10.48px. Pinned, so it does not track the number down. */
.kpi-v small { font-size: .9rem; font-weight: 700; color: var(--text-mute); margin-inline-start: 3px; }
.kpi-s { font-size: .8rem; color: var(--text-dim); line-height: 1.5; overflow-wrap: anywhere; }

.d-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; }

.d-srv-t th[scope="row"] { font-weight: 700; white-space: nowrap; }
.d-srv-n { display: flex; gap: 8px; align-items: center; }
.d-srv-n .badge { text-transform: uppercase; font-size: 11px; letter-spacing: .04em; }

.d-chart { display: flex; align-items: flex-end; gap: 5px; height: 168px; padding: 14px 14px 0; }
.d-col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0; height: 100%; }
.d-stack { flex: 1 1 auto; width: 100%; max-width: 26px; display: flex; flex-direction: column;
    justify-content: flex-end; gap: 2px; }
.d-bar { display: block; width: 100%; border-radius: 4px; min-height: 3px; }
.d-bar.s { background: var(--success); }
.d-bar.f { background: var(--danger); }
.d-bar.z { height: 3px; background: var(--border); border-radius: 4px; }
.d-x { font-size: 11px; color: var(--text-mute); font-variant-numeric: tabular-nums; }
.d-legend { display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
    padding: 10px 16px 14px; font-size: 11.5px; color: var(--text-dim); }
.d-legend .sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px;
    margin-inline-end: 6px; vertical-align: -1px; }
.d-legend .sw.s { background: var(--success); }
.d-legend .sw.f { background: var(--danger); }

.d-fails { list-style: none; margin: 0; padding: 6px 16px 16px; display: flex; flex-direction: column; gap: 10px; }
.d-fails li { display: grid; grid-template-columns: minmax(0, 1fr) 84px 40px; gap: 10px; align-items: center; }
.d-fail-t { font-size: 12.5px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.d-fail-m { height: 7px; border-radius: 4px; background: var(--bg-card); overflow: hidden; }
.d-fail-bar { display: block; height: 100%; border-radius: 4px; background: var(--danger); min-width: 3px; }
.d-fail-c { text-align: end; font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text); }

.d-empty { padding: 22px 16px; text-align: center; font-size: 13px; color: var(--text-mute); }

@media (max-width: 1280px) { .d-kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 900px) {
    .d-cols { grid-template-columns: 1fr; }
    .d-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .d-chart { height: 140px; }
}
@media (max-width: 520px) {
    .d-kpis { grid-template-columns: 1fr; }
    .kpi-v { font-size: 1.3rem; }
    .d-fails li { grid-template-columns: minmax(0, 1fr) 52px 34px; }
}
@media (prefers-reduced-motion: reduce) {
    .kpi, .d-bar, .d-fail-bar { transition: none !important; }
}

.dash .d-off { opacity: .62; }
.dash .d-h-act { margin-inline-start: auto; display: inline-flex; gap: 12px; align-items: center; }
.dash .d-inline-f { display: inline; }
.dash .d-age { display: block; font-size: 11px; font-weight: 400; color: var(--text-mute); }
.dash .d-srv-t tbody th[scope="row"] { font-size: 13px; font-weight: 700; text-transform: none;
    letter-spacing: 0; color: var(--text); background: transparent; }
.dash .d-other-cur { grid-column: 1 / -1; padding: 9px 13px; border-radius: 10px; border: 1px solid var(--border);
    background: var(--bg-panel); font-size: 12px; color: var(--text-dim); }
.dash .d-fail-rest { display: block; }

/* ===================================================== admin shell icons ===
   The sidebar used emoji. They render at a different size and baseline in
   every OS, several were unreadable at 18px, and an emoji cannot take
   currentColor -- so the active row's white label sat next to a full-colour
   glyph. dash_icon() already shipped a 24x24 stroke set for the dashboard, so
   the nav now draws from that same family and inherits the row's colour.     */
.nav-ic { display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; flex:0 0 18px; }
.nav-ic svg { width:18px; height:18px; display:block; }
.adm-side .logo { display:flex; align-items:center; gap:9px; }
.logo-ic { display:inline-flex; width:20px; height:20px; color:var(--accent); }
.logo-ic svg { width:20px; height:20px; display:block; }

/* Fourteen flat rows had no hierarchy: four are opened daily, ten are
   configuration. The headings let the eye skip a whole block.                */
.adm-nav-g {
    padding:14px 13px 5px; font-size:10px; font-weight:800;
    letter-spacing:.09em; text-transform:uppercase; color:var(--text-mute);
    user-select:none;
}
.adm-nav-g:first-child { padding-top:4px; }

/* The theme knob's glyph was a literal moon emoji in the markup, which meant
   it never changed when the theme did. Now it is drawn per theme.            */
.tswitch .knob { display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; }
.tswitch .knob::before {
    content:""; width:14px; height:14px; border-radius:50%;
    background:var(--text-dim);
    box-shadow:inset -4px -4px 0 0 var(--bg-card);   /* light theme: a crescent */
}
:root[data-theme="dark"] .tswitch .knob::before {
    background:var(--warn, #d29922); box-shadow:none;  /* dark theme: a full sun */
}
.who { display:inline-flex; align-items:center; gap:7px; }
.who svg { width:15px; height:15px; opacity:.75; }

/* Collapsed rail (<=820px): the group headings would otherwise stack three
   stray words above a column of icons. */
@media (max-width:820px) {
    .adm-nav-g { display:none; }
    .adm-side .logo { justify-content:center; }
}

/* ============================================ an operation, read back =====
   Three sources that can disagree -- the handset, the customer's screen and
   the provider -- so each gets its own surface instead of three raw <pre>
   blocks stacked in one cell.                                              */
.opd {
    display:flex; flex-direction:column; gap:18px; padding:4px 2px 8px;
    /* --text-mute is an indicator grey; at the 9.5-11px this block uses it
       measures 2.4:1 on --bg-card and 2.05:1 on --bg-inset. These are the text
       weights of the same greys. --ok is not defined in app.css at all, so the
       green log lines were falling back to #1a7f37 at 4.11:1. */
    --opd-mute:   #5A6473;   /* 5.71:1 on --bg-card  */
    --opd-mute-i: #54606F;   /* 5.16:1 on --bg-inset */
    --opd-ok:     #15662D;   /* 5.72:1 on --bg-inset */
}
[data-theme="dark"] .opd {
    --opd-mute:   #A8B4C6;   /* 4.96:1 on --bg-card  */
    --opd-mute-i: #A8B4C6;   /* 8.66:1 on --bg-inset */
    --opd-ok:     #3FB950;   /* 7.01:1 on --bg-inset */
}
.opd-sec { min-width:0; }
.opd-h {
    display:flex; align-items:center; gap:8px; margin-bottom:10px;
    font-size:12px; font-weight:800; color:var(--text);
}
.opd-h svg { width:15px; height:15px; color:var(--accent); flex:0 0 15px; }
.opd-h .hint { font-weight:600; color:var(--opd-mute); font-size:11px; margin-inline-start:auto; }
.opd-none {
    padding:14px 16px; border-radius:9px; background:var(--bg-inset);
    border:1px solid var(--border); font-size:12px; line-height:1.8; color:var(--text-dim);
}

/* the device, in the same key-over-value card the customer's panel uses */
.opd-cards { display:grid; gap:9px; grid-template-columns:repeat(auto-fill, minmax(178px, 1fr)); }
.opd-card {
    padding:10px 12px; border-radius:9px;
    background:var(--bg-card); border:1px solid var(--border); min-width:0;
}
.opd-card .lbl {
    font-size:10.5px; font-weight:800; letter-spacing:.05em; text-transform:uppercase;
    color:var(--opd-mute); margin-bottom:4px;
    text-align:start; unicode-bidi:plaintext;
}
.opd-card .val {
    font-size:12.5px; font-weight:700; color:var(--text); line-height:1.5;
    font-variant-numeric:tabular-nums; word-break:break-word;
    /* machine values: a model number or an OS build reads left to right even
       on an RTL panel, the same rule the tool's own .val carries */
    direction:ltr; text-align:start;
}

/* the three logs */
.opd-tabs { display:flex; gap:4px; margin-bottom:10px; flex-wrap:wrap; }
.opd-tab {
    display:inline-flex; align-items:center; gap:7px; cursor:pointer;
    min-height:34px; padding:0 13px; border-radius:8px;
    border:1px solid var(--border); background:var(--bg-card);
    font-family:inherit; font-size:11.5px; font-weight:700; color:var(--text-dim);
    transition:background .14s, border-color .14s, color .14s;
}
.opd-tab svg { width:13px; height:13px; opacity:.8; }
.opd-tab b { font-weight:800; font-size:10px; padding:1px 6px; border-radius:999px;
    background:var(--bg-inset); color:var(--opd-mute-i); }
.opd-tab:hover { color:var(--text); border-color:var(--border-2); }
.opd-tab.is-on { background:var(--accent); border-color:transparent; color:#fff; }
/* the dark theme's --accent is a LIGHT blue (#58a6ff), so white on it measures
   3.68:1. A darker fill keeps the white label and matches the light theme. */
[data-theme="dark"] .opd-tab.is-on { background:#1c5fb8; }
/* white on a .22 white-over-accent tint measured 2.06:1; darkening the pill
   instead of lightening it keeps the badge legible on either accent. */
.opd-tab.is-on b { background:rgba(0,0,0,.35); color:#fff; }
.opd-tab:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }

.opd-pane { display:none; }
.opd-pane.is-on { display:block; }

/* the customer's log, rendered the way they saw it rather than as raw text */
.opd-log {
    max-height:340px; overflow:auto; padding:10px 12px; border-radius:9px;
    background:var(--bg-inset); border:1px solid var(--border);
    font-size:11.5px; line-height:1.85;
    direction:ltr; text-align:start;   /* keeps the timestamp gutter on the left */
}
.opd-log p { display:flex; gap:10px; margin:0 0 3px; }
.opd-log time {
    flex:0 0 auto; color:var(--opd-mute-i); font-family:var(--mono); font-size:10.5px;
    padding-top:1px;   /* the .85 opacity took the timestamp to 2.05:1 */
}
/* each message resolves its OWN base direction, so a Persian line reads right
   to left inside the left-pinned gutter -- as .log-msg does in the tool */
.opd-log span { flex:1; min-width:0; color:var(--text-dim); unicode-bidi:plaintext; }
.opd-log p.kv span { color:var(--text); font-weight:700; }
.opd-log p.ok span { color:var(--opd-ok); }
.opd-log p.bad span { color:var(--danger); }
.opd-log p.warn span { color:var(--warn); }

.opd-x + .opd-x { margin-top:12px; }
.opd-x-h { font-size:11px; font-weight:800; color:var(--text-dim); margin-bottom:6px; }
.opd-x-h .hint { font-weight:600; color:var(--opd-mute); margin-inline-start:6px; }
.opd-pre {
    margin:0; max-height:300px; overflow:auto; padding:11px 13px; border-radius:9px;
    background:var(--bg-inset); border:1px solid var(--border);
    font-family:var(--mono); font-size:11px; line-height:1.75; color:var(--text-dim);
    white-space:pre-wrap; word-break:break-word; direction:ltr; text-align:start;
}

.opd-meta { display:grid; gap:8px 18px; grid-template-columns:repeat(auto-fit, minmax(210px, 1fr)); }
.opd-kv { display:flex; flex-direction:column; gap:3px; min-width:0; }
.opd-kv.wide { grid-column:1 / -1; }
.opd-kv .k { font-size:10.5px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; color:var(--opd-mute); }
.opd-kv .v { font-size:12px; color:var(--text); word-break:break-all; direction:ltr; text-align:start; }
.opd-kv .v.code {
    font-family:var(--mono); font-size:11px; background:var(--bg-inset);
    padding:6px 8px; border-radius:6px; border:1px solid var(--border);
}

@media (max-width:640px) {
    .opd-cards { grid-template-columns:1fr 1fr; }
    .opd-tab span { display:none; }
}

/* ------------------------------------------- the operation popup --------- */
.opd-modal {
    width:900px; max-width:calc(100vw - 32px);
    max-height:calc(100vh - 48px);
    display:flex; flex-direction:column; padding:0; overflow:hidden;
}
.opd-modal-h {
    display:flex; align-items:flex-start; gap:12px;
    padding:18px 20px 14px; border-bottom:1px solid var(--border); flex:0 0 auto;
}
.opd-modal-hx { flex:1 1 auto; min-width:0; }
.opd-modal-h h3 { margin:0; font-size:16px; font-weight:800; color:var(--text); }
.opd-modal-meta {
    margin:4px 0 0; font-size:12px; line-height:1.7; color:var(--opd-mute, var(--text-dim));
    word-break:break-word;
}
.opd-modal-h .pop-x {
    flex:0 0 auto; position:static; width:32px; height:32px; line-height:1;
    display:grid; place-items:center; cursor:pointer; font-size:19px;
    border-radius:8px; border:1px solid var(--border);
    background:var(--bg-card); color:var(--text-dim);
}
.opd-modal-h .pop-x:hover { background:var(--bg-inset); color:var(--text); }
.opd-modal-h .pop-x:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
/* the body scrolls, the header stays put, so a 400-line log never pushes the
   close button off the screen */
.opd-modal-b { flex:1 1 auto; min-height:0; overflow-y:auto; padding:4px 20px 18px; }
.opd-modal-b .opd { padding-top:14px; }
/* inside the popup the log panes get the height the drawer could not give them */
.opd-modal-b .opd-log { max-height:min(46vh, 420px); }
.opd-modal-b .opd-pre { max-height:min(46vh, 420px); }

@media (max-width:640px) {
    .opd-modal { max-height:calc(100vh - 16px); }
    .opd-modal-h { padding:14px 14px 12px; }
    .opd-modal-b { padding:4px 14px 14px; }
}

/* ------------------------------------------- resellers & API ------------- */
.rsf { padding-top: 4px; }
.rs-grid { display:grid; gap:14px; grid-template-columns:repeat(auto-fit, minmax(210px, 1fr)); }
.rs-f { display:flex; flex-direction:column; gap:6px; min-width:0; }
.rs-f.rs-wide { grid-column:1 / -1; }
/* first-of-type, not every direct span: the .hint under a field is also a
   direct child, and uppercasing a whole sentence of guidance made the form
   shout its explanations louder than its labels. */
.rs-f > span:first-of-type {
    font-size:11px; font-weight:800; letter-spacing:.05em; text-transform:uppercase;
    color:var(--opd-mute, var(--text-dim)); text-align:start; unicode-bidi:plaintext;
}
.rs-f .hint { font-size:11px; font-weight:500; line-height:1.6; color:var(--opd-mute, var(--text-dim)); }
.rs-f .inp {
    width:100%; min-width:0; height:40px; padding:0 12px;
    border:1px solid var(--border); border-radius:8px;
    background:var(--bg-card); color:var(--text);
    font-family:inherit; font-size:13px; font-variant-numeric:tabular-nums;
}
.rs-f .inp:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px rgba(9,105,218,.16); }

.rs-sec { margin-top:20px; padding-top:16px; border-top:1px solid var(--border); }
.rs-sec-h { font-size:12px; font-weight:800; color:var(--text); margin-bottom:12px; }
.rs-sec-h .hint { font-weight:600; font-size:11px; color:var(--opd-mute, var(--text-dim)); margin-inline-start:8px; }

.rs-srv { margin-bottom:12px; }
.rs-srv-h { font-size:11px; font-weight:800; color:var(--opd-mute, var(--text-dim)); margin-bottom:6px; }
.rs-srv-h .hint { font-weight:600; color:var(--opd-mute, var(--text-dim)); margin-inline-start:6px; }
.rs-srv > .switch { margin-bottom:8px; }
.rs-chk {
    display:flex; align-items:center; gap:9px; cursor:pointer;
    padding:9px 12px; margin-bottom:6px; border-radius:8px;
    border:1px solid var(--border); background:var(--bg-card); font-size:12.5px;
}
.rs-chk:hover { border-color:var(--border-2); }
.rs-chk input { width:16px; height:16px; accent-color:var(--accent); cursor:pointer; flex:0 0 auto; }
.rs-chk span { font-weight:700; color:var(--text); }
.rs-chk i { margin-inline-start:auto; font-style:normal; color:var(--opd-mute, var(--text-dim)); }

.rs-foot {
    display:flex; align-items:center; gap:14px; flex-wrap:wrap;
    margin-top:20px; padding-top:16px; border-top:1px solid var(--border);
}

.rs-keys { display:grid; gap:10px; grid-template-columns:repeat(auto-fit, minmax(240px, 1fr)); margin-bottom:12px; }
.rs-kv { display:flex; flex-direction:column; gap:4px; min-width:0; }
.rs-kv .k { font-size:11px; font-weight:800; text-transform:uppercase; letter-spacing:.05em; color:var(--opd-mute, var(--text-dim)); }
.rs-kv .k i { font-style:normal; text-transform:none; letter-spacing:0; font-weight:600; }
.rs-kv .v.code {
    font-family:var(--mono); font-size:11.5px; color:var(--text);
    background:var(--bg-inset); border:1px solid var(--border);
    padding:8px 10px; border-radius:7px; word-break:break-all;
    direction:ltr; text-align:start;
}
.rs-pre {
    margin:0 0 12px; padding:13px 15px; border-radius:9px;
    background:var(--bg-inset); border:1px solid var(--border);
    font-family:var(--mono); font-size:11px; line-height:1.85;
    color:var(--text-dim); white-space:pre-wrap; word-break:break-word;
    direction:ltr; text-align:start; max-height:340px; overflow:auto;
}

/* ===========================================================================
   MUTED TEXT IN THE CONTENT COLUMN
   --text-mute is an indicator grey inherited from app.css: #9ca3af in light,
   #94a3b8 in dark. Every admin table header, every .mini under a cell, every
   .kpi-l and .fld .hint is painted with it at 10-12px, and measured on the
   surfaces they actually sit on that is 2.43:1 light and 4.04:1 dark -- both
   under the 4.5:1 small text needs. These are the text-weight versions of the
   same greys, already measured for the operation-detail block above.

   SCOPED TO .adm-main, NOT .adm, ON PURPOSE. .adm-side is painted with a dark
   gradient in BOTH themes, and its .foot uses --text-mute too; darkening the
   token for the light theme site-wide would fix the tables and make the
   sidebar footer unreadable on that dark panel. The sidebar keeps the light
   grey that is correct against its own backdrop.
   =========================================================================== */
.adm-main { --text-mute: #5A6473; }              /* 5.71:1 on --bg-card, 5.92:1 on --bg-panel */
[data-theme="dark"] .adm-main { --text-mute: #A8B4C6; }  /* 4.96:1 on --bg-card, 8.63:1 on --bg-root */

/* ---------------------------------------------------------------------------
   INDICATOR HUES USED AS TEXT
   --success / --danger / --warn are chosen to be seen as a DOT or a FILL, and
   they are correct there. The status pills and the danger button reuse the
   same three as INK over a 12-16% wash of themselves, and measured on the
   surfaces those actually land on none of the six theme/state pairs reaches
   4.5:1 -- the green "live" pill is 2.22:1 in light, and every one of the
   three is under 4.2:1 over --bg-card in dark, which is the hovered table row.

   These are the same hues walked along their own lightness ramp until the
   worst surface of the three (--bg-panel, --bg-card, --bg-inset) clears 4.6:1,
   so a pill reads the same whether it sits on a card, a panel or a hovered row.
   The dot and fill colours are deliberately NOT changed.

   .btn.danger:hover swaps the wash for a solid fill, and #fff on it was 3.76:1
   in light and 2.77:1 in dark. The fill becomes the ink colour and the label
   takes whichever of white/near-black the theme needs.
   --------------------------------------------------------------------------- */
.adm-main {
    --ind-ok-ink:   #0E6C4D;   /* >=4.64:1 on its wash over panel/card/inset */
    --ind-bad-ink:  #B81717;   /* >=4.63:1 */
    --ind-warn-ink: #82560B;   /* >=4.68:1 */
    --ind-bad-on:   #ffffff;   /* 6.62:1 on a solid #B81717 */
}
[data-theme="dark"] .adm-main {
    --ind-ok-ink:   #58DBAB;   /* >=4.62:1 */
    --ind-bad-ink:  #FAA2A2;   /* >=4.60:1 */
    --ind-warn-ink: #F8B84D;   /* >=4.60:1 */
    --ind-bad-on:   #0f172a;   /* 8.7:1 on a solid #FAA2A2 */
}
.adm-main .pill2.success,
.adm-main .pill2.active     { color:var(--ind-ok-ink); }
.adm-main .pill2.failed,
.adm-main .pill2.blocked,
.adm-main .pill2.disabled   { color:var(--ind-bad-ink); }
.adm-main .pill2.running    { color:var(--ind-warn-ink); }
.adm-main .btn.danger       { color:var(--ind-bad-ink); }
.adm-main .btn.danger:hover { background:var(--ind-bad-ink); color:var(--ind-bad-on); }

/* --accent is the brand blue, and as a FILL behind white it is right. As INK
   over its own 8-15% wash it is 4.44:1 on a card and 3.77:1 on an inset in
   light, and 2.41:1 on a card in dark -- so the operation-id badges, the info
   flash and the active tab label were all under the line. Same treatment: the
   same hue walked until the worst of the three surfaces clears 4.6:1. The fill,
   the focus ring and the icon tints keep the vivid --accent. */
.adm-main { --ind-accent-ink: #1453DD; }              /* >=4.62:1 over its wash */
[data-theme="dark"] .adm-main { --ind-accent-ink: #98BDFA; }  /* >=4.64:1 */

.adm-main .badge,
.adm-main .flash.info,
.adm-main .section-title,
.adm-main .api-guide a,
.adm-main .tabs .tab.active,
.adm-main .btn.ghost:hover,
.adm-main .card2.accent .n { color:var(--ind-accent-ink); }

/* The KPI numbers are 26px, so they only owe 3:1 -- but --success is 2.54:1
   and --warn 2.919:1 on --bg-panel in light, so two of the four states missed
   even that. The ink versions clear 4.5:1, comfortably past what this size
   needs, and keep each state its own colour. */
.adm-main .card2.good .n { color:var(--ind-ok-ink); }
.adm-main .card2.bad .n  { color:var(--ind-bad-ink); }
.adm-main .card2.warn .n { color:var(--ind-warn-ink); }

/* An <a> with no class falls back to the UA's #0000EE, which is 1.90:1 on the
   dark panel -- a link added to any admin view is unreadable in dark until
   someone remembers to colour it. Colour every link in the content column
   once, so the next one added is right by default. The button classes set
   their own colour at higher specificity and are unaffected. */
.adm-main a:not([class]) { color:var(--ind-accent-ink); }

/* ---------------------------------------------------------------------------
   SOLID BUTTON FILLS
   .btn.primary is #fff on --accent. That is 5.17:1 in light and 3.68:1 in
   dark, so the main action on nine tabs was the least readable thing on the
   screen in the dark theme. The dark fill is the same hue taken down until
   white clears 4.6:1 on it, rather than swapping the label to dark, so a
   primary button reads the same way in both themes. It still sits at 3.6:1
   against --bg-root, past the 3:1 a control needs.

   .btn.danger-solid replaces an inline background:var(--danger);color:#fff on
   the settings "clear activity logs" button, which was 3.76:1 in light and
   2.77:1 in dark -- a destructive action was the hardest label to read.
   --------------------------------------------------------------------------- */
.adm-main { --btn-primary-bg: var(--accent); }
[data-theme="dark"] .adm-main { --btn-primary-bg: #1A6DF4; }   /* #fff 4.63:1 */
.adm-main .btn.primary { background:var(--btn-primary-bg); }
.adm-main .btn.danger-solid { background:var(--ind-bad-ink); color:var(--ind-bad-on); font-weight:800; }
.adm-main .btn.danger-solid:hover { filter:brightness(1.08); }

/* The account token, shown on the one render that follows minting it. Given a
   loud surface on purpose: only its hash is stored, so an admin who scrolls
   past this has to issue a new one, and the reseller's integration breaks in
   the meantime. */
.rs-once {
    display:flex; flex-direction:column; gap:7px;
    margin:0 0 14px; padding:14px 16px; border-radius:11px;
    background:var(--warn-soft, rgba(245,158,11,.16));
    border:1px solid var(--ind-warn-ink, var(--warn));
    border-inline-start-width:3px;
}
.rs-once b { font-size:12.5px; font-weight:800; color:var(--ind-warn-ink, var(--warn)); }
.rs-once .v.code {
    font-family:var(--mono); font-size:12.5px; font-weight:700; color:var(--text);
    background:var(--bg-inset); border:1px solid var(--border);
    border-radius:8px; padding:9px 12px; word-break:break-all;
    direction:ltr; unicode-bidi:embed; user-select:all;
}
.rs-once i { font-size:11.5px; font-style:normal; color:var(--opd-mute, var(--text-dim)); }

/* ===========================================================================
   THE RESELLER'S API TAB (dashboard.php)
   Reuses .cards/.card2, .panel/.panel-h, .tbl and .pill2 from above so this
   tab is visibly the same product as the two beside it. Only what is genuinely
   new is defined here: the credential grid, the once-only token, and the two
   long machine values in every log row.

   Scoped under .ap* rather than restyling .tbl, because the History tab three
   pixels away uses the same table and must not move.
   =========================================================================== */
.ap-h { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; }
.ap-h .hint { font-size:11.5px; font-weight:600; color:var(--text-mute); }

.ap-state { display:inline-flex; align-items:center; gap:6px; font-size:11.5px; font-weight:800;
            letter-spacing:.03em; text-transform:uppercase; }
.ap-state i { width:8px; height:8px; border-radius:50%; flex:0 0 8px; }
.ap-state.on  { color:var(--ind-ok-ink, var(--success)); }
.ap-state.on i  { background:var(--ind-ok-ink, var(--success)); box-shadow:0 0 0 3px rgba(16,185,129,.16); }
.ap-state.off { color:var(--ind-warn-ink, var(--warn)); }
.ap-state.off i { background:var(--ind-warn-ink, var(--warn)); box-shadow:0 0 0 3px rgba(245,158,11,.18); }

.ap-grid {
    display:grid; grid-template-columns:repeat(auto-fit, minmax(270px, 1fr));
    gap:12px; padding:16px 18px;
}
.ap-kv { display:flex; flex-direction:column; gap:5px; min-width:0; }
.ap-kv .k {
    font-size:11px; font-weight:800; letter-spacing:.05em; text-transform:uppercase;
    color:var(--text-mute); display:flex; align-items:baseline; gap:6px;
}
.ap-kv .k i { font-style:normal; font-weight:600; letter-spacing:0; text-transform:none; font-size:11px; opacity:.85; }

/* A machine value and its copy button. direction:ltr because a base64 token
   read right-to-left is not the same string, whatever the page language. */
.ap-val {
    display:flex; align-items:center; gap:6px; min-width:0;
    background:var(--bg-inset); border:1px solid var(--border);
    border-radius:8px; padding:7px 8px 7px 10px;
}
.ap-val code {
    flex:1 1 auto; min-width:0; font-family:var(--mono); font-size:12px; font-weight:600;
    color:var(--text); background:none; border:none; padding:0;
    direction:ltr; unicode-bidi:embed;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.ap-val.big code { font-size:13.5px; white-space:normal; word-break:break-all; }
.ap-val.ok code { color:var(--ind-ok-ink, var(--success)); }
.ap-val .ap-since { flex:0 0 auto; font-style:normal; font-size:10.5px; color:var(--text-mute); }
.ap-copy {
    flex:0 0 auto; width:26px; height:26px; padding:0; cursor:pointer;
    display:inline-flex; align-items:center; justify-content:center;
    background:var(--bg-panel); color:var(--text-mute);
    border:1px solid var(--border-2); border-radius:7px;
    transition:color .12s, border-color .12s;
}
.ap-copy svg { width:13px; height:13px; }
.ap-copy:hover { color:var(--accent); border-color:var(--accent); }
.ap-copy.done { color:var(--ind-ok-ink, var(--success)); border-color:var(--ind-ok-ink, var(--success)); }
.ap-empty { color:var(--text-mute); font-size:12.5px; }

.ap-prices { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:0 18px 14px; }
.ap-prices .k { font-size:11px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; color:var(--text-mute); }
.ap-price { font-size:12.5px; font-weight:700; background:var(--accent-soft);
            color:var(--ind-accent-ink, var(--accent)); border-radius:20px; padding:4px 11px; }

.ap-actions { display:flex; align-items:center; gap:9px; flex-wrap:wrap; padding:0 18px 12px; }
.ap-inline { display:inline; }
.ap-warn { margin:0; padding:0 18px 16px; font-size:11.5px; color:var(--text-mute); }
.ap-warn code { font-size:11px; }

/* The one render on which a freshly generated token exists in readable form. */
.ap-once {
    margin-bottom:18px; padding:15px 17px; border-radius:12px;
    background:var(--warn-soft, rgba(245,158,11,.16));
    border:1px solid var(--ind-warn-ink, var(--warn)); border-inline-start-width:3px;
}
.ap-once-h { display:flex; align-items:center; gap:8px; margin-bottom:9px;
             color:var(--ind-warn-ink, var(--warn)); font-size:13px; }
.ap-once-h svg { width:16px; height:16px; flex:0 0 16px; }
.ap-once p { margin:9px 0 0; font-size:12px; color:var(--text-dim); }

.ap-tbl { min-width:1000px; }
.ap-tbl td { vertical-align:middle; }
.ap-tbl .ap-val { padding:5px 6px 5px 9px; max-width:260px; }
.ap-tbl .ap-val code { font-size:11.5px; }
.ap-id  { font-family:var(--mono); font-weight:800; white-space:nowrap; }
.ap-t   { white-space:nowrap; font-variant-numeric:tabular-nums; color:var(--text-dim); font-size:12.5px; }
.ap-took, .ap-chg { white-space:nowrap; font-variant-numeric:tabular-nums; }
.ap-why { font-size:12.5px; color:var(--ind-bad-ink, var(--danger)); }
.ap-none { text-align:center; padding:34px; color:var(--text-mute); }

@media (max-width:640px) {
    .ap-grid { grid-template-columns:1fr; padding:14px; }
    .ap-actions, .ap-prices { padding-inline:14px; }
}


/* ===========================================================================
   THE SAME CORRECTIONS, FOR THE CUSTOMER DASHBOARD
   dashboard.php loads this stylesheet too, but its container is .dash-page and it
   has no .adm-main anywhere, so every token and every rule above missed it:
   measured at 2.36:1 on the KPI labels and 4.04:1 on the table headers.
   Generated by replaying the block above with the container renamed, so the
   two can never drift; edit the block above and re-apply, never this copy.
   .dash-page is safe to include for the same reason .adm-main was: there is no
   dark gradient panel beneath it, which is the only thing the narrow scope
   was protecting.
   =========================================================================== */
/* ===========================================================================
   MUTED TEXT IN THE CONTENT COLUMN
   --text-mute is an indicator grey inherited from app.css: #9ca3af in light,
   #94a3b8 in dark. Every admin table header, every .mini under a cell, every
   .kpi-l and .fld .hint is painted with it at 10-12px, and measured on the
   surfaces they actually sit on that is 2.43:1 light and 4.04:1 dark -- both
   under the 4.5:1 small text needs. These are the text-weight versions of the
   same greys, already measured for the operation-detail block above.

   SCOPED TO .dash-page, NOT .adm, ON PURPOSE. .adm-side is painted with a dark
   gradient in BOTH themes, and its .foot uses --text-mute too; darkening the
   token for the light theme site-wide would fix the tables and make the
   sidebar footer unreadable on that dark panel. The sidebar keeps the light
   grey that is correct against its own backdrop.
   =========================================================================== */
.dash-page { --text-mute: #5A6473; }              /* 5.71:1 on --bg-card, 5.92:1 on --bg-panel */
[data-theme="dark"] .dash-page { --text-mute: #A8B4C6; }  /* 4.96:1 on --bg-card, 8.63:1 on --bg-root */

/* ---------------------------------------------------------------------------
   INDICATOR HUES USED AS TEXT
   --success / --danger / --warn are chosen to be seen as a DOT or a FILL, and
   they are correct there. The status pills and the danger button reuse the
   same three as INK over a 12-16% wash of themselves, and measured on the
   surfaces those actually land on none of the six theme/state pairs reaches
   4.5:1 -- the green "live" pill is 2.22:1 in light, and every one of the
   three is under 4.2:1 over --bg-card in dark, which is the hovered table row.

   These are the same hues walked along their own lightness ramp until the
   worst surface of the three (--bg-panel, --bg-card, --bg-inset) clears 4.6:1,
   so a pill reads the same whether it sits on a card, a panel or a hovered row.
   The dot and fill colours are deliberately NOT changed.

   .btn.danger:hover swaps the wash for a solid fill, and #fff on it was 3.76:1
   in light and 2.77:1 in dark. The fill becomes the ink colour and the label
   takes whichever of white/near-black the theme needs.
   --------------------------------------------------------------------------- */
.dash-page {
    --ind-ok-ink:   #0E6C4D;   /* >=4.64:1 on its wash over panel/card/inset */
    --ind-bad-ink:  #B81717;   /* >=4.63:1 */
    --ind-warn-ink: #82560B;   /* >=4.68:1 */
    --ind-bad-on:   #ffffff;   /* 6.62:1 on a solid #B81717 */
}
[data-theme="dark"] .dash-page {
    --ind-ok-ink:   #58DBAB;   /* >=4.62:1 */
    --ind-bad-ink:  #FAA2A2;   /* >=4.60:1 */
    --ind-warn-ink: #F8B84D;   /* >=4.60:1 */
    --ind-bad-on:   #0f172a;   /* 8.7:1 on a solid #FAA2A2 */
}
.dash-page .pill2.success,
.dash-page .pill2.active     { color:var(--ind-ok-ink); }
.dash-page .pill2.failed,
.dash-page .pill2.blocked,
.dash-page .pill2.disabled   { color:var(--ind-bad-ink); }
.dash-page .pill2.running    { color:var(--ind-warn-ink); }
.dash-page .btn.danger       { color:var(--ind-bad-ink); }
.dash-page .btn.danger:hover { background:var(--ind-bad-ink); color:var(--ind-bad-on); }

/* --accent is the brand blue, and as a FILL behind white it is right. As INK
   over its own 8-15% wash it is 4.44:1 on a card and 3.77:1 on an inset in
   light, and 2.41:1 on a card in dark -- so the operation-id badges, the info
   flash and the active tab label were all under the line. Same treatment: the
   same hue walked until the worst of the three surfaces clears 4.6:1. The fill,
   the focus ring and the icon tints keep the vivid --accent. */
.dash-page { --ind-accent-ink: #1453DD; }              /* >=4.62:1 over its wash */
[data-theme="dark"] .dash-page { --ind-accent-ink: #98BDFA; }  /* >=4.64:1 */

.dash-page .badge,
.dash-page .flash.info,
.dash-page .section-title,
.dash-page .api-guide a,
.dash-page .tabs .tab.active,
.dash-page .btn.ghost:hover,
.dash-page .card2.accent .n { color:var(--ind-accent-ink); }

/* The KPI numbers are 26px, so they only owe 3:1 -- but --success is 2.54:1
   and --warn 2.919:1 on --bg-panel in light, so two of the four states missed
   even that. The ink versions clear 4.5:1, comfortably past what this size
   needs, and keep each state its own colour. */
.dash-page .card2.good .n { color:var(--ind-ok-ink); }
.dash-page .card2.bad .n  { color:var(--ind-bad-ink); }
.dash-page .card2.warn .n { color:var(--ind-warn-ink); }

/* An <a> with no class falls back to the UA's #0000EE, which is 1.90:1 on the
   dark panel -- a link added to any admin view is unreadable in dark until
   someone remembers to colour it. Colour every link in the content column
   once, so the next one added is right by default. The button classes set
   their own colour at higher specificity and are unaffected. */
/* NOT every <a>: the dashboard styles several anchors AS BUTTONS with their
   own fill and their own label colour (.ac-btn, .dbtn, .btn). Recolouring
   those repainted the Add-Credit button's white text brand-blue on gold --
   1.23:1, measured, and worse than what it replaced. The admin copy of this
   rule is safe only because no such anchor exists there. */
.dash-page a:not([class]) { color:var(--ind-accent-ink); }

/* ---------------------------------------------------------------------------
   SOLID BUTTON FILLS
   .btn.primary is #fff on --accent. That is 5.17:1 in light and 3.68:1 in
   dark, so the main action on nine tabs was the least readable thing on the
   screen in the dark theme. The dark fill is the same hue taken down until
   white clears 4.6:1 on it, rather than swapping the label to dark, so a
   primary button reads the same way in both themes. It still sits at 3.6:1
   against --bg-root, past the 3:1 a control needs.

   .btn.danger-solid replaces an inline background:var(--danger);color:#fff on
   the settings "clear activity logs" button, which was 3.76:1 in light and
   2.77:1 in dark -- a destructive action was the hardest label to read.
   --------------------------------------------------------------------------- */
.dash-page { --btn-primary-bg: var(--accent); }
[data-theme="dark"] .dash-page { --btn-primary-bg: #1A6DF4; }   /* #fff 4.63:1 */
.dash-page .btn.primary { background:var(--btn-primary-bg); }
.dash-page .btn.danger-solid { background:var(--ind-bad-ink); color:var(--ind-bad-on); font-weight:800; }
.dash-page .btn.danger-solid:hover { filter:brightness(1.08); }

/* Two more that live in app.css and therefore cannot be edited there: app.php
   loads app.css and is frozen, so a fix at the source would change the tool.
   Overridden here instead, where only the dashboard can see them.
     .dbtn.gold  --gold on its own 10% tint  = 2.67:1
     .ac-btn.af  #fff on solid --success     = 2.54:1
   index3d.css:959 records the same two numbers, but that file is orphaned and
   loaded by nothing, so the fix never reached a page. */
.dash-page .dbtn.gold { color:var(--ind-warn-ink, var(--gold)); }
.dash-page .dbtn.gold:hover { background:var(--ind-warn-ink, var(--gold)); color:var(--ind-bad-on, #fff); }
.dash-page .ac-btn.af { background:var(--ind-ok-ink, var(--success)); color:var(--ind-bad-on, #fff); }
/* The plain Add-Credit button is #fff on --accent, which is 3.68:1 in dark --
   the same defect .btn.primary had, so it takes the same corrected fill. */
.dash-page .ac-btn { background:var(--btn-primary-bg, var(--accent)); }

/* ===========================================================================
   THE RESELLER PANEL (reseller.php)
   Its own page, but built from the same .panel / .cards / .tbl vocabulary as
   the dashboard and the admin, so it reads as one product. Only the shell --
   header, section nav, and the two-level server list -- is new.
   =========================================================================== */
.rs-shell { max-width:1180px; margin:0 auto; padding:22px 20px 60px; }

/* The API pane is authored as a dashboard tab, so it ships hidden. On this
   page there are no tabs to switch between and every section is always open. */
.rs-page .tab-pane { display:block; }

.rs-top {
    display:flex; align-items:center; justify-content:space-between; gap:14px;
    flex-wrap:wrap; margin-bottom:16px;
}
.rs-brand { display:flex; align-items:center; gap:11px; min-width:0; }
.rs-logo { display:inline-flex; width:28px; height:28px; color:var(--accent); flex:0 0 28px; }
.rs-brand b { font-size:16px; font-weight:800; letter-spacing:-.01em; }
.rs-brand .sub { font-size:12px; color:var(--text-mute); }
.rs-acts { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.rs-bal {
    font-size:12.5px; font-weight:700; color:var(--text-dim);
    background:var(--bg-card); border:1px solid var(--border);
    border-radius:20px; padding:6px 13px; white-space:nowrap;
}
.rs-bal b { color:var(--text); font-variant-numeric:tabular-nums; }

.rs-nav {
    display:flex; gap:5px; flex-wrap:wrap; margin-bottom:20px;
    padding-bottom:12px; border-bottom:1px solid var(--border);
}
.rs-nav a {
    font-size:13px; font-weight:700; text-decoration:none;
    color:var(--text-dim); background:var(--bg-card);
    border:1px solid var(--border); border-radius:20px; padding:6px 14px;
}
.rs-nav a:hover { border-color:var(--accent); color:var(--ind-accent-ink, var(--accent)); }

.rs-foot { margin-top:28px; padding-top:18px; border-top:1px solid var(--border);
           color:var(--text-mute); font-size:12.5px; }

/* ---- the two-level server list ---- */
.ap-svc { padding:6px 18px 14px; }
.ap-svc-h { font-size:11px; font-weight:800; letter-spacing:.06em; text-transform:uppercase;
            color:var(--text-mute); margin:10px 0 8px; }
.ap-prov { border:1px solid var(--border); border-radius:11px; margin-bottom:10px; overflow:hidden; }
.ap-prov-h {
    display:flex; align-items:center; gap:10px; flex-wrap:wrap;
    background:var(--bg-card); padding:9px 13px; border-bottom:1px solid var(--border);
}
.ap-prov-h code {
    font-family:var(--mono); font-size:12px; font-weight:700; color:var(--ind-accent-ink, var(--accent));
    background:var(--accent-soft); border:none; border-radius:6px; padding:2px 8px;
    direction:ltr; unicode-bidi:embed;
}
.ap-prov-n { font-size:13px; font-weight:700; }
.ap-srvs { display:flex; flex-wrap:wrap; gap:7px; padding:11px 13px; }
.ap-srv {
    display:inline-flex; align-items:center; gap:7px;
    background:var(--bg-inset); border:1px solid var(--border);
    border-radius:9px; padding:6px 10px; font-size:12px;
}
.ap-srv code {
    font-family:var(--mono); font-size:11.5px; font-weight:700; color:var(--text);
    background:none; border:none; padding:0; direction:ltr; unicode-bidi:embed;
}
.ap-srv b { font-weight:600; color:var(--text-dim); }
.ap-srv i { font-style:normal; font-size:11px; color:var(--text-mute); }
/* Offline machines stay listed rather than vanishing: a reseller who pinned one
   needs to see that it is down, not to find it silently missing. */
.ap-srv.down { opacity:.62; }
.ap-srv.down code { color:var(--ind-bad-ink, var(--danger)); text-decoration:line-through; }

@media (max-width:640px) {
    .rs-shell { padding:16px 12px 48px; }
    .ap-svc { padding-inline:14px; }
}


/* ===========================================================================
   THE SAME CORRECTIONS, FOR THE SIGN-IN PAGES
   login.php, login-admin.php and login-reseller.php use .auth-* and carry no
   .adm-main or .dash-page, so every token above missed them too: .auth-sub,
   .auth-alt, .auth-foot a and .reg-sub all sat at 2.54:1.
   SAFE here, unlike .adm-side: .auth-visual is a dark gradient but paints its
   own text with explicit white alphas and never reads --text-mute, so
   darkening the token cannot touch it.
   Generated by replaying the block above with the container renamed.
   =========================================================================== */
/* ===========================================================================
   MUTED TEXT IN THE CONTENT COLUMN
   --text-mute is an indicator grey inherited from app.css: #9ca3af in light,
   #94a3b8 in dark. Every admin table header, every .mini under a cell, every
   .kpi-l and .fld .hint is painted with it at 10-12px, and measured on the
   surfaces they actually sit on that is 2.43:1 light and 4.04:1 dark -- both
   under the 4.5:1 small text needs. These are the text-weight versions of the
   same greys, already measured for the operation-detail block above.

   SCOPED TO .auth-body, NOT .adm, ON PURPOSE. .adm-side is painted with a dark
   gradient in BOTH themes, and its .foot uses --text-mute too; darkening the
   token for the light theme site-wide would fix the tables and make the
   sidebar footer unreadable on that dark panel. The sidebar keeps the light
   grey that is correct against its own backdrop.
   =========================================================================== */
.auth-body { --text-mute: #5A6473; }              /* 5.71:1 on --bg-card, 5.92:1 on --bg-panel */
[data-theme="dark"] .auth-body { --text-mute: #A8B4C6; }  /* 4.96:1 on --bg-card, 8.63:1 on --bg-root */

/* ---------------------------------------------------------------------------
   INDICATOR HUES USED AS TEXT
   --success / --danger / --warn are chosen to be seen as a DOT or a FILL, and
   they are correct there. The status pills and the danger button reuse the
   same three as INK over a 12-16% wash of themselves, and measured on the
   surfaces those actually land on none of the six theme/state pairs reaches
   4.5:1 -- the green "live" pill is 2.22:1 in light, and every one of the
   three is under 4.2:1 over --bg-card in dark, which is the hovered table row.

   These are the same hues walked along their own lightness ramp until the
   worst surface of the three (--bg-panel, --bg-card, --bg-inset) clears 4.6:1,
   so a pill reads the same whether it sits on a card, a panel or a hovered row.
   The dot and fill colours are deliberately NOT changed.

   .btn.danger:hover swaps the wash for a solid fill, and #fff on it was 3.76:1
   in light and 2.77:1 in dark. The fill becomes the ink colour and the label
   takes whichever of white/near-black the theme needs.
   --------------------------------------------------------------------------- */
.auth-body {
    --ind-ok-ink:   #0E6C4D;   /* >=4.64:1 on its wash over panel/card/inset */
    --ind-bad-ink:  #B81717;   /* >=4.63:1 */
    --ind-warn-ink: #82560B;   /* >=4.68:1 */
    --ind-bad-on:   #ffffff;   /* 6.62:1 on a solid #B81717 */
}
[data-theme="dark"] .auth-body {
    --ind-ok-ink:   #58DBAB;   /* >=4.62:1 */
    --ind-bad-ink:  #FAA2A2;   /* >=4.60:1 */
    --ind-warn-ink: #F8B84D;   /* >=4.60:1 */
    --ind-bad-on:   #0f172a;   /* 8.7:1 on a solid #FAA2A2 */
}
.auth-body .pill2.success,
.auth-body .pill2.active     { color:var(--ind-ok-ink); }
.auth-body .pill2.failed,
.auth-body .pill2.blocked,
.auth-body .pill2.disabled   { color:var(--ind-bad-ink); }
.auth-body .pill2.running    { color:var(--ind-warn-ink); }
.auth-body .btn.danger       { color:var(--ind-bad-ink); }
.auth-body .btn.danger:hover { background:var(--ind-bad-ink); color:var(--ind-bad-on); }

/* --accent is the brand blue, and as a FILL behind white it is right. As INK
   over its own 8-15% wash it is 4.44:1 on a card and 3.77:1 on an inset in
   light, and 2.41:1 on a card in dark -- so the operation-id badges, the info
   flash and the active tab label were all under the line. Same treatment: the
   same hue walked until the worst of the three surfaces clears 4.6:1. The fill,
   the focus ring and the icon tints keep the vivid --accent. */
.auth-body { --ind-accent-ink: #1453DD; }              /* >=4.62:1 over its wash */
[data-theme="dark"] .auth-body { --ind-accent-ink: #98BDFA; }  /* >=4.64:1 */

.auth-body .badge,
.auth-body .flash.info,
.auth-body .section-title,
.auth-body .api-guide a,
.auth-body .tabs .tab.active,
.auth-body .btn.ghost:hover,
.auth-body .card2.accent .n { color:var(--ind-accent-ink); }

/* The KPI numbers are 26px, so they only owe 3:1 -- but --success is 2.54:1
   and --warn 2.919:1 on --bg-panel in light, so two of the four states missed
   even that. The ink versions clear 4.5:1, comfortably past what this size
   needs, and keep each state its own colour. */
.auth-body .card2.good .n { color:var(--ind-ok-ink); }
.auth-body .card2.bad .n  { color:var(--ind-bad-ink); }
.auth-body .card2.warn .n { color:var(--ind-warn-ink); }

/* An <a> with no class falls back to the UA's #0000EE, which is 1.90:1 on the
   dark panel -- a link added to any admin view is unreadable in dark until
   someone remembers to colour it. Colour every link in the content column
   once, so the next one added is right by default. The button classes set
   their own colour at higher specificity and are unaffected. */
.auth-body a:not([class]) { color:var(--ind-accent-ink); }

/* ---------------------------------------------------------------------------
   SOLID BUTTON FILLS
   .btn.primary is #fff on --accent. That is 5.17:1 in light and 3.68:1 in
   dark, so the main action on nine tabs was the least readable thing on the
   screen in the dark theme. The dark fill is the same hue taken down until
   white clears 4.6:1 on it, rather than swapping the label to dark, so a
   primary button reads the same way in both themes. It still sits at 3.6:1
   against --bg-root, past the 3:1 a control needs.

   .btn.danger-solid replaces an inline background:var(--danger);color:#fff on
   the settings "clear activity logs" button, which was 3.76:1 in light and
   2.77:1 in dark -- a destructive action was the hardest label to read.
   --------------------------------------------------------------------------- */
.auth-body { --btn-primary-bg: var(--accent); }
[data-theme="dark"] .auth-body { --btn-primary-bg: #1A6DF4; }   /* #fff 4.63:1 */
.auth-body .btn.primary { background:var(--btn-primary-bg); }
.auth-body .btn.danger-solid { background:var(--ind-bad-ink); color:var(--ind-bad-on); font-weight:800; }
.auth-body .btn.danger-solid:hover { filter:brightness(1.08); }

/* Three that the tokens alone do not reach.
     .auth-btn   #fff on --accent          3.68:1 in dark -- the .btn.primary
                                           defect again, so the same fill.
     .av-foot    rgba(255,255,255,.4) on   3.80:1 -- its own colour, on the
                 the dark visual panel     gradient, nothing to do with tokens.
     .auth-foot a 18px tall                under the 24px a standalone link
                                           should offer. */
.auth-body .auth-btn:not(.ghost) { background:var(--btn-primary-bg, var(--accent)); }
.auth-body .av-foot { color:rgba(255,255,255,.72); }
.auth-body .auth-foot a { padding:4px 2px; }

/* The register popup and the ghost button, which the token aliases do not
   reach because they name their own colours:
     .reg-go        #fff on --accent           1.93:1 in dark
     .reg-badge     --accent on its own wash   3.28:1 in dark
     .auth-btn.ghost                           3.12:1 in dark, measured
     .auth-link     16px tall                  under the 24px a standalone
                                               control should offer */
.auth-body .reg-go { background:var(--btn-primary-bg, var(--accent)); }
.auth-body .reg-badge { color:var(--ind-accent-ink, var(--accent)); }
.auth-body .auth-btn.ghost { color:var(--text); }
.auth-body .auth-link { padding:4px 2px; }

/* The "how this works" strip on Resellers and Staff Users. Deliberately not a
   .panel: it is guidance, and giving it the same surface as the data would make
   the eye treat it as another table to read every visit. */
.rs-how {
    margin-bottom:18px; padding:15px 18px; border-radius:12px;
    background:var(--accent-soft); border:1px solid var(--border);
    border-inline-start:3px solid var(--ind-accent-ink, var(--accent));
}
.rs-how.compact { margin-bottom:16px; padding:13px 16px; }
.rs-how-h { display:flex; align-items:center; gap:8px; margin-bottom:9px;
            color:var(--ind-accent-ink, var(--accent)); font-size:13px; }
.rs-how-h svg { width:16px; height:16px; flex:0 0 16px; }
.rs-how-steps { margin:0; padding-inline-start:20px; display:flex; flex-direction:column; gap:7px; }
.rs-how-steps li { font-size:13px; color:var(--text-dim); line-height:1.65; }
.rs-how-steps b { color:var(--text); }
.rs-how-note { margin:10px 0 0; font-size:12.5px; color:var(--text-dim); line-height:1.65; }
.rs-how.compact .rs-how-note { margin-top:0; }
.rs-how-note b { color:var(--text); }
@media (max-width:640px) { .rs-how { padding:13px 14px; } }

/* ===========================================================================
   RESELLER PANEL: USERS, FORMS AND FIGURES (reseller.php)
   Same vocabulary as the rest of the panel -- .panel, .rs-f/.inp, .tbl -- with
   only what the new sections need on top.
   =========================================================================== */
/* app.css pins html/body to the viewport for the tool; this page scrolls. */
html:has(body.rs-page), body.rs-page { height:auto; min-height:100vh; overflow:auto; width:auto; }

.tbl .num { text-align:end; font-variant-numeric:tabular-nums; white-space:nowrap; }

.rp-sec, #user, #keys, #log, #servers, #credit { scroll-margin-top:14px; }
.rp-balbtn { cursor:pointer; font-family:inherit; }
.rp-balbtn:hover { border-color:var(--accent); color:var(--text); }

.rp-note { margin:0; padding:12px 18px 0; font-size:12.5px; line-height:1.75; color:var(--text-dim); }
.rp-note:last-child { padding-bottom:16px; }
.rp-hint { margin:0 0 12px; font-size:12px; line-height:1.7; color:var(--text-mute); }
.rp-signin { padding-top:12px; padding-bottom:4px; }

.rp-form { padding:16px 18px 18px; margin:0; }
.rp-form-h { font-size:12px; font-weight:800; color:var(--text); margin-bottom:12px; }
.rp-form-foot { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:14px; }
.rp-create { border-top:1px solid var(--border); margin-top:14px; }

.rp-bar { display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap;
          padding:12px 18px; border-top:1px solid var(--border); }
.rp-search { display:flex; gap:6px; flex-wrap:wrap; align-items:center; margin:0; }
.rp-search input { padding:8px 11px; min-width:230px; border:1px solid var(--border-2); border-radius:8px;
                   background:var(--bg-card); color:var(--text); font-family:inherit; font-size:12.5px; }

.rp-users { min-width:940px; }
.rp-users td { vertical-align:middle; }
.rp-user-cell b { display:block; font-weight:700; color:var(--text); }
.rp-user-cell .mini { display:block; unicode-bidi:plaintext; }
.rp-users tr.rp-on td { background:var(--accent-soft); }
.rp-move { display:flex; align-items:center; gap:6px; flex-wrap:nowrap; margin:0; }
.rp-move input { width:96px; height:32px; padding:0 9px; border:1px solid var(--border-2); border-radius:7px;
                 background:var(--bg-card); color:var(--text); font-family:inherit; font-size:12.5px;
                 font-variant-numeric:tabular-nums; }
.rp-move input:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }

/* Isolated LTR: in the Persian layout "+5" otherwise renders as "5+". */
.amt { font-variant-numeric:tabular-nums; white-space:nowrap; font-weight:700; direction:ltr; unicode-bidi:isolate; }
.amt.pos { color:var(--ind-ok-ink, var(--success)); }
.amt.neg { color:var(--ind-bad-ink, var(--danger)); }
.pill2.transfer { background:var(--accent-soft); color:var(--ind-accent-ink, var(--accent)); }

.rp-prices { display:grid; gap:12px; grid-template-columns:repeat(auto-fit, minmax(230px, 1fr)); padding:16px 18px 4px; }
.rp-price { display:flex; flex-direction:column; gap:5px; padding:12px 14px; border:1px solid var(--border);
            border-radius:11px; background:var(--bg-card); font-size:12.5px; color:var(--text-dim); }
.rp-price b { font-size:13px; font-weight:800; color:var(--text); letter-spacing:.03em; }
.rp-price strong { color:var(--text); font-variant-numeric:tabular-nums; }

.rp-sel { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.rp-sel .panel-h .pill2 { margin-inline-start:8px; vertical-align:middle; }
.rp-back { font-size:12.5px; font-weight:700; text-decoration:none; color:var(--ind-accent-ink, var(--accent)); }
.rp-back:hover { text-decoration:underline; }
.rp-kv { display:grid; gap:14px; grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); padding:16px 18px; }
.rp-kv > div { display:flex; flex-direction:column; gap:4px; min-width:0; }
.rp-kv .k { font-size:11px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; color:var(--text-mute); }
.rp-kv .v { font-size:15px; font-weight:800; color:var(--text); font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
.rp-kv .v.mini { font-size:12.5px; font-weight:600; margin:0; color:var(--text-dim); }
/* A 1px grid gap over the border colour draws the dividers between the boxes.
   Always two columns (one on a phone): four boxes in an auto-fit grid left the
   fourth alone on its row beside an empty cell painted in the border colour. */
.rp-cols { display:grid; gap:1px; background:var(--border); grid-template-columns:repeat(2, minmax(0, 1fr));
           border-top:1px solid var(--border); border-bottom:1px solid var(--border); }
.rp-cols > .rp-box { background:var(--bg-panel); }
.rp-sub-h { padding:14px 18px 8px; font-size:12px; font-weight:800; color:var(--text); }

.rp-grid2 { display:grid; gap:16px; grid-template-columns:repeat(auto-fit, minmax(320px, 1fr)); margin-bottom:22px; }
.rp-grid2 > .panel { margin-bottom:0; }

.rp-opm { width:760px; }
.rp-opm h3 { margin:0 0 4px; }
.rp-opm #omMeta { margin-bottom:16px; }
.rp-opm .section-title { padding:0 0 8px; }
.rp-opm .om-cards { margin-bottom:18px; grid-template-columns:repeat(3, 1fr); gap:12px; }
.rp-opm .om-cards .info-card .val { word-break:break-all; }
.rp-oplog { background:var(--bg-inset); border-radius:8px; padding:14px; font-family:var(--mono); font-size:11.5px;
            color:var(--text-dim); white-space:pre-wrap; word-break:break-word; max-height:300px; overflow:auto;
            margin:0; direction:ltr; text-align:start; }
.ac-sub { color:var(--text-mute); font-size:13px; margin:4px 0 18px; }
.rp-contact { margin:0 0 4px; }

@media (max-width:680px) { .rp-opm .om-cards { grid-template-columns:1fr 1fr; } }
@media (max-width:640px) {
    .rp-search input { min-width:0; flex:1 1 160px; }
    .rp-form, .rp-kv, .rp-bar { padding-inline:14px; }
    .rp-cols { grid-template-columns:1fr; }
}
@media (max-width:460px) { .rp-opm .om-cards { grid-template-columns:1fr; } }

/* ---- admin: customers list, one customer's page, reseller credit box ---- */
.cu-tbl { min-width:980px; }
.cu-tbl td { vertical-align:middle; }
.cu-name { font-weight:700; color:var(--text); text-decoration:none; display:block; }
.cu-name:hover { color:var(--ind-accent-ink, var(--accent)); text-decoration:underline; }
.cu-acts { display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.cu-acts .rp-move input { width:84px; }
.rp-move input.cu-note { width:120px; }
.cd-top { margin:-4px 0 12px; }
.cd-title { display:inline-flex; align-items:center; gap:8px; flex-wrap:wrap; }
.cd-notes { height:auto; min-height:74px; padding:9px 12px; resize:vertical; line-height:1.6; }

/* ---- the tool owner's connection steps ----
   Four numbered steps down the left, each with its own heading and body. The
   number is a badge rather than a list marker because the steps are not a list
   the browser should renumber -- they are named stages an integrator follows in
   order, and step 3 has a code block in it that a real <ol> indents badly. */
.ot-steps { display:flex; flex-direction:column; gap:22px; padding:18px 18px 4px; }
.ot-step { display:flex; gap:14px; align-items:flex-start; }
.ot-step > b {
    flex:0 0 auto; width:28px; height:28px; border-radius:50%;
    display:grid; place-items:center; font-size:.8rem; font-weight:800;
    background:var(--accent-soft); color:var(--ind-accent-ink, var(--accent));
    margin-top:2px;
}
.ot-step > div { flex:1 1 auto; min-width:0; }
.ot-step h4 { margin:2px 0 4px; font-size:.95rem; font-weight:800; color:var(--text); }
.ot-step p  { margin:0 0 10px; font-size:.86rem; line-height:1.7; color:var(--text-dim); }
.ot-step .ap-grid { padding:0; }
.ot-step .rs-pre { margin:0; }

/* the configblob, per mode (2026-10-04): which field, read with what, an example */
.ot-mode { border:1px solid var(--border); border-radius:11px; padding:10px 12px 12px; margin:0 0 12px; background:var(--bg-card); }
.ot-mode-h { font-size:.86rem; font-weight:800; color:var(--text); margin-bottom:8px; display:flex; align-items:center; gap:8px; }
.ot-mode .tbl-wrap { margin-bottom:10px; }
.ot-fields td { font-size:.8rem; vertical-align:top; }
.ot-fields code { font-size:.76rem; white-space:nowrap; }
.ot-opt { font-size:.68rem; font-weight:700; color:var(--text-mute); border:1px solid var(--border); border-radius:6px; padding:0 5px; margin-inline-start:4px; }
.ot-relink { border-bottom:1px dashed var(--border); margin-bottom:6px; }
/* the server column of the work log, and the servers table */
.ap-srvc { white-space:nowrap; font-size:.8rem; }
.ap-srvc code { font-size:.72rem; margin-inline-start:4px; }
.ap-srvtbl td { vertical-align:middle; }

/* ---- admin: one customer, managed ----
   The two halves of a customer's page (Settings / Information) and the pieces
   only they use. .cu-b is the action button in a list row: the same .btn, given
   an icon and a label that hides on a narrow screen so the column stops growing
   while the buttons stay reachable. */
.cu-b { gap:6px; }
.cu-b svg { width:14px; height:14px; flex:0 0 auto; }
/* The three manage buttons stay on one line and the table scrolls instead: a
   row that grows to two lines when a name is long makes the whole list jagged,
   and .tbl-wrap already scrolls sideways on a narrow screen. */
.cu-tbl { min-width:1160px; }
.cu-tbl .cu-acts { flex-wrap:nowrap; }
/* .pill2 capitalises every word, which is right for the one-word statuses it
   was made for (active, blocked, failed) and wrong for a pill holding a
   sentence -- "Created By Admin", "Profile Changed By Admin". .txt opts out. */
.pill2.txt { text-transform:none; }
.cd-src .mini { display:block; margin-top:3px; unicode-bidi:plaintext; }

.cd-tabs { display:flex; gap:4px; margin:0 0 18px; border-bottom:1px solid var(--border); }
.cd-tab { display:inline-flex; align-items:center; gap:8px; padding:11px 16px; font-size:.875rem;
          font-weight:600; color:var(--text-mute); text-decoration:none;
          border-bottom:2px solid transparent; margin-bottom:-1px; }
.cd-tab svg { width:16px; height:16px; }
.cd-tab:hover { color:var(--text); }
.cd-tab.active { color:var(--ind-accent-ink, var(--accent)); border-bottom-color:var(--accent); }

.cd-kpis { display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); gap:12px; margin-bottom:22px; }
/* The four settings cards hold very different amounts. Stretched to a common
   height, Credit and Password become mostly empty box. */
.cd-grid { align-items:start; }

.cd-time td:nth-child(2) { white-space:nowrap; }
.cd-time .pill2 { max-width:none; }

.cd-srv:first-of-type { margin-top:4px; border-top:0; padding-top:0; }
.cd-srv-list { display:grid; gap:8px; grid-template-columns:repeat(auto-fit, minmax(240px, 1fr)); margin-top:10px; }

@media (max-width:1100px) { .cd-kpis { grid-template-columns:repeat(2, minmax(0, 1fr)); } }
@media (max-width:760px)  { .cu-b span { display:none; } .cu-b { padding:7px 9px; } }
@media (max-width:520px)  { .cd-kpis { grid-template-columns:1fr; } .cd-tab { padding:10px 12px; } }
.rs-f select.inp { cursor:pointer; }
.rp-pad { padding:0 18px 16px; margin-top:0; }
.rs-credit { flex-wrap:wrap; }
.rs-credit input.cu-note { width:220px; }

/* =====================================================================
   THE STAFF DOOR (login/auth/admin/login-admin.php)

   Shares .auth-body.auth-split with the customer and reseller logins, so
   the three doors are one layout with three different left panels. Only
   what is specific to the staff one lives here.
   ===================================================================== */

/* A darker, quieter field than the customer panel's: this is the door
   nobody should arrive at by accident, and the two soft colour washes
   give it depth without the marketing energy of the customer side. */
.auth-visual.av-admin {
    background:
        radial-gradient(circle at 12% 8%, rgba(37, 99, 235, .18), transparent 46%),
        radial-gradient(circle at 92% 88%, rgba(16, 185, 129, .12), transparent 48%),
        linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
    justify-content: space-between;
}
.auth-visual.av-admin::before { content: none; }   /* the customer panel's ring */

.av-admin .av-copy { margin: auto 0; max-width: 480px; }
.av-lock {
    display: inline-grid;
    place-items: center;
    width: 64px;
    height: 64px;
    margin-bottom: 32px;
    border-radius: 16px;
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .10);
    color: #60a5fa;
}
.av-lock svg { width: 32px; height: 32px; }
.av-admin .av-copy h1 {
    margin: 0 0 16px;
    font-size: clamp(2.25rem, 4vw, 3rem);
    font-weight: 800;
    line-height: 1.08;
    letter-spacing: -.03em;
}
.av-admin .av-copy > p {
    margin: 0;
    font-size: 1.05rem;
    line-height: 1.65;
    color: rgba(255, 255, 255, .66);
}

/* The form column's own controls, out of the card so the card stays one
   idea: the credentials and the button that submits them. */
.auth-top-float {
    position: absolute;
    top: 24px;
    inset-inline-end: 24px;
    display: flex;
    align-items: center;
    gap: 10px;
    z-index: 3;
}
.auth-form-side { position: relative; }

.auth-split .auth-card { width: 400px; max-width: 100%; }
.auth-head { margin-bottom: 28px; }
.auth-head h2 {
    margin: 0 0 8px;
    font-size: 1.875rem;
    font-weight: 800;
    letter-spacing: -.025em;
    color: var(--text);
}
.auth-head p { margin: 0; font-size: .95rem; line-height: 1.55; color: var(--text-mute); }

/* An icon inside the field, not beside it: the label already says what the
   field is, so the glyph is a landmark for the eye scanning down the form
   rather than a second label competing with the first. */
.in-wrap { position: relative; display: flex; align-items: center; }
.in-wrap > svg {
    position: absolute;
    inset-inline-start: 14px;
    width: 20px;
    height: 20px;
    color: var(--text-mute);
    pointer-events: none;
    transition: color .15s;
}
.in-wrap input { padding-inline-start: 44px !important; }
.in-wrap:focus-within > svg { color: var(--accent); }

.auth-form label { margin: 18px 0 8px; font-size: .875rem; font-weight: 600; color: var(--text); }
.auth-form label:first-child { margin-top: 0; }
.auth-form input {
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-panel);
    font-size: 1rem;
}
.auth-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin-top: 24px;
    padding: 14px;
    border-radius: 10px;
    font-size: 1rem;
    font-weight: 600;
}
.auth-btn svg { width: 18px; height: 18px; }
.auth-foot { justify-content: center; margin-top: 24px; }

@media (max-width: 900px) {
    .auth-top-float { top: 14px; inset-inline-end: 14px; }
    .auth-split .auth-card { margin-top: 56px; }
}

/* ---------------------------------------------------------------------------
   REGISTER / VERIFY / RESET — the self-service doors.

   They reuse .auth-card and everything around it; only what the sign-in form
   never needed is added here. The register form is wider because it asks for
   seven things rather than two, and pairs the short ones so the card does not
   become a column of lonely boxes.
   --------------------------------------------------------------------------- */
.auth-card-wide { width:520px; }
.auth-split .auth-card-wide { width:560px; }

.auth-row2 { display:grid; grid-template-columns:1fr 1fr; gap:0 14px; }
@media (max-width:520px) { .auth-row2 { grid-template-columns:1fr; } }

/* A heading inside the form, for the "reach you" pair. */
.auth-sect { margin:20px 0 4px; font-size:11.5px; font-weight:800; letter-spacing:.04em;
    text-transform:uppercase; color:var(--text-dim); }

/* Said under the field it belongs to, in the colour of a problem. Blocks so it
   sits on its own line rather than shoving the next input sideways. */
.auth-fielderr { display:block; margin:-6px 0 10px; font-size:11.5px; line-height:1.6; color:var(--danger); }
.auth-hint     { display:block; margin:-6px 0 10px; font-size:11.5px; line-height:1.6; color:var(--text-mute); }

/* The single-panel pages: a confirmation result has nothing to sell. */
.auth-body.auth-single { display:grid; place-items:center; padding:24px 0; }
.auth-single .auth-card { border:1px solid var(--border); box-shadow:var(--sh); background:var(--bg-panel); padding:30px; }

.auth-done { text-align:center; padding:8px 0 4px; }
.auth-done-ic { font-size:44px; line-height:1; margin-bottom:14px; }
.auth-done h2 { margin:0 0 10px; font-size:19px; font-weight:800; color:var(--text); }
.auth-done p { margin:0 0 10px; font-size:13.5px; line-height:1.8; color:var(--text-dim); }
.auth-done-sub { font-size:12.5px !important; color:var(--text-mute) !important; }
.auth-done .auth-btn { display:inline-block; margin-top:14px; text-decoration:none; text-align:center; }
.auth-done .auth-link { display:block; margin-top:12px; }

/* ---------------------------------------------------------------------------
   SECTION PAGES — the row of pills above the content when a rail section holds
   more than one page (Money: Ledger · Pricing; Website: Where to buy · Ads …).
   Pills rather than a second rail: the pages of a section are siblings, and a
   reader should see all of them at once without opening anything.
   --------------------------------------------------------------------------- */
.adm-subnav { display:flex; flex-wrap:wrap; gap:6px; margin:0 0 18px; padding:4px;
    background:var(--bg-card); border:1px solid var(--border); border-radius:12px; width:fit-content; max-width:100%; }
.adm-subnav a { padding:7px 14px; border-radius:9px; font-size:12.5px; font-weight:700;
    color:var(--text-dim); text-decoration:none; transition:background .12s, color .12s; white-space:nowrap; }
.adm-subnav a:hover { color:var(--text); background:var(--bg-inset); }
.adm-subnav a.on { color:#fff; background:var(--accent); }
@media (max-width:640px) { .adm-subnav { width:100%; } .adm-subnav a { flex:1 1 auto; text-align:center; } }

/* reseller.php (2026-10-03): the credit price line, the DHRU steps, the public ids */
.rp-price1 { margin:0; padding:8px 18px 0; font-size:17px; font-weight:800; color:var(--accent); }
.rp-dhru { margin:14px 18px 4px; padding:12px 14px; border:1px dashed var(--border-2); border-radius:10px; background:var(--bg-card); }
.rp-dhru b { display:block; margin-bottom:6px; font-size:13.5px; }
.rp-dhru ol { margin:0; padding-inline-start:20px; line-height:1.9; font-size:13px; color:var(--text-dim); }
.ap-id.pub { font-family:ui-monospace,Consolas,monospace; white-space:nowrap; font-size:12px; }

/* the two DHRU services, and the username button inside its box */
.rp-dhru .tbl-wrap { margin-top:10px; }
.rp-svc td, .rp-svc th { font-size:12.5px; }
.rp-svc b { display:inline; margin:0; font-size:12.5px; }
.rp-svc code { font-size:11.5px; padding:1px 6px; border-radius:5px; background:var(--bg-panel); border:1px solid var(--border); }
.rp-genwrap { position:relative; display:block; }
.rp-genwrap .inp { width:100%; padding-right:42px; }   /* physical: the box is always ltr */
.rp-gen { position:absolute; right:5px; top:50%; transform:translateY(-50%); width:32px; height:30px;
  display:inline-flex; align-items:center; justify-content:center; padding:0; border:0; border-radius:7px;
  background:transparent; color:var(--accent); cursor:pointer; }
.rp-gen svg { width:17px; height:17px; }
.rp-gen:hover, .rp-gen:focus-visible { background:rgba(37,99,235,.12); outline:none; }
.rp-gen:disabled { cursor:progress; opacity:.6; }
.rp-gen.busy svg { animation:rp-gen-spin .9s linear infinite; }
@keyframes rp-gen-spin { to { transform:rotate(360deg); } }
.hint.rp-gen-err { color:var(--danger); }

/* api/dhru-docs.php */
.dg-page .panel p { margin:0; padding:12px 18px 0; font-size:13px; line-height:1.85; color:var(--text-dim); }
.dg-page .panel p:last-child, .dg-page .panel ul:last-child, .dg-page .panel ol:last-child { padding-bottom:16px; }
.dg-page .panel ul, .dg-page .panel ol { margin:0; padding:10px 18px 0; padding-inline-start:38px; font-size:13px; line-height:1.9; color:var(--text-dim); }
.dg-page .panel .ap-grid { padding-bottom:6px; }
.dg-page .tbl-wrap { margin:12px 18px 16px; }
.dg-page .tbl td { font-size:12.5px; vertical-align:top; }
.dg-page .tbl code { font-size:11.5px; white-space:normal; }
.dg-page .rs-foot a { color:inherit; }

/* ===========================================================================
   THE TOOL OWNER'S API LIST (ownertool.php, 2026-10-05)
   One card open at a time. The folded ones sit side by side in a row ABOVE
   it, each a bar with its icon, name and endpoint (the owner's layout); the
   open card takes the full width below them. A narrow screen wraps the bars
   onto lines of their own. ui.js moves .is-open.
   =========================================================================== */
.ot-lead { margin:0; padding:12px 18px 0; font-size:13px; line-height:1.8; color:var(--text-dim); }
.ot-acc { display:flex; flex-wrap:wrap; align-items:stretch; gap:10px; padding:16px 18px 6px; }
.ot-acc-i {
    --c: 37 99 235; --ci: var(--ind-accent-ink, var(--accent));
    position:relative; flex:1 1 230px; min-width:0; display:flex; overflow:hidden;
    border:1px solid var(--border); border-radius:14px; background:var(--bg-card);
    transition:border-color .2s, box-shadow .2s;
}
.ot-acc-i.t-green  { --c: 16 185 129; --ci: var(--ind-ok-ink, var(--success)); }
.ot-acc-i.t-orange { --c: 245 158 11; --ci: var(--ind-warn-ink, var(--warn)); }
.ot-acc-i::before { content:""; position:absolute; inset:0 0 auto 0; height:3px; background:rgb(var(--c)); opacity:.8; }
.ot-acc-i:not(.is-open):hover { border-color:rgb(var(--c) / .45); box-shadow:0 6px 18px -10px rgb(var(--c) / .5); }
/* the open card: a line of its own, after every folded one */
.ot-acc-i.is-open { flex:1 0 100%; order:1; background:var(--bg-panel); border-color:rgb(var(--c) / .35); }

.ot-acc-tab {
    flex:1; display:flex; flex-direction:row; align-items:center; gap:12px; padding:12px 14px;
    border:0; background:none; font:inherit; color:var(--text); cursor:pointer; text-align:start;
}
.ot-acc-tab:focus-visible { outline:2px solid rgb(var(--c)); outline-offset:-4px; border-radius:inherit; }
.ot-acc-ic {
    flex:0 0 auto; width:36px; height:36px; display:grid; place-items:center; border-radius:10px;
    background:rgb(var(--c) / .14); color:var(--ci);
}
.ot-acc-ic svg { width:18px; height:18px; }
.ot-acc-tab-t { font-size:.92rem; font-weight:800; line-height:1.4; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; }
.ot-acc-tag {
    font-family:var(--mono); font-size:.68rem; font-weight:700; white-space:nowrap;
    border-radius:999px; padding:2px 9px; background:rgb(var(--c) / .12); color:var(--ci);
}
.ot-acc-go {
    flex:0 0 auto; margin-inline-start:auto; width:28px; height:28px; display:grid; place-items:center; border-radius:50%;
    border:1px solid rgb(var(--c) / .32); background:rgb(var(--c) / .1); color:var(--ci);
    transition:background .2s, transform .2s;
}
.ot-acc-go svg { width:14px; height:14px; }
.ot-acc-tab:hover .ot-acc-go { background:rgb(var(--c) / .2); transform:scale(1.08); }
.ot-acc-body { display:none; flex:1; min-width:0; flex-direction:column; gap:4px; padding:18px 18px 14px; }
.ot-acc-i.is-open .ot-acc-tab { display:none; }
.ot-acc-i.is-open .ot-acc-body { display:flex; animation:otAccIn .25s ease both; }
@keyframes otAccIn { from { opacity:0; } to { opacity:1; } }
@media (prefers-reduced-motion: reduce) { .ot-acc-i.is-open .ot-acc-body { animation:none; } }
.ot-acc-head { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:6px; }
.ot-acc-head h3 { margin:0; font-size:1.02rem; font-weight:800; color:var(--text); }
.ot-acc-p { margin:0 0 6px; font-size:13px; line-height:1.85; color:var(--text-dim); }
.ot-acc-body .ot-steps { padding:10px 0 4px; }
.ot-acc-body .ap-grid.ot-flat { padding:8px 0 10px; }
.ot-acc-body .rs-sec { margin-top:14px; }
.ot-acc-foot { display:flex; justify-content:flex-end; margin-top:12px; padding-top:12px; border-top:1px dashed var(--border); }
.ot-doclink {
    display:inline-flex; align-items:center; gap:7px; font-size:12.5px; font-weight:800;
    color:var(--ci); text-decoration:none; padding:7px 12px; border-radius:9px;
    background:rgb(var(--c) / .1); border:1px solid rgb(var(--c) / .28);
}
.ot-doclink:hover { background:rgb(var(--c) / .18); }
.ot-doclink svg, .ot-doc > svg { width:15px; height:15px; flex:0 0 auto; }
[dir="rtl"] .ot-doclink svg, [dir="rtl"] .ot-doc > svg { transform:scaleX(-1); }

/* the documentation, as the landing's own "API documentation" row */
.ot-doc {
    --c: 139 92 246; --ci: var(--ind-accent-ink, var(--accent));
    display:flex; align-items:center; gap:12px; margin:10px 18px 18px; padding:12px 14px;
    border:1px solid rgb(var(--c) / .3); border-radius:12px; background:rgb(var(--c) / .07);
    color:var(--text); text-decoration:none; transition:background .2s, border-color .2s;
}
.ot-doc:hover { background:rgb(var(--c) / .13); border-color:rgb(var(--c) / .5); }
.ot-doc-t { flex:1; min-width:0; display:grid; gap:2px; }
.ot-doc-t b { font-size:13.5px; font-weight:800; }
.ot-doc-t small { font-size:12px; line-height:1.6; color:var(--text-dim); }

/* the account token: its full value, a copy button, and its date as a tooltip */
.ot-tip {
    position:relative; flex:0 0 auto; width:26px; height:26px; display:inline-grid; place-items:center;
    border:1px solid var(--border-2); border-radius:7px; background:var(--bg-panel); color:var(--text-mute); cursor:help;
}
.ot-tip svg { width:13px; height:13px; }
.ot-tip::after {
    content:attr(data-tip); position:absolute; bottom:calc(100% + 8px); inset-inline-end:-4px; z-index:20;
    white-space:nowrap; padding:6px 10px; border-radius:7px; font-size:11.5px; font-weight:700; direction:ltr;
    background:var(--text); color:var(--bg-panel); box-shadow:var(--sh);
    opacity:0; transform:translateY(4px); pointer-events:none; transition:opacity .15s, transform .15s;
}
.ot-tip:hover::after, .ot-tip:focus::after, .ot-tip:focus-visible::after { opacity:1; transform:none; }
.ot-tip:focus-visible { outline:2px solid var(--accent); outline-offset:1px; }
.ot-tokval { overflow:visible; }
.ot-tokold { display:block; margin-top:2px; }
.ot-regen { padding:6px 0 0; }
.ot-regen .hint { font-size:11.5px; }

/* step 5: what a job costs, set by the admin */
.ot-prices { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin:0 0 8px; }
.ot-prices .k { font-size:11px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; color:var(--text-mute); }

/* the activity list */
.ot-act { min-width:900px; }
.ot-search { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin:0 0 12px; }
.ot-search .inp { flex:1 1 240px; max-width:360px; }
.ot-act td { vertical-align:middle; }
.ot-what { white-space:nowrap; }
.ot-srv { display:block; margin-top:4px; font-size:11.5px; color:var(--text-dim); }
.ot-srv code { font-size:10.5px; margin-inline-start:3px; }
.ot-door { display:inline-block; font-size:11.5px; font-weight:800; padding:2px 8px; border-radius:6px; white-space:nowrap;
    background:var(--bg-inset); color:var(--text-dim); border:1px solid var(--border); }
.ot-door.api { background:var(--accent-soft); color:var(--ind-accent-ink, var(--accent)); border-color:transparent; }
.ot-door.xst { background:color-mix(in srgb, var(--success) 13%, transparent); color:var(--ind-ok-ink, var(--success)); border-color:transparent; }
.ot-res { width:230px; max-width:230px; }
.ot-res .ap-why { display:block; font-size:12px; line-height:1.6; }
.ot-res .ap-val { max-width:220px; padding:5px 6px 5px 9px; }
.ot-res .ap-val code { font-size:11.5px; }
.amt.zero { color:var(--text-mute); }

/* the allowed addresses */
.ot-ips { width:100%; min-height:92px; font-family:var(--mono); font-size:12.5px; line-height:1.7; resize:vertical; }
.ot-ipme { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin:8px 0 12px; font-size:12.5px; color:var(--text-dim); }
.ot-ipme code { font-family:var(--mono); font-size:12px; padding:2px 7px; border-radius:6px; background:var(--bg-inset); border:1px solid var(--border); }
#ips { scroll-margin-top:14px; }
#activity, #api-token, #api-balance, #api-errors { scroll-margin-top:14px; }

@media (max-width: 900px) {
    .ot-acc { gap:10px; padding:14px 14px 6px; }
    .ot-acc-body { padding:16px 14px 12px; }
    .ot-doc { margin:8px 14px 14px; }
    .ot-lead { padding-inline:14px; }
}
@media (max-width: 560px) {
    .ot-acc-tab .ot-acc-tag { display:none; }
}
.ot-acc-head h3:focus { outline:none; }
.ot-acc-body .rs-pre.full { max-height:none; }
.ot-errtbl td:first-child code { white-space:normal; word-break:break-word; }
/* a phone has no room for three columns of prose: each code becomes a block */
@media (max-width: 560px) {
    .ot-errtbl thead { display:none; }
    .ot-errtbl, .ot-errtbl tbody, .ot-errtbl tr, .ot-errtbl td { display:block; width:auto; }
    .ot-errtbl tr { padding:10px 2px; border-bottom:1px solid var(--border); }
    .ot-errtbl td { border:0; padding:2px 6px; }
    .ot-errtbl td:nth-child(2) { display:inline-block; font-weight:800; color:var(--text-mute); font-size:11px; }
    .ot-errtbl td:first-child { display:inline-block; }
    .ot-errtbl td:last-child { line-height:1.75; }
}
.ot-ips::placeholder { color:var(--text-mute); opacity:.75; font-family:var(--sans); }
/* Admin -> Resellers & API -> DHRU services (2026-10-05): the form sits in a bare .panel, so it brings its own padding */
#dhru-services .rsf { padding:14px 18px 16px; }
#dhru-services .rs-foot { margin-top:14px; padding-top:14px; }

/* ---------------------------------------------- Admin -> Email (2026-10-06) -- */
.ml-warn { margin:0 var(--pad-card) 14px; padding:10px 14px; border-radius:10px; font-size:.85rem; line-height:1.6;
           background:color-mix(in srgb, var(--warn) 12%, transparent); border:1px solid color-mix(in srgb, var(--warn) 32%, transparent); color:var(--text); }
.ml-stats { display:flex; flex-wrap:wrap; gap:8px 22px; padding:0 var(--pad-card) 6px; font-size:.85rem; color:var(--text-dim); }
.ml-stats b { font-size:1.05rem; color:var(--text); margin-inline-end:3px; }
.ml-stats .bad b { color:var(--danger); }
.ml-empty { color:var(--text-mute); }
.ml-acc { margin:0 var(--pad-card) 14px; border:1px solid var(--border); border-radius:12px; background:var(--bg-panel); scroll-margin-top:14px; }
.ml-acc:target { border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.ml-acc.off { opacity:.72; }
.ml-head { display:flex; align-items:center; gap:14px; padding:14px 16px 10px; flex-wrap:wrap; }
.ml-n { flex:0 0 auto; width:28px; height:28px; border-radius:50%; display:grid; place-items:center; font-weight:800; font-size:.85rem;
        background:var(--accent-soft); color:var(--accent); }
.ml-who { flex:1 1 260px; min-width:0; }
.ml-name { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.ml-name b { font-size:.98rem; word-break:break-word; }
.ml-addr { margin-top:4px; font-size:.8rem; color:var(--text-mute); word-break:break-all; text-align:start; }
.ml-state { display:flex; flex-direction:column; align-items:flex-end; gap:4px; }
.ml-err { margin:0 16px 10px; padding:10px 12px; border-radius:10px; font-size:.84rem; line-height:1.6;
          background:color-mix(in srgb, var(--danger) 8%, transparent); border:1px solid color-mix(in srgb, var(--danger) 24%, transparent); }
.ml-raw { margin-top:4px; font-family:"LatinNum","Cascadia Mono",Consolas,ui-monospace,monospace; font-size:.76rem; color:var(--text-dim);
          word-break:break-word; text-align:start; unicode-bidi:plaintext; }
.ml-hint { margin-top:6px; color:var(--text); font-size:.82rem; line-height:1.65; }
.ml-okline { margin:-4px 16px 10px 58px; }
[dir="rtl"] .ml-okline { margin:-4px 58px 10px 16px; }
.ml-bar { display:flex; flex-wrap:wrap; align-items:center; gap:8px; padding:10px 16px 12px; border-top:1px solid var(--border); background:var(--bg-card); }
.ml-inl { display:inline-flex; align-items:center; gap:6px; margin:0; }
.ml-test { margin-inline-start:auto; }
.ml-test input { width:220px; max-width:52vw; padding:7px 10px; border:1px solid var(--border); border-radius:8px; background:var(--bg-panel);
                 color:var(--text); font:inherit; font-size:.82rem; }
.ml-test input:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.ml-edit { border-top:1px solid var(--border); }
.ml-edit > summary { cursor:pointer; padding:10px 16px; font-size:.85rem; font-weight:700; color:var(--accent); list-style:none; }
.ml-edit > summary::-webkit-details-marker { display:none; }
.ml-edit > summary::before { content:"▸"; display:inline-block; margin-inline-end:6px; transition:transform .15s; }
[dir="rtl"] .ml-edit > summary::before { content:"◂"; }
.ml-edit[open] > summary::before { transform:rotate(90deg); }
[dir="rtl"] .ml-edit[open] > summary::before { transform:rotate(-90deg); }
.ml-edit .form-grid { padding-top:6px; }
.ml-del { padding:0 16px 14px; }
.ml-guide-cell { grid-column:span 2; }
@media (max-width: 700px) { .ml-guide-cell { grid-column:auto; } }
.ml-guide { padding:12px 14px; border-radius:10px; font-size:.83rem; line-height:1.8; color:var(--text-dim);
            background:var(--accent-soft); border:1px solid color-mix(in srgb, var(--accent) 22%, transparent); }
.ml-guide b { color:var(--text); }
.ml-guide a { color:var(--accent); font-weight:700; }
.ml-guide[hidden] { display:none; }
.ml-form .form-actions .hint { font-size:.8rem; color:var(--text-mute); }
.ml-dom { margin:0 var(--pad-card) 14px; border:1px solid var(--border); border-radius:12px; overflow:hidden; }
.ml-dom-h { display:flex; align-items:center; gap:12px; flex-wrap:wrap; padding:12px 16px; background:var(--bg-card); }
.ml-dom-h > b { font-size:.95rem; }
.ml-dom-h form { margin-inline-start:auto; }
.ml-dns-t td { vertical-align:top; }
.ml-dns-s { width:1%; white-space:nowrap; }
.ml-dns-n { width:1%; white-space:nowrap; }
.ml-dns-d { font-size:.8rem; line-height:1.6; word-break:break-word; unicode-bidi:plaintext; text-align:start; }
.ml-logbar { display:flex; align-items:center; gap:8px; flex-wrap:wrap; padding:14px var(--pad-card); }
.ml-logbar .btn.on { background:var(--accent-soft); color:var(--accent); border-color:color-mix(in srgb, var(--accent) 30%, transparent); }
.ml-logbar form { margin-inline-start:auto; }
.ml-log td { vertical-align:top; }
.ml-log td .ml-raw { max-width:520px; }
#mail-top, #mail-accounts, #mail-add, #mail-dns, #mail-log { scroll-margin-top:14px; }
@media (max-width: 640px) {
    .ml-acc, .ml-dom { margin-inline:12px; }
    .ml-state { align-items:flex-start; flex-direction:row; flex-wrap:wrap; }
    .ml-okline, [dir="rtl"] .ml-okline { margin-inline:16px; }
    .ml-test { margin-inline-start:0; width:100%; }
    .ml-test input { flex:1 1 auto; width:auto; max-width:none; }
}
.ml-log td.ml-em { white-space:nowrap; text-align:start; }

/* ------------------------------ Telegram card on every user panel (2026-10-05) -- */
.tgu-card { --tg:#229ED9; scroll-margin-top:14px; }
.tgu-h { gap:10px; }
.tgu-title { display:inline-flex; align-items:center; gap:10px; }
.tgu-logo { width:30px; height:30px; flex:none; border-radius:50%; display:grid; place-items:center; color:#fff;
            background:linear-gradient(135deg,#37AEE2,#1E96C8); box-shadow:0 4px 12px rgba(34,158,217,.35); }
.tgu-bot { font-size:.78rem; font-weight:700; color:var(--tg); background:color-mix(in srgb, var(--tg) 10%, transparent);
           padding:4px 10px; border-radius:99px; }
.tgu-body { padding:16px 20px 18px; }
.tgu-lead { margin:0 0 12px; color:var(--text-dim); font-size:.9rem; line-height:1.7; }
.tgu-list { list-style:none; margin:0 0 16px; padding:0; display:grid; grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); gap:8px; }
.tgu-list li { display:flex; align-items:center; gap:10px; padding:10px 12px; border:1px solid var(--border); border-radius:10px;
               background:var(--bg-card); font-size:.86rem; line-height:1.5; }
.tgu-list li span { font-size:1.1rem; flex:none; }
.tgu-actions { display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin:0; }
.tgu-actions form { margin:0; }
.tgu-card .btn.tgu-go, .tgu-card .btn.primary.tgu-go { background:linear-gradient(135deg,#37AEE2,#1E96C8); border-color:transparent; color:#fff; text-decoration:none; }
.tgu-card .btn.tgu-go:hover { filter:brightness(1.07); }
.tgu-steps { margin:0 0 14px; padding-inline-start:20px; line-height:1.95; font-size:.9rem; color:var(--text); }
.tgu-hint { margin:12px 0 0; font-size:.8rem; color:var(--text-mute); line-height:1.75; }
.tgu-connect { margin:12px 0 0; display:flex; flex-direction:column; gap:8px; }
.tgu-hash-l { font-size:.82rem; font-weight:600; color:var(--text); }
.tgu-hash-in { width:100%; min-height:52px; resize:vertical; font-family:"LatinNum","Cascadia Mono",Consolas,ui-monospace,monospace;
               font-size:.76rem; line-height:1.5; word-break:break-all; padding:8px 10px; border:1px solid var(--border);
               border-radius:8px; background:var(--bg-inset); color:var(--text); }
.tgu-hash-in:focus { outline:none; border-color:var(--tg); }
.tgu-connect .btn { align-self:flex-start; }
.tgu-code { font-family:"LatinNum","Cascadia Mono",Consolas,ui-monospace,monospace; font-size:.78rem; background:var(--bg-inset);
            border:1px solid var(--border); padding:2px 8px; border-radius:6px; user-select:all; word-break:break-all; }
.tgu-spin { display:inline-block; width:10px; height:10px; border-radius:50%; border:2px solid var(--tg); border-right-color:transparent;
            animation:tgu-spin .8s linear infinite; vertical-align:-1px; }
@keyframes tgu-spin { to { transform:rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .tgu-spin { animation:none; } }
.tgu-who { display:flex; align-items:center; gap:12px; padding:12px 14px; border-radius:12px; margin-bottom:14px;
           background:color-mix(in srgb, var(--tg) 7%, transparent); border:1px solid color-mix(in srgb, var(--tg) 22%, transparent); }
.tgu-dot { width:10px; height:10px; border-radius:50%; background:var(--text-mute); flex:none; }
.tgu-dot.on { background:var(--success); box-shadow:0 0 8px var(--success); }
.tgu-dot.bad { background:var(--danger); }
.tgu-at { margin-inline-start:8px; color:var(--tg); font-weight:700; font-size:.85rem; }
.tgu-since { font-size:.78rem; color:var(--text-mute); margin-top:2px; }
.tgu-prefs { display:grid; gap:10px; margin:0; }
.tgu-opt { display:flex; align-items:flex-start; gap:10px; padding:10px 12px; border:1px solid var(--border); border-radius:10px; cursor:pointer; }
.tgu-opt:hover { border-color:color-mix(in srgb, var(--tg) 40%, var(--border)); }
.tgu-opt input { margin-top:3px; width:17px; height:17px; accent-color:var(--tg); flex:none; }
.tgu-opt b { display:block; font-size:.9rem; }
.tgu-opt small { color:var(--text-mute); font-size:.78rem; line-height:1.6; }
.tgu-unlink { margin:14px 0 0; }
.tgu-flash { margin:12px 20px 0; }
@media (max-width: 560px) { .tgu-body { padding:14px 14px 16px; } .tgu-flash { margin-inline:14px; } .tgu-actions .btn { flex:1 1 auto; } }
.tgu-wh { padding:10px 14px; border:1px dashed var(--border-2, var(--border)); border-radius:8px; font-size:.85rem; color:var(--text-dim); word-break:break-all; text-align:start; }

/* ----------------------------------------- Pricing as a percentage (2026-10-06) -- */
.pr-tbl td { vertical-align:top; }
.pr-tbl tr.pr-none td { background:color-mix(in srgb, var(--danger) 5%, transparent); }
.pr-tbl .pr-pct { width:96px; padding:7px 10px; border:1px solid var(--border); border-radius:8px; background:var(--bg-panel); color:var(--text); font:inherit; font-size:.85rem; }
.pr-tbl .pr-pct:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.tp-price-h { padding-top:10px; margin-top:4px; border-top:1px dashed var(--border); }
.tp-price-h label { font-size:.95rem; }
#price-kinds, #price-shop, #price-table, #cd-price { scroll-margin-top:14px; }

/* ------------------------------- Token servers from a JSON file (2026-10-06) -- */
#tp-import, #price-shop-list { scroll-margin-top:14px; }
.tpi-text { width:100%; min-height:110px; padding:10px 12px; border:1px solid var(--border); border-radius:8px; background:var(--bg-panel); color:var(--text); font:12.5px/1.5 ui-monospace, SFMono-Regular, Consolas, monospace; resize:vertical; }
.tpi-tbl td { vertical-align:top; }
.tpi-tbl tr.tpi-removed td { background:color-mix(in srgb, var(--danger) 6%, transparent); }
.tpi-tbl tr.tpi-new td { background:color-mix(in srgb, var(--success) 6%, transparent); }
.tpi-ch { max-width:340px; font-size:.82rem; color:var(--text-dim); word-break:break-word; }
.pill2.tpi-st-on  { background:rgba(16, 185, 129, .16); }
.pill2.tpi-st-warn { background:rgba(245, 158, 11, .18); }
.pill2.tpi-st-err { background:rgba(239, 68, 68, .16); }
.tpi-err ul, .tpi-notes { margin:6px 0 0; padding-inline-start:20px; line-height:1.7; font-size:.86rem; }
.pr-find { width:100%; max-width:360px; margin:10px 0; padding:8px 11px; border:1px solid var(--border); border-radius:8px; background:var(--bg-panel); color:var(--text); font:inherit; }
