/* @file: src/public/app.css
 * @description: Единый дизайн сервиса a.rt7.ru.
 *
 *   Один файл на все страницы: главная, кабинет, визитки, страницы ссылок
 *   (пароль и скачивание), страницы ошибок, демо, оплата, админка.
 *
 *   Тема: светлая по умолчанию. Тёмная включается атрибутом
 *   <html data-theme="dark"> — переключатель ставит его из localStorage
 *   (см. inline-скрипт темы в шапке страниц и /static/app.js).
 *
 *   Правило проекта: цвета, отступы и радиусы берутся ТОЛЬКО из переменных
 *   ниже. Так правка дизайна делается в одном месте, а не в 12 файлах.
 *
 * @author: Дима
 * @date: 2026-10-08
 */

/* ============================================================
   1. ТОКЕНЫ
   ============================================================ */

:root {
  /* Поверхности и фон */
  --bg: #f5f7fb;
  --bg-soft: #eef1f8;
  --surface: #ffffff;
  --surface-2: #f9fafb;
  --surface-3: #f3f4f8;
  /* Подложка активной вкладки. В обеих темах она светлее дорожки .tabs
     (--surface-2), поэтому выбранная вкладка читается как «выдвинутый»
     сегмент, а не как залитая акцентом кнопка */
  --tab-active: #ffffff;

  /* Границы */
  --border: #e5e7ef;
  /* Границы, которые должны быть видны (поле ввода, контур «призрачной» кнопки):
     контраст к белому 3.29:1 — по WCAG 1.4.11 нужно минимум 3:1.
     Было #d3d7e3 (1.44:1) — поле «сливалось» с карточкой */
  --border-strong: #868e9e;

  /* Текст */
  --text: #111827;
  /* 5.82:1 на белом, 5.43:1 на фоне страницы. Было #667085 — 4.64:1 впритык */
  --text-muted: #5b6676;
  /* 5.04:1 на белом, 4.59:1 на самой тёмной подложке. Было #98a1b3 — 2.36…2.60,
     ниже нормы WCAG AA: футер, placeholder и disabled-поля читались плохо */
  --text-soft: #64707f;
  /* Текст на залитом акцентом фоне. В тёмной теме акцент светлый, поэтому текст
     на нём — тёмный. Кнопки и плашки обязаны брать цвет отсюда, а не белый */
  --on-primary: #ffffff;

  /* Акцент */
  --primary: #4f46e5;
  --primary-hover: #4338ca;
  --primary-soft: #eef2ff;
  --primary-border: #c7d2fe;

  /* Статусы. Значения темнее прежних: белый текст на заливке и цветной текст на
     светлой подложке либо проходили впритык, либо не проходили WCAG AA
     (было 3.77:1 у success и 4.41:1 у плашки danger) */
  --danger: #b91c1c;
  --danger-hover: #991b1b;
  --danger-soft: #fef2f2;
  --danger-border: #fecaca;

  --success: #047857;
  --success-hover: #036b4f;
  --success-soft: #ecfdf5;
  --success-border: #a7f3d0;

  --warning: #b45309;
  --warning-soft: #fffbeb;
  --warning-border: #fde68a;

  /* Telegram: цвет бренда был вписан прямо в кнопку (#229ed9) и давал белый
     текст с контрастом 3.02:1 — ниже нормы. Теперь это обычный токен темы */
  --telegram: #1976a8;
  --telegram-hover: #14618b;

  /* Тени */
  --shadow-xs: 0 1px 2px rgba(16, 24, 40, 0.05);
  --shadow-sm: 0 1px 3px rgba(16, 24, 40, 0.08), 0 1px 2px rgba(16, 24, 40, 0.04);
  --shadow: 0 4px 16px rgba(16, 24, 40, 0.08);
  --shadow-lg: 0 12px 32px rgba(16, 24, 40, 0.12);

  /* Радиусы */
  --r-xs: 6px;
  --r-sm: 8px;
  --r: 12px;
  --r-lg: 16px;
  --r-xl: 22px;
  --r-pill: 999px;

  /* Отступы: базовая шкала, чтобы «ничего не прижималось» */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 20px;
  --s-6: 24px;
  --s-7: 32px;
  --s-8: 40px;

  /* Типографика */
  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --fs-sm: 13px;
  --fs-base: 16px;
  --fs-lg: 18px;
  --fs-xl: 22px;
  --fs-2xl: 28px;

  /* Ширина контента */
  --w-narrow: 460px;
  --w-page: 720px;
  --w-wide: 1080px;

  /* Слои: единая шкала вместо разрозненных 50/100/200/100000 */
  --z-dropdown: 10;
  --z-sticky: 50;
  --z-modal: 100;
  --z-toast: 200;

  --transition: 0.18s ease;
}

/* Тёмная тема: переопределяем только переменные */
html[data-theme='dark'] {
  --bg: #0e1220;
  --bg-soft: #141a2c;
  --surface: #171e30;
  --surface-2: #1d2540;
  --surface-3: #232c48;
  /* Светлее дорожки .tabs (--surface-2): выбранная вкладка «выступает» над ней */
  --tab-active: #232c48;

  --border: #2b3450;
  /* 3.09:1 к карточке — по WCAG 1.4.11 нужно 3:1 (было #3a4664 — 1.77:1) */
  --border-strong: #5d6a8c;

  --text: #e9edf7;
  --text-muted: #a4adc4;
  /* 5.53:1 на карточке, 4.59:1 на самой светлой подложке
     (было #7d879e — 3.82:1, ниже нормы) */
  --text-soft: #8b95ad;
  /* Тёмный текст на светлых кнопках: у акцента, success и danger в тёмной теме
     светлые заливки, поэтому белый текст на них нечитаем (1.92…2.96:1) */
  --on-primary: #0e1220;

  --primary: #7f8dfb;
  --primary-hover: #98a3ff;
  --primary-soft: #1f2744;
  --primary-border: #3b4670;

  --danger: #f87171;
  --danger-hover: #fca5a5;
  --danger-soft: #2a1720;
  --danger-border: #5c2a33;

  --success: #34d399;
  --success-hover: #6ee7b7;
  --success-soft: #10251d;
  --success-border: #1e4d3b;

  --warning: #fbbf24;
  --warning-soft: #2a2110;
  --warning-border: #5c4a1a;

  /* Telegram в тёмной теме светлее: текст на кнопке — --on-primary */
  --telegram: #3fa9d9;
  --telegram-hover: #5cb8e2;

  --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.45);
  --shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
  --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.55);

color-scheme: dark;
}
  /* ============================================================
   2. БАЗА
   ============================================================ */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: 1.55;
  color: var(--text);
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { line-height: 1.25; font-weight: 700; color: var(--text); }
h1 { font-size: var(--fs-2xl); }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-lg); }
h4 { font-size: var(--fs-base); }

a { color: var(--primary); }
a:hover { color: var(--primary-hover); }

img { max-width: 100%; height: auto; }

code, kbd { font-family: var(--font-mono); font-size: 0.92em; }

:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: var(--r-xs); }
::selection { background: var(--primary-soft); }

/* Скрытый, но доступный для скринридеров текст */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ------------------------------------------------------------
   Иконки (/static/icons.js): тонкая обводка, цвет — как у текста.
   Разметка: <i data-icon="link"></i> — скрипт подставит SVG.
   Размер по умолчанию 18px, толщина обводки 1.75 задаётся в самом SVG.
   ------------------------------------------------------------ */

.icon {
  display: inline-block;
  flex: 0 0 auto;
  vertical-align: -0.16em; /* иконка садится на строку текста, а не висит на базовой линии */
  color: inherit;
  pointer-events: none;      /* клик всегда достаётся кнопке, а не иконке внутри неё */
}

/* Иконка сама себе не задаёт отступ: расстояние держит контейнер (gap) */
.btn .icon, .tab .icon, .menu__item .icon, .badge .icon, .sticker .icon { vertical-align: -0.14em; }

/* Внутри плашек и подсказок иконка заметно мельче текста вокруг */
.badge .icon, .sticker .icon { width: 13px; height: 13px; }

[hidden] { display: none !important; }

/* ============================================================
   3. КАРКАС СТРАНИЦЫ
   ============================================================ */

.page {
  width: 100%;
  max-width: var(--w-page);
  margin: 0 auto;
  padding: var(--s-6) var(--s-4) var(--s-7);
  flex: 1 0 auto;
}

.page--wide { max-width: var(--w-wide); }
.page--narrow { max-width: var(--w-narrow); }
.page--center { display: flex; flex-direction: column; justify-content: center; }

/* Шапка страницы: та же ширина и те же боковые отступы, что у .page, поэтому
   логотип и заголовок стоят ровно над содержимым, а не растягиваются на экран.
   .site-header--wide / --narrow прижимают её к .page--wide / .page--narrow */
.site-header {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  width: 100%;
  max-width: var(--w-page);
  margin: 0 auto var(--s-5);
  padding: var(--s-6) var(--s-4) 0;
}
.site-header--wide { max-width: var(--w-wide); }
.site-header--narrow { max-width: var(--w-narrow); }
.site-header__title { display: flex; align-items: center; gap: var(--s-3); min-width: 0; flex: 1 1 auto; }
/* Заголовок обрезается многоточием: длинное имя визитки или адрес страницы
   иначе выдавливал кнопки из шапки на телефоне (A4) */
.site-header__title h1 { font-size: var(--fs-xl); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.site-header__title > .badge { flex: 0 0 auto; }
.site-header__actions { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin-left: auto; flex: 0 0 auto; }
/* «⋯» в шапке: на десктопе все действия видны кнопками, на телефоне второстепенные
   прячутся в это меню — иначе они уезжали под заголовок отдельным рядом.

   Селектор из двух классов, а не один .header-more: у самого .menu ниже стоит
   display: inline-flex, и при равной точности правила спорят по порядку в файле.
   «.menu» шло позже и побеждало, поэтому меню «⋯» показывалось и на компьютере:
   там оно висело рядом с уже видимой кнопкой «Выйти», и в меню была та же самая
   «Выйти» — понять, одно это действие или разные, было нельзя. Два класса в
   селекторе точнее одного, поэтому правило перестаёт зависеть от порядка */
.menu.header-more { display: none; }

/* Логотип-домик и любые иконки-кнопки */
.logo-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--r);
  background: var(--primary-soft);
  border: 1px solid var(--primary-border);
  color: var(--primary);
  text-decoration: none;
  font-size: var(--fs-lg);
  transition: background var(--transition), color var(--transition), transform var(--transition);
}

.logo-btn:hover { background: var(--primary); color: var(--on-primary); transform: translateY(-1px); }
.logo-btn svg { width: 20px; height: 20px; }

/* Футер */
.site-footer {
  flex: 0 0 auto;
  width: 100%;
  max-width: var(--w-page);
  margin: 0 auto;
  padding: var(--s-5) var(--s-4) var(--s-6);
  text-align: center;
  color: var(--text-soft);
  font-size: var(--fs-sm);
}

/* Футер под широкой/узкой страницей: та же ширина, что у .page--wide / .page--narrow */
.site-footer--wide { max-width: var(--w-wide); }
.site-footer--narrow { max-width: var(--w-narrow); }

.site-footer a { color: var(--text-muted); text-decoration: none; }
.site-footer a:hover { color: var(--text); text-decoration: underline; }
.site-footer__sep { margin: 0 var(--s-2); opacity: 0.5; }

/* ============================================================
   4. КНОПКИ
   ============================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: 11px var(--s-5);
  border: 1px solid transparent;
  border-radius: var(--r);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-base);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}

.btn:active { transform: translateY(1px); }
.btn:disabled, .btn[aria-disabled='true'] { opacity: 0.55; cursor: not-allowed; transform: none; }

/* Основная. Текст — из --on-primary, а не белый: в тёмной теме акцент светлый,
   и белая надпись на нём давала контраст 2.96:1 вместо 6.31:1 */
.btn--primary { background: var(--primary); color: var(--on-primary); }
.btn--primary:hover:not(:disabled) { background: var(--primary-hover); color: var(--on-primary); }

/* Нейтральная с рамкой */
.btn--ghost { background: var(--surface); color: var(--text); border-color: var(--border-strong); }
.btn--ghost:hover:not(:disabled) { background: var(--surface-3); border-color: var(--primary-border); color: var(--text); }

/* Смысловые. В тёмной теме заливки светлые — текст тёмный (--on-primary):
   было 1.92:1 у success и 2.77:1 у danger */
.btn--success { background: var(--success); color: var(--on-primary); }
.btn--success:hover:not(:disabled) { background: var(--success-hover); color: var(--on-primary); }

.btn--danger { background: var(--danger); color: var(--on-primary); }
.btn--danger:hover:not(:disabled) { background: var(--danger-hover); color: var(--on-primary); }

.btn--danger-ghost { background: var(--surface); color: var(--danger); border-color: var(--danger-border); }
.btn--danger-ghost:hover:not(:disabled) { background: var(--danger-soft); color: var(--danger); }

.btn--telegram { background: var(--telegram); color: var(--on-primary); }
.btn--telegram:hover:not(:disabled) { background: var(--telegram-hover); color: var(--on-primary); }

/* Размеры */
.btn--sm { padding: 7px var(--s-3); font-size: var(--fs-sm); border-radius: var(--r-sm); }
.btn--icon { padding: 0; width: 36px; height: 36px; }
.btn--icon.btn--sm { width: 32px; height: 32px; }
.btn--block { width: 100%; }
/* Кнопка «Сохранить всё» в настройках админки: шире обычных, чтобы подпись
   не переносилась на две строки */
.btn--save-all { min-width: 200px; }

/* Группа кнопок */
.btn-row { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }
.btn-col { display: flex; flex-direction: column; gap: var(--s-2); }
.btn-col > .btn { width: 100%; }

/* Ряд кнопок одной ширины: сетка равных долей вместо flex-строки.
 *
 * Зачем отдельный класс. В flex ширина кнопки зависит от подписи, и панель
 * действий в блоке результата «плясала»: «Скопировать» шире «Открыть», а
 * «Создать ещё» (единственная кнопка без иконки) уже обоих. Неровные края в ряду
 * кнопок читаются как небрежность — а это первое, что видит человек после
 * создания ссылки.
 *
 * Колонки равные по построению (1fr), а их число выбирает браузер по ширине
 * места: на широком экране все кнопки в одну строку, на телефоне — по две в ряд.
 * auto-fit, а не auto-fill: пустые дорожки схлопываются. Это важно, потому что
 * кнопок может быть три — «Поделиться» показывается только там, где браузер умеет
 * системное меню; тогда оставшиеся три занимают строку целиком, а не оставляют
 * дырку на месте четвёртой.
 *
 * align-items: stretch — чтобы кнопки совпали ещё и по высоте: подпись без иконки
 * даёт строку на пиксель ниже, чем подпись с иконкой. grid-auto-rows: 1fr делает
 * одинаковыми и сами строки: иначе нижняя строка (одна кнопка) была бы на пиксель
 * ниже верхней — на глаз это «прыгающая» панель
 *
 * minmax(8.5rem, 1fr) — минимум по самой длинной подписи с иконкой
 * («Скопировать»): ниже подпись начала бы обрезаться, а ужать отступы кнопки
 * значит сломать её вид на остальных страницах */
.btn-row--equal {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  grid-auto-rows: 1fr;
  align-items: stretch;
}

/* Иконка-кнопка в шапке (в т.ч. переключатель темы) */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--text-muted);
  font-size: var(--fs-lg);
  font-family: inherit;
  line-height: 1;
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.icon-btn:hover { background: var(--surface-3); color: var(--text); border-color: var(--primary-border); }

/* ============================================================
   5. ФОРМЫ
   ============================================================ */

.field { margin-bottom: var(--s-5); }
.field:last-child { margin-bottom: 0; }

.label, label.label {
  display: block;
  margin-bottom: var(--s-2);
  color: var(--text);
  font-size: var(--fs-base);
  font-weight: 600;
}

.label__optional { color: var(--text-soft); font-weight: 400; font-size: var(--fs-sm); }

.input,
.select,
input[type='text'],
input[type='password'],
input[type='email'],
input[type='url'],
input[type='number'],
input[type='search'],
input[type='date'],
input[type='tel'],
select,
textarea {
  width: 100%;
  padding: 12px var(--s-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-base);
  line-height: 1.4;
  transition: border-color var(--transition), box-shadow var(--transition), background var(--transition);
}

.input::placeholder, textarea::placeholder { color: var(--text-soft); }

.input:focus, .select:focus,
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-soft);
}

input:disabled, select:disabled, textarea:disabled {
  background: var(--surface-3);
  color: var(--text-soft);
  cursor: not-allowed;
}

select {
  appearance: none;
  padding-right: 38px;
  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='%23667085' d='M1.4 1.4 6 6l4.6-4.6 1 1L6 8 0.4 2.4z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
}

textarea { resize: vertical; min-height: 96px; }

.field--invalid .input,
.field--invalid input, .field--invalid textarea, .field--invalid select {
  border-color: var(--danger);
  background: var(--danger-soft);
}

.hint { margin-top: var(--s-2); color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.45; }

.field-error {
  display: none;
  margin-top: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--danger-border);
  border-radius: var(--r-sm);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: var(--fs-sm);
  line-height: 1.45;
}

.field-error.show { display: block; }

/* Чекбокс-строка */
.check {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--s-2);
  font-size: var(--fs-base);
  font-weight: 400;
  color: var(--text);
  cursor: pointer;
}

.check input[type='checkbox'] {
  width: 18px; height: 18px; margin-top: 2px; flex: 0 0 auto;
  accent-color: var(--primary); cursor: pointer;
}

.check__text { min-width: 0; }
.check__sub { display: block; color: var(--text-muted); font-size: var(--fs-sm); font-weight: 400; }

/* Строка «поле + кнопка» */
.field-row { display: flex; gap: var(--s-2); align-items: stretch; }
.field-row > .input, .field-row > input, .field-row > select { flex: 1 1 auto; min-width: 0; }
.field-row > .btn { flex: 0 0 auto; }

/* Префикс перед полем (например a.rt7.ru/a/) */
.input-group { display: flex; align-items: stretch; gap: var(--s-2); flex-wrap: wrap; }
.input-group__prefix {
  display: inline-flex; align-items: center;
  padding: 0 var(--s-1);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  white-space: nowrap;
}
.input-group > .input, .input-group > input { flex: 1 1 200px; min-width: 0; }

/* ============================================================
   6. ПАНЕЛИ И РАЗДЕЛЫ
   ============================================================ */

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--s-6);
  box-shadow: var(--shadow-sm);
}

.panel + .panel { margin-top: var(--s-5); }

/* Раздел формы внутри панели/страницы: отделён отступом и рамкой */
.section {
  padding: var(--s-5);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
}

.section + .section { margin-top: var(--s-5); }

.section__head { margin-bottom: var(--s-4); }
.section__head h2, .section__head h3 { font-size: var(--fs-lg); margin-bottom: var(--s-1); }
.section__head p { color: var(--text-muted); font-size: var(--fs-sm); }

/* Сводка (цифры) */
.summary-card {
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface-2);
}

/* Пустое состояние */
.empty {
  padding: var(--s-8) var(--s-4);
  text-align: center;
  color: var(--text-muted);
}
.empty__icon { font-size: 40px; line-height: 1; margin-bottom: var(--s-3); }
.empty__title { font-size: var(--fs-lg); font-weight: 600; color: var(--text); margin-bottom: var(--s-2); }
.empty .btn { margin-top: var(--s-4); }

/* ============================================================
   7. ВКЛАДКИ
   ============================================================ */

.tabs {
  display: flex;
  gap: var(--s-1);
  padding: var(--s-1);
  margin-bottom: var(--s-5);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface-2);
  overflow-x: auto;
}

.tab {
  flex: 1 1 0;
  min-width: max-content;
  padding: 11px var(--s-4);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-muted);
  font-family: inherit;
  font-size: var(--fs-base);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  /* Иконка и подпись держатся одной строкой: без flex иконка уезжала
     на строку выше текста */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition);
}

.tab:hover:not(.active) { background: var(--surface-3); color: var(--text); }

/* Активная вкладка — «выдвинутый» сегмент: светлее дорожки, с акцентной
   рамкой и акцентной подписью. Раньше была плотная заливка акцентом: она
   перетягивала внимание на себя и не сочеталась с плашками нового стиля */
.tab.active {
  background: var(--tab-active);
  border-color: var(--primary-border);
  color: var(--primary);
  box-shadow: var(--shadow-sm);
}
.tab.active:hover { border-color: var(--primary); color: var(--primary); }
/* Иконка активной вкладки — в цвет подписи, а не в цвет текста */
.tab.active .icon { color: var(--primary); }

.tab-content { display: none; }
.tab-content.active { display: block; }

/* Панель вкладки на главной: переключается атрибутом hidden */
.tab-pane[hidden] { display: none; }

/* ============================================================
   8. БЕЙДЖИ, УВЕДОМЛЕНИЯ, ПОДСКАЗКИ
   ============================================================ */

/* Плашка статуса: небольшой скруглённый прямоугольник с тонкой рамкой.
   Рядом с текстом читается как «стикер» и не спорит с содержимым страницы */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid var(--primary-border);
  border-radius: 8px;
  background: var(--primary-soft);
  color: var(--primary);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}

.badge--ok { border-color: var(--success-border); background: var(--success-soft); color: var(--success); }
.badge--off { border-color: var(--danger-border); background: var(--danger-soft); color: var(--danger); }
.badge--muted { border-color: var(--border); background: var(--surface-2); color: var(--text-muted); }
.badge--draft { border-color: var(--warning-border); background: var(--warning-soft); color: var(--warning); }
/* Срок вышел: ссылка уже не работает, но удалять её пользователь не спешит */
.badge--expired { border-color: var(--warning-border); background: var(--warning-soft); color: var(--warning); }

/* Плашка-свойство: сообщает не статус, а характеристику (пароль, QR, срок).
   Спокойнее статуса: серая рамка, без цветной заливки. Иконка — тонкая. */
.sticker {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--text-soft);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.5;
  white-space: nowrap;
}

.notice {
  display: none;
  padding: var(--s-4);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface-2);
  font-size: var(--fs-base);
}

.notice.show { display: block; }
.notice--error { border-color: var(--danger-border); background: var(--danger-soft); color: var(--danger); }
.notice--success { border-color: var(--success-border); background: var(--success-soft); color: var(--success); }
.notice--info { border-color: var(--primary-border); background: var(--primary-soft); color: var(--primary); }
.notice--warn { border-color: var(--warning-border); background: var(--warning-soft); color: var(--warning); }
.notice ul { margin: var(--s-2) 0 0 var(--s-4); }

/* Всплывающие сообщения (тосты) — /static/ui.js → UI.toast().
   Виджета раньше не было вообще: вместо него использовались alert() и плашка
   .notice вверху страницы, которую на телефоне закрывала клавиатура.
   На телефоне стек показывается сверху по центру — там всегда видно. */
.toast-stack {
  position: fixed;
  right: var(--s-4);
  bottom: var(--s-4);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  width: min(380px, calc(100vw - var(--s-6)));
  pointer-events: none; /* пустая область не мешает нажимать страницу */
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-sm);
  line-height: 1.45;
  box-shadow: var(--shadow-lg);
  pointer-events: auto;
  animation: rt7-toast-in 0.18s ease both;
}

.toast--leaving { animation: rt7-toast-out 0.18s ease both; }
.toast__icon { flex: 0 0 auto; line-height: 1.45; }
.toast__text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; white-space: pre-line; }

.toast__close {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  margin: -4px -6px -4px 0;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: inherit;
  font-family: inherit;
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
}

.toast__close:hover { background: rgba(127, 127, 127, 0.18); }

.toast--success { border-color: var(--success-border); background: var(--success-soft); color: var(--success); }
.toast--error { border-color: var(--danger-border); background: var(--danger-soft); color: var(--danger); }
.toast--info { border-color: var(--primary-border); background: var(--primary-soft); color: var(--primary); }

@keyframes rt7-toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes rt7-toast-out {
  from { opacity: 1; }
  to { opacity: 0; transform: translateY(4px); }
}

/* Телефон: сверху по центру — так сообщение не перекрывает палец и клавиатуру */
@media (max-width: 640px) {
  .toast-stack {
    top: var(--s-3);
    bottom: auto;
    left: var(--s-3);
    right: var(--s-3);
    width: auto;
  }
}

/* ============================================================
   Меню «⋯» (действия строки таблицы или списка)
   ============================================================ */

.menu { position: relative; display: inline-flex; }

/* Позицию (top/left) задаёт скрипт: меню лежит в position: fixed, иначе его
   обрезала бы таблица с горизонтальной прокруткой (так в админке) */
.menu__list {
  position: fixed;
  z-index: var(--z-dropdown);
  display: flex;
  flex-direction: column;
  min-width: 208px;
  max-height: 60vh;
  overflow-y: auto;
  padding: var(--s-1);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}

.menu__list[hidden] { display: none; }

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  width: 100%;
  padding: var(--s-2) var(--s-3);
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-base);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  /* Пункт меню бывает и кнопкой, и ссылкой (открыть в новой вкладке) */
  text-decoration: none;
}

.menu__item:hover { background: var(--surface-3); }
.menu__item--danger { color: var(--danger); }
.menu__item--danger:hover { background: var(--danger-soft); }
/* Призыв к премиуму */
.upsell {
  padding: var(--s-5);
  border: 1px dashed var(--primary-border);
  border-radius: var(--r);
  background: var(--primary-soft);
  color: var(--text);
  font-size: var(--fs-base);
  text-align: center;
}

.upsell .btn { margin-top: var(--s-3); }

/* Значок-кнопка «входит в премиум» */
.premium-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: 2px var(--s-3);
  border: 0;
  border-radius: var(--r-pill);
  background: var(--primary);
  color: var(--on-primary);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: opacity var(--transition);
}

.premium-tag:hover { opacity: 0.88; }

/* ============================================================
   9. ТАБЛИЦЫ
   ------------------------------------------------------------
   Отдельного класса для таблицы в проекте нет. Раньше здесь были
   правила для table.table, но такой разметки не осталось: таблицы
   всегда помечены .table-stack (админка, списки) или .links-table.
   Поэтому правило не применялось ни к одному элементу и удалено.
   Где теперь живут стили таблиц:
     · списки и «карточки строк» на телефоне — раздел 16 (адаптив);
     · плотные отступы таблиц на планшете — там же, в запросе 1024px.
   ============================================================ */

/* ============================================================
   10. КАРТОЧКИ СПИСКОВ (ссылки, визитки)
   ============================================================ */

.list { display: flex; flex-direction: column; gap: var(--s-3); }

.list-item {
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  transition: border-color var(--transition), box-shadow var(--transition);
}

.list-item:hover { border-color: var(--primary-border); box-shadow: var(--shadow-sm); }

.list-item__head { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-2); }
.list-item__title { flex: 1 1 240px; min-width: 0; font-size: var(--fs-base); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-item__sub { color: var(--text-soft); font-size: var(--fs-sm); word-break: break-all; }
.list-item__meta { display: flex; gap: var(--s-4); flex-wrap: wrap; color: var(--text-muted); font-size: var(--fs-sm); margin-top: var(--s-2); }
.list-item__actions { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-4); padding-top: var(--s-4); border-top: 1px solid var(--border); }

/* Загрузка файла перетаскиванием */
.dropzone {
  position: relative;
  display: block;
  padding: var(--s-7) var(--s-5);
  border: 2px dashed var(--border-strong);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  text-align: center;
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}

.dropzone:hover, .dropzone:focus-within { border-color: var(--primary); background: var(--primary-soft); }
.dropzone.dragover { border-color: var(--success); background: var(--success-soft); }
.dropzone.has-file { border-style: solid; border-color: var(--success); background: var(--success-soft); }
.dropzone input[type='file'] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.dropzone__icon { font-size: 32px; line-height: 1; margin-bottom: var(--s-3); pointer-events: none; }
.dropzone__title { font-size: var(--fs-lg); font-weight: 600; pointer-events: none; }
.dropzone__hint { margin-top: var(--s-1); color: var(--text-muted); font-size: var(--fs-sm); pointer-events: none; }
.dropzone__file { display: none; margin-top: var(--s-3); pointer-events: none; }
.dropzone.has-file .dropzone__file { display: block; }
.dropzone.has-file .dropzone__title, .dropzone.has-file .dropzone__hint { display: none; }
.dropzone__name { display: block; font-weight: 600; color: var(--success); word-break: break-all; }
.dropzone__size { display: block; color: var(--text-muted); font-size: var(--fs-sm); }

/* Файл больше лимита: зона и подпись становятся красными, отправка выключена
   (проверка размера — до загрузки, чтобы не ждать впустую) */
.dropzone.size-error { border-color: var(--danger); border-style: solid; background: var(--danger-soft); }
.dropzone.size-error .dropzone__name { color: var(--danger); }
.dropzone__size--error { color: var(--danger); }

/* Полоса прогресса загрузки */
.progress { padding: var(--s-4); }
.progress__track { height: 8px; border-radius: var(--r-pill); background: var(--surface-3); overflow: hidden; }
.progress__bar { height: 100%; width: 0; background: var(--primary); transition: width var(--transition); }
.progress__status { margin-top: var(--s-3); text-align: center; color: var(--text-muted); font-size: var(--fs-sm); }

/* Результат создания ссылки */
.result-card { padding: var(--s-5); border: 1px solid var(--success-border); border-radius: var(--r-lg); background: var(--success-soft); }
.result-card h3 { margin-bottom: var(--s-3); }

/* Экран результата — одна ось: слева информация и кнопки, справа QR (A7).
   Раньше в карточке было три разных оси: ссылка слева, кнопки слева, QR и
   подпись о сроке по центру — взгляд не находил общий край */
.result-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s-6);
  align-items: center;
}
.result-info { min-width: 0; }

.result-link {
  display: block;
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--success);
  word-break: break-all;
  text-decoration: none;
  margin-bottom: var(--s-2);
}
.result-qr { text-align: center; margin: 0; }
.result-qr__save { margin-top: var(--s-3); }
/* QR всегда квадрат: width фиксируем, height считает aspect-ratio.
   Явные height вместе с global img{max-width:100%} давали вытянутый QR,
   как только контейнер становился уже картинки. */
.result-qr img {
  width: 180px;
  max-width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  vertical-align: middle;
  border-radius: var(--r-sm);
  background: #ffffff;
}
.result-qr__caption { display: block; margin-top: var(--s-2); color: var(--text-muted); font-size: var(--fs-sm); }

/* ============================================================
   11. МОДАЛЬНЫЕ ОКНА
   ============================================================ */

.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--s-4);
  background: rgba(17, 24, 39, 0.55);
  overflow-y: auto;
}

.modal.open { display: flex; }
/* Окна админки открываются классом show (кабинет — open) */
.modal.show { display: flex; }

.modal__card {
  width: 100%;
  max-width: 560px;
  max-height: 90vh;
  overflow: auto;
  padding: var(--s-6);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}

.modal__head { margin-bottom: var(--s-5); }
.modal__head h2 { font-size: var(--fs-xl); margin-bottom: var(--s-1); }
.modal__sub { color: var(--text-muted); font-size: var(--fs-sm); word-break: break-word; }

/* Шапка окна с крестиком закрытия: заголовок занимает всю ширину, кнопка — своё место */
.modal__head--row { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-4); }
.modal__head--row h2, .modal__head--row h3 { flex: 1 1 auto; margin: 0; }

/* Крестик закрытия окна */
.modal__close {
  flex: 0 0 auto; width: 36px; height: 36px; border: 0; border-radius: var(--r-sm);
  background: var(--surface-3); color: var(--text-muted);
  font-size: var(--fs-lg); font-family: inherit; line-height: 1; cursor: pointer;
}
.modal__close:hover { background: var(--danger-soft); color: var(--danger); }

/* Широкое окно (реклама, статистика): обычного 560px не хватает */
.modal__card--wide { max-width: 720px; }

.modal__actions {
  display: flex;
  gap: var(--s-2);
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  margin-top: var(--s-6);
  padding-top: var(--s-4);
  border-top: 1px solid var(--border);
}

/* Действия по краям: слева «Удалить», справа «Сохранить».
   Идёт после .modal__actions — иначе justify-content перекрывался бы */
.modal__actions--split { justify-content: space-between; }

/* Диалог подтверждения (UI.confirm) — уже обычного окна: текст короткий */
.modal__card--narrow { max-width: 420px; }
.modal__card--narrow p { color: var(--text-muted); white-space: pre-line; }

/* ============================================================
   12. СТРОКИ-ЭЛЕМЕНТЫ (редактор визитки)
   ============================================================ */

.item {
  padding: var(--s-4);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface-2);
}

.item + .item { margin-top: var(--s-3); }
.item--hidden { opacity: 0.62; border-style: dashed; }

.item__head { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin-bottom: var(--s-3); }
.item__type { flex: 1 1 220px; min-width: 0; }
.item__type select { font-weight: 600; }
.item__tools { display: flex; gap: var(--s-1); align-items: center; margin-left: auto; }

.item__label { display: block; font-size: var(--fs-sm); font-weight: 600; color: var(--text-muted); margin-bottom: var(--s-2); }
.item__foot { display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap; margin-top: var(--s-3); }

/* Панель «быстро добавить поле» */
.add-bar { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-4); }

/* ============================================================
   13. ПРЕДПРОСМОТР
   ============================================================ */

.editor-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }

.preview-pane { min-width: 0; }

.preview-pane__bar { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin-bottom: var(--s-3); }
.preview-pane__title {
  font-size: var(--fs-sm); font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-muted); margin-right: auto;
}

.phone-frame {
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: var(--bg-soft);
  padding: var(--s-3);
  box-shadow: var(--shadow-sm);
}

.phone-frame iframe { display: block; width: 100%; height: 620px; border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--surface); }

.preview-note { color: var(--text-soft); font-size: var(--fs-sm); margin-top: var(--s-2); }

/* ============================================================
   14. ПУБЛИЧНАЯ ВИЗИТКА (.vc-*)
   Одни стили и для страницы /v/<slug>, и для предпросмотра
   ============================================================ */

.vc-wrap { display: flex; flex-direction: column; align-items: center; padding: var(--s-5) var(--s-3); }
.vc-card {
  width: 100%;
  max-width: 420px;
  padding: var(--s-6);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--shadow);
}

.vc-photo { display: flex; justify-content: center; margin-bottom: var(--s-4); }
.vc-photo img { width: 104px; height: 104px; border-radius: 50%; object-fit: cover; border: 3px solid var(--vc-accent, var(--primary)); }

.vc-name { font-size: var(--fs-xl); font-weight: 700; text-align: center; word-break: break-word; }
.vc-tagline { margin-top: var(--s-2); text-align: center; color: var(--text-muted); font-size: var(--fs-base); }
.vc-description { margin-top: var(--s-4); color: var(--text); font-size: var(--fs-base); line-height: 1.6; word-break: break-word; }

.vc-action {
  display: block;
  width: 100%;
  margin-top: var(--s-5);
  padding: var(--s-4);
  border-radius: var(--r);
  background: var(--vc-accent, var(--primary));
  /* Белый текст осознанно: акценты визиток затемнены до 5.02–7.90:1
     (см. ACCENT_COLORS в routes/businessCards.js) */
  color: #fff;
  font-size: var(--fs-base);
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  transition: filter var(--transition);
}

.vc-action:hover { filter: brightness(1.08); color: #fff; }

.vc-sep { height: 1px; background: var(--border); margin: var(--s-5) 0; }
.vc-rows { display: flex; flex-direction: column; gap: var(--s-2); }

.vc-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3);
  border-radius: var(--r);
  background: var(--surface-2);
  color: var(--text);
  text-decoration: none;
  transition: background var(--transition);
}

a.vc-row:hover { background: var(--surface-3); color: var(--text); }
.vc-row__icon {
  flex: 0 0 auto; width: 36px; height: 36px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm); background: var(--surface); font-size: var(--fs-lg);
}
.vc-row__text { flex: 1 1 auto; min-width: 0; }
.vc-row__label { display: block; color: var(--text-muted); font-size: var(--fs-sm); }
.vc-row__value { display: block; font-size: var(--fs-base); font-weight: 500; word-break: break-word; }
.vc-row__chevron { flex: 0 0 auto; color: var(--text-soft); }

.vc-buttons { display: flex; gap: var(--s-2); margin-top: var(--s-5); }
.vc-buttons > * { flex: 1 1 0; }

.vc-footer { margin-top: var(--s-5); text-align: center; color: var(--text-soft); font-size: var(--fs-sm); }
.vc-footer a { color: var(--text-muted); text-decoration: none; }

/* ============================================================
   15. БЛОК АККАУНТА (главная, кабинет)
   ============================================================ */

/* Блок аккаунта: карточка по центру — и текст, и кнопки. На узких экранах
   кнопки становятся во всю ширину (см. адаптив ниже) */
.account {
  padding: var(--s-5);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  font-size: var(--fs-base);
  margin-bottom: var(--s-5);
  text-align: center;
}

.account__text { min-width: 0; }
.account__note { display: block; color: var(--text-muted); font-size: var(--fs-sm); margin-top: var(--s-1); }
.account__actions { display: flex; gap: var(--s-2); flex-wrap: wrap; justify-content: center; margin-top: var(--s-4); }
.account__status { margin-top: var(--s-3); color: var(--text-muted); font-size: var(--fs-sm); }

/* Блок входа без аккаунта: тонкая строка вместо карточки на четверть экрана.
   Раньше здесь лежали два абзаца текста и кнопка во всю ширину, и форма
   уезжала за пределы первого экрана на телефоне.
   Селектор из двух классов — чтобы правило перекрывало `.account` в медиазапросах
   независимо от порядка объявления */
.account.account--guest {
  padding: var(--s-2) var(--s-3);
  text-align: center;
}

/* Кнопка входа и «что это даёт» — по центру одной строкой, блок занимает
   минимум места. justify-content: center требует, чтобы подпись не растягивалась
   (иначе она занимает остаток строки и прижимает кнопку к левому краю).
   У раскрытого объяснения ширина 100% — там растяжение как раз нужно */
.account__guest { display: flex; align-items: center; justify-content: center; gap: var(--s-2) var(--s-3); flex-wrap: wrap; }

/* Кнопка входа — самая компактная в сервисе: это напоминание, а не главное
   действие страницы (главное — форма под ней) */
.account__guest .btn { padding: 5px var(--s-3); }
.account__guest .btn .icon { width: 14px; height: 14px; }

/* «Зачем это»: раскрывается нативным details — без скриптов, доступно с
   клавиатуры, читалка объявляет состояние («свёрнуто» / «развёрнуто»).
   Ширину не тянем и не сжимаем (0 0 auto) — блок занимает ровно свою подпись.
   Растяжение сбивало центрирование кнопки, а сжатие рвало подпись на две
   строки; когда места не хватает, flex-wrap переносит блок целиком */
.account__why { flex: 0 0 auto; font-size: var(--fs-sm); color: var(--text-muted); }
.account__why > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  cursor: pointer;
  color: var(--primary);
  /* Маркер браузера убираем: указатель рисует иконка из общего набора */
  list-style: none;
}
.account__why > summary::-webkit-details-marker { display: none; }
.account__why > summary:hover { text-decoration: underline; }
/* Стрелка разворачивается при открытии: состояние видно без чтения */
.account__why > summary .icon { transition: transform var(--transition); }
.account__why[open] > summary .icon { transform: rotate(180deg); }
.account__why > p { margin: var(--s-2) 0 0; }

/* Раскрытое объяснение занимает всю строку: рядом с кнопкой текст сжимался бы
   в узкую колонку в несколько слов */
.account__why[open] { flex-basis: 100%; }

/* Сообщение о ходе входа — под строкой, без лишнего отступа */
.account--guest .account__status { margin-top: var(--s-2); }

/* Имя вошедшего: жирное, у премиума — цветом акцента */
.account-name { font-weight: 700; }
.account-name--premium { color: var(--primary); }

/* Плашка «кто вошёл» */
.whoami {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 6px var(--s-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}

.whoami:hover { border-color: var(--primary); background: var(--primary-soft); color: var(--primary); }
.whoami:empty { display: none; }
.whoami--premium { border-color: var(--primary); color: var(--primary); background: var(--primary-soft); }

/* ============================================================
   16. АДАПТИВ
   ============================================================ */

@media (min-width: 1000px) {
  .editor-layout { grid-template-columns: minmax(0, 1fr) 380px; align-items: start; }
  .preview-pane { position: sticky; top: var(--s-5); }
}

/* Предпросмотр в редакторе: на телефоне — открывающаяся панель */
.preview-toggle { display: none; }

@media (max-width: 999px) {
  .preview-toggle { display: inline-flex; }
  .preview-pane { display: none; }
  .preview-pane.open { display: block; margin-bottom: var(--s-5); }
  .phone-frame iframe { height: 520px; }
}

/* ------------------------------------------------------------
   Планшет ландшафт и небольшой ноутбук (≤1024)
   Таблицы ещё помещаются целиком, поэтому карточный режим здесь не нужен —
   только плотнее отступы и меньше минимальная ширина ячеек в фильтрах.
   ------------------------------------------------------------ */
@media (max-width: 1024px) {
  .dashboard { padding: var(--s-5) var(--s-3) var(--s-6); }
  .links-table th, .links-table td,
  .clicks-table-small th, .clicks-table-small td { padding: var(--s-2); }
  .filters-row { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
  .stats-grid, .summary { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
}

/* ------------------------------------------------------------
   Планшет портрет и крупный телефон (≤768)
   Главное изменение: таблицы становятся карточками. Класс .table-stack и
   подписи data-label проставляет /static/ui.js — он читает заголовки колонок
   и подставляет их к ячейкам, поэтому разметка генераторов не меняется.
   Зачем: в админке 8 колонок, на телефоне их приходилось крутить вбок, и было
   не видно, какая строка перед глазами (проблема A6 из аудита).
   ------------------------------------------------------------ */
@media (max-width: 768px) {
  /* Вкладки: 7 разделов админки не влезают в строку. Раньше был только
     горизонтальный скролл без подсказки — часть разделов «не находилась» (A5) */
  .tabs { flex-wrap: wrap; overflow-x: visible; }
  .tab { flex: 1 1 auto; min-width: 0; padding: 10px var(--s-3); font-size: var(--fs-sm); }

  /* Панель фильтров: по два поля в ряд вместо одного узкого столбца */
  .filters-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-3); }

  /* Шапка админки и кабинета: заголовок и кнопки переносятся, но не «липнут» */
  .header { gap: var(--s-2); }
  .header h1 { font-size: var(--fs-lg); }
  .header-actions { gap: var(--s-1); }

  /* Таблица в карточном режиме не скроллится вбок: рамку даёт каждая карточка */
  .links-table, .clicks-table-small { overflow: visible; border: 0; background: none; }

  .table-stack { border-collapse: separate; border-spacing: 0; }
  /* Шапку таблицы прячем визуально, но оставляем читалкам экрана:
     подписи колонок переходят в карточку */
  .table-stack thead {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .table-stack tbody tr {
    display: block;
    margin-bottom: var(--s-3);
    padding: var(--s-2) var(--s-4) var(--s-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--surface);
  }
  .table-stack tbody tr:hover { background: var(--surface); border-color: var(--primary-border); }
  .table-stack tbody td {
    display: flex;
    gap: var(--s-3);
    align-items: baseline;
    justify-content: space-between;
    min-height: 30px;
    padding: var(--s-2) 0;
    border-bottom: 1px solid var(--border);
    text-align: right;
  }
  .table-stack tbody tr td:last-child { border-bottom: 0; }
  /* Подпись колонки — то, что раньше жило в шапке таблицы */
  .table-stack tbody td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    max-width: 45%;
    color: var(--text-muted);
    font-size: var(--fs-sm);
    font-weight: 600;
    text-align: left;
    white-space: normal;
  }
  /* Пустая ячейка и ячейка без подписи (кнопки действий) — без пустой строки */
  .table-stack tbody td[data-label='']::before,
  .table-stack tbody td:empty::before { display: none; }
  .table-stack tbody td:empty { display: none; }
  /* Строка-заглушка («Визиток нет», «Загрузка…») остаётся во всю ширину */
  .table-stack tbody td[colspan] { display: block; text-align: center; border-bottom: 0; }
  .table-stack tbody td[colspan]::before { display: none; }
  /* Обрезка в одну строку в карточке не нужна: место есть, текст важнее (A6) */
  .table-stack .cell-truncate { max-width: none; white-space: normal; overflow: visible; text-overflow: clip; }
  /* Действия строки — своим рядом по правому краю */
  .table-stack tbody td.actions { flex-wrap: wrap; justify-content: flex-end; min-height: 0; padding-top: var(--s-3); }
  /* Колонка отметки в карточке не узкая: ширина 46px задана для таблицы на
     большом экране, а здесь из-за неё подпись ломалась по буквам. Флажок —
     первым в карточке и слева: он относится ко всей строке */
  .table-stack tbody td.cell-select { width: auto; justify-content: flex-start; min-height: 0; padding-bottom: 0; }
  /* Флажок пальцем: сам квадратик мелкий, поэтому увеличиваем область нажатия */
  .table-stack tbody td.cell-select input[type='checkbox'] { width: 22px; height: 22px; }
  /* Модальное окно статистики: таблица в карточках на узком экране */
  .modal .table-stack tbody td { text-align: right; }
}

@media (max-width: 640px) {
  :root { --fs-2xl: 24px; --fs-xl: 20px; }

  .page { padding: var(--s-4) var(--s-3) var(--s-6); }
  .panel { padding: var(--s-4); border-radius: var(--r); }
  .section { padding: var(--s-4); }
  .modal__card { padding: var(--s-5); border-radius: var(--r); }

  .site-header { gap: var(--s-2); margin-bottom: var(--s-4); flex-wrap: nowrap; }
  .site-header__title h1 { font-size: var(--fs-lg); }
  /* Кнопки остаются в строке с заголовком. Раньше блок действий растягивался на
     всю ширину и уезжал под заголовок — шапка вырастала вдвое, а переключатель
     темы оказывался в разных местах на разных страницах (A4). Второстепенные
     действия при этом прячутся в меню «⋯» (класс .hide-mobile) */
  .site-header__actions { gap: var(--s-1); }
  .hide-mobile { display: none !important; }
  .menu.header-more { display: inline-flex; }

  /* Кабинет и админка: шапка тоже держится одной строки, а длинное имя
     пользователя обрезается, а не выдавливает кнопки */
  .header { flex-wrap: nowrap; }
  .header h1 { font-size: var(--fs-lg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .top-links, .header-actions { gap: var(--s-1); flex-wrap: nowrap; }
  .top-links .whoami { max-width: 40vw; overflow: hidden; text-overflow: ellipsis; }

  .tabs { gap: 2px; }
  .tab { padding: 10px var(--s-3); font-size: var(--fs-sm); }

  .list-item { padding: var(--s-4); }
  .list-item__actions { flex-direction: column; align-items: stretch; }
  .list-item__actions > .btn, .list-item__actions .btn { width: 100%; }
  /* Кнопка «⋯» не растягивается: у неё фиксированный квадратный размер */
  .list-item__actions .menu .btn { width: auto; }

  .item__tools { margin-left: 0; width: 100%; }

  .btn-row > .btn { flex: 1 1 auto; }
  .modal__actions { flex-direction: column-reverse; align-items: stretch; }
  .modal__actions .btn { width: 100%; }

  /* Блок входа: текст по центру, кнопки — во всю ширину экрана */
  .account { padding: var(--s-4); }
  .account__actions { flex-direction: column; align-items: stretch; }
  .account__actions .btn { width: 100%; }

  /* Без входа блок остаётся одной строкой: кнопка входа не растягивается,
     иначе она снова займёт пол-экрана и отодвинет форму */
  .account.account--guest { padding: var(--s-2) var(--s-3); }
  .account__guest { gap: var(--s-2) var(--s-3); }
  .account__guest .btn { width: auto; }

  .vc-card { padding: var(--s-5); }
  .vc-buttons { flex-direction: column; }

  /* Результат: одна колонка, QR под текстом — «одна ось» сохраняется (A7) */
  .result-layout { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); justify-items: center; }
  .result-info { width: 100%; }
  .result-qr img { width: 150px; }

  /* Предпросмотр изображения и лайтбокс на телефоне */
  .preview img { max-height: 300px; }
  .lightbox-toolbar { gap: var(--s-1); padding: var(--s-1) var(--s-3); }
  .lightbox-toolbar button { padding: 6px var(--s-2); }
}

@media (max-width: 480px) {
  /* Панель фильтров: одно поле в ряд, кнопка во всю ширину */
  .filters-row { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------------------
   Маленькие телефоны (≤375: iPhone SE и подобные)
   Отступы минимальные, иначе содержимое сжимается в узкий столбик
   ------------------------------------------------------------ */
@media (max-width: 375px) {
  .page { padding: var(--s-3) var(--s-2) var(--s-5); }
  .dashboard { padding: var(--s-4) var(--s-2) var(--s-5); }
  .panel, .list-item, .section { padding: var(--s-3); }
  .table-stack tbody tr { padding: var(--s-1) var(--s-3) var(--s-2); }
  /* Подпись колонки и значение делим пополам: на 320px иначе не хватает места */
  .table-stack tbody td::before { max-width: 50%; }
}

/* ============================================================
   17. ПЕЧАТЬ (публичная визитка и QR)
   ============================================================ */

@media print {
  body { background: #fff; }
  .site-header, .site-footer, .vc-buttons, .no-print { display: none !important; }
  .vc-card { box-shadow: none; border: 0; max-width: none; }
  .vc-row { background: none; border-bottom: 1px solid #e5e7eb; border-radius: 0; }
}

/* ============================================================
   18. СОВМЕСТИМОСТЬ: страницы ссылок, пароля, оплаты, демо
   ------------------------------------------------------------
   Генераторы страниц (routes/links.js, payments.js, demo.js) собирают
   разметку строками и используют свои имена классов. Чтобы они выглядели
   так же, как остальной сайт, эти имена описаны здесь один раз — править
   их в каждом генераторе не нужно.
   ============================================================ */

/* Строки «иконка + текст» на странице файла.
   flex-wrap — длинное имя файла переносится, а не сжимает QR-код рядом. */
.file-head { display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-5); }
.file-head-text { flex: 1 1 240px; min-width: 0; }
.file-head-text h1 { margin-bottom: var(--s-1); overflow-wrap: anywhere; }

.info { color: var(--text-muted); font-size: var(--fs-sm); }

/* QR на странице скачивания.
   flex: 0 0 auto — строка с длинным именем файла не может сжать блок;
   margin: auto — если блок перенёсся на свою строку, он встаёт по центру.
   Картинке height не задаём: высоту считает aspect-ratio, поэтому квадрат
   остаётся квадратом при любой ширине контейнера. Padding даёт белый
   «тихий» фон вокруг кода — так он лучше читается сканером и в тёмной теме. */
.qr { flex: 0 0 auto; margin: 0 auto; text-align: center; }
.qr img {
  display: block;
  width: 180px;
  max-width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  padding: var(--s-1);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: #ffffff;
}
.qr-caption { display: block; margin-top: var(--s-2); color: var(--text-soft); font-size: var(--fs-sm); }

/* Реклама */
.ad-container { margin: var(--s-5) 0; padding: var(--s-4); border: 1px solid var(--border); border-radius: var(--r); background: var(--surface-2); text-align: center; }
/* Баннеры сверху и снизу главной — той же ширины, что и страница */
#adMainTop, #adMainBottom { width: 100%; max-width: var(--w-page); margin-left: auto; margin-right: auto; }
.ad-container img, .ad-container iframe { max-width: 100%; }

/* Предпросмотр изображения на странице файла */
.preview { margin: 0 0 var(--s-5); text-align: center; }
.preview img {
  display: block;
  width: 100%;
  max-height: 420px;
  object-fit: contain;
  background: var(--surface-2);
  border-radius: var(--r);
  border: 1px solid var(--border);
  cursor: zoom-in;
  transition: transform var(--transition);
}
.preview img:hover { transform: scale(1.01); }
.preview figcaption {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-top: var(--s-2);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  text-align: left;
}
.preview figcaption a { color: var(--primary); white-space: nowrap; }

/* Лайтбокс (увеличение картинки при клике) */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(9, 12, 20, 0.92);
  overflow: hidden;
  user-select: none;
}

.lightbox[hidden] { display: none; }
.lightbox img { max-width: none; cursor: grab; transform-origin: center center; }
.lightbox img.dragging { cursor: grabbing; }

.lightbox-toolbar {
  position: absolute;
  top: var(--s-3);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: var(--s-2);
  align-items: center;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(6px);
}

.lightbox-toolbar button {
  padding: 6px var(--s-3);
  border: 0;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
  font-family: inherit;
  font-size: var(--fs-sm);
  cursor: pointer;
}

.lightbox-toolbar button:hover { background: rgba(255, 255, 255, 0.3); }
.lightbox-toolbar .zoom-value { color: #fff; font-size: var(--fs-sm); min-width: 46px; text-align: center; }

.lightbox-hint {
  position: absolute;
  bottom: var(--s-4);
  left: 50%;
  transform: translateX(-50%);
  color: rgba(255, 255, 255, 0.7);
  font-size: var(--fs-sm);
  text-align: center;
  padding: 0 var(--s-4);
}

/* Загрузка / индикатор */
.spinner {
  width: 40px;
  height: 40px;
  margin: var(--s-5) auto;
  border: 3px solid var(--border);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: rt7-spin 0.9s linear infinite;
}

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

/* Поле «дрогнуло» — неверный пароль */
.shake { animation: rt7-shake 0.4s ease; }

@keyframes rt7-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-6px); }
  75% { transform: translateX(6px); }
}

.text { color: var(--text-muted); font-size: var(--fs-base); text-align: center; }
.sum { color: var(--text); font-weight: 600; }

/* Детали заказа (демо-оплата) */
.details {
  margin-top: var(--s-5);
  padding: var(--s-4);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface-2);
  text-align: left;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.details > div { display: flex; justify-content: space-between; gap: var(--s-3); padding: var(--s-1) 0; }
.details b { color: var(--text); }

/* Информационные страницы состояний (оплата, проверка, демо): крупный
   значок по центру, дальше заголовок и пояснение */
.page-icon {
  display: flex;
  justify-content: center;
  color: var(--text-muted);
  margin-bottom: var(--s-4);
}

.page-icon .icon { width: 44px; height: 44px; stroke-width: 1.5; }
.demo-hint {
  margin-top: var(--s-4);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--primary-border);
  border-radius: var(--r-sm);
  background: var(--primary-soft);
  color: var(--primary);
  font-size: var(--fs-sm);
  text-align: left;
}
.demo-error {
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-4);
  border: 1px solid var(--danger-border);
  border-radius: var(--r-sm);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: var(--fs-sm);
}

/* ============================================================
   19. СОВМЕСТИМОСТЬ: кабинет (/me) и демо-кабинет
   ------------------------------------------------------------
   Кабинет — большой файл с историей: здесь те же классы, что были,
   но собранные из токенов. Так дизайн один, а переписывать разметку
   и логику кабинета не нужно.
   ============================================================ */

.top { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-5); }
.top-title { flex: 1 1 auto; min-width: 0; font-size: var(--fs-xl); font-weight: 700; }
.top-links { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; margin-left: auto; }
.home-logo {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: var(--r);
  background: var(--primary-soft); border: 1px solid var(--primary-border);
  color: var(--primary); font-size: var(--fs-lg); text-decoration: none;
}
.home-logo:hover { background: var(--primary); color: var(--on-primary); }
.subtitle { color: var(--text-muted); font-size: var(--fs-base); margin-bottom: var(--s-5); }

/* Раздел формы. Класс form-section-title проверяется тестом — он остаётся */
.form-section { padding: var(--s-5); border: 1px solid var(--border); border-radius: var(--r); background: var(--surface); }
.form-section + .form-section { margin-top: var(--s-5); }
.form-section-title {
  font-size: var(--fs-sm); font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-muted); margin-bottom: var(--s-4);
}

/* Строка ссылки */
.link {
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  margin-bottom: var(--s-3);
}
.link:hover { border-color: var(--primary-border); }
.link--off { background: var(--surface-2); opacity: 0.85; }
.link-head { display: flex; align-items: center; gap: var(--s-2) var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-2); }
.link-short { font-family: var(--font-mono); font-size: var(--fs-base); font-weight: 600; word-break: break-all; }
.link-target { color: var(--text-soft); font-size: var(--fs-sm); word-break: break-all; }
/* Данные карточки — ровной сеткой на всю ширину. Раньше всё липло к левому краю,
   а справа оставалась пустота */
.link-meta {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s-2) var(--s-4);
  color: var(--text-muted); font-size: var(--fs-sm); margin-top: var(--s-3);
}
.link-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; margin-top: var(--s-4); padding-top: var(--s-4); border-top: 1px solid var(--border); }
.actions-group, .link-actions-group { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }
.link-actions-group--end { margin-left: auto; }

/* Сводка и статистика */
.stats { margin-top: var(--s-5); }
.stats-grid, .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: var(--s-3); }
.summary-label { color: var(--text-muted); font-size: var(--fs-sm); }
.summary-value { font-size: var(--fs-lg); font-weight: 700; }
/* Сводка кабинета — отдельный блок: держим отступ до фильтров под ней */
.summary { margin-bottom: var(--s-5); }
.stats-grid { margin-bottom: var(--s-5); }

/* Панель фильтров и постраничный переход. Вид панели общий с админской .filters
   (см. «Поиск и фильтры» ниже) — здесь только раскладка */
.toolbar {
  display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: center;
  margin-bottom: var(--s-5);
}
.toolbar > input[type='search'] { flex: 1 1 240px; width: auto; min-width: 0; }
.toolbar-filters { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }
.toolbar-export { margin-left: auto; display: flex; gap: var(--s-2); flex-wrap: wrap; }
.pager { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; justify-content: center; margin-top: var(--s-5); }

/* Поля адреса-псевдонима */
.alias-prefix { color: var(--text-muted); font-size: var(--fs-sm); align-self: center; white-space: nowrap; }
.alias-input { flex: 1 1 180px; min-width: 0; }
.readonly-row { color: var(--text-muted); font-size: var(--fs-sm); word-break: break-all; }
.plain { color: var(--text-muted); font-size: var(--fs-sm); }

/* Блок премиума */
.premium-block { padding: var(--s-5); border: 1px solid var(--primary-border); border-radius: var(--r); background: var(--primary-soft); }
.premium-block-title { font-size: var(--fs-lg); font-weight: 700; margin-bottom: var(--s-2); }
.premium-field { margin-bottom: var(--s-3); }
.premium-field--locked { opacity: 0.7; }
.premium-field--locked .input, .premium-field--locked input { background: var(--surface-3); cursor: not-allowed; }
.premium-note { color: var(--text-muted); font-size: var(--fs-sm); margin: 0 0 var(--s-4); }
.premium-status { font-weight: 600; color: var(--primary); }
.premium-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-4); }
.premium-tag--icon { padding: 2px var(--s-2); }

/* Способы оплаты */
.pay-badges { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-3); }
.pay-badge {
  display: inline-flex; align-items: center; gap: var(--s-1);
  padding: var(--s-1) var(--s-3); border: 1px solid var(--border-strong);
  border-radius: var(--r-pill); background: var(--surface);
  color: var(--text-muted); font-size: 12px;
}
.pay-badge--card { color: var(--text); }

/* Плашка демо-режима */
.demo-banner {
  display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: center;
  padding: var(--s-4); margin-bottom: var(--s-5);
  border: 1px solid var(--primary-border); border-radius: var(--r); background: var(--primary-soft);
}
.demo-banner-actions { margin-left: auto; display: flex; gap: var(--s-2); flex-wrap: wrap; }

/* Футер кабинета. Кабинет — широкая страница (.page--wide), поэтому футер
   держим той же ширины: он стоит ровно под содержимым, а не уже него */
.page-footer {
  width: 100%; max-width: var(--w-wide); margin: 0 auto;
  padding: var(--s-5) var(--s-4) var(--s-6); text-align: center;
  color: var(--text-soft); font-size: var(--fs-sm);
}
.page-footer a { color: var(--text-muted); text-decoration: none; }
.page-footer a:hover { color: var(--text); text-decoration: underline; }
.page-footer-sep { margin: 0 var(--s-2); opacity: 0.5; }
.page-footer-line { margin-bottom: var(--s-2); }
.page-footer-premium {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-4); margin-bottom: var(--s-3);
  border: 1px solid var(--primary-border); border-radius: var(--r-pill);
  background: var(--primary-soft); color: var(--primary);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
}
.page-footer-premium:hover { background: var(--primary); color: var(--on-primary); }

/* ============================================================
   20. СОВМЕСТИМОСТЬ: админ-панель
   ------------------------------------------------------------
   Админка — служебная страница: те же токены и компоненты, но своими
   именами классов. Так она выглядит в одном стиле с сайтом, а логика
   (2200+ строк JS) остаётся нетронутой.
   ============================================================ */

.login-container {
  display: flex; flex-direction: column; justify-content: center;
  min-height: 100vh; padding: var(--s-5);
}

.login-box {
  width: 100%; max-width: var(--w-narrow); margin: 0 auto;
  padding: var(--s-7); border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); box-shadow: var(--shadow);
  text-align: center;
}

.login-box h1 { font-size: var(--fs-xl); margin-bottom: var(--s-5); }
.login-box form { display: flex; flex-direction: column; gap: var(--s-3); }
.login-box input { text-align: center; }

/* Плашка сообщения (в т.ч. в форме входа) */
.alert {
  display: none;
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-4);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text);
  font-size: var(--fs-sm);
  text-align: left;
}

.alert.show { display: block; }
.alert-error { border-color: var(--danger-border); background: var(--danger-soft); color: var(--danger); }

/* Каркас панели. Ширину задаёт сам .dashboard, поэтому и шапка, и вкладки,
   и разделы стоят в одной колонке по центру. Раньше ширину задавал только
   .header, а вкладки и таблицы растягивались на весь экран */
.dashboard { display: none; }
.dashboard.show {
  display: block;
  width: 100%;
  max-width: var(--w-wide);
  margin: 0 auto;
  padding: var(--s-6) var(--s-4) var(--s-7);
}

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

.header h1 { font-size: var(--fs-xl); margin-right: auto; }
.header-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }

.dashboard > * + * { margin-top: var(--s-5); }

/* Поиск и фильтры: админка (.filters) и кабинет (.toolbar) — один вид */
.filters, .toolbar {
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface-2);
}

.filters { margin-bottom: var(--s-5); }

.filters-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s-3) var(--s-4);
  align-items: end;
}
.filter-group { display: flex; flex-direction: column; gap: var(--s-1); min-width: 0; }
.filter-group > label { margin: 0; color: var(--text-muted); font-size: var(--fs-sm); font-weight: 600; }
.filter-group .input, .filter-group input, .filter-group select { width: 100%; min-width: 0; }
/* Кнопка действия занимает всю ячейку — ровно под полями рядом */
.filter-group > .btn { width: 100%; }

/* Поля в панелях фильтров компактнее обычных: панель не должна «съедать» экран.
   У select справа оставляем место под стрелку (иначе текст налезет на неё) */
.filters input:not([type='checkbox']):not([type='radio']),
.toolbar input:not([type='checkbox']):not([type='radio']) {
  padding: 9px var(--s-3);
  font-size: var(--fs-sm);
  border-radius: var(--r-sm);
}
.filters select,
.toolbar select {
  padding: 9px 30px 9px var(--s-3);
  font-size: var(--fs-sm);
  border-radius: var(--r-sm);
}

/* Таблицы админки: компактные отступы и данные по центру строки — так таблица
   выглядит ровно и занимает меньше места */
.links-table, .clicks-table-small {
  width: 100%; border-collapse: collapse; font-size: var(--fs-sm);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  overflow-x: auto;
}
.links-table th, .clicks-table-small th {
  padding: var(--s-2) var(--s-3); text-align: left; background: var(--surface-2);
  color: var(--text-muted); font-weight: 600; white-space: nowrap;
}
.links-table td, .clicks-table-small td {
  padding: var(--s-2) var(--s-3); border-top: 1px solid var(--border);
  vertical-align: middle; word-break: break-word;
}
.links-table tbody tr:hover { background: var(--surface-2); }

/* Отметка строки для массовых действий: подсветка и место под флажок */
.links-table td.cell-select { width: 46px; }
.links-table tbody tr.is-marked,
.links-table tbody tr.is-marked:hover { background: var(--primary-soft); }

/* Панель массовых действий над таблицей: появляется, когда отмечена хотя бы
   одна строка. Раньше «отключить десять ссылок» означало десять подтверждений
   и десять запросов — по одной кнопке в каждой строке */
.bulk-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
  margin-bottom: var(--s-3); padding: var(--s-2) var(--s-3);
  border: 1px solid var(--primary-border); border-radius: var(--r);
  background: var(--primary-soft);
}
.bulk-bar[hidden] { display: none; }
.bulk-bar__count { font-weight: 600; color: var(--primary); }
.bulk-bar__spacer { flex: 1 1 auto; }
/* Кнопки не растягиваются во всю ширину: их четыре, и на телефоне «лесенка»
   заняла бы пол-экрана */
.bulk-bar .btn { flex: 0 0 auto; }

/* Переключатель языка в шапке: компактная кнопка рядом с темой. Подпись не
   переносится — иначе шапка становится двухстрочной на телефоне */
.lang-btn { white-space: nowrap; }
.lang-btn .icon { width: 15px; height: 15px; }

/* Короткий код не переносится: раньше «/d/xxxxxx» ломался на три строки */
.short-code { font-family: var(--font-mono); font-size: var(--fs-sm); white-space: nowrap; }

/* Длинный текст в ячейке (адрес, referer, user-agent) — одна строка с многоточием */
.cell-truncate { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.status-badge {
  display: inline-flex; align-items: center; gap: var(--s-1);
  padding: 2px var(--s-2); border-radius: var(--r-pill);
  background: var(--surface-3); color: var(--text-muted); font-size: 12px; white-space: nowrap;
}
.status-badge.active { background: var(--success-soft); color: var(--success); }

/* Сводка и карточки статистики */
.stats-summary { padding: var(--s-4); border: 1px solid var(--border); border-radius: var(--r); background: var(--surface); }
.stat-card, .stats-card-mini {
  padding: var(--s-3) var(--s-4); border: 1px solid var(--border); border-radius: var(--r); background: var(--surface);
}
.stat-card .value, .stats-card-mini .value { font-size: var(--fs-lg); font-weight: 700; }
.stat-card .sub, .stats-card-mini .sub { color: var(--text-muted); font-size: var(--fs-sm); }

/* Информационные блоки на публичных страницах: «Как это работает» и «Что можно».
   Появились в L4: форма отвечает на вопрос «что сделать», а эти блоки — на
   вопросы «что это вообще» и «какие тут правила» для того, кто впервые.
   Свёрнуты по умолчанию (нативный details): страница нужна ради формы, а
   объяснения — для того, кому интересно. Развёрнутые, они отодвигали «Мои
   ссылки» за пределы первого экрана */
.info-block {
  padding: var(--s-5);
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface);
}
.info-block[hidden] { display: none; }
.info-block__title {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--fs-lg);
}

/* Заголовок — сам раскрывающий элемент. Маркер браузера убран: указатель рисует
   стрелка из общего набора иконок, поворачиваясь при раскрытии, поэтому
   состояние видно без чтения, а читалка объявляет его голосом */
.info-block__summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  cursor: pointer;
  list-style: none;
}
.info-block__summary::-webkit-details-marker { display: none; }
.info-block__summary:hover .info-block__title { text-decoration: underline; }
.info-block__summary .icon { color: var(--text-muted); transition: transform var(--transition); }
/* Отступ только у раскрытого блока: у свёрнутого он растянул бы рамку впустую */
.info-block[open] > .info-block__summary { margin-bottom: var(--s-4); }
.info-block[open] > .info-block__summary .icon { transform: rotate(180deg); }

/* Шаги: номер в кружке слева, текст справа. Номер — обычное число, а не
   картинка: так он читается и в тёмной теме, и при отключённых изображениях */
.how { display: flex; flex-direction: column; gap: var(--s-4); margin: 0; padding: 0; list-style: none; }
.how__step { display: flex; gap: var(--s-3); align-items: flex-start; }
.how__num {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--primary-soft); color: var(--primary);
  font-weight: 700; font-size: var(--fs-sm);
}
.how__step div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.how__step b { font-size: var(--fs-base); }
.how__step .hint { margin: 0; }

/* «Что можно»: сетка плиток. minmax(150px) — на телефоне две плитки в ряд,
   на компьютере до шести: длинные подписи переносятся, а не режутся */
.info-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s-3);
}
.info-item {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-1);
  padding: var(--s-3);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-2);
  text-align: center;
}
.info-item > i { color: var(--primary); }
.info-item b { font-size: var(--fs-base); }
.info-item span { color: var(--text-muted); font-size: var(--fs-sm); }

/* График по кликам */
.chart-container { margin-top: var(--s-5); padding: var(--s-5); border: 1px solid var(--border); border-radius: var(--r); background: var(--surface); }
.chart-bars { display: flex; align-items: flex-end; gap: var(--s-2); height: 180px; margin-top: var(--s-4); }
.chart-bar { flex: 1 1 0; min-width: 8px; background: var(--primary); border-radius: var(--r-xs) var(--r-xs) 0 0; }
.chart-bar-label { display: block; margin-top: var(--s-2); color: var(--text-soft); font-size: 11px; text-align: center; }
.chart-bar-value { display: block; color: var(--text); font-size: 12px; text-align: center; margin-bottom: var(--s-1); }

/* Постраничный переход */
.pagination { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; justify-content: center; margin-top: var(--s-5); }


/* Пустое состояние, загрузка, заголовки разделов */
.empty-state { padding: var(--s-8) var(--s-4); text-align: center; color: var(--text-muted); }
.empty-state-icon { font-size: 40px; line-height: 1; margin-bottom: var(--s-3); }
.loading { padding: var(--s-8) var(--s-4); text-align: center; color: var(--text-muted); }
.section-header { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-4); }
.section-header h2, .section-header h3 { margin-right: auto; }
/* Первый блок растягивается, поэтому кнопка справа прижата к краю — даже если
   заголовок обёрнут в div (как в разделе «Визитки») */
.section-header > :first-child { flex: 1 1 auto; min-width: 0; }
.actions { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }
.form-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-5); }

/* Настройки: сетка карточек */
.settings-page { display: flex; flex-direction: column; gap: var(--s-5); }
.settings-hero {
  padding: var(--s-5); border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface-2);
}
.settings-hero h2 { font-size: var(--fs-lg); margin-bottom: var(--s-1); }
.settings-hero-hint { color: var(--text-muted); font-size: var(--fs-sm); }

.settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--s-4); align-items: start; }

.settings-card {
  padding: var(--s-5); border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface);
}
.settings-card-head { display: flex; gap: var(--s-3); align-items: flex-start; margin-bottom: var(--s-4); }
.settings-card-icon { font-size: var(--fs-xl); line-height: 1; }
.settings-card-head h3 { font-size: var(--fs-lg); margin-bottom: var(--s-1); }
.settings-card-head p { color: var(--text-muted); font-size: var(--fs-sm); }
.settings-card-foot { margin-top: var(--s-4); }

.settings-field { display: flex; flex-direction: column; }
.settings-field--wide { grid-column: 1 / -1; }
.settings-secrets { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--s-4); }

.settings-check { display: inline-flex; align-items: center; gap: var(--s-2); cursor: pointer; }
.settings-check-text { font-size: var(--fs-sm); color: var(--text-muted); }

/* Переключатель в настройках */
.toggle-switch { position: relative; display: inline-block; width: 46px; height: 26px; flex: 0 0 auto; }
.toggle-switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.toggle-slider {
  position: absolute; inset: 0; border-radius: var(--r-pill);
  background: var(--border-strong); transition: background var(--transition);
}
.toggle-slider::before {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px; border-radius: 50%;
  background: #fff; transition: transform var(--transition);
}
.toggle-switch input:checked + .toggle-slider { background: var(--success); }
.toggle-switch input:checked + .toggle-slider::before { transform: translateX(20px); }

/* Панель «Сохранить всё» */
.settings-savebar {
  position: sticky; bottom: 0; z-index: 50;
  display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: center;
  margin-top: var(--s-5); padding: var(--s-4);
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface); box-shadow: var(--shadow);
}
.settings-savebar-info { color: var(--text-muted); font-size: var(--fs-sm); margin-right: auto; }
.settings-savebar-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; }

/* Выбор файла в настройках (реклама) */
.file-pick { display: flex; gap: var(--s-3); align-items: center; flex-wrap: wrap; }
.file-pick-btn { cursor: pointer; }
.file-pick-info { color: var(--text-muted); font-size: var(--fs-sm); word-break: break-all; }

/* Поле формы в админке */
.form-group { margin-bottom: var(--s-5); }
.form-group:last-child { margin-bottom: 0; }
.form-group > label, .form-group > .label { display: block; margin-bottom: var(--s-2); font-weight: 600; }
.form-group > .hint, .form-group > small { display: block; margin-top: var(--s-2); color: var(--text-muted); font-size: var(--fs-sm); }

/* Состояние секретов: заполнено / пусто */
.settings-secrets--set { border-color: var(--success-border); }

/* Панель «Сохранить всё»: подсветка, когда есть несохранённое */
.settings-savebar--dirty { border-color: var(--warning-border); background: var(--warning-soft); }
.settings-savebar--saved { border-color: var(--success-border); background: var(--success-soft); }

/* Оплаты по способам */
.payments-by-method { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--s-3); margin-top: var(--s-4); }

/* ============================================================
   Системная настройка «уменьшить движение»
   ============================================================ */

/* Пользователи с вестибулярными расстройствами отключают анимации в системе
   (Windows: «Эффекты анимации», macOS/iOS: «Уменьшить движение», Android:
   «Удалить анимацию»). Раньше это не учитывалось: крутился спиннер, дёргалась
   плашка ошибки, тосты выезжали, кнопки «прыгали» при нажатии.
   Длительность оставляем ненулевой, чтобы `transitionend`/`animationend`
   продолжали срабатывать — иначе часть скриптов, ждущих окончания, зависла бы. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* Спиннеры теряют смысл без вращения: показываем статичную точку */
  .spinner { animation: none !important; }
}
