/* BlankTrail Proxy Cabinet — deep-black neon theme, blue→cyan→violet accent. */
/* Webfonts (Sora display + Manrope body) are loaded via <link> in each page head.
   They were previously avoided because the external Google Fonts request tripped an
   RKN throttle stall on the Cloudflare path; that constraint was removed by a hosting
   change (2026-07). Manrope carries Cyrillic; Sora (Latin-only) falls back to it. */

/* ─── Reset & base ─────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  /* Deep-black neon palette */
  --bg:        #04050a;
  --bg-2:      #06070e;
  --bg-card:   #0b0f1c;
  --bg-input:  #0c101d;
  --border:    #1b2237;
  --border-soft: #161c2e;
  --border-bright: rgba(110,130,255,0.35);
  --border-focus: #5b8bff;

  /* Text */
  --text:      #e7ebf5;
  --text-muted:#8b96b5;
  --text-faint:#5f6a88;
  --text-link: #7fa5ff;

  /* Accent: blue → cyan → violet (name --accent-teal kept so existing rules recolor) */
  --accent-teal:   #5b8bff;
  --accent-indigo: #8b5cf6;
  --accent-cyan:   #22d3ee;
  --accent-grad:   linear-gradient(100deg, #5b8bff 8%, #22d3ee 55%, #8b5cf6 100%);
  --accent-grad-btn: linear-gradient(180deg, #4f8bff, #2f5cf0);

  /* Status colours */
  --success: #4ade80;
  --error:   #ef4444;
  --info:    #38bdf8;
  --warning: #fbbf24;

  /* Spacing scale */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;

  --radius: 0.75rem;
  --radius-sm: 0.5rem;
  --radius-lg: 1rem;
  /* Manrope = body/UI (has Cyrillic). Sora = Latin display; falls back to Manrope
     for Cyrillic (Sora ships no Cyrillic subset), so RU headings stay consistent. */
  --font: 'Manrope', 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-display: 'Sora', 'Manrope', 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, sans-serif;
}

html { scroll-behavior: smooth; }
/* Прыжок по якорю — штатное действие с тех пор, как у статьи появилось
   оглавление. Тому, кто просил меньше движения, он достаётся мгновенным. */
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  font-family: var(--font);
  font-size: 0.9375rem;
  line-height: 1.6;
  letter-spacing: -0.006em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  color: var(--text);
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

a { color: var(--text-link); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Display font (Sora → Manrope) on headings + numeric emphasis. */
h1, h2, h3, h4 { font-family: var(--font-display); letter-spacing: -0.01em; }

/* Atmosphere: subtle neon auroras behind every page (fixed, non-interactive). */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(760px 500px at 12% -12%, rgba(59,91,255,0.11), transparent 70%),
    radial-gradient(680px 460px at 100% -6%, rgba(139,92,246,0.09), transparent 70%);
}

/* ─── Layout containers ────────────────────────────────────────────────────── */
.container {
  width: 100%;
  max-width: 1100px;
  margin-left: auto; margin-right: auto;
  padding-left: var(--space-6); padding-right: var(--space-6);
}

/* ─── Header ───────────────────────────────────────────────────────────────── */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: linear-gradient(180deg, rgba(5,6,12,0.9), rgba(5,6,12,0.62));
  backdrop-filter: blur(14px);
  border-bottom: 1px solid rgba(27,34,55,0.7);
}

/* The header bar is wider than the content container so the full nav fits;
   it wraps onto a second row instead of showing a horizontal scrollbar. */
.site-header .container { max-width: 1320px; }
.header-inner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 3.5rem;
  flex-wrap: wrap;
  padding-top: var(--space-2); padding-bottom: var(--space-2);
}

.brand {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.3px;
  background: var(--accent-grad);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  flex-shrink: 0;
}
.brand:hover { text-decoration: none; }

/* ─── Navigation ───────────────────────────────────────────────────────────── */
.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex: 1;
  flex-wrap: wrap;
}

.nav-link {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-size: 0.875rem;
  font-weight: 500;
  white-space: nowrap;
  transition: color 0.15s, background 0.15s;
}
.nav-link:hover { color: var(--text); background: rgba(90,110,220,0.10); text-decoration: none; }
.nav-link.active { color: #fff; background: linear-gradient(90deg, rgba(74,100,255,0.22), rgba(74,100,255,0.04)); box-shadow: inset 0 0 0 1px rgba(91,120,255,0.28); }

/* Logout sits at the right end */
.nav-end { margin-left: auto; }

/* Unread-count pill on the Support nav link (JS-driven; hidden when 0) */
.nav-badge {
  display: inline-block;
  min-width: 1.1em;
  margin-left: var(--space-1);
  padding: 0 0.35em;
  border-radius: 999px;
  background: linear-gradient(180deg,#ff6b81,#e0344f);
  color: #fff;
  box-shadow: 0 0 10px rgba(224,52,79,0.5);
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.4;
  text-align: center;
  vertical-align: text-top;
}
.nav-badge[hidden] { display: none; }

/* ─── Main ─────────────────────────────────────────────────────────────────── */
.site-main {
  flex: 1;
  padding-top: var(--space-8); padding-bottom: var(--space-8);
}

/* ─── Flash messages ───────────────────────────────────────────────────────── */
.flash-region {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-6);
}

.flash {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius);
  border-left: 4px solid;
  font-size: 0.9rem;
  /* 🔴 Отступа снизу не было вовсе, и flash приклеивался к тому, что идёт
   * следом: на страницах кабинета баннер 2FA сливался с таблицей заказов, с
   * таблицей лицензий и с карточкой заявки — выглядело как один сломанный
   * блок. Ничего при этом не «падало»: flash отрисовывался верно, просто
   * вплотную. */
  margin-bottom: var(--space-4);
}

.flash-success {
  border-color: var(--success);
  background: rgba(34, 197, 94, 0.1);
  color: #86efac;
}

.flash-error {
  border-color: var(--error);
  background: rgba(239, 68, 68, 0.1);
  color: #fca5a5;
}

.flash-info {
  border-color: var(--info);
  background: rgba(56, 189, 248, 0.1);
  color: #7dd3fc;
}

/* ─── Card ─────────────────────────────────────────────────────────────────── */
.card {
  position: relative;
  background: linear-gradient(180deg, #0b0f1c, #090c16);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-6);
  box-shadow: 0 12px 40px rgba(0,0,0,0.35);
}

.card + .card { margin-top: var(--space-4); }

.card-title {
  font-size: 1.1rem;
  font-weight: 600;
  margin-bottom: var(--space-4);
  color: var(--text);
}

.card-subtitle {
  font-size: 0.875rem;
  color: var(--text-muted);
  margin-bottom: var(--space-6);
}

/* ─── Table ─────────────────────────────────────────────────────────────────── */
.table-wrap { overflow-x: auto; }

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.table th,
.table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  border-bottom: 1px solid var(--border);
}

.table th {
  color: #7c88ad;
  font-weight: 800;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: #0c1120;
  border-bottom: 1px solid var(--border);
}

.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr { transition: background 0.15s; }
.table tbody tr:hover td { background: rgba(84,104,255,0.06); }

/* ─── Forms ─────────────────────────────────────────────────────────────────── */
.form-group { margin-bottom: var(--space-4); }

.form-label {
  display: block;
  margin-bottom: var(--space-1);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-muted);
}

/* Base element rules — unclassed native controls inherit the dark theme.
   Classed variants (.form-input etc.) stay authoritative (defined below). */
input[type=text],
input[type=email],
input[type=password],
input[type=number],
input[type=search],
input[type=url],
input[type=date],
input[type=tel],
select,
textarea {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 0.9375rem;
  font-family: inherit;
  transition: border-color 0.15s, box-shadow 0.15s;
  appearance: none;
  -webkit-appearance: none;
}

input[type=text]:focus,
input[type=email]:focus,
input[type=password]:focus,
input[type=number]:focus,
input[type=search]:focus,
input[type=url]:focus,
input[type=date]:focus,
input[type=tel]:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--border-focus);
  box-shadow: 0 0 0 3px rgba(45,212,191,0.15);
}

/* Restore select caret after appearance reset */
select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%2394a3b8' d='M0 0l6 8 6-8z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  padding-right: 2rem;
}

/* Multi-select: no caret needed, allow height */
select[multiple] {
  background-image: none;
  padding-right: var(--space-3);
}

/* Fieldset: remove browser default box; use subtle card-like look */
fieldset {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  background: transparent;
}

legend {
  padding-left: var(--space-2); padding-right: var(--space-2);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Checkboxes and radios: teal accent, aligned */
input[type=radio],
input[type=checkbox] {
  width: auto;
  display: inline;
  accent-color: var(--accent-teal);
  cursor: pointer;
  vertical-align: middle;
  margin-right: var(--space-1);
}

/* Hint / helper text under form fields */
.form-hint {
  margin-top: var(--space-1);
  font-size: 0.8rem;
  color: var(--text-muted);
}

.form-input,
.form-select,
.form-textarea,
.form-control {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 0.9375rem;
  font-family: inherit;
  transition: border-color 0.15s, box-shadow 0.15s;
}

.form-input:focus,
.form-select:focus,
.form-textarea:focus,
.form-control:focus {
  outline: none;
  border-color: var(--border-focus);
  box-shadow: 0 0 0 3px rgba(45,212,191,0.15);
}

.form-error {
  margin-top: var(--space-1);
  font-size: 0.8rem;
  color: #fca5a5;
}

/* ─── Admin form layout ──────────────────────────────────────────────────────── */
.admin-form {
  max-width: 560px;
}

.admin-form .form-group label {
  display: block;
  margin-bottom: var(--space-1);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-muted);
}

.admin-form .btn + .btn {
  margin-left: var(--space-3);
}

/* The two CONTENT editors are the exception to the 560px above. That cap is
   right for the rest of the panel, where a form is a short column of unrelated
   settings and a wide one only makes the eye travel. These two are not that:
   every field is repeated once per locale, so the width buys a side-by-side
   view of the same value in three languages. Named explicitly — one line per
   page — rather than loosened globally, because the cap is load-bearing
   everywhere else it applies. */
.admin-blog-edit .admin-form,
.landing-blockform .admin-form {
  max-width: none;
}

/* ─── Changelog admin: dynamic item rows ─────────────────────────────────────── */
.changelog-locale-group,
.changelog-items-group { margin-bottom: var(--space-4); }

.changelog-items-rows { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-2); }

.changelog-item-row { display: flex; align-items: center; gap: var(--space-2); }
.changelog-item-row .form-input { flex: 1 1 auto; width: auto; }
.changelog-item-row .changelog-item-remove { flex: 0 0 auto; }

/* ─── Table alias — .data-table maps to .table styles ───────────────────────── */
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.data-table th,
.data-table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  border-bottom: 1px solid var(--border);
}

.data-table th {
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover td { background: rgba(255,255,255,0.02); }

/* ─── Buttons ────────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border: none;
  border-radius: var(--radius-sm);
  font-size: 0.9375rem;
  font-family: inherit;
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
  transition: opacity 0.15s, filter 0.15s;
  white-space: nowrap;
}

.btn:hover { filter: brightness(1.1); text-decoration: none; }
.btn:active { filter: brightness(0.95); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Keyboard focus ring for interactive landing elements (a11y). Shown only on
   keyboard navigation (:focus-visible), on-brand accent; existing focus styling
   elsewhere is left intact. */
.btn:focus-visible,
.nav-link:focus-visible,
.landing-nav-link:focus-visible,
.locale-link:focus-visible {
  outline: 2px solid var(--accent-teal);
  outline-offset: 2px;
}

/* Primary: neon blue gradient with glow */
.btn-primary {
  background: var(--accent-grad-btn);
  color: #fff;
  box-shadow: 0 4px 16px rgba(38,66,180,0.42), inset 0 1px 0 rgba(255,255,255,0.18);
}
.btn-primary:hover { filter: brightness(1.1); box-shadow: 0 6px 22px rgba(48,86,230,0.5), inset 0 1px 0 rgba(255,255,255,0.2); }

.btn-secondary {
  background: var(--bg-input);
  border: 1px solid var(--border);
  color: var(--text);
}

.btn-danger {
  background: var(--error);
  color: #fff;
}

.btn-warning {
  background: var(--warning);
  color: #0f1117;
}

.btn-success {
  background: var(--success);
  color: #0f1117;
}

.btn-sm {
  padding: var(--space-1) var(--space-3);
  font-size: 0.8rem;
}

/* ─── Badge / status pill ────────────────────────────────────────────────────── */
.badge {
  display: inline-block;
  padding: 0.125rem 0.5rem;
  border-radius: 9999px;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.03em;
}

.badge-active    { background: rgba(34,197,94,0.15);  color: #86efac; }
.badge-inactive  { background: rgba(148,163,184,0.15); color: #94a3b8; }
.badge-pending   { background: rgba(245,158,11,0.15); color: #fcd34d; }
.badge-paid      { background: rgba(34,197,94,0.15);  color: #86efac; }
.badge-expired   { background: rgba(239,68,68,0.15);  color: #fca5a5; }
.badge-suspended { background: rgba(239,68,68,0.15);  color: #fca5a5; }
.badge-trial     { background: rgba(99,102,241,0.15); color: #a5b4fc; }
/* Вид интеграционного ключа: партнёрский платит владельцу, внутренний —
 * только считает. Разные цвета, потому что путать их дорого. */
.badge-developer { background: rgba(99,102,241,0.15); color: #a5b4fc; }
.badge-internal  { background: rgba(148,163,184,0.15); color: #cbd5e1; }

/* Landing revision statuses (/admin/landing). Published borrows the green of
   .badge-active because it means the same thing — this is the row the public
   is looking at. Draft borrows the grey of .badge-inactive: nothing depends
   on it yet. Archived is amber rather than a second grey, because draft and
   archived are precisely the two rows whose buttons differ (Edit/Delete vs
   Rollback) and they must not read alike at a glance: one was never live,
   the other is what a rollback goes back to. */
.badge-draft     { background: rgba(148,163,184,0.15); color: #94a3b8; }
.badge-published { background: rgba(34,197,94,0.15);  color: #86efac; }
.badge-archived  { background: rgba(245,158,11,0.15); color: #fcd34d; }

/* Ledger + webhook-log badges. These carry the provider's own status names
   verbatim (Paid, Partial, Declined...) so a row reads the same here as in the
   provider dashboard — hence the capitalised class names. badge-warn is ours:
   it marks a row that needs a human, without claiming the payment failed. */
.badge-Paid      { background: rgba(34,197,94,0.15);  color: #86efac; }
.badge-Partial   { background: rgba(245,158,11,0.15); color: #fcd34d; }
.badge-Pending   { background: rgba(245,158,11,0.15); color: #fcd34d; }
.badge-Created   { background: rgba(148,163,184,0.15); color: #94a3b8; }
.badge-Declined  { background: rgba(239,68,68,0.15);  color: #fca5a5; }
.badge-warn      { background: rgba(245,158,11,0.15); color: #fcd34d; }

/* Webhook delivery log cells. One line per cell is the registry-wide default
   (see "Admin registry tables"); these only set the typography that tells a
   timestamp from a provider UUID at a glance. */
.webhook-time { font-variant-numeric: tabular-nums; }
.webhook-external {
  font-family: 'Fira Mono', 'Consolas', monospace;
  font-size: 0.78rem;
}

/* Raw webhook payload, shown collapsed under its delivery row. */
.payload {
  font-family: 'Fira Mono', 'Consolas', monospace;
  font-size: 0.75rem;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 18rem;
  overflow: auto;
  padding: var(--space-3);
  border-radius: 0.375rem;
  background: rgba(148,163,184,0.08);
}

/* Blockchain transaction hash: long, opaque, and worth copying whole. */
.tx-hash {
  font-family: 'Fira Mono', 'Consolas', monospace;
  font-size: 0.7rem;
  word-break: break-all;
}

/* ─── Page section headings ─────────────────────────────────────────────────── */
.page-title {
  font-size: 1.5rem;
  font-weight: 700;
  margin-bottom: var(--space-6);
  letter-spacing: -0.02em;
}

.section-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: var(--space-4);
}

/* ─── Key/code display ───────────────────────────────────────────────────────── */
.key-display {
  font-family: 'Fira Mono', 'Consolas', monospace;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  font-size: 0.85rem;
  color: var(--accent-teal);
  word-break: break-all;
}

/* ─── Grid helpers ────────────────────────────────────────────────────────────── */
.grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-4);
}

.grid-3 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-4);
}

/* ─── Stat block (home page) ──────────────────────────────────────────────── */
.stat-block {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.stat-value {
  font-family: var(--font-display);
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--text);
}

.stat-label {
  font-size: 0.8rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* ─── Footer ──────────────────────────────────────────────────────────────── */
.site-footer {
  border-top: 1px solid var(--border);
  padding-top: var(--space-6); padding-bottom: var(--space-6);
  margin-top: auto;
}

.footer-inner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.footer-brand {
  font-weight: 700;
  font-size: 0.9rem;
  background: var(--accent-grad);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.footer-tagline {
  font-size: 0.8rem;
  color: var(--text-muted);
  flex: 1;
}

/* Footer bottom bar: copyright + security badge + social (matches the mockup). */
.footer-bottom {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-6);
  padding-top: var(--space-6);
  border-top: 1px solid var(--border-soft);
  font-size: 0.8rem;
  color: var(--text-faint);
}
/* The copyright line. It had no rule of its own until now and took
   .footer-bottom's --text-faint, which is #5f6a88 — around 3:1 against the
   footer ground, under the 4.5:1 that body text needs. --text-muted is the
   same treatment .footer-tagline gets one row above, and reads at about 6:1.
   Its neighbours .footer-secure and .footer-social both set their own colour;
   this was the only child of the bottom bar that did not. */
.footer-copy {
  font-size: 0.8rem;
  color: var(--text-muted);
}
.footer-secure {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 9px 14px; border-radius: 11px;
  background: #0a0e1a; border: 1px solid var(--border);
  color: #aeb8d6; font-size: 0.78rem; font-weight: 600;
}
.footer-secure svg { color: var(--success); flex: none; }
.footer-social { margin-left: auto; display: flex; gap: 10px; }
.footer-social a {
  width: 36px; height: 36px; border-radius: 10px;
  display: grid; place-items: center;
  background: #0c101d; border: 1px solid var(--border); color: #8f9abb;
  transition: color 0.16s, border-color 0.16s;
}
.footer-social a:hover { color: #fff; border-color: var(--border-bright); text-decoration: none; }

/* ─── Locale switcher ────────────────────────────────────────────────────────── */
.locale-switcher {
  display: flex;
  gap: var(--space-1);
}

.locale-link {
  font-size: 0.75rem;
  font-weight: 600;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  transition: color 0.15s;
}

.locale-link:hover { color: var(--text); text-decoration: none; }
.locale-link.locale-active { color: var(--accent-teal); }

/* ─── Responsive ──────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  .container { padding-left: var(--space-4); padding-right: var(--space-4); }
  .site-nav { gap: 0; }
  .nav-link { padding: var(--space-2); font-size: 0.8rem; }
  .card { padding: var(--space-4); }
  .page-title { font-size: 1.25rem; }
  .footer-inner { flex-direction: column; align-items: flex-start; gap: var(--space-2); }
}

/* ─── Auth pages ─────────────────────────────────────────────────────────── */
/* Narrow centered card used by login / register / verify */
.auth-page {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: var(--space-12); padding-bottom: var(--space-12);
}

.auth-card {
  width: 100%;
  max-width: 420px;
}

.auth-card .card-title {
  font-size: 1.35rem;
  margin-bottom: var(--space-6);
}

.auth-alt-link {
  margin-top: var(--space-4);
  font-size: 0.875rem;
  color: var(--text-muted);
  text-align: center;
}

.auth-hint {
  font-weight: 400;
  color: var(--text-muted);
  font-size: 0.8rem;
}

@media (max-width: 640px) {
  .auth-page { padding-top: var(--space-8); padding-bottom: var(--space-8); }
}

/* ─── Plans page (cabinet) ───────────────────────────────────────────────────── */
.plans-page .page-subtitle {
  color: var(--text-muted);
  margin-bottom: var(--space-8);
}

.plan-cards {
  display: grid;
  /* 🔴 Верхняя граница дорожки, а не 1fr. С 1fr единственный тариф — а это
   * обычное состояние аккаунта с одной подпиской — растягивался в полосу и
   * оставлял рядом пустое поле в две трети экрана. Потолок в 340px делает
   * карточку карточкой при любом их числе. */
  grid-template-columns: repeat(auto-fit, minmax(260px, 340px));
  justify-content: start;
  gap: var(--space-6);
  align-items: start;
}

/* Each plan card inherits the landing pricing-card look */
.plan-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  position: relative;
  transition: border-color 0.2s;
}

.plan-card:hover {
  border-color: rgba(45,212,191,0.35);
}

/* Recommended tier highlight (matches landing pricing-card-recommended) */
.plan-card-recommended {
  border-color: var(--accent-teal);
  background: linear-gradient(160deg, rgba(45,212,191,0.06) 0%, rgba(99,102,241,0.06) 100%);
  box-shadow: 0 0 0 1px var(--accent-teal), 0 8px 32px rgba(45,212,191,0.12);
}

.plan-recommended-badge {
  position: absolute;
  top: -1px;
  right: var(--space-4);
  transform: translateY(-50%);
  background: var(--accent-grad);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.2rem 0.6rem;
  border-radius: 9999px;
}

.plan-card-header { display: flex; flex-direction: column; gap: var(--space-2); }

.plan-name {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--text);
}

.plan-price {
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: -0.04em;
  color: var(--text);
}

.plan-period {
  font-size: 0.875rem;
  font-weight: 400;
  color: var(--text-muted);
}

.plan-meta { font-size: 0.85rem; color: var(--text-muted); }

.plan-features {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 1;
  padding: 0;
}

.plan-features li {
  font-size: 0.875rem;
  color: var(--text-muted);
  padding-left: 1.25rem;
  position: relative;
}

.plan-features li::before {
  content: '✓';
  position: absolute;
  left: 0;
  color: var(--accent-teal);
  font-weight: 700;
}

/* Promo code row inside a plan card */
.promo-row {
  margin-bottom: var(--space-3);
}

.promo-row input {
  font-size: 0.875rem;
  padding: var(--space-2) var(--space-3);
}

/* ─── Promo-code page (/app/promo) ──────────────────────────────────────────── */
.promo-page { max-width: 560px; }
.promo-intro { color: var(--text-muted); margin-bottom: var(--space-6); }
.promo-form { display: flex; gap: var(--space-3); flex-wrap: wrap; align-items: center; }
.promo-form input[type=text] { flex: 1; min-width: 220px; }
.promo-hint { color: var(--text-muted); font-size: 0.85rem; margin-top: var(--space-3); }

/* ─── Баннер «включите 2FA» (layout.tmpl) ───────────────────────────────────
 *
 * 🔴 Ни один из классов totp-nudge* не был описан. Баннер жил на одном .flash,
 * а .flash — не флекс-контейнер, поэтому <form> с кнопкой «Позже» как блочный
 * элемент уезжал на ВТОРУЮ строку под текст, и баннер вырастал вдвое. Плюс
 * отсутствие отступа снизу (см. .flash выше) склеивало его со следующим
 * блоком. Обе беды видны на всех страницах кабинета сразу.
 */
.totp-nudge {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}
/* Форма — такой же элемент строки, как ссылка рядом: без этого она снова
 * станет блочной и утащит кнопку на новую строку. */
.totp-nudge-later { display: inline-flex; margin: 0; }
/* Текст забирает свободное место, кнопки прижимаются вправо — так баннер
 * читается одинаково и на 900px, и на 1320px. */
.totp-nudge > span { flex: 1 1 22ch; }

/* ─── Security page (/app/security) ─────────────────────────────────────────
 *
 * Страница ехала БЕЗ единого собственного правила: классы из шаблона
 * (.security-page, .security-qr, .security-secret …) не были описаны нигде, и
 * браузер раскладывал голые .card во всю ширину колонки. На широком мониторе
 * это выглядело как пустое поле в тысячу пикселей рядом с QR-кодом в 256, а
 * поле на ШЕСТЬ цифр растягивалось на всю карточку.
 *
 * Ширина взята той же, что у соседней страницы-настроек (.promo-page, 560px),
 * но чуть шире: здесь в одну строку должны уместиться QR и инструкция к нему.
 */
.security-page { max-width: 760px; }
.security-page > .card + .card { margin-top: var(--space-6); }

/* Включение: QR слева, всё остальное справа. Колонка QR фиксированной ширины —
 * его нельзя ни сжать, ни растянуть без вреда для считывания, поэтому гибкая
 * здесь ровно одна колонка, текстовая. */
.security-enroll-grid {
  display: grid;
  grid-template-columns: 256px minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}
.security-qr {
  background: #fff;               /* тихая зона нужна светлая — иначе не читается */
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  line-height: 0;                 /* убирает щель под inline-картинкой */
}
.security-qr img { display: block; width: 100%; height: auto; }
/* Ячейка грида по умолчанию не уже своего содержимого: без min-width:0 длинный
 * неразрывный ключ распёр бы колонку и утащил QR за край карточки. */
.security-enroll-side { min-width: 0; }

.security-intro { color: var(--text-muted); margin-bottom: var(--space-4); }
.security-manual-label { color: var(--text-muted); font-size: 0.85rem; margin-bottom: var(--space-1); }

/* Ключ для ручного ввода: моноширинный и переносимый. Он длинный и без
 * пробелов, так что без break-all распирал бы колонку. */
.security-secret {
  margin-bottom: var(--space-4);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.security-secret code {
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  word-break: break-all;
  user-select: all;               /* один клик выделяет весь ключ целиком */
}

/* Поле кода — по размеру содержимого, а не по ширине карточки. Шесть цифр в
 * поле на 700px читаются как «сюда, наверное, надо что-то длинное». */
.security-enable-form input[type="text"],
.security-disable-form input[type="text"] {
  width: 9.5em;
  font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
  font-size: 1.05rem;
  letter-spacing: 0.28em;
  text-align: center;
}
.security-enable-form,
.security-disable-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3); }
.security-enable-form .form-label,
.security-disable-form .form-label { flex-basis: 100%; margin-bottom: 0; }

.security-hint { color: var(--text-muted); font-size: 0.85rem; margin-top: var(--space-4); }

/* Включённое состояние: две короткие строки статуса, потом форма отключения. */
.security-confirmed,
.security-codes-left { color: var(--text-muted); font-size: 0.9rem; margin-bottom: var(--space-2); }

/* Пароль: текст ограничен по длине строки — во всю ширину карточки читать
 * неудобно, а карточка широка ради QR в соседней секции. */
.security-password .security-hint { max-width: 62ch; margin-top: 0; margin-bottom: var(--space-4); }

@media (max-width: 640px) {
  .security-enroll-grid { grid-template-columns: minmax(0, 1fr); }
  .security-qr { max-width: 256px; }
}

.promo-saved {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-card); padding: var(--space-6);
}
.promo-saved p { margin: 0 0 var(--space-4); }
.promo-saved-actions { display: flex; gap: var(--space-3); flex-wrap: wrap; }

/* Payment method radio group */
.pay-options {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  background: transparent;
  margin-bottom: var(--space-3);
}

.pay-options legend {
  padding-left: var(--space-2); padding-right: var(--space-2);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.pay-option {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-top: var(--space-1); padding-bottom: var(--space-1);
  font-size: 0.9rem;
  color: var(--text);
  cursor: pointer;
}

.pay-option input[type=radio] {
  width: auto;
  display: inline;
  flex-shrink: 0;
}

.btn-block { width: 100%; }

/* Responsive: stack on narrow screens */
@media (max-width: 768px) {
  .plan-cards {
    grid-template-columns: 1fr;
    max-width: 380px;
    margin-left: auto; margin-right: auto;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LANDING PAGE — public marketing sections
   Mobile-first; no authed cabinet chrome.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Extra spacing scale used by landing only */
/* ═══════════════════════════════════════════════════════════════════════
   СВЕТЛАЯ ТЕМА — по умолчанию с этого релиза.

   Класс `light` вешается на <html> ДО отрисовки (скрипт в layout), иначе первый
   кадр успевает мигнуть тёмным. Тёмная палитра выше никуда не делась: она
   остаётся тем, что включает тумблер, и тем, что видит человек, однажды
   выбравший её.

   Здесь переопределяются ТОЛЬКО переменные. Цвета, записанные в правилах
   напрямую, чинятся точечно ниже — их в этом файле несколько сотен, и разом
   они не переводятся.
   ═══════════════════════════════════════════════════════════════════════ */
html.light {
  --bg:          #f4f7fb;
  --bg-2:        #eef2f9;
  --bg-card:     #ffffff;
  --bg-input:    #ffffff;
  --border:      #e3e9f4;
  --border-soft: #eef2f9;
  --border-bright: rgba(59,91,255,0.35);
  --border-focus:  #3b5bff;

  --text:       #0f172a;
  --text-muted: #64748b;
  --text-faint: #94a3b8;
  --text-link:  #2942d8;

  /* Имя --accent-teal сохранено, как и в тёмной: по нему перекрашены все правила. */
  --accent-teal:   #3b5bff;
  --accent-indigo: #7c5cf0;
  --accent-cyan:   #0891b2;
  --accent-grad:   linear-gradient(100deg, #3b5bff 8%, #0891b2 55%, #7c5cf0 100%);
  --accent-grad-btn: linear-gradient(180deg, #5b7cff, #3b5bff);

  /* Статусы затемнены: на белом неоновые оттенки не читаются. */
  --success: #16a34a;
  --error:   #dc2626;
  --info:    #0284c7;
  --warning: #b45309;
}

/* Фоновые «ауры» рассчитаны на чёрный холст — на белом они дают грязное пятно. */
html.light body::before,
html.light body::after { opacity: .35; }

:root {
  --space-16: 4rem;
  --space-20: 5rem;
  --space-24: 6rem;
}

/* ── Landing body resets ────────────────────────────────────────────────── */
.landing-body { display: block; } /* override cabinet flex-column */
/* Landing headings match the mockup EXACTLY: Sora for Latin, the browser default
   sans-serif for Cyrillic (Sora ships no Cyrillic subset) — NOT Manrope, which
   renders rounder/heavier. Scoped to the landing so the cabinet keeps Manrope. */
.landing-body h1, .landing-body h2, .landing-body h3 { font-family: 'Sora', sans-serif; }

/* ── Landing header ─────────────────────────────────────────────────────── */
.landing-header .header-inner {
  gap: var(--space-4);
}

/* Wraps rather than scrolls. `overflow-x: auto` here used to zero out the flex
   item's automatic minimum size, so the nav shrank below its own content and
   grew a horizontal scrollbar inside the header — which is exactly what the
   .site-header comment above says must not happen. Keeping overflow visible
   restores min-width:auto, and flex-wrap lets the links fall onto a second row
   instead of overflowing the page. Mirrors .site-nav. */
.landing-nav {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  row-gap: var(--space-1);
  flex: 1;
  flex-wrap: wrap;
}

.landing-nav-link {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-size: 0.875rem;
  font-weight: 500;
  white-space: nowrap;
  transition: color 0.15s, background 0.15s;
}
.landing-nav-link:hover {
  color: var(--text);
  background: rgba(255,255,255,0.06);
  text-decoration: none;
}

/* The page the visitor is already on. render.go's navActive marks it with
   .nav-link-active — a DIFFERENT class from the cabinet nav's .nav-link.active
   (line 155), which is why this went unstyled: the mark was computed on every
   public page and then rendered as nothing, so the nav never showed where you
   were. It settles at the hover state rather than borrowing the cabinet's blue
   gradient, which belongs to a different surface. */
.nav-link-active {
  color: var(--text);
  background: rgba(255,255,255,0.06);
}

.landing-header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}

/* ── Section layout helpers ─────────────────────────────────────────────── */
.landing-kicker {
  text-align: center;
  font-size: 12px; font-weight: 800; letter-spacing: 0.22em; text-transform: uppercase;
  color: #5b8bff; margin-bottom: 14px;
}
.landing-section-title {
  font-size: 2.25rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  text-align: center;
  margin-bottom: var(--space-3);
}

.landing-section-sub {
  font-size: 1rem;
  color: var(--text-muted);
  text-align: center;
  max-width: 680px;
  margin-left: auto; margin-right: auto;
  margin-bottom: var(--space-12);
}

/* ── Hero ───────────────────────────────────────────────────────────────── */
.landing-hero {
  padding-top: var(--space-20); padding-bottom: var(--space-16);
  text-align: center;
  position: relative;
  overflow: hidden;
}

/* Subtle radial glow behind the headline */
.landing-hero::before {
  content: '';
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  background: radial-gradient(ellipse 80% 60% at 50% 0%, rgba(45,212,191,0.08) 0%, transparent 70%);
  pointer-events: none;
}

.hero-inner {
  position: relative;
}

.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 8px 16px;
  background: linear-gradient(90deg, rgba(59,91,255,0.12), rgba(139,92,246,0.08));
  border: 1px solid rgba(91,120,255,0.28);
  border-radius: 100px;
  font-size: 0.78rem;
  font-weight: 700;
  color: #a9c2ff;
  letter-spacing: 0.02em;
  margin-bottom: var(--space-6);
}
.hero-pip { width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--accent-cyan); box-shadow: 0 0 10px var(--accent-cyan); }

/* ── JS Solver announcement band (full-width, below the hero) ───────────────
   Centered neon callout linking to #features. Graphic left, copy middle, CTA
   right; stacks + centers on narrow viewports. Deliberately a standalone
   section — it never sits inside the hero column, so it can't cramp the hero. */
.landing-solver-section { padding: var(--space-4) 0 var(--space-8); }

.solver-band {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(18px, 3vw, 40px);
  padding: clamp(20px, 3vw, 30px) clamp(22px, 3.5vw, 44px);
  border: 1.5px solid transparent;
  border-radius: 22px;
  background:
    linear-gradient(180deg, rgba(20,26,48,0.90), rgba(11,15,28,0.93)) padding-box,
    var(--accent-grad) border-box;
  box-shadow: 0 0 0 1px rgba(91,120,255,0.05), 0 26px 64px -30px rgba(70,110,255,0.6);
  text-decoration: none;
  overflow: hidden;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.solver-band::before {
  content: ''; position: absolute; left: 2%; top: 50%; transform: translateY(-50%);
  width: 340px; height: 340px; pointer-events: none; z-index: 0;
  background: radial-gradient(circle, rgba(45,140,255,0.18), transparent 62%);
}
.solver-band:hover {
  transform: translateY(-3px);
  box-shadow: 0 0 0 1px rgba(91,120,255,0.16), 0 32px 74px -26px rgba(70,110,255,0.8);
}
.solver-band:focus-visible { outline: 2px solid var(--accent-teal); outline-offset: 4px; }
.solver-band > * { position: relative; z-index: 1; }

.solver-band-art { flex: none; width: clamp(104px, 12vw, 150px); }
.solver-band-art img {
  display: block; width: 100%; height: auto;
  filter: drop-shadow(0 8px 26px rgba(40,140,255,0.42));
}

.solver-band-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.solver-band-badge {
  align-self: flex-start;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 4px 11px; border-radius: 100px;
  background: linear-gradient(90deg, rgba(34,211,238,0.16), rgba(139,92,246,0.12));
  border: 1px solid rgba(34,211,238,0.34);
  font-size: 0.7rem; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase;
  color: #bdeaff;
}
.solver-band-pip {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--accent-cyan); box-shadow: 0 0 8px var(--accent-cyan);
  animation: solverPipPulse 1.8s ease-in-out infinite;
}
@keyframes solverPipPulse {
  0%, 100% { opacity: 1;    box-shadow: 0 0 8px var(--accent-cyan); }
  50%      { opacity: 0.45; box-shadow: 0 0 16px var(--accent-cyan); }
}
.solver-band-title {
  font-size: clamp(1.15rem, 1.1vw + 0.85rem, 1.55rem);
  font-weight: 800; line-height: 1.22; color: #fff; letter-spacing: -0.01em;
}
.solver-band-desc {
  font-size: 0.94rem; line-height: 1.55; color: var(--text-muted); max-width: 780px;
}
.solver-band-note { font-size: 0.76rem; line-height: 1.5; color: var(--text-faint); }

.solver-band-cta {
  flex: none; align-self: center; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 22px; border-radius: 12px;
  border: 1px solid rgba(120,150,255,0.4);
  background: rgba(30,42,74,0.5);
  color: #d3e2ff; font-weight: 700; font-size: 0.95rem;
  transition: border-color 0.2s, background 0.2s;
}
.solver-band-cta svg { transition: transform 0.2s ease; }
.solver-band:hover .solver-band-cta { border-color: rgba(120,150,255,0.8); background: rgba(46,62,104,0.65); }
.solver-band:hover .solver-band-cta svg { transform: translateX(3px); }

@media (prefers-reduced-motion: reduce) {
  .solver-band-pip { animation: none; }
  .solver-band, .solver-band-cta svg { transition: none; }
}

@media (max-width: 820px) {
  .solver-band { flex-direction: column; text-align: center; gap: 18px; padding: 26px 22px; }
  .solver-band::before { display: none; }
  .solver-band-body { align-items: center; }
  .solver-band-badge { align-self: center; }
  .solver-band-cta { width: 100%; justify-content: center; }
}

/* White headline with a single gradient-accent phrase (matches the mockup). */
.hero-headline {
  font-size: clamp(2.1rem, 4vw + 1rem, 3.75rem);
  font-weight: 700;
  letter-spacing: -0.022em;
  line-height: 1.05;
  color: var(--text);
  max-width: 640px;
  margin-left: auto; margin-right: auto;
  margin-bottom: var(--space-6);
}
.hero-accent {
  background: linear-gradient(100deg, #5b8bff 10%, #22d3ee 55%, #8b5cf6);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.hero-subhead {
  font-size: clamp(1rem, 1vw + 0.65rem, 1.125rem);
  color: var(--text-muted);
  max-width: 590px;
  margin-left: auto; margin-right: auto;
  margin-bottom: var(--space-8);
  line-height: 1.62;
}

.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  justify-content: center;
  align-items: center;
}

.btn-lg {
  padding: 0.8rem 1.65rem;
  font-size: 1rem;
  border-radius: 13px;
}
/* Secondary hero button: dark "how it works" with a play chip (matches mockup). */
.btn-play {
  background: #0c101d;
  border: 1px solid var(--border);
  color: var(--text);
}
.btn-play:hover { border-color: var(--border-bright); color: #fff; filter: none; }
.btn-play-ic {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: rgba(91,139,255,0.16); border: 1px solid rgba(91,139,255,0.4); color: #bcd0ff;
}

/* Hero two-column: copy left, product screenshot right.
   The hero breaks out wider than the 1100px content container so the dashboard
   screenshot renders large + crisp (matches the approved mockup ~800px). */
.landing-body .container { max-width: 1300px; }
.landing-hero .hero-inner { max-width: 1440px; }
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0,0.8fr) minmax(0,1.2fr);
  gap: var(--space-12);
  align-items: center;
  text-align: left;
}
.hero-copy { min-width: 0; }
.hero-grid .hero-headline,
.hero-grid .hero-subhead { max-width: none; margin-left: 0; margin-right: 0; }
.hero-grid .hero-ctas,
.hero-grid .hero-tech { justify-content: flex-start; }
.hero-media { min-width: 0; position: relative; animation: heroFloat 7s ease-in-out infinite; }
@keyframes heroFloat { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-11px); } }
@media (prefers-reduced-motion: reduce) { .hero-media { animation: none; } }
.hero-media::before {
  content: ''; position: absolute; inset: -10% -6% -14% -6%; z-index: 0;
  background: radial-gradient(60% 55% at 60% 30%, rgba(70,100,255,0.20), transparent 70%);
  pointer-events: none;
}
.hero-dash {
  position: relative; z-index: 1;
  /* height:auto is REQUIRED — the <img> has width/height attributes, so without it
     the presentational height="784" hint sticks and width:100% squishes the image
     horizontally. */
  width: 100%; height: auto; display: block; border-radius: var(--radius-lg);
  border: 1px solid #1c2440;
  box-shadow: 0 40px 90px -30px rgba(0,0,0,0.7), 0 0 0 1px rgba(90,120,255,0.06);
}
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; text-align: center; }
  .hero-grid .hero-headline, .hero-grid .hero-subhead { margin-left: auto; margin-right: auto; }
  .hero-grid .hero-ctas, .hero-grid .hero-tech { justify-content: center; }
  .hero-media { max-width: 660px; margin: var(--space-8) auto 0; }
}

/* Cases carousel pulled below the hero into its own section */
.landing-cases-section { padding-top: var(--space-8); padding-bottom: var(--space-8); }
.landing-cases-section .hero-cases { margin-top: 0; }

/* Scale showcase band — image cards (chain / cache / detect) */
.landing-scale { padding-top: var(--space-16); padding-bottom: var(--space-16); }
.scale-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: var(--space-6); margin-top: var(--space-8); }
.scale-card { position: relative; border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden;
  background: #090c16; display: flex; flex-direction: column; transition: transform 0.2s, border-color 0.2s; }
.scale-card:hover { transform: translateY(-4px); border-color: rgba(91,120,255,0.32); }
.scale-art { position: relative; height: 176px; overflow: hidden; background: radial-gradient(120% 100% at 50% 0%, #0d1226, #090c16); }
.scale-art img { width: 100%; height: 100%; object-fit: cover; opacity: 0.95; }
.scale-art::after { content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(9,12,22,0.55) 0%, transparent 30%, transparent 55%, #090c16); }
.scale-body { padding: 0 var(--space-6) var(--space-6); margin-top: -14px; position: relative; }
.scale-body h3 { font-family: var(--font-display); font-size: 1.2rem; font-weight: 700; margin-bottom: var(--space-2); }
.scale-body p { color: var(--text-muted); font-size: 0.92rem; line-height: 1.6; }
@media (max-width: 860px) { .scale-grid { grid-template-columns: 1fr; } }

/* CTA globe illustration */
.cta-globe { position: absolute; left: -40px; top: 50%; transform: translateY(-50%);
  width: 360px; max-width: 46%; opacity: 0.5; pointer-events: none; z-index: 0;
  -webkit-mask-image: radial-gradient(closest-side, #000, transparent);
  mask-image: radial-gradient(closest-side, #000, transparent); }
.landing-cta-strip .cta-strip-inner { z-index: 1; }

/* ── Features grid ──────────────────────────────────────────────────────── */
.landing-features {
  padding-top: var(--space-16); padding-bottom: var(--space-16);
}

.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
  margin-top: 44px;
}

.feature-card {
  background: linear-gradient(180deg, #0b0f1c, #090c16);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 26px 24px 24px;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  transition: border-color 0.2s, transform 0.2s;
}

.feature-card:hover {
  border-color: rgba(91,120,255,0.3);
  transform: translateY(-4px);
}

/* Boxed icon (matches the mockup .fi), colour cycles per card. */
.feature-icon {
  width: 50px; height: 50px; border-radius: 13px; flex-shrink: 0;
  display: grid; place-items: center;
  background: rgba(120,130,255,0.05);
  border: 1.5px solid rgba(96,150,250,0.5);
  color: #7fb0ff;
  margin-bottom: var(--space-2);
  transition: box-shadow 0.2s;
}
.feature-card:nth-child(3n+2) .feature-icon { border-color: rgba(150,130,255,0.5); color: #b7a5ff; }
.feature-card:nth-child(3n) .feature-icon { border-color: rgba(74,222,128,0.55); color: #5ee79b; }
.feature-card:hover .feature-icon { box-shadow: 0 0 22px rgba(80,120,255,0.35), inset 0 0 14px rgba(80,120,255,0.12); }

/* Newest feature (JS Solver) — subtle neon accent so it stands out in the grid. */
.feature-card-solver {
  border-color: rgba(91,139,255,0.45);
  box-shadow: 0 0 0 1px rgba(91,139,255,0.10), 0 18px 44px -26px rgba(70,100,255,0.5);
}
.feature-card-solver .feature-icon {
  border-color: rgba(34,211,238,0.55);
  color: #7fe0ff;
  box-shadow: 0 0 18px rgba(34,211,238,0.22), inset 0 0 12px rgba(34,211,238,0.10);
}

.feature-title {
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--text);
  line-height: 1.3;
}

.feature-desc {
  font-size: 0.875rem;
  color: var(--text-muted);
  line-height: 1.6;
}

@media (max-width: 900px) { .feature-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 620px) { .feature-grid { grid-template-columns: 1fr; } }

/* ── Pricing ────────────────────────────────────────────────────────────── */
.landing-pricing {
  padding-top: var(--space-16); padding-bottom: var(--space-16);
}

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-6);
  align-items: stretch;
}

.pricing-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  position: relative;
}

.pricing-card-recommended {
  border-color: var(--accent-teal);
  background: linear-gradient(160deg, rgba(45,212,191,0.06) 0%, rgba(99,102,241,0.06) 100%);
  box-shadow: 0 0 0 1px var(--accent-teal), 0 8px 32px rgba(45,212,191,0.12);
}

.pricing-badge-recommended {
  position: absolute;
  top: -1px;
  right: var(--space-4);
  transform: translateY(-50%);
  background: var(--accent-grad);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.2rem 0.6rem;
  border-radius: 9999px;
}

.pricing-plan-name {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--text);
}

.pricing-price {
  display: flex;
  align-items: baseline;
  gap: 0.25rem;
  /* Перенос нужен карточке раздачи: прежняя цена и слово «Бесплатно» в одну
     строку не помещаются и без него вылезают за край карточки. */
  flex-wrap: wrap;
}

.pricing-amount {
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: -0.04em;
  color: var(--text);
}

/* Прежняя цена рядом с «Бесплатно». Перечёркивание — единственное, что отличает
   её от действующей цены, поэтому оно и есть смысл правила, а не украшение. */
/* Прежняя цена рядом с «Бесплатно». Перечёркивание — единственное, что отличает
   её от действующей цены, поэтому оно и есть смысл правила, а не украшение.

   🔴 Своя СТРОКА, а не место слева. Рядом со словом «Бесплатно» пара не влезает
   в карточку (их пять в ряд) и обрезается краем. display:block работает в обоих
   контейнерах: в .pricing-price (флекс) его блокирует flex-basis, в .plan-price
   кабинета (обычный блок) — само по себе.

   🔴 nowrap обязателен. FormatMoney отделяет знак валюты ОБЫЧНЫМ пробелом
   ("400,00 ₽"; неразрывный там только разделитель тысяч), и по этому пробелу
   строка рвалась, оставляя рубль болтаться отдельной строкой. */
.pricing-amount-was {
  display: block;
  flex-basis: 100%;
  margin-bottom: 0.15rem;
  font-size: 1.1rem;
  font-weight: 600;
  text-decoration: line-through;
  color: var(--text-muted);
  white-space: nowrap;
}

/* Выравнивание строк под ценой, когда в сетке есть карточка раздачи.
   У неё блок цены в две строки (86 px против 51 px у платных), и без резерва
   всё, что ниже цены, съезжало у неё на 35 px против соседей.

   Резерв висит на :has() НАМЕРЕННО: когда раздача выключена, карточки раздачи в
   сетке нет, правило не срабатывает, и четыре платные карточки не получают
   пустой строки ни за что. Браузер без :has() покажет прежнее расхождение —
   это ухудшение вида, а не поломка.

   И только от 769px: на 768 и уже сетка становится одноколоночной (правило
   .pricing-grid ниже), карточки идут одна под другой, выравнивать между ними
   нечего — резерв там был бы чистой пустотой в каждой из пяти. */
@media (min-width: 769px) {
  .pricing-grid:has(.pricing-amount-was) .pricing-price {
    min-height: 5.5rem;
  }
}

.pricing-period {
  font-size: 0.875rem;
  color: var(--text-muted);
}

.pricing-seats {
  font-size: 0.85rem;
  color: var(--text-muted);
}
/* The thread/port line, directly under .pricing-seats and the same kind of
   fact about the plan. With no rule of its own it took the card's inherited
   15px at full --text, so the less important of the two specs shouted louder
   than the one above it. */
.pricing-ports {
  font-size: 0.85rem;
  color: var(--text-muted);
}

/* The plan's one-line description. Also unstyled until now, also at full
   --text, which put it level with the plan name for weight of voice. It is
   supporting copy: same size as the spec lines, muted, with room to breathe
   since it is the only multi-line text in the card. */
.pricing-note {
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-muted);
}

/* Per-plan hooks on the note, emitted as pricing-note-{{.Code}}. They exist so
   one plan's note can be singled out without touching the others, and today
   none needs to be: the deliberate styling is none at all. Listed rather than
   left absent so the stylesheet says that out loud, and so the guard in
   internal/web/landing_css_test.go can see a decision instead of a hole.

   NOTE: these names come from the plan CODES in the catalogue, not from this
   file. Adding a plan adds a class, and the guard will fail naming it — which
   is the prompt to decide whether that plan's note wants its own treatment,
   and to add its code to this selector if it does not. */
.pricing-note-lite,
.pricing-note-standard,
.pricing-note-pro,
.pricing-note-enterprise {
  /* deliberately nothing; see above */
}

/* "Price on request" where the other cards carry a numeral. .pricing-amount is
   2rem at weight 800 with tight tracking — right for $19.99, wrong for a
   phrase, which wrapped onto two lines and pushed the rest of the card down.
   This keeps it a price line while letting it be words. */
.pricing-amount-quote {
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.3;
}

/* The by-request card. It is not self-serve — its button goes to /contacts,
   not to registration — and with no rule it was indistinguishable from the
   plan beside it. A dashed border is the smallest mark that says "talk to us"
   rather than "buy now"; everything else stays .pricing-card. */
.pricing-card-unlimited {
  border-style: dashed;
}

.pricing-features {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 1;
}

.pricing-feature-item {
  font-size: 0.875rem;
  color: var(--text-muted);
  padding-left: 1.25rem;
  position: relative;
}

.pricing-feature-item::before {
  content: '✓';
  position: absolute;
  left: 0;
  color: var(--accent-teal);
  font-weight: 700;
}

.pricing-cta {
  display: block;
  text-align: center;
  margin-top: auto;
}

/* Consistent price-row height so the amount and "By request" line up across cards. */
.pricing-price { min-height: 2.4rem; }

/* Gap between the plan grid and the money-back note. */
.pricing-moneyback {
  margin-top: var(--space-8);
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9rem;
}

.pricing-unavailable {
  text-align: center;
  color: var(--text-muted);
  font-size: 0.9rem;
  padding-top: var(--space-8); padding-bottom: var(--space-8);
}

/* ── FAQ ────────────────────────────────────────────────────────────────── */
.landing-faq {
  padding-top: var(--space-16); padding-bottom: var(--space-16);
  background: rgba(255,255,255,0.015);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.faq-inner {
  max-width: 720px;
}

.faq-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.faq-item {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.faq-question {
  cursor: pointer;
  padding: var(--space-4) var(--space-6);
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--text);
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  user-select: none;
  transition: color 0.15s;
}

.faq-question::-webkit-details-marker { display: none; }

.faq-question::after {
  content: '+';
  font-size: 1.2rem;
  color: var(--accent-teal);
  flex-shrink: 0;
}

.faq-item[open] .faq-question::after {
  content: '−';
}

.faq-question:hover { color: var(--accent-teal); }

.faq-question:focus-visible {
  outline: 2px solid var(--accent-teal);
  outline-offset: -2px;
}

.faq-answer {
  padding: 0 var(--space-6) var(--space-4);
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.7;
}

/* ── Bottom CTA strip ───────────────────────────────────────────────────── */
.landing-cta-strip {
  padding-top: var(--space-16); padding-bottom: var(--space-16);
  position: relative;
}
/* CTA band: headline + subtext left, button + links right (matches the mockup). */
.cta-band {
  position: relative;
  overflow: hidden;
  border-radius: 20px;
  border: 1px solid rgba(91,120,255,0.3);
  background: linear-gradient(120deg, #0a0f22, #0b1020 55%, #0e0c22);
  display: flex;
  align-items: center;
  gap: 30px;
  padding: 40px 48px;
}
.cta-band::before {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(50% 120% at 12% 40%, rgba(34,120,255,0.28), transparent 60%),
    radial-gradient(60% 130% at 92% 60%, rgba(139,92,246,0.24), transparent 62%);
}
.cta-copy { position: relative; z-index: 1; flex: 1; text-align: left; }
.cta-strip-headline {
  font-size: clamp(1.5rem, 2vw + 0.9rem, 1.875rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.18;
  color: var(--text);
}
.cta-strip-sub { font-size: 0.95rem; color: var(--text-muted); margin-top: 10px; }
.cta-side {
  position: relative; z-index: 1; flex-shrink: 0;
  display: flex; flex-direction: column; align-items: flex-end; gap: 14px;
}
.cta-strip-links { font-size: 0.8rem; color: var(--text-muted); }
@media (max-width: 760px) {
  .cta-band { flex-direction: column; align-items: flex-start; padding: 32px; }
  .cta-side { align-items: flex-start; }
}

/* ── Footer landing extras ──────────────────────────────────────────────── */
/* Wraps: this nav grew to five links (changelog, terms, privacy, login,
   register) and must not push the footer wider than the viewport. */
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  row-gap: var(--space-2);
}

/* ── Landing responsive ─────────────────────────────────────────────────── */
/* Below ~1200px the six nav links can no longer share a row with the brand and
   the action buttons. Give the nav its own full-width row rather than letting it
   wrap raggedly around them: two tidy rows instead of three, and still never a
   scrollbar. Actions stay right-aligned on the first row. */
@media (max-width: 1200px) {
  .landing-header-actions { margin-left: auto; }
  .landing-nav {
    order: 3;
    flex-basis: 100%;
    justify-content: center;
  }
}

@media (max-width: 768px) {
  .landing-nav { display: none; }
  .landing-header-actions { gap: var(--space-2); }
  .landing-section-title { font-size: 1.4rem; }
  .pricing-grid {
    grid-template-columns: 1fr;
    max-width: 380px;
    margin-left: auto; margin-right: auto;
  }
  .pricing-badge-recommended { right: var(--space-3); }
}

@media (max-width: 640px) {
  .landing-hero { padding-top: var(--space-12); padding-bottom: var(--space-8); }
  .hero-ctas { flex-direction: column; }
  .btn-lg { width: 100%; max-width: 280px; }
  .feature-grid { grid-template-columns: 1fr; }
  .landing-features,
  .landing-pricing,
  .landing-faq,
  .landing-cta-strip { padding-top: var(--space-12); padding-bottom: var(--space-12); }
  .landing-section-sub { margin-bottom: var(--space-8); }
}

/* ─── Brand mark — BlankTrail Proxy padlock (single master: /static/logo.png) ───── */
.brand { display: inline-flex; align-items: center; gap: 0.5rem; }
.brand::before {
  content: "";
  width: 1.2em; height: 1.2em;
  background: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2064%2064'%20width='64'%20height='64'%3E%20%3Cdefs%3E%20%3ClinearGradient%20id='g'%20x1='0'%20y1='0'%20x2='0'%20y2='1'%3E%20%3Cstop%20offset='0'%20stop-color='%23818cf8'/%3E%20%3Cstop%20offset='1'%20stop-color='%236366f1'/%3E%20%3C/linearGradient%3E%20%3C/defs%3E%20%3Crect%20width='64'%20height='64'%20rx='15'%20fill='url(%23g)'/%3E%20%3Cpath%20d='M32%209%2053%2016.5V34c0%2011.5-9%2019.5-21%2023-12-3.5-21-11.5-21-23V16.5Z'%20fill='%23fff'/%3E%20%3Ccircle%20cx='32'%20cy='29'%20r='5.5'%20fill='%236366f1'/%3E%20%3Cpath%20d='M28.8%2030h6.4l-1.6%2013h-3.2Z'%20fill='%236366f1'/%3E%20%3C/svg%3E") center / contain no-repeat;
  flex: none;
}

/* ─── Hero technical mentions ──────────────────────────────────────────────── */
.hero-tech {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;
  margin-top: var(--space-8); max-width: 600px;
}
.hero-tech .chip {
  display: inline-flex; align-items: center; gap: 8px;
  height: 38px; padding: 0 15px; border-radius: 9px;
  background: #0a0e1a; border: 1px solid var(--border);
  color: #c3cadf; font-size: 0.8rem; font-weight: 700; white-space: nowrap;
}
.hero-tech .chip svg { flex: none; color: var(--accent-cyan); }
.hero-tech .chip:nth-child(2n) svg { color: #a5b4fc; }
.hero-tech .chip:nth-child(3n) svg { color: var(--success); }

/* ─── Hero use-case carousel ───────────────────────────────────────────────── */
.hero-cases { position: relative; max-width: 960px; margin: var(--space-12) auto 0; }
/* Match the other section kickers (centered, blue, bold). */
.cases-eyebrow {
  text-align: center;
  font-size: 12px; font-weight: 800; letter-spacing: 0.22em;
  text-transform: uppercase; color: #5b8bff; margin-bottom: 14px;
}
.cases-viewport { overflow: hidden; border-radius: var(--radius); }
.cases-track {
  display: flex; align-items: flex-start;
  transition: transform 0.5s cubic-bezier(0.22,0.61,0.36,1);
}
.case-slide { flex: 0 0 100%; min-width: 0; padding: 0 4px; }

/* Text-only case card (no screenshot): neon-bordered panel. */
.case-cap {
  text-align: center; padding: var(--space-8) var(--space-6);
  background: linear-gradient(180deg, #0b0f1c, #090c16);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: 0 24px 60px -30px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.03);
}
.case-tag {
  display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.72rem; font-weight: 800;
  letter-spacing: 0.06em; text-transform: uppercase; color: #aecaff;
  border: 1px solid rgba(91,120,255,0.32); border-radius: 9999px;
  padding: 0.3rem 0.85rem; margin-bottom: var(--space-4);
  background: linear-gradient(90deg, rgba(59,91,255,0.14), rgba(139,92,246,0.09));
}
.case-title { font-size: 1.5rem; font-weight: 700; line-height: 1.22; margin-bottom: var(--space-3); letter-spacing: -0.01em; }
.case-desc { color: var(--text-muted); font-size: 1rem; line-height: 1.62; max-width: 680px; margin: 0 auto; }

.cases-controls {
  display: flex; align-items: center; justify-content: center;
  gap: var(--space-4); margin-top: var(--space-6);
}
.cases-arrow {
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
  font-size: 1.3rem; line-height: 1; cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}
.cases-arrow:hover { border-color: var(--border-bright); color: #fff; box-shadow: 0 0 14px rgba(80,110,255,0.22); }
.cases-dots { display: flex; gap: 0.5rem; }
.cases-dots button {
  width: 8px; height: 8px; padding: 0; border: none; border-radius: 50%;
  background: var(--border); cursor: pointer; transition: background 0.15s, transform 0.15s;
}
.cases-dots button[aria-selected="true"] { background: #5b8bff; transform: scale(1.35); box-shadow: 0 0 8px rgba(91,139,255,0.7); }
.cases-dots button:hover { background: var(--text-muted); }

/* Flex-gap fallback (probe may false-positive on modern browsers). */
.no-flexgap .cases-controls > * + * { margin-left: var(--space-4); }
.no-flexgap .cases-dots > * + * { margin-left: 0.5rem; }

@media (prefers-reduced-motion: reduce) {
  .cases-track { transition: none; }
}
@media (max-width: 560px) {
  .case-title { font-size: 1.12rem; }
  .cases-eyebrow { text-align: center; }
}

/* ─── Advanced capabilities grid ───────────────────────────────────────────── */
.adv-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 18px;
  margin-top: var(--space-12); text-align: left;
}
.feature-example {
  margin-top: 0.9rem; padding-top: 0.85rem; border-top: 1px solid var(--border);
  color: var(--text-muted); font-size: 0.9rem; line-height: 1.6;
}
@media (max-width: 760px) { .adv-grid { grid-template-columns: 1fr; } }

/* ─── Downloads page (OS-grouped) ────────────────────────────────────────────── */
.download-license-head {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; gap: var(--space-2);
  padding-bottom: var(--space-4); margin-bottom: var(--space-6);
  border-bottom: 1px solid var(--border);
}
.download-plan { font-weight: 700; font-size: 1.05rem; color: var(--text); }
.download-meta { color: var(--text-muted); font-size: 0.85rem; }

/* One OS group (Linux / Windows) — distinct bordered card inside the license. */
.download-os {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-6);
  background: rgba(255,255,255,0.015);
}
.download-os + .download-os { margin-top: var(--space-6); }
.download-os-title {
  font-size: 1.1rem; font-weight: 700; color: var(--text);
  margin-bottom: var(--space-4);
}

/* Each option (recommended primary + secondary subgroup). */
.download-option { padding: var(--space-4) 0; }
.download-option + .download-option { border-top: 1px solid var(--border); }
.download-option-primary {
  border-left: 3px solid var(--accent-teal);
  padding-left: var(--space-4); margin-left: calc(-1 * var(--space-4) - 3px);
}
.download-option-head {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
}
.download-subhead { font-size: 0.95rem; font-weight: 600; color: var(--text); margin: 0; }
.download-recommended { text-transform: uppercase; letter-spacing: 0.04em; }
.download-help { color: var(--text-muted); font-size: 0.875rem; margin: var(--space-2) 0 var(--space-3); }

.install-oneliner {
  background: var(--bg-input); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: var(--space-3) var(--space-4);
  font-family: 'Fira Mono', 'Consolas', monospace; font-size: 0.85rem;
  color: var(--accent-teal); overflow-x: auto;
}
.download-archives {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--space-2);
}

/* Downloads: muted advisory note (e.g. the MITM/antivirus exclusion hint). */
.page-note {
  color: var(--text-muted);
  font-size: 0.9rem;
  line-height: 1.5;
  margin: 0 0 var(--space-4);
}

/* ─── Support page (channel chooser + web chat) ─────────────────────────────── */
.support-page { max-width: 760px; }

.support-intro {
  color: var(--text-muted);
  font-size: 0.95rem;
  line-height: 1.6;
  margin: 0 0 var(--space-8);
  max-width: 56ch;
}

/* Channel chooser — two cards (Telegram recommended + web chat). */
.support-channels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-6);
  align-items: stretch;
}

.support-channel {
  position: relative;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-8) var(--space-6) var(--space-6);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  transition: border-color 0.2s, transform 0.2s;
}
.support-channel:hover { border-color: rgba(45,212,191,0.4); transform: translateY(-2px); }

.support-channel-recommended {
  border-color: var(--accent-teal);
  background: linear-gradient(160deg, rgba(45,212,191,0.06) 0%, rgba(99,102,241,0.06) 100%);
  box-shadow: 0 0 0 1px var(--accent-teal), 0 8px 32px rgba(45,212,191,0.12);
}

.support-channel-badge {
  position: absolute;
  top: -1px;
  right: var(--space-4);
  transform: translateY(-50%);
  background: var(--accent-grad);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.2rem 0.6rem;
  border-radius: 9999px;
}

.support-channel-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  color: #fff;
  background: var(--accent-grad);
}
.support-channel-icon-web {
  color: var(--accent-teal);
  background: rgba(45,212,191,0.12);
}

.support-channel-name {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--text);
  margin: 0;
}
.support-channel-desc {
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.6;
  margin: 0;
  flex: 1; /* push the button to the bottom so both cards align */
}
.support-channel-note {
  font-size: 0.82rem;
  color: var(--text-muted);
  margin: 0;
}
.support-channel-connected { color: var(--accent-teal); }
.support-channel .btn { margin-top: var(--space-2); }

/* Web chat panel */
.support-chat { margin-top: var(--space-2); }

.support-back {
  background: none;
  border: none;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  padding: 0;
  margin-bottom: var(--space-4);
}
.support-back:hover { color: var(--accent-teal); }

.support-chat-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.support-thread {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-height: 52vh;
  min-height: 160px;
  overflow-y: auto;
  padding-right: var(--space-2);
}

.support-msg { display: flex; }
.support-msg-user { justify-content: flex-end; }
.support-msg-staff { justify-content: flex-start; }

.support-bubble {
  max-width: 78%;
  padding: var(--space-3) var(--space-4);
  border-radius: 14px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.support-msg-user .support-bubble {
  background: var(--accent-grad);
  color: #fff;
  border-bottom-right-radius: 4px;
}
.support-msg-staff .support-bubble {
  background: var(--bg-input);
  border: 1px solid var(--border);
  color: var(--text);
  border-bottom-left-radius: 4px;
}

.support-msg-sender {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  opacity: 0.8;
  text-transform: uppercase;
}
.support-msg-body {
  font-size: 0.92rem;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}
.support-msg-time { font-size: 0.68rem; opacity: 0.6; align-self: flex-end; }

.support-empty {
  color: var(--text-muted);
  font-size: 0.9rem;
  text-align: center;
  padding: var(--space-8) var(--space-4);
  margin: 0;
}

.support-compose { display: flex; flex-direction: column; gap: var(--space-3); }
.support-compose-body { resize: vertical; min-height: 3.5rem; }
.support-compose-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.support-compose-hint { font-size: 0.8rem; color: var(--accent-teal); }

/* Attachments: the picker on the compose form and the files inside a bubble.
   Images are laid out as a wrapping row of thumbnails, everything else as a
   labelled download chip — a name a customer can recognise beats an icon. */
.support-attach { display: flex; flex-direction: column; gap: var(--space-2); }
.support-attach-label { display: flex; flex-direction: column; gap: var(--space-2); }
.support-attach-hint { font-size: 0.78rem; color: var(--text-muted); }
.support-attach-list {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.78rem;
  color: var(--text-muted);
  word-break: break-all;
}

.support-files {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.support-file { display: inline-flex; align-items: center; text-decoration: none; }
.support-file-image img {
  display: block;
  max-width: 180px;
  max-height: 180px;
  border-radius: 8px;
  object-fit: cover;
}
.support-file-doc {
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid currentColor;
  border-radius: 8px;
  font-size: 0.8rem;
  opacity: 0.9;
}
.support-file-doc:hover { opacity: 1; }
.support-file-name { word-break: break-all; }
.support-file-size { opacity: 0.65; white-space: nowrap; }

/* The customer's list of conversations. Rows are links rather than cards: a
   history is scanned top to bottom for the one you half-remember, and a
   subject in a readable weight beside a date does that better than a grid of
   equal-looking tiles. A resolved row is dimmed rather than hidden — it is
   still theirs to open. */
.support-history { margin-top: var(--space-8); }
.support-history-title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0 0 var(--space-4);
}
.support-list { list-style: none; margin: 0; padding: 0; }
.support-list-item + .support-list-item { border-top: 1px solid var(--border-soft); }
.support-list-item-resolved { opacity: 0.62; }
.support-list-item-resolved:hover { opacity: 1; }
.support-list-link {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-2);
  text-decoration: none;
  color: inherit;
  border-radius: 8px;
}
.support-list-link:hover { background: rgba(255,255,255,0.04); }
.support-list-subject {
  font-size: 0.94rem;
  font-weight: 500;
  word-break: break-word;
}
.support-list-meta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
  font-size: 0.78rem;
  color: var(--text-muted);
}
.support-list-status {
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  white-space: nowrap;
}
.support-list-status-open { color: var(--accent-teal); }
.support-list-status-resolved { color: var(--text-muted); }
.support-list-date { white-space: nowrap; }
/* The unread count. Deliberately loud where the rest of the row is quiet —
   it is the only thing on this page that asks for attention. */
.support-list-unread {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.35rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--accent-teal);
  color: #06231f;
  font-size: 0.72rem;
  font-weight: 700;
}

/* Raising a new topic. Hidden until asked for, and only offered when nothing
   is open — see internal/cabinet/support.go on why one at a time. */
.support-new {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-6);
  padding: var(--space-4);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
}
.support-new-title { font-size: 1rem; font-weight: 600; margin: 0; }
.support-new-label { font-size: 0.8rem; color: var(--text-muted); }

/* One conversation. */
.support-thread-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.support-page-thread .page-title { word-break: break-word; }
.support-resolved-note {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-radius: 8px;
  background: rgba(255,255,255,0.04);
  color: var(--text-muted);
  font-size: 0.86rem;
}
.support-resolve {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
  flex-wrap: wrap;
}
.support-resolve-hint { font-size: 0.78rem; color: var(--text-muted); }

@media (max-width: 768px) {
  .support-channels { grid-template-columns: 1fr; }
  .support-bubble { max-width: 88%; }
  .support-list-link { flex-direction: column; align-items: flex-start; gap: var(--space-2); }
}

/* ─── Detail pages (license / order): header, definition list, actions ──────── */
.page-header { margin-bottom: var(--space-6); }
.back-link {
  display: inline-block;
  color: var(--text-muted);
  font-size: 0.9rem;
  margin-bottom: var(--space-3);
}
.back-link:hover { color: var(--accent-teal); }

.empty-state {
  color: var(--text-muted);
  font-size: 0.9rem;
  padding: var(--space-6) var(--space-4);
}

.license-info-card { max-width: 560px; }

/* ─── Ширины страниц кабинета ───────────────────────────────────────────────
 *
 * 🔴 Классы-обёртки (.developer-page, .downloads-page, .orders-page …) не были
 * описаны НИ ОДНИМ правилом, поэтому ширину не держало ничто, кроме общего
 * .cab-content (1320px). Форме заявки и инструкции по установке столько не
 * нужно: строка в 1300 пикселей не читается, а поле ввода во всю ширину
 * выглядит так, будто в него ждут что-то очень длинное.
 *
 * Таблицам (заказы, лицензии) ширина НУЖНА — у них много колонок, и сужать их
 * значило бы получить перенос в ячейках. Им правило задано пустым по смыслу и
 * с пояснением, чтобы «нет max-width» читалось как решение, а не как забытая
 * строка.
 */
.developer-page { max-width: 860px; }
/* Формы уже поверх карточки, ширина которой задана страницей. Ограничение
 * здесь — про длину СТРОКИ ВВОДА: «Название ПО» в поле на 800px выглядит так,
 * будто туда ждут абзац. */
.developer-apply-form,
.developer-new-app { max-width: 520px; }
.developer-apply-form .form-group:last-of-type { margin-bottom: var(--space-6); }
.developer-withdraw { margin-top: var(--space-4); }
/* Префикс ключа — идентификатор, а не проза: моноширинный, чтобы одинаковые
 * префиксы визуально совпадали по длине. */
.developer-key-prefix {
  font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
  font-size: 0.85rem;
}
/* Ключ целиком — его копируют, поэтому поле, а не текст: клик выделяет всё
 * значение. Ячейка задаёт ширину, иначе 51 символ растягивает таблицу. */
.developer-key-cell { min-width: 22rem; }
.developer-key-kind { white-space: nowrap; }
.developer-key-attributed { text-align: right; font-variant-numeric: tabular-nums; }
.developer-internal-key-section { margin-top: var(--space-6); }
.developer-internal-key-form { max-width: 520px; }
.developer-key-value {
  font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
  font-size: 0.85rem;
  width: 100%;
}
.downloads-page { max-width: 1040px; }   /* строка установки длинная, но не 1320 */
.orders-page,
.licenses-page { max-width: 100%; }      /* таблицы: ширина по колонкам, намеренно */

/* Label / value pairs rendered as a clean two-column grid (was an unstyled dl). */
.detail-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-3) var(--space-8);
  margin: 0;
  align-items: baseline;
}
.detail-list dt {
  color: var(--text-muted);
  font-size: 0.85rem;
  font-weight: 500;
}
.detail-list dd {
  margin: 0;
  color: var(--text);
  font-size: 0.95rem;
  word-break: break-word;
}
.detail-list .license-key {
  font-family: 'Fira Mono', 'Consolas', monospace;
  letter-spacing: 0.02em;
}

.license-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: var(--space-6) 0;
}

.devices-section { margin-top: var(--space-8); }
.devices-section h2 {
  font-size: 1.15rem;
  font-weight: 700;
  margin-bottom: var(--space-4);
}
.device-id {
  font-family: 'Fira Mono', 'Consolas', monospace;
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* Per-device actions cell — keep deactivate + rename controls on one tidy row. */
.device-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}
.device-actions form {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}
.input-sm {
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
  padding: 0.3rem 0.5rem;
  max-width: 160px;
}
.input-sm:focus { outline: none; border-color: var(--border-focus); }

@media (max-width: 560px) {
  .detail-list { grid-template-columns: 1fr; gap: var(--space-1) 0; }
  .detail-list dd { margin-bottom: var(--space-3); }
}

/* ═══ Admin + cabinet shared components (previously unstyled classes) ═══════════ */

/* Metric / stat cards (analytics, home, balance, referrals). stat-value/stat-label
   are already styled; these give the missing card + row layout. */
.stats-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-4);
  margin: var(--space-6) 0;
}
.stat-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.stat-card .stat-value { font-size: 1.6rem; font-weight: 800; letter-spacing: -0.02em; color: var(--text); }
.stat-card .stat-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.stat-link { text-decoration: none; transition: border-color 0.2s, transform 0.2s; }
.stat-link:hover { border-color: rgba(45,212,191,0.4); transform: translateY(-2px); }
.stat-held .stat-value { color: var(--warning); }

/* Per-day bar list (analytics) */
.day-bars { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.day-bar-row { display: grid; grid-template-columns: 108px 1fr auto; align-items: center; gap: var(--space-3); font-size: 0.85rem; }
.day-bar-label { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.day-bar-track { height: 8px; background: var(--bg-input); border-radius: 9999px; overflow: hidden; }
.day-bar-fill { display: block; height: 100%; min-width: 2px; background: var(--accent-grad); border-radius: 9999px; }
.day-bar-value { color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Analytics range form + states */
.analytics-range { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-4); margin: var(--space-4) 0 var(--space-6); }
.analytics-range label { display: flex; flex-direction: column; gap: var(--space-1); font-size: 0.85rem; color: var(--text-muted); }
.analytics-section { margin-top: var(--space-8); }
.analytics-unavailable {
  color: #fcd34d; background: rgba(245,158,11,0.1);
  border: 1px solid rgba(245,158,11,0.3); border-radius: var(--radius);
  padding: var(--space-3) var(--space-4); font-size: 0.9rem;
}

/* Status pill + status color variants (support tickets, updates, payouts, etc.) */
.status-badge, .badge-status {
  display: inline-block; padding: 0.15rem 0.6rem; border-radius: 9999px;
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.02em;
  background: rgba(148,163,184,0.15); color: var(--text-muted);
}
.status-open, .status-answered { background: rgba(45,212,191,0.15); color: #5eead4; }
/* status-awaiting is not a stored status but the derived "a customer is waiting
   on us" — see telegram.awaitingExpr. Amber rather than the teal above: teal is
   what every settled state uses here, and a queue item that needs work must not
   look settled. */
.status-awaiting { background: rgba(251,191,36,0.16); color: #fcd34d; }
.row-awaiting > td { background: rgba(251,191,36,0.05); }
.support-awaiting-summary {
  display: flex; align-items: center; gap: var(--space-2);
  margin: 0 0 var(--space-4); font-size: 0.88rem; color: var(--text);
}
.support-awaiting-clear { color: var(--text-muted); }
/* archived spells the neutral grey out instead of inheriting it from
   .status-badge. The catalog price history is checked by a guard that reads
   the classes off the RENDERED page, so a status with no selector of its own
   fails that check even when the colour it falls back to is the right one. */
.status-closed, .status-archived { background: rgba(148,163,184,0.15); color: var(--text-muted); }
.status-active { background: rgba(34,197,94,0.15); color: #86efac; }
/* status-overdue is not an entity status but a waiting time that has run past
   its promise (a refund request older than the money-back window). It shares the
   red treatment because it reads the same way: something needs attention. */
.status-suspended, .status-revoked, .status-failed, .status-overdue { background: rgba(239,68,68,0.15); color: #fca5a5; }
.status-pending, .status-awaiting_payment, .status-held_aml { background: rgba(245,158,11,0.15); color: #fcd34d; }
.status-paid { background: rgba(34,197,94,0.15); color: #86efac; }
.status-refunded { background: rgba(168,85,247,0.15); color: #d8b4fe; }
/* expired / superseded / cancelled deliberately keep the neutral .status-badge
   grey: they are closed-without-money, not a problem to look at. */
.badge-muted { background: rgba(148,163,184,0.15); color: var(--text-muted); }

/* Client-release page (/admin/dist): exactly one release is live at a time, so
   "published" carries the green live treatment and everything else stays the
   neutral grey. Scoped to this page — elsewhere "published" is one of many
   states, not the answer to "what are customers getting right now?". */
.admin-dist .status-published { background: rgba(34,197,94,0.15); color: #86efac; }
.admin-dist .dist-current { margin: 0.5rem 0 1.5rem; }
.admin-dist .dist-missing { margin: 0.35rem 0 0; padding-left: 1.1rem; font-size: 0.82rem; color: var(--text-muted); }
/* The incomplete-publish button is a testing action, so its caption is warning
   coloured rather than muted like an ordinary hint: it has to be readable next
   to the button, not below the fold of attention. */
.admin-dist .dist-incomplete-warning { display: block; margin: 0.3rem 0 0; font-size: 0.8rem; color: #fbbf24; }

/* Operator picture library (/admin/media): upload form + thumbnail grid.
   Scoped under .admin-media, like .admin-dist above — media-grid/media-card
   are specific to this one page, not generic components. */
.admin-media .media-upload { margin-bottom: var(--space-6); }
.admin-media .media-count { color: var(--text-muted); font-size: 0.9rem; margin: 0 0 var(--space-4); }
.admin-media .media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-4);
}
.admin-media .media-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
/* Bounded regardless of the source file's own dimensions — a 4000px camera
   original crops into this box; it can never stretch the grid around it. */
.admin-media .media-card img {
  display: block;
  width: 100%;
  height: 170px;
  object-fit: cover;
}
.admin-media .media-card figcaption {
  padding: var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.admin-media .media-title { color: var(--text); font-weight: 600; font-size: 0.9rem; word-break: break-word; }
.admin-media .media-meta { color: var(--text-faint); font-size: 0.78rem; }
.admin-media .media-url { font-family: 'Fira Mono', 'Consolas', monospace; font-size: 0.78rem; }
.admin-media .media-pager { display: flex; gap: var(--space-3); margin-top: var(--space-6); }

/* Landing revision list (/admin/landing). The note cell is free text an
   operator typed, so it wraps instead of stretching the table; the version
   reads as a number, not prose. */
.admin-landing .landing-version { font-variant-numeric: tabular-nums; white-space: nowrap; }
.admin-landing .data-table td { vertical-align: top; }
.admin-landing .row-actions .form-input { width: 9rem; }

/* Landing block editor (/admin/landing/{id}/edit).

   Two columns: the block list and the selected block's generated form. The
   grid collapses to one column below 60rem — the form is a stack of full-width
   fields either way, and a 12rem sidebar next to it on a phone leaves neither
   readable. */
.admin-landing-edit { min-width: 0; }
.landing-editor {
  display: grid;
  grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}
@media (max-width: 60rem) {
  .landing-editor { grid-template-columns: minmax(0, 1fr); }
}
.landing-editor-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-4) 0; }

.landing-blocklist {
  padding: var(--space-4);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.landing-blocklist-title { margin: 0 0 var(--space-3); font-size: 0.9rem; color: var(--text-muted); }
.landing-blocklist-items { list-style: none; margin: 0; padding: 0; }
.landing-blocklist-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-2);
  border-radius: var(--radius-sm);
}
/* The selected block, not a hover state: it says which form is on the right. */
.landing-blocklist-active { background: var(--bg-input); }
.landing-blocklist-link { color: var(--text); font-weight: 600; font-size: 0.9rem; }
.landing-blocklist-anchor {
  font-family: 'Fira Mono', 'Consolas', monospace;
  font-size: 0.78rem;
  color: var(--text-faint);
}
/* The four structure buttons of one row: move up, move down, hide/show,
   delete. Each is its own form (four routes, four actions), so this is a flex
   row of forms; it takes the full width of the list item so the buttons sit
   under the block's name rather than pushing it out of a 12rem column. */
.landing-blocklist-ops {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  width: 100%;
}
/* The "add a block" menu, under the list. Stacked rather than inline: the type
   names are the longest strings in this column. */
.landing-blocklist-add {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

.landing-blockform { min-width: 0; }
/* One field across every locale, laid out exactly as .blog-group is and for
   the same reasons — see the comment there. Spelt separately rather than
   shared: a rule named for one page doing the other's layout is the coupling
   nobody notices until it breaks one of them. */
.landing-group {
  margin-bottom: var(--space-6);
  padding-left: var(--space-3);
  border-left: 2px solid var(--border);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  gap: 0 var(--space-6);
  align-items: start;
}
/* The block form and one item card are column grids too, but with auto-FILL
   where the groups use auto-fit. The difference matters for the STRAY singles
   these two hold — the block's anchor, a row's icon, picture and link. Those
   are unrelated controls, not one value in three languages: auto-fit collapses
   the tracks they do not fill and lets the survivor stretch, which is how a
   short identifier like "hero" ended up in an input the width of the page.
   auto-fill keeps the empty tracks, so a lone control occupies ONE column.
   Anything that lays out its own columns spans the row instead. */
.landing-blockform .admin-form,
.landing-item {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(22rem, 100%), 1fr));
  gap: 0 var(--space-6);
  align-items: start;
}
.landing-blockform .admin-form > .landing-group,
.landing-blockform .admin-form > fieldset,
.landing-blockform .admin-form > p,
.landing-item > .landing-group,
.landing-item > .landing-item-head {
  grid-column: 1 / -1;
}

/* ─── Landing editor: the page-code pane ─────────────────────────────────────
   The pinned control above the block list.

   It is a CARD and not a list row. The first version was a plain link in the
   same size and weight as the "Blocks" heading below it, and it was invisible
   as a control: the owner opened the page and asked where the option was while
   looking at it. Every block row below carries ↑ ↓ Hide Delete underneath,
   which is what makes those read as items; this one has no controls of its
   own, so it has to say "press me" some other way. */
.landing-codelink {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  transition: border-color 0.15s, background 0.15s;
}
.landing-codelink:hover { border-color: var(--border-bright); text-decoration: none; }
/* The open state matches .landing-blocklist-active's job on the rows below —
   this column always says which pane the centre is showing. */
.landing-codelink-active {
  border-color: var(--border-focus);
  /* No second background token: the palette has none for an elevated surface,
     and inventing var(--bg-elevated, …) would have made the FALLBACK the real
     value while reading like a themed choice. The border and the left accent
     carry the state. */
  box-shadow: inset 3px 0 0 var(--accent-teal);
}
.landing-codelink-glyph {
  flex: none;
  font-family: 'Fira Mono', 'Consolas', monospace;
  font-size: 0.85rem;
  color: var(--accent-teal);
}
.landing-codelink-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.landing-codelink-title { font-size: 0.9rem; font-weight: 600; color: var(--text); }
/* The line that says what pressing it opens. Without it the name alone is
   ambiguous between "the code of this page" and "a page about code". */
.landing-codelink-hint { font-size: 0.75rem; color: var(--text-muted); }

/* The code form opts OUT of the column grid every other form on this page
   opted into. Its boxes are whole HTML documents: three of them side by side
   at a third of the width each would be unreadable, and the whole point of
   the mode is the operator writing a page. Spelt with the parent selector so
   it outranks `.landing-blockform .admin-form` rather than relying on which
   rule comes last in the file. */
.landing-blockform .admin-form.landing-code-form { display: block; }

/* The import control sits between the document boxes and the save button, in
   a form of its own — HTML has no nested forms and it posts elsewhere. */
.landing-code-import {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-4) 0;
}
.landing-code-import-hint { margin: 0; }

/* The two mode radios, side by side — they are one either/or question. */
.landing-code-mode { display: flex; flex-wrap: wrap; gap: var(--space-6); align-items: center; }

/* What code mode costs, stated on the screen where it is switched on. Amber
   and not red: it is the description of a deliberate choice, not an error —
   the same treatment .blog-edit-warn gives the address warning. */
.landing-code-warn { color: #fbbf24; }

/* The live findings: a stylesheet nothing links, an absolute href, a price
   placeholder naming no plan. A list rather than a paragraph because their
   number is the point — the pinned entry shows that count as a badge. */
.landing-code-warnings {
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  list-style: none;
  border: 1px solid #78350f;
  border-left: 3px solid #fbbf24;
  border-radius: var(--radius-sm);
  background: rgba(251, 191, 36, 0.06);
}
.landing-code-warning { font-size: 0.85rem; line-height: 1.5; color: var(--text-muted); }
.landing-code-warning + .landing-code-warning { margin-top: var(--space-2); }

/* The <link> line to copy, shown as code because it IS code — the operator is
   meant to select it and paste it into the box below. */
.landing-code-snippet {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem;
  color: var(--text);
  word-break: break-all;
}

/* The document and stylesheet boxes.
   white-space: pre with wrap="off" on the element is what gives this the
   horizontal scrollbar a code editor is expected to have: a long line stays
   one line, so the structure of the markup survives being looked at. */
.landing-code-area {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem;
  line-height: 1.5;
  white-space: pre;
  overflow: auto;
  tab-size: 2;
  resize: vertical;
  min-height: 18rem;
}

/* Pictures and params: no language, no grouping, straight into the columns. */
.landing-fieldset-flow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  gap: 0 var(--space-6);
  align-items: start;
}
.landing-fieldset-flow > .landing-fieldset-title { grid-column: 1 / -1; }
.landing-blockform-title { margin: 0 0 var(--space-4); font-size: 1.1rem; }

.landing-fieldset {
  margin: var(--space-6) 0 0;
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.landing-fieldset-title { padding: 0 var(--space-2); font-size: 0.85rem; color: var(--text-muted); }

/* One repeating child. The dashed rule is what tells two adjacent rows apart
   when both are full of half-filled boxes. */
.landing-item {
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
}
.landing-item-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.landing-item-title { font-weight: 600; font-size: 0.85rem; color: var(--text-muted); }
/* The row's number, alone in its own element so the add-row control can set it
   without touching the translated word in front of it. Tabular figures for the
   same reason .landing-version has them: a column of numbers should not shift
   as it passes 9. */
.landing-item-num { font-variant-numeric: tabular-nums; }
.landing-item-drop {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: 0.8rem;
  color: var(--text-muted);
}
/* "Add a row", under the last row. The dashed cards above it carry their own
   bottom margin, so this only needs to keep the <p>'s default one off the top
   of the button. */
.landing-items-add { margin: 0; }

/* Blog post list (/admin/blog). The switch sits above the list in its own
   card, because it is a decision about the whole section rather than about any
   row: turning it off withdraws every public blog address at once. */
/* min-width: 0 for the same reason .admin-landing-edit has it: this page's
   table scrolls inside .table-responsive, and a grid/flex descendant refuses
   to shrink below its content without it — the table would widen the shell
   instead of scrolling. */
.admin-blog { min-width: 0; }
.blog-switch { margin-bottom: var(--space-6); }
/* The filter tabs and "New post" on one line, so the primary action is not
   pushed below the fold on a wide table. It wraps rather than compresses: four
   tabs and a button do not fit a phone. */
.blog-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
/* The tabs carry their own bottom margin (.filter-tabs); inside the toolbar
   the flex gap does that instead, or the button sits above its own line. */
.blog-toolbar .filter-tabs { margin-bottom: 0; }
/* The two links at the right of the toolbar. They sit in one span so the
   toolbar's space-between keeps the tabs at one end and both actions at the
   other, rather than spreading three children evenly across the row. */
.blog-toolbar-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
/* The slug is a URL fragment, not prose: monospace so an l and a 1 differ, and
   allowed to break so a long one does not stretch the table. */
.blog-slug {
  font-family: 'Fira Mono', 'Consolas', monospace;
  font-size: 0.8rem;
  color: var(--text-muted);
  word-break: break-all;
}
/* Tabular figures and no wrap, for the same reason .landing-version has them:
   a column of dates should not shift or fold. */
.blog-date { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Blog post statuses (/admin/blog). Draft and Published borrow the grey and
   green already defined above, because they mean the same thing there. A
   scheduled post is neither: it is published, and it is not on the site yet.
   Blue rather than the archive's amber — amber reads as "needs attention" and
   a post waiting on its date needs none. */
.badge-scheduled { background: rgba(56,189,248,0.15); color: #7dd3fc; }

/* Blog post editor (/admin/blog/new, /admin/blog/{id}/edit).

   ONE column, unlike the landing editor's two: a post is a single document
   with a fixed set of fields, so there is no block list to put beside it.
   min-width: 0 keeps the grid child from refusing to shrink.

   There is NO width cap. There used to be a 62rem one, against prose typed
   across a 2000-pixel line — a real complaint, but the cap answered it by
   leaving two thirds of a wide screen empty. What answers it now is
   .blog-group / .blog-fieldset-flow: the width goes into COLUMNS, so no single
   box gets wider than a readable measure and the three locales of one field
   are visible at once instead of scrolled past one another. */
.admin-blog-edit { min-width: 0; }
/* The button rows: back/preview above the form, Save under it. Wraps rather
   than compresses, exactly as .landing-editor-actions does. */
.blog-edit-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: var(--space-4) 0; }
/* Same shape as .landing-fieldset, spelled separately rather than shared: the
   two pages are free to diverge, and a rule named for the landing doing the
   blog's layout is the kind of coupling nobody notices until it breaks one of
   them. */
.blog-fieldset {
  margin: var(--space-6) 0 0;
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.blog-fieldset-title { padding: 0 var(--space-2); font-size: 0.85rem; color: var(--text-muted); }
/* One field across all three locales. The rule down the left is what says the
   three boxes below it are the same field in different languages rather than
   three unrelated ones. */
.blog-group {
  margin-bottom: var(--space-6);
  padding-left: var(--space-3);
  border-left: 2px solid var(--border);
  /* auto-fit, not auto-fill: with three locales and room for four tracks the
     empty one collapses and the three stretch, so the row is never three boxes
     and a gap. The 22rem floor is what makes three of them fit in the LANDING
     editor, whose form shares its row with a 16rem block list — at 26rem that
     column took two locales and pushed the third onto a line of its own, which
     is the stacking this layout exists to remove. Below three the grid drops to
     two columns, then one.

     min(22rem, 100%) and not a bare 22rem: a floor WIDER than the container is
     a track the grid cannot shrink, so on a phone the panel grew a horizontal
     scrollbar — measured at a 400px viewport, scrollWidth 481. min() lets the
     floor give way once there is less room than that. */
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  gap: 0 var(--space-6);
  align-items: start;
}
/* The note belongs to the whole field, not to its first locale. */
.blog-group-hint { margin-top: 0; grid-column: 1 / -1; }
/* The address block: four controls that carry no language and do not depend on
   one another, so they flow into the same columns without being grouped. */
.blog-fieldset-flow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  gap: 0 var(--space-6);
  align-items: start;
}
.blog-fieldset-flow > .blog-fieldset-title { grid-column: 1 / -1; }
/* The address warning, next to the ordinary hint under the same box: amber,
   because changing a published address breaks links that already exist and
   that is the one note on this page an operator must not skim. */
.blog-edit-warn { color: var(--warning); }
/* The tag checkboxes, wrapped rather than stacked: a blog with twenty tags
   would otherwise push Save off the screen. */
.blog-tags { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); }
.blog-tag-option { margin: 0; }
/* The slug beside the tag's name. Monospace and muted for the reason
   .blog-slug is: it is an address, not prose, and two tags whose names read
   alike in one locale are told apart by it. */
.blog-tag-slug {
  font-family: 'Fira Mono', 'Consolas', monospace;
  font-size: 0.75rem;
  color: var(--text-faint);
}
/* Publish / unpublish / delete, below the save form and outside it — HTML has
   no nested forms and these post to routes of their own. The rule above them
   separates "what the post says" from "whether it is on the site". */
.blog-edit-ops {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.blog-edit-ops form { margin: 0; }

/* Blog tags (/admin/blog/tags).

   ONE CARD PER TAG, not one table row: a tag is four boxes — an address and a
   name per locale — and four inputs across a row are unreadable at any width
   this panel actually gets. The width cap is the post editor's, for the post
   editor's reason: these are text boxes, and a box stretched to the whole shell
   makes what was typed into it harder to check. */
.admin-blog-tags { min-width: 0; max-width: 62rem; }
/* "Back to posts", above the first card. */
.blog-tag-actions { margin: var(--space-4) 0; }
.blog-tag-card { margin-bottom: var(--space-4); }
/* The row's identity line: the name as this operator reads it, the address, and
   how many posts carry it. It wraps rather than truncates — the address is the
   half that gets long, and it is also the half that has to stay readable. */
.blog-tag-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
  font-weight: 600;
}
/* How many posts carry the tag. Deliberately NOT a badge: it is a quantity,
   not a state, and it is the number the delete confirmation names — dressing it
   as a status would invite skimming past the one thing that has to be read
   before that button. */
.blog-tag-count { font-weight: 400; font-size: 0.85rem; color: var(--text-muted); }
/* Delete, below the rename form and outside it — HTML has no nested forms, and
   it posts to a route of its own. The rule above it separates "what the tag
   says" from "whether it exists at all". */
.blog-tag-ops {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.blog-tag-ops form { margin: 0; }
/* Amber, for the reason .blog-edit-warn is amber: renaming moves a public
   address and breaks links that already exist, and that is the note on this
   page an operator must not skim. Spelled separately rather than shared with
   the editor's rule — a class named for one page doing another page's layout is
   the coupling nobody notices until it breaks one of them. */
.blog-tag-warn { color: var(--warning); }

/* Telegram link pills */
.tg-badge { display: inline-block; padding: 0.15rem 0.6rem; border-radius: 9999px; font-size: 0.78rem; font-weight: 600; }
.tg-linked { background: rgba(56,189,248,0.15); color: #7dd3fc; }
.tg-none { background: rgba(148,163,184,0.15); color: var(--text-muted); }

/* Support thread (admin support_detail) + message list (user_detail history) */
.support-ticket-thread, .message-list { display: flex; flex-direction: column; gap: var(--space-3); }
.thread-message, .message-item {
  padding: var(--space-3) var(--space-4); border-radius: var(--radius);
  background: var(--bg-input); border: 1px solid var(--border);
}
.thread-staff { border-color: rgba(45,212,191,0.3); }
.thread-meta { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: baseline; margin-bottom: var(--space-2); font-size: 0.75rem; }
.thread-sender, .message-sender { font-weight: 600; color: var(--text); }
.thread-source { color: var(--accent-teal); text-transform: uppercase; letter-spacing: 0.04em; }
.thread-time, .message-time { color: var(--text-muted); margin-left: auto; font-size: 0.75rem; }
.thread-body, .message-body { font-size: 0.92rem; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
.ticket-meta { color: var(--text-muted); font-size: 0.85rem; margin-bottom: var(--space-3); }

/* Consistent vertical spacing for the many action/section wrapper divs */
.status-actions, .upgrade-actions, .admin-license-devices, .support-reply-section,
.balance-overview-section, .apply-balance-section, .mark-paid-section, .mark-refunded-section,
.refund-section, .payout-request-section, .payout-history-section, .ledger-section,
.referral-code-section, .referral-summary-section, .referral-list-section,
.payment-instructions, .home-actions { margin-top: var(--space-8); }
.support-close-section { margin-top: var(--space-4); }
.status-actions h2, .upgrade-actions h2 { margin-bottom: var(--space-4); }
.status-actions form, .upgrade-actions form { display: inline-block; margin: 0 var(--space-2) var(--space-2) 0; }
.home-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* Labeled select/input forms (upgrade, filters) */
/* Summary strip above the payment + webhook registries. The markup (a row of
   .stat boxes) had no rule at all, so the boxes stacked as plain blocks and each
   label ran inline with its own number. column-reverse puts the figure on top
   like .stat-card while leaving the label first in the DOM, where it reads as
   "Failures: 3" rather than "3: Failures". */
.summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-4);
  /* Both pages put this directly under .filter-bar, which carries no margin. */
  margin: var(--space-6) 0;
}
.summary-grid .stat {
  display: flex; flex-direction: column-reverse; gap: var(--space-1);
  padding: var(--space-4) var(--space-6);
  background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius);
}
.summary-grid .stat-value { font-size: 1.6rem; font-weight: 800; letter-spacing: -0.02em; }
.summary-grid .stat-label { font-size: 0.72rem; }

.upgrade-form, .filter-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-4); }
.upgrade-form label, .filter-bar label { display: flex; flex-direction: column; gap: var(--space-1); font-size: 0.85rem; color: var(--text-muted); }
/* The column default above is for "caption over input" fields; a checkbox and
   its text are one line. */
.filter-bar label.filter-check { flex-direction: row; align-items: center; gap: var(--space-2); height: 2.5rem; }

/* Перенос ревизии лендинга: форма стоит под списком версий и должна читаться
   как отдельный инструмент, а не как продолжение таблицы. */
.landing-import { margin-top: var(--space-8); }
.landing-import .admin-form { max-width: 560px; }

/* Редактор документации: каждое значение — строка на три языка.
   Языки рядом, а не тремя отдельными формами: перевод правят, глядя на
   оригинал, и переключение вкладок между ними — это и есть тот шов, по
   которому раньше расходились три отдельных файла. */
/* ─── Документация в панели: дерево слева, страница читателя справа ─── */
.docs-form { max-width: none; }
.code-input { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.8rem; }
.docs-langbar { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-4); }
.docs-langbar-title { font-size: 0.8rem; color: var(--text-muted); }
.docs-langbar-link {
  padding: 0.15rem var(--space-3); border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: 0.8rem; text-transform: uppercase;
}
.docs-langbar-link.active { background: var(--bg-input); color: var(--accent-teal); border-color: var(--accent-teal); }
.docs-admin { display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: var(--space-6); align-items: start; }
.docs-admin-tree { border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-3); }
.docs-admin-view { min-width: 0; }
.docs-admin-bar {
  display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center;
  justify-content: space-between; margin-bottom: var(--space-3);
}
.docs-admin-frame {
  width: 100%; height: 70vh; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--bg-card);
}
.docs-tree, .docs-tree ul { list-style: none; margin: 0; padding: 0; }
.docs-tree ul { margin-left: var(--space-4); }
.docs-tree-row { display: flex; align-items: center; gap: var(--space-2); padding: 0.1rem 0; }
.docs-tree-link { flex: 1 1 auto; font-size: 0.9rem; color: var(--text); }
.docs-tree-item.active > .docs-tree-row > .docs-tree-link { color: var(--accent-teal); font-weight: 600; }
.docs-tree-section > .docs-tree-row > .docs-tree-link { color: var(--text-muted); text-transform: uppercase; font-size: 0.75rem; letter-spacing: 0.05em; }
.docs-tree-actions { display: flex; gap: var(--space-1); position: relative; }
.docs-icon-btn {
  background: none; border: none; color: var(--text-muted); cursor: pointer;
  font-size: 0.9rem; line-height: 1; padding: 0.15rem 0.3rem; border-radius: var(--radius-sm);
}
.docs-icon-btn:hover { color: var(--accent-teal); background: var(--bg-input); }
.docs-tree-add { margin-top: var(--space-3); }
.docs-menu {
  position: absolute; right: 0; top: 100%; z-index: 20; min-width: 190px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--space-1); box-shadow: 0 8px 24px rgba(0,0,0,0.35);
}
.docs-menu-form { margin: 0; }
.docs-menu-item {
  display: block; width: 100%; text-align: left; background: none; border: none;
  color: var(--text); font-size: 0.85rem; padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm); cursor: pointer;
}
.docs-menu-item:hover { background: var(--bg-input); }
.docs-menu-item[disabled] { color: var(--text-muted); cursor: not-allowed; }
.docs-menu-danger { color: var(--danger, #f87171); }
.docs-dialog {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-card); color: var(--text); padding: var(--space-6);
  max-width: 560px; width: calc(100vw - 2rem);
}
.docs-dialog::backdrop { background: rgba(0,0,0,0.55); }

/* ─── Редактор страницы: вкладка на язык ─── */
.docs-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-6); }
.docs-tab-radio { position: absolute; opacity: 0; pointer-events: none; }
.docs-tab-label {
  order: -1; padding: var(--space-2) var(--space-4); border: 1px solid var(--border);
  border-radius: var(--radius-sm); cursor: pointer; text-transform: uppercase;
  font-size: 0.8rem; color: var(--text-muted);
}
.docs-tab-radio:checked + .docs-tab-label { color: var(--accent-teal); border-color: var(--accent-teal); background: var(--bg-input); }
.docs-tab-panel { display: none; flex-basis: 100%; order: 1; }
.docs-tab-radio:checked + .docs-tab-label + .docs-tab-panel { display: block; }
/* Поисковые подписи стоят внутри языковой вкладки статьи, ниже её текста:
   они про выдачу, а не про страницу, и отделены подзаголовком, а не вторым
   набором вкладок — две группы радиокнопок разъезжались бы между собой. */
.blog-seo-title { margin: var(--space-6) 0 var(--space-3); font-size: 0.8rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted);
  padding-top: var(--space-4); border-top: 1px solid var(--border); }
.docs-body-input { min-height: 24rem; }
.docs-snippets { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; margin-bottom: var(--space-4); }
.docs-children { margin: var(--space-6) 0 0 1.2rem; }
@media (max-width: 900px) {
  .docs-admin { grid-template-columns: 1fr; }
  .docs-admin-frame { height: 60vh; }
}

/* ─── Вертикальный ритм страниц админки ──────────────────────────────────────
   Сброс в начале файла снимает margin со ВСЕХ элементов, поэтому блок админ-
   страницы стоит на отступе только если кто-то задал его этому блоку лично.
   Панель фильтров, одиночная кнопка над списком и абзац между секциями своего
   не имели — и вставали к соседу вплотную: ряд кнопок читался как часть
   таблицы, а подпись секции — как продолжение предыдущего абзаца.

   Шаг задан по контейнеру страницы, а не в шаблонах: пар «панель → список» в
   панели несколько десятков, и отступ, положенный в каждый шаблон руками,
   забудется в следующем. Схлопывание вертикальных margin оставляет за блоком с
   собственным отступом его значение, поэтому правило ничего не ломает там, где
   ритм уже выстроен. .admin-form исключена — у её полей шаг свой, .admin-hint
   и .admin-search однострочные. Вторая строка селектора — секционная карточка:
   её обёртку каждая страница называет по-своему (.suspicious-params,
   .suspicious-fork-directive), общего имени нет, зато у всех первым идёт
   .section-title. */
body.adm [class^="admin-"]:not(.admin-form):not(.admin-hint):not(.admin-search) > * + *,
body.adm div:has(> .section-title) > * + * {
  margin-top: var(--space-4);
}
/* Панель действий и список под ней — шаг крупнее рядового, иначе панель
   читается как первая строка списка. */
body.adm .filter-bar { margin-bottom: var(--space-6); }
/* Строка поиска: поле и кнопка в одну линию (растянутое во всю ширину поле
   выглядело как оборванная форма), и такой же отступ до списка под ней. */
body.adm .admin-search {
  display: flex; align-items: center; gap: var(--space-2);
  max-width: 520px; margin-bottom: var(--space-6);
}
body.adm .admin-search .form-input { flex: 1; }
body.adm .btn + .table-responsive,
body.adm .btn + .empty-state,
body.adm .btn + .info-state,
body.adm .btn + .summary-grid,
body.adm .btn + .notice,
body.adm .btn + .notice-warning { margin-top: var(--space-6); }

/* Выбор колонок списка: свёрнутая строка над таблицей, раскрывается в сетку
   флажков. Сетка авто-заполняемая, а не с фиксированным числом столбцов —
   набор колонок у каждого списка свой и будет расти, и вёрстка, посчитанная
   под сегодняшние восемнадцать, развалится на девятнадцатой. */
body.adm .columns-picker { margin-bottom: var(--space-6); }
body.adm .columns-picker-summary {
  cursor: pointer; color: var(--text-link); font-size: 0.9rem;
  list-style: none; display: inline-block;
}
body.adm .columns-picker-summary::-webkit-details-marker { display: none; }
body.adm .columns-picker-form {
  margin-top: var(--space-3); padding: var(--space-4);
  border: 1px solid var(--border); border-radius: var(--radius);
}
body.adm .columns-picker-hint { color: var(--text-muted); font-size: 0.85rem; margin: 0 0 var(--space-3); }
body.adm .columns-picker-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: var(--space-2); margin-bottom: var(--space-4);
}
body.adm .columns-picker-item {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: 0.88rem; cursor: pointer;
}

/* Подпись секции внутри страницы: сверху — воздух и линия, иначе несколько
   секций подряд читаются как один сплошной текст. Первая линию не рисует —
   ей нечего отделять от шапки карточки. */
body.adm .section-title:not(:first-child) {
  margin-top: var(--space-6);
  padding-top: var(--space-6);
  border-top: 1px solid var(--border);
}

/* Hints / notices / advisory text */
.hint, .page-hint, .page-intro, .info-state, .instructions-text, .support-owner, .payout-owner {
  color: var(--text-muted); font-size: 0.9rem; line-height: 1.5;
}
.hint, .page-hint, .page-intro { margin-bottom: var(--space-4); }
.notice, .notice-warning, .flash-warning {
  border-radius: var(--radius); padding: var(--space-3) var(--space-4); font-size: 0.9rem; margin-bottom: var(--space-4);
  background: rgba(56,189,248,0.1); border: 1px solid rgba(56,189,248,0.25); color: var(--text);
}
.notice-warning, .flash-warning { background: rgba(245,158,11,0.1); border-color: rgba(245,158,11,0.3); }

/* Table action links + responsive scroll + link-styled button */
.table-responsive { overflow-x: auto; }
.action-link { color: var(--text-link); font-size: 0.85rem; white-space: nowrap; }
.action-link + .action-link { margin-left: var(--space-3); }
.btn-link, .stat-link { cursor: pointer; }
.btn-link { background: none; border: none; color: var(--text-link); font: inherit; padding: 0; }

/* Monospace code-ish values */
.ref-code, .promo-code, .referral-code-value, .referral-share-link,
.bundle-file-ref, .bundle-version, .dist-version, .cabinet-version, .payout-address {
  font-family: 'Fira Mono', 'Consolas', monospace; font-size: 0.85rem;
}
/* Commit and date sit beside the version as secondary detail. */
.cabinet-build { color: var(--text-muted); margin-left: var(--space-2); }
.payout-address, .referral-share-link { word-break: break-all; }

/* Money + numeric cells */
.money-cell, .money-value, .ledger-amount, .seats-cell { font-variant-numeric: tabular-nums; white-space: nowrap; }
.money-value { font-weight: 600; }
.seats-sep { color: var(--text-muted); margin: 0 0.15rem; }
.ledger-kind { color: var(--text); }
.ledger-ref { color: var(--text-muted); font-size: 0.85rem; }
.referral-reward-rate { color: var(--accent-teal); font-weight: 700; }
.referral-user-label { color: var(--text); }

/* Audit table cells */
.audit-action { font-weight: 600; color: var(--text); }
.audit-actor, .audit-target { color: var(--text-muted); }
.audit-at { color: var(--text-muted); white-space: nowrap; font-size: 0.85rem; }
.audit-meta { font-family: 'Fira Mono', 'Consolas', monospace; font-size: 0.78rem; color: var(--text-muted); word-break: break-all; }

/* Filter tabs (support, bug-reports) */
.filter-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-4); }
.filter-tab {
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm);
  color: var(--text-muted); font-size: 0.9rem; border: 1px solid var(--border);
}
.filter-tab:hover { color: var(--text); text-decoration: none; }
.filter-tab-active { color: var(--accent-teal); border-color: var(--accent-teal); background: rgba(45,212,191,0.1); }

/* Checkbox row (payments toggles) */
/* Checkbox + its label.
 *
 * align-items is flex-START, not center, and that is the whole point: a label
 * whose text wraps to two lines would otherwise float the box to the vertical
 * middle of the block, detached from the sentence it belongs to. The box is
 * nudged down instead, by exactly half the leading, so it sits centred on the
 * FIRST line no matter how many follow.
 *
 * Sizing the box in `em` rather than leaving the UA default keeps that offset
 * exact — the arithmetic below assumes a 1em box on a 1.5em line — and makes
 * the control scale with the text instead of staying a fixed 13px speck.
 */
.checkbox-label {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: 0.95rem;
  line-height: 1.5;
}
.checkbox-label > input[type="checkbox"] {
  flex: 0 0 auto;          /* never let a long sentence squeeze the box */
  width: 1em;
  height: 1em;
  margin: calc((1.5em - 1em) / 2) 0 0;
}
/* A sentence — possibly with inline links — as ONE flex item.
 *
 * 🔴 Without a wrapper the flex container turns every run of text between the
 * links into its own anonymous item, and flex items do not wrap: they shrink.
 * "Я принимаю" then breaks in half inside itself, the gap is painted at every
 * seam, and the row reads as four fragments instead of one sentence. Inside
 * this span the links are ordinary inline content and the text wraps on word
 * boundaries like text.
 */
.checkbox-label > .checkbox-text { min-width: 0; }

/* Generic detail card + order detail bits */
.detail-card {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--space-6); max-width: 560px;
}

/* Error page */
.error-page { text-align: center; padding: var(--space-12) var(--space-4); }
.error-404 { font-size: 3rem; font-weight: 800; color: var(--accent-teal); line-height: 1; }
.error-message { color: var(--text-muted); margin-top: var(--space-3); }

@media (max-width: 560px) {
  .day-bar-row { grid-template-columns: 80px 1fr auto; }
  .thread-time, .message-time { margin-left: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   DOCUMENTATION (/docs) — sidebar + content layout
   ═══════════════════════════════════════════════════════════════════════════ */
.docs-layout {
  max-width: 1320px;
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: var(--space-8);
  align-items: start;
  padding-top: var(--space-8);
  padding-bottom: var(--space-12);
}

/* ─── Sidebar ─── */
.docs-sidebar {
  position: sticky;
  top: var(--space-4);
  align-self: start;
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
}
.docs-sidebar-toggle {
  display: none;
  width: 100%;
  padding: var(--space-3) var(--space-4);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-size: 0.9375rem;
  font-weight: 600;
  cursor: pointer;
}
.docs-nav-group { margin-bottom: var(--space-6); }
.docs-nav-group-title {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: var(--space-2);
  padding-left: var(--space-3);
}
.docs-nav-list { list-style: none; margin: 0; padding: 0; }
.docs-nav-list .docs-nav-list { margin-left: var(--space-3); }
.docs-nav-item { margin-bottom: 0.1rem; }
/* Узел без собственного текста — заголовок группы: он не ссылка, и отбивка
   сверху отделяет его ветку от предыдущей. */
.docs-nav-section { margin-top: var(--space-4); }
.docs-nav-section:first-child { margin-top: 0; }
.docs-nav-link {
  display: block;
  padding: 0.35rem var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  border-left: 2px solid transparent;
  font-size: 0.9rem;
}
.docs-nav-link:hover { color: var(--text); background: rgba(255,255,255,0.05); text-decoration: none; }
.docs-nav-link.active {
  color: var(--accent-teal);
  background: rgba(45,212,191,0.1);
  border-left-color: var(--accent-teal);
  font-weight: 600;
}

/* ─── Content column ─── */
.docs-content { min-width: 0; }
.docs-breadcrumb { font-size: 0.85rem; color: var(--text-muted); margin-bottom: var(--space-4); }
.docs-breadcrumb a { color: var(--text-muted); }
.docs-breadcrumb a:hover { color: var(--accent-teal); }
.docs-breadcrumb-sep { margin: 0 var(--space-2); opacity: 0.5; }
.docs-title { font-size: 2rem; font-weight: 800; line-height: 1.2; margin-bottom: var(--space-3); }
.docs-lede { font-size: 1.05rem; color: var(--text-muted); margin-bottom: var(--space-6); max-width: 46rem; }

/* On-page table of contents */
.docs-toc {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-6);
  margin-bottom: var(--space-8);
}
.docs-toc-title { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); margin-bottom: var(--space-2); }
.docs-toc ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); }
.docs-toc a { color: var(--text-link); font-size: 0.9rem; }

/* Sections + typography */
/* ─── Тело страницы: разметка приходит из базы ───

   Стили висят и на классах (docs-p, docs-callout, …), и на самих элементах
   внутри .docs-body. Классы — то, что печатал прежний шаблон и что вставляют
   заготовки редактора; селекторы по элементу — страховка на РУКОПИСНЫЙ HTML:
   абзац, набранный как <p> без класса, иначе выглядел бы голым, и заметил бы
   это читатель, а не автор.
   Якорь теперь на самом заголовке (<h2 id>), поэтому scroll-margin-top
   переехал на .docs-h2 с обёртки-секции, которой больше нет. */
.docs-body { min-width: 0; }
.docs-body > p, .docs-body > h2 + p { margin-bottom: var(--space-4); max-width: 46rem; }
.docs-body > ul:not([class]), .docs-body > ol:not([class]) { margin: 0 0 var(--space-4) 1.4rem; max-width: 46rem; }
.docs-body > ul:not([class]) li, .docs-body > ol:not([class]) li { margin-bottom: var(--space-2); }
.docs-body img { max-width: 100%; height: auto; }
.docs-body > pre { overflow-x: auto; }
.docs-block-section { margin-bottom: var(--space-8); }

.docs-h2 { font-size: 1.4rem; font-weight: 700; scroll-margin-top: var(--space-6); margin: var(--space-8) 0 var(--space-4); padding-bottom: var(--space-2); border-bottom: 1px solid var(--border); }
.docs-h2 .docs-anchor { color: var(--text); }
.docs-h2 .docs-anchor:hover { color: var(--accent-teal); text-decoration: none; }
.docs-h3 { font-size: 1.1rem; font-weight: 700; margin: var(--space-6) 0 var(--space-3); }
.docs-p { margin-bottom: var(--space-4); max-width: 46rem; }
.docs-ul, .docs-ol, .docs-steps { margin: 0 0 var(--space-4) 1.4rem; max-width: 46rem; }
.docs-ul li, .docs-ol li { margin-bottom: var(--space-2); }
.docs-steps { counter-reset: step; list-style: none; margin-left: 0; }
.docs-steps li { position: relative; padding-left: 2.4rem; margin-bottom: var(--space-4); }
.docs-steps li::before {
  counter-increment: step; content: counter(step);
  position: absolute; left: 0; top: 0;
  width: 1.7rem; height: 1.7rem;
  display: grid; place-items: center;
  background: var(--accent-grad); color: #0f1117;
  border-radius: 50%; font-size: 0.8rem; font-weight: 700;
}

/* Code blocks */
.docs-code {
  background: #0b0d13;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  overflow-x: auto;
  margin-bottom: var(--space-4);
  font-size: 0.85rem;
  line-height: 1.55;
}
.docs-code code { font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace; color: #d6deeb; white-space: pre; }
.docs-p code, .docs-ul code, .docs-ol code, .docs-table code {
  background: var(--bg-input); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 0.05rem 0.35rem;
  font-family: ui-monospace, 'SF Mono', 'Cascadia Code', Consolas, monospace;
  font-size: 0.85em; color: var(--accent-teal);
}

/* Callouts */
/* Список внешних адресов документации: реестр образов, пакет, репозиторий.
   Отдельным блоком, а не строкой в абзаце, — адрес в тексте экранируется и
   остаётся некликабельным. */
.docs-links { list-style: none; margin: 0 0 var(--space-4); padding: 0; max-width: 46rem; }
.docs-links li { margin-bottom: var(--space-2); }
.docs-link { font-weight: 600; }
.docs-link-desc { display: block; font-size: 0.85rem; color: var(--text-muted); }

.docs-callout {
  display: flex; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  border-left-width: 3px;
  background: var(--bg-card);
  margin-bottom: var(--space-4);
  max-width: 46rem;
}
.docs-callout-label { font-weight: 700; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap; padding-top: 0.1rem; }
.docs-note { border-left-color: var(--info); }
.docs-note .docs-callout-label { color: var(--info); }
.docs-tip { border-left-color: var(--success); }
.docs-tip .docs-callout-label { color: var(--success); }
.docs-warn { border-left-color: var(--warning); }
.docs-warn .docs-callout-label { color: var(--warning); }

/* Figures / screenshots */
.docs-figure { margin: 0 0 var(--space-6); }
.docs-figure img {
  max-width: 100%; height: auto; display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 32px rgba(0,0,0,0.35);
}
.docs-figure figcaption { color: var(--text-muted); font-size: 0.85rem; margin-top: var(--space-2); text-align: center; }

/* Tables */
.docs-table-wrap { overflow-x: auto; margin-bottom: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius); }
.docs-table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
.docs-table th, .docs-table td { text-align: left; padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--border); vertical-align: top; }
.docs-table th { background: var(--bg-input); color: var(--text-muted); font-weight: 600; white-space: nowrap; }
.docs-table tr:last-child td { border-bottom: none; }

/* API endpoint blocks */
.docs-endpoint {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-6);
  margin-bottom: var(--space-6);
}
.docs-endpoint-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-3); }
.docs-method { font-weight: 700; font-size: 0.7rem; letter-spacing: 0.05em; padding: 0.2rem 0.5rem; border-radius: var(--radius-sm); color: #0f1117; }
.docs-method-GET { background: var(--info); }
.docs-method-POST { background: var(--success); }
.docs-method-PUT { background: var(--warning); }
.docs-method-DELETE { background: var(--error); }
.docs-endpoint-path { font-family: ui-monospace, 'SF Mono', Consolas, monospace; font-size: 0.9rem; color: var(--text); word-break: break-all; }
.docs-auth { font-size: 0.7rem; padding: 0.15rem 0.5rem; border-radius: 999px; border: 1px solid var(--border); }
.docs-auth-req { color: var(--warning); border-color: var(--warning); }
.docs-auth-none { color: var(--text-muted); }
.docs-endpoint-summary { color: var(--text-muted); margin-bottom: var(--space-3); }
.docs-example-label { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); margin: var(--space-3) 0 var(--space-2); }
.docs-endpoint-notes { margin: var(--space-3) 0 0 1.2rem; color: var(--text-muted); font-size: 0.875rem; }
.docs-params td code { color: var(--accent-teal); }

/* Prev / next pager */
.docs-pager { display: flex; justify-content: space-between; gap: var(--space-4); margin-top: var(--space-12); border-top: 1px solid var(--border); padding-top: var(--space-6); }
.docs-pager-link { display: flex; flex-direction: column; gap: 0.2rem; padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-card); max-width: 48%; }
.docs-pager-link:hover { border-color: var(--accent-teal); text-decoration: none; }
.docs-pager-next { text-align: right; margin-left: auto; }
.docs-pager-dir { font-size: 0.75rem; color: var(--text-muted); }
.docs-pager-title { font-weight: 600; color: var(--text); }

/* Responsive: collapse the sidebar into a toggle on narrow screens */
@media (max-width: 900px) {
  .docs-layout { grid-template-columns: 1fr; gap: var(--space-4); }
  .docs-sidebar { position: static; max-height: none; margin-bottom: var(--space-4); }
  .docs-sidebar-toggle { display: block; }
  .docs-nav { display: none; margin-top: var(--space-3); }
  .docs-sidebar.open .docs-nav { display: block; }
  .docs-title { font-size: 1.6rem; }
}

/* ─── Admin sidebar shell (/admin) ────────────────────────────────────────────
   The panel's 22 sections used to be a top nav that wrapped onto three rows. As
   a sidebar they fit in labelled groups, and the content column stops being
   capped at .container's 1100px — which the wide registry tables needed.

   Built on the global tokens (--bg, --border, --text-*), NOT on the cabinet's
   .cab shell: that one declares its palette on body.cab and restyles
   .card/.data-table/.page-title for the client mockup, which would silently
   redesign every admin page. Only the chrome is new here; page markup is
   untouched. */
body.adm {
  /* body is display:flex column for the public site's sticky footer; the shell
     below is its own grid and would otherwise be stretched by it. */
  display: block;
}

.adm-shell { display: grid; grid-template-columns: 250px 1fr; min-height: 100vh; }

/* Sidebar: brand and account box are pinned, only the nav scrolls — with this
   many sections the list is taller than a laptop viewport, and signing out must
   never require scrolling to reach. */
.adm-side {
  position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column;
  gap: var(--space-2); padding: var(--space-4) var(--space-3);
  background: linear-gradient(180deg, #080b14, #070912);
  border-right: 1px solid var(--border-soft);
}
.adm-side-scroll { flex: 1; min-height: 0; overflow-y: auto; scrollbar-width: thin; }

.adm-brand { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-2) var(--space-3); }
.adm-brand:hover { text-decoration: none; }
.adm-logo {
  /* Логотип берётся вариантом под тёмную шапку; подложка и тень не нужны. */
  width: 48px; height: 48px; flex: none;
}
.adm-brand b { font-family: var(--font-display); font-size: 1rem; font-weight: 700; color: #fff; }
.adm-brand-tag {
  font-size: 0.6rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent-teal); border: 1px solid rgba(91,139,255,0.35); border-radius: 999px;
  padding: 0.1rem 0.4rem;
}

.adm-nav { display: flex; flex-direction: column; }
.adm-nav-cap {
  font-size: 0.63rem; letter-spacing: 0.16em; text-transform: uppercase; line-height: 1.2;
  color: var(--text-faint); font-weight: 800; padding: var(--space-3) var(--space-2) var(--space-1);
}
.adm-nav-cap:first-child { padding-top: var(--space-1); }
/* line-height is tightened from the body's 1.6: 22 links plus their captions
   have to fit a laptop viewport before the list starts scrolling. */
.adm-nav-link {
  display: flex; align-items: center; gap: 10px; padding: 0.35rem var(--space-2);
  border-radius: var(--radius-sm); position: relative;
  font-size: 0.82rem; font-weight: 600; line-height: 1.35; color: var(--text-muted);
  transition: color 0.15s, background 0.15s;
}
.adm-nav-link:hover { background: rgba(90,110,220,0.10); color: var(--text); text-decoration: none; }
/* Awaiting-reply count on the Support link. Same treatment as the client nav's
   unread badge (.cab-nav-badge) — the two mean the same thing on opposite
   sides, and reading as one thing is the point. */
.adm-nav-badge {
  margin-left: auto; min-width: 20px; height: 18px; padding: 0 6px;
  border-radius: 20px; display: grid; place-items: center;
  font-size: 10.5px; font-weight: 800; color: #fff;
  background: linear-gradient(180deg, #ff5f7a, #e0344f);
  box-shadow: 0 0 10px rgba(224,52,79,.5);
}
.adm-nav-badge[hidden] { display: none; }
.adm-nav-link svg { width: 17px; height: 17px; flex: none; color: var(--text-faint); transition: color 0.15s; }
.adm-nav-link:hover svg { color: var(--text-muted); }
.adm-nav-link.active {
  color: #fff; background: linear-gradient(90deg, rgba(74,100,255,0.22), rgba(74,100,255,0.04));
  box-shadow: inset 0 0 0 1px rgba(91,120,255,0.28);
}
.adm-nav-link.active svg { color: var(--accent-teal); }

/* Account box + sign-out, pinned to the bottom of the sidebar. */
.adm-userbox {
  display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-card);
}
.adm-avatar {
  width: 32px; height: 32px; border-radius: var(--radius-sm); flex: none; display: grid; place-items: center;
  font-family: var(--font-display); font-weight: 700; font-size: 0.85rem; text-transform: uppercase;
  color: #dfe6ff; background: linear-gradient(155deg, #2a3566, #1a2247);
  border: 1px solid rgba(120,140,255,0.3);
}
.adm-u-meta { min-width: 0; }
.adm-u-mail { font-size: 0.75rem; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.adm-u-sub { font-size: 0.65rem; font-weight: 700; color: var(--accent-teal); }
.adm-u-out-form { margin-left: auto; display: flex; }
.adm-u-out {
  width: 30px; height: 30px; border-radius: var(--radius-sm); display: grid; place-items: center;
  color: var(--text-muted); background: transparent; border: 1px solid transparent; cursor: pointer;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.adm-u-out:hover { color: #fff; background: #121736; border-color: var(--border); }

/* Language switcher: a segmented row of the supported locales, sized to split
   the sidebar evenly however many there are. Quieter than .adm-nav-link — it is
   chrome, not a section — but the current locale is filled in so the answer to
   "which language am I in" needs no click. */
.adm-lang { display: flex; gap: 4px; }
.adm-lang-opt {
  flex: 1 1 0; min-width: 0; text-align: center; padding: 0.25rem 0.3rem;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  font-size: 0.7rem; font-weight: 700; line-height: 1.4; color: var(--text-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.adm-lang-opt:hover { color: var(--text); background: rgba(90,110,220,0.10); text-decoration: none; }
.adm-lang-opt.active { color: #fff; background: #121736; border-color: var(--border); }

/* Sidebar fine-print — deliberately quieter than .adm-nav-link. */
.adm-side-links { display: flex; justify-content: center; gap: var(--space-3); }
.adm-side-links a { font-size: 0.7rem; font-weight: 600; color: var(--text-faint); }
.adm-side-links a:hover { color: var(--text-muted); text-decoration: none; }

.adm-main { min-width: 0; }
.adm-content { padding: var(--space-6) var(--space-8) var(--space-12); max-width: 1560px; }

/* The cap above is a reading guard: a form or a paragraph stretched across an
   ultrawide monitor is unreadable. A register is not read across, it is scanned
   column by column, and the widest ones need more than 1560px — the users
   registry alone asks for ~2170. Capping those buys a horizontal scrollbar with
   several hundred pixels of screen still unused to the right of the panel, so a
   page that carries a register takes the whole main column instead. Browsers
   without :has() simply keep the cap, which is today's behaviour. */
.adm-content:has(.table-responsive) { max-width: none; }

/* Responsive: below 1080px the sidebar becomes a top strip — the same trade the
   cabinet makes. Captions and the account box drop out; sign-out stays. */
@media (max-width: 1080px) {
  .adm-shell { grid-template-columns: 1fr; }
  .adm-side {
    position: static; height: auto; flex-direction: row; flex-wrap: wrap; align-items: center;
    gap: var(--space-1) var(--space-2); padding: var(--space-3) var(--space-4);
    border-right: 0; border-bottom: 1px solid var(--border-soft);
  }
  .adm-brand { padding: 0 var(--space-2) 0 0; }
  .adm-side-scroll { overflow: visible; width: 100%; display: flex; flex-wrap: wrap; gap: var(--space-1); }
  .adm-nav { flex-direction: row; flex-wrap: wrap; }
  .adm-nav-cap, .adm-u-meta, .adm-avatar, .adm-side-links { display: none; }
  .adm-userbox { margin-left: auto; border: 0; background: transparent; padding: 0; }
  .adm-content { padding: var(--space-4) var(--space-4) var(--space-8); }
}

/* ─── Admin registry tables ───────────────────────────────────────────────────
   The panel's registries run nine to twelve columns wide. Given no width
   guidance the browser buys those columns by folding whichever cell has the
   shortest words: a "2 Jan 2026 15:04" stamp lands on three lines, a header
   such as "Триал использован" on two, and an actions cell stacks its buttons
   vertically. Pinning cells to one line and letting the table scroll inside its
   .table-responsive wrapper trades that folding for a horizontal scrollbar —
   the trade an operator scanning a register wants.

   Columns whose content has no natural width opt back into wrapping with
   .cell-text (prose: descriptions, error strings, joined address lists) or
   .cell-code (opaque tokens: UUIDs, payout addresses, JSON blobs). Because the
   table's containing block is the wrapper, those columns absorb the leftover
   width and wrap inside it, and the table only scrolls once the one-line
   columns alone outgrow the viewport. */
body.adm .data-table th,
body.adm .data-table td { white-space: nowrap; }

/* macOS draws overlay scrollbars, which stay invisible until something moves —
   on an overflowing register that hides the fact that there are more columns
   (and, on the action-heavy ones, the buttons themselves). A styled scrollbar
   is drawn permanently, and only on the wrappers that actually scroll. */
body.adm .table-responsive { scrollbar-width: thin; }
body.adm .table-responsive::-webkit-scrollbar { height: 10px; }
body.adm .table-responsive::-webkit-scrollbar-track { background: var(--bg-input); border-radius: 999px; }
body.adm .table-responsive::-webkit-scrollbar-thumb { background: #2a3566; border-radius: 999px; }
body.adm .table-responsive::-webkit-scrollbar-thumb:hover { background: #3a4780; }

body.adm .data-table .cell-text { white-space: normal; min-width: 16rem; }
body.adm .data-table .cell-code { white-space: normal; word-break: break-all; min-width: 11rem; }

/* Per-row controls. Each control is its own CSRF-protected <form>, and how many
   a row shows depends on its status, so they carry their own spacing instead of
   relying on the whitespace between tags. Only the children are laid out — the
   cell itself stays a table-cell, or it would drop out of the column grid. */
.row-actions { white-space: nowrap; }
.row-actions form { display: inline-flex; align-items: center; gap: var(--space-2); margin: 0; }
.row-actions > * + * { margin-left: var(--space-2); }
/* Inputs default to display:block/width:100% for stacked forms; inside a row
   they sit beside their button. */
.row-actions .form-input,
.row-actions .input-sm { display: inline-block; width: auto; }

/* Caption + control pair inside a row action form (developer rate, app name). */
.form-label-inline {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: 0.8rem; color: var(--text-muted); white-space: nowrap;
}

/* ─── Cabinet sidebar shell (/app) — ports design/site-mockup/cabinet.html ─────
   Everything is scoped under body.cab (set by layout.tmpl's /app branch) so the
   admin panel + landing keep their own styling. Colors are the mockup's own
   scale, held in .cab-scoped custom properties so they never leak. */
body.cab {
  --cbg: #04050a; --cpanel: #0a0d17; --ccard: #0b0f1c;
  --cline: #1b2237; --cline-soft: #161c2e; --cline-bright: rgba(110,130,255,.35);
  --ctxt: #e7ebf5; --ctxt-dim: #c3cadf; --ctxt-mute: #8b96b5; --ctxt-faint: #5f6a88;
  --cblue: #3b5bff; --cblue-hi: #5b8bff; --cgreen: #4ade80; --cviolet: #8b5cf6; --camber: #fbbf24;
  background: var(--cbg); color: var(--ctxt);
  font-variant-numeric: tabular-nums;
}
/* The HTML `hidden` attribute must win over the component display rules in this
   section — otherwise a JS-toggled panel (e.g. the support channel chooser, which
   gets display:grid below) stays visible after being hidden. */
.cab [hidden] { display: none !important; }

/* То же самое для админки. Здесь защита была точечной (.adm-nav-badge,
   .rf-toolbar), то есть каждый новый скрываемый узел с собственным display
   пришлось бы вспоминать отдельно — а забытый просто не скрывался бы, молча.
   Правило снимает этот класс ошибок разом. */
body.adm [hidden] { display: none !important; }
.cab .cab-aurora { position: fixed; inset: 0; pointer-events: none; z-index: 0; overflow: hidden; }
.cab .cab-aurora span { position: absolute; border-radius: 50%; filter: blur(30px); }
.cab .cab-aurora .a1 { top: -260px; left: 200px; width: 820px; height: 520px; background: radial-gradient(closest-side, rgba(59,91,255,.14), transparent 70%); }
.cab .cab-aurora .a2 { top: -160px; right: -140px; width: 720px; height: 480px; background: radial-gradient(closest-side, rgba(139,92,246,.12), transparent 70%); }

.cab-shell { position: relative; z-index: 2; display: grid; grid-template-columns: 264px 1fr; min-height: 100vh; }

/* Sidebar */
.cab-side { position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column;
  background: linear-gradient(180deg, #080b14, #070912); border-right: 1px solid var(--cline-soft); padding: 20px 16px; }
.cab-brand { display: flex; align-items: center; gap: 12px; padding: 6px 8px 20px; }
.cab-brand:hover { text-decoration: none; }
/* Логотип берётся вариантом под тёмную шапку; подложка и тень не нужны. */
.cab-logo { width: 56px; height: 56px; flex: none; }
.cab-brand b { font-family: 'Sora', sans-serif; font-size: 17.5px; font-weight: 700; letter-spacing: -.01em; color: #fff; }
.cab-brand b i { font-style: normal; color: var(--cblue-hi); }

.cab-nav { display: flex; flex-direction: column; }
.cab-nav-cap { font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; color: #5b658a; font-weight: 800; padding: 14px 12px 8px; }
.cab-nav-link { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: 10px;
  font-size: 14px; font-weight: 600; color: #93a0c2; transition: .15s; position: relative; margin-bottom: 2px; }
.cab-nav-link:hover { background: rgba(90,110,220,.08); color: #e7ecfa; text-decoration: none; }
.cab-nav-link svg { width: 18px; height: 18px; color: #7c88ad; transition: .15s; flex: none; }
.cab-nav-link:hover svg { color: #aab6d8; }
.cab-nav-link.active { color: #fff; background: linear-gradient(90deg, rgba(74,100,255,.22), rgba(74,100,255,.04)); box-shadow: inset 0 0 0 1px rgba(91,120,255,.3); }
.cab-nav-link.active svg { color: var(--cblue-hi); }
.cab-nav-link.active::before { content: ""; position: absolute; left: -16px; top: 8px; bottom: 8px; width: 3px; border-radius: 0 3px 3px 0;
  background: linear-gradient(180deg, #5b8bff, #7c4bff); box-shadow: 0 0 10px rgba(91,120,255,.8); }
.cab-nav-badge { margin-left: auto; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 20px; display: grid; place-items: center;
  font-size: 11px; font-weight: 800; color: #fff; background: linear-gradient(180deg, #ff5f7a, #e0344f); box-shadow: 0 0 10px rgba(224,52,79,.5); }
.cab-nav-badge[hidden] { display: none; }
/* Стрелка у единственной ссылки меню, которая уводит со страницы: она
   открывает документацию в новой вкладке, и знать это лучше до нажатия. */
.cab-nav-out { margin-left: auto; font-size: 12px; opacity: .55; }
.cab-nav-link:hover .cab-nav-out { opacity: .9; }

.cab-spacer { flex: 1; }
.cab-userbox { display: flex; align-items: center; gap: 11px; padding: 11px; border-radius: 12px; background: #0a0e1a; border: 1px solid var(--cline); margin-top: 8px; }
.cab-userbox-guest { justify-content: center; gap: 9px; color: var(--ctxt-dim); font-weight: 700; font-size: 13.5px; }
.cab-userbox-guest:hover { color: #fff; border-color: var(--cline-bright); text-decoration: none; }
.cab-avatar { width: 38px; height: 38px; border-radius: 10px; flex: none; display: grid; place-items: center; text-transform: uppercase;
  font-family: 'Sora', sans-serif; font-weight: 700; font-size: 15px; color: #dfe6ff;
  background: linear-gradient(155deg, #2a3566, #1a2247); border: 1px solid rgba(120,140,255,.3); }
.cab-u-meta { min-width: 0; }
.cab-u-mail { font-size: 12.5px; font-weight: 700; color: #dbe2f5; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cab-u-sub { font-size: 11px; color: #7ee0a6; font-weight: 700; }
.cab-u-out-form { margin-left: auto; display: flex; }
.cab-u-out { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; color: #8b96b5; border: 1px solid transparent; background: transparent; cursor: pointer; transition: .15s; }
.cab-u-out:hover { color: #fff; border-color: #2a3352; background: #121736; }

/* Language switcher — the cabinet twin of .adm-lang, restated against the .cab
   palette rather than shared: those classes read --text-faint/--border, which
   body.cab does not define, so a shared rule would render invisible here.
   Sized like .cab-legal below it: the sidebar's content box is 232px and three
   locales at flex 1 leave each ~74px, ample for "English" at 11px. */
.cab-lang { display: flex; gap: 4px; margin-top: 10px; }
.cab-lang-opt {
  flex: 1 1 0; min-width: 0; text-align: center; padding: 4px 5px; border-radius: 8px;
  border: 1px solid transparent; font-size: 11px; font-weight: 700; line-height: 1.4;
  color: var(--ctxt-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: .15s;
}
.cab-lang-opt:hover { color: #e7ecfa; background: rgba(90,110,220,.08); text-decoration: none; }
.cab-lang-opt.active { color: #fff; background: #121736; border-color: #2a3352; }

/* Sidebar fine-print: legal reference links, deliberately quieter than .cab-nav-link. */
/* Wraps: measured at 231px inside a 232px sidebar box — zero slack, so a longer
   translation or a different font fallback would have overflowed it. */
.cab-legal { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 14px; margin-top: 10px; }
.cab-legal a { font-size: 11px; font-weight: 600; color: var(--ctxt-faint); }
.cab-legal a:hover { color: var(--ctxt-mute); text-decoration: none; }

/* Main column + topbar */
.cab-main { min-width: 0; display: flex; flex-direction: column; }
/* flex-wrap + a breakable title: the home greeting is "<hello> <email>", and an
   email is one unbreakable token, so without both the topbar pushed the whole
   cabinet into a horizontal scroll on narrow viewports. */
.cab-topbar { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; flex-wrap: wrap; gap: 16px; padding: 16px 32px;
  background: linear-gradient(180deg, rgba(5,6,12,.9), rgba(5,6,12,.62)); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid rgba(27,34,55,.7); }
.cab-pt-title { font-family: 'Sora', sans-serif; font-size: 20px; font-weight: 700; color: #fff;
  overflow-wrap: anywhere; }
.cab-pt-sub { font-size: 13px; color: var(--ctxt-mute); margin-top: 1px; }
.cab-tb-right { margin-left: auto; display: flex; align-items: center; gap: 11px; }
.cab-balance-chip { display: flex; align-items: center; gap: 10px; height: 42px; padding: 0 15px; border-radius: 11px;
  background: linear-gradient(180deg, #0c1120, #0a0e1a); border: 1px solid var(--cline); }
.cab-balance-chip:hover { border-color: var(--cline-bright); text-decoration: none; }
.cab-bc-ic { width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center; color: #5ee79b; flex: none;
  border: 1px solid rgba(74,222,128,.4); background: rgba(46,200,120,.08); }
.cab-bc-txt { display: flex; flex-direction: column; }
.cab-bc-l { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: #7986a8; font-weight: 800; }
.cab-bc-v { font-family: 'Sora', sans-serif; font-size: 15px; font-weight: 700; color: #eef2fd; line-height: 1.1; }

/* Cabinet buttons (scoped override of the global .btn) — mockup pill style */
.cab .btn { display: inline-flex; align-items: center; justify-content: center; gap: 9px; font-family: inherit;
  cursor: pointer; border: 0; transition: .18s; font-weight: 700; white-space: nowrap; text-decoration: none; }
.cab .btn:hover { text-decoration: none; }
.cab .btn-primary { height: 42px; padding: 0 20px; border-radius: 11px; font-size: 13.5px; color: #fff;
  background: linear-gradient(180deg, #4f8bff, #2f5cf0); box-shadow: 0 4px 16px rgba(38,66,180,.45), inset 0 1px 0 rgba(255,255,255,.22); }
.cab .btn-primary:hover { filter: brightness(1.1); }
.cab .btn-ghost { height: 42px; padding: 0 18px; border-radius: 11px; background: #0c101d; border: 1px solid var(--cline); color: var(--ctxt-dim); font-size: 13.5px; }
.cab .btn-ghost:hover { border-color: var(--cline-bright); color: #fff; }
.cab .btn-sm { height: 36px; padding: 0 15px; font-size: 12.5px; border-radius: 9px; }

.cab-content { padding: 26px 32px 46px; max-width: 1320px; width: 100%; }

/* Stat cards */
.cab-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 22px; }
.cab-stat { display: flex; align-items: center; gap: 15px; padding: 18px 20px; border-radius: 14px;
  background: linear-gradient(180deg, #0c101f, #0a0e1a); border: 1px solid var(--cline); }
.cab-stat-ic { width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center; flex: none; background: rgba(120,130,255,.05); }
.cab-stat-ic.green { border: 1.5px solid rgba(74,222,128,.5); color: #5ee79b; box-shadow: 0 0 14px rgba(52,211,120,.15); }
.cab-stat-ic.blue { border: 1.5px solid rgba(96,150,250,.5); color: #7fb0ff; box-shadow: 0 0 14px rgba(80,130,255,.15); }
.cab-stat-ic.violet { border: 1.5px solid rgba(150,130,255,.5); color: #b7a5ff; box-shadow: 0 0 14px rgba(139,102,255,.15); }
.cab-stat-ic.amber { border: 1.5px solid rgba(251,191,36,.5); color: #fcd34d; box-shadow: 0 0 14px rgba(251,191,36,.15); }
.cab-stat-l { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: #8b96b5; font-weight: 800; margin-bottom: 6px; }
.cab-stat-v { font-family: 'Sora', sans-serif; font-size: 24px; font-weight: 700; line-height: 1; color: #fff; }
.cab-stat-v small { font-size: 13px; color: var(--ctxt-mute); font-weight: 600; }

/* Content grid + panels */
.cab-grid2 { display: grid; grid-template-columns: 1.62fr 1fr; gap: 20px; margin-bottom: 22px; }
.cab-col { display: flex; flex-direction: column; gap: 20px; }
.cab-panel { border-radius: 16px; border: 1px solid var(--cline); background: linear-gradient(180deg, #0b0f1c, #090c16); overflow: hidden; }
.cab-panel-h { display: flex; align-items: center; gap: 12px; padding: 17px 22px; border-bottom: 1px solid var(--cline-soft); }
.cab-panel-h h3 { font-size: 15.5px; font-weight: 600; color: #fff; }
.cab-panel-h .cab-link { margin-left: auto; font-size: 12.5px; font-weight: 700; color: var(--cblue-hi); }
.cab-panel-h .cab-link:hover { color: #9fbcff; }

/* License card */
.cab-license { position: relative; border-radius: 16px; border: 1px solid rgba(91,120,255,.28); overflow: hidden; margin-bottom: 22px;
  background: linear-gradient(100deg, #0a0e1c 0%, #0b0f20 52%, #0e0c22 100%); }
.cab-license .cab-art { position: absolute; right: 0; top: 0; bottom: 0; width: 470px; pointer-events: none; overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 42%); mask-image: linear-gradient(90deg, transparent 0, #000 42%); }
.cab-license .cab-art img { position: absolute; right: -24px; top: 50%; transform: translateY(-50%); height: 132%; width: auto; max-width: none; }
.cab-license .cab-glow { position: absolute; right: -40px; top: -90px; width: 520px; height: 320px; pointer-events: none;
  background: radial-gradient(closest-side, rgba(124,90,255,.22), transparent 72%); }
.cab-lic-in { position: relative; z-index: 2; padding: 26px 30px; display: flex; flex-direction: column; gap: 18px; }
.cab-lic-top { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.cab-badge-pro { display: inline-flex; align-items: center; gap: 7px; height: 28px; padding: 0 13px; border-radius: 8px; font-size: 12px; font-weight: 800; letter-spacing: .03em;
  color: #0a0d17; background: linear-gradient(90deg, #7fd0ff, #5b8bff); box-shadow: 0 0 18px rgba(91,139,255,.45); }
.cab-badge-live { display: inline-flex; align-items: center; gap: 8px; height: 28px; padding: 0 12px; border-radius: 8px; font-size: 12.5px; font-weight: 800;
  color: #4ee79b; background: rgba(46,200,120,.1); border: 1px solid rgba(46,200,120,.32); }
.cab-badge-live .pip { width: 7px; height: 7px; border-radius: 50%; background: #3ee585; box-shadow: 0 0 9px #3ee585; }
.cab-lic-title { font-family: 'Sora', sans-serif; font-size: 26px; font-weight: 700; letter-spacing: -.01em; color: #fff; }
.cab-lic-meta { display: flex; gap: 34px; flex-wrap: wrap; }
.cab-lm { display: flex; flex-direction: column; gap: 5px; }
.cab-lm .k { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: #7986a8; font-weight: 800; }
.cab-lm .v { font-size: 15px; font-weight: 700; color: #e7ecfa; }
.cab-lm .v.g { color: #5ee79b; }
.cab-lic-bar { display: flex; flex-direction: column; gap: 8px; max-width: 520px; }
.cab-lic-bar .row { display: flex; justify-content: space-between; font-size: 12.5px; color: var(--ctxt-mute); font-weight: 600; }
.cab-lic-bar .row b { color: #d4dbf0; }
.cab-track { height: 8px; border-radius: 5px; background: #141a30; overflow: hidden; }
.cab-track i { display: block; height: 100%; border-radius: 5px; background: linear-gradient(90deg, #38bdf8, #818cf8); box-shadow: 0 0 10px rgba(90,130,255,.6); }
.cab-lic-actions { display: flex; gap: 12px; flex-wrap: wrap; }
.cab-keyrow { display: flex; align-items: center; gap: 10px; padding: 12px 22px; border-top: 1px solid var(--cline-soft); position: relative; z-index: 2; }
.cab-keyrow .kk { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: #7986a8; font-weight: 800; }
.cab-keyrow code { font-family: 'Sora', sans-serif; font-size: 13.5px; color: #cdd6ef; letter-spacing: .04em; }
.cab-copy { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; cursor: pointer;
  border: 1px solid #262f4e; background: #10152a; color: #8b96b5; transition: .15s; }
.cab-copy:hover { color: #fff; border-color: var(--cline-bright); }

/* Orders table (scoped so the global table styles are untouched) */
.cab-panel table { width: 100%; border-collapse: collapse; }
.cab-panel thead th { padding: 12px 22px; text-align: left; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: #7c88ad; font-weight: 800;
  background: #0c1120; border-bottom: 1px solid var(--cline-soft); white-space: nowrap; }
.cab-panel tbody td { padding: 14px 22px; border-bottom: 1px solid #12172a; font-size: 13.5px; color: #c9d1e8; }
.cab-panel tbody tr:last-child td { border-bottom: none; }
.cab-panel tbody tr { transition: background .15s; }
.cab-panel tbody tr:hover { background: rgba(84,104,255,.05); }
.cab-oid { font-family: 'Sora', sans-serif; font-weight: 700; color: #e7ecfa; }
.cab-pill { display: inline-flex; align-items: center; gap: 7px; height: 24px; padding: 0 11px; border-radius: 20px; font-size: 11.5px; font-weight: 800; }
.cab-pill.paid { color: #5ee79b; background: rgba(46,200,120,.1); border: 1px solid rgba(46,200,120,.3); }
.cab-pill.wait { color: #fcd34d; background: rgba(251,191,36,.1); border: 1px solid rgba(251,191,36,.3); }
.cab-pill.cancel { color: #95a0be; background: rgba(120,130,160,.08); border: 1px solid rgba(120,130,160,.24); }
.cab-pill .pip { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* Right-column mini panels */
.cab-mini { padding: 20px 22px; }
.cab-mini h3 { font-size: 15px; font-weight: 600; margin-bottom: 6px; display: flex; align-items: center; gap: 9px; color: #fff; }
.cab-mini h3 svg { width: 18px; height: 18px; color: var(--cblue-hi); flex: none; }
.cab-mini p { font-size: 13px; color: var(--ctxt-mute); line-height: 1.55; margin-bottom: 14px; }
.cab-ref-code { display: flex; align-items: center; gap: 10px; padding: 11px 14px; border-radius: 11px; background: #0a0e1a; border: 1px dashed #2a3352; margin-bottom: 14px; }
.cab-ref-code code { font-family: 'Sora', sans-serif; font-size: 14px; color: #cdd6ef; font-weight: 700; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cab-ref-stat { display: flex; gap: 10px; }
.cab-ref-stat .rs { flex: 1; padding: 12px; border-radius: 11px; background: #0a0e1a; border: 1px solid var(--cline); text-align: center; }
.cab-ref-stat .rn { font-family: 'Sora', sans-serif; font-size: 20px; font-weight: 700; color: #eef2fd; }
.cab-ref-stat .rl { font-size: 11px; color: var(--ctxt-mute); margin-top: 3px; }
.cab-support-card { background: linear-gradient(150deg, #0b1226, #0c0f20); border-color: rgba(91,120,255,.24); }

/* Downloads */
.cab-sec-h { font-family: 'Sora', sans-serif; font-size: 16px; font-weight: 600; margin: 6px 2px 14px; letter-spacing: .01em; color: #fff; }
.cab-dl-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.cab-dl { display: flex; flex-direction: column; gap: 13px; padding: 20px; border-radius: 14px; background: #0a0e1a; border: 1px solid var(--cline); transition: .2s; }
.cab-dl:hover { border-color: rgba(91,120,255,.32); transform: translateY(-3px); }
.cab-dl-top { display: flex; align-items: center; gap: 13px; }
.cab-dl-os { width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center; background: rgba(120,130,255,.05); border: 1px solid var(--cline); color: #c5cef0; flex: none; }
.cab-dl-name { font-family: 'Sora', sans-serif; font-size: 15px; font-weight: 700; color: #fff; }
.cab-dl-ver { font-size: 12px; color: var(--ctxt-mute); margin-top: 2px; }
.cab-dl .btn { width: 100%; }

/* Cabinet responsive: collapse the sidebar into a top strip under 1080px so the
   nav stays reachable (the mockup hides it outright; keeping it is better UX). */
@media (max-width: 1080px) {
  .cab-shell { grid-template-columns: 1fr; }
  .cab-side { position: static; height: auto; flex-direction: row; flex-wrap: wrap; align-items: center;
    gap: 6px 10px; padding: 12px 16px; border-right: 0; border-bottom: 1px solid var(--cline-soft); }
  .cab-brand { padding: 0 8px 0 0; }
  .cab-side .cab-nav { flex-direction: row; flex-wrap: wrap; }
  .cab-nav-cap, .cab-spacer, .cab-userbox { display: none; }
  .cab-nav-link { margin-bottom: 0; }
  .cab-nav-link.active::before { display: none; }
  .cab-topbar { padding: 14px 16px; }
  .cab-content { padding: 20px 16px 40px; }
  .cab-stats { grid-template-columns: 1fr 1fr; }
  .cab-grid2 { grid-template-columns: 1fr; }
  .cab-license .cab-art { display: none; }
  .cab-dl-grid { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .cab-stats { grid-template-columns: 1fr; }
  .cab-pt-title { font-size: 17px; }
}

/* ─── Cabinet inner pages — restyle the shared content classes to the mockup
   language WITHOUT touching page HTML. All scoped under body.cab so admin/auth
   keep the global styling. The sticky topbar already carries the page title, so
   the in-content .page-title/.page-subtitle are hidden. */
.cab h1, .cab h2, .cab h3 { font-family: 'Sora', sans-serif; }
.cab .page-title, .cab .page-subtitle { display: none; }
.cab .page-header { margin-bottom: 18px; }
.cab .back-link { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--cblue-hi); }
.cab .back-link:hover { color: #9fbcff; text-decoration: none; }
.cab .page-intro, .cab .page-note { color: var(--ctxt-mute); font-size: 14px; max-width: 720px; margin-bottom: 14px; }
.cab .section-title { font-family: 'Sora', sans-serif; font-size: 16px; font-weight: 600; color: #fff; margin: 6px 2px 14px; letter-spacing: .01em; }
.cab section { margin-bottom: 22px; }

/* Panels / cards */
.cab .card { border-radius: 16px; border: 1px solid var(--cline); background: linear-gradient(180deg, #0b0f1c, #090c16); padding: 22px 24px; margin-bottom: 20px; }

/* Tables */
.cab .table-responsive { border: 1px solid var(--cline); border-radius: 14px; overflow: hidden; overflow-x: auto; background: linear-gradient(180deg, #0b0f1c, #090c16); margin-bottom: 20px; }
.cab .data-table, .cab table.table { width: 100%; border-collapse: collapse; }
.cab .data-table th, .cab table.table th { padding: 12px 18px; text-align: left; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: #7c88ad; font-weight: 800; background: #0c1120; border-bottom: 1px solid var(--cline-soft); white-space: nowrap; }
.cab .data-table td, .cab table.table td { padding: 13px 18px; border-bottom: 1px solid #12172a; font-size: 13.5px; color: #c9d1e8; }
.cab .data-table tbody tr:last-child td { border-bottom: none; }
.cab .data-table tbody tr { transition: background .15s; }
.cab .data-table tbody tr:hover { background: rgba(84,104,255,.05); }
.cab .money-cell, .cab .money-value, .cab .ledger-amount { font-variant-numeric: tabular-nums; }

/* Badges → pills (mapped by status modifier) */
.cab .badge { display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 11px; border-radius: 20px; font-size: 11.5px; font-weight: 800;
  color: #95a0be; background: rgba(120,130,160,.08); border: 1px solid rgba(120,130,160,.24); }
.cab .badge-active, .cab .badge-paid, .cab .badge-credited, .cab .badge-completed, .cab .badge-done, .cab .badge-approved { color: #5ee79b; background: rgba(46,200,120,.1); border-color: rgba(46,200,120,.3); }
.cab .badge-pending, .cab .badge-awaiting_payment, .cab .badge-held_aml, .cab .badge-requested, .cab .badge-processing { color: #fcd34d; background: rgba(251,191,36,.1); border-color: rgba(251,191,36,.3); }
.cab .badge-cancelled, .cab .badge-expired, .cab .badge-superseded, .cab .badge-refunded, .cab .badge-inactive, .cab .badge-suspended, .cab .badge-rejected, .cab .badge-failed { color: #95a0be; background: rgba(120,130,160,.08); border-color: rgba(120,130,160,.24); }

/* Buttons — extend the earlier .cab button set with the remaining variants */
.cab .btn-secondary { height: 38px; padding: 0 15px; border-radius: 10px; background: #0c101d; border: 1px solid var(--cline); color: var(--ctxt-dim); font-size: 12.5px; }
.cab .btn-secondary:hover { border-color: var(--cline-bright); color: #fff; }
.cab .btn-danger { height: 38px; padding: 0 15px; border-radius: 10px; background: rgba(224,52,79,.12); border: 1px solid rgba(224,52,79,.4); color: #ff8ba0; font-size: 12.5px; }
.cab .btn-danger:hover { background: rgba(224,52,79,.2); color: #fff; }
.cab .btn-warning { height: 42px; padding: 0 18px; border-radius: 11px; background: rgba(251,191,36,.12); border: 1px solid rgba(251,191,36,.4); color: #fcd34d; font-size: 13px; }
.cab .btn-warning:hover { background: rgba(251,191,36,.2); color: #fff; }
.cab .btn-block { width: 100%; }

/* Empty / info / notice states */
.cab .empty-state, .cab .info-state { padding: 26px 22px; text-align: center; color: var(--ctxt-mute); font-size: 14px; border: 1px dashed #26304e; border-radius: 14px; background: rgba(10,14,26,.5); }
.cab .notice { padding: 14px 16px; border-radius: 12px; background: #0a0e1a; border: 1px solid var(--cline); color: var(--ctxt-dim); font-size: 13.5px; margin-bottom: 16px; }
.cab .notice-warning { border-color: rgba(251,191,36,.4); color: #fcd34d; background: rgba(251,191,36,.06); }

/* Stat tiles (inner pages: label + value, no icon) */
.cab .stats-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 22px; }
.cab .stat-card { padding: 18px 20px; border-radius: 14px; background: linear-gradient(180deg, #0c101f, #0a0e1a); border: 1px solid var(--cline); }
.cab .stat-label { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: #8b96b5; font-weight: 800; margin-bottom: 8px; }
.cab .stat-value { font-family: 'Sora', sans-serif; font-size: 24px; font-weight: 700; line-height: 1; color: #fff; }
.cab .stat-held { font-size: 11.5px; color: var(--ctxt-mute); margin-top: 6px; }

/* Definition list (license detail) */
.cab .detail-list { display: grid; grid-template-columns: max-content 1fr; gap: 12px 28px; align-items: center; }
.cab .detail-list dt { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: #7986a8; font-weight: 800; }
.cab .detail-list dd { font-size: 14px; font-weight: 600; color: #e7ecfa; }
.cab .license-key, .cab .device-id { font-family: 'Sora', sans-serif; letter-spacing: .03em; color: #cdd6ef; }

/* Forms */
.cab .form-group { margin-bottom: 14px; }
.cab .form-label { display: block; font-size: 12px; font-weight: 700; color: var(--ctxt-dim); margin-bottom: 6px; }
.cab .form-hint, .cab .form-note { font-size: 13px; color: var(--ctxt-mute); margin-bottom: 12px; }
.cab input[type=text], .cab input[type=email], .cab input[type=password], .cab input[type=number], .cab .input, .cab .input-sm, .cab textarea, .cab select {
  width: 100%; background: #0a0e1a; border: 1px solid var(--cline); border-radius: 10px; color: var(--ctxt); font-family: inherit; font-size: 13.5px; padding: 10px 13px; }
.cab .input-sm { padding: 7px 10px; font-size: 12.5px; }
.cab input:focus, .cab .input:focus, .cab textarea:focus, .cab select:focus { outline: none; border-color: var(--cline-bright); }
.cab input::placeholder { color: var(--ctxt-faint); }

/* Plans */
/* Fixed track maximum, not 1fr: the subscribed page shows a single card (the
   plan you own, with its renew and upgrade forms) and a flexible track would
   stretch that one card across the full 1320px content width. */
/* 🔴 Это правило ВЫИГРЫВАЕТ у generic .plan-cards: кабинет целиком рендерится
 * под .cab, и специфичность здесь выше. Потолок был 480px — единственная
 * подписка (обычное состояние аккаунта) растягивалась в полосу почти в пол-экрана
 * и оставляла рядом пустоту. justify-content:start прижимает ряд к тексту
 * страницы, а не размазывает по ширине. */
.cab .plan-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 360px)); justify-content: start; gap: 20px; }
.cab .plan-card { display: flex; flex-direction: column; border-radius: 16px; border: 1px solid var(--cline); background: linear-gradient(180deg, #0b0f1c, #090c16); overflow: hidden; }
.cab .plan-card-header { padding: 24px 24px 18px; border-bottom: 1px solid var(--cline-soft); }
.cab .plan-name { font-size: 20px; font-weight: 700; color: #fff; margin-bottom: 10px; }
.cab .plan-price { font-size: 30px; font-weight: 800; color: #fff; }
.cab .plan-price .plan-period { font-family: 'Manrope', sans-serif; font-size: 14px; font-weight: 600; color: var(--ctxt-mute); }
.cab .plan-meta, .cab .plan-ports { font-size: 13px; color: var(--ctxt-mute); margin-top: 8px; }
/* Строка пробы на карточке своего тарифа: повышенный тариф временный, и это
   должно читаться как пометка, а не как купленное. */
.cab .plan-trial { font-size: 13px; font-weight: 600; color: var(--ctxt-dim); margin-top: 8px; }
.cab .plan-features { list-style: none; margin: 0; padding: 18px 24px; display: flex; flex-direction: column; gap: 10px; }
.cab .plan-features li { position: relative; padding-left: 26px; font-size: 13.5px; color: var(--ctxt-dim); }
.cab .plan-features li::before { content: ""; position: absolute; left: 0; top: 2px; width: 16px; height: 16px; border-radius: 50%; background: rgba(46,200,120,.12); border: 1px solid rgba(46,200,120,.4); }
.cab .plan-features li::after { content: ""; position: absolute; left: 5px; top: 5px; width: 4px; height: 8px; border-right: 2px solid #5ee79b; border-bottom: 2px solid #5ee79b; transform: rotate(40deg); }
.cab .plan-card-footer { margin-top: auto; padding: 18px 24px 24px; border-top: 1px solid var(--cline-soft); }
.cab .promo-row { margin-bottom: 12px; }
.cab .pay-options { border: 0; padding: 0; margin: 0 0 14px; display: flex; flex-direction: column; gap: 8px; }
.cab .pay-options legend { font-size: 12px; font-weight: 700; color: var(--ctxt-dim); margin-bottom: 6px; padding: 0; }
.cab .pay-option { display: flex; align-items: center; gap: 9px; padding: 10px 13px; border-radius: 10px; border: 1px solid var(--cline); background: #0a0e1a; cursor: pointer; font-size: 13.5px; color: var(--ctxt-dim); }
.cab .pay-option input { width: auto; }

/* Self-serve renewal (subscribed Plans page) — term picker */
.cab .renew-form { display: flex; flex-direction: column; gap: 12px; }
.cab .renew-title { font-size: 13px; font-weight: 700; color: var(--ctxt-dim); }
.cab .renew-terms { display: flex; flex-direction: column; gap: 8px; }
/* The form is a flex column with its own gap — the hint's bottom margin would
   double the space before the submit button. */
.cab .renew-form .form-hint { margin: 0; }
.cab .renew-term { display: flex; align-items: center; gap: 11px; padding: 11px 13px; border-radius: 11px; border: 1px solid var(--cline); background: #0a0e1a; cursor: pointer; transition: .15s; }
.cab .renew-term:hover { border-color: var(--cline-bright); }
.cab .renew-term:has(input:checked) { border-color: rgba(91,120,255,.5); background: linear-gradient(90deg, rgba(74,100,255,.12), rgba(74,100,255,.03)); box-shadow: inset 0 0 0 1px rgba(91,120,255,.3); }
.cab .renew-term input { width: auto; flex: none; accent-color: #5b8bff; }
.cab .renew-term-info { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; }
.cab .renew-term-dur { font-family: 'Sora', sans-serif; font-size: 14px; font-weight: 700; color: #e7ecfa; }
.cab .renew-term-price { font-size: 13.5px; color: var(--ctxt-mute); }
.cab .renew-term-badge { font-size: 11px; font-weight: 800; color: #5ee79b; background: rgba(46,200,120,.12); border: 1px solid rgba(46,200,120,.35); border-radius: 20px; padding: 2px 9px; white-space: nowrap; }

/* License detail extras */
.cab .license-actions { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 24px; }
.cab .devices-section h2 { font-size: 16px; font-weight: 600; color: #fff; margin-bottom: 14px; }
.cab .device-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.cab .device-actions form { display: flex; gap: 6px; align-items: center; margin: 0; }
.cab .device-actions .input-sm { width: auto; min-width: 120px; }

/* Referrals */
.cab .referral-code-display { margin: 10px 0 14px; }
.cab .referral-code-value { display: inline-block; font-family: 'Sora', sans-serif; font-size: 22px; font-weight: 800; letter-spacing: .08em; color: #fff; padding: 8px 18px; border-radius: 12px; background: #0a0e1a; border: 1px dashed #2a3352; }
.cab .referral-reward-rate { font-size: 14px; color: var(--ctxt-dim); margin-bottom: 8px; }
.cab .referral-share-link { margin-top: 12px; }
.cab .referral-user-label { font-family: 'Sora', sans-serif; font-weight: 700; color: #e7ecfa; }

/* Balance / ledger */
.cab .ledger-amount { font-weight: 700; }
.cab .payout-address, .cab .ledger-ref { font-family: 'Sora', sans-serif; font-size: 12.5px; color: #aab6d8; word-break: break-all; }

/* Downloads */
.cab .download-license { padding: 0; overflow: hidden; }
.cab .download-license-head { display: flex; align-items: center; gap: 12px; padding: 18px 24px; border-bottom: 1px solid var(--cline-soft); }
.cab .download-plan { font-family: 'Sora', sans-serif; font-size: 16px; font-weight: 700; color: #fff; }
.cab .download-meta { margin-left: auto; font-size: 12.5px; color: var(--ctxt-mute); }
.cab .download-os { padding: 20px 24px; border-bottom: 1px solid var(--cline-soft); }
.cab .download-os:last-child { border-bottom: none; }
.cab .download-os-title { font-size: 15px; font-weight: 700; color: #dfe6ff; margin-bottom: 14px; }
.cab .download-option { margin-bottom: 16px; }
.cab .download-option:last-child { margin-bottom: 0; }
.cab .download-option-primary { padding: 16px; border-radius: 12px; background: #0a0e1a; border: 1px solid var(--cline); }
.cab .download-option-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.cab .download-subhead { font-size: 14px; font-weight: 700; color: #e7ecfa; }
.cab .download-help { font-size: 13px; color: var(--ctxt-mute); margin-bottom: 10px; }
.cab .download-recommended { margin-left: auto; }
.cab .download-archives { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.cab .install-oneliner { background: #06080f; border: 1px solid var(--cline); border-radius: 10px; padding: 12px 14px; margin: 0; }
/* Команды переносятся, а не уезжают в горизонтальную прокрутку: строку за краем
   окна не прочитать и не выделить целиком. pre-wrap хранит собственные переносы
   команды, overflow-wrap ломает слишком длинный токен (URL с токеном загрузки). */
.cab .install-oneliner code { font-family: ui-monospace, 'SF Mono', Consolas, monospace; font-size: 12.5px; color: #cdd6ef; white-space: pre-wrap; overflow-wrap: anywhere; }

/* Support — channel chooser + web chat */
.cab .support-intro { color: var(--ctxt-mute); font-size: 14px; max-width: 720px; margin-bottom: 18px; }
.cab .support-docs { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; max-width: 720px; margin: 0 0 18px; padding: 12px 14px; border-radius: 12px; border: 1px solid var(--cline); background: #0a0e1a; color: var(--ctxt-mute); font-size: 14px; line-height: 1.5; }
.cab .support-docs-link { font-weight: 700; color: var(--cblue-hi); white-space: nowrap; }
.cab .support-docs-out { margin-left: 4px; font-size: 12px; opacity: .6; }
.cab .support-channels { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.cab .support-channel { position: relative; display: flex; flex-direction: column; gap: 6px; padding: 22px; border-radius: 16px; border: 1px solid var(--cline); background: linear-gradient(180deg, #0b0f1c, #090c16); }
.cab .support-channel-recommended { border-color: rgba(91,120,255,.35); }
.cab .support-channel-badge { position: absolute; top: 14px; right: 14px; font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: #8fb4ff; padding: 3px 9px; border-radius: 20px; background: rgba(91,120,255,.12); border: 1px solid rgba(91,120,255,.35); }
.cab .support-channel-icon { width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center; flex: none; background: rgba(120,130,255,.05); border: 1px solid var(--cline); color: var(--cblue-hi); margin-bottom: 6px; }
.cab .support-channel-name { font-size: 16px; font-weight: 700; color: #fff; }
.cab .support-channel-desc { font-size: 13px; color: var(--ctxt-mute); flex: 1; }
.cab .support-channel-note { font-size: 12px; color: var(--ctxt-faint); }
.cab .support-channel-connected { color: #5ee79b; }
.cab .support-channel .btn { margin-top: 10px; }
.cab .support-back { background: none; border: 0; color: var(--cblue-hi); cursor: pointer; font-size: 13px; font-weight: 600; padding: 0; margin-bottom: 14px; }
.cab .support-back:hover { color: #9fbcff; }
.cab .support-chat-card { border: 1px solid var(--cline); border-radius: 16px; background: linear-gradient(180deg, #0b0f1c, #090c16); overflow: hidden; }
.cab .support-thread { display: flex; flex-direction: column; gap: 12px; padding: 20px; max-height: 460px; overflow-y: auto; }
.cab .support-msg { display: flex; }
.cab .support-msg-user { justify-content: flex-end; }
.cab .support-msg-staff { justify-content: flex-start; }
.cab .support-bubble { max-width: 72%; padding: 11px 14px; border-radius: 14px; background: #0f1526; border: 1px solid var(--cline); }
.cab .support-msg-user .support-bubble { background: linear-gradient(180deg, #274093, #1c2f6b); border-color: rgba(91,120,255,.4); }
.cab .support-msg-sender { font-size: 11px; font-weight: 800; color: var(--ctxt-mute); margin-bottom: 4px; text-transform: uppercase; letter-spacing: .05em; }
.cab .support-msg-body { font-size: 13.5px; color: #e7ecfa; white-space: pre-wrap; word-break: break-word; }
.cab .support-msg-time { font-size: 10.5px; color: var(--ctxt-faint); margin-top: 5px; }
.cab .support-empty { text-align: center; color: var(--ctxt-mute); padding: 30px; font-size: 14px; }
/* border-BOTTOM, not top: the compose form sits above the newest-first thread. */
.cab .support-compose { display: flex; flex-direction: column; gap: 10px; padding: 16px 20px; border-bottom: 1px solid var(--cline-soft); }
.cab .support-compose-body { resize: vertical; min-height: 70px; }
.cab .support-compose-actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.cab .support-compose-hint { font-size: 12px; color: var(--ctxt-mute); }
.cab .support-attach { display: flex; flex-direction: column; gap: 6px; }
.cab .support-attach-label { display: flex; flex-direction: column; gap: 6px; }
.cab .support-attach-hint { font-size: 12px; color: var(--ctxt-faint); }
.cab .support-attach-list { list-style: none; margin: 0; padding: 0; font-size: 12px; color: var(--ctxt-mute); word-break: break-all; }
.cab .support-files { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.cab .support-file { display: inline-flex; align-items: center; text-decoration: none; }
.cab .support-file-image img { display: block; max-width: 180px; max-height: 180px; border-radius: 10px; border: 1px solid var(--cline); object-fit: cover; }
.cab .support-file-doc { gap: 8px; padding: 7px 11px; border-radius: 10px; border: 1px solid var(--cline); background: rgba(255,255,255,.04); color: #e7ecfa; font-size: 12.5px; }
.cab .support-file-doc:hover { background: rgba(255,255,255,.08); }
.cab .support-file-name { word-break: break-all; }
.cab .support-file-size { color: var(--ctxt-faint); white-space: nowrap; }

/* Order detail — definition card, payment instructions, refund states */
.cab .detail-card { border-radius: 16px; border: 1px solid var(--cline); background: linear-gradient(180deg, #0b0f1c, #090c16); padding: 22px 24px; margin-bottom: 20px; }
.cab .payment-instructions h2, .cab .refund-section h2 { font-size: 16px; font-weight: 600; color: #fff; margin-bottom: 12px; }
.cab .apply-balance-section { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--cline-soft); }
.cab .apply-balance-section h3 { font-size: 14px; font-weight: 700; color: #e7ecfa; margin-bottom: 10px; }
.cab .instructions-text { white-space: pre-wrap; word-break: break-word; font-size: 13px; color: var(--ctxt-dim); background: #06080f; border: 1px solid var(--cline); border-radius: 10px; padding: 12px 14px; margin-bottom: 12px; }
.cab .ref-code { font-family: 'Sora', sans-serif; letter-spacing: .03em; color: #cdd6ef; }
.cab .refund-done { color: #5ee79b; font-size: 13.5px; }
.cab .refund-pending { color: #fcd34d; font-size: 13.5px; }
.cab .refund-section .hint, .cab .hint { color: var(--ctxt-mute); font-size: 13px; margin-bottom: 12px; }
.cab .refund-section label { display: block; font-size: 12px; font-weight: 700; color: var(--ctxt-dim); margin-bottom: 12px; }
.cab .refund-section label input { margin-top: 6px; }
.cab .btn-link { background: none; border: 0; padding: 0; height: auto; color: var(--cblue-hi); font-weight: 600; }
.cab .btn-link:hover { color: #9fbcff; }
.cab .seats-cell { font-variant-numeric: tabular-nums; }
.cab .seats-used { color: #e7ecfa; font-weight: 700; }
.cab .seats-total { color: var(--ctxt-mute); }
.cab .seats-sep { color: var(--ctxt-faint); margin: 0 2px; }

/* Promo entry */
.cab .promo-intro { color: var(--ctxt-mute); font-size: 14px; max-width: 620px; margin-bottom: 18px; }
.cab .promo-form { display: flex; gap: 10px; max-width: 520px; flex-wrap: wrap; }
.cab .promo-form input[type=text] { flex: 1; min-width: 220px; }
.cab .promo-hint { font-size: 13px; color: var(--ctxt-mute); margin-top: 12px; }
.cab .promo-saved { max-width: 560px; padding: 20px 22px; border-radius: 14px; border: 1px solid rgba(46,200,120,.32); background: rgba(46,200,120,.06); }
.cab .promo-saved p { color: #e7ecfa; margin-bottom: 14px; }
.cab .promo-saved-actions { display: flex; gap: 12px; flex-wrap: wrap; }
.cab .form-error { color: #ff8ba0; font-size: 13.5px; padding: 10px 14px; border-radius: 10px; background: rgba(224,52,79,.1); border: 1px solid rgba(224,52,79,.3); margin-bottom: 14px; max-width: 520px; }

@media (max-width: 700px) {
  .cab .support-channels { grid-template-columns: 1fr; }
  .cab .support-bubble { max-width: 86%; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   FLEX-GAP FALLBACK — pre-84 Chromium (older Vivaldi/Opera) lack flexbox `gap`.
   A tiny <head> probe adds `.no-flexgap` to <html> ONLY when flex `gap` is
   unsupported, so every rule below is dead on modern browsers (their native
   `gap` stands, byte-for-byte). Grid `gap` works since Chromium 57, so grid
   layouts need no fallback. Technique: column flex → margin-top between
   children; row flex → margin-left. Containers using justify-content:
   space-between already self-space and are intentionally omitted.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Column flex: vertical spacing between stacked children ──────────────── */
.no-flexgap .flash-region > * + * { margin-top: var(--space-2); }
.no-flexgap .stat-block > * + * { margin-top: var(--space-1); }
.no-flexgap .plan-card > * + * { margin-top: var(--space-4); }
.no-flexgap .plan-card-header > * + * { margin-top: var(--space-2); }
.no-flexgap .plan-features > * + * { margin-top: var(--space-2); }
.no-flexgap .feature-card > * + * { margin-top: var(--space-3); }
.no-flexgap .pricing-card > * + * { margin-top: var(--space-4); }
.no-flexgap .pricing-features > * + * { margin-top: var(--space-2); }
/* Keep the CTA bottom-aligned even in the flex-gap fallback (the generic rule
   above would otherwise pin it right under the note). */
.no-flexgap .pricing-card > .pricing-cta { margin-top: auto; }
/* The recommended badge is absolutely positioned; don't let the generic sibling
   margin push the plan name (and everything below it) down by one gap. */
.no-flexgap .pricing-card > .pricing-badge-recommended + * { margin-top: 0; }
.no-flexgap .faq-list > * + * { margin-top: var(--space-2); }
.no-flexgap .cta-strip-inner > * + * { margin-top: var(--space-6); }
.no-flexgap .support-channel > * + * { margin-top: var(--space-3); }
.no-flexgap .support-chat-card > * + * { margin-top: var(--space-4); }
.no-flexgap .support-thread > * + * { margin-top: var(--space-3); }
.no-flexgap .support-bubble > * + * { margin-top: 2px; }
.no-flexgap .support-compose > * + * { margin-top: var(--space-3); }
.no-flexgap .stat-card > * + * { margin-top: var(--space-1); }
.no-flexgap .day-bars > * + * { margin-top: var(--space-2); }
.no-flexgap .analytics-range label > * + * { margin-top: var(--space-1); }
.no-flexgap .upgrade-form label > * + *,
.no-flexgap .filter-bar label > * + * { margin-top: var(--space-1); }
.no-flexgap .support-ticket-thread > * + *,
.no-flexgap .message-list > * + * { margin-top: var(--space-3); }
.no-flexgap .docs-pager-link > * + * { margin-top: 0.2rem; }

/* ── Row flex: horizontal spacing between children ──────────────────────── */
.no-flexgap .header-inner > * + * { margin-left: var(--space-4); }
.no-flexgap .site-nav > * + * { margin-left: var(--space-1); }
.no-flexgap .footer-inner > * + * { margin-left: var(--space-4); }
.no-flexgap .locale-switcher > * + * { margin-left: var(--space-1); }
.no-flexgap .landing-nav > * + * { margin-left: var(--space-1); }
.no-flexgap .landing-header-actions > * + * { margin-left: var(--space-3); }
.no-flexgap .hero-ctas > * + * { margin-left: var(--space-4); }
.no-flexgap .pricing-price > * + * { margin-left: 0.25rem; }
.no-flexgap .footer-links > * + * { margin-left: var(--space-3); }
.no-flexgap .hero-tech > * + * { margin-left: 0.6rem; }
.no-flexgap .fp-dots > * + * { margin-left: 0.35rem; }
.no-flexgap .download-option-head > * + * { margin-left: var(--space-3); }
.no-flexgap .download-archives > * + * { margin-left: var(--space-2); }
.no-flexgap .device-actions > * + * { margin-left: var(--space-2); }
.no-flexgap .device-actions form > * + * { margin-left: var(--space-2); }
.no-flexgap .license-actions > * + * { margin-left: var(--space-3); }
.no-flexgap .analytics-range > * + * { margin-left: var(--space-4); }
.no-flexgap .home-actions > * + * { margin-left: var(--space-3); }
.no-flexgap .upgrade-form > * + *,
.no-flexgap .filter-bar > * + * { margin-left: var(--space-4); }
.no-flexgap .thread-meta > * + * { margin-left: var(--space-2); }
.no-flexgap .filter-tabs > * + * { margin-left: var(--space-2); }
.no-flexgap .checkbox-label > * + * { margin-left: var(--space-2); }
.no-flexgap .pay-option > * + * { margin-left: var(--space-2); }
.no-flexgap .docs-toc ul > * + * { margin-left: var(--space-6); }
.no-flexgap .docs-callout > * + * { margin-left: var(--space-3); }
.no-flexgap .docs-endpoint-head > * + * { margin-left: var(--space-3); }

/* Inline-flex text pairs whose gap sits between an ::before / icon and text */
.no-flexgap .brand::before { margin-right: 0.5rem; }
.no-flexgap .btn > * + * { margin-left: var(--space-2); }

/* Containers that switch to a column at a breakpoint — swap the margin axis */
@media (max-width: 640px) {
  .no-flexgap .hero-ctas > * + * { margin-left: 0; margin-top: var(--space-4); }
  .no-flexgap .footer-inner > * + * { margin-left: 0; margin-top: var(--space-2); }
}


/* ══ Changelog / release timeline ═══════════════════════════════════════════ */
.changelog-section{padding:clamp(2.5rem,6vw,4.5rem) 0 5rem}
.changelog-section .container{max-width:840px}
.changelog-heading{
  font-size:clamp(1.9rem,4.5vw,2.75rem);font-weight:800;letter-spacing:-.025em;line-height:1.1;
  margin:0 0 .6rem;color:var(--text);
}
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .changelog-heading{
    background:var(--accent-grad);-webkit-background-clip:text;background-clip:text;
    -webkit-text-fill-color:transparent;color:transparent;
  }
}
.changelog-intro{color:var(--text-muted);font-size:1.05rem;line-height:1.6;max-width:58ch;margin:0 0 2.75rem}

.changelog-timeline{list-style:none;margin:0;padding:0;position:relative}
.changelog-timeline::before{
  content:"";position:absolute;left:9px;top:8px;bottom:8px;width:2px;
  background:linear-gradient(180deg,rgba(45,212,191,.55),rgba(99,102,241,.35) 30%,var(--border) 100%);
}
.changelog-release{position:relative;padding:0 0 2.25rem 2.5rem}
.changelog-release:last-child{padding-bottom:.25rem}
.changelog-release::before{
  content:"";position:absolute;left:2px;top:6px;width:16px;height:16px;border-radius:50%;
  background:var(--bg);border:2px solid var(--accent-teal);box-shadow:0 0 0 4px rgba(45,212,191,.12);
}
.changelog-release:first-child::before{
  background:var(--accent-grad);border-color:transparent;
  box-shadow:0 0 0 4px rgba(45,212,191,.20),0 0 14px rgba(45,212,191,.45);
}
.changelog-version{display:flex;align-items:center;flex-wrap:wrap;gap:.6rem;margin:0 0 .85rem;line-height:1.25}
.cl-ver{
  display:inline-flex;align-items:center;flex:none;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:.8rem;font-weight:700;letter-spacing:.02em;padding:.22rem .6rem;border-radius:2rem;
  color:var(--accent-teal);background:rgba(99,102,241,.12);border:1px solid rgba(45,212,191,.28);
}
.changelog-release:first-child .cl-ver{color:#0b0e14;background:var(--accent-grad);border-color:transparent}
.cl-title{font-size:1.2rem;font-weight:700;letter-spacing:-.01em;color:var(--text)}
.changelog-items{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.7rem}
.changelog-item{position:relative;padding-left:1.4rem;color:var(--text-muted);font-size:.975rem;line-height:1.65}
.changelog-item::before{
  content:"";position:absolute;left:.05rem;top:.62rem;width:6px;height:6px;border-radius:50%;
  background:var(--accent-teal);opacity:.8;
}
@media (max-width:640px){.changelog-release{padding-left:2rem}.cl-title{font-size:1.08rem}}
.no-flexgap .changelog-version>*+*{margin-left:.6rem}
.no-flexgap .changelog-items>*+*{margin-top:.7rem}

/* ══ Blog feed (/blog) ══════════════════════════════════════════════════════ */
.blog-section{padding:clamp(2.5rem,6vw,4.5rem) 0 5rem}
.blog-heading{
  font-size:clamp(1.9rem,4.5vw,2.75rem);font-weight:800;letter-spacing:-.025em;line-height:1.1;
  margin:0 0 .6rem;color:var(--text);
}
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .blog-heading{
    background:var(--accent-grad);-webkit-background-clip:text;background-clip:text;
    -webkit-text-fill-color:transparent;color:transparent;
  }
}
.blog-intro{color:var(--text-muted);font-size:1.05rem;line-height:1.6;max-width:58ch;margin:0 0 2.75rem}

/* Three cards to a row on a desktop, which is what makes a page of nine
   (web.BlogPerPage) end on a full row rather than a ragged one. auto-fill
   rather than three fixed columns so a narrow viewport drops to two and then
   to one without a breakpoint per step. */
.post-grid{
  display:grid;gap:var(--space-6);
  grid-template-columns:repeat(auto-fill,minmax(17rem,1fr));
}
/* position:relative anchors the title link's overlay below, which is what makes
   the whole card clickable while leaving exactly one focusable element in it —
   keyboard focus then lands where the pointer does. */
.post-card{
  position:relative;display:flex;flex-direction:column;overflow:hidden;
  background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);
  transition:border-color .15s ease,transform .15s ease;
}
.post-card:hover{border-color:var(--border-focus);transform:translateY(-2px)}
.post-card-cover{display:block;aspect-ratio:16/9;overflow:hidden;background:var(--bg)}
.post-card-cover img{width:100%;height:100%;object-fit:cover;display:block}
.post-card-body{display:flex;flex-direction:column;gap:var(--space-2);padding:var(--space-4) var(--space-4) var(--space-6)}
.post-card-title{font-size:1.15rem;font-weight:700;letter-spacing:-.01em;line-height:1.3;margin:0}
.post-card-link{color:var(--text);text-decoration:none}
.post-card-link:hover{color:var(--accent-teal)}
.post-card-link::after{content:"";position:absolute;inset:0}
.post-card-date{margin:0;color:var(--text-muted);font-size:.8125rem;letter-spacing:.01em}
.post-card-excerpt{margin:0;color:var(--text-muted);font-size:.9375rem;line-height:1.6}

.blog-pager{display:flex;align-items:center;justify-content:center;gap:var(--space-4);margin-top:var(--space-12)}
.pager-link{
  display:inline-flex;align-items:center;padding:.5rem 1rem;border-radius:2rem;
  border:1px solid var(--border);background:var(--bg-card);
  color:var(--text);text-decoration:none;font-size:.9rem;font-weight:600;
}
.pager-link:hover{border-color:var(--border-focus);color:var(--accent-teal)}
/* The two directions share a rule today and are named apart so that giving
   them arrows on opposite sides later needs no change to the markup. */
.pager-prev,.pager-next{white-space:nowrap}
.pager-status{color:var(--text-muted);font-size:.85rem;font-variant-numeric:tabular-nums}

@media (max-width:640px){.post-grid{gap:var(--space-4)}.post-card-title{font-size:1.08rem}}
.no-flexgap .post-card-body>*+*{margin-top:var(--space-2)}
.no-flexgap .blog-pager>*+*{margin-left:var(--space-4)}

/* ─── Нумерация страниц ленты ────────────────────────────────────────────
   Номера лежат списком между «новее» и «старее», которые остались ссылками с
   rel=prev/next: на них опирается разметка в <head>, и разносить эти два
   утверждения по разным правилам значит однажды их рассогласовать. */
.pager-list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-1);list-style:none;margin:0;padding:0}
.pager-item{display:block}
.pager-num{
  display:grid;place-items:center;min-width:2.25rem;height:2.25rem;padding:0 .5rem;
  border:1px solid var(--border);border-radius:var(--radius-sm);
  color:var(--text-muted);font-size:.9rem;font-weight:600;text-decoration:none;
}
a.pager-num:hover{border-color:var(--border-focus);color:var(--text)}
/* Текущая страница — не ссылка: у неё другой вид, потому что она и есть
   другое, а не «ссылка в состоянии hover». */
.pager-num.is-current{background:var(--bg-card);border-color:var(--accent-teal);color:var(--text)}
.pager-gap{color:var(--text-muted);padding:0 .25rem;user-select:none}
.no-flexgap .pager-list>*+*{margin-left:var(--space-1)}

/* ─── Поиск по разделу ───────────────────────────────────────────────────
   Строка над рядом разделов, на каждой странице ленты и на выдаче.

   Кнопка не сжимается (flex:0 0 auto), поле забирает остаток: на узком экране
   ужиматься должно поле, а не подпись «Найти», которая от сжатия превращается
   в многоточие.

   Ширина ограничена — строка поиска во всю ширину сетки из трёх карточек
   выглядит как поле для сочинения, а не как строка для одного слова. */
.blog-search{display:flex;gap:var(--space-2);margin:0 0 var(--space-6);max-width:34rem}
.blog-search-input{
  flex:1 1 auto;min-width:0;
  padding:.55rem .9rem;border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--bg-input);color:var(--text);font:inherit;font-size:.95rem;
}
.blog-search-input::placeholder{color:var(--text-muted)}
.blog-search-input:focus{outline:none;border-color:var(--border-focus)}
.blog-search-btn{
  flex:0 0 auto;
  padding:.55rem 1.1rem;border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--bg-card);color:var(--text);font:inherit;font-size:.9rem;font-weight:600;
  cursor:pointer;
}
.blog-search-btn:hover{border-color:var(--accent-teal)}
.no-flexgap .blog-search>*+*{margin-left:var(--space-2)}

/* ─── Разделы блога ──────────────────────────────────────────────────────
   Ряд над сеткой. Ведёт на теговые ленты, которые и так есть; раздела, за
   которым нет ни одной видимой записи, в ряду нет — за это отвечает
   blogChips, а не стиль. */
.blog-chips{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:0 0 var(--space-8)}
.blog-chip{
  display:inline-flex;align-items:center;gap:.35rem;
  padding:.35rem .75rem;border:1px solid var(--border);border-radius:999px;
  background:var(--bg-card);color:var(--text-muted);
  font-size:.85rem;font-weight:600;text-decoration:none;
}
a.blog-chip:hover{border-color:var(--border-focus);color:var(--text)}
.blog-chip.is-current{border-color:var(--accent-teal);color:var(--text)}
.blog-chip-count{color:var(--text-muted);font-weight:500;font-size:.8em}
.no-flexgap .blog-chips>*+*{margin-left:var(--space-2)}

/* ─── Статья: шапка, текст, колонка оглавления ───────────────────────────
   Одна сетка на три части. На узком экране это одна колонка, и порядок в
   разметке — порядок чтения: заголовок, оглавление, текст. На широком —
   оглавление уходит вправо и прилипает при прокрутке. */
.post-layout{display:grid;grid-template-columns:minmax(0,1fr);gap:0 var(--space-8)}
.post-head{grid-column:1/-1}
.post-toc{margin:var(--space-6) 0 0}
.post-toc-inner{
  padding:var(--space-4) var(--space-6);
  background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);
}
.post-toc-title{
  margin:0 0 var(--space-2);color:var(--text-muted);
  font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;
}
.post-toc-list{list-style:none;margin:0;padding:0}
.post-toc-item{margin:0 0 .35rem}
.post-toc-sub{padding-left:var(--space-4)}
.post-toc-link{
  color:var(--text-muted);text-decoration:none;font-size:.9rem;line-height:1.45;
  display:block;border-left:2px solid transparent;padding-left:.6rem;
}
.post-toc-link:hover{color:var(--text)}
/* Раздел, до которого дочитали. Ставит и снимает класс маленький скрипт в
   blog_post.tmpl; без него оглавление остаётся рабочим списком ссылок. */
.post-toc-link.is-current{color:var(--text);border-left-color:var(--accent-teal)}

@media (min-width:1100px){
  /* Ширину задаёт та же страница, что и колонки: контейнер статьи иначе
     остаётся узким (44rem), и второй колонке некуда встать. */
  .post-article .container.post-layout-toc{max-width:64rem}
  .post-layout-toc{grid-template-columns:minmax(0,44rem) 15rem;justify-content:center}
  .post-layout-toc .post-main{grid-column:1}
  .post-layout-toc .post-toc{grid-column:2;grid-row:2;margin:0}
  .post-layout-toc .post-toc{align-self:start}
  .post-layout-toc .post-toc-inner{position:sticky;top:6rem;max-height:calc(100vh - 8rem);overflow-y:auto}
}

/* Tag chips. ONE rule for both places a chip appears — the feed card and the
   foot of an article — so the two cannot drift apart, the arrangement
   .post-grid and .post-card already have between the feed and "read next". */
.post-tag{
  display:inline-flex;align-items:center;white-space:nowrap;
  padding:.2rem .6rem;border-radius:2rem;
  border:1px solid var(--border);background:var(--bg);
  color:var(--text-muted);text-decoration:none;
  font-size:.75rem;font-weight:600;letter-spacing:.01em;
  transition:border-color .15s ease,color .15s ease;
}
.post-tag:hover{border-color:var(--border-focus);color:var(--accent-teal)}
/* position/z-index are LOAD-BEARING here, not decoration: .post-card-link::after
   lays a transparent overlay across the whole card so that a click anywhere on
   it opens the post, and anything under that overlay is a link nobody can
   click. Lifting the chip row above it is what keeps these reachable — at the
   cost of the card holding more than one focusable element, which is the right
   trade for links that go somewhere else. */
.post-card-tags{position:relative;z-index:1;display:flex;flex-wrap:wrap;gap:.4rem;margin:0}
/* Under an article the chips sit below the body, so they carry the space
   themselves rather than inheriting a grid's gap. */
.post-tags{display:flex;flex-wrap:wrap;gap:.4rem;margin:var(--space-8) 0 0}
.no-flexgap .post-card-tags>*+*{margin-left:.4rem}
.no-flexgap .post-tags>*+*{margin-left:.4rem}

/* ══ Blog post (/blog/{slug}) ════════════════════════════════ */
/* One article, one column, and a narrower one than the feed's. The body is
   operator-written prose, and a full-width line of prose on a wide monitor is
   unreadable; the feed's card grid has the opposite need, which is why this is
   its own section rather than a modifier on .blog-section. */
.post-article{padding:clamp(2.5rem,6vw,4.5rem) 0 3rem}
.post-article .container{max-width:44rem}
/* Хлебные крошки раздела — и над лентой тега, и над статьёй. Строка
   приглушена намеренно: она говорит, где читатель, и не спорит за внимание с
   заголовком под собой. Последнее звено — не ссылка (см. партиал), поэтому
   правило ссылки его не красит и подчёркивание ему не грозит. */
.blog-crumbs{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);
  margin:0 0 var(--space-6);color:var(--text-muted);font-size:.9rem;font-weight:600;
}
.blog-crumbs a{color:var(--text-muted);text-decoration:none}
.blog-crumbs a:hover{color:var(--accent-teal)}
.blog-crumbs-sep{opacity:.5}
/* Отступ несёт flex-gap; без него звенья слиплись бы в одно слово. */
.no-flexgap .blog-crumbs>*+*{margin-left:var(--space-2)}
.post-header{margin:0 0 var(--space-6)}
.post-title{
  font-size:clamp(1.8rem,4vw,2.6rem);font-weight:800;letter-spacing:-.025em;line-height:1.15;
  margin:0 0 var(--space-3);color:var(--text);
}
/* The byline row. A post with no author draws the date alone, so the spacing
   has to come from the row's gap and not from a margin on an element that is
   not there. */
.post-meta{display:flex;flex-wrap:wrap;gap:var(--space-3);margin:0;color:var(--text-muted);font-size:.875rem}
.post-date{letter-spacing:.01em}
.post-author{letter-spacing:.01em}
.post-cover{
  display:block;width:100%;height:auto;margin:var(--space-6) 0 0;
  border:1px solid var(--border);border-radius:var(--radius);
}
.post-standfirst{
  margin:var(--space-6) 0 0;color:var(--text-muted);
  font-size:1.075rem;line-height:1.65;
}

/* The body is admin-authored HTML (see blogPostData.Body in blog.go), so its
   elements are styled BY TYPE: nothing here may depend on a class the editor
   did not write. Every rule is scoped under .post-body so none of it leaks
   into the shell, the cards or the landing's own richtext block. */
.post-body{margin-top:var(--space-8);color:var(--text);font-size:1.0625rem;line-height:1.75}
.post-body>*:first-child{margin-top:0}
.post-body>*:last-child{margin-bottom:0}
.post-body p{margin:0 0 1.15em}
.post-body h2{font-size:1.5rem;font-weight:700;letter-spacing:-.015em;line-height:1.25;margin:2em 0 .6em;color:var(--text)}
.post-body h3{font-size:1.2rem;font-weight:700;line-height:1.3;margin:1.7em 0 .5em;color:var(--text)}
/* 🔴 Запас под ЛИПКУЮ шапку: без него переход по пункту оглавления ставит
   заголовок ровно под неё, и читатель попадает на середину предыдущего
   раздела. Запас с избытком — на узком окне шапка переносится на вторую
   строку. */
.post-body h2,.post-body h3{scroll-margin-top:6rem}
.post-body a{color:var(--accent-teal);text-decoration:underline;text-underline-offset:.15em}
.post-body ul,.post-body ol{margin:0 0 1.15em;padding-left:1.4em}
.post-body li{margin:0 0 .4em}
.post-body img{max-width:100%;height:auto;display:block;margin:1.5em 0;border-radius:var(--radius)}
.post-body figure{margin:1.5em 0}
.post-body figcaption{margin-top:.5em;color:var(--text-muted);font-size:.875rem}
.post-body blockquote{
  margin:1.5em 0;padding:.2em 0 .2em 1.1em;
  border-left:3px solid var(--accent-teal);color:var(--text-muted);
}
/* Long code lines scroll inside the block rather than widening the page. */
.post-body pre{
  margin:1.5em 0;padding:var(--space-4);overflow-x:auto;
  background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);
  font-size:.875rem;line-height:1.6;
}
.post-body code{font-size:.9em}
.post-body hr{border:0;border-top:1px solid var(--border);margin:2em 0}
.post-body table{width:100%;border-collapse:collapse;margin:1.5em 0;font-size:.9375rem}
.post-body th,.post-body td{border:1px solid var(--border);padding:.5em .7em;text-align:left}

/* "Read next" reuses the feed's .post-grid and .post-card, so the two pages
   cannot drift apart about what a card looks like. */
.post-related{padding:0 0 5rem}
.post-related-heading{
  font-size:1.35rem;font-weight:700;letter-spacing:-.015em;
  margin:0 0 var(--space-6);color:var(--text);
}

@media (max-width:640px){.post-title{font-size:1.6rem}.post-body{font-size:1rem}}
.no-flexgap .post-meta>*+*{margin-left:var(--space-3)}

/* ══ Contacts page (/contacts) ═══════════════════════════════════════════════ */
.contacts-section { padding: clamp(2.5rem,6vw,4.5rem) 0 5rem; }
.contacts-inner { max-width: 640px; text-align: center; }
.contacts-heading { font-size: clamp(1.9rem,4.5vw,2.5rem); font-weight: 800; letter-spacing: -.02em; line-height: 1.15; margin: 0 0 .6rem; }
.contacts-intro { color: var(--text-muted); font-size: 1.05rem; line-height: 1.6; max-width: 52ch; margin: 0 auto 2.5rem; }
.contact-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-8) var(--space-6);
  max-width: 420px;
  margin: 0 auto;
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
}
.contact-card-icon {
  width: 56px; height: 56px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent-grad); color: #0b0e14;
}
.contact-card-title { font-size: 1.15rem; font-weight: 700; margin: 0; }
.contact-card-desc { color: var(--text-muted); margin: 0 0 var(--space-2); }
.contact-card .btn { min-width: 200px; }
.no-flexgap .contact-card > * + * { margin-top: var(--space-3); }

/* ══ Legal documents (/terms, /privacy) ══════════════════════════════════════ */
.legal-section { padding: clamp(2.5rem,6vw,4.5rem) 0 5rem; }
.legal-container { max-width: 52rem; }
.legal-heading { font-size: clamp(1.9rem,4.5vw,2.5rem); font-weight: 800; letter-spacing: -.02em; line-height: 1.15; margin: 0 0 .6rem; }
.legal-meta { color: var(--text-muted); font-size: 0.9rem; margin-bottom: 1.5rem; }
.legal-intro { margin-bottom: 1rem; line-height: 1.7; }
.legal-fallback-notice,
.legal-unavailable {
  border-left: 3px solid var(--accent-teal);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  margin-bottom: 1.5rem;
  background: var(--bg-card);
  color: var(--text-muted);
}
.legal-toc { margin: 2rem 0 2.5rem; }
.legal-toc ol { padding-left: 1.25rem; line-height: 1.9; }
.legal-clause { margin-bottom: 2rem; scroll-margin-top: 5rem; }
.legal-clause-heading { font-size: 1.15rem; margin-bottom: 0.75rem; color: var(--text); }
.legal-clause p { line-height: 1.7; margin-bottom: 0.75rem; }
.legal-list { padding-left: 1.25rem; line-height: 1.7; margin-bottom: 0.75rem; }
.legal-list li { margin-bottom: 0.35rem; }
.legal-print { margin-top: 3rem; }

/* Print: drop the site chrome so a clause list prints as a clean document. */
@media print {
  .site-header, .site-footer, .legal-print, .locale-switch { display: none; }
  .legal-section { padding: 0; }
  .legal-container { max-width: none; }
  .legal-clause { page-break-inside: avoid; }
  a[href^="/"]::after { content: ""; }
}

/* Admin editor rows (/admin/legal) */
.legal-section-row {
  display: grid;
  grid-template-columns: 4rem 1fr auto;
  gap: 0.5rem;
  align-items: start;
  margin-bottom: 0.75rem;
}
.legal-section-row .legal-body { grid-column: 1 / -1; font-family: inherit; }

/* Price-change control on /admin/catalog: currency, amount and the button on one
   line inside a table cell. The row is dense on purpose — the panel exists so a
   price change takes a minute, and a multi-line form in every row would make the
   table unreadable at four products, let alone more. */
.catalog-price-form {
  display: flex; gap: 0.4rem; align-items: center; flex-wrap: nowrap;
}
.catalog-price-form .form-input { width: auto; min-width: 5.5rem; }

/* The product code beside the product name. It has to be shown — the price form
   posts by code and the history table below lists by code — but with no styling
   of its own it renders in the name's font on the same line, so "Pro" + "pro"
   reads as a duplicated entry rather than as a name and its identifier. */
.product-code {
  display: block;
  font-family: 'Fira Mono', 'Consolas', monospace;
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* Per-market amounts on the promo form: one box per currency, on one line.
   A campaign names the markets it runs in, and seeing them side by side is what
   makes an unfilled one read as "not covered" rather than as "forgotten". */
.promo-amounts { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.promo-amounts .form-input { width: auto; min-width: 6rem; }

/* ─── Rich field (/admin/landing — see internal/ui/static/js/editor.js) ────────
   Both modes of one field live in a single .rf wrapper.

   The <textarea> is the field that POSTS, so it must be visible by default:
   with the script blocked it is the whole editor. Hiding it is therefore keyed
   on .rf-live, which editor.js adds only after it has upgraded the field — a
   plain `.rf-source { display: none }` would leave an operator with scripts
   blocked nothing to type into, and the page would look fine while doing it.

   The toolbar and the surface ship with the `hidden` attribute for the same
   reason, and each needs its own [hidden] rule: the UA's `[hidden]{display:none}`
   loses to any class rule of ours that sets display. */
.rf { display: block; position: relative; }

.rf-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  margin-bottom: var(--space-2);
}
.rf-toolbar[hidden] { display: none; }

.rf-btn {
  min-width: 2rem;
  padding: var(--space-1) var(--space-2);
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: inherit;
  font-size: 0.8rem;
  line-height: 1.5;
  cursor: pointer;
}
.rf-btn:hover { filter: brightness(1.15); }
.rf-btn[aria-pressed="true"] { border-color: var(--border-focus); color: var(--accent-teal); }
/* The source toggle sits apart from the format buttons — it changes the mode,
   it does not format a selection. */
.rf-btn-mode { margin-left: auto; font-family: 'Fira Mono', 'Consolas', monospace; }

.rf-surface {
  min-height: 12rem;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 0.9375rem;
  line-height: 1.6;
  overflow-wrap: break-word;
}
.rf-surface[hidden] { display: none; }
.rf-surface:focus {
  outline: none;
  border-color: var(--border-focus);
  box-shadow: 0 0 0 3px rgba(45,212,191,0.15);
}

/* .rf-source is .form-input in a monospace face — it holds HTML, not prose. */
.rf-source { font-family: 'Fira Mono', 'Consolas', monospace; font-size: 0.85rem; }
.rf-live .rf-source { display: none; }
.rf-live .rf-source.rf-source-visible { display: block; }

/* Shown only when the visual mode would drop markup the stored HTML contains;
   the field then opens in source mode instead. */
.rf-lossy { color: var(--warning); }

/* ─── Визуальный редактор документации ──────────────────────────────────
   Поверхность показывает страницу так же, как её увидит читатель: классы
   docs-* в этом файле не привязаны к публичной оболочке, поэтому врезки,
   таблицы и карточки ручек выглядят здесь ровно так же, как на сайте.

   Хром выделения — рамка и панель — лежит СНАРУЖИ поверхности и наводится по
   координатам: всё, что скрипт положил бы внутрь, попало бы в разметку
   страницы, а сравнение «изменилось ли поле» идёт по innerHTML поверхности. */
.docs-surface { min-height: 32rem; }
/* Карточка целиком выбирается щелчком, а текстовые островки внутри неё —
   обычный курсор: по указателю видно, что будет дальше. */
.rf-surface [contenteditable="false"] { cursor: pointer; }
.rf-surface [contenteditable="true"] { cursor: text; }
.rf-surface [contenteditable="true"]:focus { outline: 1px dashed var(--accent-teal); outline-offset: 2px; }
.rf-btn-card { border-color: var(--accent-teal); }

.rfd-outline {
  position: absolute;
  pointer-events: none;
  border: 2px solid var(--accent-teal);
  border-radius: var(--radius-sm);
  z-index: 2;
}
.rfd-outline[hidden] { display: none; }
.rfd-cardbar {
  position: absolute;
  display: flex;
  gap: var(--space-1);
  padding: 2px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 2px 8px rgba(0,0,0,0.25);
  z-index: 3;
}
.rfd-cardbar[hidden] { display: none; }

.rfd-dialog {
  width: min(48rem, 94vw);
  padding: var(--space-6);
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.rfd-dialog::backdrop { background: rgba(0,0,0,0.55); }
.rfd-dialog-title { font-size: 1.1rem; font-weight: 700; margin-bottom: var(--space-4); }
.rfd-dialog-body { max-height: 68vh; overflow-y: auto; }
.rfd-dialog-foot { display: flex; gap: var(--space-3); margin-top: var(--space-6); }
.rfd-field { margin-bottom: var(--space-4); }
.rfd-label { display: block; font-size: 0.8rem; color: var(--text-muted); margin-bottom: var(--space-1); }
.rfd-rows, .rfd-grid { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-2); }
.rfd-row { display: flex; gap: var(--space-2); align-items: center; }
.rfd-row .form-input { flex: 1 1 0; min-width: 0; }
.rfd-grid-tools { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.rfd-head-cell { font-weight: 700; }
.rfd-file { display: block; margin-top: var(--space-2); color: var(--text-muted); }
/* Место под сообщение о загрузке держится всегда: диалог не должен прыгать
   на глазах, когда файл ушёл на сервер. */
.rfd-status { min-height: 1.2em; }

/* Оглавление поверхности: на странице справочника карточек ручек четыре
   десятка, и прокрутка — единственное, чем их иначе искать. Свёрнуто по
   умолчанию: короткой странице оно не нужно. */
.rfd-toc {
  margin-bottom: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
}
.rfd-toc[hidden] { display: none; }
.rfd-toc > summary {
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.rfd-toc-list {
  max-height: 16rem;
  overflow-y: auto;
  padding: 0 var(--space-2) var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.rfd-toc-item {
  text-align: left;
  padding: var(--space-1) var(--space-2);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}
.rfd-toc-item:hover { background: var(--bg-input); }
.rfd-toc-h2 { font-weight: 600; }
.rfd-toc-h3 { padding-left: var(--space-6); color: var(--text-muted); }
.rfd-toc-ep { padding-left: var(--space-6); font-family: ui-monospace, Consolas, monospace; font-size: 0.8rem; }
.rfd-toc-method { font-weight: 700; color: var(--accent-teal); }
/* Кнопка стоит в самой подсказке о непринятой разметке — там, где человек
   читает, что именно мешает. */
.rfd-clean { margin-left: var(--space-2); }

/* ─── Переключатель темы ────────────────────────────────────────────────
   Стоит в шапке кабинета и в боковой панели админки. Значок солнца плюс
   дорожка с бегунком: положение бегунка показывает, включена ли светлая. */
.theme-toggle{
  display:inline-flex;align-items:center;gap:8px;
  background:var(--bg-input);border:1px solid var(--border);
  border-radius:9px;padding:7px 10px;cursor:pointer;color:var(--text-muted);
  transition:border-color .18s,color .18s;
}
.theme-toggle:hover{border-color:var(--border-bright);color:var(--text)}
.theme-toggle svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round}
.tt-track{
  position:relative;width:30px;height:16px;border-radius:999px;
  background:var(--border);transition:background .18s;flex:none;
}
.tt-knob{
  position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;
  background:var(--text-faint);transition:transform .18s,background .18s;
}
html.light .tt-track{background:rgba(59,91,255,.25)}
html.light .tt-knob{transform:translateX(14px);background:var(--accent-teal)}
/* В боковой панели админки места меньше — подпись не нужна, значок и дорожка. */
.adm-userbox .theme-toggle{margin-right:auto}

/* ═══════════════════════════════════════════════════════════════════════
   СВЕТЛАЯ ТЕМА — цвета, записанные в правилах напрямую
   ═══════════════════════════════════════════════════════════════════════
   Переменные выше сами по себе страницу не осветляют: карточки, таблицы,
   сайдбары и поля залиты тёмными литералами (`.card`, `.cab-*`, `.adm-*`),
   и они перебивают палитру. Здесь эти литералы переводятся — по
   ПОВЕРХНОСТЯМ, а не по одному правилу: одна и та же заливка встречается
   в десятке мест, и точечная правка каждого разошлась бы при первой же
   новой странице.

   Блок стоит последним в файле: у `html.light .cab …` и у `.cab …` вес
   почти одинаковый, и спор решает порядок (см. память
   reference_cabinet_css_cab_scope_shadows — `.cab` здесь перебивает всё).

   Тёмная тема не тронута: без класса `light` ни одно правило отсюда не
   применяется, и тумблер возвращает ровно прежний вид.
   ═══════════════════════════════════════════════════════════════════════ */

/* Собственная палитра кабинета (/app). Держит сайдбар, панели и линии. */
html.light .cab {
  --cbg:        #f4f7fb;
  --cpanel:     #ffffff;
  --ccard:      #ffffff;
  --cline:      #e3e9f4;
  --cline-soft: #eef2f9;
  --cline-bright: rgba(59,91,255,.45);

  /* Шкала текста — та часть, без которой светлел только фон: значения
     кабинета рассчитаны на чёрный холст (--ctxt почти белый), и на белом
     подписи, суммы и даты выцветали до нечитаемых. */
  --ctxt:       #0f172a;
  --ctxt-dim:   #334155;
  --ctxt-mute:  #5b6584;
  --ctxt-faint: #94a3b8;

  /* Акценты: неон под чёрный фон, на белом даёт блёклую заливку. */
  --cblue-hi:   #3b5bff;
  --cgreen:     #16a34a;
  --cviolet:    #7c3aed;
  --camber:     #b45309;
}

/* ── Карточки и панели: белый лист вместо тёмного градиента ─────────── */
html.light .card,
html.light .cab .card,
html.light .cab-panel,
html.light .cab .plan-card,
html.light .cab .detail-card,
html.light .cab .support-channel,
html.light .cab .support-chat-card,
html.light .cab .table-responsive,
html.light .feature-card {
  background: #ffffff;
  border-color: var(--border);
}

/* Тень тёмной темы (0.35 чёрного) на белом читается как грязь. */
html.light .card,
html.light .cab .card,
html.light .cab-panel,
html.light .cab .plan-card,
html.light .cab .detail-card,
html.light .cab .support-channel,
html.light .cab .support-chat-card {
  box-shadow: 0 1px 2px rgba(15,23,42,0.04), 0 8px 24px rgba(15,23,42,0.06);
}

/* ── Таблицы ───────────────────────────────────────────────────────── */
html.light .table th,
html.light .cab-panel thead th,
html.light .cab .data-table th,
html.light .cab table.table th {
  background: #f6f8fc;
  color: var(--text-muted);
}
html.light .cab-panel tbody td,
html.light .cab .data-table td,
html.light .cab table.table td {
  border-bottom-color: var(--border-soft);
}

/* ── Сайдбары кабинета и админки ───────────────────────────────────── */
html.light .cab-side,
html.light .adm-side {
  background: #ffffff;
  border-right: 1px solid var(--border);
}
html.light .cab-userbox,
html.light .adm-userbox,
html.light .cab-balance-chip,
html.light .cab-stat,
html.light .cab .stat-card,
html.light .cab .notice,
html.light .cab-ref-code,
html.light .cab-ref-stat .rs,
html.light .cab-dl,
html.light .cab .pay-option,
html.light .cab .renew-term,
html.light .cab .referral-code-value,
html.light .cab .download-option-primary,
html.light .cab .support-card,
html.light .cab-support-card {
  background: #f6f8fc;
  border-color: var(--border);
}
html.light .cab-u-out:hover,
html.light .adm-u-out:hover,
html.light .cab-lang-opt.active,
html.light .adm-lang-opt.active {
  background: #e8edf9;
}
html.light .cab-avatar,
html.light .adm-avatar {
  background: linear-gradient(155deg, #5b7cff, #3b5bff);
  color: #ffffff;
}
html.light .cab-track { background: #e3e9f4; }
html.light .cab-copy  { background: #f6f8fc; }

/* ── Поля ввода и кнопки-призраки ──────────────────────────────────── */
html.light .cab input[type=text],
html.light .cab input[type=email],
html.light .cab input[type=password],
html.light .cab input[type=number],
html.light .cab select,
html.light .cab textarea {
  background: #ffffff;
  border-color: var(--border);
  color: var(--text);
}
html.light .cab .btn-ghost,
html.light .btn-ghost {
  background: #ffffff;
  border-color: var(--border);
  color: var(--text);
}
html.light .cab .btn-ghost:hover,
html.light .btn-ghost:hover { background: #f0f4fd; }

/* Заливка кнопки на светлой теме тёмная — тёмный текст на ней пропадает. */
html.light .btn-success,
html.light .btn-warning { color: #ffffff; }

/* ── Моноширинные блоки: команда установки, реквизиты, код ──────────── */
html.light .cab .install-oneliner,
html.light .cab .instructions-text,
html.light .docs-code {
  background: #0f172a;
  border-color: #1e293b;
  color: #e2e8f0;
}

/* ── Переписка с поддержкой ────────────────────────────────────────── */
html.light .cab .support-bubble {
  background: #f1f5fb;
  color: var(--text);
}
html.light .cab .support-msg-user .support-bubble {
  background: linear-gradient(180deg, #5b7cff, #3b5bff);
  color: #ffffff;
}

/* ── Значки с тёмным текстом на ярком фоне ─────────────────────────── */
html.light .cab-badge-pro,
html.light .docs-steps li::before,
html.light .docs-method { color: #ffffff; }
html.light .changelog-release:first-child .cl-ver,
html.light .contact-card-icon { color: #ffffff; }

/* ── Подвал ────────────────────────────────────────────────────────── */
html.light .footer-secure,
html.light .footer-social a {
  background: #f6f8fc;
  border-color: var(--border);
}

/* Шапка залита полупрозрачным чёрным (под blur), поэтому светлеет отдельно
   от переменных — иначе поверх белой страницы остаётся тёмная полоса. */
html.light .site-header {
  background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(255,255,255,0.72));
  border-bottom-color: var(--border);
}

/* ── Светлая тема: текст, написанный белым ─────────────────────────────
   Заголовки, значения и названия в кабинете залиты `#fff` напрямую —
   на белой карточке они исчезают. Белым остаётся только текст, лежащий
   на ПЛОТНОЙ цветной заливке (кнопка-акцент, счётчик-«таблетка»); там,
   где заливка полупрозрачная, он темнеет вместе с фоном. */
html.light .cab-brand b,
html.light .adm-brand b,
html.light .cab-pt-title,
html.light .cab-bc-v,
html.light .cab-stat-v,
html.light .cab-panel-h h3,
html.light .cab-lic-title,
html.light .cab-lm .v,
html.light .cab-oid,
html.light .cab-mini h3,
html.light .cab-ref-stat .rn,
html.light .cab-sec-h,
html.light .cab-dl-name,
html.light .cab .section-title,
html.light .cab .stat-value,
html.light .cab .detail-list dd,
html.light .cab .plan-name,
html.light .cab .plan-price,
html.light .cab .renew-term-dur,
html.light .cab .devices-section h2,
html.light .cab .referral-code-value,
html.light .cab .referral-user-label,
html.light .cab .download-plan,
html.light .cab .download-subhead,
html.light .cab .support-channel-name,
html.light .cab .support-msg-body,
html.light .cab .support-file-doc,
html.light .cab .payment-instructions h2,
html.light .cab .refund-section h2,
html.light .cab .apply-balance-section h3,
html.light .cab .seats-used,
html.light .cab .promo-saved p {
  color: var(--text);
}

/* Наведение и активный пункт: заливка полупрозрачная, текст должен темнеть. */
html.light .cab-nav-link:hover,
html.light .cab-userbox-guest:hover,
html.light .cab-u-out:hover,
html.light .adm-u-out:hover,
html.light .cab-copy:hover,
html.light .cab .btn-ghost:hover,
html.light .cab .btn-secondary:hover,
html.light .cab .btn-danger:hover,
html.light .cab .btn-warning:hover,
html.light .cab-lang-opt:hover { color: var(--text); }

html.light .cab-nav-link.active,
html.light .adm-nav-link.active,
html.light .cab-lang-opt.active,
html.light .adm-lang-opt.active { color: var(--accent-teal); }

/* Янтарь и розовый подобраны под чёрный фон и на белом не читаются. */
html.light .cab-stat-ic.amber,
html.light .cab-pill.wait,
html.light .cab .badge-pending,
html.light .cab .badge-awaiting_payment,
html.light .cab .notice-warning,
html.light .cab .refund-pending,
html.light .cab .btn-warning { color: var(--warning); }
html.light .cab .btn-danger,
html.light .cab .form-error { color: var(--error); }

/* Верхняя панель кабинета — тот же полупрозрачный чёрный, что и в шапке сайта. */
html.light .cab-topbar {
  background: linear-gradient(180deg, rgba(255,255,255,.92), rgba(255,255,255,.72));
  border-bottom-color: var(--border);
}

/* Знак и графика под тему: в разметке лежат оба варианта, лишний скрыт. */
.logo-on-light, .art-on-light { display: none; }
html.light .logo-on-dark, html.light .art-on-dark  { display: none; }
html.light .logo-on-light, html.light .art-on-light { display: block; }

/* Пустое состояние: пунктирная рамка и подложка тоже из тёмной палитры. */
html.light .cab .empty-state,
html.light .cab .info-state {
  background: #f8fafd;
  border-color: #d7e0f0;
}

/* Рамки, заданные тёмными литералами мимо --border. */
html.light .cab-u-out:hover,
html.light .cab-lang-opt.active,
html.light .adm-lang-opt.active,
html.light .cab-copy { border-color: var(--border); }
html.light .cab-ref-code,
html.light .cab .referral-code-value { border-color: #cbd6ea; }

/* ── Правки по замечаниям владельца ────────────────────────────────────
   Три разных корня, дававших один и тот же эффект «тема почти светлая»:
   вторичная кнопка осталась чёрной, пастельные плашки не читались на
   белом, а блоки команд выбивались тёмными пятнами. */

/* Вторичная кнопка кабинета («Просмотр», «Позже», ссылки загрузок) залита
   тёмным напрямую — на светлой теме она читалась как чужая. */
html.light .cab .btn-secondary {
  background: #ffffff;
  border-color: var(--border);
  color: var(--text);
}
html.light .cab .btn-secondary:hover {
  background: #f0f4fd;
  border-color: var(--border-focus);
}

/* Плашки: пастельные оттенки подобраны под чёрный фон и на белом почти
   сливаются с собственной подложкой. */
html.light .flash-success { color: #15803d; }
html.light .flash-error   { color: #b91c1c; }
html.light .flash-info    { color: #0369a1; }
html.light .form-error    { color: var(--error); }
html.light .tg-linked     { color: #15803d; }
html.light .analytics-unavailable { color: var(--warning); }

/* Блоки команд и реквизитов: на светлой странице тёмный «терминал»
   читается как дыра, поэтому и они становятся светлыми. */
html.light .cab .install-oneliner,
html.light .cab .instructions-text,
html.light .docs-code {
  background: #f1f5f9;
  border-color: #dbe3f0;
  color: #0f172a;
}
html.light .docs-code code { color: #0f172a; }
/* Сама команда лежит во вложенном <code> со своим цветом — он перебивает
   цвет блока, поэтому светлеет отдельно. */
html.light .cab .install-oneliner code { color: #0f172a; }

/* Подвал: собственные приглушённые тона тоже из тёмной палитры. */
html.light .footer-secure { color: var(--text-muted); }
html.light .footer-social a { color: var(--text-muted); }
html.light .footer-social a:hover { color: var(--text); }

/* ── Остаток бледной шкалы кабинета ────────────────────────────────────
   Значения ячеек, ключи, адреса и значки статусов заданы литералами мимо
   --ctxt*, поэтому переопределение переменных до них не дошло. */

/* Значения в таблицах и подписи — основной читаемый текст. */
html.light .cab-panel tbody td,
html.light .cab .data-table td,
html.light .cab table.table td,
html.light .cab-dl-os,
html.light .cab .download-os-title,
html.light .cab-userbox-name { color: var(--ctxt-dim); }

/* Моноширинные значения: ключ лицензии, идентификатор устройства, адрес. */
html.light .cab-keyrow code,
html.light .cab-ref-code code,
html.light .cab .license-key,
html.light .cab .device-id,
html.light .cab .ref-code,
html.light .cab .payout-address,
html.light .cab .ledger-ref { color: var(--text); }

/* Зелёные значки лежат на подложке в 10% — на белом она почти не видна,
   и весь контраст держит текст. */
html.light .cab-bc-ic,
html.light .cab-stat-ic.green,
html.light .cab-lm .v.g,
html.light .cab-pill.paid,
html.light .cab .renew-term-badge,
html.light .cab .support-channel-connected,
html.light .cab .refund-done { color: var(--cgreen); }

html.light .cab-pill.cancel { color: var(--ctxt-mute); }

/* Ссылки при наведении и значок канала: неоновый синий на белом блёкнет. */
html.light .cab-panel-h .cab-link:hover,
html.light .cab .back-link:hover,
html.light .cab .support-back:hover,
html.light .cab .btn-link:hover,
html.light .cab .support-channel-badge { color: var(--cblue-hi); }

html.light .cab-nav-link:hover svg { color: var(--ctxt-mute); }

/* ── Значки статусов и остаток подписей ────────────────────────────────
   Подложка значка — 8–12% цвета: на чёрном её держал светлый текст, на
   белом она почти исчезает, и читаемость целиком на цвете надписи. */
html.light .cab .badge-active,
html.light .cab .badge-paid,
html.light .cab .badge-credited,
html.light .cab .badge-completed,
html.light .cab .badge-done,
html.light .cab .badge-approved,
html.light .cab-badge-live,
html.light .cab-u-sub { color: var(--cgreen); }

html.light .cab .badge-cancelled,
html.light .cab .badge-expired,
html.light .cab .badge-superseded,
html.light .cab .badge-refunded,
html.light .cab .badge-inactive,
html.light .cab .badge-suspended,
html.light .cab .badge-rejected,
html.light .cab .badge-failed { color: var(--ctxt-mute); }

html.light .cab .badge-held_aml,
html.light .cab .badge-processing,
html.light .cab .badge-requested { color: var(--camber); }

html.light .cab-stat-ic.blue { color: var(--cblue-hi); }
html.light .cab-stat-ic.violet { color: var(--cviolet); }

/* Почта в блоке аккаунта и значения в полосе лицензии. */
html.light .cab-u-mail,
html.light .cab-lic-bar .row b { color: var(--ctxt-dim); }
html.light .cab-nav-link { color: var(--ctxt-mute); }

/* Янтарный значок: рамка и подложка достались от тёмной темы и были ярче
   собственной надписи — значок читался как наклейка, выбиваясь из ряда,
   где у зелёного и серого рамка приглушена под цвет текста. Теперь все
   три тона живут по одному правилу. */
html.light .cab .badge-pending,
html.light .cab .badge-awaiting_payment,
html.light .cab .badge-held_aml,
html.light .cab .badge-requested,
html.light .cab .badge-processing,
html.light .cab-pill.wait,
html.light .badge-pending {
  background: #fff8ec;
  border-color: rgba(180,83,9,.22);
  color: var(--warning);
}

/* ── Карточка лицензии ─────────────────────────────────────────────────
   Единственный блок, который оставался чёрным: его подложка — не
   переменная, а собственный тёмный градиент, а поверх лежала графика,
   нарисованная под чёрный фон. Теперь подложка светлая, а картинку
   подменяет пара .art-on-dark / .art-on-light. */
html.light .cab-license {
  background: linear-gradient(100deg, #f7f9ff 0%, #eef2ff 52%, #f4f1ff 100%);
  border-color: rgba(59,91,255,.20);
}
/* Свечение рассчитано на чёрный холст — на светлом даёт мутное пятно. */
html.light .cab-license .cab-glow {
  background: radial-gradient(closest-side, rgba(124,90,255,.10), transparent 72%);
}
/* Подписи-ключи в карточке и в строке лицензионного ключа. */
html.light .cab-lm .k,
html.light .cab-keyrow .kk { color: var(--ctxt-mute); }

/* Светлая картинка почти квадратная, тогда как тёмная — вытянутая: при
   общей посадке (высота 132%) её правый край срезало окно контейнера. */
html.light .cab-license .cab-art img {
  height: 124%;
  right: -18px;
}

/* ── Плитки статистики и загрузок ──────────────────────────────────────
   Им была дана та же серая заливка, что и вложенным блокам вроде блока
   аккаунта. На тёмной теме это работало: там любая подложка светлее
   холста и потому видна. На светлой холст сам серый (#f4f7fb), и плитки
   растворились в нём. Они — карточки верхнего уровня, значит белые и с
   тенью, как соседние панели. */
html.light .cab-stat,
html.light .cab-dl,
html.light .cab .stat-card {
  background: #ffffff;
  border-color: var(--border);
  box-shadow: 0 1px 2px rgba(15,23,42,0.04), 0 8px 24px rgba(15,23,42,0.06);
}
/* У плитки загрузки уже есть переход — отзыв на наведение делаем заметным. */
html.light .cab-dl:hover {
  border-color: rgba(59,91,255,.35);
  box-shadow: 0 2px 4px rgba(15,23,42,0.05), 0 12px 30px rgba(59,91,255,0.12);
}

/* ── Сплошная проверка остальных страниц кабинета ──────────────────────
   Прогон по всем правилам с фоном и цветом показал три недочёта, которые
   на просмотренных страницах просто не попадались на глаза. */

/* 1. Поле по классу .input (баланс, партнёрский раздел) осталось тёмным:
      светлели только поля, выбранные по типу — .input в тот список не
      попал, хотя объявлен с ними в одной строке. */
html.light .cab .input,
html.light .cab .input-sm {
  background: #ffffff;
  border-color: var(--border);
  color: var(--text);
}

/* 2. Общие значки и статусы вне `.cab` (админка, письма, списки релизов)
      держали пастель тёмной темы. Затемняем по смыслу цвета, а не по
      месту: один тон — одно правило. */
html.light .badge-active,
html.light .badge-paid,
html.light .badge-Paid,
html.light .badge-published,
html.light .status-active,
html.light .status-paid,
html.light .admin-dist .status-published { color: var(--success); }

html.light .badge-Declined,
html.light .badge-expired,
html.light .badge-suspended,
html.light .status-failed,
html.light .status-overdue,
html.light .status-revoked,
html.light .status-suspended { color: var(--error); }

html.light .badge-Partial,
html.light .badge-Pending,
html.light .badge-archived,
html.light .badge-warn,
html.light .status-awaiting,
html.light .status-awaiting_payment,
html.light .status-held_aml,
html.light .status-pending,
html.light .landing-code-warn { color: var(--warning); }

html.light .badge-Created,
html.light .badge-draft,
html.light .badge-inactive,
html.light .badge-internal,
html.light .cab .badge { color: var(--text-muted); }

html.light .badge-developer,
html.light .badge-trial { color: #6366f1; }
html.light .badge-scheduled { color: var(--info); }
html.light .status-open,
html.light .status-answered { color: #0d9488; }
html.light .status-refunded { color: #9333ea; }

/* Заливка активного пункта полупрозрачная — белая надпись на ней пропала бы. */
html.light .nav-link.active { color: var(--accent-teal); }

/* 3. Ползунок прокрутки в таблицах админки. */
html.light body.adm .table-responsive::-webkit-scrollbar-thumb { background: #c7d2e6; }
html.light body.adm .table-responsive::-webkit-scrollbar-thumb:hover { background: #a9b8d4; }

/* ── Статусы и значки: один язык оформления ────────────────────────────
   На тёмной теме статус — это плотная цветная заливка без рамки, и она
   там читается. На светлой такой приём даёт леденцовый ряд: заливки
   насыщеннее соседних значков, рамки нет, и два вида ярлыков на одной
   странице выглядят из разных систем.

   Приводим к принципу, принятому для значков кабинета: светлая подложка,
   приглушённая рамка в тон и насыщенная надпись. Группировка — по
   смыслу состояния, а не по имени класса. */
html.light .status-badge,
html.light .badge-status,
html.light .badge { border: 1px solid transparent; }

/* Успех: активно, оплачено, опубликовано. */
html.light .status-active,
html.light .status-paid,
html.light .badge-active,
html.light .badge-paid,
html.light .badge-Paid,
html.light .badge-published {
  background: #edfcf3; border-color: rgba(22,163,74,.22); color: var(--success);
}

/* Обращение в работе — бирюза, чтобы «открыт» не путался с «оплачен». */
html.light .status-open,
html.light .status-answered {
  background: #ecfdfa; border-color: rgba(13,148,136,.22); color: #0d9488;
}

/* Ожидание. Тон согласован с уже принятым янтарным значком. */
html.light .status-pending,
html.light .status-awaiting,
html.light .status-awaiting_payment,
html.light .status-held_aml,
html.light .badge-Partial,
html.light .badge-Pending,
html.light .badge-archived,
html.light .badge-warn {
  background: #fff8ec; border-color: rgba(180,83,9,.22); color: var(--warning);
}

/* Требует внимания: просрочено, отозвано, приостановлено, отклонено. */
html.light .status-failed,
html.light .status-overdue,
html.light .status-revoked,
html.light .status-suspended,
html.light .badge-expired,
html.light .badge-suspended,
html.light .badge-Declined {
  background: #fef4f4; border-color: rgba(220,38,38,.20); color: var(--error);
}

/* Возврат средств — отдельный смысл, отдельный тон. */
html.light .status-refunded {
  background: #faf5ff; border-color: rgba(147,51,234,.20); color: #9333ea;
}

/* Нейтральное: закрыто, черновик, неактивно. */
html.light .status-closed,
html.light .status-archived,
html.light .badge-Created,
html.light .badge-draft,
html.light .badge-inactive,
html.light .badge-internal,
html.light .cab .badge {
  background: #f4f7fb; border-color: var(--border); color: var(--text-muted);
}

html.light .badge-scheduled {
  background: #eff8ff; border-color: rgba(2,132,199,.20); color: var(--info);
}
html.light .badge-trial,
html.light .badge-developer {
  background: #f1f2fe; border-color: rgba(99,102,241,.22); color: #6366f1;
}

/* Внутри кабинета базовое правило `.cab .badge` весит больше одиночного
   `.badge-<состояние>` и гасило бы цвет до нейтрального. Поэтому смысловые
   группы повторяются с тем же охватом — иначе весь ряд значков серый. */
html.light .cab .badge-active,
html.light .cab .badge-paid,
html.light .cab .badge-credited,
html.light .cab .badge-completed,
html.light .cab .badge-done,
html.light .cab .badge-approved,
html.light .cab .badge-published {
  background: #edfcf3; border-color: rgba(22,163,74,.22); color: var(--success);
}
html.light .cab .badge-pending,
html.light .cab .badge-awaiting_payment,
html.light .cab .badge-held_aml,
html.light .cab .badge-requested,
html.light .cab .badge-processing,
html.light .cab .badge-archived,
html.light .cab .badge-warn {
  background: #fff8ec; border-color: rgba(180,83,9,.22); color: var(--warning);
}
html.light .cab .badge-expired,
html.light .cab .badge-suspended,
html.light .cab .badge-rejected,
html.light .cab .badge-failed {
  background: #fef4f4; border-color: rgba(220,38,38,.20); color: var(--error);
}
html.light .cab .badge-cancelled,
html.light .cab .badge-superseded,
html.light .cab .badge-refunded,
html.light .cab .badge-inactive,
html.light .cab .badge-draft {
  background: #f4f7fb; border-color: var(--border); color: var(--text-muted);
}
html.light .cab .badge-scheduled {
  background: #eff8ff; border-color: rgba(2,132,199,.20); color: var(--info);
}
html.light .cab .badge-trial,
html.light .cab .badge-developer {
  background: #f1f2fe; border-color: rgba(99,102,241,.22); color: #6366f1;
}

/* ── Переписка с поддержкой ────────────────────────────────────────────
   Цвет задаётся не пузырю, а тому, что внутри: подпись, тело и время —
   три отдельных правила. Поэтому «сделать пузырь синим и белым» мало:
   тело сообщения красилось общим правилом кабинета в тёмный и на синем
   пропадало, а подпись со временем оставались серыми. Ниже каждый из
   трёх слоёв назначается для обеих сторон переписки. */

/* Сообщение поддержки: светлая подложка, рамка отделяет её от карточки. */
html.light .cab .support-bubble {
  background: #f4f7fb;
  border-color: var(--border);
  border-radius: 14px 14px 14px 4px;
}
html.light .cab .support-msg-sender { color: var(--ctxt-mute); }
html.light .cab .support-msg-body   { color: var(--text); }
html.light .cab .support-msg-time   { color: var(--text-faint); }

/* Сообщение владельца аккаунта: плотная заливка — значит всё внутри белым,
   иначе получается тёмный текст на синем, как оно и выглядело. */
html.light .cab .support-msg-user .support-bubble {
  background: linear-gradient(180deg, #5b7cff, #3b5bff);
  border-color: transparent;
  border-radius: 14px 14px 4px 14px;
}
html.light .cab .support-msg-user .support-msg-sender { color: rgba(255,255,255,.72); }
html.light .cab .support-msg-user .support-msg-body   { color: #ffffff; }
html.light .cab .support-msg-user .support-msg-time   { color: rgba(255,255,255,.62); }
html.light .cab .support-msg-user .support-bubble a   { color: #ffffff; text-decoration: underline; }

/* Короткая реплика съёживалась до узкого столбика — минимум ширины
   не даёт пузырю разваливаться на «проверка» в три строки. */
html.light .cab .support-bubble { min-width: 132px; }
/* Небольшая тень отделяет реплики от белой карточки. */
html.light .cab .support-bubble { box-shadow: 0 1px 2px rgba(15,23,42,.05); }
html.light .cab .support-msg-user .support-bubble { box-shadow: 0 3px 10px rgba(59,91,255,.22); }

/* ── Список обращений ──────────────────────────────────────────────────
   Строки шли сплошняком, разделённые линией, а решённые целиком гасились
   прозрачностью — на белом это читается как «страница не догрузилась».
   Делаем строки карточками, а приглушаем не всю строку, а только её
   правую часть: тема обращения нужна и в закрытом. */
html.light .support-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
html.light .support-list-item {
  background: #ffffff;
  border: 1px solid var(--border);
  border-radius: 12px;
  transition: border-color .15s, box-shadow .15s;
}
html.light .support-list-item + .support-list-item { border-top: 1px solid var(--border); }
html.light .support-list-item:hover {
  border-color: rgba(59,91,255,.35);
  box-shadow: 0 2px 4px rgba(15,23,42,.04), 0 10px 24px rgba(59,91,255,.10);
}
html.light .support-list-item-resolved { opacity: 1; }
html.light .support-list-item-resolved .support-list-subject { color: var(--text-muted); }

html.light .support-list-link { padding: 14px 16px; }
html.light .support-list-link:hover { background: transparent; }
html.light .support-list-subject { font-weight: 600; color: var(--text); }

/* Статус — такой же ярлык, как в таблицах, а не строчка капсом. */
html.light .support-list-status {
  padding: 3px 10px;
  border-radius: 9999px;
  border: 1px solid transparent;
  font-weight: 700;
  letter-spacing: .02em;
}
html.light .support-list-status-open {
  background: #ecfdfa; border-color: rgba(13,148,136,.22); color: #0d9488;
}
html.light .support-list-status-resolved {
  background: #f4f7fb; border-color: var(--border); color: var(--text-muted);
}
html.light .support-list-date { color: var(--text-faint); }

/* ── Страница загрузок: блоки по системам ──────────────────────────────
   Все три системы лежали внутри одной белой карточки и разделялись только
   тонкой линией — на светлой теме граница между ними почти пропадала.
   Контейнер становится прозрачным, а каждая система — собственной белой
   плиткой с тенью на сером холсте страницы: тень на белом внутри белого
   объёма не даёт, здесь она работает. */
html.light .download-license.card {
  background: transparent;
  border: none;
  box-shadow: none;
  padding: 0;
}
html.light .download-license-head {
  padding: 0 4px 14px;
  border-bottom: none;
}
html.light .download-os {
  background: #ffffff;
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 22px 24px;
  box-shadow: 0 1px 2px rgba(15,23,42,.04), 0 8px 24px rgba(15,23,42,.06);
}
html.light .download-os + .download-os { margin-top: 18px; }
html.light .download-os-title {
  font-size: 1.05rem;
  color: var(--text);
}
/* Рекомендованный способ — выделенная врезка внутри плитки. */
html.light .cab .download-option-primary,
html.light .download-option-primary {
  background: #f6f9ff;
  border: 1px solid #e0e8fa;
  border-radius: 12px;
  padding: 16px 18px;
}

/* ── Выбор языка в верхней панели кабинета ─────────────────────────────
   Раньше жил внизу боковой панели тремя ссылками. В шапке ему нужен
   компактный вид, поэтому это select, оформленный под соседние кнопки. */
.cab-lang-select {
  /* Высота НЕ задаётся: select считает её от строки сам, а с фиксированной
     высотой текст обрезался снизу. */
  padding: 7px 30px 7px 12px;
  border-radius: 10px;
  border: 1px solid var(--cline);
  background: transparent;
  color: var(--ctxt-dim);
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  appearance: none;
  /* Стрелка нарисована фоном: нативную у select не покрасить под тему. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%238b96b5' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 11px;
}
.cab-lang-select:hover { border-color: var(--cline-bright); }
.cab-lang-select:focus-visible { outline: 2px solid var(--cblue); outline-offset: 1px; }

html.light .cab-lang-select {
  background-color: #ffffff;
  border-color: var(--border);
  color: var(--text);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%2364748b' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
html.light .cab-lang-select:hover { border-color: var(--border-focus); }

/* ── Кнопка копирования внутри блока кода ──────────────────────────────
   Раньше она стояла у заголовка секции, в отрыве от того, что копирует, —
   по кнопке нельзя было понять, какая из двух команд уедет в буфер. */
.code-block { position: relative; }
.code-block .install-oneliner { margin: 0; padding-right: 52px; }
.code-copy {
  position: absolute;
  top: 8px;
  right: 8px;
  /* Видна сразу, иначе про неё не узнают: подсказка появляется только при
     наведении, а копировать команду хочется с первого взгляда. */
  opacity: .85;
  transition: opacity .15s, border-color .15s;
}
.code-block:hover .code-copy { opacity: 1; }
.code-copy:focus-visible { opacity: 1; }
html.light .code-copy {
  background: #ffffff;
  border: 1px solid var(--border);
  color: var(--text-muted);
}
html.light .code-copy:hover { color: var(--text); border-color: var(--border-focus); }

/* Версия сборки: строка «Версия 1.3.1» терялась в сером углу карточки,
   хотя это первое, что сверяют перед установкой. */
.download-version {
  display: inline-block;
  margin-left: 6px;
  padding: 3px 10px;
  border-radius: 9999px;
  font-weight: 800;
  letter-spacing: .01em;
}
html.light .download-version {
  background: #eef2ff;
  border: 1px solid rgba(59,91,255,.22);
  color: var(--accent-teal);
}
html.light .download-license-head .download-meta { color: var(--text-muted); }

/* ═══ Аналитика (/admin/analytics) ══════════════════════════════════════
   Было: восемь секций подряд, каждая — таблица во всю ширину; при одной-двух
   строках страница тянулась на несколько экранов, и сравнить разрезы между
   собой было невозможно — они не помещались в один взгляд. Теперь разрезы
   идут карточками в сетке, а доля внутри строки показана полосой: она и
   отвечает на вопрос «что преобладает», без чтения чисел. */
.an-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;margin-top:20px}
.an-grid-wide{grid-template-columns:repeat(2,minmax(0,1fr))}
@media (max-width:1100px){ .an-grid, .an-grid-wide{grid-template-columns:1fr} }

.an-card{
  border-radius:14px;padding:16px 18px 12px;
  border:1px solid var(--border);background:var(--bg-card);
}
.an-card-h{margin:0 0 12px;font-size:15px;font-weight:700;letter-spacing:-.01em}

/* Шапка колонок: та же сетка, что и у строки, иначе числа разъезжаются. */
/* Одна сетка на шапку и строки. Колонки чисел шире подписей «Просмотры» и
   «Уникальные»: с прежними 52/44 px заголовки не влезали и наезжали друг на
   друга. */
.an-head,.an-row{
  display:grid;grid-template-columns:minmax(0,1fr) 70px 92px 96px 44px;
  align-items:center;gap:10px;
}
.an-head{
  padding:0 0 8px;border-bottom:1px solid var(--border-soft);
  /* Разрядка меньше обычной для мелких подписей: с .09em русские
     «Просмотры» и «Уникальные» не влезали и обрезались многоточием. */
  font-size:10.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;
  color:var(--text-faint);
}
/* Раскладка ячеек шапки больше не подпирается grid-column: в разметке у неё
   те же пять ячеек, что и у строки, включая пустые под полосу и процент. */
.an-head>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.an-head-days{grid-template-columns:1fr;justify-items:end}

.an-rows{list-style:none;margin:0;padding:0}
.an-row{padding:9px 0;border-bottom:1px solid var(--border-soft);font-size:13px}
.an-row:last-child{border-bottom:none}
.an-key{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.an-num{text-align:right;font-variant-numeric:tabular-nums;font-weight:600}
.an-dim{color:var(--text-muted);font-weight:500}
.an-pct{text-align:right;font-size:12px;color:var(--text-muted);font-variant-numeric:tabular-nums}

/* Полоса доли — единственный элемент, который читается боковым зрением. */
.an-bar{height:6px;border-radius:4px;background:var(--border-soft);overflow:hidden}
.an-bar i{display:block;height:100%;border-radius:4px;background:var(--accent-teal)}

.an-empty{padding:14px 0;color:var(--text-muted);font-size:13px}
.an-bots{display:flex;gap:18px;margin-bottom:10px;font-size:13px;color:var(--text-muted)}
.an-bots b{color:var(--text);font-size:15px}

/* Предупреждение о неполной сборке на странице релизов: янтарь под чёрный
   фон, на белом не читается. (.btn-play в этот список не входит намеренно —
   он живёт только в блочном лендинге, который остаётся тёмным.) */
html.light .admin-dist .dist-incomplete-warning { color: var(--warning); }

/* ═══ Админка: секции как карточки ══════════════════════════════════════
   На тёмной теме блоки разделялись сами — фон панели был темнее холста. На
   светлой холст белый, и страница читалась как текст, разбросанный по
   пустому листу: заголовки, пары «ключ — значение» и одиночные кнопки висели
   без опоры.

   Карточка навешивается по СТРУКТУРЕ, а не по классу: у каждой страницы
   обёртки называются по-своему (.suspicious-params, .analytics-section и так
   далее), общего класса у них нет, зато все начинаются с .section-title.
   :has даёт зацепиться за это, не правя полсотни шаблонов. */
html.light body.adm .admin-form,
html.light body.adm div:has(> .section-title) {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 18px 20px;
  margin-bottom: 18px;
}
/* Вложенную обёртку второй раз в рамку не берём — иначе карточка в карточке. */
html.light body.adm div:has(> .section-title) div:has(> .section-title),
html.light body.adm div:has(> .section-title) .admin-form {
  background: none;
  border: none;
  padding: 0;
  margin-bottom: 0;
}
html.light body.adm .section-title {
  margin: 0 0 14px;
  color: var(--text);
  font-size: 12px;
}
/* Пары «ключ — значение»: без разделителей строки сливались в столбик текста. */
/* Промежуток между колонками убран, а воздух даёт padding у ключа: с
   column-gap разделительная линия рвалась ровно посередине строки. */
html.light body.adm .detail-list {
  gap: 0;
}
html.light body.adm .detail-list > dt,
html.light body.adm .detail-list > dd {
  padding: 9px 0;
  border-bottom: 1px solid var(--border-soft);
}
html.light body.adm .detail-list > dt {
  color: var(--text-muted);
  padding-right: var(--space-8);
}
html.light body.adm .detail-list > dd { font-weight: 600; }
/* Таблица на белом холсте сливалась с ним: своего фона ей мало, нужна рамка. */
html.light body.adm .table-responsive {
  border: 1px solid var(--border);
  border-radius: 14px;
  /* 🔴 По оси X — auto, и никогда overflow: hidden. Скругление углов просят
     закрыть обрезкой по обеим осям, и та обрезка перебивает базовый
     `.table-responsive { overflow-x: auto }`: широкий реестр обрезается
     МОЛЧА, без скроллбара, и всё, что правее сгиба, становится недостижимо.
     Реестры панели идут от девяти колонок и шире экрана по построению, так
     что это не крайний случай, а обычный. Углы всё равно обрезаны: когда по
     одной оси не visible, браузер считает обрезку и по второй. */
  overflow-x: auto;
  overflow-y: hidden;
}
/* Пустое состояние — не просто серая строка посреди листа. */
html.light body.adm .empty-state {
  border: 1px dashed #d7e0f0;
  border-radius: 12px;
  background: #f8fafd;
  text-align: center;
}

/* Кнопка без модификатора (btn без btn-primary) в админке светлой темы
   оставалась серой заливкой без границ и читалась как неактивная. */
html.light body.adm .btn:not([class*="btn-"]) {
  background: var(--bg-card);
  border: 1px solid var(--border);
  color: var(--text);
  font-weight: 600;
}
html.light body.adm .btn:not([class*="btn-"]):hover {
  border-color: var(--border-focus);
  filter: none;
}
