/* PARANOIA — landing styles. Ported from the approved design mockup.
   Palette is fixed (see CLAUDE.md «Дизайн»). */

/* Светлые значения нужны и на отдельном элементе, а не только на корне:
   копия страницы во время перехода тем несёт старую тему на себе. */
:root,
[data-theme="light"] {
  /* ── Монохром. Итерация 2 ──
     Цвета ровно два: фон и текст. Всё остальное — их оттенки. Акцент тоже
     монохромный: на белом чёрный, на чёрном белый. Так видно саму вёрстку,
     не прикрытую цветом; цвет вернём, когда каркас устоится. */

  /* Светлая тема — по умолчанию */
  --bg: #ffffff;
  --depth: #ffffff;
  --card: #ffffff;
  --border: #111111;
  --border-soft: #c9c9c9;
  --accent: #111111;
  --accent-hover: #000000;
  --text: #111111;
  --muted: #3f3f3f;
  /* Третий уровень текста: мелкие подписи тише приглушённого, но контраст
     к фону держим не ниже 4.5:1 (тут ~5.3:1) — норма читаемости. */
  --muted-2: #6b6b6b;
  --ink: #ffffff;        /* текст на акценте */
  /* QR-код не переворачивается с темой: сканер читает только тёмные модули
     на светлом поле. В тёмной теме не переопределяем — так и задумано. */
  --qr-bg: #ffffff;
  --qr-fg: #111111;

  /* Каналы отдельно: нужны там, где цвет берётся с прозрачностью. */
  --accent-rgb: 17,17,17;
  --bg-rgb: 255,255,255;
  --text-rgb: 17,17,17;

  /* Смысловые цвета — единственные цветные во всей палитре. Ошибку и
     предупреждение чёрно-белыми не сделать: они должны выделяться. */
  --danger: #b00020;
  --danger-rgb: 176,0,32;
  /* Зелёный статуса Telegram — разрешён владельцем (2026-08-27) только для
     карточки на вкладке «Аккаунт». Блеклый, под монохром. */
  --tg-ok: #2f7d43;
  --tg-ok-rgb: 47,125,67;
  --warn: #8a5a00;
  --warn-rgb: 138,90,0;

  /* Зависящее от темы — переменными, а не правилами вида
     `[data-theme="dark"] .нечто`. Такое правило действует на всё внутри тёмного
     предка, включая копию страницы в светлой теме, которая живёт во время
     перехода: её логотип обращался сразу, ещё до анимации. Переменные же
     наследуются от ближайшего предка, и копия перебивает значение корня. */
  --logo-light: block;
  --logo-dark: none;
  --icon-sun: none;
  --icon-moon: block;

  /* Лестница в rem: при обычных настройках это те же 4…96 px, а если человек
     увеличил шрифт в браузере, отступы растут вместе с текстом и пропорции
     не разъезжаются (WCAG 1.4.4; отчёт о ритме, 2026-09-24). */
  --s-1: .25rem; --s-2: .5rem;  --s-3: .75rem; --s-4: 1rem;
  --s-5: 1.5rem; --s-6: 2rem;   --s-7: 3rem;   --s-8: 4rem;  --s-9: 6rem;
  --maxw: 1080px;
  --gutter: clamp(20px, 5vw, 48px);
  --section-y: clamp(56px, 8vw, 96px);
  /* Ритм лендинга — ОДНО расстояние между крупными блоками: первый экран →
     тарифы → вопросы → подвал (правка ревьюера №17, 2026-09-24). Прежде у
     каждого стыка было своё: 92, 104 и 40 пикселей, FAQ лип к подвалу.
     Считается и от высоты окна, а не только от ширины: лендинг листается
     экранами, и на невысоком ноутбуке большой отступ выталкивал бы блок
     за край. Каждая секция берёт половину сверху и половину снизу — на
     стыке складывается ровно один шаг. */
  --rhythm: clamp(64px, min(12vh, 10vw), 128px);
  /* Роли отступов (первая очередь по отчёту о ритме, 2026-09-24). Своих чисел
     у ролей нет — только ссылки на ступени лестницы: одна роль = одно число
     по всему сайту. Прежде «заголовок карточки → текст» был 4, 12, 16 и 24
     в разных местах, шаги в кабинете — 14, 24 и 22. Правило «внутреннее ≤
     внешнего»: каждая следующая роль не меньше предыдущей. */
  --sp-inline: var(--s-1);    /*  4 — внутри строки: иконка ↔ текст, строки списков */
  --sp-tie:    var(--s-2);    /*  8 — связка: подпись → поле → подсказка, цена → «в месяц» */
  --sp-text:   var(--s-3);    /* 12 — надзаголовок → заголовок, заголовок карточки → текст */
  --gap: var(--s-5);          /* 24 — между блоками: карточки, полосы, шаги кабинета */
  /* Шкала кеглей. Одна роль — один размер во всех разделах. Прежде заголовок
     карточки был 23px в одном блоке, 21px в другом и двумя разными clamp в
     третьем и четвёртом; текст — 16.5px, 16px и ещё два clamp. */
  --fs-title: clamp(2rem, 4.2vw, 3.125rem);        /* заголовок раздела */
  --fs-card-title: clamp(1.25rem, 1.6vw, 1.75rem);   /* заголовок карточки */
  --fs-card-title-lg: clamp(1.625rem, 2.4vw, 2.375rem);/* главная карточка блока */
  --fs-body: clamp(1rem, 1.05vw, 1.125rem);        /* основной текст */
  --fs-subhead: clamp(1.0625rem, 1.3vw, 1.5rem);      /* подзаголовок: вопрос FAQ,
                                                  заголовок блока кабинета */
  --fs-label: .8125rem;                            /* надстрочники и метки */

  /* Межстрочный интервал. Крупный текст набирается теснее мелкого: у
     заголовка строки короткие, и полуторный интервал разваливает их на
     отдельные фразы. 1.6 остаётся только основному тексту.
     Пять значений на весь сайт (2026-09-24): прежде их было десять —
     1.08, 1.12, 1.25, 1.28, 1.3, 1.45, 1.5, 1.6, 1.7, 1 — и соседние
     различались на сотые, чего глаз не видит, а система теряет порядок.
     Нижняя граница заголовков — 1.1: у Unbounded капсом строки ниже ~1.11
     касаются друг друга (бреве Й над хвостом Д, замер шрифта). */
  --lh-tight: 1;         /* цены, счётчики — одна строка крупных цифр */
  --lh-title: 1.1;       /* заголовок первого экрана, раздела, H1 страницы */
  --lh-head: 1.25;       /* заголовок карточки, модалки, ответа FAQ */
  --lh-ui: 1.5;          /* мелкий текст в несколько строк: полосы, сноски, согласие */
  --lh-body: 1.6;        /* основной текст */

  --font-body: 'Golos Text', system-ui, sans-serif;
  --font-display: 'Unbounded', 'Golos Text', sans-serif;
  --r-sm: 8px; --r-md: 10px; --r-lg: 14px; --r-glass: 20px;

  /* Стекло стало плоской поверхностью с волосяной рамкой: полупрозрачные
     подложки на чистом белом превращаются в грязь. */
  --glass-bg: none;
  --glass-bg-hot: none;
  --glass-border: #c9c9c9;
  --glass-hi: none;
  --glass-shadow: none;

  --clip-btn: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
  --clip-btn16: polygon(16px 0, 100% 0, 100% calc(100% - 16px), calc(100% - 16px) 100%, 0 100%, 0 16px);
  --clip-panel: polygon(22px 0, 100% 0, 100% calc(100% - 22px), calc(100% - 22px) 100%, 0 100%, 0 22px);
  --ice-edge: #c9c9c9;
  --ice-edge-hot: #111111;
}

/* Тёмная тема. Ставится сервером на <html> по куке, поэтому страница
   приходит уже в нужном виде и не моргает при загрузке. */
[data-theme="dark"] {
  --bg: #000000;
  --depth: #000000;
  --card: #000000;
  --border: #ffffff;
  --border-soft: #4a4a4a;
  --accent: #ffffff;
  --accent-hover: #ffffff;
  --text: #ffffff;
  --muted: #c4c4c4;
  --muted-2: #9a9a9a;    /* третий уровень, контраст к чёрному ~7.5:1 */
  --ink: #000000;

  --accent-rgb: 255,255,255;
  --bg-rgb: 0,0,0;
  --text-rgb: 255,255,255;
  --danger: #ff9b9b;
  --danger-rgb: 255,155,155;
  --tg-ok: #8fce9f;
  --tg-ok-rgb: 143,206,159;
  --warn: #ffce78;
  --warn-rgb: 255,206,120;

  --logo-light: none;
  --logo-dark: block;
  --icon-sun: block;
  --icon-moon: none;

  --glass-border: #4a4a4a;
  --ice-edge: #4a4a4a;
  --ice-edge-hot: #ffffff;
}

/* Wide screens: widen the column so 2K/4K don't squeeze content into a
   narrow strip. Everything else scales fluidly via clamp(). */
/* Колонка растёт до предела и дальше не идёт. Раньше на 4K она доходила до
   2200 пикселей: строка в такой ширине не читается, а первый экран рядом с ней
   выглядел вдвое уже остальной страницы. */
@media (min-width: 1700px) { :root { --maxw: 1200px; } }

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

/* ── Перелистывание лендинга по блокам (решение владельца 2026-08-26) ──
   Основную работу делает js/snap.js: один жест колеса — один блок. CSS-снап
   здесь только для сенсорных экранов и пути без JavaScript (proximity —
   мягкое выравнивание, когда блок уже рядом); на десктопе snap.js его
   ВЫКЛЮЧАЕТ: два аниматора на одном скролле поддёргивали страницу в конце
   пути. mandatory не используем сознательно: шаг колеса меньше полэкрана,
   и жёсткий снап утаскивал страницу назад — «экран прилипал к верху».
   :has(.hero) ограничивает эффект лендингом; без :has() прилипания просто
   нет — страница остаётся обычной. */
html:has(.hero) { scroll-snap-type: y proximity; }
.hero,
main.deep > .section { scroll-snap-align: start; }
/* У футера своей остановки нет: он часть последнего экрана (хвост FAQ +
   футер = «конец сайта»), отдельно к нему не листаем. proximity позволяет
   докрутить до низа свободно. */
/* Отступы секций лендинга — по полшага ритма (--rhythm) сверху и снизу:
   на любом стыке блоков выходит один и тот же шаг. Прежние поля были
   подобраны по отдельности (тарифы 56/48, FAQ 56/40) и дали три разных
   расстояния, а FAQ прилип к подвалу (правка ревьюера №17, 2026-09-24).
   Вместить экран по-прежнему помогает то, что ритм зависит от высоты окна.
   Нижний запас футера на лендинге срезан по-прежнему (владелец,
   2026-08-26); на остальных страницах футер не трогаем. */
main.deep > .section { padding-block: calc(var(--rhythm) / 2); }
/* Вторая половина шага до черты подвала: у футера нет своего поля сверху,
   и без неё FAQ стоял бы к подвалу вдвое ближе, чем блоки друг к другу. */
html:has(.hero) .footer { margin-top: calc(var(--rhythm) / 2); }
html:has(.hero) .footer .footer__inner { padding-block: 28px 40px; }

html, body {
  margin: 0;
  padding: 0;
  color: var(--text);
  font-family: var(--font-body);
  line-height: var(--lh-body);
}

/* Цвет фона живёт на html, а не на body. Слой с кодом уходит за содержимое
   отрицательным порядком, а фон body рисуется позже него — и полностью его
   закрывал бы. Фон html рисуется раньше, поэтому слой остаётся виден. */
html { background: var(--depth); }
/* Точка отсчёта для слоя с кодом: без неё он привязался бы к окну. */
body { position: relative; background: transparent; }

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

.mono { font-family: 'JetBrains Mono', ui-monospace, monospace; }

/* ─────────────────────────── HERO ─────────────────────────── */
.hero {
  position: relative;
  display: flex;            /* nav + content in a column; content stretches */
  flex-direction: column;
  width: 100%;
  /* Не 100vh: содержимое собрано в узкую колонку, и растянутый на весь экран
     блок просто отодвигал бы пустоту вниз. Высота — по содержимому, с нижней
     границей, чтобы на низких экранах ничего не слипалось. */
  /* Полная высота экрана, но не выше разумного на больших мониторах:
     полоса устройств должна стоять внизу первого экрана, а не подпирать
     карточку пробного периода. */
  min-height: min(100vh, 900px);
  overflow: hidden;
  /* Прозрачно: под страницей лежит закреплённый слой с кодом, а цвет даёт
     body. Непрозрачный фон здесь закрывал бы этот слой. */
  background-color: transparent;
}

/* (2) left-side readability gradient */

/* (3) submarine — continuous pass left→right, deep under the waterline */

/* (4) fish — continuous pass right→left */

/* (6) seamless dive: hero bottom dissolves into the sections' depth colour */

/* (5) snow — three CSS-particle layers, visible ONLY above the waterline */

/* ── Hero content ── */
.nav {
  position: relative;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Между названием, меню и кнопками — воздух, но умеренный: всё должно
     уместиться в ширину колонки. Это НАИМЕНЬШИЙ зазор — остаток ширины
     space-between раздаёт поровну. Потолок 20, а не 32: при 32 семь зазоров
     съедали 224 пикселя, и строка вылезала за колонку. */
  gap: clamp(12px, 1.6vw, 20px);
  /* Шапка — контейнер: кегль названия и меню считается от ЕЁ ширины (cqi),
     а не от ширины окна. Колонка перестаёт расти на --maxw, а кегль в vw
     рос дальше — на 1440 правый край шапки вылезал за колонку на 89px, на
     1920 — на 195 (владелец, 2026-09-24: «хедер кривой, не отцентрован»).
     Фиксированных потомков у шапки нет — сдерживание их не заденет. */
  container-type: inline-size;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding-inline: var(--gutter);
  /* Выше прежнего: логотип вырос, и в старую шапку он не помещался.
     Ограничение по высоте экрана обязательно: 10vw на мониторе 1920×1000 —
     это 192 пикселя шапки при тысяче доступных, и полоса устройств внизу
     первого экрана уже не помещалась. */
  height: clamp(148px, min(10vw, 18vh), 210px);
  flex-shrink: 0;
}
.nav__brand { display: flex; align-items: center; gap: 14px; }
.nav__logo {
  width: clamp(112px, 8vw, 168px);
  height: clamp(112px, 8vw, 168px);
  object-fit: contain;
}
.nav__name {
  font-family: var(--font-display);
  font-weight: 700;
  /* Один размер с пунктами меню, от ширины шапки (см. .nav): строка
     «название · меню · кнопки» при любом окне укладывается ровно от края
     до края колонки. 1.48cqi — наибольшее, что влезает с логотипом 168px
     и кнопкой «Кабинет» (худший случай — окно 1699: колонка ещё 984, а
     логотип и кнопка уже крупные); первая строка — запас для браузера
     без cqi. Границы здесь в px, хотя остальной сайт в rem (2026-09-24):
     строка шапки подогнана к колонке впритык, и крупный шрифт браузера
     через rem выдавил бы её за край. Растёт шапка вместе с зумом — зум
     сужает окно в CSS-пикселях, и на ≤760 меню и так сворачивается. */
  font-size: clamp(.8125rem, 1.05vw, 1rem);
  font-size: clamp(13px, 1.48cqi, 17px);
  letter-spacing: 3px;
  color: var(--text);
}
.nav__right { display: flex; align-items: center; gap: clamp(10px, 1.1vw, 20px); }

/* ── Колокольчик уведомлений ── */
.bell { position: relative; }
.bell__btn {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(44px, 2.6vw, 52px);
  height: clamp(44px, 2.6vw, 52px);
  padding: 0;
  border: none;
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: color .2s ease;
}
.bell__btn:hover, .bell__btn[aria-expanded="true"] { color: var(--text); }
.bell__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bell__icon { width: 60%; height: 60%; }
.bell__badge {
  position: absolute;
  top: 12%; right: 10%;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 100px;
  background: var(--accent);
  color: var(--ink);
  font-family: 'JetBrains Mono', monospace;
  font-size: .6875rem;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
}
.bell__badge[hidden] { display: none; }

.bell__panel {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  z-index: 40;
  width: min(360px, calc(100vw - 2 * var(--gutter)));
  padding: var(--s-4);
  clip-path: var(--clip-panel);
  background: linear-gradient(150deg, rgba(var(--bg-rgb),.97), rgba(var(--bg-rgb),.97));
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
}
.bell__panel[hidden] { display: none; }
.bell__head { margin-bottom: var(--s-3); }
.bell__title {
  font-family: 'JetBrains Mono', monospace;
  font-size: .6875rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--muted);
}
.bell__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-2); }
.bell__item {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  padding: var(--s-3);
  /* Без серой подложки: на чёрном она давала мутное пятно. Элементы
     разделяет только полоса слева. */
  background: transparent;
  border-left: 2px solid var(--border);
}
.bell__item--warning { border-left-color: var(--warn); background: rgba(var(--warn-rgb),.07); }
.bell__item-body { flex: 1; min-width: 0; }
.bell__item-title { margin: 0; font-size: .875rem; font-weight: 600; color: var(--text); }
.bell__item-text { margin: 4px 0 0; font-size: .8125rem; color: var(--muted); line-height: var(--lh-ui); }
.bell__item-action {
  display: inline-block;
  margin-top: var(--s-2);
  padding: 5px 12px;
  clip-path: var(--clip-btn);
  /* Сплошная заливка вместо полупрозрачной: та давала серый прямоугольник,
     который спорил с текстом рядом. */
  background: var(--accent);
  font-size: .8125rem;
  font-weight: 600;
  color: var(--ink);
  transition: background .2s ease;
}
a.bell__item-action:hover { background: rgba(var(--accent-rgb),.3); color: var(--accent); }
/* действие показано, но пока недоступно */
.bell__item-action--off {
  background: rgba(var(--text-rgb),.05);
  box-shadow: inset 0 0 0 1px var(--border);
  color: var(--muted-2);
  cursor: not-allowed;
}
.bell__item-note {
  display: block;
  margin-top: var(--sp-inline);
  font-size: .6875rem;
  color: var(--muted-2);
}
.bell__empty { margin: 0; padding: var(--s-3) 0; font-size: .8125rem; color: var(--muted-2); text-align: center; }
.bell__empty[hidden] { display: none; }

.nav__links {
  /* display: contents распускает контейнер: «Тарифы», черта и «FAQ»
     становятся элементами самой шапки, и её space-between раскладывает
     РАВНЫЕ промежутки между всеми пятью элементами строки — от текста
     логотипа до кнопок (владелец, 2026-08-26). Любая раскладка внутри
     отдельного контейнера давала «разные отступы»: у полосы меню свои
     края, и её ритм не совпадал с ритмом всей строки. Шрифтовые свойства
     ниже наследуются ссылкам — бокса у контейнера больше нет. */
  display: contents;
  /* Тот же шрифт, кегль и разрядка, что у названия: пункты меню читаются
     как продолжение логотипа, а не как подпись под ним. */
  font-family: var(--font-display);
  /* Тот же кегль, что у названия, и тоже капсом: «Тарифы» строчными рядом
     с латинскими PARANOIA и FAQ выглядели чужеродно — у строчной кириллицы
     другая оптическая высота. Капс уравнивает алфавиты (владелец,
     2026-08-26). Кегль — от ширины шапки, как у названия (см. .nav). */
  font-size: clamp(.8125rem, 1.05vw, 1rem);
  font-size: clamp(13px, 1.48cqi, 17px);
  font-weight: 700;
  letter-spacing: 3px;
  text-transform: uppercase;
}
/* Перенос запрещён: «Как подключить» ломался на две строки и задирал шапку.
   Поля сверху и снизу поднимают высоту ссылки до 44 пикселей — порога, при
   котором палец попадает уверенно. Внешне ничего не меняется: текст остаётся
   на месте, растёт только область нажатия. */
.nav__links a { white-space: nowrap; padding-block: 10px; justify-self: center; }
/* Обёртка-форма главной CTA героя: распускается, чтобы кнопка была прямым
   элементом ряда кнопок и вставала в общий gap (правка ревьюера 2026-09-08). */
.hero__cta-form { display: contents; }
/* Та же распущенная обёртка у CTA пробной полосы тарифов. */
.tariff__cta-form { display: contents; }

/* ── Полоса пробного периода над тарифами (владелец, 2026-09-08) ──
   Низкая и широкая: срок и цена слева, условия строкой по центру, кнопка
   справа. Плита та же, что у тарифов (.tariff), меняется только раскладка. */
.tariff.ttrial { /* двойной класс: перебивает колонку .tariff, стоящую ниже по файлу */
  --ttrial-fs: clamp(12px, .82vw, 14px);   /* единый кегль всего текста полосы */
  flex-direction: row;
  align-items: center;
  gap: clamp(16px, 2.5vw, 48px);
  padding: var(--s-4) var(--s-6);
  margin-bottom: var(--gap);
}
.ttrial__head { flex-shrink: 0; }
/* В полосе заголовок и подпись стоят вплотную: нижний отступ от карточек
   тарифов здесь лишний. */
.ttrial__head .tariff__period { margin-bottom: 0; }
/* Подпись и характеристики — единый набор техметок: один шрифт, один кегль
   (владелец, 2026-09-08: «шрифты разного стиля и размера, несуразно»).
   Отличаются только цветом: подпись приглушена, характеристики основные. */
.ttrial__sub {
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--ttrial-fs);
  color: var(--muted);
  margin-top: 4px;
}
/* Правила пробного — сплошной текст в тех же техметках, что и подпись
   слева: один шрифт и кегль на всю полосу. Ширину не задаём — занимает
   всё между заголовком и кнопкой. */
.ttrial__rules {
  flex: 1;
  margin: 0;
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--ttrial-fs);
  line-height: var(--lh-body);
  color: var(--text);
  max-width: 68ch;
}
.ttrial__cta { flex-shrink: 0; min-width: 200px; padding: 0 26px; height: clamp(48px, 3vw, 60px); }
/* Узкий экран: полоса складывается в столбик, кнопка на всю ширину. */
@media (max-width: 900px) {
  .tariff.ttrial { flex-direction: column; align-items: stretch; }
}

/* «Как подключить» — самый длинный пункт меню: на 761–960px строка шапки
   с ним не помещается («Войти» выдавливалась за экран, замер 2026-09-08).
   Прячем только его — «Тарифы | FAQ» на этих ширинах влезают. */
@media (max-width: 960px) {
  .nav__howto, .nav__sep--howto { display: none; }
}

/* Вертикальная черта между пунктами, растворяющаяся к концам — как линия
   под первым экраном и как фон с кодом. */
/* Вертикальная черта, растворяющаяся к концам. Высота в em — растёт вместе
   с кеглем меню. */
.nav__sep {
  flex: none;
  width: 1px;
  height: 1.7em;
  background: linear-gradient(to bottom,
    transparent, var(--border-soft) 22%, var(--border-soft) 78%, transparent);
}
/* Основной цвет, а не приглушённый: это разделы сайта, а не сноски. */
.nav__links a { color: var(--text); }
/* Наведение без нового цвета: --accent совпадает с --text, и «смена цвета»
   была невидимой (аудит 02.10) — даём подчёркивание. */
.nav__links a:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 4px; }

.hero__content {
  position: relative;
  z-index: 10;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  /* Верхний отступ скромный: он теперь отделяет текст от шапки, а не
     центрирует его. Нижний — полшага ритма, как у секций ниже: вместе с
     верхним полем тарифов выходит тот же шаг, что между остальными блоками
     (правка ревьюера №17). */
  padding: clamp(28px, 4vh, 56px) var(--gutter) calc(var(--rhythm) / 2);
  /* fill the hero so the platforms strip can sit at the bottom edge */
  flex: 1;
  display: flex;
  flex-direction: column;
  /* Содержимое начинается сразу под шапкой, полоса устройств прижата к низу.
     По центру оно смотрелось спокойнее, но съедало весь запас высоты: на
     невысоких экранах полоса уходила за нижний край. Видимость важнее. */
  justify-content: flex-start;
  align-items: center;
  text-align: center;
  /* Один отступ на все группы вместо шести собственных у каждого блока.
     Считается от высоты экрана, а не задан числом: на невысоком мониторе три
     таких отступа — почти сто пикселей, из-за которых полоса устройств
     не помещалась. Значение одно на все группы, поэтому ритм не сбивается. */
  gap: clamp(20px, 3.2vh, 32px);
  min-height: 0;
}
.hero__title {
  font-family: var(--font-display);
  /* Крупнее прежнего: в центрированной вёрстке заголовок — единственный
     держатель композиции, мелкий теряется среди пустоты.

     Потолок 64, а не 76. Строка «Шифрование трафика.» примерно в 15 раз шире
     кегля, и при 76 пикселях ей нужно 1150 — больше, чем ширина колонки. Она
     разрывалась, заголовок становился трёхстрочным, и эти лишние 70 пикселей
     выталкивали полосу устройств за нижний край экрана. */
  font-size: clamp(2rem, 4.4vw, 4rem);
  font-weight: 700;
  line-height: var(--lh-title);
  letter-spacing: -1.5px;
  margin: 0;
  /* Строки разделены переносом в разметке. Мера подобрана так, чтобы длинная
     из них — «Шифрование трафика.» — умещалась целиком: иначе она рвётся
     посередине и центрированный заголовок теряет форму.

     24, а не 21: на широких экранах кегль растёт быстрее, чем мера в ch, и
     с 21 строка всё-таки разрывалась. Третья строка — это ещё 80 пикселей
     высоты, из-за которых полоса устройств уходила за нижний край. */
  max-width: min(100%, 24ch);
  /* Страховка на 320 px (зум 400%, WCAG 1.4.10): слово шире строки
     переносится, а не вылезает за экран. Обычный перенос по словам это
     не трогает — срабатывает, только когда слово не помещается целиком. */
  overflow-wrap: break-word;
}
.hero__title .line-1 { color: var(--text); }
.hero__title .line-2 { color: var(--accent); }
.hero__lead {
  font-size: clamp(1rem, 1.05vw, 1.3125rem);
  color: var(--muted);
  /* Мера строки короче обычного: длинные строки по центру читать тяжело —
     глаз теряет начало следующей. 58 знаков — верх этого диапазона; вместе
     с суженной колонкой страницы первый экран и разделы ниже перестали
     различаться по ширине вдвое. */
  max-width: 58ch;
  margin: 0;
}
/* Группа: внутри вдвое теснее, чем между группами — так подпись читается
   как часть кнопок, а надстрочник как часть заголовка. */
.hero__group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
}
.hero__actions { display: flex; gap: var(--s-4); flex-wrap: wrap;
                 align-items: center; justify-content: center; }
.hero__trust {
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.6875rem, .76vw, 1rem);
  letter-spacing: 1px;
  color: var(--muted-2);
  margin-top: 0;
}
/* Free-trial card — a shard of ice in the hero */
/* Во всю ширину колонки — кромками вровень с тарифами, FAQ и подвалом
   (правка ревьюера №17). Прежде 660–760 из 984, и первый экран выглядел
   уже остальных. Раскладка и поля — как у полосы пробного в тарифах. */
.hero__trial {
  position: relative;
  margin-top: 0;
  width: 100%;
  display: flex;
  align-items: center;
  text-align: left;
  gap: clamp(16px, 2.5vw, 48px);
  padding: var(--s-4) var(--s-6);
  background: linear-gradient(150deg, rgba(var(--bg-rgb),.82), rgba(var(--bg-rgb),.72));
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  clip-path: var(--clip-panel);
}
.hero__trial-head { flex-shrink: 0; }
.hero__trial-body { flex: 1; }
/* Первая строка правой части встаёт вровень с заголовком слева: её
   отступ сверху был рассчитан на стопку под заголовком. */
.hero__trial-body > :first-child { margin-top: 0; }
.hero__trial-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.625rem, .72vw, .9375rem);
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--accent);
}
.hero__trial-title {
  font-family: var(--font-display);
  font-size: var(--fs-card-title);
  font-weight: 600;
  line-height: var(--lh-head);
  margin: var(--sp-text) 0 0;       /* надзаголовок → заголовок, как везде */
  letter-spacing: -.3px;
}
/* Условия пробного (дни · устройства · трафик) — техметкой, как «200 ₽ в
   месяц» под кнопками: это цифры, а не проза (правка ревьюера 2026-09-08). */
.hero__trial-terms {
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.6875rem, .8vw, .9375rem);
  letter-spacing: 1px;
  color: var(--text);
  margin: 8px 0 0;
}
.hero__trial-text { font-size: clamp(.75rem, .88vw, 1.125rem); color: var(--muted); margin: var(--sp-tie) 0 0; }

/* compact platforms strip at the bottom of the hero */
/* Platforms strip — pinned to the bottom of the hero via `margin-top:auto`, so
   it fills the empty space on tall/4K screens and simply follows the content on
   short ones. Everything scales with vw and wraps, so it never overflows. */
/* ── Невысокий экран: распускаем вширь, а не сжимаем ──
   Когда высоты не хватает, а ширина есть, строки растягиваются в стороны и
   блоки становятся ниже. Размеры шрифтов и отступы при этом не трогаем:
   порядок и вид первого экрана остаются прежними, меняется только перенос. */
@media (max-height: 900px) and (min-width: 1100px) {
  .hero__lead { max-width: min(100%, 82ch); }
  /* Плашка пробного здесь больше не сужается: она во всю колонку на любой
     высоте окна (правка ревьюера №17). */
  .hero__trial-text { max-width: none; }
}

/* ── Код на фоне страницы ──
   Слева шум, справа тот же набор знаков ровными столбцами.

   Ширина поля не задана числом, а считается: сколько места осталось рядом с
   колонкой текста, столько поле и занимает. Поэтому на широком мониторе оно
   растёт вместе с полями страницы, а на узком сходит на нет — фиксированные
   пиксели выглядели бы правильно только на одной ширине. */
.codebg {
  --free: max(0px, (100vw - var(--maxw)) / 2);
  /* Привязано к странице, а не к окну: при прокрутке фон уходит вместе с
     содержимым, а не едет следом за глазами. Слой лежит за содержимым
     отрицательным порядком — иначе рисовался бы поверх текста. */
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: -1;
  /* Небольшой заход под колонку: край поля не должен обрываться ровно на
     границе текста, там его гасит растворение. */
  width: calc(var(--free) + 90px);
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  overflow: hidden;
  font-family: 'JetBrains Mono', monospace;
  font-size: .8125rem;
  line-height: 1.5;
  letter-spacing: .14em;
  color: var(--muted-2);
  opacity: .3;
  white-space: pre;
  user-select: none;
  pointer-events: none;
  /* Растворение только к середине страницы: от края поле идёт в полную силу
     и гаснет там, где начинается текст. Вертикального растворения нет —
     из-за него верх и низ фона уходили в ноль, хотя гасить их незачем. */
  -webkit-mask-image: var(--side-fade);
          mask-image: var(--side-fade);
}
.codebg--noise {
  left: 0;
  --side-fade: linear-gradient(to right, #000 45%, transparent);
}
.codebg--order {
  right: 0;
  /* Строки прижаты к правому краю: длинная строка уходит наружу влево и
     срезается, а не обрывается на виду у края экрана. */
  text-align: right;
  --side-fade: linear-gradient(to left, #000 45%, transparent);
}

/* Лента едет вверх ровно на длину одной копии строк (их выведено две, см.
   base.html) и по кругу: в конце цикла на месте первой копии стоит вторая,
   такая же, — стыка не видно, лента бесконечная. Сдвиг через transform: его
   считает видеокарта.
   Раньше лента сдвигалась на одну строку и прыгала назад. Строки разные,
   поэтому раз в 7 с (справа — в 21 с) весь текст дёргался на строку —
   владелец заметил на долгой стоянке на странице (2026-10-02).
   Скорость прежняя: строка за 7 с, 252 строки — 1764 с. Число строк кратно
   84, иначе на стыке менялась бы прозрачность строк (nth-child ниже). */
.codebg__tape {
  display: flex;
  flex-direction: column;
  animation: tape-drift 1764s linear infinite;
}
.codebg--order .codebg__tape {
  /* Порядок ползёт медленнее (строка за 21 с) и в другую сторону: рядом с
     текущим шумом он читается как почти неподвижный — в этом и мысль. */
  animation-duration: 5292s;
  animation-direction: reverse;
}
@keyframes tape-drift {
  to { transform: translateY(-50%); }   /* ровно одна копия строк */
}

/* Неровная плотность: одинаковая прозрачность у всех строк складывается в
   ровную серую полосу, а разброс делает поле живым. */
.codebg span { opacity: .9; }
.codebg span:nth-child(3n)   { opacity: .55; }
.codebg span:nth-child(4n+1) { opacity: 1; }
.codebg span:nth-child(7n)   { opacity: .35; }

/* Свободного места рядом с колонкой не осталось — убираем: узкая полоска у
   самого края выглядит обрезком, а не приёмом. */
@media (max-width: 1180px) {
  .codebg { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .codebg__tape { animation: none; }
}

.hero__platforms {
  width: 100%;
  margin-top: auto;
  padding-top: clamp(28px, 5vh, 64px);
  /* Черта нарисована градиентом, а не рамкой: рамка обрывается ровными
     торцами, а здесь края растворяются — как у фона с кодом. */
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(16px, 2.2vw, 44px);
  flex-wrap: wrap;
}
.hero__platforms::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(to right,
    transparent, var(--border-soft) 18%, var(--border-soft) 82%, transparent);
}
.hero__platforms-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.6875rem, .74vw, .9375rem);
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--muted-2);
}
.hero__platforms-icons {
  display: flex;
  align-items: center;
  gap: clamp(18px, 2.4vw, 52px);
  flex-wrap: wrap;
}
.hplat {
  display: inline-flex;
  align-items: center;
  gap: clamp(8px, .7vw, 14px);
  font-family: var(--font-display);
  font-size: clamp(.75rem, .84vw, 1.125rem);
  font-weight: 500;
  color: var(--text);
}
.hplat svg {
  width: clamp(20px, 1.6vw, 33px);
  height: clamp(20px, 1.6vw, 33px);
  opacity: .9;
}

/* ─────────────────── Glass button component (.gbtn) ───────────────────
   Approved geometry & palette — chamfered glass + turquoise hover glow.
   Border follows the срез via an inset shadow (traces clip-path on every
   side); the hover glow uses drop-shadow so it hugs the cut corners —
   a box-shadow glow would stay rectangular and spill past the chamfer. */
.gbtn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: clamp(56px, 3.7vw, 74px);
  min-width: clamp(228px, 16vw, 300px);
  padding: 0 clamp(24px, 2vw, 40px);
  border: none;
  background: none;
  cursor: pointer;
}
.gbtn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* Glass fill — real backdrop blur, inset volume, and a 1.4px edge that
   traces the chamfer on every side, including the two cut corners. */
/* Второстепенная кнопка: прозрачная, только рамка. Полупрозрачная заливка на
   чистом белом даёт грязно-серое пятно, а размытие фона нечего размывать. */
.gbtn__glass {
  position: absolute;
  inset: 0;
  z-index: 0;
  clip-path: var(--clip-btn16);
  background: transparent;
  /* Рамку рисует не тень, а полигон .cedge: inset-тень не умеет обводить
     диагональ и обрывается ровно на срезанных углах. */
  transition: background .2s ease;
}
/* Hover: plumper glass + a turquoise halo that follows the chamfered
   silhouette (drop-shadow tracks the shape; box-shadow would stay rectangular). */
.gbtn:hover .gbtn__glass { background: rgba(var(--text-rgb), .07); }

/* ── Фокус с клавиатуры ──
   Общее правило для всего, у чего нет своего. У элементов со срезом угла
   (clip-path) внешняя рамка срезается целиком — рисуем её внутри
   (WCAG 2.4.7, аудит 02.10). */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.themebtn:focus-visible,
.cab__copy:focus-visible,
.devlist__unlink:focus-visible,
.devall:focus-visible,
.checkout__change:focus-visible,
.modal__close:focus-visible,
.bell__item-action:focus-visible,
.field__input:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; }

/* ── Chamfer edge (.cedge) — shared crisp SVG polygon stroke that traces the
   срез on EVERY side, including the cut corners (box-shadow / inset / mask
   cannot stroke a diagonal). chamfer-edge.js injects & sizes the <svg> per
   block from its clip-path; stroke colour is set per component below. ── */
.cedge {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
/* Цвет обводки по умолчанию — иначе у блоков без своего правила полигон
   рисуется без штриха, то есть невидим, и срез остаётся без рамки. */
.cedge polygon {
  fill: none;
  stroke: var(--border-soft);
  stroke-width: 1.4;
  transition: stroke .3s ease;
}

/* Действия и рамки, к которым нужно привлечь внимание, — основным цветом. */
.bell__item-action > .cedge polygon,
.devlist__unlink > .cedge polygon,
.devall > .cedge polygon,
.checkout__change > .cedge polygon,
.cab__copy > .cedge polygon { stroke: var(--border); }

/* Смысловые блоки сохраняют свой цвет: рамка должна совпадать с текстом. */
.authbox__error > .cedge polygon,
.panel-error > .cedge polygon { stroke: var(--danger); }
.authbox__notice > .cedge polygon,
.panel-ok > .cedge polygon { stroke: var(--accent); }
.devnote > .cedge polygon,
.testbar > .cedge polygon { stroke: var(--warn); }

/* glass button — gradient stroke, brightens on hover */
.gbtn > .cedge polygon { stroke: var(--border); }
.gbtn:hover > .cedge polygon { stroke: var(--accent); }

/* ice panels */
.glass > .cedge polygon,
.tariff > .cedge polygon,
.faq__panel > .cedge polygon,
.authbox > .cedge polygon { stroke: var(--ice-edge); }
.glass:hover > .cedge polygon,
.tariff:hover > .cedge polygon { stroke: var(--ice-edge-hot); }
.hero__trial > .cedge polygon,
.tglink > .cedge polygon { stroke: var(--ice-edge-hot); }

/* small outline buttons */

.gbtn__label {
  position: relative;
  z-index: 2;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(.8125rem, .9vw, 1.125rem);
  text-align: center;
  color: var(--text);
}

/* Compact size (nav) — same component, smaller box */
.gbtn--sm {
  height: clamp(44px, 2.8vw, 58px);
  min-width: 0;
  padding: 0 clamp(20px, 1.5vw, 30px);
}
.gbtn--sm .gbtn__label { font-size: clamp(.75rem, .78vw, 1rem); }

/* Главное действие — сплошная заливка. В монохроме полутон не читается как
   кнопка: он выглядит выключенным, а не главным. */
.gbtn--accent .gbtn__glass {
  background: var(--accent);
  box-shadow: none;
}
.gbtn--accent:hover .gbtn__glass { background: var(--accent-hover); opacity: .85; }   /* отклик заметен: --accent-hover почти равен --accent */
.gbtn--accent > .cedge polygon { stroke: var(--accent); }
.gbtn--accent:hover > .cedge polygon { stroke: var(--accent-hover); }
.gbtn--accent .gbtn__label { color: var(--ink); text-shadow: none; }

@media (prefers-reduced-motion: reduce) {
  .gbtn__glass { transition: none; }
}

/* ─────────────────────────── SECTIONS ─────────────────────────── */
/* All sections below the hero live in one deep, faintly glowing volume —
   плоский фон, панели отделены только рамкой. */
.deep { position: relative; background: transparent; overflow: hidden; }
.deep > .section, .deep > .footer { position: relative; z-index: 1; }

.section {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding-block: var(--section-y);
  padding-inline: var(--gutter);
}
.eyebrow {
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--fs-label);
  letter-spacing: 3px;
  color: var(--accent);
  text-transform: uppercase;
  margin-bottom: var(--s-3);
}
.section__title {
  font-size: var(--fs-title);
  /* 700 вместо 800: разница в одну ступень на глаз не читается как иерархия,
     а начертаний в системе становится на одно больше. Осталось три на весь
     сайт — 400 текст, 600 подзаголовки, 700 заголовки и крупные числа. */
  font-weight: 700;
  line-height: var(--lh-title);
  letter-spacing: -1.2px;
  margin: 0 0 var(--s-7);
  overflow-wrap: break-word;        /* та же страховка, что у первого экрана */
}
.section__head-cta { margin-top: var(--s-4); }

/* Art placeholder — describes the illustration that goes here (TODO: art) */

/* auto-fit grid — used by steps & tariffs. */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--gap);
  align-items: stretch;
}

/* ── Glass surface — the shared premium panel ── */
.glass {
  position: relative;
  border-radius: 0;
  clip-path: var(--clip-panel);
  padding: var(--s-6);
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  overflow: hidden;
}

/* ── Tariffs — одинаковые ледяные плиты, отличается только метка ── */
.tariffs { align-items: stretch; }
.tariff {
  position: relative;
  border-radius: 0;
  clip-path: var(--clip-panel);
  padding: var(--s-6);
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  display: flex;
  flex-direction: column;
}
/* Рекомендуемый тариф отличается только меткой — ни подъёма, ни подсветки. */
.tariff__badge-row {
  display: flex;
  align-items: center;
  height: 30px;                  /* место под метку резервируется во всех карточках */
  flex-shrink: 0;
  margin-bottom: var(--s-3);
}
.tariff__badge {
  display: inline-block;
  padding: 5px 14px;
  background: var(--accent);
  color: var(--ink);
  clip-path: var(--clip-btn);
  font-family: 'JetBrains Mono', monospace;
  font-size: .8125rem;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
}
.tariff__period {
  font-family: 'JetBrains Mono', monospace;
  font-size: .875rem;
  letter-spacing: 2px;
  color: var(--accent);
  text-transform: uppercase;
  margin-bottom: var(--s-3);
}
/* Цена — ступенью ниже заголовка раздела. Прежние 48px против 50px читались
   как один уровень: два почти равных размера рядом спорят, а не строят
   иерархию. */
.tariff__price { font-size: clamp(1.75rem, 2.6vw, 2.5rem); font-weight: 700; line-height: var(--lh-tight); }
.tariff__price .cur { font-size: .52em; color: var(--muted-2); font-weight: 600; }
.tariff__permonth {
  font-family: 'JetBrains Mono', monospace;
  font-size: .9375rem;
  color: var(--muted-2);
  margin: var(--s-2) 0 var(--s-5);
  min-height: 20px;                 /* fixed slot so the row aligns across cards */
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.tariff__save {
  font-family: 'JetBrains Mono', monospace;
  font-size: .8125rem;
  font-weight: 700;
  color: var(--accent);
  background: rgba(var(--accent-rgb),.12);
  border-radius: 100px;
  padding: 2px 8px;
}
.tariff__feats { list-style: none; margin: 0 0 var(--s-6); padding: 0; flex: 1; }
.tariff__feats li {
  font-size: var(--fs-body);
  color: var(--muted);
  padding: var(--sp-tie) 0;
  border-bottom: 1px solid var(--border-soft);
  display: flex;
  gap: 10px;
}
.tariff__feats .check { color: var(--accent); font-weight: 700; }
/* CTA внутри карточки — тот же компонент .gbtn, на всю ширину */
.tariff__cta { width: 100%; min-width: 0; }

/* ── FAQ — вопросы слева, ответ справа (паттерн вкладок) ── */
.faq {
  display: grid;
  grid-template-columns: minmax(280px, 0.9fr) 1.1fr;
  gap: clamp(24px, 3vw, 56px);
  align-items: start;
}

/* левая колонка — список вопросов */
.faq__list { display: flex; flex-direction: column; }
.faq__q {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  width: 100%;
  padding: clamp(16px, 1.4vw, 24px) 0;
  border: none;
  border-bottom: 1px solid var(--border);
  background: none;
  color: var(--muted);
  font-family: var(--font-body);
  font-size: var(--fs-subhead);
  font-weight: 600;
  line-height: var(--lh-head);
  text-align: left;
  cursor: pointer;
  transition: color .25s ease, padding-left .25s ease;
}
.faq__list .faq__q:last-child { border-bottom: none; }
.faq__q:hover { color: var(--text); }
.faq__q:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.faq__q.is-active { color: var(--text); padding-left: var(--s-3); cursor: default; }
/* бирюзовая засечка у активного вопроса */
.faq__q::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 2px; height: 0;
  background: var(--accent);
  transform: translateY(-50%);
  transition: height .3s ease;
}
.faq__q.is-active::before { height: 62%; }
.faq__q-mark {
  flex-shrink: 0;
  width: 10px; height: 10px;
  border-right: 1.5px solid currentColor;
  border-top: 1.5px solid currentColor;
  transform: rotate(45deg);
  opacity: .45;
  transition: opacity .25s ease, color .25s ease;
}
.faq__q.is-active .faq__q-mark { opacity: 1; color: var(--accent); }

/* правая колонка — панель с ответом (стекло со срезом) */
.faq__panel {
  position: relative;
  display: grid;                 /* все ответы в одной ячейке → нет скачков высоты */
  border-radius: 0;
  clip-path: var(--clip-panel);
  padding: clamp(28px, 2.6vw, 48px);
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
}
.faq__a {
  grid-area: 1 / 1;
  opacity: 0;
  transform: translateX(28px);
  pointer-events: none;
  transition: opacity .45s ease, transform .45s cubic-bezier(.2,.7,.2,1);
}
.faq__a[hidden] { display: block; }   /* прячем через прозрачность, а не display */
/* …но и для программ чтения с экрана: прозрачный ответ оставался в дереве, и
   озвучивались все пять (аудит 02.10). visibility меняется после затухания. */
.faq__a:not(.is-active) { visibility: hidden; transition: opacity .45s ease, transform .45s cubic-bezier(.2,.7,.2,1), visibility 0s linear .45s; }
.faq__a.is-active {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
.faq__a-num {
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.6875rem, .78vw, .9375rem);
  letter-spacing: 3px;
  color: var(--accent);
}
.faq__a-title {
  font-family: var(--font-display);
  font-size: var(--fs-card-title);
  font-weight: 600;
  line-height: var(--lh-head);
  letter-spacing: -.4px;
  margin: var(--s-3) 0 0;
}
.faq__a-text {
  font-size: var(--fs-body);
  color: var(--muted);
  margin: var(--sp-text) 0 0;       /* заголовок → текст — одна роль по сайту */
  max-width: 46ch;
}

/* ─────────────── Внутренние страницы (/login, /checkout, /cabinet) ─────────────── */
.page { min-height: 100vh; display: flex; flex-direction: column; }
.page__body {
  position: relative;
  z-index: 1;
  flex: 1;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(32px, 5vh, 72px) var(--gutter) clamp(48px, 7vh, 96px);
}
.page__body--wide { max-width: var(--maxw); }
.page__title {
  font-family: var(--font-display);
  font-size: clamp(1.625rem, 2.4vw, 3rem);
  font-weight: 700;
  line-height: var(--lh-title);
  letter-spacing: -.8px;
  margin: var(--s-3) 0 var(--s-7);
}
/* Подводка под заголовком страницы — одно предложение о том, что сейчас
   произойдёт. Отрицательное верхнее поле съедает нижний отступ заголовка:
   иначе между ними было бы 48 пикселей, вдвое больше, чем внутри группы. */
.page__lead {
  max-width: 62ch;
  margin: calc(-1 * var(--s-6)) 0 var(--s-7);
  font-size: var(--fs-body);
  color: var(--muted);
}

/* Что произойдёт после оплаты. Не приглушено до сноски: это ответ на главный
   вопрос перед списанием денег. */
.checkout__after {
  margin: var(--s-4) 0 0;
  font-size: clamp(.8125rem, .9vw, 1rem);
  color: var(--muted);
  line-height: var(--lh-ui);
}

.panel-error {
  margin: 0 0 var(--gap);
  padding: var(--s-3) var(--s-5);
  clip-path: var(--clip-btn);
  background: rgba(var(--danger-rgb),.08);
  color: var(--danger);
  font-size: clamp(.8125rem, .9vw, 1rem);
}

.panel-ok {
  margin: 0 0 var(--gap);
  padding: var(--s-3) var(--s-5);
  clip-path: var(--clip-btn);
  background: rgba(var(--accent-rgb),.08);
  color: var(--accent);
  font-size: clamp(.8125rem, .9vw, 1rem);
}

.page__stub-note {
  margin: var(--s-7) 0 0;
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.6875rem, .78vw, .875rem);
  color: var(--muted-2);
  border-left: 2px solid var(--ice-edge);
  padding-left: var(--s-4);
}

/* ── Поля формы ── */
.field { margin-bottom: var(--s-5); }
.field__label {
  display: block;
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.6875rem, .74vw, .875rem);
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--muted-2);
  margin-bottom: var(--s-2);
}
.field__input {
  width: 100%;
  padding: clamp(14px, 1.2vw, 20px) clamp(16px, 1.3vw, 22px);
  border: none;
  border-radius: 0;
  clip-path: var(--clip-btn);
  background: rgba(var(--bg-rgb),.6);
  /* Рамка — --muted-2: у --ice-edge контраст с фоном 1.66 / 2.37 при норме 3:1
     для границ элементов управления (WCAG 1.4.11, аудит 02.10). */
  box-shadow: inset 0 0 0 1.4px var(--muted-2);
  color: var(--text);
  font-family: var(--font-body);
  font-size: clamp(.9375rem, 1vw, 1.125rem);
  transition: box-shadow .25s ease;
}
.field__input::placeholder { color: var(--muted-2); }
.field__input:focus {
  outline: none;
  box-shadow: inset 0 0 0 1.4px var(--accent);
}
.field__input--code {
  font-family: 'JetBrains Mono', monospace;
  letter-spacing: 8px;
  text-align: center;
}
.field__hint { font-size: clamp(.75rem, .84vw, .9375rem); color: var(--muted-2); margin: var(--s-2) 0 0; }

/* ── /login — страница строго в один экран, без прокрутки ── */
.page--auth { min-height: 100vh; display: flex; flex-direction: column; }
.page__center {
  position: relative;
  z-index: 1;
  flex: 1;
  display: grid;
  place-items: center;
  padding: clamp(12px, 2.5vh, 32px) var(--gutter);
  min-height: 0;
}
.page__minifoot {
  position: relative;
  z-index: 1;
  display: flex;
  justify-content: center;
  gap: var(--s-5);
  padding: 0 var(--gutter) clamp(14px, 2.5vh, 28px);
  font-size: clamp(.75rem, .82vw, .9375rem);
  color: var(--muted-2);
}
/* Область нажатия добирается полями, а отрицательные отступы возвращают
   строке прежнюю высоту: страница входа обязана умещаться в один экран. */
.page__minifoot a { color: var(--muted-2); display: inline-block; padding: 13px 0; margin: -13px 0; }
.page__minifoot a:hover { color: var(--accent); }

.authbox {
  width: 100%;
  max-width: 460px;
  padding: clamp(22px, 2.2vw, 38px);
  position: relative;
  border-radius: 0;
  clip-path: var(--clip-panel);
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
}
.authbox__title {
  font-family: var(--font-display);
  font-size: clamp(1.3125rem, 1.7vw, 2rem);
  font-weight: 700;
  line-height: var(--lh-title);
  letter-spacing: -.6px;
  margin: var(--s-2) 0 var(--sp-text);
}
.authbox__lead {
  font-size: clamp(.8125rem, .9vw, 1rem);
  color: var(--muted);
  margin: 0 0 var(--s-5);
}
.authbox__lead strong { color: var(--text); }
.authbox__change {
  border: none; background: none; padding: 0;
  color: var(--accent); font: inherit; cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.authbox__submit { width: 100%; min-width: 0; }

.authbox__sep {
  display: flex; align-items: center; gap: var(--s-4);
  margin: var(--s-5) 0;
  font-family: 'JetBrains Mono', monospace;
  font-size: .75rem; letter-spacing: 2px; text-transform: uppercase; color: var(--muted-2);
}
.authbox__sep::before, .authbox__sep::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
.authbox__note { font-size: clamp(.6875rem, .8vw, .875rem); color: var(--muted-2); margin: var(--s-3) 0 0; }
.authbox__error {
  margin: 0 0 var(--s-4);
  padding: var(--s-3) var(--s-4);
  background: rgba(var(--danger-rgb),.08);
  clip-path: var(--clip-btn);
  color: var(--danger);
  font-size: clamp(.8125rem, .88vw, 1rem);
}
.authbox__againform { margin-top: var(--s-4); text-align: center; }

/* ── Подарок в окне пробного периода ──
   Крышка приподнимается и замирает открытой: коробка так и остаётся
   раскрытой, а не дёргается по кругу — повторяющееся движение в модальном
   окне мешает читать текст. */
.gift {
  display: block;
  width: 84px;
  height: 84px;
  margin: 0 auto var(--s-4);
  color: var(--accent);
}
.gift svg {
  width: 100%;
  height: 100%;
  overflow: visible;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.gift__box { fill: rgba(var(--accent-rgb), .07); }
.gift__ribbon { opacity: .55; }

.gift__lid {
  transform-origin: 32px 28px;
  animation: gift-open .85s cubic-bezier(.2, .9, .3, 1.2) .25s both;
}
@keyframes gift-open {
  from { transform: none; }
  60%  { transform: translateY(-15px) rotate(-11deg); }
  to   { transform: translateY(-11px) rotate(-8deg); }
}

/* Человеку, попросившему меньше движения, показываем сразу открытую коробку. */
@media (prefers-reduced-motion: reduce) {
  .gift__lid {
    animation: none;
    transform: translateY(-11px) rotate(-8deg);
  }
}

/* Кнопки в окне идут одна под другой, без отступов от форм. */
.modal__form { margin: 0; }
.modal__form + .modal__form { margin-top: var(--s-3); }
/* Второстепенное действие в окне: отказ не должен спорить с основной
   кнопкой, поэтому это тихая ссылка, а не второй заливной прямоугольник. */
.modal__skip {
  border: none;
  background: none;
  padding: 6px 0;
  color: var(--muted);
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color .2s ease;
}
.modal__skip:hover { color: var(--text); }

/* ── Окно с подарком: уход в колокольчик и возврат оттуда ──
   Размытие рассеивается, коробка уезжает в значок уведомлений и схлопывается
   в точку. Смещение считает gift.js — оно зависит от того, где на этой ширине
   оказался значок. */
.modal { transition: background-color .42s ease,
                     -webkit-backdrop-filter .42s ease,
                     backdrop-filter .42s ease; }
.modal[hidden] { display: none; }

/* Клики по исчезающему слою ловить незачем — под ним уже живая страница. */
.modal--leaving { pointer-events: none; }

/* Прозрачный фон без размытия: конец ухода и начало возврата.

   Вес правила обязателен. Основное описание `.modal` с фоном и размытием стоит
   в файле ниже и при равном весе побеждало бы — фон не гас вовсе, а пропадал
   рывком вместе со скрытием окна. Поэтому селектор с атрибутом. */
[data-gift].modal--closed,
[data-gift].modal--leaving {
  background-color: rgba(var(--bg-rgb), 0);
  -webkit-backdrop-filter: blur(0);
  backdrop-filter: blur(0);
}

/* Размытие держится ровно столько, сколько длится весь уход коробки: полёт
   .42s плюс затухание .16s. Иначе фон проясняется раньше, коробка ещё летит,
   и получаются два отдельных движения вместо одного. */
[data-gift].modal--leaving {
  transition: background-color .58s ease,
              -webkit-backdrop-filter .58s ease,
              backdrop-filter .58s ease;
}

/* Возврат: выплывает из значка и притормаживает, придя на место. */
[data-gift] .modal__box {
  transition: transform .43s cubic-bezier(.18, .85, .3, 1),
              opacity .22s ease-out;
}

/* Уход: коробка разгоняется к значку и остаётся непрозрачной весь полёт.
   Гаснет только после прилёта — задержка прозрачности (.42s) равна длительности
   перемещения. Раньше она падала всю дорогу, и коробка пропадала на полпути. */
[data-gift].modal--leaving .modal__box {
  transition: transform .42s cubic-bezier(.45, 0, .85, .65),
              opacity .16s ease-in .42s;
}

/* Поставить коробку в исходную точку без перехода — перед возвратом. */
[data-gift].modal--instant,
[data-gift].modal--instant .modal__box { transition: none; }

/* Значок коротко подрастает — показать, что предложение теперь здесь. */
.bell__badge--pulse { animation: bell-pulse .9s ease-out; }
@keyframes bell-pulse {
  0%   { transform: scale(1); }
  30%  { transform: scale(1.55); }
  100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .modal, .modal--leaving .modal__box { transition: none; }
  .bell__badge--pulse { animation: none; }
}

/* Сообщение «код уже отправлен» — не ошибка, поэтому не красное. */
.authbox__notice {
  margin: 0 0 var(--s-4);
  padding: var(--s-3) var(--s-4);
  background: rgba(var(--accent-rgb), .07);
  clip-path: var(--clip-btn);
  color: var(--muted);
  font-size: clamp(.8125rem, .88vw, 1rem);
}
/* На странице входа плашка стоит плотнее: экран один и запаса высоты нет. */
.page--auth .authbox__notice { margin-bottom: var(--s-3); padding: 10px var(--s-4); }

.authbox__change[disabled] {
  color: var(--muted-2);
  cursor: default;
  text-decoration: none;
}

/* Плашка режима разработки: код входа вместо письма.
   Отрисовывается только при PARANOIA_MAIL_BACKEND=console. */
.devnote {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  align-items: center;
  margin: 0 0 var(--s-5);
  padding: var(--s-3) var(--s-4);
  clip-path: var(--clip-btn);
  background: rgba(var(--warn-rgb), .07);
}
.devnote__tag {
  font-family: 'JetBrains Mono', monospace;
  font-size: .6875rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--warn);
}
.devnote__code {
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(1.5rem, 2vw, 2.125rem);
  font-weight: 700;
  letter-spacing: 8px;
  color: var(--text);
  user-select: all;
}
.authbox__legal {
  font-size: clamp(.6875rem, .8vw, .875rem);
  color: var(--muted-2);
  margin: var(--s-5) 0 0;
  padding-top: var(--s-4);
  border-top: 1px solid var(--border);
}
.gbtn[disabled] { opacity: .5; cursor: not-allowed; }

/* низкие экраны — ещё плотнее, чтобы гарантированно влезло */
@media (max-height: 760px) {
  .authbox { padding: clamp(18px, 2vw, 28px); }
  .authbox__title { font-size: clamp(1.1875rem, 1.5vw, 1.625rem); margin-bottom: var(--s-3); }
  .authbox__lead { margin-bottom: var(--s-4); }
  .authbox__sep { margin: var(--s-4) 0; }
  .authbox__legal { margin-top: var(--s-4); padding-top: var(--s-3); }
  .field { margin-bottom: var(--s-4); }
}

/* совсем низкие экраны (iPhone SE и подобные) — ужимаем шапку и подвал */
@media (max-height: 700px) {
  .page--auth .nav { height: 58px; }
  .page--auth .nav__logo { width: 38px; height: 38px; }
  .page__center { padding-block: var(--s-2); }
  .page__minifoot { padding-bottom: var(--s-3); }
  .authbox__sep { margin: var(--s-3) 0; }
}

/* ── /cabinet ── */
/* Обе части выровнены по нижней границе: почта, колокольчик и выход встают
   на одну линию с заголовком. Прежде правая часть прижималась к верху и
   зависала между надстрочником и заголовком — то есть не совпадала ни с чем. */
.cab__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s-5);
  flex-wrap: wrap;
  margin-bottom: var(--s-6);
}
.cab__head .page__title { margin-bottom: 0; }
/* min-width: 0 — чтобы на узком экране длинная почта сжималась в многоточие,
   а не выталкивала колокольчик и выход за край (на 320 px — на 5px,
   автопроверка вёрстки 2026-09-24). */
.cab__user { display: flex; align-items: center; gap: var(--s-3); min-width: 0; max-width: 100%; }

/* Подпись кабинета: аватар + имя из Telegram, почта под ним. */
.cab__id { display: flex; align-items: center; gap: var(--s-3); min-width: 0; }
.cab__avatar {
  flex: none;
  display: grid; place-items: center;
  width: clamp(34px, 2.2vw, 44px);
  height: clamp(34px, 2.2vw, 44px);
  border-radius: 50%;              /* лицо в круге — единственное исключение
                                      из срезов, как и логотип в шапке */
  overflow: hidden;
  background: var(--card);
  border: 1px solid var(--border);
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.6875rem, .72vw, .8125rem);
  letter-spacing: .5px;
  color: var(--muted-2);
}
.cab__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cab__id-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.cab__id-name {
  font-size: clamp(.8125rem, .92vw, 1rem);
  color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cab__id-name--mono { font-family: 'JetBrains Mono', monospace; font-size: clamp(.75rem, .84vw, .9375rem); color: var(--muted); }
.cab__id-sub {
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.625rem, .68vw, .75rem);
  color: var(--muted-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cab__logout {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: none; background: none; padding: 0;
  color: var(--muted-2);
  cursor: pointer;
  transition: color .2s ease;
}
.cab__logout:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cab__logout svg { width: 58%; height: 58%; }
.cab__logout:hover { color: var(--accent); }

/* Подсказка «привяжите Telegram» — компактная акцентная полоса.
   Показывается только непривязанным аккаунтам, скрывается крестиком. */
.tglink {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s-4);
  flex-wrap: wrap;
  margin-bottom: var(--gap);
  padding: var(--s-3) var(--s-5);
  clip-path: var(--clip-btn);
  /* Без серого градиента — только рамка (владелец, 2026-10-02). */
  background: none;
}
.tglink[hidden] { display: none; }
.tglink__icon {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 30px; height: 30px;
  color: var(--accent);
}
.tglink__icon svg { width: 100%; height: 100%; }
.tglink__text {
  flex: 1;
  min-width: 220px;
  margin: 0;
  font-size: clamp(.8125rem, .9vw, 1rem);
  color: var(--muted);
  line-height: var(--lh-ui);
}
.tglink__text strong { color: var(--text); font-weight: 600; }
.tglink__cta { flex-shrink: 0; }
.tglink__close {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  /* Крестик добирает 44 пикселя полями, а отрицательные отступы возвращают
     полосе прежнюю высоту — иначе она подрастает ради одного значка. */
  width: 44px; height: 44px;
  margin: -8px -8px -8px 0;
  padding: 0;
  border: none;
  background: none;
  color: var(--muted-2);
  cursor: pointer;
  transition: color .2s ease;
}
.tglink__close:hover { color: var(--text); }
.tglink__close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tglink__close svg { width: 18px; height: 18px; }

/* Две независимые колонки. Общая сетка здесь не годится: блоки сильно
   разной высоты (список устройств бывает длинным), и правая колонка
   простаивала бы, пока левая тянется вниз. */
.cab {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: var(--gap);
  align-items: start;
}
/* Вкладка с одним блоком: колонка на всю ширину, но с потолком — растянутый
   на весь экран список платежей читается хуже узкой колонки. */
.cab--one {
  grid-template-columns: 1fr;
  max-width: 720px;
}

/* ── Навигация по вкладкам кабинета ──
   Кабинет разнесён по вкладкам (решение владельца 2026-08-26). Вкладки —
   те же фирменные кнопки gbtn, что и по всему сайту, растянутые поровну
   на ширину страницы; активная — залитая (gbtn--accent). Переключение
   мгновенное (cabtabs.js), адреса остаются рабочими без JS. */
.cabtabs {
  display: flex;
  gap: var(--s-3);
  /* Шаг до и после вкладок — общий --gap, как между всеми блоками кабинета.
     Прежде 18 и 22: вместе с 14 под заголовком выходило три разных шага
     подряд (аудит 2026-09-24). */
  margin-block: var(--gap);
}
.cabtabs__item {
  flex: 1 1 0;
  min-width: 0;               /* gbtn задаёт min-width под одиночные кнопки */
}
/* Телефон: четыре кнопки в ряд не вмещают свои надписи («Устройства» шире
   кнопки в 75px) — складываем 2×2 (владелец, 2026-08-27). */
@media (max-width: 640px) {
  .cabtabs { display: grid; grid-template-columns: 1fr 1fr; }
}

/* Кабинет и оплата компактнее обычных страниц: шапка и логотип ужаты, чтобы
   содержимое помещалось в экран без прокрутки (владелец, 2026-08-27 и
   2026-09-08 — на оплате карточка с суммой уезжала под сгиб). */
.page--cab .nav,
.page--pay .nav { height: clamp(84px, 10vh, 128px); }
.page--cab .nav__logo,
.page--pay .nav__logo { width: clamp(56px, 4.6vw, 88px); height: clamp(56px, 4.6vw, 88px); }

/* Оплата: заголовок и подводка по центру, кегль ужат — чтобы карточка
   с суммой оставалась в первом экране (владелец, 2026-09-08). */
.page--pay .page__title {
  text-align: center;
  font-size: clamp(1.5rem, 2.2vw, 2.375rem);
  margin-bottom: var(--s-4);
}
.page--pay .page__body { padding-top: var(--s-4); }
.page--pay .page__lead { margin-top: 0; }
.page--cab .cab__head { margin-block: 0 var(--gap); }
.page--cab .page__title { font-size: clamp(1.625rem, 2.4vw, 2.5rem); }

/* Однокол­оночная вкладка на всю ширину страницы (устройства, платежи). */
.cab--wide { grid-template-columns: 1fr; }

/* Аккаунт: два блока рядом, одной высоты. */
.cab--duo {
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
}

/* Строка «заголовок блока + действие справа». */
.cab__sechead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}
.cab__sechead .cab__section-title { margin: 0; }

/* «Показать все» — заметная кнопка справа от заголовка блока. Рамку по
   срезанным углам рисует chamfer-edge.js (inset-тень диагональ не умеет). */
.devall {
  flex-shrink: 0;
  padding: 10px 20px;
  clip-path: var(--clip-btn);
  background: transparent;   /* прозрачная: только рамка .cedge и текст (владелец, 2026-10-02 — серые подложки выглядели неактивными) */
  color: var(--text);
  font-size: .875rem;
  text-decoration: none;
}
.devall:hover { color: var(--accent); background: rgba(var(--text-rgb),.06); }   /* как у «Копировать» */
.devall:hover > .cedge polygon { stroke: var(--accent); }

/* Модальное окно «Все устройства»: открывается по :target — без JS.
   Длинный список прокручивается внутри окна, не страницей. */
.cabmodal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 90;
  place-items: center;
  padding: var(--s-4);
  /* Затемнение ЦВЕТОМ ТЕМЫ с размытием — как у родных окон (.modal).
     Пока слои были рассогласованы (вуаль не накрывала шапку и футер),
     размытие «наплывало» на время перехода темы; после починки слоёв
     владелец вернул его (2026-08-27). */
  background: rgba(var(--bg-rgb), .72);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.cabmodal:target { display: grid; }
/* Пока окно открыто, страница под ним не листается: колесо крутит либо
   список в окне, либо ничего (владелец, 2026-08-27). */
html:has(.cabmodal:target) { overflow: hidden; }
/* ...и вуаль накрывает ВСЮ страницу, включая шапку и футер. Сами они живут
   в слоях выше контента (z:10 и z:1 против z:1 у page__body), а окно заперто
   внутри слоя контента — сколько ему z ни дай, поверх шапки ему не выбраться.
   Поэтому на время окна опускаем их слои. Без этого логотип и футер торчали
   над вуалью и «меняли тон» при переходе темы: в копии перехода вуаль
   накрывала их, а вживую — нет (владелец, 2026-08-27). */
html:has(.cabmodal:target) .nav,
html:has(.cabmodal:target) .footer { z-index: 0; }
.cabmodal__backdrop {
  position: absolute;
  inset: 0;
}
.cabmodal__box {
  position: relative;
  width: min(680px, 100%);
  padding: var(--s-5);
}
.cabmodal__scroll {
  max-height: min(62vh, 560px);
  overflow-y: auto;
  margin-top: var(--s-3);
}
.cabmodal__close {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;      /* как остальные крестики сайта; было 36 */
  margin: calc(var(--s-2) * -1);
  color: var(--muted);
}
.cabmodal__close:hover { color: var(--accent); }
.cabmodal__close svg { width: 20px; height: 20px; }

/* Карточка Telegram на вкладке «Аккаунт». Зелёный — единственное отступление
   от монохрома кроме --danger/--warn, разрешён владельцем (2026-08-27)
   только здесь; оттенки блеклые, парой под обе темы. */
.tgcard {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  text-align: center;
}
/* Промежутки в карточке задаёт её gap, как советует Material 3: собственное
   нижнее поле заголовка складывалось с ним, и заголовок → текст выходил 24
   вместо общих 12 (аудит 2026-09-24). */
.tgcard .cab__section-title { margin: 0; }
.tgcard__icon { width: 56px; height: 56px; }
.tgcard__icon svg { width: 100%; height: 100%; }
.tgcard--off {
  box-shadow: inset 0 0 0 1px rgba(var(--danger-rgb), .5);
  background: rgba(var(--danger-rgb), .06);
}
.tgcard--off .tgcard__icon { color: var(--danger); }
.tgcard--on {
  box-shadow: inset 0 0 0 1px rgba(var(--tg-ok-rgb), .55);
  background: rgba(var(--tg-ok-rgb), .07);
}
.tgcard--on .tgcard__icon { color: var(--tg-ok); }
.cab__col {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  min-width: 0;
}

/* ── История платежей списком (в узкой колонке таблица не помещается) ── */
.paylist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.paylist__item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--border-soft);
}
.paylist__item:last-child { border-bottom: none; }
.paylist__body { display: flex; flex-direction: column; gap: var(--sp-inline); min-width: 0; }
.paylist__title { font-size: clamp(.8125rem, .9vw, 1rem); color: var(--text); }
.paylist__meta {
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.6875rem, .76vw, .8125rem);
  color: var(--muted-2);
}
.paylist__amount {
  flex-shrink: 0;
  font-family: var(--font-display);
  font-size: clamp(.875rem, 1vw, 1.125rem);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.cab__section-title {
  font-family: var(--font-display);
  font-size: var(--fs-subhead);
  font-weight: 600;
  line-height: var(--lh-head);
  margin: 0 0 var(--s-3);
}
.cab__hint { font-size: clamp(.8125rem, .88vw, 1rem); color: var(--muted-2); margin: 0 0 var(--s-4); }

.cab__status-row { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-3); }
.badge {
  font-family: 'JetBrains Mono', monospace;
  font-size: .75rem; letter-spacing: 1px; text-transform: uppercase;
  padding: 4px 12px; clip-path: var(--clip-btn);
}
.badge--ok { background: var(--accent); color: var(--ink); }
.badge--warn { background: var(--warn); color: var(--ink); }
.badge--off { background: rgba(var(--text-rgb),.1); color: var(--muted); box-shadow: inset 0 0 0 1px var(--border); }
.badge--trial { background: rgba(var(--accent-rgb),.16); color: var(--accent); box-shadow: inset 0 0 0 1px var(--ice-edge-hot); }
.badge--accentline { background: none; color: var(--accent); box-shadow: inset 0 0 0 1px var(--ice-edge-hot); }
.cab__till { font-family: 'JetBrains Mono', monospace; font-size: clamp(.75rem, .84vw, .9375rem); color: var(--muted-2); }
.cab__plan {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 1.6vw, 1.875rem);
  font-weight: 600; line-height: var(--lh-head); letter-spacing: -.4px;
  margin: 0 0 var(--sp-text);
}
.cab__meter { height: 6px; background: rgba(var(--bg-rgb),.7); box-shadow: inset 0 0 0 1px var(--border); }
.cab__meter-fill { height: 100%; background: var(--accent); }
.cab__meter-text { font-size: clamp(.8125rem, .88vw, 1rem); color: var(--muted); margin: var(--s-3) 0 var(--s-5); }
.cab__meter-text strong { color: var(--text); }
.cab__cta { width: 100%; min-width: 0; }

.cab__link {
  display: flex; align-items: stretch; gap: var(--s-2);
  margin-bottom: var(--s-5);
}
.cab__link-value {
  flex: 1; min-width: 0;
  padding: var(--s-3) var(--s-4);
  background: rgba(var(--bg-rgb),.6);
  box-shadow: inset 0 0 0 1px var(--border);
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.6875rem, .78vw, .875rem);
  color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cab__copy {
  flex-shrink: 0;
  padding: 0 var(--s-4);
  border: none;
  clip-path: var(--clip-btn);
  background: transparent;   /* прозрачная: только рамка .cedge и текст (владелец, 2026-10-02 — серые подложки выглядели неактивными) */
  color: var(--text);
  font-family: var(--font-body);
  font-size: clamp(.75rem, .84vw, .9375rem);
  font-weight: 600;
  cursor: pointer;
  transition: background .2s ease;
}
.cab__copy:hover { background: rgba(var(--text-rgb),.06); }
.cab__copy.is-done { background: var(--accent); color: var(--ink); }
.cab__qr {
  display: grid; place-items: center;
  width: fit-content;
  margin: 0 auto var(--s-5);     /* по центру карточки, а не по левому краю */
  padding: var(--s-3);
  /* QR — не оформление, а код: сканер читает только тёмные модули на светлом
     фоне. Цвета фиксированы и НЕ берутся из --text/--ink: те переворачиваются
     вместе с темой, и в светлой теме получался белый код на чёрном — сканеру
     он не читается. Белое поле с чёрными модулями в любой теме. */
  background: var(--qr-bg);
  color: var(--qr-fg);             /* модули берут currentColor */
}
/* Ужат (владелец, 2026-08-27): с прежним размером блок «Подключение»
   не помещался в экран вместе со списком приложений. */
.cab__qr svg { display: block; width: clamp(120px, 10vw, 160px); height: auto; }
.cab__apps { display: flex; flex-direction: column; gap: 4px; font-size: clamp(.75rem, .84vw, .9375rem); color: var(--muted); }
.cab__apps-title {
  font-family: 'JetBrains Mono', monospace;
  font-size: .6875rem; letter-spacing: 2px; text-transform: uppercase; color: var(--muted-2);
  /* С зазором списка (4) выходит связка 8 — как подпись → поле. */
  margin-bottom: var(--sp-inline);
}

.cab__big { font-family: var(--font-display); font-size: clamp(2.125rem, 3vw, 3.5rem); font-weight: 700; line-height: var(--lh-tight); margin: 0 0 var(--s-4); }
.cab__big-sub { font-size: .4em; color: var(--muted-2); font-weight: 400; }

/* ── Список устройств ── */
.devlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
/* Строка — подобие таблицы (владелец, 2026-08-27): столбцы «устройство /
   платформа / версия ПО / действие», между именем и кнопкой нет пустоты. */
.devlist__item {
  display: grid;
  /* Последний столбец ФИКСИРОВАННЫЙ: каждая строка — своя сетка, и auto
     давал им разную ширину (у заголовка ячейка пустая, у строк — кнопка),
     из-за чего столбцы расползались и не сходились в линию. */
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) 136px;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--border-soft);
}
.devlist__item:last-child { border-bottom: none; }
/* Весь текст таблицы — основным цветом, без серых вкраплений
   (владелец, 2026-08-27). */
.devlist__item--head {
  font-family: 'JetBrains Mono', monospace;
  font-size: .6875rem;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text);
  padding-block: 8px;
}
.devlist__name { font-size: clamp(.8125rem, .9vw, 1rem); color: var(--text); }
.devlist__cell { font-family: 'JetBrains Mono', monospace; font-size: clamp(.6875rem, .76vw, .8125rem); color: var(--text); }
/* На узком экране четыре столбца не помещаются — имя над остальным. */
@media (max-width: 640px) {
  .devlist__item { grid-template-columns: 1fr 1fr auto; row-gap: var(--sp-tie); }
  .devlist__item--head { display: none; }
  .devlist__name { grid-column: 1 / -1; }
}

/* Телефон (владелец, 2026-10-02, Samsung S24 — 360px). Заголовок и
   «Показать все» в одну строку не помещаются: кнопка вылезала за край
   карточки на 37px. Переносим её под список, во всю ширину — так её и
   ищут на телефоне, после последних строк. display: contents снимает
   обёртку строки, порядок задаёт order (DOM не трогаем — без JS тоже). */
@media (max-width: 640px) {
  .cab .glass { padding: var(--gap); }
  .cab__devices,
  .cab__history { display: flex; flex-direction: column; }
  .cab__devices > .cab__sechead,
  .cab__history > .cab__sechead { display: contents; }
  .cab__devices > .cab__sechead > .cab__section-title,
  .cab__history > .cab__sechead > .cab__section-title { margin-bottom: var(--sp-text); }
  .cab__sechead > .devall {
    order: 1;
    align-self: stretch;
    text-align: center;
    margin-top: var(--sp-text);
  }
  /* Подсказка про поддержку — после кнопки, отдельным абзацем. Не
     :last-child: последним в карточке стоит рамка .cedge от chamfer-edge.js. */
  .cab__history > .paylist ~ .cab__hint { order: 2; margin: var(--gap) 0 0; }

  /* Платёж: название во всю ширину, под ним дата и статус слева, сумма
     справа. Раньше сумма отъедала колонку, и название рвалось
     («Standart, 1 / год»). */
  .paylist__item {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: baseline;
    row-gap: var(--sp-tie);
  }
  .paylist__body { display: contents; }
  .paylist__title { grid-column: 1 / -1; }
}
.devlist__unlink {
  flex-shrink: 0;
  /* Крупнее прежнего (владелец, 2026-08-27): кнопка — основное действие
     строки, а не сноска. Рамку рисует chamfer-edge.js. */
  padding: 10px 20px;
  border: none;
  clip-path: var(--clip-btn);
  background: transparent;   /* прозрачная: только рамка .cedge и текст (владелец, 2026-10-02 — серые подложки выглядели неактивными) */
  color: var(--text);
  font-family: var(--font-body);
  font-size: .875rem;
  cursor: pointer;
}
.devlist__unlink:disabled { cursor: not-allowed; opacity: .8; }
.devlist__unlink:not(:disabled):hover {
  background: rgba(var(--danger-rgb),.10);
  box-shadow: inset 0 0 0 1px rgba(var(--danger-rgb),.45);
  color: var(--danger);                /* действие разрушительное — подсказываем цветом */
}
.devlist__form { flex-shrink: 0; margin: 0; justify-self: end; }

/* ── Список «ключ — значение» ── */
.deflist { margin: 0; }
.deflist__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--border-soft);
}
.deflist__row:last-child { border-bottom: none; }
.deflist__row dt { margin: 0; font-size: clamp(.75rem, .84vw, .9375rem); color: var(--muted-2); }
.deflist__row dd {
  margin: 0;
  font-size: clamp(.75rem, .84vw, .9375rem);
  color: var(--text);
  display: flex; align-items: center; gap: var(--s-2);
  text-align: right;
  word-break: break-all;
}
.dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.dot--ok { background: var(--accent); }
.dot--off { background: var(--muted-2); }
/* Тот же красный, что у сообщений об ошибке (.panel-error) — нового цвета
   в палитру не вводим, берём уже существующий в макете. */
.dot--warn { background: var(--danger); }
.link-warn { color: var(--danger); }
.link-warn:hover { color: var(--danger); }

/* ── Реферальная статистика ── */
.refstats { display: flex; gap: clamp(20px, 2.4vw, 44px); flex-wrap: wrap; margin-top: var(--s-4); }
.refstats__item { display: flex; flex-direction: column; gap: var(--sp-inline); }
.refstats__num {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2vw, 2.25rem);
  font-weight: 700;
  line-height: var(--lh-tight);
}
.refstats__label {
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(.625rem, .72vw, .75rem);
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted-2);
}
/* Ожидающие начисления приглушаем: они ещё не на счету. */
.refstats__num--pending { color: var(--muted-2); }
.refnote {
  margin: var(--s-3) 0 0;
  font-size: clamp(.75rem, .84vw, .9375rem);
  color: var(--muted-2);
}

/* ── Статус в таблице платежей ── */
.status { font-size: clamp(.6875rem, .78vw, .8125rem); }
.status--ok { color: var(--accent); }
.status--warn { color: var(--warn); }
.status--off { color: var(--muted-2); }
/* Полная ссылка подписки без JavaScript (см. _panels.html). */
.cab__link-full { margin-top: var(--s-3); font-size: var(--fs-label); color: var(--muted); }
.cab__link-full summary { cursor: pointer; }
.cab__link-full .cab__link-value { display: block; margin-top: var(--sp-tie); white-space: normal; overflow-wrap: anywhere; }

/* Отменённый или возвращённый платёж: сумма не списана — приглушаем и
   зачёркиваем, иначе в списке он не отличался от оплаченного (аудит 02.10). */
.paylist__item--off .paylist__amount { color: var(--muted-2); text-decoration: line-through; }
.cab__table-wrap { overflow-x: auto; }

/* ── /checkout ── */
/* Одна колонка: выбор тарифа с этой страницы убран (правка ревьюера
   2026-09-08), осталась только карточка «Итого» — растягивать её на всю
   ширину незачем. */
.checkout { display: grid; grid-template-columns: minmax(0, 980px); justify-content: center; gap: var(--gap); align-items: start; }
/* Состав подписки значками (владелец, 2026-09-08): три факта в ряд —
   устройства, трафик, срок. Значок и подпись на одной оси, тон приглушённый:
   это уточнение к тарифу, а не отдельный раздел. */
.planfacts {
  list-style: none;
  margin: 0 0 var(--s-5);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.planfacts__item {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: clamp(.8125rem, .9vw, 1rem);
  color: var(--text);
}
.planfacts__icon {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  color: var(--muted);
}
.planfacts__icon svg { width: 22px; height: 22px; }

/* Мелкие тексты карточки — одним кеглем (аудит 2026-09-08: подряд шли три
   размера — 12, 13 и 11px, выглядело неряшливо). Иерархию держим цветом:
   «что после оплаты» основным приглушённым, согласие и возврат — тише. */
.checkout__summary .agree,
.checkout__summary .checkout__after,
.checkout__summary .authbox__note { font-size: clamp(.8125rem, .9vw, 1rem); }
.checkout__summary .agree { color: var(--muted); }

/* Внутри карточки две колонки: слева «за что и сколько», справа «чем платить»
   (владелец, 2026-09-08 — карточка была узкой и высокой). */
.checkout__summary {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(24px, 3vw, 56px);
  align-items: start;
}
.checkout__side { min-width: 0; }
/* Подводка над карточкой — по центру, как и сама карточка. Одного
   text-align мало: у блока ограничена ширина (62ch) и нулевые боковые поля,
   поэтому центрируем и сам блок (владелец, 2026-09-08). */
.page--pay .page__lead { text-align: center; margin-inline: auto; }
@media (max-width: 860px) {
  .checkout__summary { grid-template-columns: 1fr; gap: var(--s-5); }
}
/* «изменить» — компактная кнопка в общем стиле сайта: срез углов и обводка,
   как у «Показать все» в кабинете (владелец, 2026-09-08: была незаметной
   ссылкой). Рамку по срезам рисует chamfer-edge.js. */
.checkout__change {
  margin-left: 14px;
  padding: 8px 16px;
  clip-path: var(--clip-btn);
  background: rgba(var(--text-rgb),.05);
  color: var(--text);
  font-size: .8125rem;
  text-decoration: none;
  white-space: nowrap;
}
.checkout__change:hover { color: var(--accent); background: rgba(var(--text-rgb),.06); }
.checkout__change:hover > .cedge polygon { stroke: var(--accent); }
/* Строка тарифа: значение и кнопка на одной оси. */
.checkout__row dd { display: inline-flex; align-items: center; }

.checkout__summary { padding: clamp(24px, 2.2vw, 40px); }
.checkout__rows { margin: 0 0 var(--s-5); }
/* align-items обязателен: без него подпись слева прилипала к верху строки,
   а значение справа стояло по центру (владелец, 2026-09-08). */
.checkout__row { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); padding: var(--s-3) 0; border-bottom: 1px solid var(--border-soft); }
.checkout__row dt { color: var(--muted-2); font-size: clamp(.8125rem, .88vw, 1rem); margin: 0; }
.checkout__row dd { margin: 0; font-size: clamp(.8125rem, .88vw, 1rem); font-variant-numeric: tabular-nums; }
.checkout__row--total { border-bottom: none; padding-top: var(--s-4); }
.checkout__row--total dt { color: var(--text); }
.checkout__row--total dd { font-family: var(--font-display); font-size: clamp(1.25rem, 1.6vw, 1.875rem); font-weight: 700; }
.checkout__cta { width: 100%; min-width: 0; }

@media (max-width: 900px) {
  .checkout { grid-template-columns: 1fr; }
  /* Колонки схлопываются в один поток. display:contents убирает обёртки,
     чтобы порядок блоков задавался через order — иначе на телефоне
     «Подключение» уехало бы в самый низ, за все блоки левой колонки. */
  /* align-items: stretch обязателен. У компьютерной раскладки стоит
     align-items: start, и при переходе в колонку оно остаётся в силе: блоки
     начинают сжиматься по содержимому, а не занимать ширину. Получался
     рваный столбец — 419, 347, 271 пикселя — и пустое место справа. */
  .cab {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--gap);
  }
  .cab__col { display: contents; }
  .cab__status  { order: 1; }
  .cab__connect { order: 2; }
  .cab__devices { order: 3; }
  .cab__account { order: 4; }
  .cab__ref     { order: 5; }
  .cab__history { order: 6; }
}

/* ── Footer ── */
.footer { border-top: 1px solid var(--border-soft); margin-top: 40px; }
.footer__inner {
  /* Та же ширина и те же поля, что у шапки и секций. Раньше здесь стояло
     жёсткое 1160px, и на широких экранах подвал оказывался уже остальной
     страницы: кромки не совпадали на 200 пикселей с каждой стороны. */
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 36px var(--gutter) 72px;
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  justify-content: space-between;
  align-items: flex-start;
}
.footer__legal { font-family: 'JetBrains Mono', monospace; font-size: .9375rem; color: var(--muted-2); line-height: var(--lh-body); }
.footer__cols { display: flex; gap: 48px; flex-wrap: wrap; }
/* Отступ между ссылками собран из их же полей: по 5px сверху и снизу —
   область нажатия сохраняется (~26px), но столбец не выглядит раздутым
   (владелец, 2026-08-26: подвал был слишком массивным для своего объёма). */
.footer__col { display: flex; flex-direction: column; gap: 2px; font-size: 1rem; }
.footer__col-title {
  font-family: 'JetBrains Mono', monospace;
  font-size: .8125rem;
  letter-spacing: 2px;
  color: var(--muted-2);
  text-transform: uppercase;
}
.footer__col a,
.footer__legal a { display: inline-block; padding: 5px 0; }
.footer__col a { color: var(--muted); }
.footer__col a:hover { color: var(--accent); }
/* Срок ответа поддержки — не ссылка, поэтому приглушённее контактов рядом. */
.footer__note { color: var(--muted-2); font-size: .9375rem; padding: 5px 0; }

/* ── Переключатель темы ──
   Стоит слева от кнопки входа. Размер 44×44 — порог уверенного попадания
   пальцем; иконка внутри меньше, чтобы не спорить с кнопкой рядом. */
.nav__theme { margin: 0; display: flex; }
.themebtn {
  display: grid;
  place-items: center;
  /* 44×44 — тот же порог попадания пальцем, что у остальных целей. Значок
     внутри прежний: растёт только область нажатия. */
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--border-soft);
  background: none;
  color: var(--text);
  cursor: pointer;
  clip-path: var(--clip-btn);
  transition: border-color .2s ease, color .2s ease;
}
.themebtn:hover { border-color: var(--border); }
.themebtn svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Показываем то, во что переключимся: на светлой теме — луну. */
.themebtn__sun { display: var(--icon-sun); }
.themebtn__moon { display: var(--icon-moon); }

/* ── Переход между темами ──
   Поверх страницы кладётся её копия в уходящей теме и обрезается кругом,
   который схлопывается в нажатую кнопку. Под копией — живая страница уже в
   новой теме. Поэтому текст, кнопки и весь интерфейс видны всё время, а по
   кромке круга они просто перекрашиваются.

   Раньше здесь была сплошная заливка: она честно втягивалась в кнопку, но
   закрывала собой содержимое.

   Копия неинтерактивна и невидима для чтения с экрана — иначе на странице
   на секунду оказалось бы два одинаковых набора кнопок. */
.themeclone {
  position: fixed;
  inset: 0;
  z-index: 9999;
  overflow: hidden;
  pointer-events: none;
  background: var(--bg);
  /* Цвет текста задаём явно. Элементы без своего правила наследуют его от
     body, а тот к этому моменту уже перекрашен в новую тему — заголовки в
     копии оказывались цвета будущей темы и пропадали на её фоне. */
  color: var(--text);
  clip-path: circle(var(--vr) at var(--vx) var(--vy));
  transition: clip-path .95s cubic-bezier(.35, 0, .25, 1);
  will-change: clip-path;
}
.themeclone--out { clip-path: circle(0 at var(--vx) var(--vy)); }

/* Копия лежит от верха документа, поэтому её сдвигают на величину прокрутки —
   иначе при прокрученной странице сверху оказался бы её заголовок. */
.themeclone__inner {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}

@media (prefers-reduced-motion: reduce) {
  .themeclone { display: none; }
}

/* ─────────────────────────── RESPONSIVE ─────────────────────────── */
/* Short viewports (laptops ~768px tall): compress the hero's vertical rhythm
   so the whole block — including the platforms strip — stays on one screen. */
@media (max-height: 860px) {
  /* Сжимаем только верх: низ — полшага общего ритма, он и так тянется
     за высотой окна. */
  .hero__content { padding-top: clamp(24px, 3.5vh, 56px); }
  .hero__platforms { padding-top: clamp(16px, 2.6vh, 32px); }
}
@media (max-width: 760px) {
        .nav__links { display: none; }
    /* Знак вырос, и на телефоне шапка перестала помещаться — кнопка входа
       уезжала за край на 40px. Здесь возвращаем умеренный размер: крупный
       логотип задуман для широкого экрана, а не для 375 пикселей. */
    .nav { height: 76px; }
    .nav__logo { width: 52px; height: 52px; }
    .nav__name { font-size: 16px; letter-spacing: 2px; }  /* шапка в px — см. .nav__name */
    .nav__brand { gap: 10px; }
    /* «Войти» вылезала за правый край на 5px (замер 2026-08-27):
       ужимаем поля кнопки — высота и кегль не меняются. */
    .nav__right .gbtn--sm { padding: 0 14px; }
  /* В столбик условия снова идут прямо под заголовком — зазор как был
     у стопки, а не как между колонками. */
  .hero__trial { flex-direction: column; align-items: flex-start; gap: var(--s-2); }
}

/* Узкие телефоны: 360 — самый массовый Android. Автопроверка вёрстки
   (devtools/layout_check.py, 2026-09-24) нашла, что здесь шапка вылезала
   вправо на 7px с «Войти» и на 24px с «Кабинет» — кнопку обрезал край
   экрана. Ужимаем знак и название, кнопки не трогаем: это главное действие. */
@media (max-width: 400px) {
  .nav { gap: var(--s-2); }
  .nav__logo { width: 40px; height: 40px; }
  .nav__name { font-size: 14px; letter-spacing: 1.5px; }
}
/* 320 px (зум 400%, WCAG 1.4.10; старые маленькие телефоны): название не
   помещается никак — остаётся знак. Он и так ссылка на главную, а подпись
   для чтения с экрана у него есть (alt логотипа). */
@media (max-width: 340px) {
  .nav__name { display: none; }
}

@media (max-width: 900px) {
  .faq { grid-template-columns: 1fr; }
  .faq__a-text { max-width: none; }
}

/* FAQ раскрывающимся списком — только на узком экране (см. faq.html). */
.faq__acc { display: none; }
@media (max-width: 900px) {
  /* .faq впереди — перебить nojs.css, где панель с ответами показана всегда. */
  .faq .faq__list,
  .faq .faq__panel { display: none; }
  .faq .faq__acc { display: block; }
}
.faq__item { border-bottom: 1px solid var(--border); }
.faq__item:last-child { border-bottom: none; }
.faq__sum {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-4) 0;
  color: var(--muted);
  font-size: var(--fs-subhead);
  font-weight: 600;
  line-height: var(--lh-head);
  cursor: pointer;
  list-style: none;
}
.faq__sum::-webkit-details-marker { display: none; }
.faq__sum:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.faq__item[open] > .faq__sum { color: var(--text); }
/* Стрелка смотрит вправо у закрытого и вниз у открытого — как у вкладок. */
.faq__item[open] > .faq__sum .faq__q-mark { opacity: 1; transform: rotate(135deg); }
.faq__item-text {
  margin: 0;
  padding: 0 0 var(--s-4);
  font-size: var(--fs-body);
  color: var(--muted);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
          .faq__a { transition: none; transform: none; }
  .faq__q, .faq__q::before, .faq__q-mark { transition: none; }
}

/* ── Экран загрузки ─────────────────────────────────────────────────────
   Показывается только при включённом JS (класс js ставится в <head> до
   отрисовки) и снимается по событию load — когда шрифты и картинки уже на
   месте. Без JS правило не срабатывает и экрана нет вовсе. */
.loader { display: none; }

html.js .loader {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-content: center;
  justify-items: center;
  background: var(--bg);
  opacity: 1;
  transition: opacity .35s ease;
}
html.js.is-loaded .loader {
  opacity: 0;
  pointer-events: none;      /* чтобы гаснущий слой не перехватывал клики */
}
/* Страховка в самих стилях: класс js ставит встроенный скрипт, а снимает
   экран только loader.js. Не дошёл файл (обрыв, блокировщик) — экран висел
   бы навсегда (аудит 02.10). Через 8 с гаснет сам. */
html.js .loader { animation: loader-out 0s linear 8s forwards; }
@keyframes loader-out { to { opacity: 0; visibility: hidden; pointer-events: none; } }

/* Квадрат задан явно, а не взят от логотипа. Логотип 128×156 — по его коробке
   кольцо получалось овалом, и при вращении дуга ездила неровно. */
.loader__mark {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(200px, 15vw, 280px);
  aspect-ratio: 1;
}
.loader__logo {
  width: clamp(124px, 9.5vw, 176px);
  height: auto;
}

/* Рисунок чёрно-серый и на тёмном фоне пропал бы: контуры и тёмные грани
   сливаются с ним. Поэтому лежат два готовых файла — обычный и обращённый.

   Обращение через filter здесь не годится: при первом переходе на тёмную тему
   браузер впервые отрисовывает картинку под фильтр, и знак на миг пропадал.
   Оба файла грузятся при открытии страницы, подмена мгновенная. */
.nav__logo--light,
.loader__logo--light { display: var(--logo-light); }
.nav__logo--dark,
.loader__logo--dark { display: var(--logo-dark); }
/* Бледная дорожка по кругу плюс яркая дуга: так вращение читается ровно,
   а не выглядит рывками одинокого сегмента. */
.loader__ring {
  position: absolute;
  inset: 0;
  /* Кольцо приглушённое, бегущий сегмент — основным цветом. Раньше оба брали
     цвет из --border и --accent, а в монохроме это одно и то же значение:
     кольцо превращалось в сплошной кружок, и вращения не было видно. */
  border: 2px solid var(--border-soft);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: loader-spin 900ms linear infinite;
  will-change: transform;      /* чтобы браузер крутил слой, а не перерисовывал */
}

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

/* Уважаем системную настройку «меньше движения»: оставляем статичное кольцо. */
@media (prefers-reduced-motion: reduce) {
  .loader__ring { animation: none; border-color: var(--border); border-top-color: var(--accent); }
  html.js .loader { transition: none; }
}

/* Подсказки прячем до решения скрипта — иначе закрытая подсказка мигает при
   каждой перезагрузке. Скрипт возвращает её классом is-shown. Без JS правило
   не действует, и подсказка видна сразу. */
html.js [data-hint] { display: none; }
html.js [data-hint].is-shown { display: flex; }

/* ── Окно с итогом действия ─────────────────────────────────────────────
   Отрисовывается сервером и закрывается обычной ссылкой, поэтому работает и
   с выключенным JavaScript. Никаких скриптов здесь намеренно нет. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 150;
  display: grid;
  place-items: center;
  padding: var(--s-5);
  background: rgba(var(--bg-rgb), .72);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.modal__box {
  width: min(100%, 460px);
  padding: clamp(28px, 2.6vw, 44px);
  clip-path: var(--clip-panel);
  text-align: center;
}
.modal__icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  margin: 0 auto var(--s-4);
  border-radius: 50%;
}
.modal__icon svg { width: 28px; height: 28px; }
.modal__icon--ok {
  color: var(--accent);
  box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .40);
  background: rgba(var(--accent-rgb), .08);
}
.modal__title {
  margin: 0 0 var(--s-3);
  font-family: var(--font-display);
  font-size: clamp(1.1875rem, 1.5vw, 1.625rem);
  line-height: var(--lh-head);
}
.modal__text {
  margin: 0 0 var(--s-5);
  color: var(--muted);
  font-size: clamp(.875rem, .95vw, 1.0625rem);
  line-height: var(--lh-ui);
}
.modal__close {
  display: inline-block;
  padding: 12px 28px;
  clip-path: var(--clip-btn);
  background: var(--accent);
  color: var(--ink);
  font-size: clamp(.875rem, .95vw, 1rem);
  text-decoration: none;
}
.modal__close:hover { background: var(--accent-hover); }

/* ── Оформление: способ оплаты, согласие, состояние платежа ──
   Переключатели и галочка нарисованы сами: системные элементы браузера синие
   и в палитру не попадают. Геометрия — как у радиокнопок способа оплаты,
   чтобы на одной странице не было двух разных языков. */
.paymethods { margin: var(--s-4) 0 0; padding: 0; border: none; }
.paymethods__item {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 13px 0; cursor: pointer;
  font-size: clamp(.8125rem, .9vw, 1rem);
  color: var(--muted);
  transition: color .2s ease;
}
.paymethods__item + .paymethods__item { border-top: 1px solid var(--border-soft); }
.paymethods__input { position: absolute; opacity: 0; pointer-events: none; }
.paymethods__mark {
  flex-shrink: 0;
  width: 18px; height: 18px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1.5px var(--muted-2);   /* контраст ≥3:1, см. .field__input */
  transition: box-shadow .2s ease, background .2s ease;
}
.paymethods__item:hover { color: var(--text); }
.paymethods__item:has(.paymethods__input:checked) { color: var(--text); }
.paymethods__item:has(.paymethods__input:checked) .paymethods__mark {
  background: var(--accent);
  box-shadow: inset 0 0 0 4px rgba(var(--bg-rgb),.85), 0 0 0 1.5px var(--accent);
}
.paymethods__item:has(.paymethods__input:focus-visible) {
  outline: 2px solid var(--accent); outline-offset: 3px;
}
.checkout__who {
  margin: var(--s-4) 0 0;
  font-size: clamp(.8125rem, .9vw, 1rem);
  color: var(--muted);
  line-height: var(--lh-ui);
}
/* Поля по 12px поднимают строку согласия с 21 до 45 пикселей. Это
   обязательный шаг перед оплатой, и промахнуться по полоске в один текст
   было слишком легко. */
.agree {
  display: flex; align-items: flex-start; gap: var(--s-3);
  padding: 12px 0;
  margin: var(--s-4) 0;
  font-size: clamp(.75rem, .84vw, .9375rem);
  color: var(--muted);
  line-height: var(--lh-ui);
  cursor: pointer;
}
.agree__input { position: absolute; opacity: 0; pointer-events: none; }
/* Квадрат со срезом, а не круг: галочка — не выбор из вариантов, и форма
   должна отличаться от переключателей выше. Срез — тот же, что у кнопок. */
.agree__mark {
  flex-shrink: 0;
  display: grid; place-items: center;
  width: 20px; height: 20px;
  margin-top: 1px;
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
  background: rgba(var(--text-rgb),.04);
  box-shadow: inset 0 0 0 1.5px var(--muted-2);   /* контраст ≥3:1, см. .field__input */
  color: transparent;
  transition: background .2s ease, box-shadow .2s ease, color .2s ease;
}
.agree__mark svg { width: 13px; height: 13px; }
.agree:has(.agree__input:checked) .agree__mark {
  background: var(--accent);
  box-shadow: inset 0 0 0 1.5px var(--accent);
  color: var(--ink);
}
.agree:has(.agree__input:focus-visible) .agree__mark {
  outline: 2px solid var(--accent); outline-offset: 3px;
}
.agree__text a { color: var(--accent); }
.agree__text a:hover { color: var(--accent-hover); }
.paystate {
  max-width: 560px; margin: 0 auto;
  padding: clamp(32px, 3vw, 56px);
  clip-path: var(--clip-panel);
  text-align: center;
}
.paystate .modal__title { margin-top: 0; }

/* Спиннер на экране ожидания оплаты. Тот же приём, что у экрана загрузки
   (loader-spin): бледное кольцо + яркая дуга. Крутится, пока платёжная система
   подтверждает платёж; со скриптом страница не перезагружается, поэтому
   вращение плавное. */
.paystate__spinner {
  display: block;
  width: 56px;
  height: 56px;
  margin: 0 auto var(--s-4);
  border: 3px solid var(--border-soft);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: loader-spin 900ms linear infinite;
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  /* Меньше движения: статичное кольцо вместо вращения. */
  .paystate__spinner { animation: none; }
}

/* ── Кнопка, которой сейчас нельзя воспользоваться ──────────────────────
   Единственный такой случай — оплата не подключена вовсе. Решётка вместо
   полупрозрачности: мутная кнопка читается как «плохо нарисовали», а
   перечёркнутая — как «пока нельзя».

   При незаполненных полях кнопка НЕ меняет вид. Раньше меняла, и это её
   дёргало: подсказки увеличивали высоту страницы, кнопка уезжала из-под
   курсора, наведение слетало, подсказки исчезали — и по кругу. */
.gbtn--off .gbtn__glass {
  background-image: repeating-linear-gradient(
    135deg,
    rgba(var(--text-rgb), .10) 0 5px,
    transparent 5px 11px
  );
}
.gbtn--off { cursor: not-allowed; }
.gbtn--off .gbtn__label { color: var(--muted-2); }

/* ── Незаполненное обязательное поле ── */
.field__req {
  display: none;
  margin: var(--s-2) 0 0;
  font-size: clamp(.75rem, .82vw, .875rem);
  line-height: var(--lh-ui);
  color: var(--danger);               /* тот же красный, что у сообщений об ошибке */
}
[data-req-field].is-missing .field__req { display: block; }
[data-req-field].is-missing .field__input {
  box-shadow: inset 0 0 0 1.5px rgba(var(--danger-rgb), .75);
}
[data-req-field].is-missing .agree__mark {
  box-shadow: inset 0 0 0 1.5px rgba(var(--danger-rgb), .75);
}
[data-req-field].is-missing .field__label,
[data-req-field].is-missing .agree__text { color: var(--danger); }

/* Полоса «идёт имитация оплаты». Намеренно заметная: перепутать тестовый
   режим с боевым — значит взять с человека деньги и не выдать доступ. */
.testbar {
  margin: 0 0 var(--gap);
  padding: var(--s-3) var(--s-5);
  clip-path: var(--clip-btn);
  background: rgba(var(--warn-rgb), .10);
  color: var(--warn);
  font-size: clamp(.8125rem, .9vw, 1rem);
}
.testpay__actions {
  display: flex; flex-wrap: wrap; gap: var(--s-4);
  justify-content: center; margin-top: var(--s-5);
}
.testpay__actions form { margin: 0; }

/* Дополнительный блок внутри окна — предложение привязать Telegram после
   оплаты. Отделён линией: это не продолжение сообщения, а отдельный шаг. */
.modal__extra {
  margin: var(--s-5) 0;
  padding-top: var(--s-5);
  border-top: 1px solid var(--border-soft);
}
.modal__extra-title {
  margin: 0 0 var(--s-2);
  font-family: var(--font-display);
  font-size: clamp(.9375rem, 1.05vw, 1.125rem);
  color: var(--text);
}
.modal__extra .modal__text { margin-bottom: var(--s-4); }

/* ══ Телефоны ═══════════════════════════════════════════════════════════
   Замерено на ширинах 360 и 375, страницы: лендинг, вход, оформление,
   кабинет. Вёрстка нигде не вылезает за экран и горизонтальной прокрутки
   не создаёт — правки нужны только там, где по экрану попадают пальцем,
   и там, где текст мельче читаемого.

   Порог 44 пикселя — минимальная цель, в которую палец попадает надёжно
   (одинаково у Apple и Google). Всё, что меньше, даёт частые промахи:
   в кабинете это были выход (34), крестик подсказки (28) и ссылки
   подвала высотой 24.

   Условие двойное. max-width ловит узкие экраны, pointer: coarse — палец
   вместо мыши. Планшет с широким экраном тоже управляется пальцем, и цели
   там нужны такие же. */
@media (max-width: 768px), (pointer: coarse) {

  /* Ссылки подвала и строка согласия теперь набирают 44 пикселя всегда —
     отдельных правил для телефона им больше не нужно. */

  /* ── Строки выбора ── */
  .paymethods__item { padding: 14px 0; }

  /* ── Мелкий текст ── */
  /* На вырост шрифта завязаны надписи в 10–11 пикселей: на телефоне с
     мелким шагом такое читается с трудом. Поднимаем до 12–13, палитру и
     разрядку не трогаем. */
  .eyebrow,
  .hero__eyebrow,
  .hero__trust,
  .hero__platforms-label,
  .cab__apps-title,
  .refstats__label { font-size: .75rem; }
  .hero__trial-label { font-size: .6875rem; letter-spacing: 1.5px; }
  .field__hint,
  .authbox__note { font-size: .8125rem; }

  /* ── Длинные ссылки ── */
  /* Ссылка подписки в экран не помещается и обрезалась без предупреждения —
     выглядело как обрыв данных. Многоточие показывает, что текст длиннее,
     а кнопка «Копировать» рядом отдаёт его целиком. */
  .cab__link-value {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* ── Планшет и узкое окно: 761–900 ─────────────────────────────────────
   В этот промежуток помещается ровно две карточки из трёх, и третья
   вставала одна у левого края — справа зияла пустота во всю ширину
   карточки. Центрируем её по строке, ширину берём как у соседей, чтобы
   ряд читался как продолжение сетки, а не как отдельный блок. */
@media (min-width: 761px) and (max-width: 900px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
  .grid > :last-child:nth-child(2n + 1) {
    grid-column: 1 / -1;
    justify-self: center;
    width: calc((100% - var(--gap)) / 2);
  }
}

/* ── Гашение окна с итогом ──────────────────────────────────────────────
   Размытие и коробка уходят разом, страница под ними уже готова — поэтому
   перезагрузка не нужна (см. modal.js). Селектор из двух классов: обычное
   описание `.modal` с фоном стоит выше и при равном весе побеждало бы.

   Подарка это не касается: у него свой уход в колокольчик, и его правила
   помечены атрибутом [data-gift]. */
.modal.modal--fade {
  background-color: rgba(var(--bg-rgb), 0);
  -webkit-backdrop-filter: blur(0);
          backdrop-filter: blur(0);
  pointer-events: none;                 /* под гаснущим слоем уже живая страница */
}
.modal.modal--fade .modal__box {
  opacity: 0;
  transform: scale(.97);
}
.modal:not([data-gift]) .modal__box {
  transition: opacity .34s ease, transform .34s ease;
}

@media (prefers-reduced-motion: reduce) {
  .modal:not([data-gift]) .modal__box { transition: none; }
}

/* Кнопка оплаты в момент отправки: показываем, что нажатие принято, и не
   даём нажать второй раз — второе нажатие завело бы вторую транзакцию. */
.gbtn.is-sending { cursor: progress; }
.gbtn.is-sending .gbtn__glass { opacity: .75; }

/* ── Юридические страницы (/oferta, /privacy) ──
   Длинный текст: узкая мера, спокойный ритм. Никакого стекла и срезов —
   документ должен читаться как документ. */
.legal { max-width: 72ch; }
.legal__date {
  font-family: 'JetBrains Mono', monospace;
  font-size: var(--fs-label);
  color: var(--muted-2);
  margin: calc(-1 * var(--s-6)) 0 var(--s-7);
}
.legal h2 {
  font-size: var(--fs-card-title);
  font-weight: 600;
  line-height: var(--lh-head);
  margin: var(--s-7) 0 var(--s-3);
}
.legal p, .legal li {
  font-size: var(--fs-body);
  color: var(--muted);
  line-height: var(--lh-body);
}
.legal p { margin: 0 0 var(--s-3); }
.legal ul { margin: 0 0 var(--s-3); padding-left: 1.2em; }
.legal li { margin-bottom: var(--s-2); }
.legal strong { color: var(--text); }

/* ── Телефон: кегль на ступень мельче (владелец, 2026-10-02) ──
   На Samsung S24 (360×~660) первый экран главной занимал 950px из 660:
   заголовок в 4 строки, описание в 5, две большие кнопки. Владелец выбрал
   «всё мельче, кроме самого мелкого». Крупные ступени на узком экране
   уменьшаем так же, как GOV.UK на 640px (см. руководство по ритму,
   reports/Стандарты отступов…), и только на ступени телефонной шкалы
   48 / 32 / 28 / 24 / 18 / 16 / 14 / 12 — без новых промежуточных
   размеров. Основной текст остаётся 16 (руководство; владелец, 2026-10-02:
   15 пробовали — ни в одной системе такой ступени нет). Мелкое
   (11–14px: метки, даты, статусы, строка доверия) не трогаем. Блок стоит в конце файла, чтобы перекрывать clamp() выше. */
@media (max-width: 640px) {
  :root {
    --fs-title: 1.75rem;            /* 32 → 28 */
    --fs-card-title: 1.125rem;      /* 20 → 18 */
    --fs-card-title-lg: 1.5rem;     /* 26 → 24 */
    --fs-subhead: 1rem;             /* 17 → 16 */
  }
  .hero__title { font-size: 1.75rem; }                 /* 32 → 28: три строки вместо четырёх */
  .tariff__permonth,
  .modal__extra-title,
  .footer__legal,
  .footer__note { font-size: .875rem; }                /* 15 → 14: ступень мелкого текста */
  .cab__big { font-size: 2rem; }                       /* 34 → 32 */
  .tariff__price,
  .page__title,
  .page--cab .page__title { font-size: 1.5rem; }       /* 26–28 → 24 */
  .authbox__title,
  .cab__plan,
  .modal__title,
  .checkout__row--total dd { font-size: 1.125rem; }    /* 19–21 → 18 */
  /* Поле ввода — 16, не мельче: при меньшем кегле Safari на iPhone сам
     увеличивает страницу, когда человек ставит курсор в поле. */
  .field__input { font-size: 1rem; }

  /* Первый экран: отступы между группами и сверху не трогаем — это
     согласованная формула (высота окна только ужимает). Короче его делает
     кегль и одна главная кнопка: «Посмотреть тарифы» — подчёркнутой
     ссылкой, связана с кнопкой ролью «связка» (8). */
  .hero__actions { gap: var(--sp-tie); }
  .hero__actions > a.gbtn {
    height: auto;
    padding: var(--s-2) 0;          /* зона нажатия ~40px: больше 24 по WCAG 2.5.8 */
    background: none;
    text-decoration: underline;
    text-underline-offset: 4px;
  }
  .hero__actions > a.gbtn > .gbtn__glass,
  .hero__actions > a.gbtn > .cedge { display: none; }
  .hero__trust { letter-spacing: 0; }
}
