﻿/*
    Элементы, общие для всей системы: поверхности, таблицы, поля, кнопки,
    сообщения.

    До этого файла у логгера были свои .panel и .button, у панели свои .card и
    button.primary. Выглядели они близко, но не одинаково: разные отступы,
    скругления, толщина границ, размер текста в таблицах. На соседних экранах
    это читается как две программы, даже когда цвета одни.

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

    Цвета — в tokens.css, каркас — в shell.css.
*/

/* ── Поверхности ───────────────────────────────────────────────────────── */

.panel, .card {
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    background: var(--surface);

    /*
        ТЕНИ У КАРТОЧКИ НЕТ, и это возврат к собственному правилу, а не новая
        мода. tokens.css над объявлением теней говорит дословно: «обычная
        карточка их не носит вовсе — только границу; иначе плотный экран
        превращается в набор парящих плиток. Тень остаётся признаком того, что
        элемент действительно ПОВЕРХ страницы».

        Правило было записано и тут же нарушено: карточка носила `--shadow` —
        ту самую тень в 45 точек размытия, которую токены держат для
        всплывающих слоёв. На карточке игрока их восемнадцать, и восемнадцать
        одинаково парящих плиток не дают глазу ни одной зацепки: раз всё
        поверх, значит ничто не поверх.

        Тень осталась ровно у двух: выдвижное меню и поиск по Ctrl+K. Они и
        правда лежат над страницей.

        Найдено UX-аудитом (04.09.2026), проверено чтением токенов.
    */

    /*
        МЕСТО, ГДЕ КАРТОЧКЕ РАЗРЕШЕНО СЖАТЬСЯ.

        Не украшение и не запас. В сетке и во флексе у ребёнка начальное
        `min-width: auto`: он НЕ становится уже своего содержимого. Значит
        карточка с широкой таблицей раздвигает сетку, сетка — рабочую область,
        и страница едет вбок целиком.

        При этом обёртка `.table-wrap` внутри не срабатывает НИКОГДА: сжиматься
        нечему, прокручивать нечего. Из 125 таблиц панели в обёртке 119, и
        страница всё равно ехала — обёртка была не забыта, а обезврежена.

        Пункт 7 приоритетного плана UX-аудита, 04.09.2026.
    */
    min-width: 0;
}

.panel + .panel { margin-top: 18px; }

/* У логгера отступы внутри задают .panel-head и .panel-copy, у панели их нет —
   карточка держит содержимое сама.

   Поля ужаты с 18/20 до 14/16: на карточке персонажа карточек тринадцать, и
   только на их поля уходило больше сотни точек высоты. */
/*
    Карточка. Отступы по шкале: внутри 20, между секциями 20.

    Было 14/16 внутри и 12 между. Двенадцать точек между карточками читаются
    как «прилеплено»: на экране поиска блок результата вставал вплотную к
    строке поиска и выглядел её продолжением, а не ответом на неё.
    Руководство просит между секциями 20-24.
*/
.card { padding: var(--pad-card); margin-bottom: var(--gap-5); }

/*
    Сетка карточек.

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

    Карточка с таблицей занимает всю ширину сама — таблицу в половину экрана
    читать нельзя. Определяется это через :has(), без разметки: иначе пришлось
    бы руками разметить восемнадцать карточек и помнить про это в каждой новой.
*/
/*
    ПОСЛЕДНИЙ РУБЕЖ: сама страница вбок не едет.

    `min-width: 0` у детей сетки — то же правило, что у карточки, но записанное
    явно: сетка держит и не-карточки.
*/
.card-grid > * { min-width: 0; }

.card-grid {
    display: grid;
    gap: var(--gap-5);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
    align-items: start;
}
.card-grid > .card { margin-bottom: 0; }
.card-grid > .card:has(table),
.card-grid > .card:has(.table-wrap),
.card-grid > .card.wide { grid-column: 1 / -1; }
.card h2 { margin-top: 0; }

.panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    min-height: 78px;
    padding: 17px 20px;
    border-bottom: 1px solid var(--line-soft);
}
.panel-head h2, .first-run h2 { margin: 5px 0 0; font-size: 1.05rem; }
.panel-copy { margin: 0; padding: 18px 20px 8px; color: var(--muted); line-height: 1.6; }

h2 { font-size: 1.05rem; margin: 0 0 10px; }

/*
    Подзаголовок внутри карточки.

    Правила для h3 не было ни в одном из четырёх файлов, и браузерное 1.17em
    делало подзаголовок КРУПНЕЕ заголовка карточки, да ещё с полем в целую
    строку сверху и снизу. Только на карточке персонажа таких подзаголовков
    одиннадцать.
*/
h3 { font-size: .86rem; font-weight: 700; margin: 14px 0 6px; }

.lede { color: var(--muted); margin: 18px 0 20px; max-width: 76ch; }

/* ── Таблицы ───────────────────────────────────────────────────────────── */

/*
    Прокрутка ЛОКАЛЬНАЯ: вбок едет таблица, а не страница.

    `min-width: 0` здесь по той же причине, что у карточки: обёртка тоже бывает
    ребёнком сетки. `max-width: 100%` — чтобы она не переросла отведённое место
    даже там, где родитель сжаться не даёт.

    `overscroll-behavior-x: contain` — чтобы прокрутка таблицы до края не
    утягивала за собой страницу.
*/
.table-wrap {
    overflow: auto;
    min-width: 0;
    max-width: 100%;
    overscroll-behavior-x: contain;
}
.table-wrap.tall { max-height: calc(100vh - 330px); min-height: 300px; }

table { width: 100%; border-collapse: collapse; }

/*
    Один стандарт таблицы на всю панель. Руководство по стилю называет разные
    таблицы на разных страницах прямо в списке запрещённого, и не зря: таблицы у
    нас на двадцати экранах из тридцати трёх, и оператор читает их весь день.

    Размеры взяты из руководства и заданы ТОКЕНАМИ, а не долями rem. Прежние
    .66rem и .76rem давали 10.6 и 12.2 точки — мельче нижней границы стандарта
    (11-12 у шапки, 13-14 у тела). Замерено на живой панели, а не прикинуто.
*/
th, td {
    padding: var(--gap-3) var(--gap-3);
    border-bottom: 1px solid var(--line-soft);
    text-align: left;
    vertical-align: middle;
}

th {
    background: var(--surface);
    color: var(--subtle);
    font-size: var(--size-badge);
    font-weight: var(--weight-strong);
    letter-spacing: .06em;
    text-transform: uppercase;
    height: var(--row-h-head);
}

/*
    Заголовок прилипает: в длинной таблице иначе через экран прокрутки уже не
    вспомнить, что в каком столбце.

    Прилипает только шапка. Прежнее правило липло к ЛЮБОМУ th, включая те, что
    стоят ключами в теле таблицы (Diagnostics): такой ключ уезжал к верху окна
    и закрашивал собой чужие данные.
*/
thead th { position: sticky; top: 0; z-index: 2; }

td { color: var(--muted); font-size: var(--size-table); }
tbody tr { height: var(--row-h); }

/*
    Плотная таблица: списки в сорок и больше строк — вещи, умения, события
    защиты, журнал. Одиннадцать точек отступа на строку стоят там целого экрана
    прокрутки.
*/
.table-tight th, .table-tight td { padding: var(--gap-2) var(--gap-3); }
.table-tight tbody tr { height: var(--row-h-compact); }
tbody tr { cursor: default; }
tbody tr.clickable { cursor: pointer; }
tbody tr.clickable:hover { background: var(--surface-2); }

/*
    Выбранная строка: подложка И левая линия акцента. Двумя признаками нарочно —
    руководство требует, чтобы состояние не держалось на одном цвете. Полоса
    рисуется вставкой тени, а не border: настоящая граница сдвинула бы
    содержимое строки на свою ширину, и таблица дёргалась бы при выборе.
*/
tbody tr.selected, tbody tr[aria-selected="true"] {
    background: var(--surface-selected);
}
tbody tr.selected > td:first-child,
tbody tr[aria-selected="true"] > td:first-child {
    box-shadow: inset 2px 0 0 var(--accent);
}

/*
    Длинный свободный текст в ячейке не должен распирать строку.

    Поймано на журнале действий: причина отказа («Cannot reach local
    (127.0.0.1:8081)...») попадала в колонку шириной 146 точек и растягивала
    строку до 231 при стандарте 34-42. Двести строк давали таблицу в тридцать
    четыре тысячи точек высотой — журнал переставал быть списком.

    Обрезка на двух строках, а не на одной: одна строка у длинной причины не
    оставляет ничего осмысленного. Полный текст остаётся в подсказке — это тот
    случай, где руководство её прямо требует (обрезанное содержимое).
*/
.cell-clamp {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/*
    Обрезка в одну строку — там, где в ячейке УЖЕ есть состояние, и причина к
    нему приписка. Две строки приписки плюс строка состояния дают ячейку в
    девяносто точек, и вся таблица встаёт по самой высокой ячейке.
*/
.cell-clamp-1 {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/*
    Моноширинная ячейка НЕ переносится.

    Моноширинным у нас набрано только то, что сравнивают посимвольно:
    идентификаторы, метки времени, номера корреляции. Перенос такого по слогам
    не просто некрасив — он ломает саму причину, по которой шрифт выбран
    моноширинным.

    Поймано на журнале действий: номер корреляции в колонке 123 точки
    разъезжался на четыре строки, метка времени на три, и строка вырастала до
    110 точек при стандарте 34-42. Двести строк давали таблицу в двадцать две
    тысячи точек.

    Обрезка многоточием, полный текст — в подсказке. Руководство требует её
    именно для обрезанных идентификаторов.
*/
td.mono, td .mono {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 24ch;
}

/* Короткая метка состояния не переносится: она подпись, а не текст. */
.state-ok, .state-warn, .state-bad, .state-unknown { white-space: nowrap; }

/* Действия строки держатся справа и не разъезжаются от длины соседних ячеек. */
td.row-actions, th.row-actions { text-align: right; white-space: nowrap; }

/* .numeric — имя логгера, .num — панели. Одно и то же. */
td.numeric, th.numeric, td.num, th.num { text-align: right; font-family: var(--font-mono); }

/* ── Поля ──────────────────────────────────────────────────────────────── */

.field { display: grid; gap: 6px; min-width: 0; }
.field > span { color: var(--muted); font-size: .74rem; font-weight: 700; }

label {
    display: block;
    margin-bottom: 6px;
    color: var(--muted);
    font-size: .74rem;
    font-weight: 700;
}

input, select, textarea {
    width: 100%;
    min-width: 0;
    max-width: 100%;
    min-height: 40px;
    padding: 9px 11px;
    /* Семейство наследуется, размер задаётся здесь. Сокращённое `font: inherit`
       забирало бы и размер, и поля становились крупнее остальных элементов. */
    font-family: inherit;
    border: 1px solid var(--line);
    border-radius: var(--radius-small);
    outline: 0;
    background: var(--field);
    color: var(--text);
    font-size: .82rem;
}
input:focus, select:focus, textarea:focus {
    border-color: var(--accent-strong);
    box-shadow: 0 0 0 2px var(--focus-ring);
}
input::placeholder { color: var(--subtle); }
input[type=checkbox] { width: auto; min-height: 0; }

form.stack { display: flex; flex-direction: column; gap: 14px; max-width: 380px; }

/*
    Строка: подписи, поля и кнопка в один ряд, по нижнему краю.

    Правило было написано как form.row, а на страницах одиннадцать раз стоит
    <div class="row"> — в настройках, диагностике и на экране читеров. Ни один
    из них строкой не собирался: поля с width:100% вставали столбиком, .grow*
    не действовали. Это был крупнейший источник пустого места в панели.
*/
.row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }

/*
    Поле внутри ячейки таблицы: обычное растянуто на всю колонку и в одиночку
    задаёт высоту всех строк списка (столбец «Роль» у операторов).
*/
.field-inline { width: auto; min-height: 30px; padding: 4px 8px; font-size: .74rem; }

/* ── Кнопки ────────────────────────────────────────────────────────────── */

.button, button.primary {
    min-height: 36px;
    padding: 8px 13px;
    font-family: inherit;
    border: 1px solid var(--line);
    border-radius: var(--radius-small);
    background: var(--surface-2);
    color: var(--text);
    font-size: .78rem;
    font-weight: 700;
    cursor: pointer;
}
.button:hover { border-color: var(--line-strong); background: var(--surface-3); }
.button.primary, button.primary {
    border-color: var(--accent);
    background: var(--accent);
    color: var(--on-accent);
}
.button.primary:hover, button.primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
.button.ghost { background: transparent; }
.button.danger { border-color: var(--danger-line); background: var(--danger-soft); color: var(--danger-text); }
.button:disabled, button:disabled { opacity: .45; cursor: not-allowed; }

/* Ссылка-кнопка: у строчного элемента min-height не работает и подчёркивание
   не снимается, поэтому ссылка рядом с кнопкой выглядела ниже неё и с чертой. */
a.button { display: inline-flex; align-items: center; text-decoration: none; }

button.link {
    min-height: 0;
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent);
    font: inherit;
    text-decoration: underline;
    cursor: pointer;
}

/* ── Сообщения ─────────────────────────────────────────────────────────── */

/*
    Направление раскладки задано явно.

    Без него уведомление с несколькими дочерними блоками раскладывалось в
    несколько узких КОЛОНОК — ровно там, где текст несущий: резервные коды,
    выданный пароль, ключ магазина, причина отказа во входе.
*/
.notice {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    margin-bottom: 16px;
    padding: 13px 15px;
    border-left: 3px solid var(--accent);
    /* Был var(--surface) — тот же цвет, что у карточки: внутри карточки
       уведомление не читалось как отдельный блок. */
    background: var(--surface-2);
    color: var(--muted);
    font-size: .78rem;
    line-height: 1.55;
}

/* Цвет достаётся только строгому без своего класса: прежнее .notice strong
   перебивало .state-bad и .state-warn (0-1-1 против 0-1-0), и разница между
   «отклонено» и «неизвестно» стиралась. */
.notice strong:not([class]) { color: var(--warn); }
.notice.warn { border-left-color: var(--warn); background: var(--warn-soft); }
.notice.bad { border-left-color: var(--danger); background: var(--danger-soft); }

/*
    УСПЕХ. Заведён 04.09.2026: разметка просила `.notice.ok` в трёх местах, а
    правила не было ни одного — плашка выходила нейтральной, и удача выглядела
    так же, как «просто сообщение».

    Отдельный вид нужен потому, что на экране пакетных работ рядом стоят обе
    плашки, и различать их по тексту оператор не обязан.
*/
.notice.ok { border-left-color: var(--good); background: var(--good-soft); }

/* ── Сведения: пары, заголовки блоков, пустота ─────────────────────────── */

/*
    Пара «подпись — значение».

    Раньше такой перечень был свёрстан семью способами и всегда таблицей: th в
    теле таблицы, td.muted, голый td, th.col-narrow. Девять фактов карточки
    персонажа занимали около четырёхсот точек по вертикали — по строке на факт
    во всю ширину экрана. Здесь тот же перечень идёт сеткой в два столбца и
    переносится сам.
*/
.kv { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 0 20px; margin: 0; }
.kv > div { display: flex; align-items: baseline; gap: 8px; min-width: 0; padding: 5px 0; border-bottom: 1px solid var(--line-soft); }
.kv dt { flex: 0 0 40%; color: var(--subtle); font-size: .66rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; }
.kv dd { margin: 0; min-width: 0; font-size: .82rem; overflow-wrap: anywhere; }
/* Один столбец: длинное значение — адрес, текст ошибки, состав. Подпись здесь
   не доля, а мерка: в один столбец строка идёт во всю ширину карточки, и те же
   40% превратились бы в пустую половину строки перед каждым значением. */
.kv.kv-1 { grid-template-columns: 1fr; }
.kv.kv-1 dt { flex: 0 0 180px; }

/*
    Строка заголовка блока: название, итог числом и свёрнутое пояснение в один
    ряд.

    Число у заголовка было только у подзаголовков; у двенадцати заголовков
    карточки персонажа его не было ни у одного, и понять, есть ли внутри
    что-нибудь, можно было только пролистав карточку до конца.
*/
.card-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px 10px; margin: 0 0 8px; }
.card-head > h2, .card-head > h3 { margin: 0; }
.count { color: var(--subtle); font-size: .8rem; font-variant-numeric: tabular-nums; }

/*
    Свёрнутое пояснение: откуда взято число, чему верить нельзя и что означает
    пустота.

    Тексты несущие, выбрасывать их нельзя. Но в потоке данных они занимали до
    шести строк перед первой цифрой и перечитывались при каждом открытии
    страницы. Здесь они стоят у заголовка и раскрываются на месте одним щелчком
    — в отличие от title, который не читается с клавиатуры, не находится
    поиском по странице и не печатается.
*/
details.why { display: inline-block; margin: 0; }
details.why > summary { display: inline-flex; align-items: center; gap: 5px; color: var(--accent); font-size: .72rem; cursor: pointer; list-style: none; }
details.why > summary::-webkit-details-marker { display: none; }
details.why > summary::before { content: "?"; display: inline-grid; place-items: center; width: 13px; height: 13px; border: 1px solid currentColor; border-radius: 999px; font-size: .58rem; font-weight: 800; }
details.why[open] { display: block; }
details.why > :not(summary) { margin: 6px 0 0; max-width: 76ch; color: var(--muted); font-size: .78rem; line-height: 1.55; }

/*
    Пустое состояние: это ответ, а не раздел.

    Раньше «Записей нет.» рисовалось полноразмерной карточкой со своим
    заголовком: только на карточке персонажа таких мест двадцать одно, по сто с
    лишним точек на два слова.
*/
.empty { margin: 0; color: var(--muted); font-size: .8rem; }

/*
    Полоса коротких значений: «компонент — одно слово», персонажи аккаунта,
    оглавление карточки, предметы внутри сделки.

    Таблица из семи строк на семь слов стоит около четырёхсот точек, та же
    полоса — две строки. И границы значений видны рамкой, а не только цветом
    ссылки.
*/
/*
    Пометки в ячейке таблицы. Узкая колонка ставила их столбиком по одной, и
    строка «Доступа» превращалась в кучу. Руководство прямо говорит не держать
    пять пометок в одной ячейке; пока их не растащили по колонкам, они хотя бы
    не разъезжаются — колонке задан минимум ширины.
*/
.chips { display: flex; flex-wrap: wrap; gap: var(--gap-1) var(--gap-2); margin: 0; padding: 0; list-style: none; }
/*
    Колонкам пометок и действий задан минимум ширины.

    Ни те, ни другие не были сломаны: обе уже флексом и обе переносятся. Их
    ломала УЗКАЯ КОЛОНКА — четыре пометки вставали столбиком по одной, три
    действия на три строки, и строка «Доступа» вырастала до 105 точек.

    Это заплатка, и честно назвать её так. Настоящий ответ руководства (§11) —
    не держать пять пометок в одной ячейке, а растащить их по колонкам или в
    панель деталей. Это перестройка экрана, и она отдельной задачей.
*/
td > .chips { min-width: 26ch; }
td > .actions { min-width: 24ch; }
.chip { display: inline-flex; align-items: baseline; gap: 6px; padding: 3px 9px; border: 1px solid var(--line-soft); border-radius: 999px; background: var(--surface-2); color: inherit; font-size: .74rem; text-decoration: none; }
a.chip:hover { border-color: var(--line); background: var(--surface-3); }
.chip .k { color: var(--subtle); font-size: .62rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; }
/* Блок, в котором ничего нет: виден, но не спорит за внимание с непустыми. */
.chip.chip-empty { opacity: .55; }

/*
    Ряд действий в одной ячейке.

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

    Прямые дети — формы, подписи и кнопки-ссылки (button.link). Кнопке с
    рамкой здесь не место: правило перебьёт ей поля, а черта слева прочтётся
    как край второй кнопки. Ряд из настоящих кнопок — это .row.
*/
.actions { display: flex; flex-wrap: wrap; align-items: center; row-gap: 4px; }
.actions > * { padding: 0 10px; border-left: 1px solid var(--line); }
.actions > :first-child { padding-left: 0; border-left: 0; }

/*
    Заметки оператора.

    Единственное на карточке, что написал человек, а не прочитала панель, — и
    выглядеть должно так же: не строкой таблицы, а записью с автором наверху.
    Переносы в тексте значащие (их ставил тот, кто писал), длинное слово рвётся
    в любом месте — иначе вставленная ссылка уезжает за край карточки.
*/
.notes { display: flex; flex-direction: column; gap: 10px; margin: 12px 0 0; padding: 0; list-style: none; }
.notes > li { padding: 10px 12px; border: 1px solid var(--line-soft); border-radius: var(--radius-small); background: var(--surface-2); }
.note-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px; margin-bottom: 5px; font-size: .74rem; }
/* Удаление уходит к правому краю: это не часть подписи, а действие над ней. */
.note-head > form { margin-left: auto; }
.note-text { font-size: .82rem; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }

/* ── Командная строка (Ctrl+K) ─────────────────────────────────────────── */

.palette-overlay {
    position: fixed;
    inset: 0;
    z-index: 90;
    display: flex;
    justify-content: center;
    padding-top: 12vh;
    background: var(--overlay);
    backdrop-filter: blur(3px);
}

.palette {
    width: min(620px, calc(100vw - 32px));
    max-height: 66vh;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow);
    overflow: hidden;
}

.palette-input {
    min-height: 52px;
    border: 0;
    border-bottom: 1px solid var(--line-soft);
    border-radius: 0;
    background: transparent;
    font-size: .95rem;
}
/*
    У ПОЛЯ ПАЛИТРЫ ОРЕОЛ БЫЛ СНЯТ НАРОЧНО — и это единственное поле, к
    которому вообще приходят только клавиатурой (Ctrl+K).

    Снят он был не зря: рамка поля внутри и без того обведённого окна выглядит
    двойной. Но снимать надо `box-shadow`, а не ВСЮ отметку фокуса — иначе
    единственный клавиатурный вход в панель остаётся без указателя.

    Общее правило `:focus-visible` из shell.css сюда доходит и рисует ореол
    снаружи; здесь гасится только внутренняя тень.
*/
.palette-input:focus { box-shadow: none; }

.palette-list { overflow-y: auto; }

.palette-row {
    display: flex;
    align-items: baseline;
    gap: 12px;
    width: 100%;
    min-height: 0;
    padding: 10px 14px;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--text);
    font-size: .82rem;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
}
.palette-row small { color: var(--subtle); font-size: .68rem; }
.palette-row.selected { background: var(--surface-3); }
.palette-row:hover { background: var(--surface-2); }

.palette-empty, .palette-foot { margin: 0; padding: 12px 14px; color: var(--subtle); font-size: .7rem; }
.palette-foot { border-top: 1px solid var(--line-soft); }

/* ── Мелочи ────────────────────────────────────────────────────────────── */

.muted { color: var(--muted); }

/* Размер относительный. Жёсткие .76rem схлопывали моношрифтовую вставку внутри
   обычной строки с пятнадцати точек до двенадцати, и строка читалась с
   провалами; таких вставок в панели под две сотни. Доля меньше единицы — потому
   что моноширинный при том же кегле выглядит крупнее соседнего текста. */
/*
    ЦИФРЫ ВЫСТРАИВАЮТСЯ В СТОЛБЕЦ.

    `tabular-nums` даёт всем цифрам одинаковую ширину. В пропорциональном
    начертании единица уже семёрки, и колонка чисел в таблице идёт волной —
    сравнить два числа на глаз становится нельзя, а именно этим оператор здесь и
    занят: сколько было и сколько стало.

    Ставится там, где цифры СРАВНИВАЮТСЯ: таблицы, номера, время, показатели.
    В обычный текст не идёт — там равная ширина цифр только мешает.

    Решение владельца 04.09.2026, вместе с переходом на свой Inter: у
    переменного Inter эта возможность есть, у прежнего системного шрифта её
    могло не быть вовсе.
*/
table,
.mono,
.num,
.count,
.metric-value,
input[type="number"] { font-variant-numeric: tabular-nums; }

.mono { font-family: var(--font-mono); font-size: .92em; }

/* В ячейке таблицы кегль уже уменьшен, и доля от него дала бы одиннадцать точек
   на дате, номере и хеше — там, где их читают чаще всего. В таблице моно живёт
   в размер ячейки. */
td.mono, td .mono, th.mono, th .mono { font-size: 1em; }

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

.state-ok { color: var(--good); }
.state-bad { color: var(--danger); }
.state-warn { color: var(--warn); }
.state-unknown { color: var(--unknown); }

/*
    Кнопка без класса — всё равно кнопка.

    Правило нужно ради ровно одного места, где кнопку забыли пометить, но
    держать его стоит: следующая такая появится там, где её никто не проверит,
    и будет выглядеть системной серой кнопкой посреди тёмной страницы.
*/
button { font-family: inherit; color: inherit; }

/*
    Полоса выполнения. Элемент общий, а оформление у него было только в стилях
    логгера — на любой другой странице такая же полоса выглядела бы браузерной.
*/
progress { width: 100%; height: 8px; border: 0; border-radius: 0; background: var(--surface-3); accent-color: var(--accent); }
progress::-webkit-progress-bar { background: var(--surface-3); }
progress::-webkit-progress-value { background: var(--accent-strong); }
progress::-moz-progress-bar { background: var(--accent-strong); }

/*
    ── Мелкая раскладка ──────────────────────────────────────────────────────

    Всё это раньше писалось атрибутом style прямо в разметке. Атрибут не
    работал: панель отдаёт себя с политикой style-src 'self', а она запрещает
    инлайновые стили — браузер их молча выбрасывал. Отступы, ширины полей и
    переносы длинных ключей не применялись ни разу, и увидеть это можно было
    только в консоли.

    Поэтому здесь названы не «отступ сверху восемь», а те несколько случаев,
    которые на страницах действительно встречаются. Шкала короткая нарочно:
    длинная сразу же разъедется.
*/

/* Блок под предыдущим: пояснение под формой, вторая карточка, ответ сервера. */
.spaced { margin-top: 12px; }
.spaced-tight { margin-top: 6px; }
.flush { margin-bottom: 0; }

/* Поля в строке формы: сколько места забирает поле относительно соседей. */
.grow { flex: 1 1 220px; }
.grow-2 { flex: 2 1 200px; }
.grow-wide { flex: 4 1 420px; }
.grow-narrow { flex: 1 1 130px; }

.row-center { align-items: center; }
.row-gap { gap: 16px; }
/* Полоса блоков, а не полей: выравнивать по нижнему краю в ней нечего. */
.row-wide-gap { gap: 28px; flex-wrap: wrap; align-items: flex-start; }

/* Одиночное поле формы: шире 420 точек поле для имени или числа не нужно, а
   растянутое на всю карточку читается как поле для текста. */
.field-block { margin-bottom: 14px; max-width: 420px; }

/* Подпись вплотную к тому, что подписывает. */
.caption-tight { margin-bottom: 4px; }
.flat { margin: 0; }

/* Столбик резервных кодов: их переписывают по одному, поэтому строки разрежены
   сильнее обычного текста. */
.code-column { margin: 8px 0; line-height: 1.7; }
.push-right { margin-left: auto; }
.col-narrow { width: 40%; }
/* Было .8rem — подсказка получалась крупнее содержимого таблиц (td — .76rem). */
.hint-small { font-size: .74rem; }

/*
    Скрытое — КЛАССОМ, а не атрибутом style.

    Панель отдаёт политику безопасности `style-src 'self'; script-src 'self'`, и
    встроенные стили браузер молча НЕ ПРИМЕНЯЕТ: атрибут в разметке стоит,
    `getComputedStyle` показывает обратное, и разметка выглядит правильной при
    неработающем поведении. Ровно так и вышло с подсказками выпадающего
    списка — четырнадцать строк вместо одной.

    Той же политикой запрещены и встроенные `<script>`, поэтому скрипты живут
    отдельными файлами в `wwwroot\js\`.
*/
.is-hidden { display: none; }

/* Ответ сервера как есть: переносы в нём значащие, схлопывать их нельзя. */
.verbatim { white-space: pre-wrap; }

/*
    Ключ или код, который человек переписывает глазами: крупнее обычного текста,
    с разрядкой, и рвётся в любом месте — иначе длинный ключ уезжает за край
    карточки и конец его не виден.
*/
.secret { margin: 6px 0; font-size: 1.05rem; letter-spacing: .08em; overflow-wrap: anywhere; word-break: break-all; }

/* ── График ────────────────────────────────────────────────────────────── */

/*
    Один стиль графика на всю панель — руководство прямо запрещает свой на
    каждой странице.

    Полотно прозрачное, разлиновка цветом обычной границы, одна основная линия
    акцентом и вторая заметно спокойнее. Заливка под основной линией — не
    украшение: она отделяет «сколько было» от разлиновки, когда значения малы и
    линия идёт у самого низа.
*/
/*
    Раскладка: слева столбец с числами, справа полотно, под ним подписи времени.
    Сеткой, а не отступами: столбец шкалы шире у трёхзначных чисел, и подгонять
    его вручную значило бы ловить перекос на каждом новом пике.
*/
.chart { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--gap-2); }

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

    Каждая подпись позиционируется долей и сдвигается на половину себя — тогда
    она встаёт ровно на линию сетки, а не под ней.
*/
/*
    Подписи стоят ОБЫЧНЫМ ПОТОКОМ, а не абсолютно.

    Первый заход ставил их абсолютно и по долям — и обе половины сломались:
    столбец шкалы схлопнулся в нулевую ширину (у него не осталось детей в
    потоке), а доли по горизонтали не применились вовсе, и все подписи времени
    свалились в одну точку.

    Потоком проще и надёжнее: деления у нас равномерные, а «равномерно между
    краями» — это ровно то, что делает space-between. Заодно столбец сам
    получает ширину по самому длинному числу.
*/
.chart-y {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: flex-end;
    height: 200px;
    /* Подпись встаёт серединой на линию сетки: половина строки сверху и снизу
       выступает за полотно, и без этого верхнее число висит ниже своей линии. */
    margin: -6px 0;
    padding: 6px 0;
}

.chart-plot { min-width: 0; }
.chart-plot svg { display: block; width: 100%; height: 200px; }

.chart-x {
    display: flex;
    justify-content: space-between;
    margin-top: var(--gap-1);
}
.chart-x .chart-tick { white-space: nowrap; }

.chart-tick {
    color: var(--subtle);
    font-size: var(--size-meta);
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

/*
    Переключатель периода. Живёт в шапке карточки, а не отдельной строкой: это
    не действие, а вид того же самого, и своей строки он не заслуживает.
*/
.chart-ranges { display: inline-flex; flex-wrap: wrap; gap: var(--gap-1); margin-left: auto; }
.chart-range {
    padding: 3px var(--gap-2);
    border: 1px solid transparent;
    border-radius: var(--radius-small);
    color: var(--subtle);
    font-size: var(--size-meta);
    text-decoration: none;
}
.chart-range:hover { border-color: var(--line); color: var(--text); }
.chart-range.on {
    border-color: var(--line-strong);
    background: var(--surface-selected);
    color: var(--text);
}

.chart-grid { stroke: var(--line-soft); stroke-width: 1; }

.chart-line {
    fill: none;
    stroke: var(--accent);
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
    /* Полотно тянется по ширине, и вместе с ним растянулась бы толщина линии:
       без этого ломаная на широком мониторе становится вдвое тоньше. */
    vector-effect: non-scaling-stroke;
}


.chart-area { fill: var(--accent); opacity: .10; stroke: none; }


.chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-2) var(--gap-5);
    margin-top: var(--gap-3);
    color: var(--muted);
    font-size: var(--size-meta);
}
.chart-key { display: inline-flex; align-items: center; gap: var(--gap-2); }
.chart-swatch {
    width: 14px; height: 3px; border-radius: 2px;
    background: var(--accent);
}
.chart-swatch-2 { background: var(--subtle); height: 2px; }

/*
    Разрыв в подписи под графиком. Пунктиром, а не сплошной: сплошная того же
    цвета читалась бы как ещё одна линия данных.
*/
.chart-swatch.gap {
    background: repeating-linear-gradient(
        90deg, var(--warn) 0 3px, transparent 3px 6px);
}

/* Одинокое наблюдение между двумя пропусками: ломаной из одной точки не
   бывает, а замер был. */
.chart-dot { fill: var(--accent); stroke: none; }

/*
    СВОДКА НАД ГРАФИКОМ.

    Требование аудита дословно: «сейчас» и «пик периода» должны быть видны без
    наведения мыши. До 04.09.2026 оба числа стояли ПОД графиком строкой
    подписи — то есть читались после того, как человек уже попытался снять их
    глазами с линии.
*/
.chart-summary {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-2) var(--gap-5);
    margin-bottom: var(--gap-3);
}
.chart-summary > div {
    display: flex;
    align-items: baseline;
    gap: var(--gap-2);
}
.chart-summary span {
    color: var(--muted);
    font-size: var(--size-meta);
}
.chart-summary strong {
    font-size: 1.35rem;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
}
.chart-summary small { color: var(--subtle); font-size: var(--size-meta); }

/*
    ПОДСКАЗКА ПРИ НАВЕДЕНИИ. Ставится скриптом, поэтому здесь только вид.

    Не перехватывает мышь: иначе она встаёт под курсор, событие уходит ей, и
    подсказка начинает моргать на каждом движении.
*/
.chart-plot { position: relative; }
.chart-hover {
    position: absolute;
    top: 0;
    z-index: 2;
    padding: 4px 8px;
    border: 1px solid var(--line);
    border-radius: var(--radius-small);
    background: var(--surface-2);
    color: var(--text);
    font-size: var(--size-meta);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    pointer-events: none;
    transform: translateX(-50%);
}
.chart-hover b { font-weight: 600; }
.chart-hover span { color: var(--muted); }

/*
    ПОЛОСА ПОКАЗАТЕЛЕЙ — сводка до подробностей.

    По концепту их четыре в ряд. `auto-fit` вместо жёстких четырёх колонок: на
    узком экране они встают по два, а не сжимаются до нечитаемых.
*/
/*
    ПЛИТКИ ЧАСТЫХ ЗАДАЧ. Три в ряд, на узком — в столбец.

    Разделены линиями, а не отступами и тенями: это один блок из трёх ходов, а
    три отдельные карточки читались бы как три разных предмета.
*/
.task-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    margin-top: var(--gap-2);
    border-top: 1px solid var(--line-soft);
}
.task {
    display: block;
    min-width: 0;
    padding: var(--gap-3) var(--gap-4);
    border-right: 1px solid var(--line-soft);
    color: inherit;
    text-decoration: none;
}
.task:last-child { border-right: 0; }
.task:hover { background: var(--surface-2); }
.task strong { display: block; margin-bottom: 4px; font-weight: 500; }
.task small { color: var(--muted); font-size: var(--size-meta); }

/*
    ЗНАК СТЕПЕНИ РИСКА.

    Цветной текст был единственным признаком опасности в каталоге из 224 строк.
    Он пропадает у всякого, кто цвета различает хуже, а на печати и в снимке
    экрана — у всех. Рамка и заливка держатся сами; цвет им помогает.
*/
.risk {
    display: inline-block;
    padding: 1px 8px;
    border: 1px solid var(--line);
    border-radius: 999px;
    font-size: var(--size-meta);
    white-space: nowrap;
}
/*
    САМИ ЗНАЧЕНИЯ ЗНАКОВ ЖИВУТ В akeron.css, а не здесь.

    Найдено итоговой проверкой аудита 04.09.2026: `.risk-wide` был объявлен
    ДВАЖДЫ — тут и там, — а `akeron.css` подключается ПОСЛЕ и побеждал. Из-за
    этого запись 2.0.15 («у необратимого заливка, у широкого её нет») была
    неверна: заливка была у обоих.

    Второе объявление снято, а не поправлено: два места на одно правило
    расходятся молча, и разошлись они за один день.
*/

/*
    ОТМЕТКА ОТБОРА. Ссылка, а не кнопка: снятие условия — это переход на другой
    адрес, и делать вид, будто это действие формы, незачем.
*/
.chips-row { flex-wrap: wrap; gap: var(--gap-1); }
.filter-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 10px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--muted);
    font-size: var(--size-meta);
    text-decoration: none;
}
.filter-chip:hover { border-color: var(--danger); color: var(--text); }

/*
    ЧЕТЫРЕ ПОКАЗАТЕЛЯ РОВНЫМ РЯДОМ ЛИБО КВАДРАТОМ.

    Общее правило `.grid` берёт `auto-fit` с потолком ширины: четвёртая карточка
    не помещалась в ряд и съезжала одна под три. Одинокая карточка под тремя
    читается как «прочее», хотя это такое же число, как остальные.

    Четыре, потом два, потом один — промежуточного «три» нет нарочно: три из
    четырёх снова дают одинокую четвёртую.
*/
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

@media (max-width: 1000px) {
    .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 460px) {
    .grid-4 { grid-template-columns: minmax(0, 1fr); }
}

/*
    ЗНАЧОК С НОМЕРОМ. Моноширинный, в рамке: номер шаблона уносят отсюда в
    жалобу и в команду, и он должен выделяться из текста заголовка.
*/
.badge {
    display: inline-block;
    padding: 1px 8px;
    border: 1px solid var(--line);
    border-radius: var(--radius-small);
    background: var(--surface-2);
    color: var(--muted);
    font-size: var(--size-meta);
}

/*
    СТОЛБИКИ СУТОЧНОГО ГРАФИКА.

    «Пришло» и «ушло» стоят рядом парой, а не друг на друге: сумма двух
    разнонаправленных величин, сложенная в один столбик, не значит ничего.
*/
.bar-in { fill: var(--good); }
.bar-out { fill: var(--danger); }
.bar-people { fill: var(--accent); }

.chart-swatch.bar-in { background: var(--good); }
.chart-swatch.bar-out { background: var(--danger); }
.chart-swatch.bar-people { background: var(--accent); }

/*
    МЕНЮ ДЕЙСТВИЙ В СТРОКЕ ТАБЛИЦЫ.

    На `<details>`, а не на скрипте: экран доступа обязан работать и без них.
    Раскрытое всплывает НАД строкой, а не раздвигает её: раздвинутая строка
    сдвигает всю таблицу, и глаз теряет ту, ради которой меню открывали.
*/
.row-menu-cell { position: relative; }
.row-menu > summary {
    display: inline-block;
    padding: 2px 10px;
    border: 1px solid var(--line);
    border-radius: var(--radius-small);
    color: var(--muted);
    font-size: var(--size-meta);
    cursor: pointer;
    list-style: none;
}
.row-menu > summary::-webkit-details-marker { display: none; }
.row-menu > summary:hover { color: var(--text); border-color: var(--accent); }
.row-menu[open] > summary { color: var(--text); border-color: var(--accent); }

.row-menu-body {
    position: absolute;
    right: 0;
    z-index: 30;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-width: 190px;
    padding: var(--gap-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-small);
    background: var(--surface-2);
    box-shadow: var(--shadow);
}

/*
    ВКЛАДКИ КАРТОЧКИ. Ссылками, а не переключателями: содержимое невыбранной
    вкладки не читается вовсе, и прятать скриптом было бы нечего.
*/
.tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-1);
    margin: var(--gap-4) 0 var(--gap-3);
    padding-bottom: 2px;
    border-bottom: 1px solid var(--line-soft);
}
.tab {
    padding: 6px var(--gap-3);
    border: 1px solid transparent;
    border-bottom: 0;
    border-radius: var(--radius-small) var(--radius-small) 0 0;
    color: var(--muted);
    text-decoration: none;
    white-space: nowrap;
}
.tab:hover { color: var(--text); background: var(--surface-2); }
/* Открытая держится НЕ ТОЛЬКО цветом: у неё своя подложка и черта снизу.
   Цвет пропадает на печати и у тех, кто различает его хуже. */
.tab.on {
    border-color: var(--line);
    background: var(--surface);
    color: var(--text);
    box-shadow: inset 0 -2px 0 var(--accent);
}

/*
    ОТМЕТКА НЕСОХРАНЁННОГО. Ставится скриптом рядом с кнопкой сохранения.
*/
.dirty-note {
    margin-left: var(--gap-2);
    color: var(--warn);
    font-size: var(--size-meta);
}

/*
    УЗКАЯ ФОРМА. Три поля, растянутые на полторы тысячи точек, читаются хуже, а
    поле пароля шириной в экран выглядит полем поиска.
*/
.narrow-form { max-width: 460px; }

/*
    ПОКАЗАТЬ НАБРАННОЕ. Кнопка ставится скриптом справа внутри поля.
*/
.reveal-wrap { position: relative; }
.reveal-wrap input { padding-right: 92px; }
.reveal-button {
    position: absolute;
    top: 50%;
    right: 6px;
    transform: translateY(-50%);
    padding: 2px 8px;
    border: 1px solid var(--line);
    border-radius: var(--radius-small);
    background: var(--surface-2);
    color: var(--muted);
    font-size: var(--size-meta);
    cursor: pointer;
}
.reveal-button:hover { color: var(--text); border-color: var(--accent); }

/*
    ПОЛОСА ВЫВОДА. Одна строка над всем экраном.

    Цвет здесь несёт смысл, поэтому «не настроено» получает НЕЙТРАЛЬНЫЙ, а не
    красный: ненастроенность это не поломка, и красный торопит чинить то, чего
    никто не ломал.
*/
.health {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--gap-2) var(--gap-3);
    margin-bottom: var(--gap-3);
    padding: var(--gap-2) var(--gap-4);
    border: 1px solid var(--line-soft);
    border-left-width: 3px;
    border-radius: var(--radius-small);
    background: var(--surface);
}
.health strong { font-weight: 600; }
.health-what { color: var(--muted); font-size: var(--size-meta); }
.health-ok { border-left-color: var(--good); }
.health-warn { border-left-color: var(--warn); }
.health-bad { border-left-color: var(--danger); }
/* Нейтральное — цветом линии, а не состояния: у него нет ни хорошего, ни
   плохого смысла. */
.health-neutral { border-left-color: var(--line-strong); }

/* Объяснение графика: раскрытие не должно висеть вплотную к подписям осей. */
.chart-help { margin-top: var(--gap-3); }

.metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: var(--gap-3);
    margin-bottom: var(--gap-4);
}
.metric {
    min-width: 0;
    padding: var(--gap-3) var(--gap-4);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    background: var(--surface);
}
.metric-label {
    display: block;
    margin-bottom: 8px;
    color: var(--muted);
    font-size: var(--size-meta);
}
.metric-value {
    display: block;
    margin-bottom: 6px;
    font-size: 1.7rem;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    letter-spacing: -.025em;
    line-height: 1.1;
}
.metric-value a { color: inherit; text-decoration: none; }
.metric-value a:hover { color: var(--accent-strong); }
.metric-value em {
    color: var(--muted);
    font-size: .8rem;
    font-style: normal;
    font-weight: 400;
}
/* Прочерк вместо числа. Приглушён нарочно: он не значение, а его отсутствие,
   и набранный в полную силу читался бы как показание. */
.metric-void { color: var(--subtle); }
/* Показатель, у которого значение — не число, а строка: адрес хоста, имя.
   Крупный кегль там режет строку, а не выделяет её. */
.metric-value.metric-text { font-size: 1rem; letter-spacing: 0; overflow-wrap: anywhere; }
.metric-note { display: block; color: var(--subtle); font-size: var(--size-meta); }

/*
    ДВЕ КОЛОНКИ ОБЗОРА: история слева, состояние справа.

    Доли 1.45 и 0.9 взяты из концепта. Правая не уже 270 точек: в ней полосы
    доступности, и на более узкой их куски перестают различаться.
*/
.overview-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.45fr) minmax(270px, .9fr);
    gap: var(--gap-4);
    align-items: start;
}

@media (max-width: 1100px) {
    .overview-grid { grid-template-columns: minmax(0, 1fr); }
}

/*
    Два числа с порта, которым не место в полосе показателей наверху.
    «Игроков в мире» отдаёт ноль при работающем мире — крупный ноль наверху
    читался бы как поломка.
*/
/*
    ПАРЫ «НАЗВАНИЕ — ЗНАЧЕНИЕ». Стояли на трёх экранах классом, которого НЕ
    БЫЛО НИ В ОДНОМ ФАЙЛЕ СТИЛЕЙ: `facts` на заданиях и в мире, `pairs` в
    объявлениях. Разметка при этом выглядела осмысленной, а рисовалась голым
    списком определений — то есть подписи и значения шли в столбик друг под
    другом, вдвое длиннее нужного.

    Найдено сплошной сверкой класс↔стиль 04.09.2026: семь имён в восьми местах.
*/
.facts { margin: 0; }
.facts > div {
    display: grid;
    grid-template-columns: minmax(120px, 34%) minmax(0, 1fr);
    gap: var(--gap-2);
    align-items: baseline;
    padding: 5px 0;
    border-top: 1px solid var(--line-soft);
}
.facts > div:first-child { border-top: 0; }
.facts dt { color: var(--muted); font-size: var(--size-meta); }
.facts dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

/*
    ПОЛОСА ОТБОРА. Аудит просит её отдельным общим составным: сейчас каждый
    экран со своим набором полей повторяет одно и то же руками.
*/
.filters {
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--gap-2) var(--gap-3);
    padding: var(--gap-3);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    background: var(--surface);
}

.side-figures { margin: var(--gap-3) 0 0; }
.side-figures > div {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    gap: var(--gap-2);
    padding: 6px 0;
    border-top: 1px solid var(--line-soft);
}
.side-figures dt { color: var(--muted); font-size: var(--size-meta); }
.side-figures dd { margin: 0; font-variant-numeric: tabular-nums; }
.side-figures p { grid-column: 1 / -1; margin: 2px 0 0; font-size: var(--size-meta); }

/*
    ПОЛОСА ДОСТУПНОСТИ: как было, а не как сейчас.

    Ширина куска пропорциональна ЕГО ДЛИТЕЛЬНОСТИ — flex-grow равен числу
    секунд. Одинаковые куски на равные доли рисовали бы минутный провал таким
    же, как трёхчасовой.
*/
.availability { margin-top: var(--gap-4); padding-top: var(--gap-3); border-top: 1px solid var(--line-soft); }
.availability-head { margin-bottom: var(--gap-3); font-size: var(--size-meta); }

.availability-row {
    display: grid;
    grid-template-columns: 68px minmax(0, 1fr) 92px;
    align-items: center;
    gap: var(--gap-2);
    margin-bottom: 8px;
}
.availability-name { color: var(--muted); font-size: var(--size-meta); }
.availability-row small { font-size: var(--size-meta); text-align: right; }

/*
    На узком экране подписи съедают полосу: 68 и 92 точки из 343 оставляют ей
    126 — измерено на стенде 04.09.2026, и куски короче процента там перестают
    различаться вовсе. Поэтому имя уходит наверх, а полоса берёт всю ширину.
*/
@media (max-width: 560px) {
    .availability-row {
        grid-template-columns: minmax(0, 1fr) auto;
        row-gap: 4px;
    }
    .availability-name { grid-column: 1; }
    .availability-row small { grid-column: 2; }
    .state-track { grid-column: 1 / -1; }
}

/*
    Полотно полосы. SVG, а не flex: инлайновый `style` с долями блокируется
    нашим же `style-src 'self'` — молча, и полоса выходит пустой.
*/
.state-track {
    display: block;
    width: 100%;
    height: 10px;
    overflow: hidden;
    border-radius: 3px;
    background: var(--surface-3);
}
.state-track .is-ok { fill: var(--good); }
.state-track .is-bad { fill: var(--danger); }
.state-track .is-odd { fill: var(--warn); }
/* Нет замера — не «работало» и не «лежало». Заметно бледнее обоих: любой
   сочный цвет здесь прочитался бы как наблюдение. */
.state-track .is-empty { fill: var(--line); }

.state-scale {
    display: flex;
    justify-content: space-between;
    margin-top: 2px;
    color: var(--subtle);
    font-size: var(--size-meta);
}
.state-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-1) var(--gap-3);
    margin-top: var(--gap-2);
    color: var(--muted);
    font-size: var(--size-meta);
}
.state-legend span { display: inline-flex; align-items: center; gap: 6px; }
.state-legend i { width: 12px; height: 8px; border-radius: 2px; }
.state-legend .is-ok { background: var(--good); }
.state-legend .is-bad { background: var(--danger); }
.state-legend .is-empty { background: var(--line); }
/*
    Кнопка выгрузки таблицы. Ставится скриптом внутрь .table-wrap первым
    элементом, поэтому липнет к правому краю и не занимает строку: у отчётов
    таблиц два десятка, и полоса под каждой съела бы экран.
*/
.table-export {
    float: right;
    margin: 0 0 4px 8px;
    padding: 2px 8px;
    font-size: 12px;
    line-height: 18px;
    color: var(--muted);
    background: transparent;
    border: 1px solid var(--line);
    border-radius: 4px;
    cursor: pointer;
}

.table-export:hover {
    color: var(--text);
    border-color: var(--accent);
}

/* Строка истории, где состояние отличается от предыдущего замера. Полоса слева,
   а не заливка: заливка на каждой второй строке превращается в шум. */
tr.changed > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

/*
    ТОЛЬКО ДЛЯ ЧТЕЦА ЭКРАНА: не видно, но читается и озвучивается.

    Заведено 05.09.2026 по находке UX-06 повторного аудита Codex. График
    присутствия водит по замерам стрелками и при этом МОЛЧИТ: меняется только
    видимая подсказка. Человек, читающий экран чтецом, слышит, что график есть,
    и не узнаёт из него ни одного числа.

    ПОЧЕМУ НЕ `display: none` И НЕ `visibility: hidden`. Оба убирают элемент из
    дерева доступности — то есть ровно из того места, ради которого он и заведён.
    Здесь нужно обратное: с глаз убрать, чтецу оставить.

    ПОЧЕМУ НЕ НУЛЕВОЙ РАЗМЕР. Элемент нулевого размера некоторые чтецы
    пропускают. Размер в один пиксель с обрезкой — приём, работающий везде.
*/
.for-screen-reader {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}
