/* =====================================================================
   ОГЛАВЛЕНИЕ

     1. Токены и тема
     2. Каркас страницы
     3. Шапка сайта
     4. Общие компоненты          кнопки, чипы, выноски, списки, поиск,
                                  поле текста, диалог
     5. Мастер методики           степпер и навигация шагов
     6. Шаг 1. Технологии
     7. Шаг 3. Модель нарушителя
     8. Шаг 4. Способы реализации
     9. Шаг 5. Выбор угроз
    10. Шаг 6. Сценарии
    11. Шаг 7. Меры защиты и отчёт
    12. Карточка системы
    12а. Предпросмотр документа
    12б. Сравнение систем
    12в. Конструктор техпроекта
    13. Мои системы, вход, регистрация
    14. Статические страницы      главная, справочники, публичный каталог,
                                  классы средств защиты
    15. Печать
    16. Адаптив, не привязанный к компоненту

   КАК СЮДА ДОПИСЫВАТЬ

   Правило одно: класс живёт в секции той страницы, для которой придуман,
   и применяется только там. Половина этого файла когда-то накопилась в
   одной секции просто потому, что дописывать в конец быстрее, чем искать
   своё место, — и вместе с этим приехала ошибка, ради которой правило и
   записано: `.tech-chip` из шага «Технологии» (отметка = «этой технологии
   в системе НЕТ», поэтому красная) применили в выгрузке документа, где
   отметка значит ровно обратное, и все семь глав выглядели как ошибки.

   Нужен тот же вид в другом месте — не переиспользуйте класс чужой
   страницы: либо поднимите его в секцию 4 «Общие компоненты», сняв с него
   смысл конкретного шага, либо заведите свой. Медиазапрос компонента
   пишется сразу под ним (см. шапку сайта), а не в секцию 15: туда идёт
   только то, что не принадлежит ни одному компоненту.
   ===================================================================== */

/* =====================================================================
   1. ТОКЕНЫ И ТЕМА
   ===================================================================== */

/* ---------------------------------------------------------------------
   Токены. Тёмная «диспетчерская» тема: приборный тёмно-синий фон,
   янтарный акцент (цвет предупреждающей маркировки на промышленных
   объектах — уместен для темы КИИ), моноширинный шрифт для кодов мер.
   Шрифты — системные, без внешних CDN: инструмент может разворачиваться
   в закрытом контуре без доступа в интернет.
   --------------------------------------------------------------------- */
:root {
    --bg: #12161b;
    --surface: #1a2027;
    --surface-2: #212832;
    --border: #2a323c;
    --text: #e8ecef;
    --text-dim: #93a1ac;
    --accent: #e8a33d;
    --accent-dim: #b87f2e;
    --accent-ink: #241a08;
    --danger: #e5636b;
    --good: #4cae7d;

    --font-body: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, "JetBrains Mono", "SF Mono", Consolas, "Liberation Mono", monospace;

    --radius: 6px;

    /* нейтральный цвет для «справочных» чипов (код меры, источник) —
       отдельно от акцента, который зарезервирован под статусы, требующие
       внимания (сверить/доп./предположительно) */
    --neutral-chip: #7fb4d4;
    --neutral-chip-dim: #3d5f75;
    --level-3: #e08a3d;   /* Н3: между акцентом (Н2) и опасностью (Н4) */
}

:root[data-theme="light"] {
    --bg: #f4f5f7;
    --surface: #ffffff;
    --surface-2: #eef0f3;
    --border: #d7dce1;
    --text: #1c2126;
    --text-dim: #5b6772;
    --accent: #b87119;
    --accent-dim: #96590f;
    --accent-ink: #fff6e8;
    --danger: #c8323b;
    --good: #2f8f5c;
    --neutral-chip: #2e6a8c;
    --neutral-chip-dim: #a9c6d8;
    --level-3: #a8500f;
}

@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) {
        --bg: #f4f5f7;
        --surface: #ffffff;
        --surface-2: #eef0f3;
        --border: #d7dce1;
        --text: #1c2126;
        --text-dim: #5b6772;
        --accent: #b87119;
        --accent-dim: #96590f;
        --accent-ink: #fff6e8;
        --danger: #c8323b;
        --good: #2f8f5c;
        --neutral-chip: #2e6a8c;
        --neutral-chip-dim: #a9c6d8;
        --level-3: #a8500f;
    }
}

* { box-sizing: border-box; }

html { color-scheme: dark; }
:root[data-theme="light"] { color-scheme: light; }
@media (prefers-color-scheme: light) {
    html:not([data-theme="dark"]) { color-scheme: light; }
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-body);
    line-height: 1.5;
}

a { color: var(--accent); }

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

/* Запасное значение отступа для якорей: шапка приклеена (position: sticky),
   и без него цель перехода встаёт ровно под неё. Настоящее значение считает
   anchor_offset.js по реальной высоте шапки — она переносится по содержимому
   и на узком экране втрое выше, чем на широком. Здесь — высота шапки на
   десктопе, то есть верный ответ для случая, когда JS не отработал. */
/* Место под полосу прокрутки резервируется всегда. Списки на шаге «Угрозы»
   и в справочнике фильтруются на лету: страница то длиннее окна, то короче,
   полоса появляется и исчезает — и всё содержимое дёргается вбок на её
   ширину при каждом нажатии фильтра. */
html { scroll-padding-top: 76px; scrollbar-gutter: stable; }

/* Ссылка «к содержимому» — первое, что получает фокус на любой странице.
   Перед содержимым стоит шапка на десять ссылок, и с клавиатуры их
   приходилось проходить заново на каждой из 490 страниц справочника.

   Не `display: none` и не нулевой размер: скрытое таким образом ссылка
   выпадает из порядка обхода и не получает фокус вовсе — то есть выглядит
   сделанной и не работает. Поэтому она на месте и просто уведена за верхний
   край окна, а по фокусу возвращается.

   z-index выше шапки (10): шапка приклеена, и без этого ссылка всплывала бы
   под ней. */
.skip-link {
    position: fixed;
    top: 0;
    left: 12px;
    z-index: 20;
    transform: translateY(-120%);
    background: var(--accent);
    color: var(--accent-ink);
    padding: 10px 16px;
    border-radius: 0 0 var(--radius) var(--radius);
    font-size: 14px;
    text-decoration: none;
    -webkit-user-select: none;
    user-select: none;
}
.skip-link:focus { transform: translateY(0); }

.page { max-width: 980px; margin: 0 auto; padding: 28px 20px 60px; }

.panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 24px;
}
.panel__head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}
.panel__actions { display: flex; gap: 8px; flex-wrap: wrap; }

h1 { font-size: 22px; margin: 0 0 4px; }
.section-title {
    font-size: 15px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-dim);
    border-top: 1px solid var(--border);
    margin: 28px 0 14px;
    padding-top: 20px;
}

/* Отступ и линейка сверху нужны, когда заголовок отделяет раздел от текста
   выше. Если он открывает панель, отделять нечего: остаются 48px пустоты
   и линия вплотную под рамкой панели. */
.panel > .section-title:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.muted { color: var(--text-dim); font-size: 13.5px; margin: 0; }
.empty-state { color: var(--text-dim); padding: 24px 0; }

.footer {
    max-width: 980px;
    margin: 0 auto;
    padding: 0 20px 40px;
    color: var(--text-dim);
    font-size: 12px;
}
.footer p { margin: 0 0 6px; }
.footer__links a { color: var(--text-dim); text-decoration: none; border-bottom: 1px solid var(--border); }
.footer__links a:hover { color: var(--accent); border-bottom-color: var(--accent-dim); }

/* =====================================================================
   3. ШАПКА САЙТА
   ===================================================================== */

.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* Перенос по содержимому, а не по брейкпоинту: ширина меню зависит от
       того, кто смотрит. Любое фиксированное число обслуживало бы только
       одного из них: на 700px у вошедшего пункты ломались пополам, а шапка
       ниже ~500px просто уезжала за край окна. Разброс стал меньше с тех пор,
       как личное уехало под «•••» (аккаунт теперь одной ширины у обычного
       пользователя и у суперпользователя), но не исчез — у анонима на месте
       «Мои системы» стоит «Войти». */
    flex-wrap: wrap;
    gap: 8px 16px;
    padding: 14px 28px;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
    position: sticky;
    top: 0;
    z-index: 10;
}
.topbar__brand { display: flex; align-items: center; gap: 12px; text-decoration: none; color: inherit; }
/* Знак сайта. Была плашка с буквами «МУ», набранная моноширинным шрифтом;
   теперь тот же файл, что и favicon (mapper/logo.py) — чтобы вкладка,
   выдача и шапка показывали один и тот же знак, а не три похожих. Плашку и
   скругление рисует сам SVG, поэтому здесь только размер. */
.topbar__mark {
    width: 28px;
    height: 28px;
    display: block;
    flex-shrink: 0;
}
.topbar__text { display: flex; flex-direction: column; line-height: 1.25; }
.topbar__sub { color: var(--text-dim); font-size: 12.5px; }
.topbar__nav { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 22px; }

/* Две группы: каталог и аккаунт. Разделены не рамкой контейнера, а тонкой
   чертой перед аккаунтом — рамка вокруг каждой группы превратила бы шапку в
   две панели, а нужен один ряд с паузой посередине. Черта рисуется границей,
   а не псевдоэлементом, чтобы исчезать вместе с переносом группы на свою
   строку: на узком экране вертикальная линия в начале строки читается как
   обрезанный элемент. */
.topbar__group { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 18px; }
/* margin-left: auto держит инвариант, на который рассчитана панель меню
   ниже: группа аккаунта заканчивает шапку справа НА ЛЮБОЙ строке, включая
   свою собственную после переноса. Без него шестой пункт каталога переносил
   группу на новую строку к левому краю, и панель, растущая влево от «•••»,
   уходила за окно на -35px (браузерная проверка «все пункты меню в окне»,
   700px). Раньше инвариант держался случайно — пунктов было пять и группа
   не переносилась. */
.topbar__group--account { border-left: 1px solid var(--border); padding-left: 22px; margin-left: auto; }
@media (max-width: 860px) {
    .topbar__group--account { border-left: 0; padding-left: 0; }
}
.topbar__nav a,
.topbar__nav .link-button {
    text-decoration: none; font-size: 14px;
    /* цель указателя не меньше 24px даже мышью (WCAG 2.5.8); на высоту шапки
       не влияет — пункты и так центрируются в ней */
    display: inline-flex; align-items: center; min-height: 24px;
}
/* Меню аккаунта. Свой компонент, а не .system-menu с карточки системы:
   тот принадлежит карточке, знает про её высоту строки и подписи-пояснения
   под пунктами, и переиспользование класса чужой страницы — ровно то, из-за
   чего .tech-chip уехал в выгрузку документа (см. оглавление файла).

   Панель прижата к правому краю: шапка справа заканчивается, и панель,
   растущая влево, никогда не вылезет за окно — в отличие от левого края,
   где на 900px её вынесло бы за экран.

   Закрытие по клику мимо и по Escape — в topbar_menu.js; <details> сам этого
   не умеет, а шапка липкая, и забытое открытым меню висит поверх текста. */
.topbar-menu { position: relative; display: inline-flex; }
.topbar-menu summary {
    list-style: none; cursor: pointer; min-height: 24px;
    display: inline-flex; align-items: center; color: var(--text-dim);
}
.topbar-menu summary::-webkit-details-marker { display: none; }
.topbar-menu summary:hover, .topbar-menu[open] summary { color: var(--accent); }
.topbar-menu__dots { font-size: 15px; font-weight: 700; line-height: 1; letter-spacing: 2px; }
.topbar-menu__panel {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 20; min-width: 190px;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 10px 14px; display: flex; flex-direction: column; gap: 9px;
    box-shadow: 0 10px 26px rgba(0, 0, 0, .35);
}
/* Имя пользователя было подписью на кнопке выхода («Выйти (maxtor)») и вместе
   с ней уехало под точки. Здесь оно заголовком панели: кто вошёл — это ответ
   на вопрос, а не действие, и место ему не на кнопке. */
.topbar-menu__who {
    font-size: 12px; color: var(--text-dim); padding-bottom: 8px;
    border-bottom: 1px solid var(--border); word-break: break-all;
}
/* Списки разделов слева от шапки. Отличаются от меню аккаунта тремя вещами.

   Подпись словом, а не точками: «Справочники» и «Инструменты» — это разделы
   сайта, по которым ходят, и они обязаны читаться так же, как читалась
   прежняя строка из шести ссылок. Цвет поэтому ссылочный, а не приглушённый.

   Панель растёт вправо от кнопки (left, а не right): эти меню стоят у левого
   края шапки, и панель, растущая влево, ушла бы за окно.

   Уголок — треугольник рамками, а не знак «▾»: у системных шрифтов Windows,
   macOS и Linux он разной ширины и по-разному сидит на базовой линии, и
   подпись съезжала бы от машины к машине. Выделение снято: по кнопке
   нажимают, а не читают её, и промах оставлял на подписи синюю полосу. */
.topbar-menu--start summary {
    color: var(--accent); font-size: 14px; gap: 6px;
    -webkit-user-select: none; user-select: none;
}
.topbar-menu--start summary:hover { color: var(--accent-dim); }
.topbar-menu__caret {
    width: 0; height: 0; flex-shrink: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid currentColor;
}
.topbar-menu--start[open] .topbar-menu__caret { transform: rotate(180deg); }
.topbar-menu--start .topbar-menu__panel { left: 0; right: auto; }
/* Пункт с пояснением: название и строка о том, что за ним стоит. Без
   пояснения «Инструменты» приходится открывать по очереди, чтобы вспомнить,
   какой из трёх мастеров нужен. */
.topbar-menu__panel--wide { min-width: 320px; gap: 4px; padding: 8px; }
/* Селектор с `.topbar__nav`, а не просто `.topbar-menu__panel--wide a`.
   Шапка задаёт своим ссылкам `display: inline-flex; align-items: center`
   дважды — в общем правиле и ещё раз в мобильных правках, — и одного класса
   не хватает: на узком экране название с пояснением вставали по центру
   панели, потому что правило из медиазапроса стоит в файле ниже. */
.topbar__nav .topbar-menu__panel--wide a {
    display: flex; flex-direction: column; align-items: stretch; gap: 1px;
    padding: 7px 8px; border-radius: var(--radius);
    text-decoration: none; color: var(--text); font-size: 13.5px; font-weight: 600;
}
.topbar__nav .topbar-menu__panel--wide a:hover { background: var(--surface-2); color: var(--accent); }
.topbar__nav .topbar-menu__panel--wide a span {
    font-size: 11.5px; font-weight: 400; line-height: 1.45; color: var(--text-dim);
}

.theme-toggle {
    background: transparent; border: 1px solid var(--border); border-radius: 999px;
    width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer; color: var(--text); font-size: 14px; padding: 0;
}
.theme-toggle:hover { border-color: var(--accent-dim); color: var(--accent); }

/* Узкий экран. Перенос меню под бренд обеспечивает flex-wrap выше — здесь
   только то, что от переноса не следует: убрать справочный подзаголовок,
   увеличить тач-таргеты, отцепить шапку от прокрутки.

   Медиазапрос стоит здесь, рядом со своим компонентом, а не в общем блоке
   мобильных правок внизу файла: дописывание «в конец» — ровно то, из-за чего
   одна секция этого файла разрослась до половины его объёма. */
@media (max-width: 640px) {
    .topbar { padding: 10px 14px; }
    /* подзаголовок с перечнем приказов — справочный, на узком экране он
       занимает целую строку и вытесняет то, по чему кликают */
    .topbar__sub { display: none; }
    .topbar__brand { flex: 1 1 100%; }
    .topbar__nav {
        flex: 1 1 100%; flex-wrap: wrap; justify-content: center; gap: 4px 16px;
        border-top: 1px solid var(--border); padding-top: 6px;
    }
    /* Группы на узком экране переносятся и центрируются каждая сама по себе:
       иначе каталог из четырёх пунктов и аккаунт из двух складываются в одну
       рваную строку, где «Войти» дописано к «Средствам защиты». */
    .topbar__group { flex: 1 1 100%; justify-content: center; gap: 4px 16px; }
    /* тач-таргеты: ссылки в шапке — самые мелкие цели на странице */
    .topbar__nav a,
    .topbar__nav .link-button,
    .topbar-menu summary { display: inline-flex; align-items: center; min-height: 36px; }
    .topbar__nav .theme-toggle { width: 36px; height: 36px; }
    /* Панель раскрывается во всю ширину ряда меню, а не рядом с кнопкой: на
       узком экране пункты центрированы, и панель, растущая от своей кнопки,
       уезжает за край окна в одну сторону или в другую.

       Точка отсчёта — .topbar__nav, и она обязана быть позиционированной.
       Раньше здесь стояло только `.topbar-menu { position: static }`, а
       позиционированного предка не оставалось вовсе: панель отсчитывала
       `top: calc(100% + 8px)` от начального содержащего блока и уезжала на
       высоту окна вниз. Проверка ширин этого не видела — она меряла левый и
       правый край, а уехало по вертикали. */
    .topbar__nav { position: relative; }
    .topbar-menu { position: static; }
    .topbar-menu__panel { left: 0; right: 0; min-width: 0; }
    /* Тем же весом селектора, что и правило для широкого экрана
       (.topbar-menu--start .topbar-menu__panel { right: auto }): одного
       класса здесь не хватает, оно перебьёт правило выше и панель снова
       вырастет по содержимому за край окна. */
    .topbar-menu--start .topbar-menu__panel { left: 0; right: 0; }
    /* Шапка в две строки — это 100px, и липкой она забирала бы их у каждого
       экрана насовсем. Шаги методики — длинные формы, навигация посреди них
       не нужна, а место нужно. Тот же приём, что у .report-toc ниже. */
    .topbar { position: static; }
}

/* =====================================================================
   4. ОБЩИЕ КОМПОНЕНТЫ
   Используются на нескольких страницах. Всё, что сюда попадает, обязано
   быть свободно от смысла конкретного шага методики.
   ===================================================================== */

.btn {
    font: inherit;
    font-size: 13.5px;
    font-weight: 600;
    border-radius: var(--radius);
    padding: 9px 16px;
    border: 1px solid transparent;
    cursor: pointer;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.btn--primary { background: var(--accent); color: var(--accent-ink); }
.btn--primary:hover { background: var(--accent-dim); }
.btn--ghost { background: transparent; border-color: var(--border); color: var(--text); }
.btn--small { padding: 3px 10px; font-size: 12px; }
.btn--ghost:hover { border-color: var(--accent-dim); color: var(--accent); }

/* Недоступное действие. Печатается <span>, а не отключённой ссылкой: ссылка
   без href всё равно остаётся в порядке обхода и открывается по Enter, а
   `disabled` у <a> в HTML нет вовсе. Курсор и aria-disabled говорят об одном
   и том же двум разным читателям. */
.btn.is-disabled {
    opacity: 0.5;
    cursor: not-allowed;
    border-color: var(--border);
    color: var(--text-dim);
}
.btn.is-disabled:hover { border-color: var(--border); color: var(--text-dim); }

/* Кнопка скачивания, пока документ собирается (download_feedback.js):
   надпись «Готовим файл…» и вращающееся кольцо. Клики гасит скрипт, а не
   pointer-events, чтобы фокус и Escape работали как обычно. */
.btn.is-busy { cursor: progress; opacity: 0.75; }
.btn.is-busy::before {
    content: "";
    width: 0.85em;
    height: 0.85em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: btn-spin 0.8s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }

/* summary — тоже интерактивный элемент и фокусируется с клавиатуры; на
   карточке системы их пять (переименование, меню действий, приказы, чипы
   технологий, охват методики), и без этой строки они молча теряли фокус.
   textarea попал сюда позже и по той же причине: два поля свободного текста
   (шаги «Последствия» и «Сценарии») были единственным, что фокусировалось
   без видимой рамки. */
.btn:focus-visible, a:focus-visible, input:focus-visible, summary:focus-visible,
select:focus-visible, textarea:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.inline-form { display: inline-flex; }

/* Подпись кнопки не выделяется мышью — то же правило, что у подписи при
   флажке ниже, и по той же причине: по кнопке кликают, а не читают её, и
   промах мимо цели или двойной клик оставлял на подписи синее выделение.
   Заодно отсюда убраны стрелки в подписях («Далее: Сценарии →»): направление
   подписывается словом, а стрелка в кнопке — украшение, которое вдобавок
   уезжает в буфер вместе с текстом.

   Таблетки степпера перечислены здесь же, а не в разделе 5: правило про
   поведение при клике, общее для всего, что нажимают, — как и строка
   :focus-visible выше, где по той же причине стоят рядом .btn, a и summary. */
.btn,
.wizard__item a,
.wizard__item button {
    -webkit-user-select: none;
    user-select: none;
}

/* Подпись при флажке не выделяется мышью. По ней кликают, чтобы отметить, а
   не чтобы прочитать: двойной клик и любое протягивание оставляли на тексте
   синее выделение поверх только что отмеченного пункта. Правило одно на все
   шаги — списки угроз, технологий, последствий, глав документа. Цена
   известная: текст такого пункта больше не выделишь для копирования; коды
   угроз копируются со страницы самой угрозы в справочнике. */
label:has(> input[type="checkbox"]),
label:has(> input[type="radio"]) {
    -webkit-user-select: none;
    user-select: none;
}

select {
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
    color: var(--text); padding: 8px 10px; font-size: 13.5px; font-family: var(--font-body);
}
.gap-level-form { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 10px 0 18px; }
.gap-level-form label { font-size: 13px; color: var(--text-dim); }

.chip {
    font-family: var(--font-mono);
    font-size: 11.5px;
    border-radius: 4px;
    padding: 2px 6px;
    border: 1px solid var(--border);
    white-space: nowrap;
}
.chip--muted { color: var(--text-dim); }
.chip--warn { color: var(--danger); border-color: var(--danger); }

/* нейтральный цвет: код меры — это ссылка на факт, а не статус, требующий
   внимания — амбер (--accent) оставлен только за chip--guess/gap/pending,
   чтобы они реально выделялись в плотных списках мер */
.chip--code { color: var(--neutral-chip); border-color: var(--neutral-chip-dim); margin-right: 8px; }

/* Статус меры: «сверить» — аналог подобран автоматически и не подтверждён,
   «доп.» — аналога нет вовсе. Придуманы для отчёта, но главная объясняет
   ровно эти два значка, показывая их вживую, — значение одно, значит и
   класс один, а не скопированный. */
.chip--pending { color: var(--neutral-chip); border-color: var(--neutral-chip-dim); margin-right: 8px; }
.chip--gap { color: var(--accent); border-color: var(--accent-dim); margin-right: 8px; }

.callout {
    border: 1px solid var(--accent-dim);
    background: rgba(232, 163, 61, 0.08);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin-bottom: 20px;
}
.callout strong { display: block; margin-bottom: 6px; color: var(--accent); font-size: 14px; }
.callout p { margin: 0 0 10px; font-size: 13px; color: var(--text-dim); }

/* Варианты выноски. Лежали в секции 10 «Меры защиты», хотя ими пользуются
   ещё три страницы (тактики, шаги «Нарушитель» и «Сценарии») и главная.
   Порядок здесь значащий: .callout--pending strong перебивает
   .callout strong той же специфичностью и обязан идти ниже него. */
.callout--pending { border-color: var(--neutral-chip-dim); background: rgba(127, 180, 212, 0.08); }
.callout--pending strong { color: var(--neutral-chip); }
.callout--warn { border-color: var(--danger); background: rgba(229, 99, 107, 0.08); }
.callout--warn strong { color: var(--danger); }
.callout em { font-style: normal; color: var(--text); }

/* Список «значок + строка текста». Заведён для мер в отчёте, но одинаково
   нужен охвату, «Об авторе», шагу «Нарушитель» и главной. Уточнение
   .measure-list li.hint-row живёт в секции 10 и обязано остаться ниже. */
.measure-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: 13.5px; }
.measure-list li { display: flex; align-items: baseline; }
.measure-list--gap { margin-top: 8px; }

/* Маркированный список внутри прозы. Заведён вместе с редполитикой: три
   и более однородных пункта выносятся из абзаца в список, и такой список
   нужен на десятке страниц — поэтому он здесь, а не в секции конкретной
   страницы. Не .measure-list: у той `li` — флекс со значком в первой
   колонке, здесь же нужен обычный маркер и обычный перенос строки. */
.prose-list { margin: 0 0 14px; padding-left: 20px; line-height: 1.55; }
.prose-list li { margin-bottom: 5px; }
.prose-list li:last-child { margin-bottom: 0; }

/* Два .muted подряд слипались: у .muted нулевой отступ, потому что чаще
   всего это одиночная подпись под заголовком. Разбить сплошной абзац на
   два — норма редполитики, так что соседний абзац (и список после него)
   отделяются здесь. Правило обязано идти ниже .prose-list: шорткат
   `margin` в нём сбрасывает margin-top. */
.muted + .muted,
.muted + .prose-list { margin-top: 8px; }

/* Плитка с числом. Заведена для страницы «Охват»; теперь теми же плитками
   открывается главная — цифры каталога на обеих страницах должны читаться
   как одно и то же, а не как два разных оформления. */
.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 8px; }
.stat-card {
    border: 1px solid var(--border); border-radius: var(--radius);
    padding: 14px; background: var(--surface-2); text-align: center;
}
.stat-card strong { display: block; font-size: 22px; color: var(--accent); }
.stat-card span { font-size: 11.5px; color: var(--text-dim); }
@media (max-width: 640px) { .stat-grid { grid-template-columns: repeat(2, 1fr); } }

.search {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}
.search input[type="search"] {
    /* min-width: 0 обязателен: у flex-элемента минимум по умолчанию — его
       собственный min-content, а у поля ввода это его size, то есть двадцать
       символов. Поле поэтому не сжимается ниже ~200px, и на 320px форма
       (262px) не вмещает его вместе с кнопкой. Вылезет ли при этом страница,
       решают метрики шрифта: на машине разработчика влезало, на раннере CI
       страница ехала вбок на 22px. */
    flex: 1;
    min-width: 0;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    padding: 10px 12px;
    font: inherit;
    font-size: 14px;
}
.search__count { color: var(--text-dim); font-size: 12.5px; white-space: nowrap; }

/* Поле свободного текста. Здесь, в общих компонентах, а не в секции шага:
   таких полей на сайте два и они в разных шагах — «другие последствия»
   (шаг 2) и обоснование сценария (шаг 6). Оба были набором inline-стилей
   прямо в разметке, почти одинаковым, но не совсем (14px против 13px,
   9/11 против 8/10) — расхождение уже началось, просто мелкое.

   font-family задан явно: textarea единственный из полей не наследует шрифт
   страницы, браузер ставит ему моноширинный. font: inherit здесь не годится
   — он утащил бы и размер строки родителя, разный в двух местах.

   resize только по вертикали: горизонтальная растяжка выносит поле за
   пределы карточки угрозы, а вернуть его мышью обратно точно в край нельзя. */
.text-field {
    width: 100%;
    /* Отступ сверху — в компоненте, а не в разметке: в обоих местах поле
       стоит под тем, что его объясняет (подпись на шаге 2, список техник на
       шаге 6), и вплотную к нему читается как их продолжение. Было 6px и
       8px — разными они стали не по замыслу, а потому что писались порознь. */
    margin-top: 8px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    padding: 9px 11px;
    font-family: var(--font-body);
    font-size: 14px;
    line-height: 1.5;
    resize: vertical;
}
.text-field::placeholder { color: var(--text-dim); }

/* кнопка «Выйти» в топбаре — визуально ссылка, семантически кнопка (logout
   требует POST) */
.link-button {
    background: none; border: none; padding: 0; margin: 0;
    font: inherit; font-size: 14px; color: var(--accent); cursor: pointer;
}
.link-button:hover { color: var(--accent-dim); }

/* флеш-сообщения (django.contrib.messages) */
.flash-messages { list-style: none; margin: 0 0 18px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.flash-messages__item {
    padding: 10px 14px; border-radius: var(--radius); font-size: 13.5px;
    border: 1px solid var(--border); background: var(--surface-2);
}
.flash-messages__item--success { border-color: var(--good); color: var(--good); }
.flash-messages__item--error { border-color: var(--danger); color: var(--danger); }

dialog.confirm-dialog {
    background: var(--surface); color: var(--text); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 20px; max-width: 380px; width: calc(100% - 40px);
}
dialog.confirm-dialog::backdrop { background: rgba(0, 0, 0, 0.55); }
.confirm-dialog__title { margin: 0 0 8px; font-size: 15px; }
.confirm-dialog__body { margin: 0 0 18px; font-size: 13.5px; color: var(--text-dim); }
.confirm-dialog__actions { display: flex; justify-content: flex-end; gap: 8px; }
.btn--danger { background: var(--danger); color: #2a0508; }
.btn--danger:hover { background: #c94850; }

/* =====================================================================
   5. МАСТЕР МЕТОДИКИ
   Степпер (_step_progress.html) и навигация «предыдущий/следующий» (_step_nav.html).
   ===================================================================== */

/* Семь шагов в строку шире карточки почти на любом экране, поэтому степпер
   едет внутри себя. Полосу прокрутки при этом видно всегда — она часть
   компонента, а не системная деталь под ним: тонкая, скруглённая, в цвете
   рамки, под курсором — акцентная. Отступ снизу — чтобы она не липла к
   таблеткам шагов. */
.wizard {
    margin-bottom: 20px; overflow-x: auto; padding-bottom: 7px;
    scrollbar-width: thin; scrollbar-color: var(--accent-dim) var(--surface-2);
}
.wizard::-webkit-scrollbar { height: 7px; }
/* без этого Chrome дорисовывает по стрелке-кнопке с каждого конца полосы */
.wizard::-webkit-scrollbar-button { width: 0; height: 0; display: none; }
.wizard::-webkit-scrollbar-track { background: var(--surface-2); border-radius: 999px; }
.wizard::-webkit-scrollbar-thumb { background: var(--accent-dim); border-radius: 999px; }
.wizard::-webkit-scrollbar-thumb:hover { background: var(--accent); }
.wizard__list {
    list-style: none; margin: 0; padding: 0; display: flex; gap: 6px; min-width: max-content;
}
/* Таблетка шага — ссылка на карточке системы и кнопка отправки формы на
   самих шагах (см. _step_progress.html). Выглядеть они обязаны одинаково,
   поэтому правило одно на оба тега, а не скопировано под кнопку: сброс
   кнопочных умолчаний (font/background/appearance) — то, чем эти две
   версии и разъезжаются. */
.wizard__item a,
.wizard__item button {
    display: flex; align-items: center; gap: 6px; text-decoration: none; color: var(--text-dim);
    font-size: 12.5px; padding: 7px 12px 7px 8px; border: 1px solid var(--border); border-radius: 999px;
    white-space: nowrap; min-height: 32px;
    font-family: inherit; font-weight: inherit; line-height: inherit;
    background: none; -webkit-appearance: none; appearance: none; cursor: pointer;
}
.wizard__item a:hover,
.wizard__item button:hover { border-color: var(--accent-dim); color: var(--text); }
.wizard__num {
    display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px;
    border-radius: 50%; border: 1px solid var(--border); font-size: 10.5px; font-family: var(--font-mono);
    flex-shrink: 0;
}
.wizard__item.is-done .wizard__num { background: var(--good); border-color: var(--good); color: #06210f; }
.wizard__item.is-done a,
.wizard__item.is-done button { color: var(--text); }
.wizard__item.is-current a,
.wizard__item.is-current button {
    background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600;
}
/* Кружок с номером на текущем шаге: тёмная заливка на янтарной таблетке —
   и цифра тем же цветом, что заливка, то есть невидимая. Цифра здесь
   единственное, что вообще отличает кружок от точки: перейдя на любой шаг,
   пользователь видел пустой кружок вместо номера. */
.wizard__item.is-current .wizard__num { background: var(--accent-ink); border-color: var(--accent-ink); color: var(--accent); }
.wizard__item.is-current.is-done .wizard__num { background: var(--good); border-color: var(--good); color: #06210f; }

/* третье состояние: шаг начат и не дозаполнен (например, сценарии есть не у
   всех выбранных угроз). Раньше такой шаг красился зелёным как готовый. */
.wizard__item.is-part a,
.wizard__item.is-part button { color: var(--text); border-color: var(--accent-dim); }
.wizard__item.is-part .wizard__num { border-color: var(--accent); color: var(--accent); }
/* Текущий и недозаполненный сразу: правило выше той же специфичности, что у
   .is-current, и перебивало его цвет текста — в тёмной теме выходил светлый
   текст на янтарной таблетке. */
.wizard__item.is-current.is-part a,
.wizard__item.is-current.is-part button { color: var(--accent-ink); }
.wizard__item.is-current.is-part .wizard__num { background: var(--accent-ink); border-color: var(--accent-ink); color: var(--accent); }

.wizard-nav {
    display: flex; justify-content: space-between; align-items: center; gap: 10px;
    margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--border);
}
.wizard-back { margin-top: 8px; }

/* =====================================================================
   6. ШАГ 1. ТЕХНОЛОГИИ
   ===================================================================== */

/* фильтр по технологиям (шаг «адаптация») */
.tech-picker__grid { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.tech-chip {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 12px; padding: 5px 10px; border: 1px solid var(--border); border-radius: 999px;
    cursor: pointer; color: var(--text-dim);
}
.tech-chip:has(input:checked) { background: var(--danger); color: #1a0808; border-color: var(--danger); font-weight: 600; }
.tech-chip input { accent-color: var(--accent); }
/* Единственный чип, который несёт не код, а фразу: «Многофакторная
   аутентификация, смарт-карты, токены» — 442px, которые в 390px экрана не
   помещаются никак. `white-space: nowrap` базового .chip придуман для
   «УБИ.006» и «ИАФ.1», где перенос посреди кода был бы ошибкой; здесь он
   утаскивал вбок всю страницу /coverage/ (110px на 390px). Переносится
   только этот вариант — базовый класс не трогаем. */
.chip--tech { color: var(--neutral-chip); border-color: var(--neutral-chip-dim); margin-right: 6px; white-space: normal; }

/* вторая ось того же шага — условия эксплуатации (catalog/conditions.py).
   Не чипы: у признака есть пояснение «когда его честно нет», и оно должно
   читаться до отметки, а не после. Отсюда колонка и строка подсказки, но
   отмеченное состояние красное — то же, что у .tech-chip, потому что
   означает ровно то же: «в системе этого НЕТ». */
.cond-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.cond-option {
    display: grid; grid-template-columns: auto 1fr; gap: 4px 10px; align-items: start;
    padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius);
    cursor: pointer; color: var(--text-dim);
}
.cond-option:has(input:checked) { border-color: var(--danger); background: rgba(200, 80, 80, 0.08); }
.cond-option input { accent-color: var(--accent); margin-top: 3px; }
.cond-option__title { font-size: 13px; color: var(--text); }
.cond-option:has(input:checked) .cond-option__title { color: var(--danger); font-weight: 600; }
.cond-option__hint { grid-column: 2; font-size: 12px; color: var(--text-dim); }

/* живой счётчик оставшихся угроз — technologies/intruders (AJAX-превью).
   Отдельный от .order-picker__note стиль: тот использовался и здесь
   раньше, но у него отрицательный верхний отступ (рассчитан на соседство
   с чекбоксами приказа в другом месте) — из-за этого счётчик наезжал на
   сетку технологий сверху. */
.threat-counter {
    margin: 18px 0; padding: 14px 16px; border-radius: var(--radius);
    border: 1px solid var(--accent-dim); background: rgba(184, 127, 46, 0.10);
    font-size: 15px; display: flex; align-items: baseline; gap: 8px;
}
.threat-counter__num { font-size: 22px; font-weight: 700; color: var(--accent); font-family: var(--font-mono, inherit); }
.threat-counter__label { color: var(--text-dim); font-size: 13px; }
.threat-counter.is-updating { opacity: 0.6; }

/* =====================================================================
   7. ШАГ 3. МОДЕЛЬ НАРУШИТЕЛЯ
   ===================================================================== */

/* модель нарушителя */
.intruder-list { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.intruder-row {
    padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2);
}
.intruder-row label { display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 14px; }
.intruder-row input[type="checkbox"] { accent-color: var(--accent); width: 16px; height: 16px; flex-shrink: 0; }
.intruder-row__name { flex: 1; }
.intruder-goals { margin: 6px 0 0 24px; padding: 0; font-size: 12.5px; color: var(--text-dim); }
.intruder-goals li { margin-bottom: 2px; }
.chip--level-Н1 { color: var(--good); border-color: var(--good); }
.chip--level-Н2 { color: var(--accent); border-color: var(--accent-dim); }
.chip--level-Н3 { color: var(--level-3); border-color: var(--level-3); }
.chip--level-Н4 { color: var(--danger); border-color: var(--danger); }
.chip--review { color: var(--danger); border-color: var(--danger); font-weight: 600; }

/* =====================================================================
   8. ШАГ 4. СПОСОБЫ РЕАЛИЗАЦИИ
   ===================================================================== */

/* Сами списки способов и интерфейсов берут .intruder-list/.intruder-row из
   секции 7 — то самое заимствование класса чужого шага, о котором
   предупреждает заголовок файла. Здесь оно пока безобидно (значение то же:
   строка-чекбокс из списка приложения), но при следующей крупной правке
   шага класс стоит поднять в секцию 4. Ниже — только то, что придумано
   для этого шага.

   Классы общие для обоих списков страницы (способы п. 5.2.3 и интерфейсы
   п. 5.2.4): пометка применимости у них одна и та же по смыслу, а разные
   классы означали бы, что однажды они разойдутся видом.

   Приглушение, а не запрет: пункт, не проходящий по модели нарушителя
   (catalog/methods.py, METHOD_REQUIREMENTS и INTERFACE_REQUIREMENTS),
   остаётся кликабельным — требования там наша конвенция, и эксперт вправе
   её перебить. Поэтому и цвет не красный: красный в этом файле уже занят
   отметкой «этого в системе нет» (.tech-chip, .cond-option), а тут отметка
   значит обратное. */
.fit-row--unfit { border-style: dashed; background: transparent; }
.fit-row--unfit .intruder-row__name { color: var(--text-dim); }
.chip--unfit { color: var(--text-dim); border-color: var(--border); flex-shrink: 0; }

/* Требование печатается у всех строк, а не только у непрошедших: до
   заполнения шага 3 это единственная подсказка, кого модель нарушителя
   должна включать, чтобы пункт вообще имел смысл. */
.fit-row__req { margin: 6px 0 0 24px; font-size: 12.5px; color: var(--text-dim); }
.fit-row__reason { margin: 2px 0 0 24px; font-size: 12.5px; color: var(--accent); }

/* Отмеченный, но не проходящий пункт — расхождение шагов 3 и 4, и
   единственное состояние на этой странице, которое стоит внимания. */
.fit-row--unfit:has(input:checked) { border-color: var(--danger); border-style: solid; }
.fit-row--unfit:has(input:checked) .intruder-row__name { color: var(--text); }
.fit-row--unfit:has(input:checked) .fit-row__reason { color: var(--danger); }

/* =====================================================================
   9. ШАГ 5. ВЫБОР УГРОЗ
   ===================================================================== */

.threat-list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 56vh;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.threat-row { border-bottom: 1px solid var(--border); }
.threat-row:last-child { border-bottom: none; }
.threat-row label {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    cursor: pointer;
    font-size: 13.5px;
}
.threat-row label:hover { background: var(--surface-2); }
.threat-row input[type="checkbox"] { flex-shrink: 0; accent-color: var(--accent); width: 16px; height: 16px; }
.threat-row__code {
    font-family: var(--font-mono);
    font-size: 12.5px;
    color: var(--accent);
    flex-shrink: 0;
    min-width: 74px;
}
.threat-row__name { flex: 1; }
.threat-row.is-hidden { display: none; }

.form-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 16px;
}

/* Полоса черновика (draft.js): вставляется скриптом над формой шага, когда
   в localStorage лежит несохранённое состояние, отличное от серверного.
   Акцентная рамка, а не цвет опасности: это предложение, не ошибка. */
.draft-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
    margin: 0 0 14px; padding: 10px 14px;
    border: 1px solid var(--accent-dim); border-radius: var(--radius);
    background: var(--surface-2); font-size: 13px; line-height: 1.5;
}
.draft-bar__text { flex: 1 1 320px; }

.facet-bar { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
/* Начертание одно на оба состояния. Выбранный чип раньше становился
   полужирным — то есть шире, — и ряд перекладывался при каждом нажатии:
   соседи разъезжались, а на узком экране ряд менял число строк, и всё под
   ним прыгало. Выбранный чип и так отличается заливкой. */
.facet-chip {
    font-size: 12px; font-weight: 600; padding: 5px 11px;
    border: 1px solid var(--border); border-radius: 999px;
    background: transparent; color: var(--text-dim); cursor: pointer; min-height: 30px;
}
.facet-chip:hover { border-color: var(--accent-dim); color: var(--text); }
.facet-chip.is-active { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }

.order-picker {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 16px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--border);
}
.order-picker__label { color: var(--text-dim); font-size: 12.5px; margin-right: 4px; }
.order-tab {
    font-size: 12.5px;
    padding: 6px 11px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--text);
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.order-tab:hover { border-color: var(--accent-dim); }
.order-tab.is-active { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 600; }
.order-tab.is-disabled { color: var(--text-dim); opacity: 0.55; cursor: not-allowed; }
.order-tab__soon {
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border: 1px solid var(--border);
    border-radius: 3px;
    padding: 1px 4px;
}

/* множественный выбор приказов */
.order-tab input[type="checkbox"] { accent-color: var(--accent); margin-right: 2px; }
.order-tab:has(input:checked) { background: var(--accent); color: var(--accent-ink);
                                border-color: var(--accent); font-weight: 600; }
.order-tab.is-disabled { cursor: not-allowed; }
.hint--cross { border-left-color: var(--accent-dim); }

/* Основания исключения — свод под врезкой «Как отфильтрован этот список».
   Свои классы, а не .chip--level-* из шага «Нарушитель»: там цвет означает
   уровень возможностей нарушителя, здесь — по какой оси ушла угроза, и общий
   класс склеил бы два разных смысла в один вид (ровно та история, из-за
   которой в оглавлении этого файла записано правило владения).

   Цвет повторяет ось, по которой считался фильтр: янтарный — профиль
   адаптации (шаг 1), нейтральный — модель нарушителя (шаг 3). Оба приглушены:
   это объяснение, а не предупреждение — исключение угрозы штатный ход
   методики, а не ошибка. */
.exclusion-grounds__count {
    display: inline-flex; align-items: center; justify-content: center; min-width: 30px;
    font-family: var(--font-mono); font-size: 12px; padding: 1px 6px; border-radius: 999px;
    border: 1px solid var(--border); color: var(--text-dim); flex-shrink: 0;
}
.exclusion-grounds__count--profile { color: var(--accent-dim); border-color: var(--accent-dim); }
.exclusion-grounds li { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
/* Коды угроз — вторым планом и на всю ширину строки: они здесь для сверки
   («а эта точно должна была уйти?»), а не для чтения подряд. */
.exclusion-grounds__codes {
    flex-basis: 100%; font-family: var(--font-mono); font-size: 11.5px; line-height: 1.5;
}
/* Оговорка про несложение чисел прижималась вплотную к последней строке кодов
   и читалась как её продолжение. */
.exclusion-grounds__note { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }

/* =====================================================================
   10. ШАГ 6. СЦЕНАРИИ
   ===================================================================== */

.typical-scenario {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 6px 0 10px;
    padding: 8px 10px; border: 1px dashed var(--border); border-radius: var(--radius);
}

/* сценарии: выбор типового сценария (пути проникновения) */
.scenario-templates {
    margin-bottom: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--border);
}
.scenario-templates__head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.scenario-templates__head h2 { font-size: 15px; margin: 0; }
.scenario-templates__head .btn { margin-left: auto; }
.scenario-templates p { margin: 6px 0 10px; }

.template-cards { display: grid; gap: 8px; }

/* Карточка пути — не переключатель, а действие: радиокнопку и обёртку <label>
   убрали вместе с общей кнопкой «Подставить в форму» (см. комментарий в
   _scenario_template_card.html). Поэтому ни cursor: pointer на всей карточке,
   ни подсветки :has(input:checked) здесь больше нет — кликабельна кнопка. */
.template-card {
    display: flex; gap: 10px; align-items: flex-start;
    padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface);
}
.template-card:hover { border-color: var(--accent-dim); }
.template-card__body { display: flex; flex-direction: column; gap: 6px; width: 100%; }

/* Состав пути и место применения. Своей кнопки у карточки нет (см. докстринг
   _scenario_template_card.html), поэтому это подпись к обзору, а не к действию. */
.template-card__preview { font-size: 12px; line-height: 1.5; }
.template-card p { margin: 0; }
.template-card__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.template-card__summary { font-size: 13px; line-height: 1.5; }
.template-card__chain { display: flex; flex-direction: row; align-items: center; gap: 6px; flex-wrap: wrap; }
.template-cards__rest { margin-top: 10px; }
.template-cards__rest .template-cards { margin-top: 8px; }

/* Общие настройки подстановки: режим «дополнить / перезаписать». Область
   применения отсюда убрана вместе с массовым применением одного пути
   произвольной группе угроз: круг у каждой кнопки теперь свой и жёсткий.
   Липкая, потому что кнопки подстановки есть и в карточках угроз далеко внизу
   списка: иначе непонятно, в каком режиме сработает та кнопка, до которой
   домотал. top: 65px — под .topbar, как у .report-toc. */
.apply-settings {
    display: flex; align-items: center; gap: 8px 18px; flex-wrap: wrap;
    margin-top: 12px; padding: 9px 12px;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2);
    position: sticky; top: 65px; z-index: 5;
}
.apply-settings__group { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.apply-settings__hint { font-size: 12px; flex: 1 1 220px; }
.apply-settings__status { font-size: 12.5px; flex-basis: 100%; color: var(--accent); }
.apply-settings__status:empty { display: none; }

/* .tech-chip красит отмеченное в цвет опасности — это семантика выбора угроз,
   а не режима подстановки: «дополнить» не предупреждение. */
.apply-settings .tech-chip:has(input:checked) {
    background: var(--accent); color: var(--accent-ink); border-color: var(--accent-dim);
}
.recommended-path {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 8px 0 0;
    padding: 7px 10px; border: 1px solid var(--accent-dim); border-radius: var(--radius);
    background: var(--surface-2); font-size: 13px;
}
.recommended-path strong { color: var(--text); }

/* сценарии: объяснение шага и обзор «какой путь каким угрозам». Кнопка
   подстановки живёт на самой карточке пути и бьёт только по её угрозам,
   а общий для страницы режим — в липкой .apply-settings выше. */
.scenario-explainer { font-size: 13.5px; line-height: 1.6; margin: 6px 0 10px; }
.scenario-templates__footnote { font-size: 12.5px; margin: 12px 0 0; }

.apply-lane {
    margin-top: 14px; padding: 12px 14px;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
}
.apply-lane__head { font-size: 14px; margin: 0 0 6px; color: var(--text); }
.apply-lane__note { font-size: 13px; line-height: 1.55; margin: 0 0 10px; }
.apply-lane__actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.apply-lane__hint { font-size: 12px; flex: 1 1 240px; }

/* Единственное сплошное действие страницы — и оно в конце обзора, после того
   как читатель увидел, какой путь каким угрозам. Отбивка сверху отделяет его
   от карточек: иначе кнопка читается как относящаяся к последней из них. */
.apply-lane__actions--final {
    margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border);
}

/* Выбор пути для ОДНОЙ угрозы. Раньше здесь была кнопка «Применить», умевшая
   только рекомендованный путь: выбрать другой одной угрозе было нечем, и всё
   остальное делалось кнопкой на карточке пути, бившей по всем угрозам сразу. */
.path-pick {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 8px 0 0;
    font-size: 13px;
}
.path-pick__label { color: var(--text-dim); }
.path-pick__select {
    flex: 1 1 260px; min-width: 0; max-width: 100%;
    padding: 5px 8px; font-size: 12.5px; font-family: var(--font-body);
    color: var(--text); background: var(--surface-2);
    border: 1px solid var(--border); border-radius: var(--radius);
}
.path-pick__select:disabled { opacity: 0.6; }

/* угрозы без рекомендации: раньше это была строка мелким серым, из которой
   не следовало ни одного действия. Блок должен читаться как невыполненная
   работа и вести к ней кнопками. */
.apply-todo {
    margin-top: 12px; padding: 10px 12px;
    border: 1px solid var(--accent-dim); border-left-width: 3px;
    border-radius: var(--radius); background: var(--surface-2);
}
.apply-todo__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 6px; font-size: 13.5px; }
.apply-todo__text { margin: 0 0 8px; font-size: 12.5px; line-height: 1.55; color: var(--text-dim); }
.apply-todo__actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0; }
.chip--todo { color: var(--accent); border-color: var(--accent-dim); font-weight: 600; }
.chip--soft { color: var(--neutral-chip); border-color: var(--neutral-chip-dim); }

.recommended-path--none { border-color: var(--border); border-style: dashed; }

/* фильтр списка угроз */
.threat-filter {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin-bottom: 10px; padding: 8px 10px;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2);
}
.threat-filter__status { font-size: 12px; margin-left: auto; }
.result-card[hidden] { display: none; }

/* Режим подстановки и фильтр — не опасность, а настройка: красный из
   .tech-chip здесь читается как предупреждение (см. ниже про .template-apply). */
.apply-lane .tech-chip:has(input:checked),
.threat-filter .tech-chip:has(input:checked) {
    background: var(--accent); color: var(--accent-ink); border-color: var(--accent-dim);
}

/* сценарии: тактики + вложенные техники (приложение 11) */
.tactic-scenario-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.tactic-scenario-block { border: 1px solid var(--border); border-radius: var(--radius); padding: 8px 10px; background: var(--surface); }
.tactic-scenario-block .technique-picker { margin: 8px 0 0; padding: 8px 10px; }
.tactic-scenario-block .technique-picker summary { font-size: 12.5px; }
.tech-chip--wide { display: flex; align-items: baseline; border-radius: 4px; width: 100%; padding: 4px 8px; white-space: normal; }

/* ---------- всплывающий список угроз у подписи «рекомендуется для N угроз» ----------
   Панель прокручиваемая: у пути insider 52 угрозы, и обычный тултип такой
   список не показал бы. Открывается по наведению, закрепляется кликом —
   см. static/mapper/js/path_threats.js. */
.path-threats { position: relative; display: inline-block; }
.path-threats__toggle {
    font: inherit; color: inherit; cursor: pointer; background: none;
    border: none; padding: 0; text-align: left;
}
.path-threats__toggle--text {
    border-bottom: 1px dashed var(--text-dim); color: var(--text-dim);
}
.path-threats__toggle--text:hover, .path-threats__toggle--chip:hover { color: var(--accent); }

/* Сброс `font: inherit` / `color: inherit` / `border: none` выше перебивал
   типографику .chip (правило .chip стоит раньше в файле, специфичность та же),
   и чип на карточке пути переставал выглядеть чипом — становился крупным
   белым текстом в рамке. Возвращаем ровно то, что задаёт .chip + .chip--soft. */
.path-threats .path-threats__toggle--chip {
    font-family: var(--font-mono); font-size: 11.5px;
    padding: 2px 6px; border-radius: 4px;
    border: 1px solid var(--neutral-chip-dim); color: var(--neutral-chip);
    white-space: nowrap; cursor: pointer;
}
.path-threats.is-open .path-threats__toggle { color: var(--accent); }

.path-threats__panel {
    position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;
    display: flex; flex-direction: column; gap: 6px;
    width: max-content; min-width: 280px; max-width: min(520px, 90vw);
    max-height: 40vh; overflow-y: auto;
    padding: 10px 12px; border: 1px solid var(--accent-dim);
    border-radius: var(--radius); background: var(--surface);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
    font-size: 12.5px; line-height: 1.5; white-space: normal;
}
.path-threats__panel--left { left: auto; right: 0; }
.path-threats__panel[hidden] { display: none; }
.path-threats__head { color: var(--text-dim); }
.path-threats__list { display: flex; flex-direction: column; gap: 4px; }
.path-threats__item { display: flex; align-items: baseline; gap: 6px; }
.path-threats__name { color: var(--text-dim); }
.path-threats__item.is-in-project .path-threats__name { color: var(--text); font-weight: 600; }

.technique-search { margin-bottom: 10px; }
.technique-list li.is-hidden, .chip-cloud li.is-hidden { display: none; }
[data-technique-group].is-hidden { display: none; }

/* =====================================================================
   11. ШАГ 7. МЕРЫ ЗАЩИТЫ И ОТЧЁТ
   ===================================================================== */

.threat-results { display: flex; flex-direction: column; gap: 12px; }

/* окно прокрутки для длинных списков threat-results (например, шаг «Сценарии») —
   тот же паттерн, что у .threat-list на шаге «Угрозы»; не вешаем на .threat-results
   напрямую, т.к. он используется и в result.html, который поддерживает печать/экспорт
   через браузер, где max-height/overflow обрежут содержимое */
.threat-results--scroll {
    max-height: 56vh;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px;
}
.result-card {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
    background: var(--surface-2);
}
.result-card header { display: flex; gap: 10px; align-items: baseline; margin-bottom: 8px; flex-wrap: wrap; }
/* Сворачиваемая карточка угрозы (шаг 6): summary вместо header, та же строка.
   Штатный маркер у display:flex пропадает, поэтому свой — треугольник в
   начале строки, повёрнутый по состоянию. */
.result-card__head { display: flex; gap: 10px; align-items: baseline; margin-bottom: 8px; flex-wrap: wrap; cursor: pointer; list-style: none; }
.result-card__head::-webkit-details-marker { display: none; }
.result-card__head::before { content: "▾"; color: var(--text-dim); font-size: 12px; }
.result-card__fold:not([open]) > .result-card__head { margin-bottom: 0; }
.result-card__fold:not([open]) > .result-card__head::before { content: "▸"; }
.threat-filter__fold { display: inline-flex; gap: 6px; }
.unresolved {
    margin: 10px 0 0;
    font-size: 12.5px;
    color: var(--danger);
    background: rgba(229, 99, 107, 0.1);
    border: 1px solid rgba(229, 99, 107, 0.35);
    border-radius: 4px;
    padding: 8px 10px;
}

.chip-cloud { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.chip-cloud li { display: flex; align-items: baseline; font-size: 13.5px; }

.category-gaps { display: flex; flex-direction: column; gap: 8px; }
.gap-card {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 10px 14px;
    background: var(--surface-2);
}
.gap-card summary { cursor: pointer; font-size: 13.5px; color: var(--text-dim); }
.gap-card[open] summary { color: var(--text); margin-bottom: 10px; }

/* источник меры (приказ + код) и статус сверки */
.chip--src { color: var(--neutral-chip); border-color: var(--neutral-chip-dim); margin-right: 6px; }

/* подсказки вероятного аналога */
/* Селектор со «своим» li: правило .measure-list li выше по специфичности
   (0,1,1 против 0,1,0), и без уточнения строка подсказки оставалась флексом
   вопреки написанному здесь display: block. Видно это только на узком экране:
   подсказка не переносилась и распирала страницу вбок на 3px (360px). */
/* .chip-cloud li.hint-row назван явно: `.chip-cloud li` специфичнее одиночного
   .hint-row и ставил подсказки в ряд с мерой — на 320 px они уезжали за край. */
.measure-list li.hint-row, .chip-cloud li.hint-row, .hint-row { display: block; margin-bottom: 10px; }
.hint { font-size: 12.5px; margin-top: 4px; padding: 5px 9px; border-radius: 4px;
        border-left: 3px solid var(--border); background: var(--surface); color: var(--text-dim); }
.hint--high { border-left-color: var(--good); }
.hint--medium { border-left-color: var(--accent); }
.hint--low, .hint--none { border-left-color: var(--danger); }
.hint__score { display: block; margin-top: 2px; font-size: 11px; opacity: .8; }

.chip--guess { color: var(--accent); border-color: var(--accent-dim);
               background: rgba(232,163,61,.12); margin-right: 6px; }

/* обоснование сопоставления угрозы с мерами */
.rationale { margin: 8px 0 12px; font-size: 12.5px; }
.rationale summary { cursor: pointer; color: var(--accent); }
.rationale p { margin: 8px 0 0; color: var(--text-dim); line-height: 1.55; }

/* подсказка о приказе-дополнении по умолчанию (№117) */
.hint--supplement { border-left-color: var(--accent); background: rgba(184, 127, 46, 0.08); color: var(--text); }
.supplement-note { margin-top: 4px; }

.legend { margin-bottom: 18px; }
.legend summary { cursor: pointer; font-size: 13px; color: var(--accent); }
.legend__body { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; font-size: 12.5px; color: var(--text-dim); }
.legend__body div { display: flex; align-items: baseline; gap: 8px; }

.report-toc {
    display: flex; flex-wrap: wrap; gap: 6px 14px; margin-bottom: 20px; padding: 10px 14px;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2);
    position: sticky; top: 65px; z-index: 5; font-size: 12.5px;
}
.report-toc a { text-decoration: none; color: var(--text-dim); }
.report-toc a:hover { color: var(--accent); }

/* =====================================================================
   12. КАРТОЧКА СИСТЕМЫ
   ===================================================================== */

/* ---------------------------------------------------------------------
   Карточка системы (system_detail.html).

   Разделы шагов раньше печатались как шесть заголовков с серой строкой под
   каждым — одинаковым кеглем и без статуса, поэтому «7 из 10 угроз» (дыра в
   документе) выглядело так же, как «9 из 9» (норма). Теперь шаг — карточка со
   статусом, числом и полоской охвата; состояния считает readiness.py.
   --------------------------------------------------------------------- */
.system-meta { color: var(--text-dim); font-size: 12.5px; margin: 3px 0 0; }
.system-title { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.system-title h1 { margin: 0; }

/* переименование и меню — раскрывающиеся <details>, без своего JS: раньше
   инпут с именем системы и кнопка «Удалить» стояли раскрытыми в шапке всегда */
.system-rename summary, .system-menu summary {
    list-style: none; cursor: pointer; color: var(--text-dim);
    border: 1px solid transparent; border-radius: var(--radius); padding: 2px 8px;
    font-size: 15px; line-height: 1.4;
}
.system-rename summary::-webkit-details-marker,
.system-menu summary::-webkit-details-marker { display: none; }
.system-rename summary:hover, .system-menu summary:hover,
.system-rename[open] summary, .system-menu[open] summary {
    color: var(--accent); border-color: var(--border); background: var(--surface-2);
}
.system-rename { position: relative; font-size: 14px; font-weight: 400; }
.system-rename .rename-form {
    position: absolute; top: calc(100% + 6px); left: 0; z-index: 5;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 10px; display: flex; gap: 6px; box-shadow: 0 10px 26px rgba(0, 0, 0, .35);
}
.system-menu { position: relative; }
/* Кнопка «ещё действия» — только точки, без рамки: рамка вокруг трёх точек
   в шапке карточки читалась как ещё одно поле формы рядом с названием
   системы. Точки набраны маркерами списка (•), а не многоточием (⋯): у
   многоточия точки тонкие и в 15px почти не видны. */
.system-menu summary { border-color: transparent; min-height: 32px; display: inline-flex; align-items: center; }
.system-menu summary:hover, .system-menu[open] summary { border-color: transparent; }
.system-menu__dots {
    font-size: 15px; font-weight: 700; line-height: 1;
    letter-spacing: 2px; margin-right: -2px;
}
.system-menu__panel {
    position: absolute; top: calc(100% + 6px); right: 0; z-index: 5; width: 260px;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 12px 14px; display: flex; flex-direction: column; gap: 8px;
    box-shadow: 0 10px 26px rgba(0, 0, 0, .35);
}
.system-menu__hint { margin: -4px 0 4px; font-size: 11.5px; color: var(--text-dim); line-height: 1.45; }
.link-button--danger { color: var(--danger); }
.link-button--danger:hover { color: var(--danger); text-decoration: underline; }

/* сводка сверху: прогресс по шагам + единственная акцентная кнопка «куда дальше» */
.system-hero {
    display: grid; grid-template-columns: auto 1fr auto; gap: 20px; align-items: center;
    padding: 16px 18px; background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--radius); margin-bottom: 18px;
}

/* Было кольцо 72px с цифрой 20px внутри: в дугу помещалось только «5» и
   подпись «из 6» кеглем 10 — самое важное число карточки читалось хуже
   всего, что на ней есть. Теперь тот же приём, что у карточек шагов ниже
   (.step-card__num-big + .step-bar): крупная цифра, подпись рядом, полоска
   охвата под ними. Заодно кольцо больше не обязано быть круглым при любой
   ширине колонки. */
.system-progress { display: flex; flex-direction: column; gap: 7px; min-width: 132px; }
.system-progress__metric { display: flex; align-items: baseline; gap: 7px; margin: 0; }
.system-progress__num {
    font: 700 38px/1 var(--font-mono); color: var(--accent); font-variant-numeric: tabular-nums;
}
.system-progress__num.is-complete { color: var(--good); }
.system-progress__unit { font-size: 12.5px; color: var(--text-dim); }
.system-hero__text strong { display: block; font-size: 15.5px; margin-bottom: 4px; }
.system-hero__text p { margin: 0; font-size: 13px; color: var(--text-dim); max-width: 52ch; }
.system-hero__cta { display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
.system-hero__cta .btn { text-decoration: none; white-space: nowrap; text-align: center; }

/* сетка карточек шагов */
.step-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 22px; }
.step-card {
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2);
    padding: 13px 15px; display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
}
.step-card--part { border-color: var(--accent-dim); }
.step-card--empty, .step-card--blocked { border-style: dashed; }
.step-card__head { display: flex; align-items: baseline; gap: 8px; width: 100%; }
.step-card__num {
    font: 400 10.5px var(--font-mono); color: var(--text-dim); border: 1px solid var(--border);
    border-radius: 50%; width: 18px; height: 18px; display: inline-grid; place-items: center;
    flex-shrink: 0;
}
.step-card__title { font-size: 14.5px; font-weight: 600; }
.step-card__ref { font-size: 11.5px; color: var(--text-dim); font-family: var(--font-mono); margin-left: auto; white-space: nowrap; }
.step-status { font-size: 11px; padding: 2px 9px; border-radius: 999px; border: 1px solid; }
.step-status--ok { color: var(--good); border-color: var(--good); }
.step-status--part { color: var(--accent); border-color: var(--accent-dim); }
.step-status--empty, .step-status--blocked { color: var(--text-dim); border-color: var(--border); border-style: dashed; }
.step-card__metric { display: flex; align-items: baseline; gap: 8px; margin: 0; flex-wrap: wrap; }
.step-card__num-big {
    font: 700 24px/1 var(--font-mono); color: var(--accent); font-variant-numeric: tabular-nums;
}
.step-card--ok .step-card__num-big { color: var(--good); }

/* ноль на незаполненном шаге — не тревога: акцент оставлен за тем, что
   действительно требует действия (частично заполненный шаг) */
.step-card--empty .step-card__num-big,
.step-card--blocked .step-card__num-big { color: var(--text-dim); }
.step-card__unit { font-size: 12.5px; color: var(--text-dim); }
.step-bar { display: block; width: 100%; height: 5px; background: var(--bg); border-radius: 3px; overflow: hidden; }
.step-bar i { display: block; height: 100%; background: var(--accent); }
.step-bar i.is-ok { background: var(--good); }
.step-card__note { margin: 0; font-size: 12.5px; color: var(--text-dim); }
.step-card__foot { display: flex; gap: 12px; flex-wrap: wrap; margin-top: auto; padding-top: 4px; }
.step-card__link { font-size: 12.5px; text-decoration: none; }
.step-card__link:hover { text-decoration: underline; }

/* технологии: чипы вместо перечисления через запятую в три строки */
.step-chips summary { list-style: none; cursor: pointer; display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }
.step-chips summary::-webkit-details-marker { display: none; }
.step-chips__more { font-size: 11.5px; color: var(--text-dim); border-bottom: 1px dashed var(--border); }
.step-chips[open] .step-chips__more { color: var(--accent); }
.step-chips__rest { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }

/* .chip задуман под коды мер и потому моноширинный; названия технологий —
   обычные слова, в моноширинном они разъезжаются и не переносятся */
.step-chips .chip { font-family: var(--font-body); white-space: normal; }

/* приказы: видны на карточке и правятся здесь же (шаг «Угрозы» пишет их же) */
.order-edit { width: 100%; }
.order-edit summary { list-style: none; cursor: pointer; display: flex; flex-wrap: wrap; gap: 5px; align-items: center; font-size: 12.5px; color: var(--text-dim); }
.order-edit summary::-webkit-details-marker { display: none; }
.order-edit__toggle { color: var(--accent); border-bottom: 1px dashed var(--accent-dim); font-size: 11.5px; }
.order-edit__form { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }
.chip--order { color: var(--accent); border-color: var(--accent-dim); }

/* единый документ: сначала что получится, потом кнопки выгрузки */
.doc-block {
    border: 1px solid var(--accent-dim); border-radius: var(--radius);
    padding: 16px 18px; display: flex; flex-direction: column; gap: 12px; margin-bottom: 18px;
}
.doc-block--not-ready { border-color: var(--border); }
.doc-block__head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.doc-block__head h2 { font-size: 16px; margin: 0; }
.doc-block__head span { font-size: 12.5px; color: var(--text-dim); }
.doc-warn {
    display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; margin: 0;
    font-size: 12.5px; color: var(--text-dim); padding: 8px 12px;
    border: 1px solid var(--accent-dim); border-radius: var(--radius);
    background: rgba(232, 163, 61, .07);
}
.doc-warn b { color: var(--accent); font-weight: 600; }
.doc-warn a { margin-left: auto; white-space: nowrap; }
.chapter-list { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.chapter-row {
    display: grid; grid-template-columns: 20px 1fr auto auto; gap: 10px; align-items: center;
    padding: 8px 12px; font-size: 13px; background: var(--surface-2);
    border-bottom: 1px solid var(--border); cursor: pointer;
}
.chapter-row:last-child { border-bottom: none; }
.chapter-row:hover { background: var(--surface); }
.chapter-row.is-off { opacity: .45; }
.chapter-row__n { font: 400 11px var(--font-mono); color: var(--text-dim); }
.chapter-row__fill { font-size: 12px; color: var(--text-dim); text-align: right; }
.chapter-row__fill em { font-style: normal; color: var(--accent); }

/* чекбокс главы означает «включить в документ» — акцент, а не цвет опасности:
   тот зарезервирован за шагом «Технологии», где отметка значит «этого нет» */
.chapter-row input[type="checkbox"] { accent-color: var(--accent); width: 15px; height: 15px; }
.doc-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.doc-row__label, .doc-row label { font-size: 12.5px; color: var(--text-dim); }
.doc-row .muted { flex: 1 1 240px; }
/* Адрес ссылки «только на чтение». break-all, а не break-word: токен — это одно
   длинное «слово» без пробелов и дефисов, и по словам его перенести нечем — на
   телефоне он растягивал карточку вбок. */
.share-link {
    display: block; flex: 1 1 100%; word-break: break-all;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 8px 10px;
    font: 400 12.5px var(--font-mono); color: var(--text);
}
.doc-select {
    background: var(--surface-2); color: var(--text); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 6px 9px; font: 400 12.5px var(--font-body);
    /* Внутренняя ширина <select> — по самой длинной опции («К1 — первый класс
       защищённости»), и как флекс-элемент он ниже неё не сжимается: min-width
       у флекс-элементов по умолчанию auto. На 320px из-за одного этого поля
       вбок ехала вся страница. */
    max-width: 100%; min-width: 0;
}

/* Приказ и его уровень — одна строка формы: уровень без приказа бессмыслен,
   и рядом их видно как одну настройку, а не две независимые. */
.order-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.doc-select--small { padding: 4px 7px; font-size: 12px; }

/* Раздел «Сверх базового набора». Таблица широкая (мера + список угроз),
   поэтому едет она внутри своего контейнера, а не тянет вбок всю страницу —
   то же правило, что у .wizard. */
.beyond-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; min-width: 0; }
/* .category-gaps — флекс-колонка, и у её элементов min-width по умолчанию auto,
   то есть не меньше min-content самой широкой строки таблицы. Без сброса
   карточка распирала страницу вбок на узком экране (поймано на 360px). */
.category-gaps > .gap-card { min-width: 0; }
.beyond-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.beyond-table th, .beyond-table td {
    text-align: left; vertical-align: top; padding: 7px 10px;
    border-bottom: 1px solid var(--border);
}
.beyond-table th { font-size: 12px; color: var(--text-dim); font-weight: 600; white-space: nowrap; }
.beyond-table tr:last-child td { border-bottom: 0; }
.beyond-table__threats { color: var(--text-dim); white-space: nowrap; }

/* дорожная карта инструмента — свёрнута: это статус разработки, не системы */
.tool-scope { border-top: 1px solid var(--border); padding-top: 14px; }
.tool-scope summary { cursor: pointer; font-size: 13px; color: var(--text-dim); }
.tool-scope summary:hover { color: var(--accent); }
.tool-scope .roadmap-list { margin-top: 12px; }

.roadmap-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.roadmap-list li {
    display: flex; align-items: baseline; gap: 8px; font-size: 13.5px; color: var(--text-dim);
    padding: 8px 10px; border: 1px dashed var(--border); border-radius: var(--radius);
}
.roadmap-list__done { border-style: solid !important; border-color: var(--good) !important; color: var(--text) !important; }

/* =====================================================================
   12а. ПРЕДПРОСМОТР ДОКУМЕНТА
   Страницы model_preview.html и system_design.html: главы «Модели угроз» и
   технического проекта, отрисованные из тех же Block, что PDF и DOCX
   (mapper/model_html_export.py).

   Оформление здесь СВОЁ, а не общие .panel-заголовки, и это не украшательство:
   страница должна читаться как документ, иначе теряется её единственный смысл —
   увидеть, что скачается. Отсюда белое поле под текстом в обеих темах, засечный
   шрифт и меньший размер заголовков, чем на остальном сайте.
   ===================================================================== */

.doc-preview {
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 32px 36px; margin-top: 16px;
    font-family: Georgia, "Times New Roman", serif; font-size: 14.5px; line-height: 1.65;
}
.doc-preview h2, .doc-preview h3, .doc-preview h4, .doc-preview h5 {
    font-family: var(--font-body); line-height: 1.3;
}
.doc-preview__title { font-size: 20px; text-align: center; margin: 0 0 6px; }
.doc-preview__h1 { font-size: 17px; margin: 26px 0 10px; }
.doc-preview__h2 { font-size: 15px; margin: 20px 0 8px; }
.doc-preview__h3 { font-size: 14px; margin: 16px 0 6px; color: var(--text-dim); }
.doc-preview p { margin: 0 0 10px; }
/* Подписи титульного листа («УТВЕРЖДАЮ», «наименование системы») — по центру
   и мельче, как в самом документе. */
.doc-preview__meta, .doc-preview__note { text-align: center; color: var(--text-dim); font-size: 13px; }
.doc-preview__disclaimer { font-size: 12.5px; color: var(--text-dim); font-style: italic; }
/* Обоснование сценария — тем же приглушённым блоком, что в PDF. */
.doc-preview__rationale { padding-left: 12px; border-left: 2px solid var(--border); }
/* «Не заполнено» — курсив и акцент: в документе это самое важное, что нужно
   заметить до выгрузки, а не сноска. */
.doc-preview__gap { font-style: italic; color: var(--accent-dim); margin: 0 0 10px; }
.doc-preview__list { margin: 0 0 12px; padding-left: 22px; }
.doc-preview__list li { margin-bottom: 5px; }
/* Разрыв страницы отмечен, но не имитируется: страница — не бумага, и рисовать
   поля А4 значило бы обещать точное совпадение вёрстки, которого нет. */
.doc-preview__page-break {
    border: none; border-top: 1px dashed var(--border); margin: 28px 0;
}
/* Схема приложения к техпроекту. Третий рендерер тех же данных, что PDF и
   DOCX (mapper/model_html_export._schema_html): корневой блок, ряд карточек
   подсистем или зон. Сетка, а не рисунок: точного совпадения с вёрсткой листа
   предпросмотр и так не обещает, а состав и подчинение видны и так. */
.doc-schema { margin: 0 0 16px; }
.doc-schema__root {
    max-width: 60%; margin: 0 auto 4px; padding: 8px 12px; text-align: center;
    font-weight: 600; border: 1px solid var(--accent); border-radius: 4px;
    background: var(--surface-2);
}
.doc-schema__row {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 10px; margin-top: 10px;
}
/* Схема размещения — инлайновый SVG (model_html_export._diagram_svg).
   Тянется по ширине колонки: чертёж посчитан под 175 мм листа, но на экране
   ширина другая, и масштабировать его должен viewBox, а не полоса набора. */
.doc-diagram { margin: 12px 0; overflow-x: auto; }
.doc-diagram svg { width: 100%; height: auto; min-width: 520px; display: block;
    background: #fff; border-radius: 6px; }

.doc-schema__card { border: 1px solid var(--border); border-radius: 3px; }
.doc-schema__head {
    padding: 6px 8px; text-align: center; font-weight: 600; font-size: 12.5px;
    background: var(--surface-2); border-bottom: 1px solid var(--border);
}
.doc-schema__head span {
    display: block; font-weight: 400; font-size: 11px; color: var(--text-dim);
}
.doc-schema__card ul { list-style: none; margin: 0; padding: 8px; font-size: 12.5px; }
.doc-schema__card li { margin-bottom: 6px; }
.doc-schema__card li span {
    display: block; font-size: 11px; color: var(--text-dim);
}

.doc-preview__scroll { overflow-x: auto; margin: 0 0 14px; }
.doc-preview__table {
    border-collapse: collapse; width: 100%; font-size: 12.5px;
    font-family: var(--font-body);
}
.doc-preview__table th, .doc-preview__table td {
    border: 1px solid var(--border); padding: 6px 8px; text-align: left; vertical-align: top;
}
.doc-preview__table th { background: var(--surface-2); font-weight: 600; }
.doc-preview__table td.is-centered, .doc-preview__table th.is-centered { text-align: center; }

@media (max-width: 640px) {
    .doc-preview { padding: 20px 16px; }
}

/* =====================================================================
   12б. СРАВНЕНИЕ СИСТЕМ
   Страница system_compare.html.
   ===================================================================== */

.compare-picker { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 16px; }
.compare-picker label { font-size: 13px; color: var(--text-dim); }

/* Три колонки на узком экране не помещаются, поэтому таблица едет внутри
   своей обёртки — то же правило, что у справочника (.ref-scroll) и у
   предпросмотра документа: страница вбок не уезжает никогда. */
.compare-scroll { overflow-x: auto; margin-top: 14px; }
.compare-table { border-collapse: collapse; width: 100%; min-width: 640px; font-size: 13px; }
.compare-table th, .compare-table td {
    border: 1px solid var(--border); padding: 8px 10px; text-align: left; vertical-align: top;
}
.compare-table thead th { background: var(--surface-2); font-size: 13.5px; }
.compare-table thead th a { text-decoration: none; }
.compare-table tbody th { width: 26%; font-weight: 600; color: var(--text); background: var(--surface-2); }
/* Совпавшая строка приглушена, но остаётся на месте: спрятанное совпадение
   пришлось бы сверять глазами — ровно та работа, ради которой страница
   и заведена. */
.compare-table tr.is-same th, .compare-table tr.is-same td { color: var(--text-dim); }
.compare-table tr.is-different th { border-left: 3px solid var(--accent); }
.compare-table__common { display: block; font-size: 11.5px; font-weight: 400; margin-top: 3px; }
.compare-table__items { list-style: none; margin: 0; padding: 0; }
.compare-table__items li { margin-bottom: 4px; }
.compare-table__items li:last-child { margin-bottom: 0; }
.compare-table__none { font-style: italic; }

/* =====================================================================
   12в. КОНСТРУКТОР ТЕХПРОЕКТА
   Форма реквизитов пояснительной записки. Своя, а не `.auth-form .field`:
   там поле — одна строка ввода в колонке 360px, здесь — восемь textarea во
   всю ширину панели, и подпись под полем несёт не ошибку, а пояснение,
   что в него вписать.
   ===================================================================== */

.design-form { display: flex; flex-direction: column; gap: 18px; align-items: flex-start; }
.design-form .field { display: flex; flex-direction: column; gap: 5px; width: 100%; }
.design-form label { font-size: 13px; font-weight: 600; }
.design-form .field p { margin: 0; font-size: 12.5px; }

/* =====================================================================
   13. МОИ СИСТЕМЫ, ВХОД, РЕГИСТРАЦИЯ
   ===================================================================== */

/* Список «Мои системы». Каждая система — карточка в рамке, а не строка,
   отчёркнутая снизу: строки без рамки под формой заведения читались как
   продолжение формы, и было не видно, где кончается одна система и
   начинается другая. Кликается вся строка по названию, поэтому рамка
   подсвечивается на наведение — как у карточек шагов. */
.systems-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.systems-list li {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 12px 14px; background: var(--surface-2);
    border: 1px solid var(--border); border-radius: var(--radius);
}
.systems-list li:hover { border-color: var(--accent-dim); }
.systems-list a.systems-list__name { font-size: 14.5px; text-decoration: none; }
.systems-list__meta { color: var(--text-dim); font-size: 12px; }
/* Имя и дата — столбиком слева, прогресс — справа. До этого строка отвечала
   только «как называется и когда трогали», а выбирают из списка по другому
   вопросу: «где я не дозаполнил». */
.systems-list__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.systems-list__main .systems-list__name { overflow-wrap: anywhere; }
.systems-list__progress { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
/* Полоска .step-bar — общий компонент карточки системы (секция 12), здесь она
   применяется как есть: смысл тот же, «сколько шагов из шести», и заводить
   вторую полоску с тем же значением было бы хуже, чем переиспользовать. */
.systems-list__progress .step-bar { width: 90px; }
.systems-list__steps { color: var(--text-dim); font-size: 12px; font-family: var(--font-mono); white-space: nowrap; }
.systems-list__steps.is-complete { color: var(--good); }
@media (max-width: 520px) {
    .systems-list li { flex-direction: column; align-items: flex-start; gap: 8px; }
    .systems-list__progress { width: 100%; }
    .systems-list__progress .step-bar { flex: 1; width: auto; }
}

/* Загрузка системы из файла — второй способ завести систему, поэтому стоит
   рядом с формой «Завести систему» и оформлен скромнее её: основной путь
   всё-таки первый. */
.import-system { margin-bottom: 18px; font-size: 13px; }
.import-system summary { cursor: pointer; color: var(--text-dim); }
.import-system summary:hover { color: var(--text); }
.import-system__form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 10px 0 6px; }
.import-system input[type="file"] { font-size: 12.5px; color: var(--text-dim); max-width: 100%; }
/* Форма из перечня угроз: поле имени и текст в столбик, кнопка под ними —
   текстовое поле в одну строку с кнопкой не поместится. */
.import-system__form--text { flex-direction: column; align-items: stretch; }
.import-system__form--text input[type="text"] {
    padding: 8px 10px; font: inherit; font-size: 13px; color: var(--text);
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
}
.import-system__form--text .btn { align-self: flex-start; }

.new-system-form { display: flex; gap: 8px; margin-bottom: 18px; }
.new-system-form input[type="text"] {
    flex: 1; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
    color: var(--text); padding: 9px 11px; font-size: 14px; font-family: var(--font-body);
}

/* формы логина/регистрации */
.auth-form { display: flex; flex-direction: column; gap: 14px; max-width: 360px; }
/* Резервные коды второго фактора: их переписывают или копируют глазами, а
   значит важны моноширинность и то, чтобы строка не читалась как абзац. */
.recovery-codes {
    list-style: none; margin: 10px 0 0; padding: 0;
    display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 6px;
}
.recovery-codes code { font: 400 13.5px var(--font-mono); letter-spacing: 0.04em; }
.auth-form .field { display: flex; flex-direction: column; gap: 5px; }
.auth-form label { font-size: 12.5px; color: var(--text-dim); }
.auth-form input {
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
    color: var(--text); padding: 9px 11px; font-size: 14px; font-family: var(--font-body);
}
.auth-form input:focus-visible { outline: none; border-color: var(--accent); }
.field-errors { list-style: none; margin: 2px 0 0; padding: 0; color: var(--danger); font-size: 12px; }
.field-errors li { margin: 0; }
.auth-form__foot { font-size: 13px; color: var(--text-dim); }

/* =====================================================================
   14. СТАТИЧЕСКИЕ СТРАНИЦЫ
   Главная, тактики, типовые пути, охват каталога, «Об авторе»,
   публичный справочник (угрозы, меры, страница тактики).
   ===================================================================== */

/* ---------------------------------------------------------------------
   Главная (project.html).

   Страница долго была одной колонкой сплошной прозы: 920 слов, пять
   одинаково блёклых заголовков и единственная кнопка ниже сгиба. Теперь
   она собрана из четырёх блоков — полоса с действием, маршрут документа,
   справочник и два конструктора, свёрнутые объяснения.

   Своих классов у неё до этого не было вовсе: она верстала себя
   компонентами страницы отчёта (.measure-list, .steps-list, .chip--gap),
   отчего и выглядела как отчёт. Общее из них поднято в секцию 4,
   остальное здесь — и .route__stage намеренно не .step-card из карточки
   системы: там статус шага значит «заполнено/не заполнено», а на главной
   заполнять нечего, и шесть «не заполнено» подряд читались бы как поломка.
   --------------------------------------------------------------------- */
.home-hero {
    display: grid; grid-template-columns: 1fr auto; gap: 24px; align-items: center;
    padding: 20px 22px; background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--radius); margin-bottom: 14px;
}
.home-hero h1 { margin: 0 0 8px; font-size: 26px; line-height: 1.25; }
.home-hero__text p { margin: 0; font-size: 14px; line-height: 1.55; color: var(--text-dim); max-width: 62ch; }
.home-hero__text .prose-list { margin: 6px 0 0; font-size: 14px; color: var(--text-dim); max-width: 62ch; }
.home-hero__cta { display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
.home-hero__cta .btn { justify-content: center; white-space: nowrap; }

/* приказы: не перечисление в скобках внутри абзаца, а видимый список того,
   подо что инструмент вообще годится */
.home-orders { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 20px; font-size: 12.5px; }
.home-orders__label { color: var(--text-dim); }
.home-order { border: 1px solid var(--border); border-radius: 999px; padding: 3px 11px; color: var(--text-dim); }
.home-order b { font: 600 12px var(--font-mono); color: var(--text); margin-right: 5px; }

/* Маршрут документа: шесть этапов и ответвление на контур ИИ.

   Стрелка рисуется в зазоре между карточками псевдоэлементом: разметке она
   не нужна, а на узкой ширине колонка одна и стрелка снимается вместе с
   рядом.
   --------------------------------------------------------------------- */
.route { list-style: none; margin: 0 0 12px; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px 24px; }
.route__stage {
    position: relative; border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); padding: 13px 15px; display: flex; flex-direction: column; gap: 7px;
}
.route__stage::after {
    content: "\2192"; position: absolute; right: -18px; top: 50%; transform: translateY(-50%);
    color: var(--text-dim); font-size: 14px; line-height: 1;
}
.route__stage:nth-child(3n)::after { content: none; }
.route__head { display: flex; align-items: baseline; gap: 8px; }
.route__num {
    font: 600 11px var(--font-mono); color: var(--accent-ink); background: var(--accent);
    border-radius: 50%; width: 19px; height: 19px; display: inline-grid; place-items: center;
    flex-shrink: 0; align-self: center;
}
.route__title { font-size: 14.5px; font-weight: 600; }
.route__ref { font-size: 11.5px; color: var(--text-dim); font-family: var(--font-mono); margin-left: auto; white-space: nowrap; }
.route__text { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--text-dim); }
.route__facts { list-style: none; margin: auto 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.route__facts li {
    font: 600 11.5px var(--font-mono); color: var(--text);
    border: 1px solid var(--border); border-radius: 999px; padding: 2px 9px;
}
.route__links { display: flex; flex-wrap: wrap; gap: 12px; font-size: 12.5px; }
/* этап, который проходится без входа, — единственный такой в ряду */
.route__stage--open { border-color: var(--accent-dim); }

/* ответвление: пунктир и полоса слева — оно не продолжает ряд, а отходит
   от него, и угрозы ИИ размечены не по БДУ */
.route-branch {
    border: 1px dashed var(--border); border-left: 3px solid var(--accent-dim);
    border-radius: var(--radius); background: var(--surface-2);
    padding: 13px 16px; display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px;
}
.route-branch__head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.route-branch__label {
    font: 600 10.5px var(--font-mono); text-transform: uppercase; letter-spacing: .06em;
    color: var(--accent);
}
.route-branch__title { font-size: 14.5px; font-weight: 600; }
.route__note { margin: 0 0 20px; font-size: 12.5px; line-height: 1.55; max-width: 78ch; }

@media (max-width: 1000px) {
    .route { grid-template-columns: repeat(2, 1fr); }
    .route__stage:nth-child(3n)::after { content: "\2192"; }
    .route__stage:nth-child(2n)::after { content: none; }
}
@media (max-width: 640px) {
    .route { grid-template-columns: 1fr; gap: 10px; }
    .route__stage::after, .route__stage:nth-child(3n)::after { content: none; }
}


/* объяснения: свёрнуты, но сохранены целиком */
.home-more-list { display: flex; flex-direction: column; gap: 8px; }
.home-more {
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); padding: 11px 15px;
}
.home-more summary { cursor: pointer; font-size: 14px; font-weight: 600; }
.home-more summary:hover { color: var(--accent); }
.home-more[open] summary { margin-bottom: 12px; }
.home-more__body p { margin: 0 0 10px; font-size: 13.5px; line-height: 1.6; }
.home-more__body p:last-child { margin-bottom: 0; }
.home-more__body .measure-list { margin: 0 0 10px; line-height: 1.5; }
.home-more__body .measure-list:last-child { margin-bottom: 0; }

.home-origin { margin-top: 22px; padding-top: 14px; border-top: 1px solid var(--border); font-size: 12px; }

@media (max-width: 640px) {
    .home-hero { grid-template-columns: 1fr; gap: 16px; padding: 16px 16px 18px; }
    .home-hero h1 { font-size: 21px; }
    .home-hero__cta { flex-direction: row; flex-wrap: wrap; }
}

/* справочник тактик и техник (приложение 11) */
.tactic-block {
    border: 1px solid var(--border); border-radius: var(--radius);
    padding: 12px 14px; margin-bottom: 10px; background: var(--surface-2);
}
.tactic-block summary { cursor: pointer; display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
.tactic-block[open] summary { margin-bottom: 10px; }
.tactic-block__task { margin: 0 0 10px; font-size: 13px; }
.tactic-block__more { margin: 0 0 10px; font-size: 12.5px; }
.tactic-block__more a { color: var(--accent); text-decoration: none; border-bottom: 1px solid var(--accent-dim); }
.technique-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.technique-list li { font-size: 13.5px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius); }

/* справочник: типовые пути нарушителя (шаблоны шага «Сценарии») */
.page-jump { display: flex; flex-wrap: wrap; gap: 8px 14px; margin-top: 10px; font-size: 13px; }
.page-jump a { color: var(--text-dim); text-decoration: none; border-bottom: 1px solid var(--border); }
.page-jump a:hover { color: var(--accent); border-bottom-color: var(--accent-dim); }

.path-card {
    border: 1px solid var(--border); border-radius: var(--radius);
    padding: 14px 16px; margin-bottom: 12px; background: var(--surface-2);
}
.path-card__head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.path-card__head strong { font-size: 15px; }
.path-card__key { font-family: var(--font-mono); font-size: 12px; color: var(--text-dim); margin-left: auto; }
.path-card > .muted { margin: 6px 0 0; font-size: 13.5px; }

.path-card__usage { margin: 4px 0 0; font-size: 12.5px; }
.path-card__chain { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin: 12px 0 0; }
.path-card__arrow { color: var(--text-dim); font-size: 12px; }

.path-card__conditions {
    display: grid; grid-template-columns: minmax(96px, auto) minmax(0, 1fr);
    gap: 6px 12px; margin: 12px 0 0; font-size: 13px;
}
/* Чипы здесь несут фразы, а не коды: без переноса на 320–360 px они
   утаскивали вбок всю страницу /tactics/. */
.path-card__conditions .chip { white-space: normal; }
.path-card__conditions dt { color: var(--text-dim); }
.path-card__conditions dd { margin: 0; display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }

.path-card .gap-card { margin-top: 12px; }
.path-steps { list-style: none; margin: 10px 0 0; padding: 0 0 0 16px; display: flex; flex-direction: column; gap: 16px; position: relative; }
.path-steps::before { content: ""; position: absolute; left: 3px; top: 6px; bottom: 6px; width: 1px; background: var(--border); }
.path-steps > li { position: relative; }
.path-steps > li::before {
    content: ""; position: absolute; left: -16px; top: 7px;
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--accent); box-shadow: 0 0 0 3px var(--surface-2);
}
.path-steps__head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.path-steps__task { margin: 4px 0 8px; font-size: 12.5px; }
.path-card__skipped { margin: 14px 0 0; font-size: 12.5px; }
.rename-form { display: inline-flex; gap: 6px; }
.rename-form input[type="text"] {
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
    color: var(--text); padding: 6px 9px; font-size: 13px; font-family: var(--font-body);
}

/* страница «Охват»: полосы охвата (плитки с числом подняты в секцию 4) */
.coverage-rows { display: flex; flex-direction: column; gap: 14px; }
.coverage-row__head { display: flex; justify-content: space-between; font-size: 13.5px; margin-bottom: 5px; }
.coverage-bar { height: 6px; background: var(--surface-2); border-radius: 3px; overflow: hidden; margin-bottom: 6px; }
.coverage-bar__fill { height: 100%; background: var(--accent); }
.ok-note { color: var(--good); font-size: 13.5px; }

/* Политика конфиденциальности — обычная проза списками.

   Свой класс, а не .measure-list со страницы отчёта: там `li` — это флекс
   «код меры + текст», и абзац, начинающийся с <strong>, разъезжается на две
   колонки, а следующее за ним предложение прилипает к выделенному без
   пробела. Ровно тот случай, о котором написано в шапке файла. */
.policy-list { margin: 0 0 4px; padding-left: 20px; font-size: 13.5px; line-height: 1.6; }
.policy-list li { margin-bottom: 7px; }
.policy-list code { font-family: var(--font-mono); font-size: 12.5px; color: var(--neutral-chip); }

/* страница «Об авторе» */
.author-head { display: flex; align-items: center; gap: 18px; margin-bottom: 20px; }
.author-avatar { width: 76px; height: 76px; border-radius: 50%; border: 1px solid var(--border); }
.author-panel p { line-height: 1.6; font-size: 14px; }
.author-disclaimer { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--border); font-size: 12px; }

/* ---------------------------------------------------------------------
   Публичный справочник каталога: /threats/, /measures/, /tactics/<код>/
   (шаблоны reference_*.html, данные — mapper/reference.py).

   Пять страниц, но верстка у них одна: шапка с кодом, ведущий абзац,
   разделы панелями, снизу переход к соседу и приглашение пройти методику.
   Поэтому классы общие с префиксом .ref-, а не по одному набору на шаблон.

   Таблица здесь своя (.ref-table), а не .beyond-table из отчёта: та значит
   «разрыв с базовым набором» и красит строки по смыслу разрыва — на
   справочнике это читалось бы как ошибка данных.
   --------------------------------------------------------------------- */

/* хлебные крошки: вне .panel, над ней — это навигация по сайту, а не
   содержимое страницы */
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-bottom: 10px; font-size: 12.5px; color: var(--text-dim); }
.crumbs a { color: var(--text-dim); text-decoration: none; border-bottom: 1px solid var(--border); }
.crumbs a:hover { color: var(--accent); border-bottom-color: var(--accent-dim); }

.ref-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.ref-head h1 { margin: 0; font-size: 23px; line-height: 1.3; }
/* Шапка со значком раздела (страница класса средств): знак слева, рубрика и
   название колонкой справа. Свой модификатор, а не правка .ref-head: у страниц
   угрозы и меры знака нет, и рубрика там стоит в строку с заголовком. */
.ref-head--icon { align-items: center; gap: 14px; }
.ref-head__stack { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; min-width: 0; }
.ref-lead { margin: 0 0 16px; font-size: 14px; line-height: 1.6; max-width: 78ch; }
.ref-source { font-size: 12px; margin: 14px 0 0; }
.ref-source a { word-break: break-all; }

/* характеристики угрозы: подпись слева, значение справа — на узком экране
   в одну колонку, иначе значения-предложения сжимаются в столбик из слов */
.ref-meta { display: grid; grid-template-columns: max-content 1fr; gap: 8px 18px; margin: 14px 0 4px; font-size: 13.5px; }
.ref-meta dt { color: var(--text-dim); }
.ref-meta dd { margin: 0; line-height: 1.5; }
/* Чипы в разметке идут вплотную, без пробелов между тегами, — иначе между
   ними появляется лишний зазор. Ценой этого они становятся одной неразрывной
   строкой и на узком экране распирают грид: колонка 1fr не может стать уже
   своего min-content. Поэтому у ячейки с чипами перенос свой, флексовый. */
.ref-meta__chips { display: flex; flex-wrap: wrap; gap: 6px; }
@media (max-width: 640px) {
    .ref-meta { grid-template-columns: 1fr; gap: 2px; }
    .ref-meta dd { margin-bottom: 10px; }
}

/* таблица «мера x приказы» */
/* Фильтр оглавления мер по приказу и уровню (/measures/). Своё имя, а не .ref-orders: тот класс уже занят списком приказов на странице меры. Селекты в ряд с
   кнопкой и сводкой; на узком экране переносятся. */
.ref-picker { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 14px; margin: 0 0 12px; }
.ref-picker__field { display: grid; gap: 3px; font-size: 12px; color: var(--text-dim); }
.ref-picker__field select {
    padding: 6px 8px; font: inherit; font-size: 13px; color: var(--text);
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); max-width: 100%;
}
.ref-picker__summary { font-size: 12.5px; color: var(--text-dim); }
.ref-download { margin: 10px 0 0; }
.ref-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; min-width: 0; }
.ref-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.ref-table th, .ref-table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
.ref-table th { font-size: 12px; color: var(--text-dim); font-weight: 600; white-space: nowrap; }
.ref-table tr:last-child td { border-bottom: 0; }
.ref-table td a { color: var(--text); text-decoration: none; border-bottom: 1px solid var(--border); }
.ref-table td a:hover { color: var(--accent); border-bottom-color: var(--accent-dim); }
.ref-table__code { white-space: nowrap; width: 1%; }

.ref-rationale { margin: 0; font-size: 13.5px; line-height: 1.6; color: var(--text-dim); max-width: 78ch; }
.ref-chain { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 10px 0 0; }
.ref-chain a.chip { text-decoration: none; }
.ref-chain a.chip:hover { color: var(--accent); border-color: var(--accent-dim); }
.ref-final { margin: 14px 0 0; font-size: 13.5px; line-height: 1.6; color: var(--text-dim); }

/* текст меры по каждому приказу */
/* Пояснение «О чём эта мера». Отдельный вид от .ref-order намеренно: там
   текст приказа, здесь наша трактовка, и различить их читатель должен
   глазом, не читая подписи. Карточкам приказов оставлена рамка по кругу,
   пояснению — полоса слева и акцентный цвет; шрифт крупнее (14px против
   13.5px), потому что это первый экран и основной ответ страницы. */
.measure-note { border-left: 3px solid var(--accent-dim); background: var(--surface-2);
                border-radius: var(--radius); padding: 14px 16px; margin: 0 0 22px; }
.measure-note p { margin: 0 0 10px; font-size: 14px; line-height: 1.65; max-width: 78ch; }
.measure-note p:last-child { margin-bottom: 0; }
.measure-note__source { font-size: 12.5px; color: var(--text-dim); margin-bottom: 12px; }

.ref-orders { display: flex; flex-direction: column; gap: 10px; }
.ref-order { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2); padding: 12px 14px; }
.ref-order--absent { border-style: dashed; }
.ref-order__head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 12.5px; }
.ref-order__text { margin: 0; font-size: 13.5px; line-height: 1.6; }
.ref-order__levels { margin: 8px 0 0; font-size: 12.5px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }

/* списки ссылок «какие угрозы закрывает» / «угрозы этой тактики» */
/* min(320px, 100%) вместо 320px: на экране уже 320px жёсткий минимум
   колонки распирал страницу — грид не сжимает колонку ниже minmax-минимума */
.ref-links { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); gap: 4px 16px; }
.ref-links a { display: flex; align-items: baseline; gap: 8px; padding: 5px 0; font-size: 13px; color: var(--text); text-decoration: none; }
.ref-links a:hover { color: var(--accent); }

.ref-paths { display: flex; flex-direction: column; gap: 12px; }
.ref-path { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2); padding: 12px 14px; }
.ref-path h3 { margin: 0 0 6px; font-size: 14.5px; }
.ref-path p { margin: 0; font-size: 12.5px; line-height: 1.55; }

/* оглавление угроз: код, название, среда, число мер — четыре колонки,
   на узком экране сворачиваются в две строки */
.ref-index__legend { font-size: 12px; margin: 10px 0 4px; }
.ref-index { list-style: none; margin: 0; padding: 0; }
.ref-index__row { display: grid; grid-template-columns: 1fr max-content 3ch; align-items: baseline; gap: 12px; padding: 6px 0; border-bottom: 1px solid var(--border); }
.ref-index__row.is-hidden { display: none; }
.ref-index__row > a { display: flex; align-items: baseline; gap: 9px; color: var(--text); text-decoration: none; min-width: 0; }
.ref-index__row > a:hover .ref-index__name { color: var(--accent); }
.ref-index__name { font-size: 13.5px; line-height: 1.45; }
.ref-index__facet { font-size: 12px; text-align: right; }
.ref-index__count { font-size: 12px; font-family: var(--font-mono); text-align: right; }
.ref-table tr.is-hidden { display: none; }

/* Страница сквозного поиска (/search/). Строки те же, что в оглавлениях
   справочника, но колонок две, а не четыре: у найденного нет ни среды, ни
   числа мер — их место занимает одна подпись, разная по смыслу у разных
   видов (среда у угрозы, коды в четырёх приказах у меры, найденные техники у
   тактики). Третья колонка в 3ch осталась бы пустым отступом справа. */
.ref-index--search .ref-index__row { grid-template-columns: 1fr max-content; }
/* Имя класса средств защиты — это и есть его «код», и оно бывает в три
   слова. nowrap базового .chip рассчитан на «УБИ.006»; здесь чип обязан
   переноситься, иначе он один занимает половину строки. */
.ref-index--search .chip--code { white-space: normal; }
.ref-index__meta { font-size: 12px; text-align: right; color: var(--text-dim); max-width: 30ch; }

/* Поле поиска на своей странице — с кнопкой: без JS отправляет только она,
   а страница обязана работать и без него, как и остальной справочник. */
.search--page { margin: 16px 0 4px; }
.search--page input[type="search"] { font-size: 15px; padding: 12px 14px; }
.search-summary { color: var(--text-dim); font-size: 13px; margin: 14px 0 0; }
/* Узкий экран: код с названием — первой строкой во всю ширину, среда и число
   мер — второй, рядом друг с другом и по левому краю. Раньше среда уходила на
   свою строку, а число оставалось прижатым к правому краю ещё ниже — одна
   угроза читалась как три несвязанных обрывка, а голое число вдалеке от
   строки не читалось никак. */
/* Здесь флексом, а не сеткой: колонка сетки шириной max-content считает свою
   ширину и по элементу, растянутому на все колонки, — ссылка с названием
   угрозы раздувала колонку среды до своей ширины, и число уезжало на третью
   строку. */
@media (max-width: 720px) {
    .ref-index__row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; }
    .ref-index__row > a { flex: 1 0 100%; }
    .ref-index__facet, .ref-index__count, .ref-index__meta { text-align: left; }
    .ref-index__count::after { content: " мер"; font-family: var(--font-body); }
}

/* ---------------------------------------------------------------------
   Классы средств защиты (/tools/ и /tools/<класс>/).

   Свои классы, а не .ref-index__row из указателя угроз: там строка — «код,
   название, среда, число», то есть таблица без таблицы, и главное в ней
   выравнивание колонок. Здесь у каждого пункта есть предложение-описание,
   и та же сетка сжала бы его в колонку шириной в слово.

   .tool-inline — тот же перечень на странице меры, но не карточками: он там
   не главное содержимое, а ответ на попутный вопрос «чем закрывают», и
   карточки перебивали бы текст самой меры выше.
   --------------------------------------------------------------------- */
.tool-grid {
    list-style: none; margin: 0 0 22px; padding: 0; display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: 10px;
}
.tool-card {
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); padding: 12px 14px; display: flex;
    flex-direction: column; gap: 6px;
}
.tool-card.is-hidden { display: none; }
.tool-card > a { display: flex; align-items: flex-start; gap: 10px; text-decoration: none; color: var(--text); }
.tool-card > a:hover .tool-card__title { color: var(--accent); }
.tool-card__title { font-size: 14.5px; font-weight: 600; }
.tool-card__full { font-size: 12px; color: var(--text-dim); line-height: 1.4; }
.tool-card__summary { margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--text-dim); }
.tool-card__foot { margin: auto 0 0; display: flex; flex-wrap: wrap; gap: 6px; padding-top: 4px; }

/* Значок раздела. Спрайт с символами — в `_tool_icons.html`, там же записано,
   почему значок один на раздел, а не на класс.

   Плашка со скруглением, а не голая линия: знак сайта — такая же скруглённая
   плашка, и карточка класса читается как его младший родственник. Заливка
   --surface на карточке --surface-2, то есть на тон в сторону от неё, — так
   плашка видна в обеих темах, не заводя третьего цвета.

   Цвет — приглушённый текст, а не акцент: акцент на сайте означает «сюда
   нужно посмотреть», а раздел ничего не требует. На наведении карточки знак
   уходит в акцент вместе с названием, чтобы цель ссылки читалась целиком. */
.tool-icon {
    flex: none; width: 30px; height: 30px; padding: 5px; border-radius: 8px;
    background: var(--surface); border: 1px solid var(--border);
    color: var(--text-dim); fill: none; stroke: currentColor;
    stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.tool-icon--lg { width: 40px; height: 40px; padding: 7px; border-radius: 10px; }
.tool-icon--sm { width: 22px; height: 22px; padding: 3px; border-radius: 6px; }
.tool-card > a:hover .tool-icon,
.tool-inline a:hover .tool-icon { color: var(--accent); border-color: var(--accent-dim); }

/* Спрайт не рисуется, но и не `display: none`: скрытый так контейнер в части
   браузеров перестаёт отдавать символы по `<use>`. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Шапка карточки: знак слева, название и расшифровка колонкой справа. */
.tool-card__name { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/* Синонимы класса. Флекс по той же причине, что у .ref-meta__chips на
   странице угрозы: чипы в разметке идут вплотную, без пробелов между тегами
   (иначе между ними появляется лишний зазор), и потому становятся одной
   неразрывной строкой — на 320px она вылезала за экран на 34px. Свой класс,
   а не .ref-meta__chips: тот принадлежит характеристикам угрозы и живёт в
   ячейке грида, здесь же это обычный абзац. */
.tool-aliases { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; }

.tool-inline { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.tool-inline li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; font-size: 13px; line-height: 1.55; }
.tool-inline a { display: inline-flex; align-items: center; gap: 8px; text-decoration: none; }
.tool-inline__title {
    color: var(--text); font-weight: 600; white-space: nowrap;
    border-bottom: 1px solid var(--border);
}
.tool-inline a:hover .tool-inline__title { color: var(--accent); border-bottom-color: var(--accent-dim); }
.tool-inline .muted { flex: 1 1 260px; font-size: 12.5px; }

/* ---------------------------------------------------------------------
   Конструктор перечня СЗИ (/tools/ с параметрами).

   Свои классы, а не форма шага мастера: там форма — единственное
   содержимое страницы и её состояние сохраняется, здесь она стоит над
   справочником и живёт целиком в адресе. Карточка результата, наоборот,
   намеренно наследует .tool-card справочника ниже — это тот же класс
   средств, и выглядеть он должен так же; .kit-card добавляет к нему
   только левую полосу ступени и место под пояснения.
   --------------------------------------------------------------------- */
.kit-form { margin: 0 0 22px; }
.kit-form__grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
    gap: 12px; margin-bottom: 12px;
}
.kit-field {
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); padding: 12px 14px; margin: 0;
}
.kit-field legend { padding: 0 6px; font-size: 13px; font-weight: 600; color: var(--accent); }
.kit-field__hint { margin: 0 0 10px; font-size: 12px; line-height: 1.5; color: var(--text-dim); }
.kit-field--wide { margin-bottom: 12px; }

/* Блок «меры вашей системы». Выделен рамкой акцента, а не спрятан среди
   остальных полей: он появляется только при переходе с шага «Меры защиты»,
   и это единственное на странице, чего в справочнике не бывает — если его
   не заметить, перечень молча соберётся по одному базовому набору. */
.kit-basis { border-color: var(--accent); background: var(--surface); }
.kit-basis .kit-check__title { font-size: 13.5px; font-weight: 600; }
.kit-basis .kit-check__hint { font-size: 12px; }

.kit-levels { display: flex; flex-direction: column; gap: 8px; }
.kit-level { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 10px; font-size: 13px; }
.kit-level__name { line-height: 1.35; }
.kit-level select { padding: 5px 8px; font-size: 12.5px; }

.kit-radio {
    display: grid; grid-template-columns: auto 1fr; gap: 2px 8px;
    align-items: baseline; padding: 5px 0; cursor: pointer;
}
.kit-radio input { grid-row: span 2; }
.kit-radio__title { font-size: 13.5px; }
.kit-radio__hint { grid-column: 2; font-size: 12px; color: var(--text-dim); line-height: 1.45; }

.kit-contexts {
    list-style: none; margin: 0; padding: 0; display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr)); gap: 6px 14px;
}
.kit-check { display: grid; grid-template-columns: auto 1fr; gap: 1px 8px; align-items: baseline; cursor: pointer; }
.kit-check input { grid-row: span 2; }
.kit-check__title { font-size: 13px; }
.kit-check__hint { grid-column: 2; font-size: 11.5px; color: var(--text-dim); line-height: 1.4; }

.kit-form__actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }

.kit-summary { margin-bottom: 12px; }
.kit-summary__for { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; margin: 0 0 6px; }
.kit-summary p.muted { margin: 0; font-size: 13px; line-height: 1.55; }
.kit-summary__warn { margin: 8px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--danger); }

.kit-section { margin: 20px 0 10px; font-size: 14.5px; }
/* Заголовок блока ступени. Ступень названа чипом того же цвета, что раньше
   стоял в подвале каждой карточки: цвет за ступенью уже закреплён, и перенести
   его вместе со смыслом дешевле, чем заводить для заголовка второй язык.
   Пояснение — отдельным абзацем под заголовком, а не в общей расшифровке
   внизу: до неё при 61 классе в перечне надо домотать. */
.kit-section--tier {
    display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
    margin: 26px 0 4px; padding-left: 11px; border-left: 3px solid var(--border);
}
/* Цвет ступени тот же, что у полосы слева на карточках этой ступени: язык
   один, и заголовок блока читается как их общая шапка, а не как отдельная
   раскраска. */
.kit-section--base { border-left-color: var(--accent); }
.kit-section--layer { border-left-color: var(--neutral-chip-dim); }
.kit-section--depth { border-left-color: var(--border); }
.kit-section--base .kit-section__name { color: var(--accent); }
.kit-section--layer .kit-section__name { color: var(--neutral-chip); }
.kit-section--depth .kit-section__name { color: var(--text-dim); }
.kit-section__name { font-size: 15px; }
.kit-section__count { font: 600 12px var(--font-mono); color: var(--text-dim); font-variant-numeric: tabular-nums; }
.kit-section__hint { margin: 0 0 12px; padding-left: 14px; max-width: 78ch; }

/* Виджеты сводки. Свои, а не .stat-card с главной: там плитка считает
   каталог целиком и живёт в раскладке героя, здесь это узкая строка чисел
   про один собранный перечень, и та плитка растянула бы её на пол-экрана. */
.kit-stats {
    list-style: none; margin: 0 0 12px; padding: 0; display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(128px, 100%), 1fr)); gap: 8px;
}
.kit-stats li {
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); padding: 9px 12px;
    display: flex; flex-direction: column; gap: 2px;
}
.kit-stat__value { font-size: 20px; font-weight: 600; font-family: var(--font-mono); }
.kit-stat__label { font-size: 11.5px; line-height: 1.35; color: var(--text-dim); }

.kit-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 14px; }
.kit-actions .muted { font-size: 12px; flex: 1 1 240px; }

/* Расшифровка обозначений. Печатается всегда, а не подсказкой при
   наведении: «Второй рубеж» — придуманное здесь понятие, а не термин
   приказа, и на телефоне наведения нет вовсе. Та же расшифровка уезжает в
   выгрузку (toolkit_content._legend). */
.kit-legend {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
    gap: 10px 24px; margin: 0 0 6px; padding: 12px 14px;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2);
}
.kit-legend__head { margin: 0 0 8px; font-size: 12.5px; color: var(--text-dim); font-weight: 600; }
.kit-legend dl { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: 6px 10px; }
.kit-legend dt { margin: 0; }
.kit-legend dd { margin: 0; font-size: 12px; line-height: 1.45; color: var(--text-dim); }
@media (max-width: 520px) {
    .kit-legend dl { grid-template-columns: 1fr; gap: 2px; }
    .kit-legend dd { margin-bottom: 6px; }
}

/* Номер позиции. Сквозной по всему перечню, поэтому стоит у названия, а не
   нумерует список: карточки разложены по разделам, и порядковый номер
   внутри раздела ссылкой на позицию не был бы. */
.kit-card__number {
    display: inline-block; min-width: 2.2ch; margin-right: 7px;
    font-family: var(--font-mono); font-size: 12px; color: var(--text-dim);
}

/* Ступень сборки — полосой слева, а не только чипом: чип читается, когда
   карточку уже нашли глазами, а полоса отвечает на вопрос «что здесь
   обязательное» на весь экран сразу. */
.kit-card { border-left: 3px solid var(--border); }
.kit-card--base { border-left-color: var(--accent); }
.kit-card--layer { border-left-color: var(--neutral-chip-dim); }
.kit-card--depth { border-left-color: var(--border); }
.kit-card__note { margin: 0; font-size: 12px; line-height: 1.5; color: var(--text-dim); }

.chip--tier-base { color: var(--accent); border-color: var(--accent-dim); }
.chip--tier-layer { color: var(--neutral-chip); border-color: var(--neutral-chip-dim); }
.chip--tier-depth { color: var(--text-dim); border-color: var(--border); }

.kit-measures summary { font-size: 12px; color: var(--text-dim); cursor: pointer; font-family: var(--font-mono); }
.kit-measures ul { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.kit-measures li { font-size: 12.5px; line-height: 1.5; }
.kit-measures .muted { font-size: 11.5px; }

.kit-measure-list { list-style: none; margin: 0 0 14px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.kit-measure-list li { font-size: 13px; line-height: 1.55; }
.kit-measure-list .muted { font-size: 11.5px; }
.kit-context-head { margin: 12px 0 6px; font-size: 13px; color: var(--text); }

.kit-drop-list { margin: 0 0 12px; padding-left: 20px; font-size: 13px; line-height: 1.6; }

.kit-fold {
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); padding: 10px 14px; margin-bottom: 12px;
}
.kit-fold > summary { font-size: 13px; cursor: pointer; }
.kit-fold[open] > summary { margin-bottom: 10px; }
.kit-fold p.muted { font-size: 12.5px; line-height: 1.55; }

@media (max-width: 520px) {
    .kit-level { grid-template-columns: 1fr; gap: 4px; }
}

/* приглашение пройти методику — общий низ всех страниц справочника */
.ref-cta { display: grid; grid-template-columns: 1fr auto; gap: 20px; align-items: center; }
.ref-cta h2 { margin: 0 0 6px; font-size: 17px; }
.ref-cta p { margin: 0; font-size: 13px; line-height: 1.55; max-width: 70ch; }
.ref-cta__actions { display: flex; flex-direction: column; gap: 8px; }
.ref-cta__actions .btn { justify-content: center; white-space: nowrap; }
@media (max-width: 720px) {
    .ref-cta { grid-template-columns: 1fr; }
}

/* переход к соседней странице справочника */
.ref-nav { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 14px; }
.ref-nav__link {
    display: flex; flex-direction: column; gap: 3px; padding: 11px 14px; font-size: 13px;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
    color: var(--text); text-decoration: none;
}
.ref-nav__link:hover { border-color: var(--accent-dim); }
.ref-nav__link--next { text-align: right; }
.ref-nav__link .muted { font-family: var(--font-mono); font-size: 11.5px; }
@media (max-width: 640px) {
    .ref-nav { grid-template-columns: 1fr; }
    .ref-nav__link--next { text-align: left; }
}

/* ---------------------------------------------------------------------
   Страницы ошибок (404, «восстановление недоступно»).

   Свой список, а не .measure-list из секции 4: у той `li` — флекс, потому что
   её пункт всегда «чип + текст», а флекс-контейнер выбрасывает текстовые узлы
   из одних пробелов. Пункт здесь — обычная фраза со ссылкой внутри, и на
   разметке `<a>Все угрозы БДУ</a> — страница...` пробел перед тире исчезал:
   на странице печаталось «Все угрозы БДУ— страница». Ровно тот случай, ради
   которого в оглавлении записано правило владения классами.
   --------------------------------------------------------------------- */
.error-list { margin: 0; padding-left: 20px; font-size: 13.5px; }
.error-list li { margin-bottom: 8px; }

/* =====================================================================
   15. ПЕЧАТЬ
   ===================================================================== */

@media print {
    .topbar, .footer, .theme-toggle, .panel__actions, .wizard, .wizard-nav, .wizard-back,
    .report-toc, .facet-bar, .search, .skip-link,
    form button[type="submit"], .btn { display: none !important; }
    body { background: #fff; color: #000; }
    .panel { border: none; padding: 0; }
    .page { max-width: 100%; }
    a { color: #000; text-decoration: underline; }
    .result-card, .gap-card, .callout { break-inside: avoid; border-color: #999; }
    /* Справочник: служебное не печатается, а широкая таблица мер печатается
       целиком — на экране она прокручивается внутри .ref-scroll, и на бумаге
       та же прокрутка означала бы обрезанные столбцы приказов. */
    .crumbs, .ref-nav, .ref-cta, .ref-picker, .ref-download, .draft-bar { display: none !important; }
    .ref-scroll { overflow: visible !important; }
    .ref-table { font-size: 10.5px; }
    .ref-table th, .ref-table td { padding: 4px 6px; }
    .chip { border-color: #999; color: #000; }
}
/* Печать всегда светлая. Фон браузер не печатает, а цвета компонентов
   берутся из переменных темы: из тёмной темы текст выходил светло-серым
   на белой бумаге. */
@media print {
    :root, :root[data-theme="dark"] {
        --bg: #fff; --surface: #fff; --surface-2: #fff; --border: #999;
        --text: #000; --text-dim: #444;
        --accent: #7a4a0c; --accent-dim: #7a4a0c; --accent-ink: #fff;
        --danger: #a01820; --good: #1f6b43;
        --neutral-chip: #1f4f6b; --neutral-chip-dim: #999; --level-3: #7a3a08;
    }
}

/* =====================================================================
   16. АДАПТИВ, НЕ ПРИВЯЗАННЫЙ К КОМПОНЕНТУ
   Только то, что не принадлежит ни одному компоненту. Медиазапрос
   компонента пишется рядом с ним — см. шапку сайта в секции 3.
   ===================================================================== */

@media (max-width: 640px) {
    .page { padding: 16px 12px 40px; }
    .panel { padding: 16px; }
    .threat-row__code { min-width: 62px; font-size: 11.5px; }
    .panel__head { flex-direction: column; }
    /* Название способа и чип «не по модели нарушителя» в одну строку не
       помещаются: чип не сжимается, и название вставало столбиком по слову. */
    .intruder-row label { flex-wrap: wrap; }
    /* Легенда отчёта: чип и пояснение в одну flex-строку на 320 px не
       помещались, пояснение идёт текстом следом за чипом. */
    .legend__body div { display: block; }
    /* Строка меры: два чипа с кодом и статусом плюс длинное первое слово
       названия на 320 px не помещались в одну flex-строку. */
    .chip-cloud li, .measure-list li { flex-wrap: wrap; }
}

@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }
}

@media (max-width: 640px) {
    /* .order-picker здесь нет намеренно: в боковой ленте приказы № 21, № 31,
       № 117 со списками уровней уезжали за край без признака прокрутки. */
    .tech-picker__grid, .facet-bar { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; -webkit-overflow-scrolling: touch; }
    .order-tab, .tech-chip, .facet-chip { min-height: 36px; }
    .threat-row label { min-height: 40px; }
    .wizard__item a,
    .wizard__item button { min-height: 36px; }
    /* Маленькие кнопки («Изменить», «Все главы») на пальце: 22 px мало. */
    .btn--small { min-height: 36px; }
    .report-toc { position: static; }
    .step-grid { grid-template-columns: 1fr; }
    .system-hero { grid-template-columns: auto 1fr; }
    .system-hero__cta { grid-column: 1 / -1; flex-direction: row; flex-wrap: wrap; }
    .chapter-row { grid-template-columns: 20px 1fr auto; row-gap: 2px; }
    .chapter-row__fill { grid-column: 2 / -1; text-align: left; }
    .system-menu__panel { width: min(260px, calc(100vw - 60px)); }
}

/* ---------------------------------------------------------------------
   История версий системы (system_versions.html)

   Строка версии — время, числа по шагам и кнопка возврата. Ряд, а не
   таблица: столбец «числа» у разных версий разной длины, и таблица либо
   растягивала бы время на треть ширины, либо переносила числа по одному
   в ячейке. Текущая версия отмечена полосой слева — это единственная
   строка без кнопки, и без метки она читалась бы как сломанная.
   --------------------------------------------------------------------- */
.version-list { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.version {
    display: grid; grid-template-columns: 150px 1fr auto; gap: 14px; align-items: center;
    border: 1px solid var(--border); border-radius: var(--radius);
    padding: 10px 14px; background: var(--surface);
}
.version--current { border-left: 3px solid var(--accent); padding-left: 12px; }
.version__when { display: flex; flex-direction: column; gap: 2px; }
.version__actions { display: flex; flex-wrap: wrap; gap: 6px; }

/* Массовые кнопки над списком угроз шага 5. */
.list-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 8px; }

/* Страница «Основания для снятых угроз»: строка — код, название, выбор. */
.grounds-bulk { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 10px 0; }
.grounds-row { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; padding: 8px 12px; font-size: 14px; }
.grounds-row .threat-row__name { flex: 1 1 260px; }
.grounds-row select { flex: 0 1 330px; max-width: 100%; }
.version__time { font-variant-numeric: tabular-nums; font-size: 13.5px; }
.version__badge { font-size: 11.5px; color: var(--accent); }
.version__summary { margin: 0; color: var(--text-dim); font-size: 13px; line-height: 1.5; }

@media (max-width: 620px) {
    .version { grid-template-columns: 1fr; gap: 8px; }
    .version__when { flex-direction: row; align-items: baseline; gap: 8px; }
}

/* Сведения об аккаунте в личном кабинете: подпись и значение парой. Список
   определений, а не таблица: пар три, и таблица ради трёх строк требует
   заголовков, которых здесь нет. */
.account-facts { margin: 0 0 22px; display: flex; flex-direction: column; gap: 6px; }
.account-facts > div { display: flex; gap: 10px; flex-wrap: wrap; align-items: baseline; }
.account-facts dt { color: var(--text-dim); font-size: 13px; min-width: 180px; }
.account-facts dd { margin: 0; font-size: 14px; }

/* Чип «к каким последствиям ведёт угроза» (шаг «Угрозы»). Нейтральный, а не
   предупреждающий: это справка, а не замечание, и спорить цветом с чипом
   «нет вашего последствия» в соседней строке ему незачем. */
.chip--link { border-color: var(--border); color: var(--text-dim); font-variant-numeric: tabular-nums; }

/* Заголовок группы угроз на странице угроз ИИ. Свой, а не .kit-section с
   конструктора перечня СЗИ: тот принадлежит своей странице и знает про её
   отступы, а переиспользование класса чужой страницы — ровно то, из-за чего
   .tech-chip уехал в выгрузку документа.

   Не .section-title: тот — мелкая подпись капителью с чертой сверху, и восемь
   групп, набранных ею вперемешку с четырьмя разделами страницы, читались бы
   как двенадцать равных разделов. Здесь нужен второй уровень: заметно мельче
   заголовка страницы и заметно крупнее подписи раздела. */
.ai-group { margin: 22px 0 10px; font-size: 15.5px; }

/* ---------------------------------------------------------------------
   Справочник угроз ИИ (/ai-threats/).

   Страница была набрана одними .tactic-block: двадцать два объекта,
   тридцать семь угроз и пятьдесят один способ шли ста десятью одинаковыми
   серыми полосками, у которых в свёрнутом виде видны только код и название.
   Ни состав каталога, ни связь угрозы со способом, ни распределение
   способов по тактикам из такой страницы не читались, а перечня из
   тридцати семи угроз без фильтра хватает, чтобы искать в нём Ctrl+F.

   Свои классы, а не .tactic-block и .path-card: те принадлежат справочнику
   приложения 11 и справочнику путей нарушителя и знают про их содержимое.
   Переиспользование класса чужой страницы — ровно то, из-за чего .tech-chip
   уехал в выгрузку документа (см. оглавление файла).
   --------------------------------------------------------------------- */

.ai-stats { margin-top: 16px; }

.ai-lead { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 16px 0 4px; }
.ai-lead .muted { flex: 1 1 280px; }

/* Схема осей каталога. Ряд из четырёх звеньев со связками между ними.

   Сетка «1fr auto» семь раз, а не flex с переносом: у звеньев и связок
   ширины разного порядка (~160px против ~70px), и при переносе по
   содержимому последнее звено уезжало на свою строку в одиночку, а связка
   перед ним оставалась висеть в конце предыдущей. Колонки auto забирают
   ровно ширину подписи связки, звенья делят остаток поровну. */
.ai-chain {
    list-style: none; margin: 14px 0 18px; padding: 0;
    display: grid; grid-template-columns: repeat(3, 1fr auto) 1fr;
    align-items: stretch; gap: 8px;
}
.ai-chain__link {
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); padding: 12px 14px;
    display: flex; flex-direction: column; gap: 4px;
}
.ai-chain__count {
    font: 700 20px/1 var(--font-mono); color: var(--accent);
    font-variant-numeric: tabular-nums;
}
.ai-chain__link strong { font-size: 14px; }
.ai-chain__code {
    font: 600 11px var(--font-mono); color: var(--neutral-chip);
    border: 1px solid var(--neutral-chip-dim); border-radius: 3px;
    padding: 1px 5px; margin-left: 4px; vertical-align: 1px;
}
.ai-chain__note { font-size: 12px; line-height: 1.5; color: var(--text-dim); }
/* Связка. Черта рисуется псевдоэлементом и тянется на всю высоту звена,
   поэтому подпись читается как надпись на соединении, а не как ещё одна
   плитка ряда. */
.ai-chain__joint {
    flex: 0 0 auto; align-self: center;
    font-size: 11.5px; color: var(--text-dim); white-space: nowrap;
    padding: 0 2px;
}
@media (max-width: 900px) {
    .ai-chain { grid-template-columns: 1fr; }
    .ai-chain__joint { align-self: flex-start; padding-left: 14px; }
}

/* Этапы жизненного цикла. Три карточки в ряд: порядок «данные, обучение,
   эксплуатация» — последовательность, и в столбик она читается так же, а в
   свёрнутых <details>, как было раньше, не читалась вовсе. */
.ai-stages { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 6px; align-items: start; }
.ai-stage {
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); padding: 12px 14px;
}
.ai-stage__title { margin: 0; font-size: 14px; }
.ai-stage__counts { margin: 2px 0 10px; font-size: 12px; color: var(--accent); font-variant-numeric: tabular-nums; }
.ai-stage__objects { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.ai-stage__objects li { display: flex; align-items: baseline; font-size: 12.5px; line-height: 1.45; }
.ai-stage__objects .muted { font-size: 12px; }
@media (max-width: 860px) { .ai-stages { grid-template-columns: 1fr; } }

/* Фильтр перечня угроз. Три вида фасетов подписаны словом слева: без
   подписи ряд из «Все / Данные / Обучение / Эксплуатация» и ряд из
   «Любому / Н1 / Н2 / Н3 / Н4» читаются как один длинный набор кнопок, в
   котором непонятно, что с чем взаимоисключающе. */
.ai-filter { margin-bottom: 14px; }
.ai-filter .search { margin-bottom: 10px; }
.ai-facets { display: flex; flex-direction: column; gap: 6px; }
/* Подпись вынесена из .facet-bar в обёртку не ради вида. На узком экране
   .facet-bar по общему правилу мобильных правок превращается в полосу с
   горизонтальной прокруткой, и подпись внутри неё уезжала бы вместе с
   кнопками, а на 320px занимала бы всю видимую часть полосы. */
.ai-facet { display: flex; align-items: center; gap: 12px; }
.ai-facet .facet-bar { margin-bottom: 0; align-items: center; min-width: 0; }
.ai-facet__label {
    font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-dim); flex: 0 0 148px;
}
/* Число внутри чипа моноширинное: иначе счётчики в ряду не выстраиваются и
   ряд выглядит рваным. */
.ai-facets__n { font-family: var(--font-mono); font-size: 11px; opacity: .75; }
@media (max-width: 640px) {
    .ai-facets { gap: 10px; }
    .ai-facet { flex-direction: column; align-items: stretch; gap: 4px; }
    .ai-facet__label { flex: 0 0 auto; }
}

/* Карточка угрозы и карточка способа. Одна и та же: в обеих сверху строка,
   которую читают взглядом (код, название, метки), а под ней описание и
   связи, которые читают по одной штуке за раз. */
.ai-group-block { margin-bottom: 8px; }
.ai-card {
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); margin-bottom: 6px;
}
.ai-card__head {
    cursor: pointer; list-style: none;
    display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
    padding: 9px 12px;
    -webkit-user-select: none; user-select: none;
}
.ai-card__head::-webkit-details-marker { display: none; }
.ai-card__head:hover .ai-card__name { color: var(--accent); }
.ai-card[open] > .ai-card__head { border-bottom: 1px solid var(--border); }
.ai-card__name { font-size: 14px; font-weight: 600; flex: 1 1 320px; }
/* Метки прижаты вправо и не переносятся по одной: это одна группа значений
   об угрозе, и разорванная посередине она читается как две разные. */
.ai-card__marks { display: flex; align-items: baseline; gap: 8px; margin-left: auto; }
/* На телефоне группа с пометкой «и без нарушителя» шире экрана, и перенос
   внутри неё — меньшее зло, чем страница, уехавшая вбок. */
@media (max-width: 480px) {
    .ai-card__marks { flex-wrap: wrap; justify-content: flex-end; }
}
/* Свойства нарушаются не все сразу, и ширина слота задана под три метки:
   иначе уровень и число способов в соседних строках стоят каждый на своём
   месте и колонкой не читаются. */
.ai-card__props { display: inline-flex; gap: 4px; justify-content: flex-end; min-width: 74px; }
.ai-card__count {
    font: 600 11.5px var(--font-mono); color: var(--text-dim);
    font-variant-numeric: tabular-nums; white-space: nowrap;
    min-width: 82px; text-align: right;
}
.ai-card__body { padding: 12px; }
.ai-card__text { margin: 0 0 12px; font-size: 13.5px; line-height: 1.6; }
.ai-card__label {
    margin: 14px 0 8px; font-size: 11.5px; text-transform: uppercase;
    letter-spacing: 0.04em; color: var(--text-dim);
}

/* Метка нарушаемого свойства. Буква, а не слово: три слова целиком забирают
   строку, которая нужна под название угрозы, а К/Ц/Д — обозначение, принятое
   в самих моделях угроз. Цвет закреплён за свойством и одинаков на всей
   странице; полное название приходит в title. */
.ai-mark {
    font: 700 11px/1 var(--font-mono); border-radius: 3px;
    border: 1px solid; padding: 3px 5px; cursor: help;
}
.ai-mark--К { color: var(--neutral-chip); border-color: var(--neutral-chip-dim); }
.ai-mark--Ц { color: var(--accent); border-color: var(--accent-dim); }
.ai-mark--Д { color: var(--good); border-color: var(--good); }

/* Осведомлённость. Своя метка, а не .chip--level-*: те закреплены за шкалой
   возможностей нарушителя, и способ, у которого рядом стоят «Н3» и
   «grey-box», не должен выглядеть как угроза с двумя уровнями. */
.ai-aware {
    font: 600 11px var(--font-mono); border-radius: 3px;
    border: 1px solid var(--border); color: var(--text-dim);
    padding: 3px 6px; white-space: nowrap;
}
.ai-aware--black { border-color: var(--border); }
.ai-aware--grey { border-color: var(--neutral-chip-dim); color: var(--neutral-chip); }
.ai-aware--white { border-color: var(--accent-dim); color: var(--accent); }

/* Характеристики карточки. Подпись слева фиксированной ширины: три строки с
   подписями разной длины иначе не выстраиваются в столбец, и значения в них
   приходится искать заново в каждой строке. */
.ai-facts { margin: 0; display: flex; flex-direction: column; gap: 7px; }
.ai-facts > div { display: flex; gap: 12px; flex-wrap: wrap; align-items: baseline; }
.ai-facts dt { font-size: 11.5px; color: var(--text-dim); min-width: 150px; }
.ai-facts dd { margin: 0; font-size: 13px; flex: 1 1 320px; line-height: 1.6; }

/* Перечень связей карточки: способы под угрозой и угрозы под способом. Код
   ссылкой — на карточку по ту сторону связи; раскрытие свёрнутой карточки по
   такой ссылке делает ai_catalog.js. */
.ai-links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.ai-links li { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: 13px; }
.ai-links li > span:first-of-type { flex: 1 1 300px; }
.ai-links .muted { font-size: 12px; white-space: nowrap; }
.ai-links a.chip { text-decoration: none; }
.ai-links a.chip:hover { color: var(--accent); border-color: var(--accent-dim); }

/* Плитки тактик ATLAS. Число сверху и полоска снизу отвечают на вопрос, где
   приёмов против моделей больше всего: в «обходе защиты» их двенадцать, в
   четырёх тактиках — по одному, и из списка свёрнутых блоков это не видно. */
.ai-tactics {
    list-style: none; margin: 14px 0 18px; padding: 0;
    display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
}
.ai-tactic {
    display: flex; flex-direction: column; gap: 2px; height: 100%;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); padding: 10px 12px;
    text-decoration: none; color: var(--text);
}
.ai-tactic:hover { border-color: var(--accent-dim); }
.ai-tactic:hover .ai-tactic__ru { color: var(--accent); }
.ai-tactic__n { font: 700 17px/1 var(--font-mono); color: var(--accent); font-variant-numeric: tabular-nums; }
.ai-tactic__ru { font-size: 13px; font-weight: 600; }
.ai-tactic__en { font-size: 11px; color: var(--text-dim); margin-bottom: 6px; }
.ai-tactic__bar { display: block; height: 3px; border-radius: 2px; background: var(--border); margin-top: auto; padding-top: 0; }
.ai-tactic__bar i { display: block; height: 100%; border-radius: 2px; background: var(--accent-dim); }
@media (max-width: 860px) { .ai-tactics { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 560px) { .ai-tactics { grid-template-columns: repeat(2, 1fr); } }

.ai-tactic-block { margin-bottom: 8px; }
/* Отступ сверху под приклеенную шапку добавляет anchor_offset.js через
   scroll-padding; здесь якорю нужна только собственная цель прокрутки. */
.ai-tactic-block > .ai-group { margin-top: 26px; }

/* Шкалы нарушителя. Полоска считается от самой полной ступени, а не от 51:
   доли от общего числа дали бы четыре почти одинаковых огрызка. */
.ai-scale { list-style: none; margin: 12px 0 10px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.ai-scale li {
    display: flex; align-items: flex-start; gap: 12px;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface-2); padding: 10px 12px;
}
.ai-scale__text { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; }
.ai-scale__text strong { font-size: 13.5px; }
.ai-scale__text .muted { font-size: 12.5px; line-height: 1.5; }
.ai-scale__meter { flex: 0 0 110px; text-align: right; }
.ai-scale__n { font: 700 14px var(--font-mono); color: var(--accent); font-variant-numeric: tabular-nums; }
.ai-scale__meter .coverage-bar { display: block; margin: 6px 0 0; }
.ai-scale__meter .coverage-bar__fill { display: block; }
@media (max-width: 560px) {
    .ai-scale li { flex-wrap: wrap; }
    .ai-scale__meter { flex: 1 1 100%; text-align: left; }
}

/* Скрытие фильтром. Своё правило, а не .technique-list li.is-hidden: то
   привязано к спискам техник и на <details> и <section> не действует. */
.ai-card.is-hidden, .ai-group-block.is-hidden { display: none; }

/* Уровень защищённости (/levels/). Четыре блока вопросов одной GET-формой;
   ответ печатается внутри своего блока, а не общим итогом внизу: человек
   пришёл за одним из трёх и читать сводку по всем ему незачем. Свои
   классы, а не .kit-*: те принадлежат конструктору перечня (правило в
   оглавлении файла). */
.lvl-form { margin: 8px 0 4px; }
.lvl-form__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); gap: 14px; }
.lvl-field { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px 14px; margin: 0; min-width: 0; }
.lvl-field legend { padding: 0 6px; font-size: 14.5px; font-weight: 600; }
.lvl-field--note { border-style: dashed; }
.lvl-field__hint { margin: 0 0 10px; font-size: 12px; line-height: 1.5; color: var(--text-dim); }
.lvl-field__text { margin: 0; font-size: 13px; line-height: 1.55; }
.lvl-field__label { margin: 12px 0 4px; font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
.lvl-option { display: grid; grid-template-columns: auto 1fr; gap: 8px; align-items: baseline; padding: 3px 0; font-size: 13px; line-height: 1.45; cursor: pointer; }
.lvl-field__pending { margin: 12px 0 0; font-size: 12.5px; color: var(--accent); }
.lvl-verdict { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; align-items: baseline; margin: 14px 0 0; padding: 10px 12px; border: 1px solid var(--accent-dim); border-radius: var(--radius); background: var(--surface-2); }
.lvl-verdict__level { font: 600 22px var(--font-mono); color: var(--accent); }
.lvl-verdict__basis { font-size: 12.5px; line-height: 1.5; color: var(--text-dim); }
.lvl-verdict .btn { grid-column: 1 / -1; justify-self: start; }
.lvl-form__actions { margin: 14px 0 0; }
/* «Записать в систему»: одна строка — система, что записать, кнопка. */
.lvl-apply { margin-top: 18px; }
.lvl-apply__row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; }
.lvl-apply__system { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; }
.lvl-option--inline { display: inline-flex; padding: 0; }
.lvl-apply__hint { margin-top: 14px; font-size: 12.5px; }
/* Блок КИИ: три десятка показателей приложения к ПП № 127 — во всю ширину,
   строкой «пункт · показатель · поле». */
.lvl-field--wide { margin-top: 14px; }
.kii-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(200px, 300px); gap: 6px 14px; align-items: center; padding: 6px 0; border-top: 1px solid var(--border); font-size: 13px; line-height: 1.4; }
.kii-row__clause { font: 12px var(--font-mono); color: var(--text-dim); margin-right: 4px; }
.kii-row__who { color: var(--text-dim); font-size: 12px; }
.kii-row__control { display: flex; align-items: center; gap: 8px; }
.kii-row__control input { width: 120px; }
.kii-row__unit { font-size: 12px; color: var(--text-dim); }
.kii-row select { width: 100%; min-width: 0; }
@media (max-width: 640px) { .kii-row { grid-template-columns: 1fr; } }
.lvl-foot { margin-top: 18px; font-size: 12.5px; line-height: 1.55; }

/* Конструктор профиля угроз ИИ. Свои правила, а не .kit-* с конструктора
   перечня СЗИ: там форма из трёх селектов и набора условий, здесь — двадцать
   два чекбокса состава решения, которым нужна сетка, и переиспользование
   чужих классов ради экономии четырёх строк уже разъезжалось (см. .tech-chip
   в оглавлении файла). */
.ai-form { margin: 8px 0 4px; }
.ai-fieldset { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; margin: 0 0 12px; }
.ai-fieldset legend { padding: 0 6px; font-size: 14.5px; }
.ai-objects { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); gap: 6px 18px; }
.ai-axes { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); gap: 0 18px; }
/* Галочка и подпись — одна цель нажатия: подпись без label кликается мимо, и
   на 22 объектах это ощущается сразу. */
.ai-check { display: flex; align-items: baseline; gap: 8px; padding: 3px 0; cursor: pointer; font-size: 14px; }
.ai-check input { margin: 0; flex-shrink: 0; }
.ai-form__actions { margin: 14px 0 0; }

/* ---------------------------------------------------------------------
   Перечень средств защиты системы (/systems/<uuid>/toolkit/).

   Список с флажками, а не карточки конструктора: здесь у каждой строки
   ровно одно действие — берём или не берём, — и карточка с полосой
   ступени, значком и чипами отвечала бы на другой вопрос («что это за
   средство»), уводя от единственного, который тут задают. Название и
   назначение при этом остаются: снимать отметку с одной аббревиатуры
   вслепую нельзя.
   --------------------------------------------------------------------- */
.kit-tier { font-size: 15px; margin: 22px 0 4px; }
.kit-list { display: flex; flex-direction: column; gap: 2px; margin: 10px 0 4px; }
/* Флажок и весь текст строки — одна цель нажатия (то же решение, что у
   .ai-check): промах по 12-пиксельному квадрату на списке из тридцати
   позиций случается постоянно. */
.kit-item {
    display: flex; gap: 10px; align-items: flex-start;
    padding: 8px 10px; border: 1px solid transparent; border-radius: var(--radius);
    cursor: pointer;
}
.kit-item:hover { background: var(--surface-2); }
.kit-item input { margin: 3px 0 0; flex-shrink: 0; accent-color: var(--accent); }
.kit-item__body { display: flex; flex-direction: column; gap: 2px; }
.kit-item__title { font-size: 14px; }
.kit-item__note { font-size: 12.5px; color: var(--text-dim); }
.kit-item__meta { font-size: 12px; color: var(--text-dim); }
/* Снятое притушено, но читается: это не архив, а то, что можно вернуть. */
.kit-item--off { opacity: .6; }
.kit-item--off:hover { opacity: 1; }
.kit-actions { margin: 14px 0 0; }


/* QR-код на странице второго фактора. Белая подложка и размер — часть
   картинки (см. totp.qr_svg): ужатие стилем размывает модули. Здесь только
   отступы. */
.totp-qr { margin: 12px 0; }
.totp-qr svg { display: block; max-width: 100%; border-radius: var(--radius); }
