/* ============================================================================
   TRACKLY UGC — ui.css
   Единая дизайн-система редизайна (тёмный SaaS, product-first, glow).
   Эталон визуального языка: mydemo5v2.html + assets/css/demo5v2.css.
   Контракт имён классов: /root/ugc-trackly/docs/redesign/00_system.md
   Витрина всех компонентов: /root/ugc-trackly/site/_kit.html

   ПРАВИЛА ФАЙЛА
   - Единственный CSS для всех новых страниц (redesign/*.html). Пер-страничных
     стилей не заводим — новый компонент добавляется сюда, в нужный раздел.
   - Имена существующих классов из старого assets/css/style.css НЕ переименовывать
     (вшиты в PHP на проде). Новые классы для новых компонентов — свободно.
   - Шрифты — самохостинг, см. раздел 00.0 ниже: @font-face + файлы в
     assets/fonts/*.woff2 (копия — public/assets/fonts/). head.php больше не
     ходит на fonts.googleapis.com. Реально применяемые начертания:
     Manrope 200/300/500/700/800 · Inter 400/500/600 · JetBrains Mono 400/500.
     Исключение — витрина _kit.html: отдельный файл вне цикла head.php, не
     входит в объём этой правки, пока держит свой прежний <link> на Google
     Fonts (не мешает: у обоих источников одни и те же имена семейств).

   ОГЛАВЛЕНИЕ
     00.0 Шрифты — самохостинг (@font-face, было Google Fonts)
     00. Токены
     01. Reset и база
     02. Типографика и mono-eyebrow
     03. Layout-утилиты (container/section/grid/flex/spacing)
     04. Glow и декоративные приёмы
     05. Кнопки
     06. Публичная шапка и мобильное меню
     07. Подвал (site-footer)
     08. Кабинет — каркас (legacy top-nav + новый cab-shell/сайдбар)
     09. Auth — каркас (legacy + новый сплит с auth-aside)
     10. Hero (простой + product-first с фейковым дашбордом)
     11. Статс-борд и .stat-tile (KPI)
     12. Pipeline (glow-диаграмма стадий)
     13. Bento-сетка
     14. Split 60/40 (маркетинговый .split + утилитарный .split-60-40)
     15. Формы
     16. Карточки и сетки данных
     17. Задания (task-card, каталог, фильтры)
     18. Бейджи и площадки
     19. Meter / Progress / Sparkline
     20. Таблицы (table-wrap legacy + .data-table новый)
     21. Tabs / Accordion
     22. Modal
     23. Flash-сообщения
     24. Pagination / Empty state
     25. Steps (старый "как это работает")
     26. Достижения (achv-card)
     27. Review / споры
     28. Doc-toc / Notif-list / Danger-zone (новые сервисные компоненты)
     29. Legal / Error-shell
     30. Утилиты-хелперы (spacing, overflow-wrap, muted…)
     31. Доступность и медиа-запросы верхнего уровня
   ============================================================================ */


/* ============================================================================
   00.0 ШРИФТЫ — САМОХОСТИНГ (assets/fonts/*.woff2, копия — public/assets/fonts/)
   ДОБАВЛЕНО 12.08.2026. Раньше три семейства тянулись с Google Fonts через
   <link> в head.php с display=swap: до прихода woff2 браузер рисовал текст
   системным шрифтом с другими метриками, затем перерисовывал веб-шрифтом —
   видимый скачок раскладки (одна из причин жалобы на «съехавшие тексты»).
   Теперь файлы лежат локально, путь короче (свой домен, без DNS/TLS на
   fonts.gstatic.com), плюс preload двух самых ходовых файлов в head.php.

   ФАЙЛЫ — ровно те же woff2, что раньше отдавал fonts.gstatic.com (просто
   со своего домена), сабсеты cyrillic + latin (кириллица + базовая латиница/
   цифры/пунктуация — этого достаточно для рус. интерфейса с англ. вставками;
   cyrillic-ext/latin-ext — доп. диакритика укр./вьет./тур. и т.п. — в контенте
   сайта не встречается, не грузим).

   ВАЖНО — ЭТО ВАРИАТИВНЫЕ ШРИФТЫ (variable fonts), не статичные насечки:
   один файл на семейство+сабсет реально содержит весь диапазон по оси wght
   (проверено fontTools: Manrope 200–800, Inter 100–900, JetBrains Mono
   400–800 — отдаётся один и тот же файл независимо от того, сколько весов
   запросить у Google). Поэтому font-weight ниже указан ДИАПАЗОНОМ ("200 800"),
   а не перечнем чисел — браузер сам вычисляет нужную насыщенность внутри
   диапазона, включая границы. Диапазон каждой пары @font-face ограничен
   РОВНО тем, что реально красится на экране (аудит всей ui.css 12.08.2026,
   grep font-weight + проверка наследования по вложенным .stat-unit и т.п.,
   лишних начертаний сверх уже использовавшихся не добавляли):
     Manrope        200–800  (200/300/500/700/800: h1–h4, .brand, .stat-num,
                    вложенный .stat-unit, .dash-payout-num, .earn-val…)
     Inter          400–600  (400 — текст по умолчанию с <body>, 500/600 —
                    кнопки, лейблы, табы). Отдельные места вроде .badge
                    просят 700/800 у Inter, не входя в исходный набор Google
                    (400/500/600) — так было и раньше: браузер и до этой
                    правки не знал true-700/800 для Inter, ужимал до 600.
                    Диапазон 400–600 сохраняет ТО ЖЕ поведение 1:1.
     JetBrains Mono 400–500  (аналогично: единичные .num на 600/700 и раньше
                    зажимались до 500, диапазон 400–500 ничего не меняет).
   ============================================================================ */
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("/assets/fonts/manrope-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("/assets/fonts/manrope-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/assets/fonts/inter-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/assets/fonts/inter-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ============================================================================
   00. ТОКЕНЫ — ровно значения из docs/redesign/00_system.md, не менять.
   ============================================================================ */
:root {
  --bg: #0B0D14;            /* фон страницы */
  --surface: #141826;       /* карточки, панели */
  --surface-2: #10131F;     /* вложенные поверхности, шапки таблиц */
  --surface-3: #1A1F31;     /* ховер-состояния, инпуты */
  --border: rgba(255,255,255,.08);
  --border-strong: rgba(255,255,255,.16);
  --text: #EDEFF7;
  --text-2: #9AA3B8;        /* вторичный текст */
  --text-3: #8B93A7;        /* подписи, мета */
  --violet: #7C5CFF;        /* основной акцент */
  --blue: #3B82F6;          /* ссылки, второй акцент */
  --mint: #4ADE9D;          /* успех, деньги-приход */
  --amber: #F5B14C;         /* ожидание, предупреждение */
  --red: #F4685E;           /* ошибка, отклонение, деньги-расход */
  /* QA-контраст (12.08.2026, точный расчёт по формуле WCAG relative luminance):
     белый текст на исходных var(--violet)/var(--blue) давал 4.35:1 и 3.68:1 —
     ниже нормы AA 4.5:1 для обычного текста на ОБОИХ концах градиента. Концы
     градиента сдвинуты темнее (только для --grad, сами --violet/--blue не
     трогаем — они используются как текст/иконки на тёмном фоне в других
     местах, и там уже проходят контраст в другую сторону, темнее там было бы
     хуже). Новые значения: белый на #7657F2 = 4.76:1, белый на #326ED1 = 4.90:1
     — оба ≥4.5:1 с запасом. --violet/--blue как токены не меняются, --grad
     держит свои собственные более тёмные литералы. */
  --grad: linear-gradient(90deg, #7657F2, #326ED1);
  --font-display: "Manrope", "Segoe UI", sans-serif;
  --font-text: "Inter", "Segoe UI", Arial, sans-serif;
  --font-mono: "JetBrains Mono", Menlo, Consolas, monospace;
  --container-w: 1240px;
  --radius: 14px;
  --radius-sm: 10px;

  /* ---- расширения системы (новые токены, ничего из блока выше не меняют) ---- */
  /* QA-контраст: чистый var(--violet) КАК ТЕКСТ на тёмном фоне не проходит
     AA — .eyebrow на --bg давал 4.47:1 (норма 4.5:1), номера шагов
     (.steps-list .num/.accordion-head .step-num, фон violet-a14 поверх
     --surface) — 3.49:1. Здесь нужен более СВЕТЛЫЙ фиолетовый (в отличие от
     --grad выше, где текст белый и фон нужно было темнить) — расчёт: белый
     текст можно только высветлять, а не темнить фон токенов. #9B83FF даёт
     6.55:1 на --bg и 5.12:1 в паре с фоном violet-a14-поверх-surface — оба
     ≥4.5:1 с запасом. Использовать ТОЛЬКО как color текста на тёмных фонах,
     не как замену --violet для иконок/бордеров/фонов. */
  --violet-text-safe: #9B83FF;
  /* Голубой для .badge-blue: на тёмном нужен СВЕТЛЫЙ, на светлом — тёмный,
     поэтому это отдельный токен, а не var(--blue). */
  --badge-blue-fg: #BFD4FF;
  /* Красный для СПЛОШНЫХ заливок с белым текстом (счётчик уведомлений).
     На самом --red (#F4685E — мягкий коралловый, подобран как текст на
     тёмном) белый даёт 3.0:1 — ниже нормы AA. Затемнённый вариант: 4.51:1. */
  --red-solid: #E91E10;
  --radius-xs: 8px;
  --radius-lg: 20px;
  --radius-pill: 999px;
  --container-narrow: 720px;

  --violet-a14: rgba(124,92,255,.14);
  --violet-a20: rgba(124,92,255,.20);
  --violet-a35: rgba(124,92,255,.35);
  --blue-a12:   rgba(59,130,246,.12);
  --blue-a30:   rgba(59,130,246,.30);
  --mint-a14:   rgba(74,222,157,.14);
  --mint-a35:   rgba(74,222,157,.35);
  --amber-a14:  rgba(245,177,76,.14);
  --amber-a35:  rgba(245,177,76,.35);
  --red-a14:    rgba(244,104,94,.14);
  --red-a35:    rgba(244,104,94,.35);

  --shadow-sm: 0 1px 2px rgba(0,0,0,.35);
  --shadow-md: 0 16px 40px rgba(0,0,0,.40);
  --shadow-lg: 0 40px 90px rgba(0,0,0,.50);
  /* Тень аппарата на первом экране — отдельно от --shadow-*: она падает от
     ФИЗИЧЕСКОГО предмета, а не от карточки интерфейса, и потому глубже. */
  --phone-drop: 0 44px 90px rgba(0,0,0,.55);

  /* ---- кривые движения (14.08.2026). Встроенные ease/ease-out в CSS слишком
     вялые: у них нет «щелчка», из-за которого анимация читается как намеренная,
     а не как задержка отрисовки. Значения не выдуманы — это канонические кривые
     из практики Эмиля Ковальски (animations.dev), проверенные на UI:
       --ease-out    — всё, что ПОЯВЛЯЕТСЯ или УХОДИТ (стартует быстро → мгновенный отклик);
       --ease-in-out — всё, что ДВИЖЕТСЯ по экрану, не появляясь и не исчезая;
       --ease-drawer — шторки и панели, тянущиеся из-за края (кривая из Ionic).
     Своих cubic-bezier сверх этих трёх не изобретаем. */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);

  /* ---- поверхности, которые раньше были вписаны литералами прямо в правила
     (14.08.2026). Все четыре подразумевали ТЁМНЫЙ фон под собой: белая плёнка
     поверх тёмного и тёмное матовое стекло. На светлой теме они либо
     исчезали, либо превращали панель в чёрную. Вынесены в токены, чтобы тема
     переопределяла их одним местом. */
  --overlay-soft: rgba(255,255,255,.04);   /* лёгкая подложка (.btn-secondary) */
  --overlay-faint: rgba(255,255,255,.015); /* зебра в таблицах */
  --surface-blur: rgba(11,13,20,.80);      /* матовое стекло липких панелей */
  --panel-blur: rgba(20,24,38,.94);        /* липкая панель сохранения */

  /* ---- шкала отступов: КАНОН — ровно 8 ступеней, 4/8/12/16/24/32/48/64px.
     Имена ниже проверены 12.08.2026 — уже совпадают по значению с каноном,
     поэтому НЕ переименовывались (--sp-1..--sp-4, --sp-6, --sp-8, --sp-12,
     --sp-16 — это и есть все 8 ступеней канона). Своих значений сверх этой
     шкалы не изобретаем: если нужного отступа нет в каноне — берём ближайший
     токен канона, а не пишем произвольный px.
     --sp-5/--sp-10/--sp-20/--sp-24 — легаси-довески вне канона (были здесь
     раньше и уже используются в файле, поэтому не удалены — правки в этом
     файле аддитивные). Для НОВЫХ стилей их не брать, только 8 ступеней
     канона выше. */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px;
  --sp-12: 48px; --sp-16: 64px; --sp-20: 80px; --sp-24: 96px;

  /* ---- типографская шкала (--fs-*), ДОБАВЛЕНО 12.08.2026 — пока НИГДЕ не
     применяется, это только фундамент на следующий этап. Сейчас в файле 168
     объявлений font-size дают 35 уникальных значений, 14 из них втиснуты в
     диапазон 10.9–14.7px (шум, а не иерархия). Ступени ниже — ориентир для
     миграции; существующие font-size в компонентах этим шагом НЕ трогаем.
     9 ступеней в rem (база 16px): 11/12/13/14/16/18/21/26/34px. Две крупные
     (26/34) — через clamp() для адаптива, остальные статичны (мелкий/UI-текст
     во fluid-масштабировании не нуждается). */
  --fs-11: 0.6875rem;
  --fs-12: 0.75rem;
  --fs-13: 0.8125rem;
  --fs-14: 0.875rem;
  --fs-16: 1rem;
  --fs-18: 1.125rem;
  --fs-21: 1.3125rem;
  --fs-26: clamp(1.375rem, 1.2rem + 0.9vw, 1.625rem);
  --fs-34: clamp(1.75rem, 1.4rem + 1.8vw, 2.125rem);
  /* Кабинетный заголовок — отдельно от --fs-34: сейчас h1 в кабинете
     наследует МАРКЕТИНГОВЫЙ clamp(2rem, 1.5rem + 3vw, 3.4rem) (раздел 01) и
     раздувается до 54.4px на широких экранах («Финансы», «Мои задания») —
     для рабочего кабинетного экрана слишком крупно. --fs-title-app — фикс-
     размер (не fluid: кабинет не должен «дышать» как маркетинг) под будущее
     переопределение h1 внутри .cab-shell. Пока тоже не применяется нигде. */
  --fs-title-app: 1.75rem;

  --z-header: 50;
  --z-offcanvas: 80;
  --z-modal: 200;
}

/* ============================================================================
   00.1 СВЕТЛАЯ ТЕМА
   ----------------------------------------------------------------------------
   Тема переключается атрибутом data-theme на <html>. Значение приходит с
   сервера из cookie (partial_head), а на первом визите его до первой отрисовки
   проставляет крошечный инлайн-скрипт по системной настройке — поэтому вспышки
   чужой темы нет ни при первом заходе, ни при переходах между страницами.

   Тут переопределяются ТОЛЬКО значения токенов. Ни одно правило ниже по файлу
   про тему не знает — это и есть цена вопроса: пока цвет живёт в переменных,
   вторая тема стоит одного блока, а не переписывания стилей.

   Почему вообще две темы, а не выбор «по категории»: аудитории две и сценарии
   у них разные. Креатор сидит в телефоне и часто вечером — там выигрывает
   тёмная. Бизнес и админ работают с таблицами за десктопом при дневном свете —
   там выигрывает светлая. Поэтому по умолчанию берём системную настройку
   человека, а переключатель оставляем на видном месте.

   Акценты на светлом — НЕ те же самые цвета. Мятный, янтарный и красный
   подобраны заново: исходные (#4ADE9D / #F5B14C / #F4685E) рассчитаны как
   текст на тёмном и на белой карточке дают 1,6–3,3:1. Новые значения найдены
   перебором яркости при сохранении тона и насыщенности до порога AA 4.5:1
   ОДНОВРЕМЕННО на белой карточке и на собственной полупрозрачной подложке
   (--*-a14), потому что в интерфейсе они встречаются в обеих ролях:
   ЧЕТЫРЁХ светлых поверхностях (--surface, --surface-2, --surface-3, --bg) и
   на своей подложке поверх каждой из них — считать только по белой карточке
   мало, самый тяжёлый случай это --surface-3 с подложкой сверху:
     фиолетовый #5A31FF — худший случай 4.52:1
     синий      #0A57D6 — 4.56:1
     мятный     #146D46 — 4.54:1
     янтарь     #885408 — 4.51:1
     красный    #BD190D — 4.51:1
   Остальные проверенные пары (все ≥ 4.5:1): текст 18.17:1 на карточке,
   --text-2 7.20:1, --text-3 5.88:1 и 5.20:1 на --surface-2, белый текст на
   заливках --violet/--blue/--mint/--amber/--red 6.27–6.35:1, белый на обоих
   концах --grad 4.76:1 и 4.90:1.

   Тени на светлом — не те же с меньшей прозрачностью, а другие: на тёмном фоне
   тень почти не видна и её делают глубокой, на светлом такая же читается как
   грязь. Смещение и мягкое размытие сохраняются, кроющая сила падает втрое.
   ============================================================================ */
:root[data-theme="light"] {
  color-scheme: light;

  --bg: #F4F6FB;
  --surface: #FFFFFF;
  --surface-2: #EEF1F8;
  --surface-3: #E3E8F2;
  --border: rgba(17,22,44,.11);
  --border-strong: rgba(17,22,44,.22);
  --text: #10142A;
  --text-2: #4C5773;
  --text-3: #5A6480;

  --violet: #5A31FF;
  --blue: #0A57D6;
  --mint: #146D46;
  --amber: #885408;
  --red: #BD190D;
  /* На светлой теме отдельный «безопасный» фиолетовый не нужен: сам --violet
     уже подобран как текст. Токен оставлен, чтобы правила, которые его
     используют, не пришлось переписывать. */
  --violet-text-safe: #5A31FF;
  --badge-blue-fg: #0A57D6;
  --red-solid: #BD190D;
  /* --grad не трогаем: на нём лежит белый текст, и оба конца уже проходят
     контраст. Заливка кнопки от темы не зависит. */

  /* Подложки поверх светлых поверхностей: те же тона, но плотнее — на белом
     14 % почти не видно, читается как грязное пятно вместо заливки. */
  --violet-a14: rgba(90,49,255,.10);
  --violet-a20: rgba(90,49,255,.16);
  --violet-a35: rgba(90,49,255,.26);
  --blue-a12:   rgba(10,87,214,.09);
  --blue-a30:   rgba(10,87,214,.22);
  --mint-a14:   rgba(20,109,70,.11);
  --mint-a35:   rgba(20,109,70,.24);
  --amber-a14:  rgba(136,84,8,.11);
  --amber-a35:  rgba(136,84,8,.24);
  --red-a14:    rgba(189,25,13,.09);
  --red-a35:    rgba(189,25,13,.22);

  --overlay-soft: rgba(17,22,44,.04);
  --overlay-faint: rgba(17,22,44,.022);
  --surface-blur: rgba(255,255,255,.80);
  --panel-blur: rgba(255,255,255,.94);

  --shadow-sm: 0 1px 2px rgba(17,22,44,.08);
  --shadow-md: 0 12px 32px rgba(17,22,44,.10);
  --shadow-lg: 0 28px 64px rgba(17,22,44,.14);
  /* На светлом чернильная тень читается как грязь. Смещение и мягкое размытие
     сохраняются, кроющая сила падает вдвое с лишним. */
  --phone-drop: 0 34px 70px rgba(17,22,44,.26);
}

/* Аппарат на первом экране от темы НЕ зависит: это устройство с включённым
   экраном, и внутри него своя световая среда. Возвращаем ему тёмные значения
   локально — иначе подпись поверх видео становится чернильной, а мятная точка
   «идёт учёт» пропадает на светлом кадре. */
:root[data-theme="light"] .hero-phone {
  --text: #EDEFF7;
  --mint: #4ADE9D;
  --mint-a35: rgba(74,222,157,.35);
}

/* Декоративные ореолы на светлом надо приглушать. На тёмном фоне цветное пятно
   светится и добавляет глубины, на светлом та же плотность читается как
   разлитая краска и тянет на себя внимание раньше заголовка. */
:root[data-theme="light"] .glow { opacity: .18; }
:root[data-theme="light"] .hero-phone::before { opacity: .5; }
:root[data-theme="light"] .hero-phone-spill { opacity: .3; }

/* ============================================================================
   01. RESET И БАЗА
   ============================================================================ */
*, *::before, *::after { box-sizing: border-box; }
/* overflow-x:hidden — ТОЛЬКО на html, не на body. Если задать его на ОБОИХ,
   по спеке CSS Overflow overflow-y у каждого из них, у кого не задан явно,
   вычисляется в "auto" (нельзя иметь одну ось hidden, а другую visible) — то
   есть html И body одновременно становятся independent scroll-контейнерами
   (вложенный двойной скролл). Вложенный sticky-потомок в такой конфигурации
   в Chromium перестаёт стыковаться с фактическим вьюпортом — обнаружено и
   подтверждено скроллом в Playwright, QA-итерация 3, п.1 (это была ТРЕТЬЯ,
   ранее не описанная причина полной поломки position:sticky на сайте — она
   маскировала эффект от фиксов 03.1/12.0/14.0/28.1 из этой же итерации).
   Правило на будущее: overflow-x:hidden всегда только на html. */
/* color-scheme: dark — ДОБАВЛЕНО 12.08.2026. Без него браузер рисует нативные
   контролы (радиокнопки, чекбоксы, <input type=date> — календарик и его поля)
   в светлой схеме поверх тёмного фона сайта: невыбранная радиокнопка/чекбокс
   выглядит как выбранная (белая заливка кольца на тёмном фоне читается как
   маркер). Проверено скриншотами на /register-creator.php и
   /business/task-form.php (12.08.2026) — исправляет и радиокнопки, и чекбоксы
   (тот же баг, тот же фикс). Затрагивает только UA-стили нативных контролов —
   на остальную вёрстку не влияет.
   НЕ ЧИНИТ: цифровой порядок в <input type="date"> (mm/dd/yyyy vs дд.мм.гггг)
   — это отдельная история, формат берётся из локали браузера/ОС пользователя,
   а не из color-scheme; у реального рос. пользователя с русским браузером
   формат и так был дд.мм.гггг вне зависимости от этой правки (проверено:
   принудительный locale="ru-RU" в headless-тесте формат тоже не поменял —
   значит дело в ICU-данных окружения, а не в CSS). */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: hidden; color-scheme: dark; }
html, body { margin: 0; padding: 0; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-text);
  font-size: var(--fs-16);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: var(--blue); text-decoration: none; }
a:hover { text-decoration: underline; }
button { font-family: inherit; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -.01em;
  margin: 0 0 .5em;
  line-height: 1.15;
  color: var(--text);
  overflow-wrap: break-word;
}
h1 { font-size: clamp(2rem, 1.5rem + 3vw, 3.4rem); }
h2 { font-size: clamp(1.5rem, 1.25rem + 1.4vw, 2.1rem); max-width: 640px; }
h3 { font-size: var(--fs-18); }
h4 { font-size: var(--fs-16); }

p { margin: 0 0 1em; color: var(--text-2); overflow-wrap: break-word; }
ul, ol { overflow-wrap: break-word; margin: 0 0 1em; padding-left: var(--sp-4); color: var(--text-2); }
/* figure/blockquote/fieldset — в UA-стилях браузера margin-inline:40px
   (figure/blockquote) и разномастные padding/margin (fieldset), отсюда «чужой»
   40px в замере отступов, которого нет в этом файле. Тегами сейчас никто не
   пользуется (grep по всей вёрстке — 0 совпадений), правило добавлено
   заранее, чтобы будущий контент не притащил обратно браузерный дефолт.
   Только spacing-свойства (margin/padding) — border/появление своего вида
   этим шагом не занимаемся (fieldset в разметке уже сбрасывается инлайн-
   стилем border:none на месте использования). */
figure { margin: 0 0 var(--sp-4); }
blockquote { margin: 0 0 var(--sp-4); }
fieldset { margin: 0; padding: var(--sp-4); }
b, strong { color: var(--text); }
small { font-size: .85em; }
hr { border: none; border-top: 1px solid var(--border); margin: 2em 0; }

:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; border-radius: 6px; }
/* Выделение текста, каретка и полосы прокрутки — тоже поверхности интерфейса.
   Полосы и каретку красит color-scheme (он задан в обеих темах), выделение
   приходится задавать руками, иначе остаётся браузерная синева, не имеющая
   отношения ни к одной из тем. */
::selection { background: var(--violet-a35); color: var(--text); }

.skip-link {
  position: absolute; left: 12px; top: -60px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-strong);
  padding: var(--sp-2) var(--sp-4); border-radius: var(--radius-xs);
  z-index: 999; transition: top .2s ease;
}
.skip-link:focus { top: 12px; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* ============================================================================
   02. ТИПОГРАФИКА И MONO-EYEBROW
   ============================================================================ */
.eyebrow {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-family: var(--font-text); font-weight: 600; font-size: var(--fs-13);
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--violet-text-safe); margin-bottom: var(--sp-3);
}
.mono-eyebrow {
  display: inline-block;
  font-family: var(--font-mono); font-weight: 500; font-size: var(--fs-13);
  letter-spacing: .04em; color: var(--mint); margin-bottom: var(--sp-4);
}
.lead { font-size: var(--fs-16); color: var(--text-2); }
.lead-narrow { max-width: 560px; }
.gradient-text { background: var(--grad); -webkit-background-clip: text; background-clip: text; color: transparent; }
.section-title { max-width: 640px; margin: 0 auto var(--sp-8); }
.section-title.left { margin: 0 0 var(--sp-8); }
.muted { color: var(--text-3); }
.meta-footnote { font-size: var(--fs-14); color: var(--text-3); margin: var(--sp-3) 0 0; }

/* mono/tabular данные и суммы — везде через .num */
.num, .tabular-nums { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* ============================================================================
   03. LAYOUT-УТИЛИТЫ
   ============================================================================ */
/* БЕЗ z-index: числовой z-index здесь создавал собственный stacking context
   на КАЖДОМ .container сайта — из-за этого дочерний off-canvas элемент с
   заведомо большим z-index (например .filters:80) всё равно проигрывал
   элементу СНАРУЖИ .container с меньшим z-index (например backdrop:79),
   потому что весь .container-поддерево "запечатывалось" на уровне z-index:1
   родителя (QA-итерация 3, п.2). z-index не нужен: .glow всегда идёт раньше
   .container по DOM (см. .hero/.section-cta), поэтому контент и так рисуется
   поверх декоративного ореола за счёт обычного порядка отрисовки — числовой
   z-index был страховкой без необходимости и ломал off-canvas-панели.
   Правило на будущее: подложка off-canvas-панели (.cab-backdrop и т.п.)
   должна быть в ТОМ ЖЕ стек-контексте, что и сама панель (см. .cab-shell —
   .cab-backdrop и .cab-sidebar лежат рядом, оба вне .container). */
.container { max-width: var(--container-w); margin: 0 auto; padding: 0 var(--sp-4); position: relative; }
@media (min-width: 768px) { .container { padding: 0 var(--sp-6); } }

/* overflow:clip (не hidden!) — обрезает декоративные .glow за пределами секции,
   но, в отличие от hidden, НЕ создаёт "scroll container" и не ломает
   containing block для position:sticky у потомков (см. QA-итерация 3, п.1а).
   Поддержка: Chrome/Edge 90+, Firefox 113+, Safari 16+ — весь стек проекта
   уже опирается на :has()/более новые фичи, так что это не новый риск. */
.section { padding: var(--sp-12) 0; position: relative; overflow: clip; }
.section-sm { padding: var(--sp-6) 0; }
.section-soft { background: var(--surface-2); }
@media (min-width: 1024px) { .section { padding: var(--sp-16) 0; } .section-sm { padding: var(--sp-12) 0; } }

.center { text-align: center; }
.text-center { text-align: center; }
/* max-width задаёт длину строки, но САМ БЛОК не центрирует: коробка остаётся
   слева, и текст выравнивается по центру коробки, а не страницы. Внутри
   .text-center любой ограниченный по ширине потомок обязан центрироваться сам.
   Без этого h2 (max-width: 640px) уезжает влево на центрированных секциях. */
.text-center h1, .text-center h2, .text-center h3, .text-center h4,
.text-center p, .text-center .lead, .text-center .lead-narrow { margin-inline: auto; }
.cta-narrow { max-width: 640px; }
.flex { display: flex; }
.flex-between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; }
.flex-wrap { flex-wrap: wrap; }
.divider-line { border-top: 1px dashed var(--border); margin: var(--sp-4) 0; }

/* ---- 03.1 Grid-safety (системный фикс "grid blowout") ----
   По умолчанию грид-трек (auto/1fr) не сжимается уже min-content любого
   потомка — если внутри лежит широкий элемент (таблица с min-width, длинная
   строка без переноса), он распирает весь трек, а с ним и колонку/страницу.
   С overflow-x:hidden на html/body это не даёт горизонтальный скролл, а тихо
   обрезает контент вправо — самый коварный источник переполнения на 390px.
   Правило: у прямого потомка ЛЮБОГО grid-контейнера системы — min-width:0,
   чтобы трек сжимался до ширины родителя, а вложенные overflow-x:auto-блоки
   (.table-wrap, .data-table-wrap) сами прятали лишнее внутри себя. */
.footer-top > *,
.cab-shell > *,
.auth-split > *,
.hero-grid > *,
.stats-grid > *,
.bento > *,
.split > *,
.split-60-40 > *,
.form-grid > *,
.radio-cards > *,
.grid > *,
.catalog-layout > *,
.meter-list > *,
.steps-cols > *,
.doc-layout > *,
.task-stats > * {
  min-width: 0;
}

/* ============================================================================
   04. GLOW И ДЕКОРАТИВНЫЕ ПРИЁМЫ
   Мягкий ореол под ключевым блоком. Всегда pointer-events:none, за контентом.
   ============================================================================ */
.glow {
  position: absolute; border-radius: 50%;
  filter: blur(120px); opacity: .35; pointer-events: none; z-index: 0;
}
.glow-violet { background: radial-gradient(circle, var(--violet), transparent 70%); }
.glow-blue   { background: radial-gradient(circle, var(--blue), transparent 70%); }
.glow-mint   { background: radial-gradient(circle, var(--mint), transparent 70%); }

.glow-hero { width: 340px; height: 340px; top: -140px; left: 10%; background: radial-gradient(circle, var(--violet), transparent 70%); }
.glow-cta  { width: 320px; height: 320px; bottom: -180px; left: 50%; transform: translateX(-50%); background: radial-gradient(circle, var(--blue), transparent 70%); }
@media (min-width: 768px) {
  .glow-hero { width: 460px; height: 460px; top: -180px; }
  .glow-cta  { width: 460px; height: 460px; bottom: -220px; }
}
@media (min-width: 1280px) {
  .glow-hero { width: 640px; height: 640px; }
  .glow-cta  { width: 600px; height: 600px; }
}

/* ============================================================================
   05. КНОПКИ
   Правило: одна градиентная кнопка на видимую зону экрана — остальные
   плоские (surface-3) или outline.
   ============================================================================ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-6);
  font-family: var(--font-text); font-weight: 600; font-size: var(--fs-16);
  border: 1px solid transparent; cursor: pointer; min-height: 44px;
  text-decoration: none; white-space: nowrap;
  transition: box-shadow .2s ease, transform .15s ease, background .2s ease, border-color .2s ease, color .2s ease;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .5; cursor: not-allowed; pointer-events: none; }

.btn-primary { background: var(--grad); color: #fff; }
.btn-primary:hover { box-shadow: 0 0 28px rgba(124,92,255,.55); transform: translateY(-1px); }

.btn-secondary { background: var(--overlay-soft); color: var(--text); border-color: var(--border-strong); }
.btn-secondary:hover { border-color: rgba(124,92,255,.6); box-shadow: 0 0 16px rgba(124,92,255,.25); }

.btn-outline { background: transparent; color: var(--text); border-color: var(--border-strong); }
.btn-outline:hover { border-color: var(--violet); color: var(--text); }

.btn-ghost { background: transparent; color: var(--text-2); }
.btn-ghost:hover { background: var(--surface-3); color: var(--text); }

.btn-danger { background: var(--red-a14); color: var(--red); border-color: var(--red-a35); }
.btn-danger:hover { background: var(--red-a35); color: #fff; }

.btn-sm { padding: var(--sp-2) var(--sp-4); font-size: var(--fs-14); min-height: 40px; }
.btn-block { width: 100%; }
.btn-group { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
/* На мобильных касание пальцем — .btn-sm обязан быть ≥44px (ТЗ: тап-зоны ≥44×44px).
   На десктопе (мышь) оставляем компактные 40px. */
@media (max-width: 640px) { .btn-sm { min-height: 44px; } }

/* ============================================================================
   06. ПУБЛИЧНАЯ ШАПКА И МОБИЛЬНОЕ МЕНЮ
   ============================================================================ */
.site-header { position: sticky; top: 0; z-index: var(--z-header); border-bottom: 1px solid var(--border); }
.site-header::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--surface-blur); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
}
/* padding-block, а не шорткат: этот блок идёт вместе с .container, и шорткат
   ставил свои 16px по горизонтали вместо контейнерных 24px — логотип и меню
   стояли на 8px левее всего остального контента страницы. */
.header-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); padding-block: var(--sp-4); min-height: 72px; }
.brand { display: flex; align-items: center; gap: var(--sp-2); font-family: var(--font-display); font-weight: 700; color: var(--text); flex-shrink: 0; }
.brand:hover { text-decoration: none; }
.brand img { width: 28px; height: 28px; border-radius: 8px; }
.brand .brand-sub { display: none; }

.main-nav { display: flex; align-items: center; gap: var(--sp-6); }
.main-nav a { color: var(--text-2); font-weight: 500; }
.main-nav a:hover { color: var(--text); text-decoration: none; }
.main-nav a.active { color: var(--text); }
.nav-auth-mobile { display: none; }

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

/* ---- переключатель темы ---- */
.theme-toggle {
  width: 40px; height: 40px; flex-shrink: 0; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-pill); border: 1px solid var(--border);
  background: var(--surface); color: var(--text-2); cursor: pointer;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
.theme-toggle:hover { color: var(--text); border-color: var(--border-strong); }
.theme-toggle svg { width: 18px; height: 18px; display: block; }
/* Иконка называет РЕЗУЛЬТАТ нажатия, а не текущее состояние: на тёмной теме
   светит солнце («нажми — станет светло»), на светлой висит месяц. Подпись для
   скринридера (aria-label) говорит ровно то же и переписывается в ui.js. */
.theme-toggle .ico-moon { display: none; }
:root[data-theme="light"] .theme-toggle .ico-sun { display: none; }
:root[data-theme="light"] .theme-toggle .ico-moon { display: block; }

/* Смена темы — редкое действие, здесь и лежит бюджет на «приятно». Новая тема
   раскрывается кругом из самой кнопки: видно, что переключил именно ты и
   именно этим. Работает через View Transitions; где его нет и при
   prefers-reduced-motion тема просто меняется мгновенно — это корректный
   исход, а не поломка. */
::view-transition-old(root) { animation: none; }
::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
html.theme-swap::view-transition-new(root) {
  animation: theme-swap 560ms var(--ease-out);
  z-index: 1;
}
html.theme-swap::view-transition-old(root) { z-index: 0; }
@keyframes theme-swap {
  from { clip-path: circle(0 at var(--swap-x, 50%) var(--swap-y, 0)); }
  to   { clip-path: circle(var(--swap-r, 150vmax) at var(--swap-x, 50%) var(--swap-y, 0)); }
}

.burger {
  display: none; width: 44px; height: 44px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface);
  align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0;
}
.burger span, .burger span::before, .burger span::after {
  content: ""; display: block; width: 20px; height: 2px; border-radius: 2px;
  background: var(--text); position: relative; transition: transform .2s ease, top .2s ease, background .2s ease;
}
.burger span::before { position: absolute; top: -7px; left: 0; }
.burger span::after { position: absolute; top: 7px; left: 0; }
.burger[aria-expanded="true"] span { background: transparent; }
.burger[aria-expanded="true"] span::before { top: 0; transform: rotate(45deg); }
.burger[aria-expanded="true"] span::after { top: 0; transform: rotate(-45deg); }

@media (max-width: 1023px) {
  .site-header { --header-h: 73px; }
  .burger { display: flex; }
  .header-actions { position: relative; z-index: calc(var(--z-header) + 5); }
  .main-nav {
    position: fixed; top: var(--header-h, 73px); right: 0;
    height: calc(100vh - var(--header-h, 73px)); width: min(320px, 84vw);
    background: var(--surface-2); border-left: 1px solid var(--border);
    flex-direction: column; align-items: flex-start; gap: var(--sp-1);
    padding: var(--sp-8) var(--sp-6) var(--sp-6); transform: translateX(100%);
    transition: transform .25s ease; z-index: var(--z-offcanvas); overflow-y: auto;
  }
  .main-nav.open { transform: translateX(0); }
  .main-nav a { width: 100%; padding: var(--sp-3) 0; border-bottom: 1px solid var(--border); min-height: 44px; display: flex; align-items: center; }
  .nav-auth-mobile { display: flex; flex-direction: column; gap: var(--sp-3); width: 100%; margin-top: var(--sp-4); }
  .nav-auth-mobile .btn { width: 100%; }
  .header-actions .btn.btn-ghost, .header-actions .btn.btn-primary { display: none; }
}

/* ============================================================================
   07. ПОДВАЛ
   ============================================================================ */
.site-footer { background: var(--bg); color: var(--text); padding: var(--sp-12) 0 var(--sp-6); border-top: 1px solid var(--border); margin-top: auto; }
.site-footer a { color: var(--text-2); }
.site-footer a:hover { color: var(--text); }
.footer-top { display: grid; grid-template-columns: 1fr; gap: var(--sp-8); padding-bottom: var(--sp-8); border-bottom: 1px solid var(--border); }
.footer-brand { display: flex; align-items: center; gap: var(--sp-2); font-family: var(--font-display); font-weight: 700; margin-bottom: var(--sp-3); color: var(--text); }
.footer-brand img { width: 26px; height: 26px; border-radius: 7px; }
.footer-desc { color: var(--text-2); font-size: var(--fs-14); max-width: 300px; margin: 0; }
.footer-col h4 { font-family: var(--font-text); font-size: var(--fs-13); text-transform: uppercase; letter-spacing: .08em; color: var(--text); margin-bottom: var(--sp-3); font-weight: 600; }
.footer-col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); font-size: var(--fs-14); }
.footer-bottom { padding-top: var(--sp-6); }
.footer-bottom .req, .req { font-size: var(--fs-14); color: var(--text-3); margin: 0 0 var(--sp-2); line-height: 1.6; }
.req a { color: var(--text-2); }
.footer-meta-note { font-size: var(--fs-13); color: var(--text-3); margin: var(--sp-2) 0 0; }

@media (min-width: 768px) { .footer-top { grid-template-columns: 1.4fr 1fr 1fr 1fr; } }
@media (max-width: 900px) and (min-width: 768px) { .footer-top { grid-template-columns: 1fr 1fr; } }

/* ============================================================================
   08. КАБИНЕТ — КАРКАС
   Legacy (верхнее меню, ещё встречается в PHP) + НОВЫЙ каркас с левым
   сайдбаром (референс Vapi/Airtable/Dovetail): .cab-shell/.cab-sidebar/.cab-topbar.
   ============================================================================ */

/* ---- 08.1 Legacy: верхняя кабинетная шапка ---- */
.cabinet-shell { display: flex; min-height: 100vh; flex-direction: column; background: var(--bg); }
.cabinet-header { position: sticky; top: 0; z-index: var(--z-header); background: var(--surface-blur); backdrop-filter: blur(12px); border-bottom: 1px solid var(--border); }
.cabinet-header .header-row { min-height: 64px; }
.cabinet-nav { display: flex; gap: var(--sp-1); align-items: center; flex-wrap: wrap; }
.cabinet-nav a { color: var(--text-2); font-weight: 600; font-size: var(--fs-14); padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-pill); white-space: nowrap; }
.cabinet-nav a:hover { background: var(--surface-3); color: var(--text); text-decoration: none; }
.cabinet-nav a.active { background: var(--violet-a14); color: var(--text); }

.user-box { display: flex; align-items: center; gap: var(--sp-3); flex-shrink: 0; }
.user-name-wrap { display: flex; flex-direction: column; line-height: 1.3; }
.user-name { font-weight: 700; font-size: var(--fs-14); color: var(--text); }
.user-role { font-size: var(--fs-12); color: var(--text-3); display: block; }

.bell {
  position: relative; width: 44px; height: 44px; border-radius: var(--radius-pill);
  border: 1px solid var(--border); background: var(--surface);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0;
}
.bell svg { width: 20px; height: 20px; stroke: var(--text); }
.bell .bell-count {
  position: absolute; top: -4px; right: -4px; min-width: 18px; height: 18px; padding: 0 var(--sp-1);
  border-radius: var(--radius-pill); background: var(--red-solid); color: #fff;
  font-size: var(--fs-12); font-weight: 800; display: flex; align-items: center; justify-content: center;
}
.bell .bell-count[data-zero="true"] { display: none; }

@media (max-width: 1023px) {
  .cabinet-nav {
    position: fixed; inset: 64px 0 0 0; background: var(--surface-2);
    flex-direction: column; align-items: stretch; padding: var(--sp-4) var(--sp-4); gap: 2px;
    overflow-y: auto; transform: translateX(100%); transition: transform .25s ease;
    box-shadow: var(--shadow-lg); z-index: var(--z-offcanvas);
  }
  .cabinet-nav.open { transform: translateX(0); }
  .cabinet-nav a { border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-3); border-bottom: 1px solid var(--border); }
  .user-box .user-name-wrap { display: none; }
}

/* ---- 08.2 Новый каркас: левый сайдбар ---- */
.cab-shell { display: grid; grid-template-columns: 1fr; min-height: 100vh; background: var(--bg); }

.cab-sidebar {
  display: flex; flex-direction: column; gap: var(--sp-6);
  width: min(280px, 84vw); background: var(--surface-2); border-right: 1px solid var(--border);
  padding: var(--sp-5) var(--sp-4) var(--sp-4);
  position: fixed; inset: 0 auto 0 0; z-index: var(--z-offcanvas);
  overflow-y: auto; transform: translateX(-100%); transition: transform .25s ease;
}
.cab-sidebar.open { transform: translateX(0); }
.cab-sidebar .brand { padding: 0 var(--sp-2); margin-bottom: var(--sp-2); }

.cab-nav { display: flex; flex-direction: column; gap: var(--sp-5); flex: 1; }
.cab-group { display: flex; flex-direction: column; gap: 2px; }
.cab-group-label {
  font-family: var(--font-mono); font-size: var(--fs-12); text-transform: uppercase; letter-spacing: .08em;
  color: var(--text-3); padding: 0 var(--sp-3); margin-bottom: var(--sp-1);
}
.cab-link {
  display: flex; align-items: center; gap: var(--sp-2); min-height: 44px;
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-sm);
  color: var(--text-2); font-weight: 500; font-size: var(--fs-14);
}
.cab-link:hover { background: var(--surface-3); color: var(--text); text-decoration: none; }
.cab-link.active { background: var(--violet-a14); color: var(--text); }
.cab-link .cab-link-icon { width: 18px; height: 18px; flex-shrink: 0; }
.cab-link .cab-link-count {
  margin-left: auto; font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-3);
  background: var(--surface-3); border-radius: var(--radius-pill); padding: 1px var(--sp-2);
}

.cab-user {
  display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3);
  border-top: 1px solid var(--border); margin-top: auto;
}
.cab-user .user-name-wrap { flex: 1; min-width: 0; }
.cab-user .user-name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cab-user form { flex-shrink: 0; }

.cab-backdrop {
  display: none; position: fixed; inset: 0; background: rgba(5,6,10,.6);
  z-index: calc(var(--z-offcanvas) - 1);
}
.cab-backdrop.open { display: block; }

.cab-main { display: flex; flex-direction: column; min-width: 0; }

.cab-topbar {
  position: sticky; top: 0; z-index: var(--z-header);
  display: flex; align-items: center; gap: var(--sp-4);
  min-height: 64px; padding: 0 var(--sp-4);
  background: var(--surface-blur); backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}
.cab-burger { display: flex; }
.cab-breadcrumbs { display: flex; align-items: center; gap: var(--sp-1); font-size: var(--fs-14); color: var(--text-3); min-width: 0; overflow: hidden; }
.cab-breadcrumbs a { color: var(--text-3); }
.cab-breadcrumbs a:hover { color: var(--text); }
.cab-breadcrumbs .current { color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cab-topbar-search {
  flex: 1; max-width: 320px; display: none;
}
.cab-topbar-search input {
  width: 100%; background: var(--surface-3); border: 1px solid var(--border);
  border-radius: var(--radius-pill); padding: var(--sp-2) var(--sp-4); color: var(--text); font-size: var(--fs-14); min-height: 40px;
}
.cab-topbar-actions { display: flex; align-items: center; gap: var(--sp-3); margin-left: auto; flex-shrink: 0; }
.cab-balance {
  display: flex; flex-direction: column; align-items: flex-end; line-height: 1.2;
}
.cab-balance .cab-balance-label { font-size: var(--fs-12); color: var(--text-3); text-transform: uppercase; letter-spacing: .05em; }
.cab-balance .cab-balance-val { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 500; color: var(--mint); font-size: var(--fs-16); }

.cab-content { padding: var(--sp-5) var(--sp-4) var(--sp-16); flex: 1; }
/* QA: h1 в кабинете наследовал маркетинговый fluid-clamp (раздел 01) и
   разрастался до 54.4px на широких экранах ("Финансы", "Мои задания") —
   слишком крупно для рабочего экрана. --fs-title-app — фикс-размер (не
   fluid), задан другим агентом заранее специально под этот случай.
   Скоуп — только .cab-content, лендинг и публичные страницы не задеты. */
.cab-content h1 { font-size: var(--fs-title-app); }

@media (min-width: 768px) { .cab-content { padding: var(--sp-6) var(--sp-6) var(--sp-16); } .cab-topbar { padding: 0 var(--sp-6); } }

@media (min-width: 1025px) {
  .cab-shell { grid-template-columns: 240px 1fr; }
  .cab-sidebar { position: sticky; top: 0; height: 100vh; transform: none; width: 240px; }
  .cab-burger { display: none; }
  .cab-backdrop { display: none !important; }
  .cab-topbar-search { display: block; }
}

/* ============================================================================
   09. AUTH — КАРКАС
   Legacy (.auth-shell/.auth-main) + новый сплит-экран (референс Supabase):
   форма слева по центру, .auth-aside — тёмная панель с glow справа.
   ============================================================================ */
.auth-shell { min-height: 100vh; display: flex; flex-direction: column; background: var(--bg); }
.auth-main { flex: 1; display: flex; align-items: center; padding: var(--sp-10) 0; }

.auth-split { display: grid; grid-template-columns: 1fr; min-height: 100vh; }
.auth-form-col { display: flex; align-items: center; justify-content: center; padding: var(--sp-10) var(--sp-4); }
.auth-form-col .form-card { width: 100%; max-width: 440px; }

.auth-aside {
  position: relative; overflow: hidden; display: flex; flex-direction: column; justify-content: center;
  gap: var(--sp-5); padding: var(--sp-10) var(--sp-8);
  background: var(--surface-2); border-top: 1px solid var(--border);
  color: var(--text);
}
.auth-aside .glow { top: -120px; right: -80px; }
.auth-aside h2 { max-width: 420px; position: relative; z-index: 1; }
.auth-aside p { position: relative; z-index: 1; color: var(--text-2); max-width: 420px; }
.auth-aside-bullets { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-3); position: relative; z-index: 1; }
.auth-aside-bullets li { display: flex; gap: var(--sp-2); align-items: flex-start; font-size: var(--fs-14); color: var(--text-2); }
.auth-aside-bullets li::before {
  content: ""; flex-shrink: 0; width: 18px; height: 18px; margin-top: 2px; border-radius: 5px;
  background: var(--mint-a14); border: 1px solid var(--mint);
}

@media (min-width: 900px) {
  .auth-split { grid-template-columns: 1.1fr .9fr; }
  .auth-aside { border-top: none; border-left: 1px solid var(--border); }
}

/* ============================================================================
   10. HERO
   ============================================================================ */
/* Обрезаем ТОЛЬКО по горизонтали. Декоративные ореолы (.glow-hero, свечение за
   аппаратом) шире экрана и без обрезки добавляли странице горизонтальный запас:
   на телефоне это ловилось щипком — страница «отъезжала» и сбоку появлялось
   пустое поле. Но обрезка по обеим осям срезала тень под аппаратом ровно по
   нижней границе секции.
   overflow-x: clip — единственный способ обрезать одну ось и оставить вторую
   свободной: с hidden браузер обязан привести соседнюю visible к auto, и
   получился бы скролл-контейнер, у clip такого требования нет. */
.hero { position: relative; padding: var(--sp-8) 0 var(--sp-8); overflow-x: clip; }
.hero-grid { display: grid; grid-template-columns: 1fr; gap: var(--sp-8); align-items: center; }
.hero-media img { border-radius: var(--radius); border: 1px solid var(--border); width: 100%; aspect-ratio: 16/10; object-fit: cover; }
.hero-cta { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-6); }
@media (min-width: 1025px) { .hero-grid { grid-template-columns: 1.1fr .9fr; } .hero { padding: var(--sp-16) 0 var(--sp-8); } }

/* ---- product-first hero (демо-дашборд), см. mydemo5v2.html ---- */
.hero-top { display: flex; flex-direction: column; gap: var(--sp-4); padding-bottom: var(--sp-2); }
/* hyphens: manual — точечная отмена переноса по слогам, который включён для h1
   на узких экранах (раздел про 480px). Там он спасает длинные слова в узких
   колонках, здесь только вредит: заголовок первого экрана короткий, все слова
   помещаются, а браузер всё равно рвал «про-смотры» с дефисом. */
.hero-copy h1 { margin-bottom: 0; hyphens: manual; -webkit-hyphens: manual; }
.hero-manifesto { display: flex; flex-direction: column; gap: var(--sp-2); max-width: 560px; }
.hero-manifesto .lead { margin: 0; }
.manifesto-link {
  display: inline-flex; align-items: center; gap: var(--sp-1); font-family: var(--font-mono); font-size: var(--fs-14);
  color: var(--text); border-bottom: 1px solid var(--border-strong); padding: var(--sp-3) 0; align-self: flex-start; min-height: 44px;
}
.manifesto-link:hover { color: var(--mint); border-color: var(--mint); text-decoration: none; }
.manifesto-link span { transition: transform .2s ease; }
.manifesto-link:hover span { transform: translateX(4px); }

/* padding-block, а НЕ шорткат padding: в hero этот блок стоит вместе с
   классом .container, и шорткат с нулями по бокам затирал горизонтальные
   отступы контейнера — кнопки вылезали на 24px левее заголовка и сноски. */
.hero-cta-row { display: flex; flex-wrap: wrap; gap: var(--sp-4); padding-block: var(--sp-6) var(--sp-8); }
.hero-cta-center { justify-content: center; }
/* Отбивка от сноски над мокапом: без неё окно дашборда приклеивается
   вплотную к строке «Сейчас на платформе активно заданий». */
.hero-dash-wrap { padding-bottom: 0; margin-top: var(--sp-8); }

/* Без max-width: мокап занимает всю ширину контейнера, поэтому его края
   стоят на той же вертикали, что заголовок, кнопки и сноска. Раньше он был
   980px по центру и начинался на 230px — не совпадал ни с чем на экране. */
.dash-window {
  position: relative; margin: 0 auto;
  border-radius: 16px 16px 0 0; border: 1px solid var(--border-strong); border-bottom: none;
  background: var(--surface); box-shadow: var(--shadow-lg); overflow: hidden;
}
.dash-titlebar { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border); background: var(--surface-2); }
.dash-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--border-strong); }
.dash-title { margin-left: var(--sp-2); font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-3); }
.dash-body { display: flex; min-height: 0; }
.dash-sidebar { display: none; flex-direction: column; gap: var(--sp-1); width: 180px; flex-shrink: 0; padding: var(--sp-4) var(--sp-3); border-right: 1px solid var(--border); background: var(--surface-2); }
.dash-nav-item { font-size: var(--fs-14); color: var(--text-2); padding: var(--sp-2) var(--sp-2); border-radius: var(--radius-xs); }
.dash-nav-item.is-active { background: var(--violet-a14); color: var(--text); }
.dash-main { flex: 1; min-width: 0; padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-4); }
.dash-table { display: flex; flex-direction: column; gap: 2px; min-width: 0; overflow-x: auto; }
.dash-row { display: grid; grid-template-columns: 1.5fr .9fr .8fr auto; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-13); border-radius: var(--radius-xs); color: var(--text-2); white-space: nowrap; }
.dash-row > span { min-width: 0; }
.dash-row > span:first-child { overflow: hidden; text-overflow: ellipsis; }
.dash-row-head { font-family: var(--font-mono); font-size: var(--fs-11); text-transform: uppercase; letter-spacing: .05em; color: var(--text-3); }
.dash-row:not(.dash-row-head) { background: var(--surface-2); color: var(--text); }
.dash-status { display: inline-flex; align-items: center; gap: var(--sp-1); font-size: var(--fs-12); font-weight: 600; padding: var(--sp-1) var(--sp-2); border-radius: var(--radius-pill); justify-self: start; white-space: nowrap; }
.dash-status::before { content: ""; flex-shrink: 0; width: 7px; height: 7px; border-radius: 50%; }
.dash-status-ok { background: var(--mint-a14); color: var(--mint); }
.dash-status-ok::before { background: var(--mint); }
.dash-status-wait { background: var(--amber-a14); color: var(--amber); }
.dash-status-wait::before { background: var(--amber); }
.dash-payout-card { display: flex; flex-direction: column; gap: 2px; padding: var(--sp-4) var(--sp-4); border-radius: var(--radius-sm); border: 1px solid var(--border-strong); background: linear-gradient(135deg, var(--violet-a14), var(--blue-a12)); }
.dash-payout-label { font-size: var(--fs-12); color: var(--text-2); }
.dash-payout-num { font-family: var(--font-display); font-weight: 800; font-size: var(--fs-26); color: var(--text); }
.dash-payout-sub { font-size: var(--fs-12); color: var(--text-3); }

@media (max-width: 400px) {
  .dash-main { padding: var(--sp-3); }
  .dash-row { grid-template-columns: 1.3fr .8fr .7fr auto; gap: var(--sp-1); padding: var(--sp-2); font-size: var(--fs-12); }
  .dash-row-head { font-size: var(--fs-11); }
  .dash-status { padding: 0; width: 16px; height: 16px; justify-content: center; }
  .dash-status-text { display: none; }
  .dash-payout-card { padding: var(--sp-3); }
  .dash-payout-num { font-size: var(--fs-21); }
}
@media (min-width: 640px) { .dash-row { grid-template-columns: 1.6fr 1fr .9fr 1.1fr; font-size: var(--fs-14); } }
@media (min-width: 768px) { .dash-sidebar { display: flex; } }
@media (min-width: 1025px) {
  .hero-top { flex-direction: row; align-items: flex-end; justify-content: space-between; gap: var(--sp-8); }
  .hero-copy { max-width: 560px; }
  .hero-manifesto { align-items: flex-end; text-align: right; margin-bottom: var(--sp-1); }
  .hero-manifesto .lead { text-align: right; }
  .manifesto-link { align-self: flex-end; }
  .hero-dash-wrap { margin-bottom: calc(var(--sp-16) * -1); }
  .dash-window { border-radius: 18px 18px 0 0; }
}

/* ============================================================================
   10.1 HERO-STAGE — телефон с лентой роликов + лента начислений
   ----------------------------------------------------------------------------
   Заменяет на главной обобщённый мокап окна дашборда (.dash-window). Причина:
   «окно браузера с фейковой таблицей» — визуал, который подходит любому SaaS и
   потому не говорит про этот продукт ничего. Здесь на первом экране сразу видно
   предмет сделки: слева реальные вертикальные ролики в аппарате, справа — счёт,
   который растёт от их просмотров. .dash-window НЕ удалён: он остался в
   mydemo5v2.html и redesign/index.html.

   Геометрия корпуса — не «скруглённый прямоугольник на глаз», а пропорции
   iPhone 17 Pro Max, пересчитанные из паспортных размеров:
     корпус 163,4 × 78,0 мм; экран 6,9″ (2868×1320 px = 440×956 pt).
     Диагональ экрана 175,26 мм при отношении 440:956 даёт 73,3 × 159,2 мм,
     отсюда рамка (78,0 − 73,3) / 2 = 2,37 мм ≈ 2,85 % ширины корпуса.
     Радиус экрана у Pro Max — 55 pt при ширине 440 pt, то есть 12,5 % ширины
     экрана; радиус корпуса = радиус экрана + рамка.
   Поэтому все размеры считаются от одной переменной --phone-w: меняешь ширину —
   корпус остаётся пропорциональным. Само отношение сторон задано экрану
   (440/956), а не корпусу, чтобы видео не растягивалось.
   ============================================================================ */
.hero-stage-wrap { margin-top: var(--sp-8); }
.hero-stage { display: grid; gap: var(--sp-8); justify-items: center; }
.hero-stage-col { min-width: 0; width: 100%; }

/* ---- корпус ---- */
.hero-phone {
  --phone-w: 268px;
  --bezel: calc(var(--phone-w) * .0285);
  --frame-r: calc(var(--phone-w) * .148);
  --screen-r: calc(var(--phone-w) * .118);
  position: relative; isolation: isolate; flex-shrink: 0;
  /* Перспектива для наклона корпуса вслед за курсором. Дальняя (1600px) —
     чтобы аппарат поворачивался, а не «разъезжался»: сильная перспектива на
     объекте такого размера читается как искажение, а не как объём. */
  perspective: 1600px;
}
/* Сам наклон живёт в двух переменных, их проставляет JS. transition даёт
   инерцию: корпус догоняет курсор, а не приклеен к нему — так он ощущается
   вещью с весом. preserve-3d здесь НЕ ставим: содержимое экрана должно
   сплющиваться в плоскость корпуса, иначе ломается обрезка по скруглению. */
.hero-phone-frame { transform: rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg)); transition: transform 320ms var(--ease-out); }
/* Свечение под аппаратом собрано из ДВУХ слоёв, и это не украшательство.
   z-index: -1 работает внутри isolation: isolate у .hero-phone — подсветка
   уходит за корпус, но остаётся поверх фона секции.

   Слой 1 (::before) — общий ореол в цветах системы: широкий фиолетовый бутон и
   холодная синяя подсветка снизу. Он статичен и держит аппарат на фоне, чтобы
   тот не выглядел вырезанным из другой картинки.

   Слой 2 (.hero-phone-spill) — засветка от САМОГО ролика: постер текущего кадра,
   растянутый и сильно размытый. Экран, который светится, обязан красить всё
   вокруг себя, и цвет этой засветки меняется вместе с роликом. Берём именно
   постер, а не второе видео: картинка размывается один раз и дальше только
   композитится, тогда как второй видеопоток стоил бы ещё одного декодера. */
.hero-phone::before {
  content: ""; position: absolute; z-index: -1; left: 50%; top: 50%;
  width: 210%; height: 84%; transform: translate(-50%, -50%);
  background:
    radial-gradient(closest-side, rgba(124,92,255,.30), transparent 74%),
    radial-gradient(closest-side at 50% 78%, rgba(59,130,246,.22), transparent 72%);
  filter: blur(28px);
}
.hero-phone-spill {
  position: absolute; z-index: -1; left: 50%; top: 50%;
  width: 142%; height: 106%; transform: translate(-50%, -50%);
  object-fit: cover; border-radius: 50%;
  filter: blur(46px) saturate(1.9); opacity: .52;
  transition: opacity 480ms var(--ease-out);
  pointer-events: none;
}
.hero-phone-frame {
  position: relative; width: var(--phone-w); padding: var(--bezel);
  border-radius: var(--frame-r);
  /* Алюминиевый юнибоди: светлые рёбра по краям, тёмная плоскость в середине —
     градиент под 150°, чтобы блик шёл по диагонали, как от источника сверху. */
  background: linear-gradient(150deg, #454B63 0%, #232840 20%, #14182A 50%, #232840 80%, #454B63 100%);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.12),
    inset 0 0 0 1px rgba(255,255,255,.07),
    var(--phone-drop);
}
.hero-phone-screen {
  position: relative; width: 100%; aspect-ratio: 440 / 956;
  border-radius: var(--screen-r); overflow: hidden; background: #000;
  /* Safari не обрезает по border-radius потомков с 3D-трансформом при обычном
     overflow: hidden. Маска сплошным цветом заставляет его сделать это. */
  -webkit-mask-image: -webkit-radial-gradient(#fff, #000);
}

/* ---- боковые кнопки: слева Action + качелька громкости, справа боковая
       кнопка и Camera Control. Позиции и длины — доли от высоты корпуса по
       реальному расположению на 17 Pro Max. ---- */
.hero-phone-btn {
  position: absolute; width: 2px; border-radius: 2px;
  background: linear-gradient(90deg, #4E556E, #262B40);
}
.hero-phone-btn.is-action { left: -2px; top: 24%; height: 5.5%; }
.hero-phone-btn.is-volup  { left: -2px; top: 32%; height: 8.5%; }
.hero-phone-btn.is-voldn  { left: -2px; top: 42%; height: 8.5%; }
.hero-phone-btn.is-power  { right: -2px; top: 32%; height: 15%; background: linear-gradient(270deg, #4E556E, #262B40); }
.hero-phone-btn.is-cam    { right: -2px; top: 51%; height: 6.7%; background: linear-gradient(270deg, #4E556E, #262B40); }

/* ---- Dynamic Island: 125 × 36,5 pt на экране 440 pt, отступ сверху ~11 pt.
       Считаем от ширины ЭКРАНА, поэтому 28,4 % / 8,3 % / 2,5 %. ---- */
.hero-phone-island {
  position: absolute; z-index: 7; left: 50%; transform: translateX(-50%);
  top: 2.5%; width: 28.4%;
  height: calc((var(--phone-w) - var(--bezel) * 2) * .083);
  border-radius: var(--radius-pill); background: #000;
}

/* Блик по стеклу — деталь, из-за которой экран читается как стекло, а не как
   картинка. Узкая диагональная полоса, а не «глянцевая» подложка на весь экран. */
.hero-phone-glass {
  position: absolute; inset: 0; z-index: 6; pointer-events: none;
  border-radius: var(--screen-r);
  background: linear-gradient(128deg, rgba(255,255,255,.11) 0%, rgba(255,255,255,.03) 16%, transparent 40%);
}

/* ---- лента роликов: вертикальная «карусель-барабан» ----
   Соседние ролики не просто уезжают вбок, а ложатся на цилиндр: тот, что снизу,
   отклоняется дальним краем вглубь (rotateX отрицательный), тот, что сверху, —
   наоборот. Это и есть жест перелистывания рилса, только показанный в объёме.
   Активный кадр всегда плоский и без трансформа — текст на видео не мылится. */
.hero-reels { position: absolute; inset: 0; perspective: 900px; transform-style: preserve-3d; }
.hero-reel {
  position: absolute; inset: 0; overflow: hidden; backface-visibility: hidden;
  transform: translate3d(0, 105%, -430px) rotateX(-38deg); opacity: 0; z-index: 1;
  transition: transform 640ms var(--ease-out), opacity 480ms var(--ease-out);
}
.hero-reel video { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Гасим встроенные элементы плеера. Их два разных случая, и оба портят кадр:
   в Chromium при незагруженном видео поверх постера рисуется кольцо-крутилка
   (видно, если у пользователя отключён JS), в iOS Safari при запрете
   автовоспроизведения — крупная кнопка «play» по центру. */
.hero-reel video::-webkit-media-controls,
.hero-reel video::-webkit-media-controls-enclosure,
.hero-reel video::-webkit-media-controls-overlay-play-button,
.hero-reel video::-webkit-media-controls-start-playback-button {
  display: none !important; -webkit-appearance: none;
}
.hero-reel[data-pos="0"]  { transform: none; opacity: 1; z-index: 4; }
.hero-reel[data-pos="1"]  { transform: translate3d(0, 57%, -195px) rotateX(-25deg); opacity: .55; z-index: 3; }
.hero-reel[data-pos="-1"] { transform: translate3d(0, -57%, -195px) rotateX(25deg); opacity: .3; z-index: 2; }
.hero-reel[data-pos="2"]  { transform: translate3d(0, 105%, -430px) rotateX(-38deg); opacity: 0; z-index: 1; }
/* Отдельное состояние «ушёл вверх за край». По кольцу из четырёх роликов слот
   вне экрана один, поэтому уходящий вверх и приходящий снизу — это один и тот
   же индекс. Без -2 уходящий кадр полетел бы ВНИЗ через весь экран. JS ставит
   -2 на время анимации, а потом бесшумно (обе позиции с opacity: 0)
   переставляет в 2. */
.hero-reel[data-pos="-2"] { transform: translate3d(0, -105%, -430px) rotateX(38deg); opacity: 0; z-index: 1; }
/* Затемнение соседних кадров: одной прозрачности мало — на светлом видео сосед
   всё равно спорит с активным за внимание. */
.hero-reel::after {
  content: ""; position: absolute; inset: 0; background: var(--bg);
  opacity: .45; transition: opacity 480ms var(--ease-out); pointer-events: none;
}
.hero-reel[data-pos="0"]::after { opacity: 0; }

/* ---- рейка реакций справа, как в ленте короткого видео ----
   Иконки нарисованы вручную одним контуром и ОДНОЙ толщиной штриха (1.6):
   разнобой в толщине — первое, по чему набор иконок читается как собранный из
   разных мест. Юникод-глифы и эмодзи вместо иконок здесь не годятся: они
   приносят чужую метрику и чужой цвет.
   Тень под иконками обязательна: ролики бывают и светлые, и тёмные, белое по
   белому без неё пропадает. */
.hero-reel-rail {
  position: absolute; z-index: 5; right: var(--sp-2); bottom: 22%;
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-4);
  pointer-events: none;
}
.hero-reel-act {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  color: #fff; font-size: var(--fs-11); font-weight: 600;
  font-variant-numeric: tabular-nums; white-space: nowrap;
  text-shadow: 0 1px 5px rgba(0,0,0,.7);
}
.hero-reel-act svg {
  width: 25px; height: 25px; display: block;
  filter: drop-shadow(0 1px 4px rgba(0,0,0,.65));
}

/* ---- подпись поверх активного ролика ---- */
.hero-phone-scrim {
  position: absolute; z-index: 5; left: 0; right: 0; bottom: 0;
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-8) var(--sp-4) var(--sp-6);
  /* Подложка под подписью держится мягкой намеренно: при плотном градиенте
     нижние 20 % экрана превращались в чёрную полосу, и казалось, что видео там
     кончилось. Читаемость подписи обеспечивает не заливка, а тень на тексте. */
  background: linear-gradient(180deg, transparent, rgba(11,13,20,.30) 42%, rgba(11,13,20,.74));
  pointer-events: none;
}
.hero-phone-handle { font-size: var(--fs-13); font-weight: 600; color: #fff; text-shadow: 0 1px 6px rgba(0,0,0,.75); }
.hero-phone-src { color: rgba(255,255,255,.62); font-weight: 400; }
/* Пилюля «идёт учёт» — на ней же живёт вспышка в момент начисления: она и
   связывает ролик в аппарате с суммой в ленте справа. */
.hero-phone-pill {
  align-self: flex-start; display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3); border-radius: var(--radius-pill);
  background: rgba(11,13,20,.72); border: 1px solid var(--mint-a35);
  font-size: var(--fs-12); color: var(--text); font-variant-numeric: tabular-nums;
}
/* Вспышка в момент начисления — @keyframes, а не transition: снимать класс с
   transition пришлось бы «без анимации назад», это лишняя возня с рефлоу. */
.hero-phone-pill.is-hit { animation: hero-hit 560ms var(--ease-out); }
@keyframes hero-hit {
  from { box-shadow: 0 0 0 0 rgba(74,222,157,.5); border-color: var(--mint); }
  to   { box-shadow: 0 0 0 9px rgba(74,222,157,0); border-color: var(--mint-a35); }
}
.hero-phone-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--mint); flex-shrink: 0; animation: hero-live 2.4s var(--ease-in-out) infinite; }
@keyframes hero-live { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* Индикатор «домой» — та самая полоска внизу экрана iPhone. Мелочь, но именно
   такие мелочи отличают снимок аппарата от скруглённого прямоугольника. */
.hero-phone-home {
  position: absolute; z-index: 7; left: 50%; bottom: 1%; transform: translateX(-50%);
  width: 34%; height: 4px; border-radius: var(--radius-pill); background: rgba(255,255,255,.55);
}

/* ============================ лента начислений ============================ */
.hero-ledger {
  width: 100%; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-md); overflow: hidden;
}
/* Отступы шапки идут в ногу с отступами строк ленты: на узком экране строки
   ужимаются до --sp-4, и шапка обязана ужаться вместе с ними, иначе сумма
   стоит левее чисел под ней. */
.hero-ledger-head { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-6) var(--sp-4); }
/* На узком экране заголовок ролика занимает две-три строки, и бейдж «живой
   счёт», прижатый к его первой строке, повисает в воздухе. Разворачиваем в
   колонку так, чтобы бейдж встал НАД заголовком (column-reverse — порядок в
   разметке остаётся логичным: сначала о чём цифра, потом пометка). */
.hero-ledger-top { display: flex; flex-direction: column-reverse; align-items: flex-start; gap: var(--sp-2); }
.hero-ledger-label { font-size: var(--fs-13); color: var(--text-2); }
.hero-ledger-title { color: var(--text); }
.hero-live { display: inline-flex; align-items: center; gap: var(--sp-2); font-family: var(--font-mono); font-size: var(--fs-11); text-transform: uppercase; letter-spacing: .05em; color: var(--text-3); white-space: nowrap; }
.hero-ledger-sum { font-family: var(--font-display); font-weight: 200; font-size: clamp(2.2rem, 4.4vw, 3rem); line-height: 1; color: var(--mint); font-variant-numeric: tabular-nums; }
.hero-ledger-sub { font-size: var(--fs-13); color: var(--text-3); font-variant-numeric: tabular-nums; }

.hero-tape { position: relative; overflow: hidden; }
.hero-tape-list { display: flex; flex-direction: column; }
/* Ширины колонок ФИКСИРОВАННЫЕ, а не auto: каждая строка ленты — отдельный
   грид (иначе её нельзя двигать целиком при вставке новой), поэтому auto
   считался бы по содержимому КАЖДОЙ строки отдельно и колонки разъезжались бы
   между строками и шапкой. */
/* Колонок РОВНО столько, сколько видимых ячеек на этой ширине. «Время» и
   «Ролик» ниже 560/900px скрыты, и когда сетка была из двух колонок на три
   видимые ячейки, «Начислено» переносилось на вторую строку — колонки
   выглядели разъехавшимися по высоте. */
.hero-tape-row {
  display: grid; grid-template-columns: 1fr 58px 72px; gap: var(--sp-1) var(--sp-3); align-items: center;
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--border);
  font-size: var(--fs-13); white-space: nowrap; opacity: 1;
  transition: opacity 380ms var(--ease-out);
}
.hero-tape-row.is-entering { opacity: 0; }
.hero-tape-time { display: none; font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-3); font-variant-numeric: tabular-nums; }
.hero-tape-who { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--text-3); }
/* Площадку на узком экране прячем целиком, а не обрезаем: «YouTube Sh…» —
   это не сокращение, а мусор. Ник несёт больше смысла и помещается. */
.hero-tape-src { display: none; }
/* Строка того ролика, который сейчас в аппарате, — светлее остальных.
   Ярче ничем не выделяем: акцентная полоса слева превратила бы ленту в плашки. */
.hero-tape-row.is-live .hero-tape-who { color: var(--text); }
.hero-tape-what { display: none; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--text-3); }
.hero-tape-views { color: var(--text-3); font-variant-numeric: tabular-nums; text-align: right; }
.hero-tape-sum { color: var(--mint); font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }
/* Шапка колонок стоит СНАРУЖИ .hero-tape: лента при новом начислении едет
   вверх целиком, и шапка внутри уехала бы вместе с ней. */
.hero-tape-head { border-top: 1px solid var(--border); font-family: var(--font-mono); font-size: var(--fs-11); text-transform: uppercase; letter-spacing: .05em; color: var(--text-3); padding-block: var(--sp-2); }
.hero-tape-head span { color: inherit; }
/* letter-spacing добавляет пробел и ПОСЛЕ последней буквы — у выключенной
   вправо колонки заголовок из-за этого стоял на 0,05em правее своих чисел. */
.hero-tape-head .hero-tape-sum { margin-right: -.05em; }

/* Подпись под лентой: объясняет, что показано, и честно помечает цифры как
   демонстрационные. Заодно уравнивает правую колонку с высотой аппарата. */
.hero-stage-note { margin: var(--sp-4) 0 0; font-size: var(--fs-13); color: var(--text-3); max-width: 62ch; }

/* Страховка на случай, где overflow-x: clip не поддержан (Safari ≤ 15): там
   декларация просто отбрасывается, и широкий ореол снова растянул бы страницу.
   На узком экране ужимаем его так, чтобы он помещался и без обрезки. */
@media (max-width: 560px) {
  .hero-phone::before { width: 128%; }
}

@media (min-width: 560px) {
  .hero-tape-row { grid-template-columns: 78px 1fr 78px 84px; gap: var(--sp-4); padding-inline: var(--sp-6); }
  .hero-tape-time { display: block; }
  .hero-tape-src { display: inline; }
  .hero-ledger-head { padding-inline: var(--sp-6); }
  .hero-ledger-top { flex-direction: row; align-items: baseline; justify-content: space-between; gap: var(--sp-4); }
}
@media (min-width: 900px) {
  .hero-stage { grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-12); align-items: center; justify-items: stretch; }
  .hero-tape-row { grid-template-columns: 78px 1.1fr 1.4fr 78px 84px; }
  .hero-tape-what { display: block; }
}
@media (min-width: 1025px) {
  .hero-phone { --phone-w: 288px; }
  .hero-stage { gap: var(--sp-16); }
}

/* Пока не сработал JS, лента и аппарат не должны выглядеть пустыми: разметка
   уже содержит первый ролик и пять строк начислений, JS только оживляет их. */
.hero-stage[data-stage] .hero-reel[data-pos] { will-change: transform, opacity; }

/* ============================================================================
   11. СТАТС-БОРД И .stat-tile (KPI)
   ============================================================================ */
.stats-strip { padding-top: var(--sp-8); }
.stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; }
.stat-card { background: var(--surface); padding: var(--sp-8) var(--sp-6); display: flex; flex-direction: column; gap: var(--sp-2); }
.stat-num { font-family: var(--font-display); font-weight: 200; font-size: clamp(2.4rem, 5vw, 3.6rem); color: var(--text); line-height: 1; font-variant-numeric: tabular-nums; white-space: nowrap; }
.stat-unit { font-size: .5em; font-weight: 500; color: var(--mint); margin-left: 2px; }
.stat-label { font-size: var(--fs-14); color: var(--text-3); max-width: 22ch; }
@media (min-width: 640px) { .stats-grid { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 1025px) { .stats-strip { padding-top: var(--sp-16); } }

/* ---- .stat-tile: контрактный компонент KPI-плитки для дашбордов ---- */
.stat-tile { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--sp-4); }
.stat-tile .stat-num { font-family: var(--font-display); font-weight: 300; font-size: clamp(1.8rem, 3vw, 2.6rem); color: var(--text); font-variant-numeric: tabular-nums; }
.stat-tile .stat-label { font-size: var(--fs-14); color: var(--text-3); margin-top: var(--sp-1); max-width: none; }
.stat-tile.link { display: block; transition: border-color .2s ease, box-shadow .2s ease; }
.stat-tile.link:hover { border-color: rgba(124,92,255,.5); box-shadow: 0 0 24px rgba(124,92,255,.16); text-decoration: none; }
.stat-tile .stat-flag { margin-top: var(--sp-2); }

/* ============================================================================
   12. PIPELINE (glow-диаграмма стадий, маркетинговый компонент процесса)
   ============================================================================ */
.pipeline { position: relative; display: flex; flex-direction: column; gap: var(--sp-6); margin-top: var(--sp-12); padding-left: var(--sp-4); }
.pipeline-track {
  position: absolute; left: 4px; top: 6px; bottom: 6px; width: 2px;
  background: linear-gradient(180deg, var(--violet), var(--blue), var(--mint));
  filter: blur(1px); opacity: .8;
}
.pipeline-stage { position: relative; display: flex; flex-direction: column; gap: var(--sp-2); }
.pipeline-stage::before {
  content: ""; position: absolute; left: -20px; top: 6px; width: 10px; height: 10px; border-radius: 50%;
  background: var(--blue); box-shadow: 0 0 12px 3px rgba(59,130,246,.6); transform: translateX(-1px);
}
.pipeline-chip {
  display: inline-flex; align-self: flex-start; font-family: var(--font-mono); font-size: var(--fs-13); font-weight: 500;
  color: var(--text); background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-pill); padding: var(--sp-1) var(--sp-3);
}
.pipeline-chip-final { background: var(--grad); border-color: transparent; color: #fff; }
.pipeline-stage p { margin: 0; max-width: 46ch; font-size: var(--fs-16); color: var(--text-2); }

@media (min-width: 1025px) {
  .pipeline { flex-direction: row; align-items: flex-start; gap: 0; padding-left: 0; }
  .pipeline-track { left: 0; right: 0; top: 6px; bottom: auto; height: 2px; width: auto; background: linear-gradient(90deg, var(--violet), var(--blue), var(--mint)); }
  .pipeline-stage { flex: 1; padding: 0 var(--sp-4); }
  .pipeline-stage:first-child { padding-left: 0; }
  .pipeline-stage:last-child { padding-right: 0; }
  .pipeline-stage::before { left: 0; top: 2px; transform: none; }
}

/* ============================================================================
   13. BENTO-СЕТКА
   ============================================================================ */
.bento { display: grid; grid-template-columns: 1fr; gap: var(--sp-4); margin-top: var(--sp-8); }
.bento-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--sp-6); transition: border-color .2s ease, box-shadow .2s ease; }
.bento-card:hover { border-color: rgba(124,92,255,.5); box-shadow: 0 0 24px rgba(124,92,255,.16); }
.bento-icon { width: 40px; height: 40px; border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; color: var(--text); background: linear-gradient(135deg, var(--violet-a35), var(--blue-a12)); margin-bottom: var(--sp-4); }
.bento-icon svg { width: 20px; height: 20px; }
.bento-card h3 { margin-bottom: var(--sp-2); }
.bento-card p { margin: 0; }
.bento-card p strong { color: var(--text); font-family: var(--font-display); }
.bento-check { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.bento-check li { position: relative; padding-left: var(--sp-6); font-size: var(--fs-14); color: var(--text-2); }
.bento-check li::before { content: ""; position: absolute; left: 0; top: 6px; width: 12px; height: 12px; border-radius: 4px; background: var(--mint-a14); border: 1px solid var(--mint); }
.bento-check-dots li::before { display: none; }
/* Запреты — тем же списком, но маркер красный: зелёная галочка на строке
   «Запрещённые фразы» означала ровно обратное тому, что написано. */
.bento-ban li::before { background: var(--red-a14); border-color: var(--red); }
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: var(--sp-2); vertical-align: middle; }
.dot-mint { background: var(--mint); }
.dot-violet { background: var(--violet); }
.bento-chat { margin-top: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-2); }
.chat-msg { display: flex; gap: var(--sp-2); align-items: flex-start; }
.chat-avatar { flex-shrink: 0; width: 26px; height: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text); background: var(--surface-3); }
.chat-avatar-admin { background: var(--grad); }
.chat-msg p { margin: 0; font-size: var(--fs-13); color: var(--text); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-xs); padding: var(--sp-2) var(--sp-3); }
.chat-msg-admin p { background: var(--violet-a14); border-color: rgba(124,92,255,.3); }

@media (min-width: 768px) { .bento { grid-template-columns: repeat(2, 1fr); } .bento-big, .bento-wide { grid-column: span 2; } }
@media (min-width: 1025px) {
  .bento { grid-template-columns: repeat(4, 1fr); grid-auto-rows: minmax(120px, auto); }
  .bento-big { grid-column: 1 / 3; grid-row: 1 / 3; }
  .bento-wide { grid-column: 1 / 5; }
}

/* ============================================================================
   14. SPLIT 60/40
   .split — маркетинговый асимметричный блок «Роли» (bias через flex);
   .split-60-40 — утилитарная сетка 60/40 для контент-страниц (например
   форма + пояснение, таблица + карточка сводки).
   ============================================================================ */
.split { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); margin-top: var(--sp-8); }
.split-col { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--sp-8); display: flex; flex-direction: column; gap: var(--sp-3); }
.split-col-biz { border-color: rgba(124,92,255,.28); }
.split-col-creator { border-color: rgba(74,222,157,.24); }
.split-col .eyebrow { margin-bottom: 0; }
.split-col h3 { margin-bottom: var(--sp-1); }
.split-col .btn { align-self: flex-start; margin-top: var(--sp-1); }
.mini-table { display: flex; flex-direction: column; border-top: 1px solid var(--border); }
.mini-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-3) 0; border-bottom: 1px solid var(--border); font-size: var(--fs-14); }
.mini-row span { color: var(--text-2); }
.mini-row strong { color: var(--text); font-family: var(--font-mono); font-weight: 500; white-space: nowrap; }
.earn-card { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-3); }
.earn-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.earn-label { font-size: var(--fs-13); color: var(--text-3); }
.earn-val { font-family: var(--font-display); font-weight: 700; color: var(--amber); font-size: var(--fs-16); white-space: nowrap; }
.earn-val-big { color: var(--mint); font-size: var(--fs-21); }

@media (min-width: 768px) { .split { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1025px) { .split { display: flex; align-items: stretch; } .split-col-biz { flex: 1.4; } .split-col-creator { flex: 1; } }

/* align-items НЕ задаём (остаётся default: stretch) — сюда часто кладут
   position:sticky карточку во второй колонке (офер задания и т.п.). Со
   stretch грид-ячейка растягивается на высоту строки и у sticky-потомка
   есть где "ехать"; с start/flex-start ячейка сжимается по контенту, и
   sticky фактически перестаёт работать (см. QA-итерация 3, п.1б). Если
   нужно визуально не растягивать саму карточку — оборачивай её в прозрачный
   <aside>/<div> без фона: он растянется незаметно, а видимая карточка внутри
   останется естественной высоты (см. task.html, biz-task-form.html). */
.split-60-40 { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); }
@media (min-width: 1025px) { .split-60-40 { grid-template-columns: 3fr 2fr; } }

/* ============================================================================
   15. ФОРМЫ
   ============================================================================ */
.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-4) var(--sp-4); }
.form-grid .field.full { grid-column: 1 / -1; }
@media (max-width: 720px) { .form-grid { grid-template-columns: 1fr; } }

.field { display: flex; flex-direction: column; gap: var(--sp-1); margin-bottom: var(--sp-4); }
.form-grid .field { margin-bottom: 0; }
.field label { font-weight: 600; font-size: var(--fs-14); color: var(--text); }
.hint { font-size: var(--fs-13); color: var(--text-3); }
.field .error-msg { font-size: var(--fs-13); color: var(--red); }
.field.has-error input, .field.has-error select, .field.has-error textarea { border-color: var(--red); }
.field.has-error input:focus, .field.has-error select:focus, .field.has-error textarea:focus { box-shadow: 0 0 0 4px var(--red-a14); }

input[type=text], input[type=email], input[type=password], input[type=tel],
input[type=number], input[type=date], input[type=url], input[type=search],
select, textarea {
  width: 100%; padding: var(--sp-3) var(--sp-3); border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  font-family: inherit; font-size: var(--fs-16); color: var(--text); background: var(--surface-3); min-height: 48px;
}
textarea { min-height: 104px; resize: vertical; }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--violet); box-shadow: 0 0 0 4px var(--violet-a14); }
input::placeholder, textarea::placeholder { color: var(--text-3); }
input[disabled], select[disabled], textarea[disabled] { opacity: .5; cursor: not-allowed; }
select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239AA3B8' stroke-width='2'><path d='M6 9l6 6 6-6'/></svg>");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 16px; padding-right: var(--sp-8);
}

/* flex-direction: row задан ЯВНО, а не по умолчанию. Эти же классы вешают на
   .field, а тот выкладывает содержимое колонкой — и квадратик оказывался НАД
   своим текстом, левее его на всю ширину ячейки. Рядом с соседним полем это
   читалось так, будто галочка принадлежит ему, а не своей подписи. Свойство
   не наследуется от .field только потому, что задано здесь. */
.checkbox-row, .radio-row { display: flex; flex-direction: row; align-items: flex-start; gap: var(--sp-2); font-size: var(--fs-14); color: var(--text-2); }
/* В сетке формы ячейка с галочкой прижимается к низу: у соседей снизу стоит
   поле ввода, и без выравнивания галочка висела на уровне их ПОДПИСЕЙ — то
   есть на строку выше, чем всё, с чем её сравнивают глазами. */
.form-grid .field.checkbox-row, .form-grid .field.radio-row { align-self: end; padding-bottom: var(--sp-3); }
.checkbox-row input, .radio-row input { width: 20px; height: 20px; margin-top: 2px; flex-shrink: 0; accent-color: var(--violet); }
.radio-group { display: flex; flex-direction: column; gap: var(--sp-2); }

.radio-cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); }
@media (max-width: 560px) { .radio-cards { grid-template-columns: 1fr; } }
.radio-card { border: 1.5px solid var(--border-strong); border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4); cursor: pointer; display: flex; gap: var(--sp-2); align-items: flex-start; background: var(--surface); }
.radio-card input { margin-top: var(--sp-1); accent-color: var(--violet); }
.radio-card:has(input:checked) { border-color: var(--violet); background: var(--violet-a14); }
.radio-card strong { display: block; font-size: var(--fs-16); color: var(--text); }
.radio-card span.desc { font-size: var(--fs-13); color: var(--text-3); }

.tag-select { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.tag-select label { display: inline-flex; align-items: center; gap: var(--sp-1); padding: var(--sp-2) var(--sp-3); border: 1.5px solid var(--border-strong); border-radius: var(--radius-pill); font-size: var(--fs-14); font-weight: 600; color: var(--text-2); cursor: pointer; min-height: 44px; }
.tag-select input { accent-color: var(--violet); }
.tag-select label:has(input:checked) { border-color: var(--violet); background: var(--violet-a14); color: var(--text); }

/* display:block обязателен — .file-drop навешивается на <label>, у которого
   display:inline по умолчанию; без явного block padding-бокс не образует
   собственной высоты по вертикали и наезжает на предыдущий блок (~12px
   наложение на карточку выше, обрезанный плейсхолдер "Перетащите" → "щите"
   на 1280/390px — QA-итерация 4, п.1). */
.file-drop { display: block; border: 2px dashed var(--border-strong); border-radius: var(--radius-sm); padding: var(--sp-6) var(--sp-4); text-align: center; color: var(--text-3); cursor: pointer; background: var(--surface-2); }
.file-drop.dragover { border-color: var(--violet); background: var(--violet-a14); }
.file-drop input[type=file] { display: none; }
.file-preview { margin-top: var(--sp-2); display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.file-preview .chip { display: flex; align-items: center; gap: var(--sp-1); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-pill); padding: var(--sp-1) var(--sp-3); font-size: var(--fs-13); color: var(--text-2); }

.repeat-row { display: flex; gap: var(--sp-2); align-items: flex-start; margin-bottom: var(--sp-2); }
.repeat-row > * { flex: 1; }
.repeat-row .btn { flex: 0 0 auto; margin-top: 0; }

.form-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--sp-6); }
@media (max-width: 560px) { .form-card { padding: var(--sp-4); } }
.form-narrow { max-width: 480px; margin: 0 auto; }
.form-actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-6); }

/* ---- Звёздный рейтинг (ввод), шаг 0,5 ----
   Каждая звезда собрана из двух половинок-меток. Порядок в разметке обратный
   (5 → 1) плюс row-reverse: тогда «input:checked ~ label» красит всё, что
   стоит левее выбранного, и половинки работают без единой строчки JS.
   Метка-половинка узкая, но по вертикали держит 44px — палец на телефоне
   попадает в цель, даже когда цель шириной в полсимвола. */
.star-input {
  display: inline-flex; flex-direction: row-reverse; justify-content: flex-end;
  font-size: 34px; line-height: 1; border: 0; padding: 0; margin: 0;
}
.star-input input { position: absolute; opacity: 0; pointer-events: none; }
.star-input label {
  /* font-size обязателен: глобальное правило для label задаёт свои 14px, и
     ширина в .5em считалась бы от них — половинка выходила семипиксельной. */
  font-size: inherit;
  cursor: pointer; color: var(--border-strong);
  width: .5em; height: 44px; position: relative; overflow: hidden;
  transition: color .12s ease;
}
.star-input label::before {
  content: "★"; position: absolute; top: 50%; transform: translateY(-50%);
  line-height: 1;
}
.star-input .star-left::before { left: 0; }
.star-input .star-right::before { left: -.5em; }
.star-input label:hover, .star-input label:hover ~ label,
.star-input input:checked ~ label { color: var(--amber); }
.star-input input:focus-visible + label { outline: 2px solid var(--violet); outline-offset: 2px; }

.stars { color: var(--amber); font-size: var(--fs-14); letter-spacing: 1px; white-space: nowrap; }

/* ---- Оценка в шапке профиля ---- */
.rating-value { font-weight: 700; color: var(--text); white-space: nowrap; }
.rating-count { font-weight: 400; color: var(--text-3); }
.rating-empty { color: var(--text-3); }

/* ---- Stepper (горизонтальный степпер этапов формы) ---- */
.stepper { display: flex; gap: 0; margin-bottom: var(--sp-6); flex-wrap: wrap; }
.stepper .step { flex: 1; min-width: 120px; text-align: center; position: relative; padding-top: var(--sp-8); font-size: var(--fs-13); color: var(--text-3); font-weight: 600; }
.stepper .step::before { content: ""; position: absolute; top: 10px; left: 50%; transform: translateX(-50%); width: 22px; height: 22px; border-radius: 50%; background: var(--surface-3); border: 2px solid var(--border-strong); }
.stepper .step::after { content: ""; position: absolute; top: 20px; left: calc(50% + 14px); right: calc(-50% + 14px); height: 2px; background: var(--border-strong); }
.stepper .step:last-child::after { display: none; }
.stepper .step.done { color: var(--mint); }
.stepper .step.done::before { background: var(--mint); border-color: var(--mint); }
.stepper .step.done::after { background: var(--mint); }
.stepper .step.current { color: var(--text); }
.stepper .step.current::before { background: var(--violet); border-color: var(--violet); box-shadow: 0 0 0 4px var(--violet-a14); }

@media (max-width: 760px) {
  .stepper { flex-direction: column; flex-wrap: nowrap; gap: 0; }
  .stepper .step { flex: none; min-width: 0; width: 100%; text-align: left; padding: var(--sp-1) 0 var(--sp-1) var(--sp-12); min-height: 44px; display: flex; align-items: center; }
  .stepper .step::before { top: 6px; left: 11px; transform: none; }
  .stepper .step::after { top: 28px; left: 21px; right: auto; width: 2px; height: calc(100% - 6px); background: var(--border-strong); }
  .stepper .step.done::after { background: var(--mint); }
}

/* ============================================================================
   16. КАРТОЧКИ И СЕТКИ ДАННЫХ
   ============================================================================ */
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.card-pad { padding: var(--sp-6); }
.grid { display: grid; gap: var(--sp-4); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-6 { grid-template-columns: repeat(6, 1fr); }
@media (max-width: 1023px) { .grid-4 { grid-template-columns: repeat(2, 1fr); } .grid-6 { grid-template-columns: repeat(3, 1fr); } .grid-3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .grid-2, .grid-3, .grid-4, .grid-6 { grid-template-columns: 1fr; } }
@media (min-width: 641px) {
  .grid-2-task { grid-template-columns: 1.6fr .9fr; }
  .grid-2-finance { grid-template-columns: .9fr 1.2fr; }
  .grid-2-dashboard { grid-template-columns: 1.3fr .9fr; }
}

.kv-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.kv-list .kv { display: flex; justify-content: space-between; gap: var(--sp-3); font-size: var(--fs-14); padding-bottom: var(--sp-2); border-bottom: 1px dashed var(--border); }
.kv-list .kv span:first-child { color: var(--text-3); }
.kv-list .kv span:last-child { font-weight: 600; color: var(--text); font-family: var(--font-mono); }

/* ============================================================================
   17. ЗАДАНИЯ (task-card, каталог, фильтры)
   ============================================================================ */
.task-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-3); transition: border-color .2s ease, box-shadow .2s ease, transform .15s ease; }
.task-card:hover { border-color: rgba(124,92,255,.5); box-shadow: 0 0 24px rgba(124,92,255,.16); transform: translateY(-2px); }
.task-card-top { display: flex; justify-content: space-between; gap: var(--sp-2); align-items: flex-start; }
.task-card h3 { font-size: var(--fs-16); margin-bottom: var(--sp-1); }
.task-card h3 a { color: var(--text); }
.task-brand { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-14); color: var(--text-3); }
.task-badges { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.task-stats { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2) var(--sp-3); font-size: var(--fs-14); padding: var(--sp-3) 0; border-top: 1px dashed var(--border); border-bottom: 1px dashed var(--border); }
.task-stats .stat-label { color: var(--text-3); display: block; font-size: var(--fs-12); max-width: none; }
.task-stats .stat-val { font-weight: 600; color: var(--text); font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.task-card-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.slots { font-size: var(--fs-13); color: var(--text-3); }
.deadline-timer { display: inline-flex; align-items: center; gap: var(--sp-1); font-weight: 600; font-size: var(--fs-14); color: var(--text); font-family: var(--font-mono); }
.deadline-timer.urgent { color: var(--red); }

/* align-items не задаём — см. пояснение у .split-60-40 (раздел 14): default
   stretch нужен, чтобы у sticky .filters было пространство для позиционирования. */
.catalog-layout { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); }
@media (min-width: 901px) { .catalog-layout { grid-template-columns: 260px 1fr; } }
.filters { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--sp-4); align-self: start; }
/* QA: max-height:calc(100vh-120px) + overflow-y:auto (прежний вариант) на
   1024/1280px обрезали панель посередине поля «Мин. ставка» — кнопка «Показать
   задания» уезжала во внутренний, ничем не обозначенный скролл и была
   недоступна (P0, каталог — главный вход креатора). align-self:start вместо
   default stretch убирает саму причину, по которой высоту вообще приходилось
   ограничивать: панель больше не тянется на всю высоту строки (вровень со
   списком карточек), а просто занимает высоту своего содержимого — расти
   дальше некуда, обрезать нечего. position:sticky на align-self:start-элементе
   не имеет большого запаса хода (containing block сжимается до содержимого),
   поэтому панель ведёт себя скорее как обычный блок в потоке, чем как
   классический sticky-сайдбар — это осознанный компромисс: гарантированная
   видимость кнопки важнее визуального "прилипания" панели при скролле списка. */
@media (min-width: 901px) { .filters { position: sticky; top: 88px; } }
.filters .field { margin-bottom: var(--sp-4); }
.filters h3 { font-size: var(--fs-16); margin-bottom: var(--sp-3); }

/* ============================================================================
   18. БЕЙДЖИ И ПЛОЩАДКИ
   ============================================================================ */
.badge { display: inline-flex; align-items: center; gap: var(--sp-1); padding: var(--sp-1) var(--sp-3); border-radius: var(--radius-pill); font-size: var(--fs-12); font-weight: 700; white-space: nowrap; line-height: 1.3; }
.badge-blue   { background: var(--blue-a12); color: var(--badge-blue-fg); }
.badge-orange { background: var(--amber-a14); color: var(--amber); }
.badge-gray   { background: var(--surface-3); color: var(--text-2); }
.badge-green  { background: var(--mint-a14); color: var(--mint); }
.badge-yellow { background: var(--amber-a14); color: var(--amber); }
.badge-red    { background: var(--red-a14); color: var(--red); }
.badge-outline{ background: transparent; border: 1px solid var(--border-strong); color: var(--text-2); }

.platform-badge { display: inline-flex; align-items: center; gap: var(--sp-1); font-size: var(--fs-13); font-weight: 600; color: var(--text-2); }
.platform-badge svg { width: 16px; height: 16px; flex-shrink: 0; }

.platform-strip { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin: var(--sp-4) 0 var(--sp-1); }
.platform-chip { display: inline-flex; align-items: center; gap: var(--sp-2); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-pill); padding: var(--sp-2) var(--sp-4); font-weight: 500; color: var(--text); font-size: var(--fs-14); min-height: 36px; }
.platform-chip svg { width: 20px; height: 20px; }

/* ============================================================================
   19. METER / PROGRESS / SPARKLINE
   ============================================================================ */
.meter { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-4); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.meter-ring { --pct: 75; --zc: var(--mint); width: 56px; height: 56px; border-radius: 50%; flex-shrink: 0; position: relative; background: conic-gradient(var(--zc) calc(var(--pct)*1%), var(--surface-3) 0); }
.meter-ring::after { content: ""; position: absolute; inset: 6px; border-radius: 50%; background: var(--surface); }
.meter-body { min-width: 0; }
.meter-value { font-weight: 700; font-size: var(--fs-18); color: var(--text); font-family: var(--font-mono); }
.meter-name { font-size: var(--fs-13); color: var(--text-3); margin-bottom: 2px; }
.meter-zone { display: inline-block; margin: var(--sp-1) 0 2px; }
.meter-explain { font-size: var(--fs-13); color: var(--text-2); margin: 0; }
.meter.zone-green .meter-ring { --zc: var(--mint); }
.meter.zone-yellow .meter-ring { --zc: var(--amber); }
.meter.zone-red .meter-ring { --zc: var(--red); }
.meter-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); }
@media (max-width: 900px) { .meter-list { grid-template-columns: 1fr; } }

.progress { height: 10px; border-radius: var(--radius-pill); background: var(--surface-3); overflow: hidden; }
.progress > span { display: block; height: 100%; background: var(--blue); border-radius: var(--radius-pill); }
.progress.warn > span { background: var(--amber); }
.progress.danger > span { background: var(--red); }

.sparkline { width: 100%; height: 56px; display: block; }
.sparkline path.fill { fill: var(--violet-a14); }
.sparkline path.line { fill: none; stroke: var(--violet); stroke-width: 2; }

/* ============================================================================
   20. ТАБЛИЦЫ
   .table-wrap — legacy-обёртка (совместимость с PHP); .data-table — новый
   плотный компонент кабинета: шапка на surface-2, зебра, ховер-строка,
   моно-цифры, карточное превращение через td[data-label] на узких экранах.
   ============================================================================ */
/* Подсказка прокрутки: градиентная маска у правых/левых краёв, видна ТОЛЬКО
   пока реально есть что доскроллить (классический приём с двумя слоями
   фона — "заливка" на background-attachment:local едет вместе с контентом
   и закрывает тень у того края, где скролла больше нет; "тень" на
   background-attachment:scroll стоит на месте). JS не нужен. Автоматически
   гасится в .table-cards/мобильном режиме через background:none в
   переопределяющих правилах ниже (сбрасывает весь background, включая
   background-image). QA-итерация 4, п.3: на 1280px колонка «Действия»
   обрезалась без намёка, что таблица скроллится внутри. */
.table-wrap {
  width: 100%; overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface);
  /* Подсказка прокрутки: тень у края появляется, только когда есть что
     прокручивать. Приём держится на том, что «шторка» цвета поверхности
     (background-attachment: local, едет вместе с содержимым) ПОЛНОСТЬЮ
     закрывает тень (attachment: scroll, стоит у края) в непрокрученном
     состоянии.
     Раньше шторка начинала таять с нулевой точки — от самого края, — и тень
     из-под неё просвечивала ВСЕГДА: у левого края каждой строки таблицы стояла
     серая полоса шириной ~14px, заметнее всего в первой колонке. Замер: 240 из
     255 на белой строке там, где обязано быть 255.
     Теперь шторка непрозрачна ровно на ширину тени (16px) и тает только после
     неё. */
  background-image:
    linear-gradient(90deg, var(--surface) 16px, transparent 32px),
    linear-gradient(270deg, var(--surface) 16px, transparent 32px),
    linear-gradient(90deg, rgba(0,0,0,.35), transparent 16px),
    linear-gradient(270deg, rgba(0,0,0,.35), transparent 16px);
  background-repeat: no-repeat;
  background-size: 32px 100%, 32px 100%, 16px 100%, 16px 100%;
  background-position: left, right, left, right;
  background-attachment: local, local, scroll, scroll;
}
.table-wrap table { width: 100%; border-collapse: collapse; min-width: 640px; }
.table-wrap th, .table-wrap td { padding: var(--sp-3) var(--sp-4); text-align: left; font-size: var(--fs-14); border-bottom: 1px solid var(--border); white-space: nowrap; color: var(--text-2); }
.table-wrap th { color: var(--text-3); font-weight: 700; font-size: var(--fs-12); text-transform: uppercase; letter-spacing: .03em; background: var(--surface-2); }
.table-wrap td { color: var(--text); }
.table-wrap td.wrap { white-space: normal; }
.table-wrap tr:last-child td { border-bottom: none; }
.table-wrap tr:hover td { background: var(--surface-3); }

/* .table-wrap БЕЗ .table-cards — таблица сохраняет min-width:640px и просто
   скроллится горизонтально внутри .table-wrap (overflow-x:auto) — это
   осознанное поведение legacy-паттерна. Раньше эта ширина могла распирать
   grid-колонку выше по дереву (страница обрезалась без видимого скролла);
   теперь это исключено правилом min-width:0 у потомков grid-контейнеров
   (раздел 03.1) — .table-wrap остаётся локально ограниченным родителем.
   .table-cards — таблица вместо горизонтального скролла превращается в
   карточки, здесь min-width:640px явно снимается на ≤760px (control-width
   390 входит в диапазон). */
@media (max-width: 760px) {
  .table-cards thead { display: none; }
  .table-cards table { display: block; width: 100%; min-width: 0; }
  .table-cards tbody, .table-cards tr, .table-cards td { display: block; width: 100%; }
  .table-cards .table-wrap { border: none; background: none; }
  .table-cards tr { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); margin-bottom: var(--sp-3); padding: var(--sp-3) var(--sp-3); }
  /* text-align:left — по умолчанию ячейка текстовая (название, статус-бейдж,
     платформа и т.п.); было right для ВСЕХ ячеек без разбора — заголовок/бейджи
     съезжали вправо-влево вперемешку ("съехавшие тексты"). Числовые ячейки
     (.num) держим по правому краю — тот же конвенция, что и в .data-table. */
  .table-cards td { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--sp-3); border: none; padding: var(--sp-2) 0; white-space: normal; text-align: left; }
  .table-cards td.num { text-align: right; }
  .table-cards td::before { content: attr(data-label); font-weight: 700; color: var(--text-3); font-size: var(--fs-12); text-transform: uppercase; text-align: left; }
}

/* ---- .data-table: новый компонент кабинета ---- */
/* Подсказка прокрутки — тот же приём, что и у .table-wrap (см. выше). */
.data-table-wrap {
  width: 100%; overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
  /* Подсказка прокрутки: тень у края появляется, только когда есть что
     прокручивать. Приём держится на том, что «шторка» цвета поверхности
     (background-attachment: local, едет вместе с содержимым) ПОЛНОСТЬЮ
     закрывает тень (attachment: scroll, стоит у края) в непрокрученном
     состоянии.
     Раньше шторка начинала таять с нулевой точки — от самого края, — и тень
     из-под неё просвечивала ВСЕГДА: у левого края каждой строки таблицы стояла
     серая полоса шириной ~14px, заметнее всего в первой колонке. Замер: 240 из
     255 на белой строке там, где обязано быть 255.
     Теперь шторка непрозрачна ровно на ширину тени (16px) и тает только после
     неё. */
  background-image:
    linear-gradient(90deg, var(--surface) 16px, transparent 32px),
    linear-gradient(270deg, var(--surface) 16px, transparent 32px),
    linear-gradient(90deg, rgba(0,0,0,.35), transparent 16px),
    linear-gradient(270deg, rgba(0,0,0,.35), transparent 16px);
  background-repeat: no-repeat;
  background-size: 32px 100%, 32px 100%, 16px 100%, 16px 100%;
  background-position: left, right, left, right;
  background-attachment: local, local, scroll, scroll;
}
.data-table { width: 100%; border-collapse: collapse; min-width: 640px; }
.data-table caption { caption-side: top; text-align: left; padding: var(--sp-3) var(--sp-4) 0; font-size: var(--fs-14); color: var(--text-3); }
.data-table thead th {
  background: var(--surface-2); color: var(--text-3); font-family: var(--font-mono);
  font-size: var(--fs-12); text-transform: uppercase; letter-spacing: .05em; font-weight: 500;
  text-align: left; padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border); white-space: nowrap;
}
.data-table tbody td { padding: var(--sp-3) var(--sp-4); font-size: var(--fs-14); color: var(--text-2); border-bottom: 1px solid var(--border); white-space: nowrap; }
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:nth-child(even) td { background: var(--overlay-faint); }
.data-table tbody tr:hover td { background: var(--surface-3); }
.data-table td.wrap, .data-table th.wrap { white-space: normal; }
.data-table td.num, .data-table th.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; text-align: right; color: var(--text); white-space: nowrap; }

/* QA: на business/tasks.php при 1280px 2-3 кнопки действий в один ряд
   (.btn-group, white-space:nowrap) требовали ~400-450px, из-за чего
   auto-layout таблицы отжимал колонку названия задания до одного слова в
   строке, а сама колонка действий обрезалась краем .data-table-wrap. На
   business/task-view.php колонка действий уезжала во внутренний скролл даже
   на 1440px и не была видна без его обнаружения. Три независимых правки: */
@media (min-width: 641px) {
  /* 1. Кнопки действий в ячейке — колонкой, а не в ряд: ширина колонки теперь
     ограничена шириной ОДНОЙ кнопки (~190px), а не суммой всех кнопок. */
  .data-table td .btn-group { flex-direction: column; align-items: stretch; }
  .data-table td .btn-group .btn { width: 100%; }
  /* 2. Освободившееся место auto-layout отдаёт другим колонкам — явный
     min-width гарантирует, что название задания получает достаточно места и
     переносится нормально (несколько слов в строке), а не по одному слову. */
  .data-table td.primary, .data-table .task-name-cell { min-width: 200px; }
  /* 3. Последняя колонка (обычно «Действия», иногда числовая) — липкая у
     правого края .data-table-wrap: остаётся видимой без обнаружения
     горизонтального скролла, независимо от того, сколько колонок левее. */
  .data-table th:last-child, .data-table td:last-child { position: sticky; right: 0; z-index: 1; }
  .data-table td:last-child { background: var(--surface); box-shadow: -8px 0 12px -6px rgba(0,0,0,.4); }
  .data-table thead th:last-child { background: var(--surface-2); box-shadow: -8px 0 12px -6px rgba(0,0,0,.4); }
}

/* Длинное название задания/компании/пользователя не должно распирать всю
   таблицу auto-layout на 2-3 ширины экрана (QA-итерация 5: 60+ символов без
   пробелов уводили колонку до ~1677px и остальные колонки за x≈2700 на
   1920, а на 390 — scrollWidth страницы до 1692px). Специфичность (0,2,2)
   выше базового .data-table tbody td{white-space:nowrap} (0,1,2) — переносится
   по словам/слогам в пределах разумного потолка ширины без !important. */
.data-table td.primary {
  color: var(--text); font-weight: 500;
  white-space: normal; overflow-wrap: break-word; max-width: 320px;
}

@media (max-width: 640px) {
  .data-table thead { display: none; }
  .data-table, .data-table tbody, .data-table tr, .data-table td { display: block; width: 100%; }
  .data-table-wrap { border: none; background: none; overflow: visible; }
  .data-table { min-width: 0; }
  .data-table tr { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); margin-bottom: var(--sp-3); padding: var(--sp-1) var(--sp-3); }
  /* white-space:normal — с !important, как соседние border/background в этом
     же правиле: без него проигрывал каскаду .data-table tbody td{nowrap}
     (0,1,2) > .data-table td (0,1,1) — перенос по словам на мобилке
     фактически не включался, длинные названия уводили страницу вбок без
     единого намёка на скролл (QA-итерация 5, п.1). */
  /* text-align:left по умолчанию (было right для всех ячеек без разбора —
     заголовок задания прижимался вправо, бейджи и текст съезжали хаотично).
     Правый край оставлен только для .num — ниже уже есть отдельное правило,
     оно теперь не дублирует базовое, а реально его переопределяет. */
  .data-table td { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--sp-3); border: none !important; padding: var(--sp-2) 0; white-space: normal !important; text-align: left; background: none !important; min-height: 44px; }
  .data-table td[data-label]::before { content: attr(data-label); font-weight: 600; color: var(--text-3); font-size: var(--fs-12); text-transform: uppercase; letter-spacing: .03em; text-align: left; font-family: var(--font-text); }
  .data-table td.num { justify-content: space-between; text-align: right; white-space: nowrap !important; }
}

/* ============================================================================
   21. TABS / ACCORDION
   ============================================================================ */
/* Подсказка прокрутки — тот же приём, что у .table-wrap/.data-table-wrap.
   Цвет "заливки" — --scroll-fade-bg (по умолчанию --bg, фон кабинета/страницы,
   где .tabs-nav обычно и живёт); если вкладки стоят на карточке (--surface),
   переопредели переменную инлайн-стилем на обёртке: style="--scroll-fade-bg:
   var(--surface)". QA-итерация 4, п.3: на biz-tasks/adm-moderation/profile-edit
   вкладки справа обрезались без намёка на скролл («Верификация» → «Верифика»). */
.tabs-nav {
  --scroll-fade-bg: var(--bg);
  display: flex; gap: var(--sp-1); border-bottom: 1px solid var(--border); overflow-x: auto; margin-bottom: var(--sp-6);
  /* Шторка непрозрачна на всю ширину тени — см. подробный разбор у .table-wrap.
     Здесь дефект был виден с ОБЕИХ сторон: липкой последней ячейки, как в
     таблице, у полосы вкладок нет, и правый край смазывало тоже. */
  background-image:
    linear-gradient(90deg, var(--scroll-fade-bg) 16px, transparent 32px),
    linear-gradient(270deg, var(--scroll-fade-bg) 16px, transparent 32px),
    linear-gradient(90deg, rgba(0,0,0,.35), transparent 16px),
    linear-gradient(270deg, rgba(0,0,0,.35), transparent 16px);
  background-repeat: no-repeat;
  background-size: 32px 100%, 32px 100%, 16px 100%, 16px 100%;
  background-position: left, right, left, right;
  background-attachment: local, local, scroll, scroll;
}
/* .tabs-nav a — вкладки-ссылки (чистая навигация без <button> внутри <a>,
   правка параллельного агента на admin/moderation.php, admin/settings.php,
   business/tasks.php): селектор был только по тегу button, ссылки оформление
   не получали. Расширено на a во всех правилах вкладок ниже + сброшены
   унаследованные от базового `a` цвет/подчёркивание. */
.tabs-nav button, .tabs-nav a { background: none; border: none; padding: var(--sp-3) var(--sp-4); font-weight: 600; font-size: var(--fs-14); color: var(--text-3); cursor: pointer; border-bottom: 2px solid transparent; white-space: nowrap; min-height: 44px; text-decoration: none; }
.tabs-nav button:hover, .tabs-nav a:hover { color: var(--text); text-decoration: none; }
.tabs-nav button.active, .tabs-nav a.active { color: var(--text); border-color: var(--violet); }
.tab-panel { display: none; }
.tab-panel.active { display: block; }

.accordion-item { border: 1px solid var(--border); border-radius: var(--radius-sm); margin-bottom: var(--sp-3); overflow: hidden; background: var(--surface); }
.accordion-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-4) var(--sp-4); cursor: pointer; font-weight: 600; background: var(--surface); border: none; width: 100%; text-align: left; font-size: var(--fs-16); min-height: 44px; color: var(--text); border-radius: inherit; }
/* Открытая секция НЕ обрезает содержимое: подсказки к полям всплывают над
   верхней границей секции, и при overflow: hidden первая строка полей теряла
   пояснение целиком. Скругление шапки при этом держится своим border-radius,
   а не обрезкой у родителя. */
.accordion-item.open { overflow: visible; }
.accordion-item.open .accordion-head { border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
.accordion-head .step-num { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; background: var(--violet-a14); color: var(--violet-text-safe); font-size: var(--fs-14); margin-right: var(--sp-2); flex-shrink: 0; font-family: var(--font-mono); }
.accordion-head .chevron { transition: transform .2s ease; flex-shrink: 0; color: var(--text-3); }
.accordion-item.open .chevron { transform: rotate(180deg); }
.accordion-body { display: none; padding: 0 var(--sp-4) var(--sp-6); color: var(--text-2); }
.accordion-item.open .accordion-body { display: block; }

/* ============================================================================
   22. MODAL
   ============================================================================ */
.modal-backdrop { position: fixed; inset: 0; background: rgba(5,6,10,.6); display: none; align-items: center; justify-content: center; z-index: var(--z-modal); padding: var(--sp-4); }
.modal-backdrop.open { display: flex; }
.modal { background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-lg); padding: var(--sp-6); max-width: 480px; width: 100%; box-shadow: var(--shadow-lg); max-height: 90vh; overflow-y: auto; }
.modal-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--sp-4); gap: var(--sp-3); }
.modal-head h3 { margin: 0; }
.modal-close { background: none; border: none; font-size: var(--fs-21); cursor: pointer; color: var(--text-3); width: 44px; height: 44px; flex-shrink: 0; }
.modal-close:hover { color: var(--text); }

/* ============================================================================
   23. FLASH-СООБЩЕНИЯ
   ============================================================================ */
.flash { border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4); font-size: var(--fs-14); display: flex; gap: var(--sp-2); align-items: flex-start; border: 1px solid transparent; }
.flash-success { background: var(--mint-a14); color: var(--mint); border-color: var(--mint-a35); }
.flash-error { background: var(--red-a14); color: var(--red); border-color: var(--red-a35); }
.flash-warn { background: var(--amber-a14); color: var(--amber); border-color: var(--amber-a35); }

/* ============================================================================
   24. PAGINATION / EMPTY STATE
   ============================================================================ */
.pagination { display: flex; justify-content: center; gap: var(--sp-2); margin-top: var(--sp-8); flex-wrap: wrap; }
.pagination a, .pagination span { min-width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); border: 1px solid var(--border); font-weight: 600; font-size: var(--fs-14); color: var(--text-2); }
.pagination a:hover { border-color: var(--violet); color: var(--text); text-decoration: none; }
.pagination .current { background: var(--grad); color: #fff; border-color: transparent; }

.empty-state { text-align: center; padding: var(--sp-12) var(--sp-4); color: var(--text-3); }
.empty-state svg { width: 56px; height: 56px; margin: 0 auto var(--sp-4); color: var(--text-3); }
.empty-state strong { color: var(--text); }

/* ============================================================================
   25. STEPS ("Как это работает" — старый двухколоночный вариант)
   ============================================================================ */
.steps-cols { display: grid; grid-template-columns: 1fr; gap: var(--sp-8); }
@media (min-width: 901px) { .steps-cols { grid-template-columns: 1fr 1fr; } }
.steps-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--sp-4); }
.steps-list li { display: flex; gap: var(--sp-3); align-items: flex-start; }
.steps-list .num { flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%; background: var(--violet-a14); color: var(--violet-text-safe); display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: var(--fs-16); font-family: var(--font-mono); }
.steps-list p { margin: 0; color: var(--text-2); }
.steps-list strong { display: block; margin-bottom: 2px; color: var(--text); }

/* ============================================================================
   26. ДОСТИЖЕНИЯ
   ============================================================================ */
.achv-card { display: flex; gap: var(--sp-3); padding: var(--sp-4); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.achv-card.locked { opacity: .5; }
.achv-icon { width: 44px; height: 44px; border-radius: 50%; background: var(--amber-a14); color: var(--amber); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.achv-icon svg { width: 22px; height: 22px; }

/* ============================================================================
   27. REVIEW / СПОРЫ
   ============================================================================ */
.review-card { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-4); background: var(--surface); }
.review-head { display: flex; justify-content: space-between; gap: var(--sp-2); align-items: center; margin-bottom: var(--sp-2); }

/* ============================================================================
   28. НОВЫЕ СЕРВИСНЫЕ КОМПОНЕНТЫ: doc-toc / notif-list / danger-zone
   ============================================================================ */

/* ---- 28.1 Doc-toc: липкое оглавление для юридических/справочных страниц ---- */
/* align-items не задаём — см. .split-60-40 (раздел 14): default stretch даёт
   sticky .doc-toc пространство для позиционирования; сам .doc-toc уже capped
   через max-height/overflow-y ниже, так что "растянутая панель" не грозит. */
.doc-layout { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); }
@media (min-width: 1025px) { .doc-layout { grid-template-columns: 240px 1fr; } }
.doc-toc { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--sp-4); }
@media (min-width: 1025px) { .doc-toc { position: sticky; top: 88px; max-height: calc(100vh - 120px); overflow-y: auto; } }
.doc-toc-title { font-family: var(--font-mono); font-size: var(--fs-12); text-transform: uppercase; letter-spacing: .05em; color: var(--text-3); margin-bottom: var(--sp-2); }
.doc-toc nav { display: flex; flex-direction: column; gap: 2px; }
.doc-toc a { display: block; padding: var(--sp-2) var(--sp-2); border-radius: var(--radius-xs); font-size: var(--fs-14); color: var(--text-2); border-left: 2px solid transparent; }
.doc-toc a:hover { color: var(--text); background: var(--surface-3); text-decoration: none; }
.doc-toc a.active { color: var(--text); border-left-color: var(--violet); background: var(--violet-a14); }

/* ---- 28.2 Notif-list: список уведомлений ---- */
.notif-list { display: flex; flex-direction: column; gap: 2px; }
.notif-item { display: flex; gap: var(--sp-3); align-items: flex-start; padding: var(--sp-4); border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--surface); margin-bottom: var(--sp-2); }
.notif-item.unread { border-color: rgba(124,92,255,.4); background: var(--violet-a14); }
.notif-icon { flex-shrink: 0; width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--surface-3); color: var(--text-2); }
.notif-icon svg { width: 18px; height: 18px; }
.notif-body { flex: 1; min-width: 0; }
.notif-title { font-size: var(--fs-14); color: var(--text); margin: 0 0 2px; }
.notif-time { font-size: var(--fs-12); color: var(--text-3); font-family: var(--font-mono); }
.notif-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: 50%; background: var(--violet); margin-top: var(--sp-2); }

/* ---- 28.3 Danger-zone: блок опасных действий (удаление аккаунта и т.п.) ---- */
.danger-zone { border: 1px solid var(--red-a35); border-radius: var(--radius); background: var(--red-a14); padding: var(--sp-4); margin-top: var(--sp-8); }
.danger-zone-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-2); }
.danger-zone-head h3 { color: var(--red); margin: 0; }
.danger-zone-body { color: var(--text-2); font-size: var(--fs-14); }
.danger-zone .btn-danger { margin-top: var(--sp-3); }

/* ============================================================================
   29. LEGAL / ERROR-SHELL
   ============================================================================ */
.legal h2 { margin-top: 1.8em; }
.legal ul { padding-left: 1.4em; }
.legal .legal-meta { color: var(--text-3); font-size: var(--fs-14); margin-bottom: 2em; }

.error-shell { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: var(--sp-8) var(--sp-4); background: var(--bg); }
.error-shell .code { font-size: 6rem; font-weight: 800; color: rgba(124,92,255,.35); line-height: 1; font-family: var(--font-display); }

/* ============================================================================
   30. УТИЛИТЫ-ХЕЛПЕРЫ
   ============================================================================ */
/* Скрыть визуально, оставить для скринридера — стандартный рецепт
   (clip-path + абсолютное позиционирование, НЕ display:none — иначе элемент
   пропадёт и из accessibility-дерева тоже). Для подписей полей форм и т.п. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}
.overflow-wrap { overflow-wrap: break-word; word-break: break-word; }
.no-scroll { overflow: hidden; }
.gap-8 { gap: var(--sp-2); } .gap-12 { gap: var(--sp-3); } .gap-16 { gap: var(--sp-4); } .gap-24 { gap: var(--sp-6); }
.mt-8 { margin-top: var(--sp-2); } .mt-16 { margin-top: var(--sp-4); } .mt-24 { margin-top: var(--sp-6); } .mt-32 { margin-top: var(--sp-8); }
.mb-0 { margin-bottom: 0; } .mb-8 { margin-bottom: var(--sp-2); } .mb-16 { margin-bottom: var(--sp-4); } .mb-24 { margin-bottom: var(--sp-6); }

/* ============================================================================
   31. ДОСТУПНОСТЬ / ГЛОБАЛЬНЫЕ АДАПТИВНЫЕ ПРАВИЛА
   Контроль ширин 1920/1280/390 выполняется на уровне каждого компонента выше
   (контейнер max-width + mobile-first). Здесь — сквозные гарантии.
   ============================================================================ */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--violet); outline-offset: 2px; border-radius: 6px;
}

/* тап-зоны: интерактивные элементы системы уже заданы с min-height:44px
   (.btn, .burger, .bell, .cab-link, .tag-select label, .accordion-head,
   .tabs-nav button, .pagination a/span, .modal-close, .star-input label) */

@media (max-width: 480px) {
  /* overflow-wrap (не word-break!) — переносит слово ТОЛЬКО если оно реально
     не помещается в строку; word-break рвал любое длинное слово посередине
     («ФудЭкспре/сс», «конфиденциальнос/ти» на 390px — QA-итерация 3, п.3).
     h1 и так наследует overflow-wrap:break-word из базового правила
     h1,h2,h3,h4 (раздел 01) — дублируем здесь явно для мобильного контекста,
     чтобы разработчик не искал его в общем правиле.

     overflow-wrap бессилен, когда слово физически не влезает даже на свою
     строку целиком (узкая колонка h1 рядом с аватаром на .profile-hero-top,
     390px) — перенос всё равно случится ровно там, где кончилось место,
     без слогов ("ФудЭкспре" / "сс"). hyphens:auto (страница имеет lang="ru")
     просит браузер переносить по слогам с дефисом там же, где неизбежен
     перенос — читаемее, чем произвольный обрыв. Плюс чуть уменьшаем сам
     кегль h1 на самых узких экранах — при чуть меньшем шрифте слово либо
     вовсе помещается на одну строку, либо перенос происходит на менее
     режущем слух месте (QA-итерация 4, п.4). Горизонтального переполнения
     не появляется — проверено scrollWidth на business-public.html/privacy.html. */
  h1 {
    overflow-wrap: break-word;
    hyphens: auto;
    -webkit-hyphens: auto;
    font-size: clamp(1.75rem, 1.35rem + 3.4vw, 2.6rem);
  }
  .container { padding: 0 var(--sp-4); }
}

/* ============================================================================
   доп. для группы A (публичные страницы) — компоненты, которых не хватало
   в базовой системе: панель фильтров каталога (Etsy/Faire/Behance-стиль),
   крупная ставка на карточке задания, горизонтальная лента достижений,
   карточка портфолио, avatar-монограмма, нумерация разделов юр. документов.
   ============================================================================ */

/* ---- Фильтры каталога: группы, чекбоксы со счётчиками, range, toggle ---- */
.filters-head { display: none; align-items: center; justify-content: space-between; margin-bottom: var(--sp-4); }
.filters-close { width: 40px; height: 40px; border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--surface-3); color: var(--text-2); font-size: var(--fs-21); line-height: 1; cursor: pointer; }

.filter-group { padding-bottom: var(--sp-4); margin-bottom: var(--sp-4); border-bottom: 1px solid var(--border); }
.filter-group:last-of-type { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
.filter-group-title { font-family: var(--font-mono); font-size: var(--fs-12); text-transform: uppercase; letter-spacing: .05em; color: var(--text-3); margin-bottom: var(--sp-3); }

.filter-option { display: flex; align-items: center; gap: var(--sp-2); min-height: 40px; font-size: var(--fs-14); color: var(--text-2); cursor: pointer; }
.filter-option input { width: 18px; height: 18px; flex-shrink: 0; accent-color: var(--violet); }
.filter-option .filter-option-label { display: inline-flex; align-items: center; gap: var(--sp-2); flex: 1; min-width: 0; }
.filter-option .filter-option-label svg { width: 16px; height: 16px; flex-shrink: 0; }
.filter-count { font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-3); background: var(--surface-3); border-radius: var(--radius-pill); padding: 1px var(--sp-2); flex-shrink: 0; }

.filter-range { display: flex; flex-direction: column; gap: var(--sp-2); }
.filter-range input[type=range] { width: 100%; accent-color: var(--violet); height: 22px; cursor: pointer; }
.filter-range-val { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-3); }

.filter-toggle { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); font-size: var(--fs-14); color: var(--text-2); cursor: pointer; }
.switch { position: relative; display: inline-block; width: 40px; height: 24px; flex-shrink: 0; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch-track { position: absolute; inset: 0; background: var(--surface-3); border: 1px solid var(--border-strong); border-radius: var(--radius-pill); transition: background .15s ease; }
.switch-track::after { content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: var(--text-2); transition: transform .15s ease, background .15s ease; }
.switch input:checked ~ .switch-track { background: var(--violet-a35); border-color: var(--violet); }
.switch input:checked ~ .switch-track::after { transform: translateX(16px); background: var(--violet); }
.switch input:focus-visible ~ .switch-track { outline: 2px solid var(--violet); outline-offset: 2px; }

.filters-actions { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: var(--sp-2); }

@media (max-width: 900px) {
  .filters-head { display: flex; }
  .filters {
    position: fixed; inset: 0 auto 0 0; z-index: var(--z-offcanvas); width: min(340px, 88vw);
    height: 100vh; overflow-y: auto; border-radius: 0; transform: translateX(-100%);
    transition: transform .25s ease; display: block;
  }
  .filters.open { transform: translateX(0); }
}

/* ---- Панель тулбара каталога: заголовок + счётчик + сортировка ---- */
.catalog-toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-2); }
.catalog-toolbar-right { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.catalog-count { font-size: var(--fs-14); color: var(--text-3); white-space: nowrap; }
.catalog-sort { display: flex; align-items: center; gap: var(--sp-2); }
.catalog-sort label { font-size: var(--fs-13); color: var(--text-3); white-space: nowrap; }
.catalog-sort select { min-width: 168px; min-height: 40px; padding: var(--sp-2) var(--sp-8) var(--sp-2) var(--sp-3); font-size: var(--fs-14); }
.filters-toggle-btn { display: none; }
@media (max-width: 900px) { .filters-toggle-btn { display: inline-flex; } }

/* ---- Карточка задания: крупная mono-ставка + прогресс слотов ---- */
.task-card-rate { display: flex; align-items: baseline; gap: var(--sp-2); }
.task-rate-num { font-family: var(--font-mono); font-weight: 500; font-size: var(--fs-26); color: var(--text); font-variant-numeric: tabular-nums; }
.task-rate-unit { font-size: var(--fs-12); color: var(--text-3); }
.slots-bar { display: flex; flex-direction: column; gap: var(--sp-1); }
.slots-bar .slots { display: flex; justify-content: space-between; font-family: var(--font-mono); }

/* ---- Достижения горизонтальной лентой (профиль креатора) ---- */
.achv-strip { display: flex; gap: var(--sp-3); overflow-x: auto; padding-bottom: var(--sp-1); margin: 0 calc(var(--sp-1) * -1); scroll-snap-type: x proximity; }
.achv-strip .achv-card { width: 84vw; max-width: 280px; flex: 0 0 auto; scroll-snap-align: start; }
@media (min-width: 640px) { .achv-strip .achv-card { width: 300px; } }

/* ---- Портфолио (профиль креатора) ---- */
.portfolio-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; display: flex; flex-direction: column; transition: border-color .2s ease, box-shadow .2s ease; }
.portfolio-card:hover { border-color: rgba(124,92,255,.5); box-shadow: 0 0 24px rgba(124,92,255,.16); }
.portfolio-preview { position: relative; aspect-ratio: 4/3; background: linear-gradient(135deg, var(--violet-a35), var(--blue-a12)); display: flex; align-items: flex-end; padding: var(--sp-3); }
.portfolio-preview .platform-badge { background: rgba(11,13,20,.55); backdrop-filter: blur(6px); border-radius: var(--radius-pill); padding: var(--sp-1) var(--sp-2); color: #fff; }
.portfolio-views { position: absolute; top: 12px; right: 12px; font-family: var(--font-mono); font-weight: 600; font-size: var(--fs-13); color: #fff; background: rgba(11,13,20,.55); backdrop-filter: blur(6px); border-radius: var(--radius-pill); padding: var(--sp-1) var(--sp-2); }
.portfolio-body { padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-2); flex: 1; }

/* ---- Avatar-монограмма (профиль бизнеса/креатора без фото) ---- */
.avatar-mono {
  width: 76px; height: 76px; border-radius: var(--radius); flex-shrink: 0;
  background: linear-gradient(135deg, var(--violet), var(--blue)); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-weight: 700; font-size: var(--fs-26);
}
.avatar-mono.round { border-radius: 50%; }
.profile-hero { position: relative; padding: var(--sp-6); overflow: hidden; }
.profile-hero-top { display: flex; gap: var(--sp-5); align-items: flex-start; flex-wrap: wrap; }
.profile-hero-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-2) 0; }

/* ---- Юридические документы: mono-нумерация разделов, читаемый интерлиньяж ---- */
.legal-num { font-family: var(--font-mono); color: var(--violet); margin-right: var(--sp-2); }
.legal-badge { display: inline-flex; align-items: center; gap: var(--sp-2); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-pill); padding: var(--sp-2) var(--sp-4); font-size: var(--fs-14); color: var(--text-2); margin-bottom: var(--sp-5); }
.legal p, .legal li { line-height: 1.75; color: var(--text-2); }

/* ---- 404: ссылки-подсказки ---- */
.error-links { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); justify-content: center; margin-top: var(--sp-6); }
.error-links a { font-size: var(--fs-14); color: var(--text-2); }
.error-links a:hover { color: var(--text); }

/* ============================================================================
   ГРУППА B — вход/регистрация/профиль (было add-b.css)
   Слито в ui.css на шаге 0 натяжки редизайна (60_integration.md). Точечные
   добавления поверх базовой системы, ничего из блоков выше не переопределяют.
   ============================================================================ */

/* ---- Поле пароля с кнопкой «показать/скрыть» (контракт единый для групп B и E:
   форма/вход/регистрация/профиль используют #id, admin-настройки — bare id;
   JS-обработчик в ui.js понимает оба варианта, см. initPassToggle). ---- */
.pass-field { position: relative; }
.pass-field input { padding-right: var(--sp-12); }
.pass-toggle {
  position: absolute; right: 4px; top: 4px; bottom: 4px;
  width: 40px; display: flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--text-3); cursor: pointer;
  border-radius: var(--radius-xs);
}
.pass-toggle:hover { color: var(--text); background: var(--surface-3); }
.pass-toggle svg { width: 20px; height: 20px; }
.pass-toggle .icon-off { display: none; }
.pass-toggle[aria-pressed="true"] .icon-on { display: none; }
.pass-toggle[aria-pressed="true"] .icon-off { display: block; }
@media (max-width: 767px) {
  .pass-toggle { width: 44px; }
  .pass-field input { padding-right: var(--sp-16); }
}

/* ---- Stepper как навигация по якорям (визуальная навигация внутри одной формы) ---- */
.stepper a.step { text-decoration: none; color: inherit; }
.stepper a.step:hover { text-decoration: none; }
.stepper a.step:focus-visible { outline-offset: 4px; }

/* ---- .repeat-row на узких экранах: ui.css держит их в одну строку (flex,
   без wrap) — на страницах группы B в строке до 3 полей + кнопка «Удалить»,
   на 390px это не помещается. Складываем в столбец, начиная с 620px. ---- */
@media (max-width: 620px) {
  .repeat-row { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
  .repeat-row > * { flex: none; width: 100%; }
  .repeat-row .btn { width: 100%; }
}

/* ---- Разделитель ролевых блоков внутри общей формы профиля ---- */
.role-block-label {
  display: inline-block; font-family: var(--font-mono); font-size: var(--fs-12);
  text-transform: uppercase; letter-spacing: .06em; color: var(--text-3);
  margin: var(--sp-6) 0 var(--sp-1);
}
.role-block-label:first-child { margin-top: 0; }

/* ---- Карточка-шапка профиля с аватаром ---- */
.profile-hero-name { margin: 0 0 2px; }
.profile-hero-role { font-size: var(--fs-14); color: var(--text-3); margin: 0; }

/* ---- Мини-виджет и юр. ссылки в .auth-aside (см. 20_auth.md, "Общий каркас
   auth-экранов"). .auth-aside — flex-column с justify-content:center; ссылки
   получают margin-top:auto, чтобы прижаться к низу панели, а не болтаться в
   центре вместе с оффером/буллетами. ---- */
.auth-aside-widget { position: relative; z-index: 1; width: 100%; max-width: 360px; }
.auth-aside-legal {
  position: relative; z-index: 1; margin-top: auto; padding-top: var(--sp-5);
  font-size: var(--fs-13); color: var(--text-3);
}
.auth-aside-legal a { color: var(--text-3); }
.auth-aside-legal a:hover { color: var(--text); }

/* ---- Тап-зоны на мобилке: строки радио-кнопок не меньше 44x44px. ---- */
@media (max-width: 767px) {
  .radio-row { min-height: 44px; align-items: center; }
}

/* ============================================================================
   ГРУППА C — кабинет бизнеса (было add-c.css): дашборд, задания, форма
   задания, просмотр задания, проверка роликов, баланс.
   ============================================================================ */

/* ---- 0. Топбар кабинета на узких экранах ----
   .cab-breadcrumbs — обычная ссылка без text-overflow:ellipsis (кроме .current),
   на 390px крошки конфликтуют с балансом/колокольчиком и обрезаются некрасиво —
   на самых узких экранах прячем крошки, оставляя баланс и колокольчик. */
@media (max-width: 480px) {
  .cab-breadcrumbs { display: none; }
}

/* ---- 0. Общая заглушка ---- */
.cab-legal-note {
  border-top: 1px dashed var(--border);
  margin-top: var(--sp-10);
  padding-top: var(--sp-5);
}

/* ----------------------------------------------------------------------
   1. ДАШБОРД
   ---------------------------------------------------------------------- */
.kpi-strip { display: grid; grid-template-columns: 1fr; gap: var(--sp-3); margin-bottom: var(--sp-6); }
@media (min-width: 640px) { .kpi-strip { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1025px) { .kpi-strip { grid-template-columns: repeat(4, 1fr); } }
.kpi-strip > * { min-width: 0; }
.kpi-strip-secondary { display: grid; grid-template-columns: 1fr; gap: var(--sp-3); margin-bottom: var(--sp-8); }
@media (min-width: 640px) { .kpi-strip-secondary { grid-template-columns: repeat(2, 1fr); max-width: 520px; } }
.kpi-strip-secondary > * { min-width: 0; }

/* align-items не задаём (default stretch) — системный фикс sticky-колонок, см. раздел 14 базовой системы */
.dash-cols { display: grid; grid-template-columns: 1fr; gap: var(--sp-8); }
@media (min-width: 1025px) { .dash-cols { grid-template-columns: 1.3fr .9fr; } }
.dash-cols > * { min-width: 0; }

.mini-preview {
  width: 40px; height: 40px; border-radius: var(--radius-xs); flex-shrink: 0;
  background: var(--surface-3); display: flex; align-items: center; justify-content: center; color: var(--text-3);
}
.mini-preview svg { width: 18px; height: 18px; }

.task-mini-list { display: flex; flex-direction: column; gap: var(--sp-3); }
.task-mini-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-4); }
.task-mini-card-head { display: flex; justify-content: space-between; gap: var(--sp-2); align-items: baseline; margin-bottom: var(--sp-2); }
.task-mini-card-head a { color: var(--text); font-weight: 600; font-size: var(--fs-16); }
.task-mini-foot { display: flex; justify-content: space-between; gap: var(--sp-2); margin-top: var(--sp-2); font-size: var(--fs-13); color: var(--text-3); }
.task-mini-foot .num { color: var(--text-2); }

.how-steps-block { margin-top: var(--sp-10); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--sp-6); }

/* ----------------------------------------------------------------------
   2. МОИ ЗАДАНИЯ
   ---------------------------------------------------------------------- */
.tasks-toolbar { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; justify-content: space-between; margin-bottom: var(--sp-4); }
.tasks-toolbar-search { display: flex; gap: var(--sp-2); flex-wrap: wrap; flex: 1; min-width: 0; }
.tasks-toolbar-search input[type=search] { max-width: 280px; }
.tasks-toolbar-search select { max-width: 220px; }

/* min-width:0 — иначе длинное название задания (флекс-потомок) не может
   сжаться и распирает родительскую ячейку таблицы (QA-итерация 5, п.2).
   max-width — тот же потолок, что у .data-table td.primary, чтобы колонка
   с task-name-cell вела себя так же на очень длинных названиях. */
/* align-items:flex-start НЕ растягивает детей по ширине колонки — без
   явного max-width:100% на <a> флекс-item в таком контексте берёт свой
   max-content (полную ширину непрерывной строки), полностью игнорируя
   доступные 296px родителя, и overflow-wrap просто не от чего отталкиваться
   (обнаружено эмпирически: .task-name-cell шириной 296px, a — 689px, хотя
   overflow-wrap:break-word уже стоял — QA-итерация 5, доп. находка при
   верификации). max-width:100% отдаёт <a> реальную доступную ширину, и перенос
   наконец включается. */
.task-name-cell { display: flex; flex-direction: column; gap: var(--sp-1); align-items: flex-start; min-width: 0; max-width: 320px; white-space: normal; }
.task-name-cell a { color: var(--text); font-weight: 600; overflow-wrap: break-word; min-width: 0; max-width: 100%; }
.task-platform-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.task-platform-chips .platform-badge { font-size: var(--fs-12); color: var(--text-3); }
.task-platform-chips .platform-badge svg { width: 13px; height: 13px; }

.mini-progress-cell { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 110px; }
.mini-progress-cell .progress { height: 6px; }
.mini-progress-cell span { font-size: var(--fs-12); color: var(--text-3); }

/* ----------------------------------------------------------------------
   3. ФОРМА ЗАДАНИЯ
   ---------------------------------------------------------------------- */
.form-layout { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); }
@media (min-width: 1025px) { .form-layout { grid-template-columns: 1fr 340px; } }
.form-layout > * { min-width: 0; }

.form-section-hint { font-size: var(--fs-14); color: var(--text-3); margin: calc(var(--sp-2) * -1) 0 var(--sp-4); }

.calc-card { position: relative; }
@media (min-width: 1025px) { .calc-card { position: sticky; top: 88px; } }
.calc-title { font-weight: 700; font-size: var(--fs-16); margin: 0 0 var(--sp-1); color: var(--text); }
.calc-row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); padding: var(--sp-2) 0; border-bottom: 1px dashed var(--border); }
.calc-row:last-of-type { border-bottom: none; }
.calc-row-label { font-size: var(--fs-13); color: var(--text-3); }
.calc-row-val { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 700; color: var(--text); font-size: var(--fs-16); white-space: nowrap; }
.calc-row-val.big { font-size: var(--fs-26); font-weight: 300; font-family: var(--font-display); color: var(--mint); }
.calc-warn { display: none; background: var(--red-a14); border: 1px solid var(--red-a35); color: var(--red); border-radius: var(--radius-xs); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-13); margin: var(--sp-3) 0; }
.calc-warn.show { display: block; }
.calc-card .form-actions { flex-direction: column; margin-top: var(--sp-4); }
.calc-card .form-actions .btn { width: 100%; }

/* площадки как единичный выбор (radio), но визуально стилизованы под .tag-select */
.tag-select label:has(input[type=radio]:checked) { border-color: var(--violet); background: var(--violet-a14); color: var(--text); }

.form-subhead { font-family: var(--font-mono); font-size: var(--fs-12); text-transform: uppercase; letter-spacing: .06em; color: var(--text-3); margin: var(--sp-6) 0 var(--sp-3); }
.form-subhead:first-child { margin-top: 0; }

/* ----------------------------------------------------------------------
   4. ПРОСМОТР ЗАДАНИЯ
   ---------------------------------------------------------------------- */
.task-view-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--sp-4); align-items: flex-start; margin-bottom: var(--sp-4); }
.task-view-metrics { margin-bottom: var(--sp-6); }

.view-layout { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); }
@media (min-width: 1025px) { .view-layout { grid-template-columns: 1fr 320px; } }
.view-layout > * { min-width: 0; }
.summary-card { position: relative; }
@media (min-width: 1025px) { .summary-card { position: sticky; top: 88px; } }

.creator-cell { display: flex; align-items: center; gap: var(--sp-2); }
.creator-avatar {
  width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
  background: var(--grad); color: #fff; font-family: var(--font-mono); font-size: var(--fs-12);
  display: flex; align-items: center; justify-content: center; font-weight: 700;
}

.verdict-history { display: flex; flex-direction: column; gap: var(--sp-2); margin: var(--sp-3) 0; }
.verdict-history-item { border-left: 2px solid var(--border-strong); padding: 2px 0 2px var(--sp-3); font-size: var(--fs-13); color: var(--text-2); }
.verdict-history-item time { display: block; font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-3); margin-top: 2px; }

/* ----------------------------------------------------------------------
   5. ПРОВЕРКА РОЛИКОВ — режим очереди
   ---------------------------------------------------------------------- */
.review-layout { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); }
@media (min-width: 1025px) { .review-layout { grid-template-columns: 240px 1fr 320px; } }
.review-layout > * { min-width: 0; }

.queue-rail { display: flex; flex-direction: row; gap: var(--sp-2); overflow-x: auto; padding-bottom: var(--sp-1); }
@media (min-width: 1025px) {
  .queue-rail { flex-direction: column; overflow-x: visible; position: sticky; top: 88px; max-height: calc(100vh - 120px); overflow-y: auto; }
}
.queue-item {
  display: flex; align-items: center; gap: var(--sp-2); text-align: left;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: var(--sp-2); cursor: pointer; flex-shrink: 0; width: 240px; font-family: inherit; color: var(--text);
  min-height: 44px;
}
@media (min-width: 1025px) { .queue-item { width: auto; } }
.queue-item:hover { border-color: rgba(124,92,255,.4); }
.queue-item.active { border-color: var(--violet); background: var(--violet-a14); }
.queue-item-thumb {
  width: 40px; height: 40px; border-radius: var(--radius-xs); flex-shrink: 0; background: var(--surface-3);
  display: flex; align-items: center; justify-content: center; color: var(--text-3); overflow: hidden;
}
.queue-item-thumb svg { width: 18px; height: 18px; }
.queue-item-body { min-width: 0; }
.queue-item-title { font-size: var(--fs-13); font-weight: 600; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.queue-item-timer { font-size: var(--fs-12); color: var(--text-3); font-family: var(--font-mono); }
.queue-item-timer.urgent { color: var(--red); }

.review-detail { display: none; flex-direction: column; gap: var(--sp-4); }
.review-detail.active { display: flex; }

.link-preview-card { display: flex; align-items: center; gap: var(--sp-3); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4); }
.link-preview-card .platform-badge { flex-shrink: 0; }
.link-preview-card-body { min-width: 0; }
.link-preview-card-url { display: block; font-size: var(--fs-14); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.link-preview-card-meta { font-size: var(--fs-12); color: var(--text-3); }

.checklist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.checklist li { position: relative; padding-left: var(--sp-6); font-size: var(--fs-13); color: var(--text-2); }
.checklist li::before {
  content: ""; position: absolute; left: 0; top: 3px; width: 15px; height: 15px; border-radius: 4px;
  border: 1.5px solid var(--border-strong); background: var(--surface-3);
}

.decision-panel { position: relative; }
@media (min-width: 1025px) { .decision-panel { position: sticky; top: 88px; } }
.decision-panel .btn-group { flex-direction: column; }
.decision-panel .btn-group .btn { width: 100%; }
.decision-note { font-size: var(--fs-12); color: var(--text-3); margin-top: var(--sp-3); }
/* Панель решения переключается вместе с очередью тем же JS (initReviewQueue,
   см. ui.js), что и .review-detail — единое правило показа по [data-queue-detail]. */
.decision-panel[data-queue-detail] { display: none; }
.decision-panel[data-queue-detail].active { display: block; }

/* ----------------------------------------------------------------------
   6. БАЛАНС
   ---------------------------------------------------------------------- */
.balance-hero {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: var(--sp-8) var(--sp-6); margin-bottom: var(--sp-6); position: relative; overflow: hidden;
  display: flex; flex-wrap: wrap; gap: var(--sp-6); align-items: center; justify-content: space-between;
}
.balance-hero-main { position: relative; z-index: 1; }
.balance-hero-label { font-size: var(--fs-14); color: var(--text-3); margin-bottom: var(--sp-1); }
.balance-hero-sum { font-family: var(--font-display); font-weight: 200; font-size: clamp(2.6rem, 6vw, 4.2rem); color: var(--text); font-variant-numeric: tabular-nums; line-height: 1; white-space: nowrap; }
.balance-hero-sub { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-3); position: relative; z-index: 1; }
.balance-hero-sub-item { font-size: var(--fs-13); color: var(--text-3); }
.balance-hero-sub-item strong { display: block; font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text); font-size: var(--fs-16); font-weight: 600; white-space: nowrap; }
.balance-hero-cta { position: relative; z-index: 1; flex-shrink: 0; }

/* специфичность выше, чем у .data-table td.num (0,2,1), чтобы перебить цвет текста */
td.num.amount-in, .amount-in { color: var(--mint) !important; }
td.num.amount-out, .amount-out { color: var(--red) !important; }

/* ============================================================================
   ГРУППА D — кабинет креатора (было add-d.css): cr-dashboard, cr-tasks,
   cr-task-work, cr-finance, cr-referrals.
   ============================================================================ */

.text-mint  { color: var(--mint); }
.text-amber { color: var(--amber); }
.text-red   { color: var(--red); }

/* ---- Мини-степпер этапа внутри узкой карточки (task-card на дашборде) ---- */
.stepper-mini { display: flex; gap: var(--sp-1); margin: 2px 0 var(--sp-1); }
.stepper-mini .seg { flex: 1; height: 6px; border-radius: var(--radius-pill); background: var(--surface-3); }
.stepper-mini .seg.done { background: var(--mint); }
.stepper-mini .seg.current { background: var(--violet); box-shadow: 0 0 0 3px var(--violet-a14); }
.stepper-mini-label { font-size: var(--fs-12); color: var(--text-3); margin: 0; }

/* ---- Липкая правая колонка cr-task-work.html (условия/начисления/чат) ---- */
.task-side { display: flex; flex-direction: column; gap: var(--sp-4); min-width: 0; }
/* .task-side сам защищён (флекс-колонка, min-width:0), но его ПРЯМЫЕ дети —
   карточки <section class="card card-pad"> — тоже флекс-потомки (главная ось
   вертикальная, но у каждого своя автоматическая min-width:auto по ширине);
   без этого правила глубоко вложенный длинный текст (например ссылка в чате
   внутри последней карточки) мог раздуть саму карточку шире колонки и
   вытолкнуть её за пределы .task-side. См. фикс .chat-widget выше. */
.task-side > * { min-width: 0; }
@media (min-width: 1025px) { .task-side { position: sticky; top: 88px; max-height: calc(100vh - 110px); overflow-y: auto; } }

/* ---- Лента вердиктов (submissions history) — cr-task-work.html ---- */
.verdict-list { display: flex; flex-direction: column; gap: var(--sp-4); }
.verdict-item { padding-bottom: var(--sp-4); border-bottom: 1px dashed var(--border); }
.verdict-item:last-child { border-bottom: none; padding-bottom: 0; }
.verdict-item-head { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-1); }
.verdict-date { font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-3); }
.verdict-item p { margin: 0; color: var(--text-2); font-size: var(--fs-14); }

/* ---- Переключатель собеседников чата (кабинет бизнеса — несколько
   креаторов на одном задании, business/task-view.php). Не покрыто демо-
   версией страницы (в биз-макете чат вынесен за рамки мокапа), стиль
   выдержан в общем языке системы (.tabs-nav/.tab-count). ---- */
.chat-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.chat-tabs a {
  display: inline-flex; align-items: center; gap: var(--sp-1); padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-pill);
  border: 1px solid var(--border); background: var(--surface); color: var(--text-2); font-size: var(--fs-14); text-decoration: none;
  min-height: 36px;
}
.chat-tabs a:hover { border-color: rgba(124,92,255,.4); color: var(--text); }
.chat-tabs a.active { border-color: var(--violet); background: var(--violet-a14); color: var(--text); }
.chat-tab-unread {
  display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 var(--sp-1);
  font-family: var(--font-mono); font-size: var(--fs-12); color: #fff; background: var(--violet); border-radius: var(--radius-pill);
}

/* ---- Виджет чата — разметка 1:1 с app/views/partials/chat_widget.php ----
   Длинная ссылка без пробелов в сообщении раздувала пузырь до ~1600px и
   вылезала за карточку: overflow-wrap:break-word НЕ уменьшает min-content
   (в отличие от overflow-wrap:anywhere), а min-width:auto (default) у каждого
   флекс-потомка в цепочке .chat-widget → .chat-thread → .chat-msg →
   .chat-msg-body позволял этому большому min-content раздувать бокс и
   перебивать max-width:88% у .chat-msg (min-width всегда выигрывает у
   max-width при конфликте). Фикс — min-width:0 на КАЖДОМ звене цепочки +
   overflow-wrap:anywhere на самом тексте. */
.chat-widget { display: flex; flex-direction: column; gap: var(--sp-3); min-width: 0; }
.chat-load-more { align-self: center; }
.chat-thread { display: flex; flex-direction: column; gap: var(--sp-2); max-height: 320px; overflow-y: auto; padding-right: 2px; min-width: 0; }
.chat-empty { margin: var(--sp-2) 0; text-align: center; }
.chat-widget .chat-msg {
  display: flex; flex-direction: column; gap: var(--sp-1); max-width: 88%; min-width: 0;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3); align-self: flex-start;
}
.chat-widget .chat-msg.mine { align-self: flex-end; background: var(--violet-a14); border-color: rgba(124,92,255,.3); }
.chat-msg-body { font-size: var(--fs-14); color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; min-width: 0; }
.chat-msg-attach { display: inline-block; font-size: var(--fs-13); color: var(--blue); }
.chat-msg-meta { font-size: var(--fs-12); color: var(--text-3); font-family: var(--font-mono); margin-top: 2px; }
.chat-form { display: flex; flex-direction: column; gap: var(--sp-2); border-top: 1px solid var(--border); padding-top: var(--sp-3); }
.chat-form textarea { min-height: 64px; }
.chat-form-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); flex-wrap: wrap; }
.chat-attach-btn { position: relative; display: inline-flex; align-items: center; gap: var(--sp-1); font-size: var(--fs-13); color: var(--text-2); cursor: pointer; min-height: 44px; }
.chat-attach-btn input[type=file] { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }

/* ---- Ячейка "задание" в .data-table (cr-tasks.html) ---- */
.dt-task { display: flex; flex-direction: column; gap: var(--sp-1); text-align: left; }
.dt-task strong { color: var(--text); font-family: var(--font-display); font-size: var(--fs-16); }
.dt-task .dt-task-brand { font-size: var(--fs-13); color: var(--text-3); }
.dt-task .task-badges { margin-top: 2px; }
@media (max-width: 640px) { .data-table td .dt-task { text-align: left; } }

/* ---- Счётчик на вкладке .tabs-nav (cr-tasks.html: "В работе · 2" и т.п.) ---- */
.tab-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 var(--sp-1); margin-left: var(--sp-1);
  font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-3);
  background: var(--surface-3); border-radius: var(--radius-pill);
}
.tabs-nav button.active .tab-count, .tabs-nav a.active .tab-count { color: var(--text); background: var(--violet-a14); }

/* ---- Мини-примеры заблокированного вывода (cr-finance.html) ---- */
.payout-example-grid { display: grid; grid-template-columns: 1fr; gap: var(--sp-3); margin-top: var(--sp-4); }
@media (min-width: 560px) { .payout-example-grid { grid-template-columns: 1fr 1fr; } }
.payout-example { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4); background: var(--surface-2); }
.payout-example strong { display: block; font-size: var(--fs-14); color: var(--text); margin-bottom: var(--sp-1); }
.payout-example p { margin: 0; font-size: var(--fs-13); color: var(--text-3); }
.payout-example .progress { margin-top: var(--sp-2); }
.payout-example .btn { margin-top: var(--sp-2); pointer-events: none; }

/* ---- Мелкие правки отступов для карточек условий/начислений в сайдбаре ---- */
.task-side .card-pad h3 { margin-bottom: var(--sp-3); }
.task-side .earn-card + p.hint { margin-top: var(--sp-2); }

/* ---- Правка хлебных крошек на 3-уровневой цепочке (cr-task-work.html) ---- */
.cab-breadcrumbs a { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 120px; }
@media (max-width: 480px) { .cab-breadcrumbs a { max-width: 64px; } }

/* ============================================================================
   ГРУППА E — админка + notifications.html + disputes.html (было add-e.css).
   .pass-field/.pass-toggle — уже объединены в блоке группы B выше (там же
   учтён вариант bare-id атрибута data-pass-toggle из adm-settings.html).
   ============================================================================ */

/* ---- 1. Очередь "Ждут решения" + амбер-подсветка KPI ---- */
.queue-list { display: flex; flex-direction: column; }
.queue-row {
  display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
  padding: var(--sp-4); border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); margin-bottom: var(--sp-2);
}
.queue-row.warn { border-color: rgba(245,177,76,.35); }
.queue-row-main { display: flex; align-items: center; gap: var(--sp-3); flex: 1 1 220px; min-width: 0; }
.queue-count {
  font-family: var(--font-mono); font-weight: 700; font-size: var(--fs-16); color: var(--text);
  background: var(--surface-3); border-radius: var(--radius-pill); min-width: 40px; height: 40px;
  padding: 0 var(--sp-1); display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.queue-row.warn .queue-count { background: var(--amber-a14); color: var(--amber); }
.queue-text { min-width: 0; }
.queue-label { font-weight: 600; color: var(--text); display: block; }
.queue-age { font-size: var(--fs-13); color: var(--text-3); display: block; margin-top: 2px; }

.stat-tile.warn { border-color: rgba(245,177,76,.4); background: linear-gradient(180deg, var(--amber-a14), transparent 65%); }

/* ---- 2. Лента "Последние события" ---- */
.activity-feed { display: flex; flex-direction: column; }
.activity-row { display: flex; gap: var(--sp-3); align-items: flex-start; padding: var(--sp-3) 0; border-bottom: 1px dashed var(--border); }
.activity-row:last-child { border-bottom: none; }
.activity-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: 50%; background: var(--violet); margin-top: var(--sp-2); }
.activity-text { flex: 1; min-width: 0; font-size: var(--fs-14); color: var(--text-2); }
.activity-text strong { color: var(--text); }
.activity-time { font-size: var(--fs-12); color: var(--text-3); font-family: var(--font-mono); white-space: nowrap; }

/* ---- 3. Zoom-превью скриншота + дельта заявленных/предыдущих цифр ---- */
.zoom-thumb {
  width: 100%; aspect-ratio: 16/9; object-fit: cover; border-radius: var(--radius-sm);
  border: 1px solid var(--border); cursor: zoom-in; margin-bottom: var(--sp-3); padding: 0;
  background: none; display: block; transition: border-color .2s ease;
}
.zoom-thumb:hover { border-color: rgba(124,92,255,.5); }
.modal-img-wrap .modal { max-width: 720px; }
.modal-img { width: 100%; border-radius: var(--radius-sm); display: block; }

.kv-delta { font-size: var(--fs-12); margin-left: var(--sp-2); font-family: var(--font-mono); font-weight: 500; }
.kv-delta.up { color: var(--mint); }
.kv-delta.down { color: var(--red); }

/* ---- 5. Липкая нижняя панель сохранения секции настроек ---- */
.settings-save-bar {
  position: sticky; bottom: 0; margin: var(--sp-6) calc(var(--sp-6) * -1) calc(var(--sp-6) * -1); padding: var(--sp-4) var(--sp-6);
  background: var(--panel-blur); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border-top: 1px solid var(--border); display: flex; gap: var(--sp-3); flex-wrap: wrap; align-items: center;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}
@media (max-width: 560px) { .settings-save-bar { margin: var(--sp-6) calc(var(--sp-4) * -1) calc(var(--sp-4) * -1); padding: var(--sp-3) var(--sp-4); } }

.settings-group + .settings-group { margin-top: var(--sp-6); }
.settings-group-title { margin-bottom: var(--sp-1); }
.settings-group-desc { font-size: var(--fs-14); color: var(--text-3); margin: 0 0 var(--sp-4); }

/* ---- 6. Карточка спора: переписка + липкий вердикт ---- */
.dispute-detail { display: grid; grid-template-columns: 1fr; gap: var(--sp-6); margin-bottom: var(--sp-8); scroll-margin-top: 88px; }
@media (min-width: 1025px) { .dispute-detail { grid-template-columns: 1.4fr 1fr; } }
.dispute-thread { display: flex; flex-direction: column; gap: var(--sp-3); }
@media (min-width: 1025px) { .dispute-side .form-card, .dispute-side > .card { position: sticky; top: 88px; } }

/* ---- 7. Чипы документов (Личность / вложения спора) ---- */
.doc-chip { display: inline-flex; align-items: center; gap: var(--sp-2); background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-pill); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-13); color: var(--text-2); margin: 2px var(--sp-1) 2px 0; }
.doc-chip svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--text-3); }

/* ---- 8. Мелкие доп. к tabs-nav / notif-list / карточкам ---- */
.tabs-nav button .tab-count, .tabs-nav a .tab-count { font-family: var(--font-mono); font-size: var(--fs-12); color: var(--text-3); margin-left: var(--sp-1); }
.notif-day-label { margin: var(--sp-6) 0 var(--sp-2); }
.notif-day-label:first-child { margin-top: 0; }
.card-list-gap { display: flex; flex-direction: column; gap: var(--sp-4); }

/* Пометка обязательного поля формы (звёздочка у подписи) */
.req-mark { color: var(--red); font-weight: 700; margin-left: 2px; }

/* ============================================================================
   ДОБАВЛЕНО 12.08.2026 — досбор профиля в кабинете (app/views/partials/
   profile_complete_modal.php) + подтверждение почты (email_verify.php).
   Обе модалки досбора переиспользуют .modal/.field/.tag-select/.repeat-row —
   новых классов не потребовалось. Ниже — только баннер подтверждения почты
   (база — .flash.flash-warn, здесь только добавки для сворачивания в строку).
   ============================================================================ */
.email-verify-banner { flex-wrap: wrap; }
.email-verify-banner-icon { width: 22px; height: 22px; flex-shrink: 0; }
.email-verify-banner-compact { display: none; font-weight: 600; }
.email-verify-banner-text { flex: 1; min-width: 220px; }
.email-verify-banner-text strong { color: var(--text); }
.email-verify-banner-actions { display: flex; align-items: center; gap: var(--sp-2); flex-shrink: 0; margin-left: auto; }
.email-verify-resend-form { display: inline-flex; }
.email-verify-banner-toggle {
  width: 32px; height: 32px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); border: none; background: transparent; color: inherit; cursor: pointer;
}
.email-verify-banner-toggle:hover { background: rgba(0,0,0,.06); }
.email-verify-banner-toggle .chevron { transition: transform .2s ease; }
.email-verify-banner.collapsed { align-items: center; }
.email-verify-banner.collapsed .email-verify-banner-toggle .chevron { transform: rotate(180deg); }
.email-verify-banner.collapsed .email-verify-banner-text,
.email-verify-banner.collapsed .email-verify-resend-form { display: none; }
.email-verify-banner.collapsed .email-verify-banner-compact { display: inline; }
@media (max-width: 640px) {
  .email-verify-banner-actions { margin-left: 0; width: 100%; justify-content: space-between; }
}

/* ============================================================================
   25. КАРТОЧКА ОПУБЛИКОВАННОГО РОЛИКА (кабинет креатора, проверка у бизнеса)
   ----------------------------------------------------------------------------
   Заменяет четыре голые плитки «оплаченные просмотры / начислено», по которым
   креатор никак не мог сверить наши цифры с тем, что показывает площадка.
   Слева обложка (её отдаёт сервис метрик и хранит у себя — она переживает
   удаление ролика), справа факты, снизу деньги.
   ============================================================================ */
.sub-card { padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-4); }
.sub-card-top { display: flex; gap: var(--sp-4); align-items: flex-start; }

/* Пропорция 9:16 — та же, что у вертикального видео. Фиксированная ширина, а
   не доля: обложка тут опознавательный знак, а не иллюстрация во всю строку. */
.sub-card-cover {
  width: 108px; flex-shrink: 0; aspect-ratio: 9 / 16; object-fit: cover;
  border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--surface-2);
}
.sub-card-cover-empty { display: flex; align-items: center; justify-content: center; color: var(--text-3); }
.sub-card-cover-empty svg { width: 28px; height: 28px; }

.sub-card-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.sub-card-head { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.sub-card-author { font-weight: 600; font-size: var(--fs-14); }
.sub-card-link { font-size: var(--fs-13); }
.sub-card-meta { font-size: var(--fs-12); color: var(--text-3); }

/* Состояние учёта: цвет несёт смысл, но не он один — рядом всегда есть слово,
   иначе состояние нечитаемо при любых проблемах с цветовосприятием. */
.sub-card-state {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-12); font-weight: 600; white-space: nowrap;
  padding: var(--sp-1) var(--sp-3); border-radius: var(--radius-pill);
  background: var(--surface-3); color: var(--text-2);
}
.sub-card-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.sub-card-state-active { background: var(--mint-a14); color: var(--mint); }
.sub-card-state-active .sub-card-dot { animation: hero-live 2.4s var(--ease-in-out) infinite; }
.sub-card-state-paused { background: var(--amber-a14); color: var(--amber); }
.sub-card-state-gone,
.sub-card-state-forbidden { background: var(--red-a14); color: var(--red); }
.sub-card-state-error { background: var(--amber-a14); color: var(--amber); }

/* Метрики — определения, а не плитки: подпись сверху мелко, значение снизу
   крупно, моноширинными цифрами, чтобы столбцы не плясали при обновлении. */
/* Сетка, а не flex-wrap: при переносе одинокая последняя метрика зависала в
   отдельной строке. auto-fill с минимумом в 104px раскладывает их ровно. */
.sub-metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: var(--sp-4); margin: var(--sp-2) 0 0; }
.sub-metrics > div { min-width: 0; }
.sub-metrics dt { font-size: var(--fs-11); color: var(--text-3); text-transform: uppercase; letter-spacing: .04em; font-family: var(--font-mono); }
.sub-metrics dd { margin: 2px 0 0; font-size: var(--fs-18); font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }

.sub-card-pay { border-top: 1px solid var(--border); padding-top: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-2); }
.sub-pay-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4); }
.sub-pay-label { font-size: var(--fs-13); color: var(--text-2); }
.sub-pay-val { font-size: var(--fs-16); font-weight: 600; font-variant-numeric: tabular-nums; }
.sub-pay-of { font-weight: 400; color: var(--text-3); }
.sub-pay-sum { font-family: var(--font-display); font-weight: 700; font-size: var(--fs-21); color: var(--mint); font-variant-numeric: tabular-nums; }
.sub-pay-bar { height: 6px; border-radius: var(--radius-pill); background: var(--surface-3); overflow: hidden; }
.sub-pay-bar > span { display: block; height: 100%; border-radius: inherit; background: var(--grad); }

.sub-card-flag {
  font-size: var(--fs-13); color: var(--text-2);
  background: var(--amber-a14); border-radius: var(--radius-sm); padding: var(--sp-3);
}

@media (max-width: 560px) {
  .sub-card-cover { width: 84px; }
  .sub-metrics { gap: var(--sp-3) var(--sp-4); }
  .sub-metrics dd { font-size: var(--fs-16); }
}

/* Обложка ролика в списке заданий креатора: узкая вертикаль 9:16 рядом с
   названием — по ней задание опознаётся быстрее, чем по тексту. */
.dt-task-with-cover { display: flex; gap: var(--sp-3); align-items: flex-start; }
.dt-task-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dt-cover {
  width: 36px; flex-shrink: 0; aspect-ratio: 9 / 16; object-fit: cover;
  border-radius: var(--radius-xs); border: 1px solid var(--border); background: var(--surface-2);
}

/* База отсчёта просмотров в карточке ролика. Не предупреждение и не ошибка —
   пояснение к арифметике, поэтому спокойный фон, а не жёлтый. */
.sub-card-base {
  font-size: var(--fs-13); color: var(--text-2);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: var(--sp-3); margin-top: var(--sp-2);
}
.sub-card-base b { color: var(--text); font-variant-numeric: tabular-nums; }

/* ============================================================================
   26. ПОЯСНЕНИЯ К ПОЛЯМ ФОРМЫ (значок с всплывающей подсказкой)
   ----------------------------------------------------------------------------
   Открывается по наведению, по фокусу с клавиатуры и по касанию. Без JS.
   Нативный title здесь не годится: на телефоне не открывается вовсе, на
   десктопе появляется с секундной задержкой, оформить текст нельзя.
   ============================================================================ */
.field-tip { position: relative; display: inline-flex; vertical-align: middle; margin-left: var(--sp-1); }
.field-tip-btn {
  width: 18px; height: 18px; padding: 0; border: 0; background: none; cursor: help;
  color: var(--text-3); display: inline-flex; align-items: center; justify-content: center;
  transition: color .15s ease;
}
.field-tip-btn:hover, .field-tip-btn:focus-visible { color: var(--violet-text-safe); }
.field-tip-btn svg { width: 100%; height: 100%; display: block; }

.field-tip-body {
  position: absolute; bottom: calc(100% + 8px); left: 50%;
  width: max-content; max-width: min(320px, 74vw);
  transform: translate(-50%, 4px);
  background: var(--surface-3); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: var(--sp-3); font-size: var(--fs-13); font-weight: 400; line-height: 1.5;
  text-transform: none; letter-spacing: normal; white-space: normal;
  box-shadow: var(--shadow-md);
  opacity: 0; visibility: hidden; pointer-events: none; z-index: 20;
  transition: opacity .16s var(--ease-out), transform .16s var(--ease-out), visibility .16s;
}
/* Появляется от собственной позиции, а не «из ничего»: сдвиг в 4px и никакого
   масштабирования — подсказку видят десятки раз за заполнение формы, и любое
   заметное движение здесь быстро начинает раздражать. */
.field-tip:hover .field-tip-body,
.field-tip-btn:focus-visible ~ .field-tip-body,
.field-tip:focus-within .field-tip-body {
  opacity: 1; visibility: visible; transform: translate(-50%, 0);
}
/* Хвостик — тот же фон и та же рамка, повёрнутый квадрат. */
.field-tip-body::after {
  content: ""; position: absolute; top: 100%; left: 50%; width: 9px; height: 9px;
  margin: -5px 0 0 -4.5px; transform: rotate(45deg);
  background: var(--surface-3);
  border-right: 1px solid var(--border-strong); border-bottom: 1px solid var(--border-strong);
}

/* Вариант «снизу» — когда сверху не помещается: у полей в верхней части
   экрана подсказка уходила под липкую шапку кабинета. Переворот ставит JS,
   потому что без замера места это не решается: CSS не знает, где сейчас
   находится элемент относительно окна. */
.field-tip-body.is-below {
  bottom: auto; top: calc(100% + 8px);
  transform: translate(-50%, -4px);
}
.field-tip:hover .field-tip-body.is-below,
.field-tip-btn:focus-visible ~ .field-tip-body.is-below,
.field-tip:focus-within .field-tip-body.is-below { transform: translate(-50%, 0); }
.field-tip-body.is-below::after {
  top: auto; bottom: 100%; margin: 0 0 -5px -4.5px;
  border-right: 0; border-bottom: 0;
  border-left: 1px solid var(--border-strong); border-top: 1px solid var(--border-strong);
}

/* На узком экране подсказку привязываем не к значку, а к самому окну.
   Привязка к значку там неизбежно уезжает за край: значок может стоять где
   угодно по ширине, а подсказка шире половины экрана. Никакая арифметика
   выравнивания это не чинит — чинит отказ от неё. Стрелка при этом теряет
   смысл и убирается: она указывала бы в пустоту. */
@media (max-width: 640px) {
  .field-tip-body {
    position: fixed; inset: auto var(--sp-4) var(--sp-4) var(--sp-4);
    width: auto; max-width: none; transform: translateY(6px);
  }
  .field-tip:hover .field-tip-body,
  .field-tip-btn:focus-visible ~ .field-tip-body,
  .field-tip:focus-within .field-tip-body { transform: translateY(0); }
  .field-tip-body::after { display: none; }
}

/* ============================================================================
   27. ПРОМОКОД КРЕАТОРА
   Код называют вслух в ролике и печатают в сторис, поэтому показываем его
   крупно, моноширинным и с увеличенным межбуквенным просветом: так «0» не
   спутать с «O», а «1» с «I» — а спутают их именно при переписывании с экрана.
   ============================================================================ */
.promo-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); margin-top: var(--sp-2); }
.promo-code {
  font-family: var(--font-mono); font-size: var(--fs-21); font-weight: 700;
  letter-spacing: .08em; color: var(--text);
  background: var(--violet-a14); border: 1px solid var(--violet-a35);
  border-radius: var(--radius-sm); padding: var(--sp-2) var(--sp-4);
  cursor: pointer; user-select: all;
}
.promo-edit { display: flex; gap: var(--sp-2); align-items: stretch; flex-wrap: wrap; }
.promo-edit input {
  font-family: var(--font-mono); letter-spacing: .06em; text-transform: uppercase;
  max-width: 220px;
}

/* ============================================================================
   28. КОНТРАКТ ПЛАТФОРМЫ (служебная страница /nopablic/contract)
   Длинный документ, который читают подряд и по которому спорят. Поэтому:
   мера строки ограничена ~72 знаками (дальше глаз теряет начало следующей
   строки), таблицы прокручиваются внутри себя, а не растягивают страницу,
   схемы в блоках кода не переносятся — в них значим каждый пробел.
   ============================================================================ */
.contract-toc {
  display: flex; flex-wrap: wrap; gap: var(--sp-2);
  margin: var(--sp-4) 0 var(--sp-6);
}
.contract-toc a {
  font-size: var(--fs-13); text-decoration: none;
  color: var(--text-2); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3);
}
.contract-toc a:hover { color: var(--text); border-color: var(--border-strong); }

.contract-section { margin-bottom: var(--sp-6); scroll-margin-top: var(--sp-20); }
.contract-file {
  font-family: var(--font-mono); font-size: var(--fs-11);
  color: var(--text-3); letter-spacing: .04em; margin-bottom: var(--sp-3);
}

.md-body { max-width: 72ch; }
.md-body h1 { font-size: var(--fs-26); margin: 0 0 var(--sp-4); }
.md-body h2 { font-size: var(--fs-21); margin: var(--sp-8) 0 var(--sp-3); }
.md-body h3 { font-size: var(--fs-18); margin: var(--sp-6) 0 var(--sp-2); }
.md-body h4 { font-size: var(--fs-16); margin: var(--sp-5) 0 var(--sp-2); }
.md-body p { color: var(--text-2); margin: 0 0 var(--sp-4); }
.md-body ul, .md-body ol { color: var(--text-2); margin: 0 0 var(--sp-4) var(--sp-5); }
.md-body li { margin-bottom: var(--sp-2); }
.md-body strong { color: var(--text); }
.md-body hr { border: 0; border-top: 1px solid var(--border); margin: var(--sp-8) 0; }
.md-body blockquote {
  border-left: 2px solid var(--violet); padding-left: var(--sp-4);
  color: var(--text-2); margin: 0 0 var(--sp-4);
}
.md-body code {
  font-family: var(--font-mono); font-size: .875em;
  background: var(--surface-3); border-radius: 4px; padding: 1px 5px;
  color: var(--text);
}
.md-pre {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: var(--sp-4);
  overflow-x: auto; margin: 0 0 var(--sp-4);
}
.md-pre code {
  background: none; padding: 0; font-size: var(--fs-12);
  color: var(--text-2); white-space: pre; line-height: 1.45;
}

/* Таблицы контракта шире меры строки: им разрешено занять всю карточку и
   прокручиваться. Ширину задаёт содержимое, поэтому обёртка обязательна —
   без неё горизонтальную полосу получает вся страница. */
.md-table-wrap { max-width: none; overflow-x: auto; margin: 0 0 var(--sp-4); }
.md-table { width: 100%; border-collapse: collapse; font-size: var(--fs-13); }
.md-table th, .md-table td {
  text-align: left; vertical-align: top;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border);
}
.md-table th { color: var(--text); background: var(--surface-2); white-space: nowrap; }
.md-table td { color: var(--text-2); }
.md-table tr:last-child td { border-bottom: 0; }


/* ============================================================================
   29. СХЕМЫ КОНТРАКТА (Mermaid, рисуется в браузере)
   Пока скрипт не отработал, в блоке лежит исходник схемы — он должен читаться
   как текст, а не разъезжаться. После отрисовки внутри оказывается SVG, и
   правила ниже отменяют для него моноширинные отступы блока кода.
   ============================================================================ */
pre.mermaid {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: var(--sp-4);
  margin: 0 0 var(--sp-6); overflow-x: auto;
  font-family: var(--font-mono); font-size: var(--fs-12);
  color: var(--text-3); line-height: 1.45; white-space: pre;
  max-width: none;
}
/* data-processed Mermaid ставит сам после успешной отрисовки. */
pre.mermaid[data-processed] {
  padding: var(--sp-4) var(--sp-2); color: inherit; text-align: center;
}
/* Схема рисуется в НАТУРАЛЬНУЮ величину и прокручивается вбок. Mermaid ставит
   svg ширину 100% и max-width по размеру содержимого — на широкой схеме (машина
   состояний, десяток переходов) это ужимало её до нечитаемой ленты в пару
   сантиметров высотой. Лучше прокрутка, чем картинка, на которой ничего не
   разобрать. */
pre.mermaid svg { width: auto !important; max-width: none !important; height: auto; }


/* ============================================================================
   ПЕРЕКЛЮЧАЕМЫЕ БЛОКИ ФОРМЫ [data-only-kind] / [data-only-content]

   Форма задания перестраивается на ходу: у текстового поста нет длительности,
   у задания за лиды — окна выплат. Резкая перекомпоновка половины экрана
   читается как сбой, поэтому появление и уход анимируются.

   Схлопывание сделано через grid-template-rows: 0fr → 1fr, а не через
   max-height с выдуманным числом. Число всегда либо мало (длинный блок
   обрезается), либо велико (короткий блок «едет» лишние сотни миллисекунд, и
   анимация выглядит вязкой). Здесь браузер считает высоту сам.

   Сам факт скрытия остаётся за атрибутом hidden — он убирает блок из потока и
   из озвучки скринридера. CSS отвечает только за то, как выглядит переход.
   ============================================================================ */
/* Схлопывание высоты требует grid — и потому применяется ТОЛЬКО к блокам без
   собственной раскладки внутри. У строки с галочкой раскладка своя, flex-ряд:
   превращение её в grid раскидывало квадратик и подпись по разным строкам, и
   было непонятно, какой текст какой галочке принадлежит. Такие блоки
   анимируются одной прозрачностью — там и схлопывать нечего, высота в одну
   строку. */
[data-only-kind], [data-only-content] {
  transition: opacity .18s ease, transform .18s ease;
}
/* Исключения через :where(), а НЕ через :not(): у :where() вес нулевой, и
   правило остаётся легче, чем [data-only-kind][hidden]{display:none} ниже.
   С :not() вес поднимался до трёх селекторов, скрытое правило проигрывало по
   весу — и блоки с hidden продолжали рисоваться как grid. */
[data-only-kind]:where(:not(.checkbox-row):not(.radio-row)),
[data-only-content]:where(:not(.checkbox-row):not(.radio-row)) {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows .18s ease, opacity .18s ease, transform .18s ease;
}
[data-only-kind] > *, [data-only-content] > * { min-height: 0; }
/* hidden обязан побеждать: display:grid выше по силе, чем правило браузера
   [hidden]{display:none}, и без этой строки скрытый блок остаётся на экране —
   отключённым, но видимым. Ровно это и случилось при первой проверке. */
/* СКРЫТО ЗНАЧИТ СКРЫТО. !important здесь не грубость, а единственный способ
   не зависеть от порядка и веса соседних правил раскладки: в этом же блоке
   два раза подряд скрытие проигрывало — сперва по весу (:not подняло вес выше
   [hidden]), потом по порядку (.accordion-item[data-only-kind] стоит ниже и
   возвращал display:block скрытому разделу «Оплата»). Атрибут hidden означает
   ровно одно, и спорить с ним раскладке не о чем. */
[data-only-kind][hidden], [data-only-content][hidden] { display: none !important; }


/* Начальное состояние появления и конечное состояние ухода — одно и то же:
   блок схлопнут, прозрачен и чуть смещён вверх. */
.switch-entering, .switch-leaving {
  grid-template-rows: 0fr;
  opacity: 0;
  transform: translateY(-4px);
}
/* Уходящий блок не должен ловить клики и мешать соседям, пока доигрывает. */
.switch-leaving { pointer-events: none; }

/* Секция-аккордеон — не grid: у неё своя разметка заголовка и тела, и
   превращать её в grid значило бы сломать вёрстку ради анимации. Ей хватает
   прозрачности. */
.accordion-item[data-only-kind] { display: block; }
.accordion-item.switch-entering, .accordion-item.switch-leaving { grid-template-rows: none; }

@media (prefers-reduced-motion: reduce) {
  [data-only-kind], [data-only-content] { transition: none; }
}


/* Логотип бренда вместо буквы. Картинка вписывается в тот же квадрат, что и
   монограмма, — иначе строка профиля прыгает в зависимости от того, загрузил
   бизнес логотип или нет. contain, а не cover: логотип обрезать нельзя. */
.avatar-mono.brand-logo {
  background: var(--surface-2);
  object-fit: contain;
  padding: var(--sp-2);
}

/* Логотип в строке бренда на карточке каталога — маленький, чтобы не спорить
   с названием задания за внимание. */
.avatar-mono.tiny { width: 22px; height: 22px; font-size: var(--fs-12); padding: 2px; border-radius: var(--radius-sm); }
.avatar-mono.tiny.round { border-radius: 50%; }
.task-brand { display: flex; align-items: center; gap: var(--sp-2); }


/* ============================================================================
   УПАКОВКА ПЛОЩАДОК [data-platform-pack]

   У задания за лиды площадок бывает шесть. Шесть плашек подряд на карточке
   занимают три строки и вытесняют то, ради чего карточку открывают, — сами
   площадки при этом никто не перечитывает, достаточно понять «их много».

   Поэтому видны первые две, остальные — под счётчиком «+4». Раскрывается
   тремя способами, и все три обязательны: наведением (мышь), фокусом
   (клавиатура) и классом .open, который вешает клик (палец — на телефоне
   наведения не существует).
   ============================================================================ */
.platform-pack { position: relative; display: inline-flex; align-items: center; gap: var(--sp-1); }

.platform-pack-more {
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-2);
  border-radius: 999px; padding: 2px 9px; font-size: var(--fs-12); font-weight: 600;
  font-family: var(--font-mono); cursor: pointer; line-height: 1.6;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.platform-pack-more:hover, .platform-pack-more:focus-visible {
  background: var(--violet-a14); color: var(--violet); border-color: var(--violet-a35);
}

/* Скрытые плашки лежат карточкой над остальным содержимым: раздвигать разметку
   при наведении нельзя — соседние элементы прыгали бы под курсором. */
.platform-pack-rest {
  position: absolute; z-index: 30; top: calc(100% + 6px); left: 0;
  display: flex; flex-wrap: wrap; gap: var(--sp-1);
  min-width: max-content; max-width: 260px;
  padding: var(--sp-2); border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); box-shadow: 0 12px 28px -12px rgba(0,0,0,.5);
  opacity: 0; visibility: hidden; transform: translateY(-4px);
  transition: opacity .15s ease, transform .15s ease, visibility .15s;
}
.platform-pack:hover .platform-pack-rest,
.platform-pack:focus-within .platform-pack-rest,
.platform-pack.open .platform-pack-rest {
  opacity: 1; visibility: visible; transform: translateY(0);
}

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

/* ============================================================================
   КАТАЛОГ ДЛЯ ГОСТЯ: витрина и баннерные места (03.09.2026)
   ============================================================================
   Незалогиненный видит первые карточки целиком, остальные — за матовым
   стеклом. Карточки под стеклом НАСТОЯЩИЕ, те же самые: человек регистрируется
   ради того, что увидел, и обязан получить именно это.

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

.catalog-gate {
  position: relative;
  margin-top: var(--sp-5);
}

/* Маска гасит низ стопки в фон страницы: без неё стекло обрывается ровной
   линией и выглядит обрезком, а не продолжением каталога. */
.catalog-gate-stack {
  -webkit-mask-image: linear-gradient(180deg, #000 0, rgba(0, 0, 0, .58) 46%, transparent 92%);
  mask-image: linear-gradient(180deg, #000 0, rgba(0, 0, 0, .58) 46%, transparent 92%);
  max-height: 640px;
  overflow: hidden;
  pointer-events: none;
  user-select: none;
}

/* Ступени глубины. Первая строка ещё почти читается — она обещает, что дальше
   такие же настоящие задания; дальше стекло матовеет. */
.catalog-gate-stack .task-card { transition: none; }
.catalog-gate-stack .grid > *:nth-child(-n+2) { filter: blur(2.5px); }
.catalog-gate-stack .grid > *:nth-child(n+3) { filter: blur(6px); }
.catalog-gate-stack .grid > *:nth-child(n+5) { filter: blur(10px); }

/* Панель лежит В ПОТОКЕ и подтянута наверх отрицательным полем, а не висит
   абсолютом: при одной спрятанной карточке абсолютная панель вылезала бы за
   стопку, а так высота сходится при любом их числе. */
.catalog-gate-panel {
  position: relative;
  z-index: 2;
  width: min(560px, 100%);
  margin: -160px auto 0;
  padding: var(--sp-6);
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}

.catalog-gate-panel h2 {
  margin: 0 0 var(--sp-2);
  font-size: 1.35rem;
  line-height: 1.25;
  text-wrap: balance;
}

.catalog-gate-panel p {
  margin: 0 0 var(--sp-5);
  color: var(--text-2);
  font-size: .95rem;
}

.catalog-gate-actions {
  display: flex;
  gap: var(--sp-3);
  justify-content: center;
  flex-wrap: wrap;
}

/* Единственный авторский момент на экране: панель приезжает снизу один раз.
   Всё остальное появляется без движения — иначе страница дёргается целиком. */
@media (prefers-reduced-motion: no-preference) {
  .catalog-gate-panel {
    animation: catalog-gate-in .5s var(--ease-out) both;
  }
}

@keyframes catalog-gate-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 720px) {
  .catalog-gate-stack { max-height: 420px; }
  .catalog-gate-panel { margin-top: -120px; padding: var(--sp-5); }
}

.catalog-gate-signin {
  margin: var(--sp-3) 0 0;
  color: var(--text-3);
  font-size: .88rem;
}

.catalog-gate-signin a {
  color: var(--text);
  text-decoration: underline;
  /* Подчёркивание по умолчанию липнет к буквам и режет выносные элементы
     кириллицы (р, у, д). Это ровно та браузерная поверхность, которую
     дизайн-система обязана назначить сама. */
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--border-strong);
}

.catalog-gate-signin a:hover { text-decoration-color: var(--text); }

/* ---- Баннерное место ---- */
/* Намеренно не похоже на карточку задания: карточка обещает работу и оплату,
   баннер — переход. Одинаковый вид означал бы, что человек кликает в одно, а
   попадает в другое. */
.catalog-banner {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  background: var(--violet-a14);
  border: 1px solid var(--violet-a35);
  border-radius: var(--radius);
  color: var(--text);
  text-decoration: none;
  transition: background .18s var(--ease-out), border-color .18s var(--ease-out);
}

.catalog-banner:hover,
.catalog-banner:focus-visible {
  background: var(--violet-a20);
  border-color: var(--violet);
  text-decoration: none;
}

.catalog-banner-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-width: 0;
}

.catalog-banner-title {
  font-family: var(--font-display);
  font-size: 1rem;
  line-height: 1.3;
}

.catalog-banner-text {
  color: var(--text-2);
  font-size: .88rem;
  line-height: 1.45;
}

.catalog-banner-cta {
  flex: none;
  font-weight: 600;
  color: var(--violet-text-safe);
  white-space: nowrap;
}

:root[data-theme="light"] .catalog-banner-cta { color: var(--violet); }

@media (max-width: 560px) {
  .catalog-banner {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-3);
  }
}
