/* logmem — фирменный стиль Keenetic / Netcraze.
 *
 * Токены ниже перенесены один в один из дизайн-системы (flutter_ui_kit,
 * lib/kitGen/{light,dark}_theme.dart + sizes.dart): те же рампы brand/gray/
 * green/red/yellow/violet, те же радиусы 4/6/8/12/16, тот же шаг отступов и та
 * же типографика Roboto с весами 400/500/600. Ничего не подобрано «на глаз» —
 * если ветка палитры поедет в ките, править здесь надо ровно те же числа.
 *
 * Две вещи, которые дизайн-система в себе не несёт, а просмотрщик логов требует:
 *
 *  1. МОНОШИРИННЫЙ слой. Roboto — шрифт интерфейса, но время, uuid, caller и
 *     тело строки читаются столбиком и сравниваются посимвольно, поэтому у
 *     данных свой шрифт. Фирменный стиль здесь — в хроме (панели, кнопки,
 *     карточки, цвета), данные остаются данными.
 *  2. ПЛОТНОСТЬ. Кит рассчитан на мобильный экран с кнопками высотой 44–56 px;
 *     на экран разбора инцидента столько воздуха не помещается. Взяты нижние
 *     ступени шкалы (padding 4/6/8, radius 4/6/8), а не средние, — пропорции
 *     кита сохранены, масштаб уменьшен.
 *
 * Тем две. Светлая — основная (в ките scaffold = gray10, у него это #F0F1F4),
 * тёмная — по системной настройке или тумблером в шапке; выбор живёт в
 * localStorage. Внешних шрифтов и CDN по-прежнему нет ни одного. */

/* ===== ТОКЕНЫ ===== */
:root {
  /* Шкала отступов и радиусов — Sizes из кита. */
  --sp-2: 2px;  --sp-4: 4px;  --sp-6: 6px;   --sp-8: 8px;
  --sp-12: 12px; --sp-16: 16px; --sp-24: 24px;
  --r-s: 4px; --r-m: 6px; --r-l: 8px; --r-xl: 12px; --r-xxl: 16px;

  /* Roboto — фирменный шрифт кита (AppTextStyles.defaultTextStyle). Локальный,
     без CDN: где его нет, подхватывается системный гротеск той же метрики. */
  --font: Roboto, "Roboto Flex", "Helvetica Neue", -apple-system, BlinkMacSystemFont,
          "Segoe UI", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono", Consolas,
          "DejaVu Sans Mono", monospace;

  color-scheme: light;

  /* --- Светлая рампа (lib/kitGen/light_theme.dart) --- */
  --brand-5:  #f0f6fe; --brand-10: #dfedfd; --brand-15: #cce3fb; --brand-20: #aad0f7;
  --brand-30: #58abe7; --brand-40: #209cde; --brand-50: #007bbe; --brand-60: #0564a0;
  --brand-70: #00416b; --brand-80: #0e2d52;

  --gray-0:  #ffffff; --gray-5:  #f9fafa; --gray-10: #f0f1f4; --gray-15: #e7e8ec;
  --gray-20: #d0d4d9; --gray-30: #9899a1; --gray-40: #787d89; --gray-50: #6a6f7c;
  --gray-60: #505660; --gray-70: #3f414d; --gray-80: #2f3237;

  --green-10: #f1f7e9; --green-20: #d4ecbd; --green-30: #a2d46f; --green-40: #81c241;
  --green-50: #66ab20; --green-60: #429c0a; --green-70: #006e1b;

  --red-10: #ffeff1; --red-15: #ffccd3; --red-20: #fdabb5; --red-30: #f55e73;
  --red-40: #e9344b; --red-50: #df031f; --red-60: #aa0015; --red-70: #780011;

  --yellow-10: #fef5e6; --yellow-15: #ffe7cb; --yellow-20: #fedaae; --yellow-30: #fea633;
  --yellow-40: #f57404; --yellow-50: #e95700; --yellow-60: #ac3f00;

  --violet-30: #b061d8; --violet-40: #9934cc; --violet-50: #8b28be;
  --cyan-30: #37ccbe; --cyan-50: #009487;

  /* --- Семантика: имена, которыми пользуется вся остальная разметка --- */
  --bg:          var(--gray-10);   /* scaffoldBackgroundColor кита */
  --surface:     var(--gray-0);
  --surface-alt: var(--gray-5);
  --surface-sunken: var(--gray-10);
  --border:      var(--gray-20);
  --border-soft: var(--gray-15);

  --fg:       var(--gray-80);
  --fg-dim:   var(--gray-50);
  --fg-faint: var(--gray-30);
  --fg-on-brand: var(--gray-0);

  --accent:       var(--brand-50);   /* primary button normal */
  --accent-hover: var(--brand-30);   /* primary button hovered */
  --accent-press: var(--brand-60);   /* primary button pressed */
  --accent-line:  var(--brand-40);   /* граница активного контрола */
  --accent-soft:  var(--brand-10);   /* выбранная вкладка, hover secondary */
  --accent-softer:var(--brand-5);
  --focus:        var(--brand-20);   /* цвет фокус-кольца, spread 2 */

  --error: var(--red-50);
  --error-soft: var(--red-10);
  --warn:  var(--yellow-50);
  --warn-soft: var(--yellow-10);
  --success: var(--green-60);
  --success-soft: var(--green-10);
  --info: var(--brand-40);

  /* Цвета уровней лога. Живут в CSS, а не в app.js, потому что их читает и
     разметка (текст уровня, полоса строки), и скрипт (сегменты графика,
     точки легенды) — одно определение на оба. */
  --lvl-debug: var(--gray-40);
  --lvl-info:  var(--brand-40);
  --lvl-warn:  var(--yellow-40);
  --lvl-error: var(--red-50);
  --lvl-fatal: var(--red-70);
  --lvl-none:  var(--gray-20);

  /* Ряд для произвольных серий (source, err_class): фирменные оттенки в
     порядке различимости, а не яркости. */
  --ser-1: var(--brand-40); --ser-2: var(--green-50); --ser-3: var(--yellow-40);
  --ser-4: var(--violet-40); --ser-5: var(--red-40);  --ser-6: var(--cyan-50);
  --ser-7: var(--brand-70);  --ser-8: var(--gray-40);

  --shadow-card: 0 1px 2px rgba(47, 50, 55, .06);
  --shadow-pop:  0 4px 16px rgba(47, 50, 55, .12);
  --ring: 0 0 0 2px var(--focus);
}

/* --- Тёмная рампа (lib/kitGen/dark_theme.dart) --- */
:root[data-theme="dark"] {
  color-scheme: dark;

  --brand-5:  #161b29; --brand-10: #293d58; --brand-15: #0e2d52; --brand-20: #293d58;
  --brand-30: #0178b5; --brand-40: #1b9ee7; --brand-50: #1cb9ff; --brand-60: #73c5fc;
  --brand-70: #b6e4ff; --brand-80: #d5eefd;

  --gray-0:  #1d2533; --gray-5:  #15181f; --gray-10: #000000; --gray-15: #2e364b;
  --gray-20: #4a4e67; --gray-30: #616a89; --gray-40: #8a90a0; --gray-50: #8b95b5;
  --gray-60: #a7abc9; --gray-70: #c4c6dd; --gray-80: #dedfec;

  --green-10: #154126; --green-20: #318552; --green-30: #3e9b4f; --green-40: #4ab167;
  --green-50: #69c89a; --green-60: #91eab0; --green-70: #baf5c9;

  --red-10: #47131f; --red-15: #792939; --red-20: #b60017; --red-30: #d50d11;
  --red-40: #ef534f; --red-50: #f66366; --red-60: #ff8997; --red-70: #ffbbc3;

  --yellow-10: #5c2500; --yellow-15: #884400; --yellow-20: #ad620c; --yellow-30: #dc8100;
  --yellow-40: #ffd468; --yellow-50: #ffec4f; --yellow-60: #ffef92;

  --violet-30: #78359a; --violet-40: #9140ba; --violet-50: #b945f3;
  --cyan-30: #37ccbe; --cyan-50: #69c8c8;

  /* Полотно у кита в тёмной теме — чистый чёрный (gray10 = #000). Панели
     всплывают над ним поверхностью gray0 = #1D2533, поэтому иерархия
     «фон < карточка» здесь строится подъёмом, а не затемнением. */
  --bg:          var(--gray-10);
  --surface:     var(--gray-0);
  --surface-alt: var(--gray-5);
  --surface-sunken: #11151d;
  --border:      var(--gray-15);
  --border-soft: #232a3a;

  --fg:       var(--gray-80);
  --fg-dim:   var(--gray-50);
  --fg-faint: var(--gray-30);
  --fg-on-brand: #06121f;

  --accent:       var(--brand-40);
  --accent-hover: var(--brand-50);
  --accent-press: var(--brand-60);
  --accent-line:  var(--brand-40);
  --accent-soft:  var(--brand-10);
  --accent-softer:var(--brand-5);
  --focus:        var(--brand-30);

  --error: var(--red-40);
  --error-soft: var(--red-10);
  --warn:  var(--yellow-40);
  --warn-soft: var(--yellow-10);
  --success: var(--green-40);
  --success-soft: var(--green-10);
  --info: var(--brand-50);

  /* debug на чёрном полотне светлее фона, а не темнее, поэтому ступень берётся
     ниже светлой: иначе самый малозначимый уровень оказывается самым ярким. */
  --lvl-debug: var(--gray-30);
  --lvl-info:  var(--brand-50);
  --lvl-warn:  var(--yellow-30);
  --lvl-error: var(--red-40);
  --lvl-fatal: var(--red-30);
  --lvl-none:  var(--gray-20);

  --ser-1: var(--brand-50); --ser-2: var(--green-40); --ser-3: var(--yellow-30);
  --ser-4: var(--violet-50); --ser-5: var(--red-40);  --ser-6: var(--cyan-50);
  --ser-7: var(--brand-70);  --ser-8: var(--gray-40);

  --shadow-card: 0 1px 2px rgba(0, 0, 0, .5);
  --shadow-pop:  0 6px 20px rgba(0, 0, 0, .6);
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--fg);
  font-family: var(--font); font-size: 13px; line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  display: flex; flex-direction: column; overflow: hidden;
}

/* Фокус виден всегда и везде одинаково: кольцо brand20 со spread 2 — ровно то,
   что кит рисует у кнопок, полей и вкладок (focused: shadowColor brand20). */
:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--r-m); }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--gray-20); border-radius: 5px; border: 2px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: var(--gray-30); }

/* ===== ШАПКА =====
 *
 * Две строки: «что ищем» и «где ищем». Подписей над контролами нет — девять
 * микро-заголовков капсом съедали высоту таблицы, ничего не добавляя к
 * плейсхолдерам. */
.bar {
  flex: none; background: var(--surface);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-card);
  position: relative; z-index: 5;
}
.bar-row { display: flex; align-items: center; gap: var(--sp-8); padding: var(--sp-8) var(--sp-16); }
.bar-row.second { padding-top: 0; padding-bottom: var(--sp-8); }
.bar-row.custom { padding-top: 0; padding-bottom: var(--sp-8); gap: var(--sp-12); }
.bar-row.off { display: none; }

/* Знак: скруглённый квадрат brand50 с меткой потока внутри. Логотип, а не
   слово капсом, — точка фирменного узнавания в интерфейсе без картинок. */
.brand { display: flex; align-items: center; gap: var(--sp-8); flex: none; margin-right: var(--sp-4); }
.brand-mark {
  width: 26px; height: 26px; border-radius: var(--r-l); flex: none;
  background: linear-gradient(135deg, var(--brand-40), var(--brand-50));
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 1px 3px rgba(0, 123, 190, .35);
}
.brand-mark svg { display: block; }
.brand-name { font-size: 15px; font-weight: 600; letter-spacing: -.2px; color: var(--fg); }

/* Вкладки — UiKitTabs: дорожка с рамкой gray15 и внутренним отступом, внутри
   пилюли; выбранная заливается brand10, наведённая brand5. */
.tabs {
  display: flex; gap: var(--sp-2); flex: none;
  background: var(--surface); border: 1px solid var(--border-soft);
  border-radius: var(--r-xl); padding: 3px;
}
.tab {
  background: transparent; color: var(--fg-dim); border: none;
  border-radius: 9px; padding: 5px 14px; cursor: pointer;
  font-family: inherit; font-size: 13px; font-weight: 500;
  transition: background .12s, color .12s;
}
.tab:hover { background: var(--accent-softer); color: var(--fg); }
.tab.active { background: var(--accent-soft); color: var(--brand-60); font-weight: 600; }
:root[data-theme="dark"] .tab.active { color: var(--brand-70); }

/* Поле поиска — самый широкий элемент экрана: это главный орган управления. */
.search-wrap {
  flex: 1 1 auto; min-width: 220px; display: flex; align-items: stretch;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-l);
  overflow: hidden; transition: border-color .12s, box-shadow .12s;
}
.search-wrap:focus-within { border-color: var(--accent-line); box-shadow: var(--ring); }
.q {
  flex: 1; background: transparent; color: var(--fg); border: none; outline: none;
  padding: 7px 12px; font-family: inherit; font-size: 13px; min-width: 0;
}
.q::placeholder { color: var(--fg-faint); }
.kind {
  flex: none; background: var(--surface-alt); color: var(--fg-dim); border: none;
  border-left: 1px solid var(--border-soft); outline: none; cursor: pointer;
  padding: 0 var(--sp-8); font-family: inherit; font-size: 12px; font-weight: 500;
}
/* Распознанное намерение подсвечивает поле: догадка о том, ЧТО спросили у
   стора, не должна быть молчаливой — иначе пустой ответ читается как «строк
   нет», хотя спрошено было не то. */
.search-wrap[data-kind="id"]     { border-color: var(--brand-40); }
.search-wrap[data-kind="id"] .kind     { color: var(--info); }
.search-wrap[data-kind="caller"] { border-color: var(--green-40); }
.search-wrap[data-kind="caller"] .kind { color: var(--success); }
.search-wrap[data-kind="needle"] .kind { color: var(--warn); }

.token {
  flex: none; width: 104px; background: var(--surface); color: var(--fg);
  border: 1px solid var(--border); border-radius: var(--r-l); padding: 7px 10px;
  font-family: inherit; font-size: 12px; outline: none; transition: border-color .12s;
}
.token:focus { border-color: var(--accent-line); box-shadow: var(--ring); }

/* Тумблер темы: иконка в квадрате secondary-кнопки. */
.icon-btn {
  flex: none; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
  background: var(--surface); color: var(--fg-dim);
  border: 1px solid var(--border); border-radius: var(--r-l); cursor: pointer;
  font-size: 14px; line-height: 1; transition: background .12s, border-color .12s, color .12s;
}
.icon-btn:hover { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }

/* ===== СЕГМЕНТЫ: окно, уровень, группировка =====
 * Дорожка с утопленным фоном, внутри пилюли — тот же приём, что у вкладок кита,
 * в плотной ступени шкалы. */
.seg {
  display: flex; flex: none; gap: 2px;
  background: var(--surface-sunken); border: 1px solid var(--border-soft);
  border-radius: var(--r-l); padding: 2px;
}
.seg button {
  background: transparent; color: var(--fg-dim); border: none;
  border-radius: var(--r-m); padding: 4px 10px; cursor: pointer;
  font-family: inherit; font-size: 12px; font-weight: 500; white-space: nowrap;
  transition: background .12s, color .12s;
}
.seg button:hover { background: var(--accent-softer); color: var(--fg); }
.seg button.active {
  background: var(--accent); border-color: var(--accent); color: var(--fg-on-brand); font-weight: 600;
}
/* Уровни красятся своим цветом в активном состоянии: «включён фильтр ошибок»
   должно читаться боковым зрением, а не вычитываться. */
.seg.levels button[data-level="error"].active { background: var(--lvl-error); }
.seg.levels button[data-level="fatal"].active { background: var(--lvl-fatal); color: #fff; }
.seg.levels button[data-level="warn"].active  { background: var(--lvl-warn); color: #2f1d00; }
.seg.levels button[data-level="info"].active  { background: var(--lvl-info); }
.seg.levels button[data-level="debug"].active { background: var(--lvl-debug); color: #fff; }
.seg.tiny button { padding: 3px 8px; font-size: 11px; }

/* Кнопки. .ghost — secondary кита: белая, рамка brand40, на наведении brand10. */
.ghost {
  background: var(--surface); color: var(--fg); border: 1px solid var(--border);
  border-radius: var(--r-l); padding: 5px 12px; cursor: pointer;
  font-family: inherit; font-size: 12px; font-weight: 500;
  transition: background .12s, border-color .12s, color .12s;
}
.ghost:hover { background: var(--accent-soft); border-color: var(--accent-line); color: var(--brand-60); }
.ghost:active { background: var(--accent); border-color: var(--accent); color: var(--fg-on-brand); }
.ghost:disabled { background: var(--surface-alt); border-color: var(--border-soft); color: var(--fg-faint); cursor: default; }
:root[data-theme="dark"] .ghost:hover { color: var(--brand-70); }

/* Селектор источника. Правило намеренно привязано к ТЕГУ: класс .src носит ещё
   и кликабельное значение источника в строке таблицы, и рамка выпадашки на нём
   читалась как кнопка, которой там нет. */
select.src {
  flex: none; max-width: 190px; background: var(--surface); color: var(--fg-dim);
  border: 1px solid var(--border); border-radius: var(--r-l); padding: 5px var(--sp-8);
  font-family: inherit; font-size: 12px; outline: none; cursor: pointer;
}
select.src:focus { border-color: var(--accent-line); box-shadow: var(--ring); }

.check {
  display: flex; align-items: center; gap: var(--sp-6); flex: none;
  color: var(--fg-dim); cursor: pointer; user-select: none; font-size: 12px; font-weight: 500;
}
.check input { accent-color: var(--accent); cursor: pointer; margin: 0; width: 14px; height: 14px; }
body.following .check { color: var(--success); }

.inline { display: flex; align-items: center; gap: var(--sp-6); color: var(--fg-dim); font-size: 12px; }
.inline input {
  background: var(--surface); color: var(--fg); border: 1px solid var(--border);
  border-radius: var(--r-m); padding: 4px var(--sp-8); font-family: inherit; font-size: 12px; outline: none;
}
.inline input:focus { border-color: var(--accent-line); box-shadow: var(--ring); }
#from, #to { width: 210px; font-family: var(--mono); font-size: 11px; }
#agg-bucket { width: 56px; }
#agg-topn, #ov-topn { width: 56px; }
.q.small {
  flex: 1 1 180px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-l); padding: 5px 10px; font-size: 12px;
}
.q.small:focus { border-color: var(--accent-line); box-shadow: var(--ring); }

/* Сдвиг окна: стрелки вплотную к полю, поле узкое — в него пишут "2w". */
.shift { display: flex; align-items: center; flex: none; }
.shift input {
  width: 78px; text-align: center; background: var(--surface); color: var(--fg);
  border: 1px solid var(--border); border-left: 0; border-right: 0;
  padding: 5px 4px; font-family: inherit; font-size: 12px; outline: none;
}
.shift input:focus { border-color: var(--accent-line); position: relative; z-index: 1; }
.shift .ghost { padding: 5px 9px; }
.shift .ghost:first-child { border-radius: var(--r-l) 0 0 var(--r-l); }
.shift .ghost:last-child { border-radius: 0 var(--r-l) var(--r-l) 0; }

.sep { flex: none; width: 1px; height: 20px; background: var(--border-soft); margin: 0 var(--sp-4); }
.grow { flex: 1 1 auto; }

/* ===== СТРОКА СОСТОЯНИЯ ===== */
.status { flex: none; color: var(--fg-dim); font-size: 12px; white-space: nowrap; }
.status b { color: var(--fg); font-weight: 600; }
.status.error { color: var(--error); }
.warnflag {
  margin-left: var(--sp-8); padding: 1px var(--sp-8); border-radius: 999px;
  background: var(--warn-soft); color: var(--yellow-60); border: 1px solid var(--yellow-20);
  font-size: 11px; font-weight: 500;
}
:root[data-theme="dark"] .warnflag { color: var(--yellow-40); border-color: var(--yellow-15); }
.flash { color: var(--success); font-weight: 500; }
.flash.fade { animation: fade 2.5s forwards; }
@keyframes fade { from { opacity: 1; } to { opacity: 0; } }
body.busy .status { opacity: .45; }

/* ===== ВКЛАДКИ ===== */
.tabpane { display: none; flex: 1; min-height: 0; flex-direction: column; }
.tabpane.active { display: flex; }

/* ===== ГРАФИК ОБЪЁМА ===== */
.chart {
  flex: none; padding: var(--sp-8) var(--sp-16) var(--sp-12);
  background: var(--surface); border-bottom: 1px solid var(--border);
}
.chart.hidden { display: none; }
.chart-head { display: flex; align-items: center; gap: var(--sp-12); margin-bottom: var(--sp-6); }
.legend { display: flex; flex-wrap: wrap; gap: var(--sp-12); color: var(--fg-dim); font-size: 11px; }
.lg-item { display: flex; align-items: center; gap: var(--sp-4); }
.lg-dot { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }

/* Столбцы: высота стопки — доля от пика окна. Масштаб от пика, а не от нуля,
   потому что на ровном потоке всплеск в полтора раза иначе неразличим. */
.chart-body { display: flex; align-items: flex-end; gap: 1px; height: 88px; }
.cbar {
  flex: 1 1 0; min-width: 1px; height: 100%; display: flex; flex-direction: column;
  justify-content: flex-end; cursor: pointer; border-radius: 2px;
}
.cbar:hover { background: var(--accent-softer); }
.cstack { width: 100%; display: flex; flex-direction: column-reverse; min-height: 1px; border-radius: 2px 2px 0 0; overflow: hidden; }
.cseg { width: 100%; min-height: 1px; }
.chart-axis {
  display: flex; justify-content: space-between; align-items: baseline;
  color: var(--fg-faint); font-size: 11px; padding-top: var(--sp-6); font-variant-numeric: tabular-nums;
}

/* ===== ПОДСКАЗКА НАД СТОЛБЦОМ =====
 * BarChartTooltip из кита: поверхность gray5, рамка gray20, радиус 4, отступ 12,
 * подпись времени сверху приглушённая, значения — тёмные и в один столбец.
 * pointer-events:none обязателен — иначе подсказка перехватывала бы наведение у
 * столбца, из-под которого всплыла, и мигала бы под курсором. */
.tip {
  position: fixed; left: 0; top: 0; z-index: 50; pointer-events: none;
  background: var(--surface-alt); border: 1px solid var(--border);
  border-radius: var(--r-s); box-shadow: var(--shadow-pop);
  padding: var(--sp-8) var(--sp-12); min-width: 148px; max-width: 320px;
  font-size: 12px; color: var(--fg);
}
.tip.hidden { display: none; }
.tip-title {
  color: var(--fg-dim); font-weight: 600; padding-bottom: var(--sp-6);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.tip-row { display: flex; align-items: baseline; gap: var(--sp-6); padding: 1px 0; }
.tip-dot { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.tip-k { color: var(--fg-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tip-v { margin-left: auto; font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Итог отбит чертой: «всего» и одна из серий рядом одинаковым весом читаются
   как две серии. */
.tip-row.strong { margin-top: 3px; padding-top: var(--sp-4); border-top: 1px solid var(--border); }
.tip-row.strong .tip-k, .tip-row.strong .tip-v { color: var(--fg); font-weight: 600; }
.tip-note { color: var(--fg-faint); font-size: 11px; padding-top: var(--sp-4); white-space: normal; }

/* ===== ТАБЛИЦА =====
 * Данные моноширинные: время, uuid и caller читаются столбиком и сравниваются
 * посимвольно — пропорциональный шрифт здесь мешал бы работе. */
.table-wrap { flex: 1; overflow: auto; min-height: 0; position: relative; background: var(--surface); }
table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 12px; }
thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface-alt); color: var(--fg-dim); text-align: left;
  padding: var(--sp-6) var(--sp-8); border-bottom: 1px solid var(--border);
  font-family: var(--font); font-weight: 600; white-space: nowrap;
  font-size: 11px; letter-spacing: .3px; text-transform: uppercase;
}
tbody tr.row { border-bottom: 1px solid var(--border-soft); cursor: pointer; }
tbody tr.row:nth-child(4n+3) { background: var(--surface-alt); }
tbody tr.row:hover { background: var(--accent-softer); }
tbody tr.row.open { background: var(--accent-soft); }
tbody td { padding: 3px var(--sp-8); vertical-align: top; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Цветная полоса слева вместо раскраски всей строки: ошибки находятся взглядом
   по краю таблицы, а текст остаётся читаемым. */
tbody tr.row td.c-ts { border-left: 3px solid transparent; color: var(--fg-faint); font-variant-numeric: tabular-nums; }
tbody tr.row.is-error td.c-ts { border-left-color: var(--lvl-error); }
tbody tr.row.is-warn  td.c-ts { border-left-color: var(--lvl-warn); }
tbody tr.row.fresh td.c-ts { border-left-color: var(--success); }
tbody tr.row.is-error { background: var(--error-soft); }
tbody tr.row.is-error:hover { background: var(--red-15); }
:root[data-theme="dark"] tbody tr.row.is-error { background: rgba(71, 19, 31, .45); }

.c-ts { width: 120px; }
.c-level { width: 44px; }
.c-source { width: 150px; }
.c-host { width: 110px; }
.c-caller { width: 250px; }
td.c-msg { white-space: nowrap; max-width: 0; width: 100%; }

.lvl { font-weight: 700; text-transform: uppercase; font-size: 11px; }
.lvl-error, .lvl-fatal, .lvl-panic { color: var(--lvl-error); }
.lvl-fatal, .lvl-panic { color: var(--lvl-fatal); }
.lvl-warn, .lvl-warning { color: var(--lvl-warn); }
.lvl-info { color: var(--lvl-info); }
.lvl-debug, .lvl-trace { color: var(--lvl-debug); }
tr.row.is-error td.c-msg { color: var(--red-60); }
tr.row.is-warn td.c-msg { color: var(--yellow-60); }
:root[data-theme="dark"] tr.row.is-error td.c-msg { color: var(--red-60); }
:root[data-theme="dark"] tr.row.is-warn td.c-msg { color: var(--yellow-40); }

/* Кликабельные значения. */
.chip, .uuid { cursor: pointer; }
.chip { color: var(--fg-dim); border-bottom: 1px dotted transparent; }
.chip.caller { color: var(--fg-dim); }
/* Источник — метка, а не кнопка: заливка без рамки, чтобы столбец не
   превращался в частокол прямоугольников на каждой строке. */
.chip.src {
  background: var(--surface-sunken); color: var(--fg-dim);
  border-radius: var(--r-s); padding: 0 var(--sp-6);
}
.chip.src:hover { background: var(--accent-soft); color: var(--brand-60); border-bottom-color: transparent; }
:root[data-theme="dark"] .chip.src:hover { color: var(--brand-70); }
.chip:hover { color: var(--accent); border-bottom-color: var(--accent); }
.uuid { color: var(--info); border-bottom: 1px dotted var(--brand-20); }
.uuid:hover { color: var(--fg-on-brand); background: var(--accent); border-radius: var(--r-s); border-bottom-color: transparent; }

/* Раскрытая строка. */
tr.detail td { padding: 0; background: var(--surface-alt); border-left: 3px solid var(--accent); }
.meta {
  display: grid; grid-template-columns: max-content 1fr; gap: var(--sp-4) var(--sp-16);
  padding: var(--sp-12) var(--sp-16) var(--sp-6); align-items: baseline;
}
.meta-row { display: contents; }
.meta-k {
  color: var(--fg-faint); text-transform: uppercase; font-size: 10px;
  letter-spacing: .5px; font-family: var(--font); font-weight: 600;
}
.meta-v { color: var(--fg); word-break: break-all; }
tr.detail pre {
  margin: 0; padding: var(--sp-8) var(--sp-16) var(--sp-16); white-space: pre-wrap; word-break: break-word;
  color: var(--fg); font-family: var(--mono); font-size: 12px;
}

/* Пустой результат объясняет, что делать: «пусто» в логах чаще значит узкое
   окно или не тот способ поиска, чем отсутствие строк. */
.empty-state { padding: 48px var(--sp-24); color: var(--fg-dim); }
.empty-title { color: var(--fg); font-size: 15px; font-weight: 600; margin-bottom: var(--sp-8); }
.empty-tips { margin: 0; padding-left: 18px; font-size: 13px; }
.empty-tips li { margin: var(--sp-4) 0; }

/* Нижняя полоса. */
.footbar {
  flex: none; display: flex; align-items: center; gap: var(--sp-12);
  padding: var(--sp-8) var(--sp-16); background: var(--surface); border-top: 1px solid var(--border);
}
.hint { color: var(--fg-faint); font-size: 12px; }
kbd {
  background: var(--surface-alt); border: 1px solid var(--border); border-bottom-width: 2px;
  border-radius: var(--r-s); padding: 0 5px; font-family: var(--mono); font-size: 11px; color: var(--fg-dim);
}
.hidden { display: none; }

/* ===== КАРТОЧКИ =====
 * UikitCard: белая поверхность, рамка gray20, радиус — верхняя ступень шкалы,
 * на наведении рамка уходит в brand. */
.panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-xl); padding: var(--sp-16);
  box-shadow: var(--shadow-card); transition: border-color .15s;
}
.panel:hover { border-color: var(--brand-20); }
.panel h3 {
  margin: 0 0 var(--sp-12); font-size: 14px; color: var(--fg); font-weight: 600;
  display: flex; align-items: baseline; gap: var(--sp-8); flex-wrap: wrap;
}
.muted { color: var(--fg-dim); font-weight: 400; font-size: 12px; }

/* ===== АНАЛИТИКА =====
 * align-content: start — панели по высоте содержимого. Растянутые на весь
 * экран блоки с одной строкой «нет групп» читаются как поломка вёрстки. */
.analytics-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-16); align-content: start;
  padding: var(--sp-16); overflow: auto; flex: 1; min-height: 0;
}

.bars { display: flex; flex-direction: column; gap: var(--sp-2); }
.bar-row-g {
  display: grid; grid-template-columns: 1fr; gap: 3px; cursor: pointer;
  padding: var(--sp-4) var(--sp-6); border-radius: var(--r-m); transition: background .12s;
}
.bar-row-g:hover { background: var(--accent-softer); }
.bar-head { display: flex; justify-content: space-between; gap: var(--sp-8); font-size: 12px; }
.bar-key { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--mono); }
.bar-count { color: var(--fg-dim); flex: none; font-variant-numeric: tabular-nums; }
.bar-track { height: 6px; background: var(--surface-sunken); border-radius: 3px; overflow: hidden; position: relative; }
.bar-fill { height: 100%; background: var(--accent); border-radius: 3px; position: relative; }
/* Слой объёма под слоем ошибок: приглушён, чтобы «плохо» читалось поверх «много». */
.bar-fill.vol { opacity: .45; }

/* height:100% у колонки — обязателен: у .hist стоит align-items:flex-end, из-за
   чего колонки НЕ растягиваются, и процентная высота столбца разрешалась бы
   относительно родителя нулевой высоты. Без этого вся гистограмма схлопывалась
   в полоску в 1px — было видно, только когда в сторе появились реальные данные. */
.hist { display: flex; align-items: flex-end; gap: 2px; height: 200px; padding-top: var(--sp-8); }
.hist-col {
  flex: 1 1 0; height: 100%; display: flex; flex-direction: column;
  justify-content: flex-end; align-items: center; min-width: 2px;
}
.hist-bar { width: 100%; background: var(--brand-40); border-radius: 3px 3px 0 0; min-height: 1px; transition: background .12s; }
.hist-bar.zero { background: var(--border-soft); }
.hist-col:hover .hist-bar { background: var(--accent); }
/* Подписи краёв: без них по гистограмме не сказать, какой это отрезок времени. */
.hist-axis {
  display: flex; justify-content: space-between;
  color: var(--fg-faint); font-size: 11px; padding-top: var(--sp-6); font-variant-numeric: tabular-nums;
}

/* ===== OVERVIEW =====
 *
 * Вкладка отвечает на вопрос «что вообще происходит», и её панели читают
 * предпосчитанные сводки, а не сканируют окно. Отсюда две особенности вёрстки,
 * которых нет у остальных вкладок: она СКРОЛЛИТСЯ целиком (панелей много, и
 * ужимать их до одного экрана значило бы прятать половину), и в ней есть место
 * под оговорки — то, чего сводка не знает, пишется словами рядом с числами. */
.tabpane#tab-overview { overflow: auto; padding: var(--sp-16); gap: var(--sp-16); }
.panel.wide { width: 100%; }
.ov-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-16); align-items: start; }

/* Инвентарь суток: столбец на сутки. Цвет — где лежат данные, штриховка —
   «сводки нет». Нарисовать такие сутки нулём значило бы соврать, что в тот день
   было тихо, поэтому у них ВИДНЫЙ признак, а не отсутствие столбца. */
.days { display: flex; align-items: flex-end; gap: 3px; height: 96px; }
.day-col {
  flex: 1 1 0; height: 100%; display: flex; flex-direction: column;
  justify-content: flex-end; align-items: center; min-width: 6px; cursor: pointer;
  border-radius: var(--r-s);
}
.day-col:hover { background: var(--accent-softer); }
.day-bar { width: 100%; border-radius: 3px 3px 0 0; min-height: 3px; background: var(--accent); }
.day-bar.disk { background: var(--gray-20); }
.day-bar.ram { background: var(--brand-40); }
.day-bar.nosum {
  background-image: repeating-linear-gradient(45deg,
    rgba(255, 255, 255, .55) 0 3px, transparent 3px 6px);
  outline: 1px dashed var(--warn); outline-offset: -1px;
}
:root[data-theme="dark"] .day-bar.nosum {
  background-image: repeating-linear-gradient(45deg, rgba(0, 0, 0, .5) 0 3px, transparent 3px 6px);
}
.day-lbl { font-size: 10px; color: var(--fg-faint); padding-top: var(--sp-4); white-space: nowrap; }
.legend-row { display: flex; gap: var(--sp-16); font-size: 11px; padding-top: var(--sp-12); align-items: center; flex-wrap: wrap; }
.legend-row .hint { margin-left: auto; }
.sw { width: 10px; height: 10px; border-radius: 3px; display: inline-block; background: var(--accent); margin-right: var(--sp-4); }
.sw.ram { background: var(--brand-40); }
.sw.disk { background: var(--gray-20); }
.sw.nosum { background: var(--gray-20); outline: 1px dashed var(--warn); outline-offset: -1px; }
.lg { display: inline-flex; align-items: center; gap: var(--sp-2); }

/* График профиля выше поискового: здесь он главный элемент панели, а не
   подсказка над таблицей. */
.ov-chart { height: 150px; }

/* Граница суток внутри ленты столбцов. Корзина остаётся прежней — сутками
   размечается ось, а не данные. Черта рисуется РАМКОЙ столбца: при
   box-sizing: border-box она не сдвигает сетку, в отличие от вставленного
   между столбцами разделителя. */
.ov-chart .cbar.day-edge { border-left: 1px solid var(--border); }

.chart-axis.by-day { justify-content: flex-start; gap: 0; }
.ax-day {
  display: flex; flex-direction: column; min-width: 0; overflow: hidden;
  padding-left: var(--sp-6); border-left: 1px solid var(--border-soft);
}
.ax-day:first-child { border-left: none; padding-left: 0; }
.ax-day-lbl { color: var(--fg-dim); font-weight: 500; white-space: nowrap; }
.ax-day-sum { color: var(--fg-faint); white-space: nowrap; }

/* Плитки чисел. Крупное значение и мелкая подпись, потому что читают их
   взглядом по диагонали, а не построчно. */
.tiles { display: flex; flex-wrap: wrap; gap: var(--sp-8); }
.tile {
  flex: 1 1 104px; background: var(--surface-alt); border: 1px solid var(--border-soft);
  border-radius: var(--r-l); padding: var(--sp-8) var(--sp-12); min-width: 104px;
  transition: border-color .12s, background .12s;
}
.tile:hover { border-color: var(--brand-20); background: var(--accent-softer); }
.tile-v { font-size: 20px; font-weight: 600; color: var(--fg); line-height: 1.2; font-variant-numeric: tabular-nums; }
.tile-l { font-size: 11px; color: var(--fg-dim); padding-top: var(--sp-4); }

/* Гистограмма лага приёма: корзина «из будущего» красится отдельно — часы
   клиента впереди наших это диагноз, а не мелкая погрешность. */
.lag { display: flex; align-items: flex-end; gap: var(--sp-4); height: 74px; padding-top: var(--sp-12); flex-wrap: wrap; }
.lag-title { color: var(--fg-dim); font-size: 11px; align-self: center; }
.lag-cell {
  flex: 0 1 44px; height: 100%; display: flex; flex-direction: column;
  justify-content: flex-end; align-items: center;
}
.lag-bar { width: 100%; background: var(--green-40); border-radius: 3px 3px 0 0; min-height: 2px; }
.lag-bar.future { background: var(--warn); }
.lag-lbl { font-size: 10px; color: var(--fg-faint); padding-top: 3px; }

/* Доля ошибок вторым слоем под объёмом: видно и «много», и «плохо». */
.bar-fill.err { background: var(--error); position: absolute; left: 0; top: 0; z-index: 1; }
.bar-fill.down { background: var(--fg-faint); }

/* Что нового: три колонки, каждая — свой вопрос про перемену. */
.novelty { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-16); }
.nov-col { min-width: 0; }
.nov-title {
  font-size: 12px; font-weight: 600; color: var(--fg); padding-bottom: var(--sp-6);
  border-bottom: 1px solid var(--border-soft); margin-bottom: var(--sp-6);
}
.nov-item {
  font-size: 12px; color: var(--fg-dim); padding: 3px var(--sp-6); border-radius: var(--r-s);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer;
  font-family: var(--mono); transition: background .12s, color .12s;
}
.nov-item:hover { background: var(--accent-softer); color: var(--fg); }
.mono { font-family: var(--mono); }

/* Сравнение окон: рост и падение — разные цвета, потому что «стало вдвое
   меньше» и «стало вдвое больше» одинаково важны и путать их нельзя. */
.changes { display: flex; flex-direction: column; gap: var(--sp-2); }
.chg { display: grid; gap: 3px; cursor: pointer; padding: var(--sp-4) var(--sp-6); border-radius: var(--r-m); transition: background .12s; }
.chg:hover { background: var(--accent-softer); }
.chg.up .bar-count { color: var(--warn); font-weight: 600; }
.chg.down .bar-count { color: var(--fg-dim); }

/* ===== CONFIG =====
 *
 * Вкладка устроена как ЭКРАН НАСТРОЙКИ: наверху разделы по задачам оператора с
 * контролами под величину, внизу свёрнутый справочник остального конфига. Отсюда
 * два разных оформления вместо одной таблицы на всё — карточка ручки (крупное
 * имя, контрол, последствие) и плотная строка справочника (ключ, значение,
 * слой). Прежняя общая сетка обходилась им обоим одинаково плохо: настройке
 * было тесно, справке — просторно.
 *
 * Скроллится вкладка целиком, как Overview. */
.tabpane#tab-config { overflow: auto; padding: var(--sp-16); gap: var(--sp-16); }

/* Факты о процессе едут в панели управления: они меняют СМЫСЛ правки (переживёт
   ли она рестарт, есть ли кому применять правила), а не украшают её. */
.cfg-proc { display: flex; flex-wrap: wrap; gap: var(--sp-8) var(--sp-16); font-size: 12px; color: var(--fg-dim); }
.cfg-proc b { color: var(--fg); font-weight: 600; font-family: var(--mono); }

/* Ручки — карточками в две колонки. Не таблицей: у контролов разная высота
   (чипы растут списком, ползунок несёт подпись), и строки таблицы прыгали бы
   вслед за самой длинной ячейкой. */
.cfg-sec { margin-bottom: var(--sp-16); }
.cfg-sec > .hint { margin: calc(-1 * var(--sp-8)) 0 var(--sp-12); }
.knobs { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: var(--sp-12); align-items: start; }
.knob {
  display: flex; flex-direction: column; gap: var(--sp-6);
  padding: var(--sp-12); border: 1px solid var(--border-soft); border-radius: var(--r-l);
  background: var(--surface-alt); transition: border-color .12s, background .12s;
}
.knob:hover { border-color: var(--border); }
/* Тронутая ручка подсвечивается рамкой: панель применения внизу называет
   число правок, а найти их глазами надо здесь. */
.knob.dirty { border-color: var(--accent-line); background: var(--accent-softer); }
.knob-head { display: flex; align-items: baseline; gap: var(--sp-6); flex-wrap: wrap; }
.knob-name { font-size: 13px; font-weight: 600; color: var(--fg); }
.knob-key { font-family: var(--mono); font-size: 11px; color: var(--fg-faint); }
/* Опасная ручка (потеря строк, выселение суток) отмечена полосой слева, а не
   цветом текста: предупреждение должно читаться до того, как в неё полезли. */
.knob.dangerous { border-left: 2px solid var(--yellow-30); }
.knob-why { font-size: 11px; color: var(--fg-dim); line-height: 1.45; }
.knob-why.fate { color: var(--fg-faint); }
.knob-ctl { display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap; min-height: 28px; }
.knob-out { font-size: 12px; color: var(--fg-dim); font-variant-numeric: tabular-nums; }
.knob-out.hot { color: var(--warn); font-weight: 500; }
.knob-ctl input[type="text"], .knob-ctl input[type="number"] {
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--border); border-radius: var(--r-m); padding: 4px var(--sp-8);
  font-family: var(--mono); font-size: 12px; outline: none; transition: border-color .12s;
  min-width: 0;
}
.knob-ctl input:focus { border-color: var(--accent-line); box-shadow: var(--ring); }
.knob-ctl input[type="text"] { flex: 1 1 120px; }
.knob-ctl .size-n { width: 88px; }
.knob-ctl .dur-in { flex: 0 0 78px; }
.size-u {
  background: var(--surface); color: var(--fg); border: 1px solid var(--border);
  border-radius: var(--r-m); padding: 4px var(--sp-6); font-family: inherit; font-size: 12px;
  cursor: pointer;
}
.ghost.tiny { padding: 2px var(--sp-8); font-size: 11px; }

/* Тумблер. Форма кита: скруглённая дорожка и кружок, без галочки — состояние
   читается положением, а не значком. */
.switch { position: relative; display: inline-flex; flex: none; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  width: 36px; height: 20px; border-radius: 10px; background: var(--gray-20);
  transition: background .15s; position: relative;
}
.switch-track::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
  border-radius: 50%; background: var(--gray-0); box-shadow: var(--shadow-card);
  transition: transform .15s;
}
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track::after { transform: translateX(16px); }
.switch input:focus-visible + .switch-track { box-shadow: var(--ring); }

/* Ползунок доли. Дорожка красится в предупреждающий цвет по мере роста не
   градиентом, а цветом бегунка: прореживание на любой доле — потеря строк. */
.knob-ctl input[type="range"] {
  flex: 1 1 140px; min-width: 120px; height: 20px; accent-color: var(--warn);
  background: transparent; cursor: pointer;
}

/* Чипы списка. Значение убирается крестиком, а не перенабором строки: список
   источников правят по одному элементу, а не целиком. */
.chips-ctl { align-items: flex-start; flex-direction: column; }
.chips { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
.chip-x {
  display: inline-flex; align-items: center; gap: var(--sp-4);
  padding: 2px var(--sp-4) 2px var(--sp-6); border-radius: var(--r-s);
  background: var(--accent-soft); color: var(--brand-60);
  font-family: var(--mono); font-size: 11px;
}
.chip-x button {
  border: none; background: transparent; color: inherit; cursor: pointer;
  font-size: 13px; line-height: 1; padding: 0 2px; opacity: .6;
}
.chip-x button:hover { opacity: 1; color: var(--error); }
/* Селектор длиннее соседнего намеренно: `.knob-ctl input[type="text"]` весит
   больше короткого `.chips-ctl .chip-input`, и его flex-grow растягивал поле
   ввода на всю высоту карточки — в колоночном flex тянется высота. */
.knob-ctl.chips-ctl input.chip-input { width: 100%; flex: none; }
:root[data-theme="dark"] .chip-x { color: var(--brand-70); }

/* Комбобокс претендентов. Не селект: список открыт — источник, который ещё не
   написал ни строки, вписать законно, его просто нечем подсказать. Отсюда
   форма — поле ввода с кнопкой, а не выпадающий список. */
.combo-wrap { position: relative; width: 100%; display: flex; align-items: center; }
.combo-btn {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  border: none; background: transparent; color: var(--fg-dim);
  cursor: pointer; font-size: 11px; line-height: 1; padding: 4px var(--sp-4);
  border-radius: var(--r-s);
}
.combo-btn:hover { color: var(--accent); background: var(--accent-soft); }
.knob-ctl.chips-ctl input.chip-input { padding-right: 24px; }

.combo-pop {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 20;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-l); box-shadow: var(--shadow-pop); overflow: hidden;
}
.combo-pop.hidden { display: none; }
.combo-head {
  padding: var(--sp-6) var(--sp-8); font-size: 11px; color: var(--fg-dim);
  background: var(--surface-alt); border-bottom: 1px solid var(--border-soft);
}
/* Потолок высоты — примерно восемь строк: список ранжирован, и девятый
   претендент по объёму уже не тот, ради кого открывают выпадашку. */
.combo-list { max-height: 232px; overflow-y: auto; }
.combo-row {
  display: grid; grid-template-columns: 1fr auto 56px; gap: var(--sp-8);
  align-items: center; padding: var(--sp-4) var(--sp-8); cursor: pointer;
}
.combo-row:hover, .combo-row.on { background: var(--accent-soft); }
.combo-key {
  font-family: var(--mono); font-size: 11px; color: var(--fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.combo-count { font-size: 11px; color: var(--fg-dim); font-variant-numeric: tabular-nums; }
/* Доля — числом И полоской под ним: «14%» и «1.4%» на глаз различаются хуже,
   чем длины, а решают именно по ней. */
.combo-share {
  font-size: 11px; color: var(--fg-dim); font-variant-numeric: tabular-nums;
  text-align: right; padding-bottom: 3px;
  background-image: linear-gradient(var(--ser-1), var(--ser-1));
  background-repeat: no-repeat; background-position: right bottom;
  background-size: var(--w, 0) 2px;
}
/* Уже выбранный претендент остаётся в списке, но не кликается: убрать его
   пропажей из выпадашки значило бы прятать от оператора то, что он только что
   сделал. */
.combo-row.taken { cursor: default; opacity: .5; grid-template-columns: 1fr auto; }
.combo-row.taken:hover { background: transparent; }
.combo-row.taken .combo-count, .combo-row.taken .combo-share { display: none; }
.combo-taken { font-size: 11px; color: var(--fg-faint); white-space: nowrap; }
.combo-empty { padding: var(--sp-8); font-size: 11px; color: var(--fg-dim); }
.combo-foot {
  padding: var(--sp-6) var(--sp-8); font-size: 11px; color: var(--fg-faint);
  border-top: 1px solid var(--border-soft); background: var(--surface-alt); line-height: 1.4;
}

/* Обратная связь под разделом: то, по чему ручки настраивают. Отделена линией,
   потому что это НЕ настройка, а её последствие. */
.cfg-feedback { margin-top: var(--sp-12); padding-top: var(--sp-12); border-top: 1px solid var(--border-soft); }
.cfg-feedback-title { font-size: 12px; color: var(--fg-dim); margin-bottom: var(--sp-8); }
.cfg-nums { display: flex; flex-wrap: wrap; gap: var(--sp-16); }
.cfg-num { display: flex; flex-direction: column; gap: 2px; min-width: 92px; }
.cfg-num b { font-size: 18px; font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
.cfg-num span { font-size: 11px; color: var(--fg-dim); }
.cfg-num.warn b { color: var(--warn); }

/* Справочник остального конфига. Свёрнут, плотный, с фильтром: сюда приходят с
   вопросом «почему значение такое», а не настраивать. */
.cfg-rest { padding: var(--sp-12) var(--sp-16); }
.cfg-rest > summary {
  cursor: pointer; font-size: 13px; font-weight: 600; color: var(--fg);
  display: flex; align-items: baseline; gap: var(--sp-8);
}
.cfg-rest > summary::marker { color: var(--fg-faint); }
.cfg-rest > summary:hover { color: var(--accent); }
.cfg-rest-body { display: flex; flex-direction: column; gap: var(--sp-8); margin-top: var(--sp-12); }
/* flex:none обязателен: у `.q` есть flex-grow, а `.cfg-rest-body` — колонка,
   и поле фильтра растягивалось по высоте на весь свободный кусок панели. */
.cfg-rest-body .q.small { max-width: 320px; flex: none; }
.cfg-ro { display: flex; flex-direction: column; }
.cfg-ro-group {
  font-family: var(--mono); font-size: 11px; color: var(--fg-faint);
  margin: var(--sp-8) 0 var(--sp-4); text-transform: uppercase; letter-spacing: .04em;
}
.cfg-ro-row {
  display: grid;
  /* Ключ и значение не тянутся: свободную ширину забирает причина — единственная
     колонка, которой её всегда мало. */
  grid-template-columns: minmax(200px, 300px) minmax(110px, 220px) auto minmax(240px, 1fr);
  gap: var(--sp-8); align-items: baseline;
  padding: var(--sp-4) var(--sp-6); border-radius: var(--r-s);
}
.cfg-ro-row:hover { background: var(--accent-softer); }
.cfg-ro-key { font-family: var(--mono); font-size: 11px; color: var(--fg); word-break: break-all; }
.cfg-ro-val { font-family: var(--mono); font-size: 11px; color: var(--fg-dim); word-break: break-all; }
/* Причина неизменяемости — одной строкой с обрезкой: целиком она всплывает
   подсказкой. Сорок развёрнутых объяснений и были той стеной текста, из-за
   которой настройки в ней тонули. */
.cfg-ro-why {
  font-size: 11px; color: var(--fg-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Панель применения. Липнет к низу вкладки: правку набирают в верхнем разделе,
   а применяют, дочитав до конца, — кнопка не должна уезжать со скроллом. */
.cfg-apply {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; align-items: center; gap: var(--sp-12);
  padding: var(--sp-8) var(--sp-16); margin-top: auto;
  background: var(--surface); border: 1px solid var(--accent-line);
  border-radius: var(--r-xl); box-shadow: var(--shadow-pop);
}
.cfg-apply.hidden { display: none; }
.cfg-bar-count { font-size: 13px; font-weight: 600; color: var(--accent); white-space: nowrap; }
.cfg-apply .muted { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cfg-apply .inline.off { opacity: .5; }

/* Метки слоя. Цветом различаются только те, что требуют внимания: перекрытие
   флагом и правка рантайма — остальные нейтральны, иначе вкладка превращается
   в светофор, на котором ничего не видно. */
.cfg-tag {
  font-size: 11px; font-weight: 500; padding: 1px var(--sp-6); border-radius: var(--r-s);
  border: 1px solid var(--border-soft); color: var(--fg-dim); background: var(--surface-alt);
  white-space: nowrap;
}
.cfg-tag.flag, .cfg-tag.env { border-color: var(--brand-20); color: var(--info); background: var(--accent-softer); }
.cfg-tag.override { border-color: var(--brand-20); color: var(--brand-60); background: var(--accent-soft); }
.cfg-tag.runtime { border-color: var(--yellow-40); color: var(--warn); }
.cfg-tag.never { border-color: var(--border); }
:root[data-theme="dark"] .cfg-tag.override { color: var(--brand-70); }

/* ===== METRICS =====
 *
 * Сетка карточек-графиков. Линии красятся рядом серий дизайн-системы (--ser-*),
 * тем же, что у графика объёма и легенд аналитики: цвет №3 на любой вкладке
 * остаётся цветом №3. */
.tabpane#tab-metrics { overflow: auto; padding: var(--sp-16); gap: var(--sp-16); }
.met-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); gap: var(--sp-16); }
.met-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-xl); padding: var(--sp-12) var(--sp-16);
  box-shadow: var(--shadow-card); display: flex; flex-direction: column; gap: var(--sp-8);
}
.met-head { display: flex; align-items: baseline; gap: var(--sp-8); }
.met-title { font-size: 13px; font-weight: 600; color: var(--fg); }
.met-peak { margin-left: auto; font-variant-numeric: tabular-nums; }
.met-svg { width: 100%; height: 90px; }
.met-line { fill: none; stroke-width: 1.4; vector-effect: non-scaling-stroke; }
.met-area { stroke: none; opacity: .15; }
.met-line.s1 { stroke: var(--ser-1); } .met-area.s1 { fill: var(--ser-1); }
.met-line.s2 { stroke: var(--ser-2); } .met-area.s2 { fill: var(--ser-2); }
.met-line.s3 { stroke: var(--ser-3); } .met-area.s3 { fill: var(--ser-3); }
.met-line.s4 { stroke: var(--ser-4); } .met-area.s4 { fill: var(--ser-4); }
.met-line.s5 { stroke: var(--ser-5); } .met-area.s5 { fill: var(--ser-5); }
.met-line.s6 { stroke: var(--ser-6); } .met-area.s6 { fill: var(--ser-6); }
.met-line.s7 { stroke: var(--ser-7); } .met-area.s7 { fill: var(--ser-7); }
.met-line.s8 { stroke: var(--ser-8); } .met-area.s8 { fill: var(--ser-8); }
.met-legend {
  display: flex; flex-wrap: wrap; gap: var(--sp-4) var(--sp-12);
  font-size: 11px; color: var(--fg-dim); min-height: 16px;
}
.met-item { display: inline-flex; align-items: center; gap: var(--sp-4); }
.met-item .dot { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }
.met-item .dot.s1 { background: var(--ser-1); } .met-item .dot.s2 { background: var(--ser-2); }
.met-item .dot.s3 { background: var(--ser-3); } .met-item .dot.s4 { background: var(--ser-4); }
.met-item .dot.s5 { background: var(--ser-5); } .met-item .dot.s6 { background: var(--ser-6); }
.met-item .dot.s7 { background: var(--ser-7); } .met-item .dot.s8 { background: var(--ser-8); }
.met-item b { color: var(--fg); font-weight: 600; font-variant-numeric: tabular-nums; }

/* ===== HEALTH =====
 *
 * Порядок чтения задан оформлением: приговоры крупные и цветные, числа под ними
 * — обычные панели. Цвет на этой вкладке несёт единственный смысл — тяжесть
 * состояния, поэтому больше нигде здесь его нет. */
.tabpane#tab-health { overflow: auto; padding: var(--sp-16); gap: var(--sp-16); }

.checks { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: var(--sp-8); }
.check {
  display: flex; gap: var(--sp-8); align-items: flex-start;
  padding: var(--sp-8) var(--sp-12); border-radius: var(--r-l);
  border: 1px solid var(--border-soft); background: var(--surface);
}
.check-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; margin-top: 5px; background: var(--fg-faint); }
.check-title { font-size: 13px; font-weight: 600; color: var(--fg); }
.check-detail { font-size: 11px; color: var(--fg-dim); line-height: 1.45; margin-top: 2px; }
.check.ok .check-dot { background: var(--success); }
.check.warn { border-color: var(--yellow-30); background: var(--warn-soft); }
.check.warn .check-dot { background: var(--warn); }
.check.bad { border-color: var(--red-30); background: var(--error-soft); }
.check.bad .check-dot { background: var(--error); }
.check.bad .check-title { color: var(--error); }
/* Выключенный механизм — серый, а не зелёный. Галочка на неработающем гарде
   убеждает не смотреть туда, куда смотреть надо. */
.check.off { border-style: dashed; }
.check.off .check-dot { background: var(--fg-faint); }

/* Полоса «сколько из сколько». Части подписаны легендой под ней, а не внутри:
   на узкой полосе подпись не помещается ровно тогда, когда часть мала, — то
   есть когда она и интересна. */
.meter-box { display: flex; flex-direction: column; gap: var(--sp-6); margin-bottom: var(--sp-12); }
.meter {
  display: flex; height: 12px; border-radius: var(--r-s); overflow: hidden;
  background: var(--surface-sunken); border: 1px solid var(--border-soft);
}
.meter-seg { height: 100%; transition: width .3s; }
.meter-seg.s1 { background: var(--ser-1); }
.meter-seg.s2 { background: var(--ser-2); }
.meter-seg.s3 { background: var(--ser-3); }
.meter-seg.s4 { background: var(--gray-20); }
.meter-legend { display: flex; flex-wrap: wrap; gap: var(--sp-4) var(--sp-12); font-size: 11px; color: var(--fg-dim); }
.meter-item { display: inline-flex; align-items: center; gap: var(--sp-4); }
.meter-item .dot { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }
.meter-item .dot.s1 { background: var(--ser-1); }
.meter-item .dot.s2 { background: var(--ser-2); }
.meter-item .dot.s3 { background: var(--ser-3); }
.meter-item .dot.s4 { background: var(--gray-20); }
.meter-total { margin-left: auto; color: var(--fg-faint); }

/* Спарклайн. Набирается страницей, поэтому подписан только текущим значением:
   ось времени у ряда, который начался с открытием вкладки, обещала бы историю,
   которой нет. */
.spark-row { display: flex; align-items: center; gap: var(--sp-8); margin-top: var(--sp-8); }
.spark-label { font-size: 11px; color: var(--fg-dim); flex: none; min-width: 130px; }
.spark { flex: 1 1 auto; height: 24px; min-width: 60px; }
.spark-line { fill: none; stroke: var(--ser-1); stroke-width: 1.2; vector-effect: non-scaling-stroke; }
.spark-area { fill: var(--accent-soft); stroke: none; }
.spark-now { font-size: 11px; color: var(--fg); font-variant-numeric: tabular-nums; flex: none; min-width: 64px; text-align: right; }

/* Заголовок блока после спарклайна не должен липнуть к нему: ряд читается
   вместе со своей подписью, и следующий заголовок рядом с ней читается как её
   продолжение. */
.spark-row + .cache-name { margin-top: var(--sp-16); }
.cache { padding: var(--sp-8) 0; border-top: 1px solid var(--border-soft); }
.cache:first-child { border-top: none; padding-top: 0; }
.cache-name { font-size: 12px; font-weight: 600; color: var(--fg); margin-bottom: var(--sp-6); }
.cache-val { font-size: 12px; color: var(--fg-dim); font-variant-numeric: tabular-nums; }

/* ---- вкладка MCP ---- */
/* .primary — единственная утвердительная кнопка страницы: выписка секрета это
   действие, а не навигация, и от .ghost она отличается ровно этим. */
.primary {
  background: var(--accent); color: var(--fg-on-brand); border: 1px solid var(--accent);
  border-radius: var(--r-l); padding: 5px 12px; cursor: pointer;
  font-family: inherit; font-size: 12px; font-weight: 600;
  transition: background .12s, border-color .12s;
}
.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.primary:active { background: var(--accent-press); border-color: var(--accent-press); }
.primary:disabled { background: var(--surface-alt); border-color: var(--border-soft); color: var(--fg-faint); cursor: default; }

.ghost.danger:hover { background: var(--error-soft); border-color: var(--error); color: var(--error); }

.mcp-url-row { display: flex; gap: var(--sp-8); align-items: center; width: 100%; margin-bottom: var(--sp-8); }
.mcp-url-row input { flex: 1 1 auto; font-family: var(--mono); font-size: 12px; }

/* Результат живой проверки. Успех — спокойный, провал — цветом ошибки: это
   единственное место вкладки, где что-то может быть сломано. */
.mcp-check { margin: var(--sp-8) 0; }
.mcp-check.hidden { display: none; }
.mcp-ok { font-size: 12px; font-weight: 600; color: var(--success); }
.mcp-fail { font-size: 12px; font-weight: 600; color: var(--error); }
.mcp-tools-bag { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-6); }
.mcp-tool {
  font-family: var(--mono); font-size: 11px; padding: 1px var(--sp-6);
  border: 1px solid var(--border-soft); border-radius: var(--r-s);
  background: var(--surface-alt); color: var(--fg-dim); cursor: default;
}
.mcp-tool:hover { border-color: var(--brand-20); color: var(--fg); }

.mcp-clients { display: flex; flex-direction: column; gap: var(--sp-8); margin-top: var(--sp-12); align-items: flex-start; }
.mcp-snip-wrap { position: relative; width: 100%; }
.mcp-snippet {
  width: 100%; margin: 0; padding: var(--sp-12); padding-right: 110px; overflow-x: auto;
  border: 1px solid var(--border-soft); border-radius: var(--r-l);
  background: var(--surface-alt); font-family: var(--mono); font-size: 11px; line-height: 1.5;
}
/* Кнопка копирования лежит НА сниппете: сниппет и есть то, что копируют, и
   бегать глазами от текста к кнопке в другом углу незачем. */
.mcp-snip-wrap > .ghost { position: absolute; top: var(--sp-8); right: var(--sp-8); }

.mcp-issue { display: flex; gap: var(--sp-8); align-items: center; margin-bottom: var(--sp-12); }
.mcp-issue input { flex: 0 1 320px; }

/* Карточки токенов. Строка кликабельна целиком — выбор токена главное действие
   списка; точка слева — состояние «этот в строке подключения». */
.mcp-tokens { display: flex; flex-direction: column; gap: var(--sp-6); }
.tok {
  display: flex; gap: var(--sp-8); align-items: center; flex-wrap: wrap;
  padding: var(--sp-8); border: 1px solid var(--border-soft); border-radius: var(--r-l);
  background: var(--surface-alt); cursor: pointer; transition: border-color .12s;
}
.tok:hover { border-color: var(--brand-20); }
.tok.picked { border-color: var(--accent-line); background: var(--accent-softer); cursor: default; }
.tok-dot {
  flex: none; width: 8px; height: 8px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface);
}
.tok.picked .tok-dot { background: var(--accent); border-color: var(--accent); }
.tok-head { display: flex; flex-direction: column; gap: 1px; min-width: 150px; }
.tok-name { font-weight: 600; font-size: 13px; }
.tok-when { font-size: 11px; }
/* Токен — единственное, что с этой строки уносят; отсюда моноширинный шрифт и
   перенос по символам: длинная строка не должна растягивать панель. */
.tok-val {
  font-family: var(--mono); font-size: 11px; padding: 2px 6px;
  border: 1px solid var(--border); border-radius: var(--r-s); background: var(--surface);
  word-break: break-all; cursor: pointer; flex: 1 1 260px;
}
.ghost.tiny.danger:hover { background: var(--error-soft); border-color: var(--error); color: var(--error); }

/* Ручной сброс суток (вкладка Config). Красная кнопка не полагается на одно
   лишь наведение: стирание с диска — не то действие, которое маскируют под
   обычное. */
.drop-old { display: flex; gap: var(--sp-8); align-items: center; flex-wrap: wrap; }
.drop-old input[type="date"] {
  background: var(--surface); color: var(--fg); border: 1px solid var(--border);
  border-radius: var(--r-m); padding: 4px var(--sp-8); font-family: inherit; font-size: 12px;
}
.drop-old .ghost.danger { color: var(--error); border-color: var(--red-15); }
.drop-old .ghost.danger:hover { background: var(--error-soft); border-color: var(--error); }

/* Узкий экран: панель переносится, таблица теряет служебные колонки. */
@media (max-width: 1100px) {
  /* Справочник конфига теряет колонку причины: она и так обрезана до строки,
     а ключ со значением нужны целиком. */
  .cfg-ro-row { grid-template-columns: minmax(160px, 1fr) auto auto; }
  .cfg-ro-why { display: none; }
  .knobs, .checks, .met-grid { grid-template-columns: 1fr; }
  .cfg-apply { flex-wrap: wrap; }
  .cfg-apply .muted { display: none; }
  .c-source, .c-host, .c-caller { display: none; }
  .bar-row { flex-wrap: wrap; }
  .analytics-grid { grid-template-columns: 1fr; }
  .ov-grid { grid-template-columns: 1fr; }
  .novelty { grid-template-columns: 1fr; }
}
