/* Панель new-proxy: светлый карточный стиль (макет B). Без фреймворков, чистый CSS. */

:root {
  --bg: #f6f7fb;
  --card: #fff;
  --border: #e5e8f0;
  --accent: #2d5bff;
  --accent-dark: #1f45cc;
  --text: #1b2233;
  --muted: #6b7280;
  --side-w: 220px;
  --drawer-w: 45%;

  --ok-fg: #14804a;   --ok-bg: #e4f7ec;
  --warn-fg: #9a6400; --warn-bg: #fff3d6;
  --bad-fg: #c02b3a;  --bad-bg: #fde7ea;
  --mute-fg: #6b7280; --mute-bg: #eef0f4;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 14px;
  color: var(--text);
  background: var(--bg);
}
a { color: inherit; text-decoration: none; }
h1, h2, h3 { margin: 0 0 12px; font-weight: 600; }
table { border-collapse: collapse; }

/* ---------- каркас: боковое меню + основная область ---------- */

.side {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: var(--side-w);
  background: var(--card);
  border-right: 1px solid var(--border);
  padding: 16px 0;
  overflow-y: auto;
  z-index: 20;
}
.brand {
  font-weight: 700;
  font-size: 16px;
  padding: 0 16px 16px;
  color: var(--accent);
}
.grp {
  padding: 12px 16px 4px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}
.side .item {
  display: block;
  padding: 8px 16px;
  color: var(--text);
  border-left: 3px solid transparent;
}
.side .item:hover { background: var(--bg); }
.side .item.on {
  border-left-color: var(--accent);
  background: var(--bg);
  color: var(--accent);
  font-weight: 600;
}

main {
  margin-left: var(--side-w);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.top {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 24px;
  background: var(--card);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
}
.top h1 { flex: 0 0 auto; font-size: 18px; }
.top .summary { flex: 1 1 auto; display: flex; flex-wrap: wrap; gap: 6px; }
.burger {
  display: none;
  background: none;
  border: none;
  font-size: 20px;
  cursor: pointer;
}

.content { padding: 24px; flex: 1; }

/* ---------- карточки, плашки-статусы, кнопки ---------- */

/* ВНИМАНИЕ: padding: 16px связан с модификатором .flush табличной системы ниже — тот
   тянет таблицу во всю ширину карточки отрицательными полями ровно на эти 16px.
   Меняете padding здесь — правьте и блок «таблица во всю ширину карточки». */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 1px 2px rgba(20, 24, 40, .04);
  padding: 16px;
}

.pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.pill.ok                     { color: var(--ok-fg);   background: var(--ok-bg); }
.pill.warn, .pill.low        { color: var(--warn-fg); background: var(--warn-bg); }
.pill.bad, .pill.offline     { color: var(--bad-fg);  background: var(--bad-bg); }
.pill.mute, .pill.paused     { color: var(--mute-fg); background: var(--mute-bg); }

.btn {
  display: inline-block;
  padding: 8px 16px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card);
  color: var(--text);
  cursor: pointer;
  font-size: 14px;
}
.btn:hover { background: var(--bg); }
.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.btn.primary:hover { background: var(--accent-dark); }

/* Моноширинный шрифт объявлен один раз на всех, кто его носит: класс .mono в разметке и
   роли-колонки таблиц (c-id / c-key / c-time). Сами роли задают только цвет и запрет
   переноса — стек шрифта не расползается копиями по файлу. */
.mono, .tbl .c-id, .tbl .c-key, .tbl .c-time { font-family: "SF Mono", Consolas, monospace; }
.muted { color: var(--muted); }

/* ---------- плитки сводки ---------- */

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}
.tile {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
  box-shadow: 0 1px 2px rgba(20, 24, 40, .04);
}
.tile-val { font-size: 22px; font-weight: 700; }
.tile-lbl { font-size: 12px; color: var(--muted); margin-top: 2px; }

/* ---------- спарклайны (macros.html::sparkline) ---------- */

.spark { display: block; margin-top: 8px; }
.tile-spark { margin: 4px 0; line-height: 0; }

/* ---------- таблицы: один скелет .tbl на всю панель ---------- */

/* Класс .tbl носят все таблицы панели — и парк боксов, и разделы. Рядом в селекторах
   базового скелета стоит table.hosts по двум причинам: за это имя держится app.js
   (живые статусы парка ищут table.hosts) и пока не переведённые на .tbl шаблоны
   разделов не осыпаются в браузерный дефолт. Собственных стилей у hosts больше нет,
   правила общие — двух разных таблиц в интерфейсе не остаётся. Когда все шаблоны
   получат .tbl, имя hosts останется только на fleet/table.html как якорь для JS, и из
   селекторов ниже его можно будет убрать. */
.tbl, table.hosts {
  width: 100%;
  font-size: 13px;
  border-collapse: collapse;
}
.tbl th, table.hosts th {
  text-align: left;
  padding: 8px 10px;
  color: var(--muted);
  font-weight: 600;
  font-size: 12px;
  /* шапка не переносится: перенос делает её двухэтажной и ломает ритм колонок */
  white-space: nowrap;
  /* фон непрозрачный, а подчёркивание — inset-тенью вместо border-bottom: при
     border-collapse граница липкой ячейки уезжает вместе с прокруткой, тень остаётся.
     Выглядит идентично, но липкая шапка в .tbl-wrap на этом не ломается. */
  background: var(--card);
  box-shadow: inset 0 -1px 0 var(--border);
}
.tbl td, table.hosts td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
/* Линия под последней строкой читается как обрезанная рамка карточки — убираем. */
.tbl tbody tr:last-child td, table.hosts tbody tr:last-child td { border-bottom: none; }
/* Подсветка строки под курсором: в таблице на пять-шесть колонок (вайтлист БД, блоки)
   это единственный способ не соскользнуть глазом на соседнюю строку. */
.tbl tbody tr:hover, table.hosts tbody tr:hover { background: var(--bg); }
/* Строка-ссылка — только там, где клик по строке действительно что-то открывает. */
.tbl tbody tr.row, table.hosts tbody tr.row { cursor: pointer; }
/* Итоговая строка: отбита сверху и не спорит с данными снизу. */
.tbl tfoot td { border-top: 2px solid var(--border); border-bottom: none; font-weight: 600; }

/* Обёртка таблицы: узкий экран не ломает широкую таблицу — она едет вбок внутри своей
   карточки, а не растягивает всю страницу. Она же — контейнер для липкой шапки. */
.tbl-wrap { overflow: auto; }
/* Липкая шапка. Липнет к верху обёртки, а не страницы: к странице пришлось бы липнуть
   с захардкоженной высотой липкой .top, а она зависит от шрифта. Без .tbl-wrap правило
   безвредно — прокрутки нет, липнуть не к чему. */
.tbl-wrap .tbl th, .tbl-wrap table.hosts th { position: sticky; top: 0; z-index: 1; }
/* Высоту ограничиваем только там, где список реально длинный (вайтлист pg_hba,
   события): вложенная вертикальная прокрутка на и без того прокручиваемой странице
   раздражает сильнее уезжающей шапки, поэтому она включается вручную, а не всем. */
.tbl-wrap.tall { max-height: 62vh; }

/* Таблица во всю ширину карточки: линии строк доходят до её краёв, и строки читаются
   как строки, а не как текст с отбивкой в случайном месте. ВНИМАНИЕ: отрицательные
   поля равны padding карточки (.card { padding: 16px }, блок «карточки» выше) —
   меняется padding, меняются и эти числа. */
.card .tbl.flush, .card .tbl-wrap.flush { margin-left: -16px; margin-right: -16px; width: calc(100% + 32px); }
.card .tbl.flush:last-child, .card .tbl-wrap.flush:last-child { margin-bottom: -16px; }
/* Компенсация: крайние ячейки возвращают себе поле карточки, иначе данные липнут к её
   краю. Правил два — на саму таблицу и на таблицу внутри обёртки: в варианте с обёрткой
   класс flush с <table> снимается, и без второй пары первая колонка теряла бы отступ. */
.tbl.flush tr > :first-child,
.tbl-wrap.flush .tbl tr > :first-child,
.tbl-wrap.flush table.hosts tr > :first-child { padding-left: 16px; }
.tbl.flush tr > :last-child,
.tbl-wrap.flush .tbl tr > :last-child,
.tbl-wrap.flush table.hosts tr > :last-child { padding-right: 16px; }

/* Длинные списки (pg_hba ~25 строк, события): та же таблица, но строка ниже на треть,
   чтобы список помещался на экран целиком. */
.tbl.dense th, .tbl.dense td { padding: 5px 10px; }

/* Колонки по содержимому, а не растянуты поровну на всю ширину: иначе «host all all»
   расползается на треть экрана и строка снова читается как сплошной текст. Лишнюю
   ширину забирает ровно одна помеченная колонка: .c-grow (обычно действия), .c-wide
   (свободный текст) или .c-bar — последняя ограничена долей, чтобы полоска не
   превращалась в километр. */
.tbl.tight th, .tbl.tight td { width: 1%; white-space: nowrap; }
.tbl.tight .c-grow { width: auto; }
.tbl.tight .c-wide { width: auto; white-space: normal; }
.tbl.tight .c-bar  { width: 40%; min-width: 140px; }

/* «Ключ → значение» (предпросмотр переноса, результат действия): подпись слева
   приглушена, значение справа — то, ради чего смотрят. */
.tbl.kv td:first-child { color: var(--muted); width: 1%; white-space: nowrap; }
.tbl.kv td:last-child { font-variant-numeric: tabular-nums; }

/* --- роли колонок: класс ставится и на th, и на каждый td этой колонки --- */

/* Число: правый край и цифры одной ширины. Без этого 9 и 25000 стоят под одним левым
   краем и столбец не сравнивается по вертикали вообще. */
.tbl .c-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Единица измерения при числе («мс», «ГБ») приглушена, чтобы колонка читалась как
   числовая, а не как текст. Рисуется макросом num_cell вторым аргументом. */
.tbl .unit { color: var(--muted); }

/* Идентификатор строки (адрес, подсеть, блок, юнит) — якорь, за который цепляется глаз:
   самый контрастный текст строки. Шрифт приходит из общего правила .mono выше. */
.tbl .c-id { white-space: nowrap; color: var(--text); }

/* Служебная колонка-константа (тип / база / пользователь / метод в pg_hba). Сама по
   себе она НЕ приглушена: гасится только повтор строки выше (класс .dup ниже), поэтому
   нештатное значение — единственный метод, отличающийся от соседей, — остаётся тёмным
   и находится взглядом само, без чтения всего столбца. */
.tbl .c-key { white-space: nowrap; }

/* Время: приглушено и без переноса, чтобы столбец стоял ровной колонкой. */
.tbl .c-time { white-space: nowrap; color: var(--muted); }

/* Повтор значения из строки выше («host all all» подряд в двух десятках записей).
   Значение не вырезается — копируется, находится поиском по странице, — а гасится до
   var(--muted): это 4.83:1 к белому, читаемо по AA, и контраст с адресом даёт то, что
   .c-id идёт var(--text). Через opacity гасить нельзя: контраст падает до 2:1 и текст
   перестаёт читаться вовсе. */
.tbl .dup { color: var(--muted); }

/* Единственная колонка, которой разрешено переносить строки: свободный текст (легенда). */
.tbl .c-wide { white-space: normal; min-width: 12em; }

/* Длинный текст в одну строку с многоточием («вывод», «последнее действие»): строки
   таблицы остаются одной высоты, а IPv6-адрес или путь к бэкапу не распирает колонку.
   Вешается на <span> ВНУТРИ ячейки — max-width на самой <td> авто-раскладка таблицы
   соблюдать не обязана. Полное значение обязательно дублировать в title="", иначе
   обрезанный хвост нельзя прочитать никак. */
.tbl .cut {
  display: inline-block;
  max-width: 26ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

/* Колонка действий: прижата к правому краю, отбита от данных и не растягивает таблицу.
   Кнопка «удалить» и плашка «защищён» перестают стоять вплотную к адресу. */
.tbl .c-act { width: 1%; white-space: nowrap; text-align: right; padding-left: 18px; }

/* Кнопки и поля внутри таблицы мельче обычных: .btn в полный рост поднимает строку до
   40px, и список на 25 строк превращается в лестницу. 4px по вертикали — нижняя
   граница: у разрушающего «удалить» это и так минимальная безопасная цель нажатия. */
.tbl .btn { padding: 4px 10px; font-size: 12px; border-radius: 6px; }
.tbl input[type="text"], .tbl input[type="number"] {
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 12px;
  color: var(--text);
}

/* Раскрывашка «вывод» в событиях: раскрытый хвост лога не должен растягивать таблицу
   ни по ширине, ни по высоте — иначе одна длинная запись выносит весь раздел. */
.tbl details > summary { cursor: pointer; color: var(--accent); }
.tbl details pre {
  margin: 6px 0 0;
  max-width: 70ch;
  max-height: 220px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 12px;
}

/* Строка вне игры: защищённая запись, запрет по умолчанию, выключенная подсеть.
   Служебный хвост pg_hba так видно отдельно от настоящих доступов. */
.tbl tr.row-off td, .tbl tr.row-off .c-id { color: var(--muted); }
.tbl tr.row-off td { background: var(--bg); }
/* Подсветка row-off под курсором задаётся на ЯЧЕЙКЕ, а не на строке: фон ячейки
   (правило выше) всегда рисуется поверх фона строки, и общий tr:hover под ней просто
   не был бы виден — специфичность тут ни при чём. Тон темнее обычного --bg по той же
   причине: строка уже стоит на --bg. */
.tbl tbody tr.row-off:hover td { background: var(--mute-bg); }

/* Строка-подпись внутри таблицы («запрет по умолчанию — конец файла» в хвосте pg_hba).
   Оформлена как заголовок группы в боковом меню (.grp): в панели один язык для «это
   подпись, а не данные». Она отвечает на вопрос «а это не забытый доступ?», которого
   одно только приглушение row-off не снимает. */
.tbl tr.tbl-grp td {
  background: var(--bg);
  padding: 6px 10px;
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}

/* Заглушка «нет данных»: по центру и с воздухом, а не прижата к левому краю строки.
   Ловится и по классу, и по атрибуту colspan — во всех шаблонах панели colspan стоит
   ровно на строках-заглушках и больше нигде, так что забыть класс на новой заглушке
   невозможно: она всё равно будет выглядеть правильно. */
.tbl td.empty, .tbl td[colspan] { text-align: center; color: var(--muted); padding: 18px 10px; }

/* ---------- полоски-доли (macros.html: bar / share_cell / count_cell) ---------- */

/* Полоска = дорожка (целое) + заливка (доля) + число. Ширину заливки считает шаблон и
   отдаёт инлайновым style: CSP разрешает style-src 'unsafe-inline', но запрещает
   скрипты — посчитать ширину в браузере нечем, да и не надо. */
.bar {
  display: flex;
  align-items: center;
  gap: 10px;
  /* потолок длины: на широком мониторе полоска иначе растягивается на пол-экрана,
     а 640px — это уже 0.15% на пиксель, точнее глазу всё равно не надо */
  max-width: 640px;
}
.bar-track {
  flex: 1 1 auto;
  min-width: 60px;
  height: 8px;
  border-radius: 999px;
  background: var(--mute-bg);
  overflow: hidden;
}
.bar-fill {
  display: block;
  height: 100%;
  min-width: 2px;              /* доля в десятые доли процента всё-таки должна быть видна */
  border-radius: 999px;
  background: var(--accent);
}
/* Число рядом с полоской: одинаковая ширина колонки и цифры одной ширины — все полоски
   заканчиваются на одной вертикали, числа сравниваются столбиком. */
.bar-num {
  flex: 0 0 auto;
  min-width: 4.5em;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
/* Тон заливки = смысл, те же четыре цвета, что у .pill — палитра в интерфейсе одна. */
.bar-fill.ok   { background: var(--ok-fg); }
.bar-fill.warn { background: var(--warn-fg); }
.bar-fill.bad  { background: var(--bad-fg); }
.bar-fill.mute { background: var(--mute-fg); }

/* ---------- панель массовых операций (по галочкам в таблице боксов) ---------- */

.bulkbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
  padding: 12px 16px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.bulk-push { display: flex; align-items: center; gap: 6px; }
.bulkbar input[type="text"] {
  padding: 7px 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 13px;
  width: 220px;
}

.bulk-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}
.bulk-row:last-child { border-bottom: none; }
.bulk-row details { margin-left: auto; }

/* ---------- выдвижная панель (карточка бокса) и модалка TOTP ---------- */

.drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: var(--drawer-w);
  background: var(--card);
  border-left: 1px solid var(--border);
  box-shadow: -8px 0 24px rgba(20, 24, 40, .1);
  overflow-y: auto;
  z-index: 30;
  padding: 20px;
  transform: translateX(100%);
  transition: transform .18s ease;
}
/* пустой div (без hx-контента) не показываем вовсе */
.drawer:empty { display: none; }
.drawer:not(:empty), .drawer.open { transform: translateX(0); }
/* «Развернуть» на вкладке «Экран» — иначе noVNC ужат в 45% ширины окна */
.drawer.wide { width: 100%; }

/* ---------- панель бокса: заголовок, вкладки, кнопки, терминал ---------- */

.drawer header {
  display: flex;
  align-items: center;
  gap: 8px;
}
.drawer header .x {
  margin-left: auto;
  background: none;
  border: none;
  font-size: 16px;
  color: var(--muted);
  cursor: pointer;
}
.drawer header .x:hover { color: var(--text); }

.tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border);
  margin: 12px 0 16px;
}
.tabs a {
  padding: 8px 12px;
  color: var(--muted);
  border-bottom: 2px solid transparent;
  cursor: pointer;
}
.tabs a:hover { color: var(--text); }
.tabs a.on {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

.btn-row, .console-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}
.console-row input { flex: 1 1 auto; }

.vnc {
  min-height: 420px;
  background: #10131c;
  border-radius: 8px;
  overflow: auto;
}

pre.term {
  background: #10131c;
  color: #d7dbe6;
  border-radius: 8px;
  padding: 12px;
  max-height: 360px;
  overflow-y: auto;
  font-family: "SF Mono", Consolas, monospace;
  font-size: 12px;
  white-space: pre-wrap;
  word-break: break-word;
}

.modal {
  position: fixed;
  inset: 0;
  background: rgba(20, 24, 40, .35);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 40;
}
.modal .card {
  width: 320px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.modal input {
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 16px;
}

/* ---------- страница событий: форма фильтров ---------- */

.filters {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 16px;
}
.filters label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--muted);
}
.filters select, .filters input {
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  color: var(--text);
}

/* ---------- страница входа ---------- */

.login-page {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
}
.card.login {
  width: 320px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 24px;
}
.card.login h1 { text-align: center; color: var(--accent); }
.card.login label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--muted);
}
.card.login input {
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  color: var(--text);
}
.err {
  color: var(--bad-fg);
  background: var(--bad-bg);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 13px;
  margin: 0;
}

/* ---------- мобильная раскладка ---------- */

@media (max-width: 768px) {
  .burger { display: inline-block; }
  .side {
    transform: translateX(-100%);
    transition: transform .18s ease;
    box-shadow: 8px 0 24px rgba(20, 24, 40, .1);
  }
  .side.open { transform: translateX(0); }
  main { margin-left: 0; }
  :root { --drawer-w: 100%; }
  /* На узком экране прячем помеченные колонки (в парке — IP и CPU): их некуда впихнуть
     без горизонтального скролла. Раньше скрытие шло по номеру колонки
     (table.hosts td:nth-child(4)), а класс hosts носят несколько разных таблиц, и
     четвёртая колонка у каждой своя — прячем помеченное, а не посчитанное. */
  .tbl .c-sm-hide, table.hosts .c-sm-hide { display: none; }
  /* Вторая, вертикальная прокрутка внутри карточки на телефоне мешает больше, чем
     помогает липкая шапка, — ограничение высоты снимаем. */
  .tbl-wrap.tall { max-height: none; }
  /* Длинный идентификатор (IPv6, путь к бэкапу) на телефоне переносится, а не распирает
     таблицу: если её забыли обернуть в .tbl-wrap, деваться ему больше некуда и вбок
     уезжает вся страница. На широком экране перенос не нужен — там колонка влезает. */
  .tbl .c-id { white-space: normal; word-break: break-all; }
  /* Шапке там же разрешаем перенос: nowrap держит ритм колонок на широком экране, но на
     380px пять непереносимых подписей задают таблице ширину больше самого экрана.
     Вторая строка нужна из-за специфичности: у роли-колонки (.tbl .c-num) вес выше, чем
     у .tbl th, и без явного перечисления «макс. cookies» осталась бы в одну строку. */
  .tbl th, table.hosts th { white-space: normal; }
  .tbl th.c-num, .tbl th.c-id, .tbl th.c-key, .tbl th.c-time { white-space: normal; }
  /* Страховка на случай таблицы, которую забыли обернуть в .tbl-wrap: пусть карточка
     едет вбок внутри себя, а не тянет за собой всю страницу. Модификатор flush от этого
     не ломается — его отрицательные поля доходят ровно до края padding-бокса карточки,
     то есть лишней прокрутки не создают. */
  .card { overflow-x: auto; }
  /* Полоске и обрезанному тексту на узком экране столько места не найдётся. */
  .bar-num { min-width: 3.5em; }
  .tbl.tight .c-bar { min-width: 90px; }
  .tbl .cut { max-width: 14ch; }
}

/* плитки пульта: цвет = состояние; серая (mute) значит «нет данных», а не «плохо» */
.tile-ok   { border-left: 3px solid var(--ok-fg); }
.tile-warn { border-left: 3px solid var(--warn-fg); }
.tile-bad  { border-left: 3px solid var(--bad-fg); }
.tile-mute { border-left: 3px solid var(--border); }
.tile-hint { font-size: 11px; color: var(--muted); margin-top: 4px; }
