/* Ассистент ЗВ — SPA — тёмная премиум-тема «Золотой Век» */
/* ── Палитра «Золотого Века» (облик утверждён 04.09.2026) ────────────────────
   Имена переменных остались прежними: на них держатся все разделы (400+ обращений
   в этом файле), и переименование ради красоты стоило бы дороже, чем даёт. Поэтому
   --gold теперь несёт ФИРМЕННЫЙ АКЦЕНТ, а он синий; настоящее золото логотипа и волны
   живёт отдельно в --wave-gold. Новый код берёт понятные имена (--brand, --panel,
   --ink-soft), старые остаются их псевдонимами.
   Светлая тема — основная: интерфейс смотрят днём, в машине и в шкафу у дороги.
   Ночная включается тем же переключателем «Тема» и стоит ниже. */
:root{
  color-scheme: light;
  /* фирменные */
  --brand:#0351c1; --brand-lt:#2f7ae0;
  --wave-blue:#00a0e0; --wave-gold:#f8e000; --logo-gold:#c9a400;
  /* поверхности */
  --bg:#eef4fb; --panel:#ffffff; --panel-2:#f1f9ff; --field:#f5f9fe; --field-on:#ffffff;
  /* Подложка сообщений — СВОЙ тон, а не общий --panel: пузырь лежит не на плоском фоне,
     а на живой ленте, и цвет панели, годный для окна с рамкой, читался на ней как белая
     заплата. В ночной теме разница особенно видна. */
  --bubble-ai:#ffffff; --bubble-you:#0351c1;
  --bg-2:var(--panel); --bg-3:var(--field);
  /* текст */
  --ink:#17325e; --ink-soft:#5b7396;
  --fg:var(--ink); --muted:var(--ink-soft); --muted-2:#8aa0bd; --strong:#0a2557;
  /* акцент (историческое имя — цвет фирменный) */
  --gold:var(--brand); --gold-lt:var(--brand-lt); --accent-text:var(--brand);
  /* состояния */
  --ok:#1a7f4b; --warn:#b47b00; --bad:#c1362b;
  --danger:var(--bad); --danger-text:#8f2f22;
  /* прочее */
  --line:#dbe6f4;
  --header-bg:rgba(255,255,255,.88);
  --code-bg:#f2f7fd;
  --scroll:#b7cbe4; --scroll-hover:#8fabcd;
  --title-a:#0a2557; --title-b:#17325e;
  --shadow:0 18px 44px rgba(3,26,70,.16);
  --radius:14px;
  --font-ui:"Golos Text",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --font-title:Jura,"Golos Text",system-ui,sans-serif;
}
:root[data-theme="dark"]{
  color-scheme: dark;
  --brand:#1668d8; --brand-lt:#4b93ea;
  --bg:#071628; --panel:#0d2038; --panel-2:#0a1a2e; --field:#0f2743; --field-on:#0f2743;
  --bubble-ai:#0f2743; --bubble-you:#1668d8;
  --ink:#e9f2fd; --ink-soft:#9db6d4;
  --muted-2:#6f8cb0; --strong:#ffffff;
  --accent-text:#8fc4ff;
  --ok:#4bbd84; --warn:#e0b256; --bad:#ff8b7e;
  --danger-text:#ffb3a8;
  --line:#1b3a63;
  --header-bg:rgba(7,22,40,.88);
  --code-bg:rgba(255,255,255,.06);
  --scroll:#1e416e; --scroll-hover:#2f5c93;
  --title-a:#ffffff; --title-b:#bed4f2;
  --shadow:0 18px 44px rgba(0,0,0,.44);
}
*{ box-sizing:border-box; margin:0; padding:0; }
html,body{ height:100%; }
/* Шрифты лежат файлами (site/vendor/fonts.css): сайт обязан работать без интернета.
   Golos Text — текст, Jura — заголовки и имя системы: у неё широкие прописные,
   ими набрано «АССИСТЕНТ ЗВ» в шапке и на входе. */
body{
  background:var(--bg); color:var(--fg);
  font-family:var(--font-ui); -webkit-font-smoothing:antialiased; overflow:hidden;
}
button{ font:inherit; cursor:pointer; border:none; background:none; color:inherit; }
input,textarea,select{ font:inherit; color:var(--fg); }

/* тонкие скроллбары в цвет темы (белая полоса дефолтного — вон) */
*{ scrollbar-width:thin; scrollbar-color:var(--scroll) transparent; }
*::-webkit-scrollbar{ width:8px; height:8px; }
*::-webkit-scrollbar-thumb{ background:var(--scroll); border-radius:4px; }
*::-webkit-scrollbar-thumb:hover{ background:var(--scroll-hover); }
*::-webkit-scrollbar-track{ background:transparent; }
*::-webkit-scrollbar-corner{ background:transparent; }
.view{ height:100dvh; }
[hidden]{ display:none !important; }

/* Экран входа переехал в shell.css (облик 04.09.2026): маяк, волны-SVG и золотая
   карточка входа заменены сценой с лентами из логотипа. */

/* ---------- ОБЩИЙ ЯЗЫК РАЗДЕЛОВ (облик 05.09.2026) ----------
   Разделов 22, и рисовать каждый по-своему значит получить 22 разных продукта. Здесь —
   их общие части: шапка с главным выводом, сворачиваемое пояснение, карточка события,
   строка списка с раскрытием. Раздел собирается из них, а не из собственной вёрстки.

   Правила, зашитые в размеры: цель нажатия не мельче 44 px (пальцем в 30 не попасть),
   данные идут ПЕРВЫМИ, пояснение свёрнуто. На телефоне это те же блоки, а не отдельная
   вёрстка: два макета одного раздела расходятся при первой же правке.
   ─────────────────────────────────────────────────────────────────────────────── */

/* Шапка: один главный вывод, под ним подробность, справа действие. Человек, открывший
   раздел, первым делом должен узнать, всё ли хорошо, — а не читать, как всё устроено. */
/* По центру, а не по верхнему краю: значок и кнопки, прижатые к верху, «всплывали» над
   текстом из двух-трёх строк и читались как оторванные от него (замечание 06.09.2026). */
.s-hero{ display:flex; align-items:center; gap:16px; flex-wrap:wrap;
  background:var(--panel); border:1px solid var(--line); border-radius:var(--radius);
  padding:18px 20px; margin-bottom:14px; }
.s-hero-i{ width:44px; height:44px; flex:none; border-radius:12px; display:grid;
  place-items:center; font-size:20px; background:var(--field); }
.s-hero.ok .s-hero-i{ background:rgba(26,127,75,.12); }
.s-hero.warn .s-hero-i{ background:rgba(180,123,0,.14); }
.s-hero.bad .s-hero-i{ background:rgba(193,54,43,.13); }
.s-hero-t{ flex:1; min-width:220px; display:flex; flex-direction:column; gap:5px; }
.s-hero-t b{ font:500 19px/1.25 var(--font-ui); color:var(--strong); }
.s-hero-t span{ font-size:13.5px; color:var(--muted); line-height:1.5; }
.s-hero-act{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; }

/* Числа-итоги: сколько чего. Не «плитки на весь экран» — строка с разделителями. */
.s-stats{ display:flex; gap:18px; flex-wrap:wrap; margin-top:2px; }
.s-stat{ display:flex; align-items:baseline; gap:6px; font-size:13px; color:var(--muted); }
.s-stat b{ font-size:17px; color:var(--ink); font-variant-numeric:tabular-nums; }
.s-stat.ok b{ color:var(--ok); } .s-stat.warn b{ color:var(--warn); }
.s-stat.bad b{ color:var(--bad); }

/* Пояснение: свёрнуто. Оно нужно один раз в жизни, а место занимает каждый день —
   на телефоне это был экран текста до первой строки данных. */
details.s-note{ background:var(--panel); border:1px solid var(--line);
  border-radius:var(--radius); margin-bottom:14px; }
details.s-note > summary{ list-style:none; cursor:pointer; padding:13px 18px;
  font-size:13.5px; color:var(--muted); display:flex; align-items:center; gap:8px;
  min-height:44px; }
details.s-note > summary::-webkit-details-marker{ display:none; }
details.s-note > summary::before{ content:"▸"; color:var(--muted-2); font-size:12px;
  transition:transform .15s; }
details.s-note[open] > summary::before{ transform:rotate(90deg); }
details.s-note > summary:hover{ color:var(--ink); }
.s-note-body{ padding:0 18px 16px; font-size:13.5px; line-height:1.6; color:var(--muted);
  display:flex; flex-direction:column; gap:10px; max-width:88ch; }
.s-note-body b{ color:var(--ink); }

/* Карточка события: то, что требует внимания. Полоса слева — тяжесть, как в письме
   и в ленте уведомлений: один и тот же смысл во всей системе выглядит одинаково. */
.s-item{ position:relative; background:var(--panel); border:1px solid var(--line);
  border-radius:12px; padding:14px 16px 14px 19px; overflow:hidden;
  display:flex; flex-direction:column; gap:8px; }
.s-item::before{ content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--muted-2); }
.s-item.bad::before{ background:var(--bad); }
.s-item.warn::before{ background:var(--warn); }
.s-item.ok::before{ background:var(--ok); }
.s-item-head{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.s-item-head b{ font-size:15px; color:var(--strong); }
.s-item-txt{ font-size:14px; line-height:1.55; color:var(--ink); }
.s-item-sub{ font-size:12.5px; line-height:1.5; color:var(--muted); }
.s-list{ display:flex; flex-direction:column; gap:9px; margin-bottom:14px; }

/* Метка состояния. Слово, а не только значок: значок без подписи каждый читает по-своему. */
.s-pill{ display:inline-flex; align-items:center; gap:5px; flex:none;
  font:500 11.5px/1 var(--font-ui); text-transform:uppercase; letter-spacing:.04em;
  border-radius:6px; padding:5px 8px; background:var(--field); color:var(--muted); }
.s-pill.ok{ background:rgba(26,127,75,.12); color:var(--ok); }
.s-pill.warn{ background:rgba(180,123,0,.16); color:var(--warn); }
.s-pill.bad{ background:rgba(193,54,43,.13); color:var(--bad); }
.s-pill.soft{ text-transform:none; letter-spacing:0; font-weight:400; }

/* Строка списка с раскрытием — вместо таблицы на четыре колонки. Таблица на телефоне
   либо режется, либо переносится по буквам; строка раскрывается везде одинаково. */
.s-rows{ background:var(--panel); border:1px solid var(--line); border-radius:var(--radius);
  overflow:hidden; }
.s-row{ width:100%; display:flex; align-items:center; gap:12px; min-height:52px;
  padding:11px 16px; background:none; border:0; border-top:1px solid var(--line);
  text-align:left; cursor:pointer; transition:background .13s; }
.s-row:first-child{ border-top:0; }
.s-row:hover{ background:var(--field); }
.s-row-n{ flex:1; min-width:0; font-size:14px; color:var(--ink); }
.s-row-d{ flex:2; min-width:0; font-size:13px; color:var(--muted);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.s-row-x{ padding:0 16px 14px 16px; font-size:13.5px; line-height:1.6; color:var(--muted);
  border-top:0; display:flex; flex-direction:column; gap:8px; }
.s-rows-foot{ padding:12px 16px; font-size:12.5px; color:var(--muted-2);
  border-top:1px solid var(--line); }
/* Вторая строка в имени: вид оборудования, состояние, дата — то, без чего строку не
   опознать. Отдельной колонкой её заводить нельзя: на телефоне колонок нет вовсе. */
.s-row-n{ display:flex; flex-direction:column; gap:2px; }
.s-row-sub{ font-size:12px; color:var(--muted-2); }
/* Действия внутри раскрытой строки. Цель нажатия 44 px здесь тоже обязательна: раскрыв
   строку пальцем, человек тут же жмёт кнопку в ней. */
.s-acts{ display:flex; gap:8px; flex-wrap:wrap; margin-top:4px; }
/* Переключатель-фильтр над списком: он выбирает, ЧТО показано, и стоит вплотную к списку,
   а не в шапке — иначе связь «выбрал → изменилось» не читается. */
.s-seg{ margin-bottom:10px; max-width:560px; }
/* Выборка, в которой ничего нет, не должна выглядеть кнопкой: нажатие на неё показало бы
   пустой список — то есть обещало работу там, где её нет. */
.s-seg button[disabled]{ opacity:.45; cursor:default; }
@media (max-width:720px){
  /* Кнопки переключателя ОБЯЗАНЫ остаться в один ряд: перенос выбрасывает последнюю вниз,
     а ползунок (он двигается только по горизонтали) остаётся на прежней строке и
     закрашивает соседа. Сетка с равными колонками и запретом переноса держит ряд, а текст
     ужимается шрифтом. */
  .s-seg{ max-width:none; display:grid; grid-auto-flow:column; grid-auto-columns:1fr; }
  .s-seg button{ font-size:12px; padding:0 2px; min-height:44px; white-space:nowrap;
    overflow:hidden; text-overflow:ellipsis; }
  /* Галочка сама остаётся мелкой (её рисует система), поэтому целью нажатия служит вся
     подпись: label с высотой 44 px ловит палец там, где человек и целится — в слово. */
  .sw-row label{ display:flex; align-items:center; gap:10px; min-height:44px; }
  .sw-row input[type="checkbox"]{ width:20px; height:20px; }
}

/* ---------- ДВИЖЕНИЕ ----------
   Одно правило на весь сайт: движение объясняет, ОТКУДА взялось то, что появилось, и
   длится меньше четверти секунды. Всё, что двигается, двигается через opacity и transform —
   их браузер считает на видеокарте, не пересчитывая раскладку; ни сервер, ни слабая машина
   этого не замечают. Высоту и ширину не анимируем нигде: именно они заставляют браузер
   пересчитывать страницу на каждом кадре и дают рывки на длинных списках.
   Человеку, который просил систему не двигаться (`prefers-reduced-motion`), не двигаем
   ничего — это не украшение, а его настройка доступности. */
.z-reveal{ animation:z-revealIn .19s cubic-bezier(.2,.8,.3,1) both; }
@keyframes z-revealIn{ from{ opacity:0; transform:translateY(-4px); } to{ opacity:1; transform:none; } }
@media (prefers-reduced-motion:reduce){
  .z-reveal, .s-row-x, .udk-card{ animation:none !important; }
}
/* Раскрытая карточка объекта и раскрытая строка проверки появляются одинаково: это одно
   и то же действие человека — «покажи подробности». */
.udk-card, .s-row-x:not([hidden]){ animation:z-revealIn .19s cubic-bezier(.2,.8,.3,1) both; }

/* Свой выпадающий список (см. dressSelect в app-nav.js). Нативный `<select>` не поддаётся
   оформлению: браузер рисует его список средствами системы, и в ночной теме он выпадал
   белым окном поверх тёмной страницы. Настоящий select остаётся внутри — он и хранит
   значение, поэтому разделы про подмену не знают. */
.s-sel{ position:relative; display:inline-flex; min-width:0; }
.s-sel select{ position:absolute; opacity:0; pointer-events:none; width:0; height:0; }
.s-sel-b{ display:flex; align-items:center; justify-content:space-between; gap:10px;
  width:100%; min-height:44px; padding:0 12px; font:400 14px/1.2 var(--font-ui);
  color:var(--ink); background:var(--field); border:1px solid var(--line);
  border-radius:11px; cursor:pointer; text-align:left;
  transition:border-color .15s,background .15s; }
.s-sel-b span{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.s-sel-b:hover{ border-color:var(--brand-lt); }
.s-sel.open .s-sel-b{ border-color:var(--brand); background:var(--panel); }
.s-sel-b[disabled]{ opacity:.55; cursor:default; }
.s-sel-a{ width:15px; height:15px; flex:none; color:var(--muted-2);
  transition:transform .18s; }
.s-sel.open .s-sel-a{ transform:rotate(180deg); }
.s-sel-pop{ position:absolute; top:calc(100% + 5px); left:0; min-width:100%; z-index:30;
  max-height:min(50vh,320px); overflow:auto; padding:6px;
  background:var(--panel); border:1px solid var(--line); border-radius:12px;
  box-shadow:var(--shadow); display:flex; flex-direction:column; gap:1px;
  animation:s-selIn .15s cubic-bezier(.2,.8,.3,1); }
.s-sel-pop.up{ top:auto; bottom:calc(100% + 5px); }
@keyframes s-selIn{ from{ opacity:0; transform:translateY(-6px); } to{ opacity:1; transform:none; } }
.s-sel-o{ display:block; width:100%; text-align:left; padding:10px 11px; border-radius:8px;
  font-size:13.5px; color:var(--ink); background:none; border:0; cursor:pointer;
  white-space:nowrap; transition:background .12s; }
.s-sel-o:hover{ background:var(--field); }
.s-sel-o.on{ background:var(--field); font-weight:500; color:var(--brand); }

/* 🔴 Пояснение под настройкой или списком. Класс потерял определение при переносе облика
   04.09 (как и `.btn-small`): пояснения рисовались обычным текстом — крупнее и темнее, чем
   сами показания, и раздел читался как статья, в которой где-то спрятаны цифры (замечание
   Михаила 06.09.2026). Пояснение обязано быть МЕЛЬЧЕ и тише того, что оно поясняет. */
.side-hint{ margin:8px 0 10px; font-size:13px; line-height:1.55; color:var(--muted);
  max-width:92ch; }
.side-hint b{ color:var(--ink); font-weight:600; }
/* Воздух между смысловыми блоками раздела. Пояснение, приросшее к следующему блоку,
   читается его началом, а кнопка, приросшая к тексту, — строчкой этого текста. */
.s-rows + details.s-note, .s-rows + .s-h, .s-list + .s-h,
details.s-note + .card, .card + .card, .s-rows + .card{ margin-top:16px; }
.side-hint + .card, .side-hint + .s-rows, .side-hint + .stat-grid,
.side-hint + .s-acts, .side-hint + .src{ margin-top:14px; }
.s-acts + .side-hint{ margin-top:12px; }
.card .s-rows + .side-hint{ margin-top:12px; }
.side-hint code{ font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:12.5px; }

/* Заголовок группы внутри раздела: не карточка, а подпись к списку под ним. */
.s-h{ font:500 13px/1 var(--font-ui); text-transform:uppercase; letter-spacing:.06em;
  color:var(--muted-2); margin:18px 0 9px; }

/* Кнопка действия в разделе: цель нажатия 44 px, а не 28. */
.s-btn{ display:inline-flex; align-items:center; justify-content:center; gap:7px;
  min-height:44px; padding:0 18px; border-radius:11px; font:500 14px/1 var(--font-ui);
  color:#fff; background:var(--brand); border:0; cursor:pointer;
  transition:filter .15s,transform .1s; }
.s-btn:hover{ filter:brightness(1.07); }
.s-btn:active{ transform:translateY(1px); }
.s-btn[disabled]{ opacity:.55; cursor:default; }
.s-btn.ghost{ background:none; color:var(--ink); border:1px solid var(--line); }
.s-btn.ghost:hover{ background:var(--field); filter:none; }
/* Опасное действие названо цветом, но не кричит: оно стоит рядом с обычными и нажимается
   реже них. Красная заливка на кнопке «Удалить» в каждой строке превратила бы список
   в предупреждение. */
.s-btn.ghost.danger{ color:var(--bad); border-color:rgba(193,54,43,.35); }
.s-btn.ghost.danger:hover{ background:rgba(193,54,43,.08); }

/* Значение, которое вводят как есть (адрес, порт, пароль): моноширинно — так видно, где
   единица, а где буква «l», и не читается ли «0» как «О». */
.mono{ font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:13px; }

/* Поиск по списку. Стоит НАД списком и вплотную к нему: он меняет то, что под ним. */
.s-find{ display:flex; align-items:center; gap:10px; margin-bottom:10px; flex-wrap:wrap; }
.s-find .s-sel{ min-width:210px; }
@media (max-width:720px){
  .s-find .s-sel, .s-find input{ flex:1 1 100%; min-width:0; }
  /* На телефоне свой список НЕ подставляется (родное колесо удобнее), значит целью
     нажатия остаётся сам `select` — и его высоту задаём здесь. */
  #sec-body select{ min-height:44px; width:100%; }
  /* Переключатель и крестик тега — цели пальца: сам ползунок мелкий, растим область
     нажатия отступами, не меняя его вид. */
  /* Ползунок остаётся прежнего размера (22 px), а цель нажатия вырастает до 44:
     растягиваем сам label, а дорожку держим по центру отступами. */
  .us-kv .switch, .tune-field .switch{ height:44px; }
  .us-kv .switch span, .tune-field .switch span{ inset:11px 0; }
  .us-tag{ min-height:44px; }
  .us-tag button{ min-height:44px; padding:0 6px; }
  /* Разделы, ещё не переписанные на общий язык (Сервер, Ассистент, Сбор данных), держат
     прежние мелкие кнопки. Размер даём здесь одним правилом: переписывать их разметку
     ради высоты значило бы трогать логику, которая работает. */
  #sec-body .btn-tiny, #sec-body .btn-primary{ min-height:44px; padding:0 14px; }
  #sec-body .sw-row label, #sec-body label.sw-row{ display:flex; align-items:center;
    gap:10px; min-height:44px; }
  #sec-body input[type="checkbox"]{ width:20px; height:20px; }
  #sec-body .s-h .link-like{ min-height:44px; display:inline-flex; align-items:center; }
  #sec-body .src-rows a{ min-height:44px; display:inline-flex; align-items:center; }
}
.s-find input{ flex:0 1 420px; min-width:0; min-height:44px; padding:0 14px;
  font-size:14px; color:var(--ink); background:var(--panel);
  border:1px solid var(--line); border-radius:11px; outline:none;
  transition:border-color .15s; }
.s-find input:focus{ border-color:var(--brand); }
.s-find-n{ font-size:12.5px; color:var(--muted-2); white-space:nowrap; }

@media (max-width:760px){
  /* На телефоне строка раскрытия отдаёт подробность вниз: в одну строку она не влезает,
     а обрезанная многоточием бесполезна. */
  .s-row{ flex-direction:column; align-items:stretch; gap:4px; }
  .s-row-d{ white-space:normal; overflow:visible; }
  .s-hero{ padding:16px; }
  .s-hero-act{ width:100%; }
  .s-hero-act .s-btn{ width:100%; }
}

/* ---------- КАРТОЧКА ЧЕЛОВЕКА (раздел «Пользователи») ----------
   Всё, что о человеке можно изменить, живёт внутри его строки. Сетка «подпись — значение»
   в один-два столбца: на телефоне столбец один, и порядок остаётся тем же. */
/* Строка человека: одинаковые колонки у всех. Ширину задаёт СЕТКА, а не длина текста —
   иначе области у каждого встают на своё место и колонка читается лесенкой. */
/* Ширины колонок с пометками и ролью ЗАДАНЫ В ПИКСЕЛЯХ, а не долями и не auto: у каждой
   строки своя сетка, и колонка auto («АДМИНИСТРАТОР» против «Рабочий») сдвигала все
   остальные — у каждого человека на свою величину. */
.us-row{ display:grid; align-items:center;
  grid-template-columns:minmax(0,1.6fr) 176px 150px minmax(0,1.4fr) 132px;
  gap:14px; }
.us-row > .s-pill{ justify-self:end; }
.us-col{ display:flex; gap:6px; flex-wrap:wrap; min-width:0; font-size:12.5px;
  color:var(--muted); }
/* Пометки внутри колонки — ПОДСЕТКА с постоянной шириной ячеек. Пока они шли подряд,
   начало второй зависело от длины первой («почта» / «почты нет»), и колонка читалась
   лесенкой. Пустая ячейка означает «этого доступа нет». */
.us-links, .us-acc{ display:grid; gap:6px; justify-items:start; align-items:center; }
.us-links{ grid-template-columns:74px auto; }
.us-acc{ grid-template-columns:50px auto; }
.us-mark.wide{ grid-column:1 / -1; }
.us-reg{ display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.us-reg i{ font-style:normal; color:var(--muted-2); }
/* Пометка «есть / ждёт / нет»: слово, а не значок — значок без подписи каждый читает
   по-своему. Приглушённая, чтобы не спорить с именем человека слева. */
.us-mark{ display:inline-flex; align-items:center; border-radius:6px; padding:3px 7px;
  background:var(--field); color:var(--muted-2); white-space:nowrap; }
.us-mark.on{ background:rgba(26,127,75,.11); color:var(--ok); }
.us-mark.wait{ background:rgba(180,123,0,.14); color:var(--warn); }
.us-mark.off{ background:var(--field); color:var(--muted-2); }
@media (max-width:900px){
  .us-row{ grid-template-columns:1fr auto; }
  .us-row .us-col{ grid-column:1 / -1; }
}
/* Карточка человека — четыре смысловые группы, а не россыпь полей: доступ, связь, права,
   отчёты. Пока поля лежали в одну сетку, между ними оставались пустые места, и было
   непонятно, что к чему относится. */
.us-card{ display:grid; grid-template-columns:repeat(auto-fit,minmax(250px,1fr));
  gap:18px 26px; margin:14px 0 12px; }
.us-card section{ display:flex; flex-direction:column; gap:10px; min-width:0; }
/* line-height у заголовка группы не может быть 1: прописные с таким интервалом упираются
   в край строки, и верх букв срезается ближайшей границей (замечание Михаила 07.09.2026). */
.us-card h5{ margin:0; font:600 11.5px/1.4 var(--font-ui); text-transform:uppercase;
  letter-spacing:.06em; color:var(--brand); }
.us-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:10px 22px; margin:2px 0 10px; }
.us-kv{ display:flex; flex-direction:column; gap:5px; min-width:0; }
.us-kv > span{ font-size:12px; color:var(--muted-2); text-transform:uppercase;
  letter-spacing:.04em; }
.us-kv > div{ display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  font-size:14px; color:var(--ink); }
.us-note{ font-size:12.5px; color:var(--muted-2); }
/* Строка «подпись — число» в списке общего языка: не кнопка, читать её нажатием нечего. */
.s-line{ display:flex; align-items:center; justify-content:space-between; gap:12px;
  min-height:48px; padding:11px 16px; border-top:1px solid var(--line);
  font-size:14px; color:var(--ink); }
.s-line:first-child{ border-top:0; }
.s-line b{ font-variant-numeric:tabular-nums; }
/* Строка инструмента: название с пояснением слева, переключатель справа. */
.asst-tool{ align-items:flex-start; gap:16px; }
.asst-tool .s-row-n{ flex:1; }
.asst-tool .sw-row{ flex:none; display:flex; align-items:center; gap:10px;
  font-size:13px; color:var(--muted); cursor:pointer; }
/* Переключатель инструмента — тот же ползунок, что во всей панели, но нарисованный
   рядом с настоящим чекбоксом: голая галочка выбивалась из облика (замечание Михаила
   06.09.2026). Сам чекбокс остаётся в разметке — на нём держится обработчик. */
/* Настоящий чекбокс спрятан ВСЕГДА и с запасом по специфичности: мобильные правила
   раздела растят галочки до 20 px, и рядом с ползунком проступала вторая — настоящая. */
.asst-sw input, #sec-body .asst-sw input[type="checkbox"]{
  position:absolute; opacity:0; width:0; height:0; margin:0; }
.switch-ui{ position:relative; display:inline-block; width:40px; height:22px; flex:none;
  border-radius:999px; background:var(--field); border:1px solid var(--line);
  transition:background .15s,border-color .15s; }
.switch-ui i{ position:absolute; top:2px; left:2px; width:16px; height:16px;
  border-radius:50%; background:var(--muted-2);
  transition:transform .15s, background .15s; }
.asst-sw input:checked + .switch-ui{ background:rgba(3,81,193,.18); border-color:var(--brand-lt); }
.asst-sw input:checked + .switch-ui i{ transform:translateX(18px); background:var(--brand); }
.asst-sw-t{ min-width:74px; }
.s-num{ flex:none; font-variant-numeric:tabular-nums; font-weight:600; color:var(--ink);
  text-align:right; min-width:56px; }
/* Заголовок группы со ссылками сортировки: сами ссылки мелкие и стоят справа от названия. */
.s-h .link-like{ font:400 12.5px/1 var(--font-ui); margin-left:12px; text-transform:none;
  letter-spacing:0; }
.us-inline{ display:flex; gap:8px; flex-wrap:wrap; width:100%; }
.us-inline input{ flex:1 1 220px; min-width:0; min-height:44px; padding:0 12px;
  font-size:14px; color:var(--ink); background:var(--field);
  border:1px solid var(--line); border-radius:11px; outline:none; }
.us-inline input:focus{ border-color:var(--brand); }
.us-tags{ display:flex; gap:7px; flex-wrap:wrap; align-items:center; }
.us-tag{ display:inline-flex; align-items:center; gap:6px; font-size:13px;
  color:var(--ink); background:var(--field); border:1px solid var(--line);
  border-radius:9px; padding:7px 9px; }
.us-tag button{ background:none; border:0; color:var(--muted-2); cursor:pointer;
  font-size:12px; line-height:1; padding:2px; }
.us-tag button:hover{ color:var(--bad); }
.us-addrow{ display:inline-flex; gap:7px; align-items:center; flex-wrap:wrap; }
.us-addrow input{ min-height:44px; padding:0 12px; font-size:14px; color:var(--ink);
  background:var(--field); border:1px solid var(--line); border-radius:11px; outline:none; }
.us-addrow input:focus{ border-color:var(--brand); }
/* «вы» рядом со своим именем — пометка, а не украшение: свою роль изменить нельзя,
   и человек должен видеть, почему у него нет части кнопок. */
.you{ font-size:11.5px; color:var(--brand); background:rgba(3,81,193,.09);
  border-radius:5px; padding:3px 6px; }

/* ---------- ЛЕНТА УВЕДОМЛЕНИЙ ----------
   Не переписка: карточки идут сверху вниз по времени, отвечать в них некому. Слева —
   полоска тяжести того же смысла, что в шапке письма; непрочитанное помечено, но не
   кричит: человек и так пришёл сюда читать. */
.sec-empty{ color:var(--muted); font-size:14px; line-height:1.6; padding:18px 2px;
  max-width:620px; }
/* Строка над лентой: сколько событий и кнопка «удалить всё». Кнопка справа и не первой
   по взгляду — читать ленту приходят чаще, чем стирать. */
.nt-bar{ display:flex; align-items:center; justify-content:flex-end; gap:12px;
  flex-wrap:wrap; margin-bottom:12px; }
.nt-note{ font-size:12.5px; color:var(--muted-2); margin-right:auto; }
/* Выборка стоит НАД строкой со счётчиком: сначала человек решает, что смотрит, и только
   потом читает, сколько этого набралось. На узком экране списки разворачиваются в столбик
   во всю ширину — выбирать вид пальцем в поле шириной 120 px невозможно. */
.nt-picks{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:10px; }
.nt-picks .s-sel{ min-width:190px; }
/* Свой период — две даты рядом с выбором периода, а не отдельной строкой ниже: они
   принадлежат тому же выбору и появляются только когда он сделан. */
.nt-range{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.nt-date{ display:flex; align-items:center; gap:6px; font-size:13px; color:var(--muted); }
.nt-date input{ min-height:44px; padding:0 10px; font-size:13.5px; color:var(--ink);
  background:var(--field); border:1px solid var(--line); border-radius:11px; outline:none;
  transition:border-color .15s; }
.nt-date input:focus{ border-color:var(--brand); }
@media (max-width:720px){
  .nt-picks{ flex-direction:column; align-items:stretch; }
  .nt-picks .s-sel{ min-width:0; width:100%; }
  .nt-range{ width:100%; }
  .nt-date{ flex:1 1 46%; }
  .nt-date input{ width:100%; min-width:0; }
  /* Цель нажатия на телефоне не мельче 44 px — иначе в неё не попасть пальцем. */
  .nt-picks button, .nt-bar button{ min-height:44px; }
}
.nt-list{ display:flex; flex-direction:column; gap:9px; }
.nt-day{ font-size:12px; color:var(--muted-2); text-transform:uppercase;
  letter-spacing:.06em; margin:16px 0 3px; }
.nt-day:first-child{ margin-top:2px; }
.nt-item{ position:relative; background:var(--panel); border:1px solid var(--line);
  border-radius:12px; padding:12px 14px 12px 17px; overflow:hidden; }
.nt-item::before{ content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--muted-2); }
.nt-bad::before{ background:var(--bad); }
.nt-warn::before{ background:var(--warn); }
.nt-ok::before{ background:var(--ok); }
/* Непрочитанное — точкой у края, а не заливкой всей карточки: заливка на десяти строках
   подряд превращается в фон, и «новое» перестаёт значить новое. */
.nt-new::after{ content:""; position:absolute; right:12px; top:14px; width:7px; height:7px;
  border-radius:50%; background:var(--brand); }
.nt-head{ display:flex; align-items:center; gap:9px; flex-wrap:wrap; margin-bottom:6px; }
.nt-kind{ font:600 11px/1 var(--font-ui); text-transform:uppercase; letter-spacing:.05em;
  color:var(--muted); background:var(--field); border-radius:5px; padding:4px 7px; }
.nt-time{ font-size:12px; color:var(--muted-2); }
.nt-title{ font-size:13.5px; color:var(--strong); font-weight:600; }
.nt-body{ font-size:14.5px; line-height:1.6; color:var(--ink); white-space:pre-wrap;
  overflow-wrap:break-word; padding-right:14px; }
.nt-body a{ color:var(--brand); text-decoration:none; border-bottom:1px solid var(--line); }
.nt-body a:hover{ border-bottom-color:var(--brand); }

/* ---------- ПЕРЕПИСКА ----------
   Рамку рабочего места держит shell.css; здесь — то, что внутри ленты сообщений.
   Ответ лежит на своей подложке, а не прямо на фоне: за ним идёт живая лента, и текст
   на ней читался бы хуже, чем должен. Подложка мягкая — светлее фона и без рамки,
   чтобы не возвращать переписке вид стопки карточек. */
.messages{
  flex:1 1 auto; min-height:0; overflow-y:auto;
  padding:clamp(14px,2.2vw,24px) clamp(12px,2.2vw,24px) 8px;
  display:flex; flex-direction:column; gap:12px; scroll-behavior:smooth;
}
.msg{ max-width:min(760px,88%); display:flex; flex-direction:column; gap:4px;
  animation:msg-in .25s ease both; }
@keyframes msg-in{ from{opacity:0; transform:translateY(6px);} to{opacity:1; transform:none;} }
.msg-user{ align-self:flex-end; align-items:flex-end; max-width:min(560px,80%); }
.msg-bot{ align-self:flex-start; }
.bubble{ font-size:15px; line-height:1.6; white-space:pre-wrap; overflow-wrap:break-word; }
.msg-bot .bubble{
  background:var(--bubble-ai); border-radius:16px; border-bottom-left-radius:6px;
  padding:13px 16px; color:var(--ink); box-shadow:0 1px 2px rgba(3,26,70,.06);
}
.msg-user .bubble{
  background:var(--bubble-you); color:#fff; border-radius:16px; border-bottom-right-radius:6px;
  padding:11px 15px; box-shadow:0 1px 2px rgba(3,26,70,.08);
}
.msg-error .bubble{ color:var(--danger-text); box-shadow:inset 0 0 0 1px var(--danger); }
.msg-bot .bubble a{ color:var(--brand); text-decoration:none;
  border-bottom:1px solid var(--line); }
.msg-bot .bubble a:hover{ border-bottom-color:var(--brand); }
.msg-user .bubble a{ color:#fff; border-bottom:1px solid rgba(255,255,255,.5); }
.bubble b, .bubble strong{ font-weight:600; }
.msg-bot .bubble b, .msg-bot .bubble strong{ color:var(--strong); }
.bubble code, .bubble pre{ font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:13.5px; background:var(--code-bg); border-radius:6px; padding:1px 5px; }
.msg-label{ font-size:11.5px; color:var(--ink-soft); padding:0 4px; }
.msg-user .msg-label{ text-align:right; }
/* Время под сообщением: когда это было сказано. В ленте, где перемешаны сегодняшнее и
   вчерашнее, без него непонятно, свежий это ответ или прошлогодний. */
.msg-time{ font-size:11.5px; color:var(--ink-soft); padding:0 4px; }
.msg-user .msg-time{ text-align:right; }

/* Сохранённая переписка приглушена, чтобы новое было видно сразу. */
.msg-old{ animation:none; opacity:.86; }
.msg-old:hover{ opacity:1; }
.chat-divider{
  align-self:center; display:flex; align-items:center; gap:10px;
  font-size:12px; color:var(--ink-soft); background:var(--panel);
  border:1px solid var(--line); border-radius:99px; padding:5px 12px; margin:6px 0;
}

/* спиннер ожидания */
.thinking .bubble{ display:flex; align-items:center; gap:10px; color:var(--ink-soft); }
.think-dots{ display:inline-flex; gap:4px; }
.think-dots i{ width:6px; height:6px; border-radius:50%; background:var(--ink-soft);
  animation:blink 1.2s ease-in-out infinite; }
.think-dots i:nth-child(2){ animation-delay:.2s; }
.think-dots i:nth-child(3){ animation-delay:.4s; }
@keyframes blink{ 0%,80%,100%{opacity:.25;} 40%{opacity:1;} }

/* Композер не стоит на белой полосе: она отрезала треть экрана и спорила с фоном.
   Поле лежит прямо на общем фоне. */
.composer{
  flex:0 0 auto; position:relative; background:none; border:0;
  padding:2px clamp(12px,2.2vw,24px) calc(10px + env(safe-area-inset-bottom));
  display:flex; flex-direction:column; gap:9px;
}
/* 🔴 Затухания над композером НЕТ, и это не упущение. Оно задумывалось, чтобы текст не
   въезжал под поле ввода на прокрутке, — но композер стоит В ПОТОКЕ под лентой сообщений
   и ничего собой не накрывает: закрывать было нечего. Зато его 28 точек глухого --bg
   ложились поверх живого фона и давали ровную горизонтальную кромку над кнопками —
   ту самую «полосу» (замечание Михаила 05.09.2026). Общее правило: слой, закрашивающий
   фон «на всякий случай», на ЖИВОМ фоне становится видимым швом. */

/* примеры вопросов в разделе «Инфо»: группы + кнопки в ряд, клик отправляет вопрос */
.ex-group{ margin-bottom:14px; }
.ex-title{ font-size:13px; color:var(--muted-2); margin-bottom:7px; }
.ex-row{ display:flex; flex-wrap:wrap; gap:6px; white-space:normal; }
.ex-row .chip{ text-align:left; font-size:13px; padding:7px 13px; cursor:pointer; }

/* Кнопки-фишки разделов (фильтры, примеры, переключатели внутри таблиц). */
.chip{
  padding:8px 14px; border-radius:999px; font-size:13.5px; color:var(--muted);
  background:var(--bg-2); border:1px solid var(--line);
  transition:color .2s, border-color .2s;
}
.chip:hover{ color:var(--fg); border-color:var(--brand); }
.chip:disabled{ opacity:.45; cursor:default; }

/* Мелочи, оставшиеся от прежней боковой панели: подзаголовок внутри карточки раздела
   и крестик у фишки подписки. Сами панель и профиль переехали в окно (shell.css). */
.side-title{ font-size:11.5px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--muted-2); margin-bottom:8px; }
.sub-tag{ display:inline-flex; align-items:center; gap:7px; font-size:13px; color:var(--fg);
  background:var(--bg-3); border:1px solid var(--line); border-radius:99px; padding:6px 10px; }
.sub-x{ background:none; border:0; color:var(--muted-2); cursor:pointer; font-size:14px;
  line-height:1; padding:0; }
.sub-x:hover{ color:var(--danger); }
.sub-x:disabled{ opacity:.4; cursor:default; }

/* ---------- РАЗДЕЛ ---------- */
/* Ширину колонки задаёт рамка (.z-center в shell.css): разделы-таблицы просят у неё
   больше места через .z-wide, а раздел сам о своей ширине не знает. */
.section-area{
  flex:1 1 auto; min-height:0; overflow-y:auto; width:100%;
  padding:18px clamp(12px,2.2vw,24px) calc(28px + env(safe-area-inset-bottom));
}
.section-head{ display:flex; align-items:center; gap:12px; margin-bottom:18px; flex-wrap:wrap; }
.section-head h2{ font-size:21px; font-weight:600; letter-spacing:-.01em; margin-right:auto;
  font-family:var(--font-title); }
.sec-loading, .sec-error, .sec-warn{
  padding:16px; border-radius:var(--radius); background:var(--bg-2);
  border:1px solid var(--line); color:var(--muted); font-size:14.5px;
}
.sec-error{ color:var(--danger-text); border-color:rgba(224,106,90,.4); }
.sec-warn{ color:var(--accent-text); border-color:rgba(227,178,58,.35); margin-bottom:16px; }

.card{
  background:var(--bg-2); border:1px solid var(--line); border-radius:var(--radius);
  padding:16px 18px; margin-bottom:16px;
}
.card-title{ font-size:15px; font-weight:600; margin-bottom:12px; }
.about p{ color:var(--muted); line-height:1.6; margin-bottom:10px; font-size:14.5px; }
ul.plain{ list-style:none; }
ul.plain li{
  display:flex; justify-content:space-between; gap:14px; align-items:baseline;
  padding:8px 0; border-bottom:1px solid var(--line); font-size:14.5px;
}
ul.plain li:last-child{ border-bottom:none; }
ul.plain li b{ color:var(--strong); font-variant-numeric:tabular-nums; white-space:nowrap; }
.dim{ color:var(--muted-2); }

/* плитки статистики */
.stat-grid{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(165px,1fr));
  gap:12px; margin-bottom:16px;
}
.stat{
  background:var(--bg-2); border:1px solid var(--line); border-radius:var(--radius);
  padding:16px 18px;
}
.stat-n{ font-size:28px; font-weight:650; letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
.stat-l{ font-size:12.5px; color:var(--muted); margin-top:4px; line-height:1.35; }
.stat.good .stat-n{ color:var(--ok); }
.stat.bad .stat-n{ color:var(--danger); }
.bar{ margin-top:10px; height:5px; border-radius:3px; background:var(--bg-3); overflow:hidden; }
.bar i{ display:block; height:100%; background:linear-gradient(90deg,var(--gold),var(--gold-lt)); }
.bar i.warn{ background:linear-gradient(90deg,#d89a3a,#e8b45c); }
.bar i.bad{ background:linear-gradient(90deg,#c0523f,#e07a63); }
/* Плашка настройки внутри карточки: то, чем управляют, выделено фоном и отделено от
   пояснения под ним. Пока настройка стояла строкой в потоке текста, её не было видно. */
.warm-box{ background:var(--field); border:1px solid var(--line); border-radius:12px;
  padding:12px 14px; margin:14px 0 10px; }
/* Переключатель — тот же ползунок, что во всей панели: голая галочка выбивалась из
   облика (замечание Михаила 07.09.2026). Состояние стоит рядом с ним, кнопка уходит
   вправо — она относится к той же настройке, а не к пояснению под плашкой. */
.warm-row{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
/* Обнуляем ВСЕ поля, а не только нижнее: у общего .sw-row есть margin-top:16px, и в
   строке, выровненной по центру, он опускал переключатель с подписью на 8 px ниже
   пилюли и кнопки (замечание Михаила 07.09.2026). `display:flex` тут обязателен: у общего
   .sw-row задано только align-items, а без flex оно не действует — ползунок и подпись
   вставали по базовой линии текста, каждый на своём уровне. */
.warm-row .sw-row{ display:flex; align-items:center; margin:0; gap:10px; cursor:pointer; }
.warm-name{ font-size:14px; color:var(--ink); }
.warm-row .s-btn{ margin-left:auto; }
@media (max-width:640px){ .warm-row .s-btn{ margin-left:0; width:100%; } }

/* Полоса, где «больше — лучше» (заряд батареи): зелёная при высоком значении. */
.bar i.ok{ background:linear-gradient(90deg,#1a7f4b,#37a76a); }
.period-seg{ margin-bottom:16px; max-width:460px; }

/* таблица пользователей */
.table-wrap{ overflow-x:auto; }
.utable{ width:100%; border-collapse:collapse; font-size:14px; }
.utable th{
  text-align:left; padding:8px 10px; font-size:11.5px; letter-spacing:.08em;
  text-transform:uppercase; color:var(--muted-2); font-weight:600; white-space:nowrap;
  border-bottom:1px solid var(--line);
}
.utable td{ padding:11px 10px; border-bottom:1px solid var(--line); vertical-align:middle; }
.utable tr:last-child td{ border-bottom:none; }
.utable tr.row-off{ opacity:.5; }
.utable select{
  background:var(--bg); border:1px solid var(--line); border-radius:9px;
  padding:6px 9px; font-size:13.5px; outline:none;
}
.utable select:focus{ border-color:rgba(227,178,58,.5); }
.you{ font-size:11px; color:var(--accent-text); border:1px solid rgba(227,178,58,.4);
  border-radius:6px; padding:1px 5px; margin-left:4px; }
.ok-dot{ color:var(--ok); }
.badge-on{ font-size:12.5px; color:var(--ok); }
.badge-off{ font-size:12.5px; color:var(--muted-2); }
.tiny{ font-size:11.5px; }
/* ячейку действий НЕ делаем flex — на td это ломает высоту и выравнивание строк */
.utable td.row-actions{ text-align:right; white-space:nowrap; }
.utable td.row-actions .btn-tiny{ margin-left:6px; }
/* многоколоночные таблицы (пользователи): плотнее ячейки и мельче служебные поля,
   чтобы всё помещалось на экран без горизонтальной прокрутки */
.utable.compact td{ padding:8px 7px; }
.utable.compact th{ padding:8px 7px; }
.utable.compact select{ padding:5px 7px; font-size:13px; }
.utable.compact .btn-tiny{ padding:5px 9px; font-size:12px; }
.utable.compact td.row-actions .btn-tiny{ margin-left:5px; }
/* Адрес почты не режем по буквам: колонка обязана вмещать его хотя бы в две строки,
   иначе «a.burna kov@ag e-golden.ru» читается как ошибка данных, а не как перенос. */
.utable .mail-cell{ min-width:186px; }
.utable.compact .mail-cell{ max-width:220px; overflow-wrap:anywhere; }
.btn-tiny{
  padding:6px 11px; border-radius:9px; font-size:12.5px; font-weight:600;
  background:var(--bg-3); border:1px solid var(--line); color:var(--muted);
  transition:color .15s, border-color .15s; white-space:nowrap;
}
.btn-tiny:hover{ color:var(--fg); border-color:rgba(227,178,58,.5); }
.btn-tiny.danger:hover{ color:var(--danger); border-color:rgba(224,106,90,.55); }
.sub-tag.mini{ font-size:12px; padding:3px 4px 3px 9px; margin:2px 4px 2px 0; }
.sub-add-mini{ font-size:12px; color:var(--muted-2); padding:3px 7px; border-radius:8px;
  border:1px dashed var(--line); white-space:nowrap; }
.sub-add-mini:hover{ color:var(--accent-text); border-color:rgba(227,178,58,.5); }
/* ---------- СОСТАВ ОТЧЁТА (диспетчеризация) ---------- */
.scope-head{ display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-bottom:10px; }
.scope-head .seg{ flex:0 0 auto; }
.scope-head .seg button{ padding:8px 14px; flex:0 0 auto; }
.sc-search{
  flex:1 1 200px; min-width:0; background:var(--bg); border:1px solid var(--line);
  border-radius:10px; padding:9px 12px; outline:none; font-size:14px; color:var(--fg);
}
.sc-search:focus{ border-color:rgba(227,178,58,.5); }
/* Узкий вариант (селект источника рядом со строкой поиска). Классом, а не inline-стилем:
   inline `flex:0 1 220px` перебивал мобильные правила, и в колоночной раскладке 220 px
   становились ВЫСОТОЙ — селект вырастал на треть экрана (аудит 27.08.2026). */
.sc-narrow{ flex:0 1 220px; }
.scope-filters{ display:flex; gap:10px; flex-wrap:wrap; margin-bottom:10px; }
.scope-filters select{
  flex:1 1 150px; min-width:0; background:var(--bg); border:1px solid var(--line);
  border-radius:10px; padding:9px 12px; outline:none; font-size:13.5px; color:var(--fg);
}
.scope-filters select:focus{ border-color:rgba(227,178,58,.5); }
.map-link{ font-size:12px; color:var(--muted-2); margin-left:6px; white-space:nowrap; }
.map-link:hover{ color:var(--accent-text); }
/* Ссылка, накинутая на само имя объекта (раздел «УДК»): выглядит как обычный текст строки
   и переносится. У .map-link другая роль — короткая приписка «карта» рядом с именем. */
.name-link{ color:inherit; text-decoration:none; border-bottom:1px dotted var(--line);
  overflow-wrap:anywhere; }
.name-link:hover{ color:var(--accent-text); }
/* переключатель «в отчёте»: состав правится одним касанием, состояние видно без чтения */
.switch{ display:inline-block; position:relative; width:40px; height:22px; cursor:pointer; }
.switch input{ position:absolute; opacity:0; width:0; height:0; }
.switch span{
  position:absolute; inset:0; border-radius:999px; background:var(--bg);
  border:1px solid var(--line); transition:background .15s, border-color .15s;
}
.switch span::after{
  content:""; position:absolute; top:2px; left:2px; width:16px; height:16px;
  border-radius:50%; background:var(--muted-2); transition:transform .15s, background .15s;
}
.switch input:checked + span{ background:rgba(227,178,58,.22); border-color:rgba(227,178,58,.55); }
.switch input:checked + span::after{ transform:translateX(18px); background:var(--accent-text); }
.switch input:disabled + span{ opacity:.5; cursor:default; }
.pager{ display:flex; align-items:center; justify-content:center; gap:14px;
  margin-top:14px; font-size:12.5px; }
.pager .btn-tiny:disabled{ opacity:.4; }
/* ---------- ДУБЛИ МЕЖДУ КАРТАМИ ---------- */
.cand{ border:1px solid var(--line); border-radius:12px; padding:12px 14px; margin-bottom:10px;
  background:var(--bg); }
.cand:last-child{ margin-bottom:0; }
.cand-head{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:10px; }
.cand-head .cn-name{ font-size:14.5px; }
.cand-dist{ font-size:12px; color:var(--muted-2); }
.cand-dist b{ color:var(--fg); font-weight:600; }
.cand-dist.warn{ color:var(--accent-text); }
.cand-side .map-link{ margin-left:0; font-size:11.5px; }
.cand-pair{ display:flex; align-items:stretch; gap:10px; }
.cand-side{ flex:1 1 0; min-width:0; display:flex; flex-direction:column; gap:3px;
  background:var(--bg-3); border:1px solid var(--line); border-radius:10px; padding:9px 11px; }
.cand-side b{ font-size:12px; color:var(--accent-text); font-weight:600; }
.cand-side span{ font-size:13px; word-break:break-word; }
.cand-eq{ align-self:center; color:var(--muted-2); font-size:15px; }
.cand-actions{ display:flex; gap:8px; flex-wrap:wrap; margin-top:10px; }
.btn-tiny.primary-tiny{ color:var(--accent-text); border-color:rgba(227,178,58,.45); }
.btn-tiny.primary-tiny:hover{ background:rgba(227,178,58,.12); }
.cand .src-list li{ padding:2px 0; }
/* ручное объединение: две колонки поиска */
/* Поля выбора не липнут к тексту-объяснению над ними: пока отступа не было, описание и
   первый ряд полей читались как одна каша (замечание Михаила 05.09.2026). */
.mm-grid{ display:flex; gap:12px; margin:14px 0 10px; }
.mm-side{ flex:1 1 0; min-width:0; }
.mm-side .sc-search{ width:100%; }
.mm-side.chosen .sc-search{ border-color:rgba(227,178,58,.55); color:var(--accent-text); }
.mm-filters{ display:flex; gap:8px; margin-bottom:8px; }
/* Одетые списки (см. dressSelect) занимают место нативных в тех же рядах. Правила писаны
   на обёртку, потому что раскладкой теперь распоряжается она. */
.mm-filters .s-sel{ flex:1 1 0; min-width:0; }
.scope-filters .s-sel{ flex:1 1 150px; min-width:0; }
.s-sel.sc-narrow{ flex:0 1 220px; }
.form-row .s-sel{ min-width:150px; }
.mm-filters select{ flex:1 1 0; min-width:0; background:var(--bg); border:1px solid var(--line);
  border-radius:9px; padding:7px 9px; font-size:12.5px; outline:none; color:var(--fg); }
.mm-filters select:disabled{ opacity:.6; }
.mm-rule{ margin:2px 0 0; }
/* ---------- МИНИ-КАРТА (простановка координат) ---------- */
.mini-map{ height:320px; border:1px solid var(--line); border-radius:12px; margin:10px 0 6px;
  overflow:hidden; z-index:0; }
.mini-map .leaflet-container{ background:var(--bg-3); font:inherit; }
/* маркер — свой кружок, без картинок: ничего лишнего не грузится */
.map-pin{ background:var(--gold); border:2px solid #fff; border-radius:50%;
  box-shadow:0 0 0 2px rgba(0,0,0,.25); }
.leaflet-control-attribution{ font-size:10px; }
/* Leaflet 1.8+ вшивает в свой знак атрибуции флаг Украины (inline SVG). Основной способ убрать —
   map.attributionControl.setPrefix(false) в коде карты; это правило страхует на случай, если
   где-то карта будет создана без него. Атрибуцию OpenStreetMap НЕ трогаем — она обязательна. */
.leaflet-attribution-flag{ display:none !important; }
:root[data-theme="dark"] .leaflet-control-attribution{ background:rgba(0,0,0,.55); color:#bbb; }
:root[data-theme="dark"] .leaflet-control-attribution a{ color:#ddd; }
/* тайлы OSM светлые — в тёмной теме приглушаем, чтобы не резало глаз */
:root[data-theme="dark"] .mini-map .leaflet-tile{ filter:brightness(.78) saturate(.85); }

/* «Добавить устройство» — раскрывается внутри карточки точки */
.obj-picker{ margin-top:10px; padding-top:10px; border-top:1px solid var(--line); }
.obj-picker .mm-side{ max-width:520px; }
.mm-found{ display:flex; flex-direction:column; gap:4px; margin-top:6px;
  max-height:220px; overflow-y:auto; }
.mm-item{ text-align:left; padding:7px 10px; border-radius:9px; border:1px solid var(--line);
  background:var(--bg-3); font-size:12.5px; line-height:1.35; }
.mm-item:hover{ border-color:rgba(227,178,58,.5); }
.mm-item b{ color:var(--accent-text); font-weight:600; }
.mm-item .tiny{ display:block; }
@media (max-width:720px){ .mm-grid{ flex-direction:column; } }

.rej-row{ display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:8px 0; border-bottom:1px solid var(--line); font-size:13px; }
.rej-row:last-child{ border-bottom:none; }
.rej-pair{ flex:1 1 240px; min-width:0; }
.rej-pair b{ color:var(--accent-text); font-weight:600; }
@media (max-width:720px){
  .cand-pair{ flex-direction:column; }
  .cand-eq{ align-self:flex-start; }
}

.sc-legend{ line-height:1.5; margin-bottom:0; }
/* Пояснение под таблицей рассылок — конец одного блока, а сразу за ним начинается карточка
   «Кто следит за объектами». Без отступа они читались одним куском текста. */
#mailing-box .sc-legend{ margin:10px 0 20px; }
/* Пояснение над формой почты — конец текста, дальше начинаются поля. Без отступа подпись
   «СЕРВЕР» шла сразу за словами и выглядела их продолжением. */
#mail-box > .side-hint{ margin:0 0 18px; }
.sc-legend b{ color:var(--fg); font-weight:600; }
.canon-chip{ cursor:pointer; }
.canon-chip.on{ color:var(--accent-text); border-color:rgba(227,178,58,.55); }
/* Мелкий чип стоит внутри строки обычного размера, и по базовой линии шрифта он «тонет»:
   11-й кегль в строке 15-го опускается ниже соседнего текста. Выравниваем по центру строки
   (та же правка, что понадобилась бейджу состояния в заявках). */
.chip.mini{ font-size:11px; padding:1px 6px;
            display:inline-flex; align-items:center; line-height:1; vertical-align:middle; }
/* пометка «стоит на одной точке с …» — отличается от «в отчёте», чтобы не сливались */
.chip.mini.alt{ color:var(--muted); border-style:dashed; }
/* «снят с карты»: объекта больше нет у вендора — требует решения диспетчера, поэтому
   заметнее прочих пометок, но не красный (объект не сломан, его просто убрали) */
.chip.mini.warn-chip{ color:var(--accent-text); border-color:rgba(227,178,58,.5); }
/* «человек отказался» / «подписывать нельзя» — красный, потому что это ЗАПРЕТ, а не
   недоделка: чинить тут нечего, надо перестать пытаться (01.09.2026). Отличать от
   «некуда отправлять» обязательно: то лечится настройкой связи, это — только человеком. */
.chip.mini.stop-chip{ color:var(--danger-text); border-color:var(--danger); }
/* строка подписки, от которой человек отказался: править в ней нечего */
.sub-refused{ opacity:.75; }
/* корзина выбранных объектов при подписке пачкой */
.sf-list{ display:flex; flex-wrap:wrap; gap:6px; margin:10px 0 4px; }
.chip.sf-chip{ display:inline-flex; align-items:center; gap:6px; }
.sf-x{ background:none; border:0; color:var(--muted); cursor:pointer; font-size:15px;
       line-height:1; padding:0 2px; }
.sf-x:hover{ color:var(--danger); }
.sf-kinds-lab{ font-size:12.5px; color:var(--muted-2); margin:12px 0 6px; }
/* раскрытый состав объединённой станции */
.src-row td{ background:var(--bg); padding:12px 14px !important; }
.src-title{ font-size:12.5px; color:var(--muted-2); margin-bottom:8px; }
.src-list{ list-style:none; display:flex; flex-direction:column; gap:6px; margin-bottom:8px; }
.src-list li{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; font-size:13px; }
.src-row .side-hint{ margin:0; }
@media (max-width:720px){
  .src-row td::before{ display:none; }      /* карточная раскладка: подпись колонки тут лишняя */
  .src-row td{ display:block; }
}

/* Сортировка по клику на заголовок таблицы */
.utable th.th-sort{ cursor:pointer; user-select:none; white-space:nowrap; }
.utable th.th-sort:hover{ color:var(--fg); }
.utable th.th-sort.on{ color:var(--accent-text); }
.utable th .sarr{ display:inline-block; width:12px; font-size:11px; }

/* Форма правки из нескольких самостоятельных полей (вкладка «УДК»): каждое со своей
   подписью и своей кнопкой — чтобы не гадать, какая кнопка что сохраняет. */
.edit-box{ display:flex; flex-direction:column; gap:14px; }
.edit-block{
  background:var(--bg-3); border:1px solid var(--line); border-radius:12px; padding:14px 16px;
}
/* Подпись группы стоит НАД полем, и её отступ — единственное, что отделяет слово «Сервер»
   от строки ввода: восьми пикселей мало, подпись читалась приклеенной к полю. */
.edit-lab{ font-size:12px; font-weight:600; color:var(--muted-2); margin-bottom:11px;
  text-transform:uppercase; letter-spacing:.04em; }
.edit-block .side-hint{ margin:8px 0 0; }

.form-row{ display:flex; gap:10px; flex-wrap:wrap; }
.form-row input, .form-row select{
  flex:1 1 150px; min-width:0; background:var(--bg); border:1px solid var(--line);
  border-radius:10px; padding:10px 12px; outline:none; font-size:14px;
}
.form-row input:focus{ border-color:rgba(227,178,58,.5); }
.btn-primary{
  padding:10px 20px; border-radius:10px; font-weight:600; font-size:14px;
  background:linear-gradient(180deg,var(--gold-lt),var(--gold)); color:#fff;
  transition:opacity .2s; white-space:nowrap;
}
.btn-primary:hover{ opacity:.92; }

/* заглушка раздела */
.stub{ text-align:center; padding:56px 20px; background:var(--bg-2);
  border:1px solid var(--line); border-radius:var(--radius); }
.stub-icon{ font-size:42px; margin-bottom:12px; }
.stub-title{ font-size:19px; font-weight:600; margin-bottom:8px; }
.stub-text{ color:var(--muted); font-size:14.5px; line-height:1.6; max-width:520px;
  margin:0 auto 8px; }
.stub-text code{ font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:13px; background:var(--code-bg); border-radius:6px; padding:2px 6px;
  color:var(--accent-text); display:inline-block; margin-top:6px; }

/* ---------- МАСТЕР ПЕРВОГО ВХОДА / МОДАЛКА ---------- */
.modal-backdrop{
  position:fixed; inset:0; z-index:70; background:rgba(0,0,0,.6);
  display:flex; align-items:center; justify-content:center; padding:20px;
  animation:fade .2s ease both;
}
.modal{
  width:min(420px,100%); background:var(--bg-2); border:1px solid var(--line);
  border-radius:20px; padding:26px 24px; box-shadow:0 30px 70px rgba(0,0,0,.5);
  animation:rise .3s cubic-bezier(.2,.7,.2,1) both;
}
.modal h3{ font-size:21px; font-weight:650; margin-bottom:10px; letter-spacing:-.01em; }
.modal-text{ color:var(--muted); font-size:14.5px; line-height:1.55; margin-bottom:16px; }
.modal-text.small{ font-size:13px; }
.modal input{
  width:100%; background:var(--bg); border:1px solid var(--line); border-radius:12px;
  padding:12px 14px; outline:none; margin-bottom:10px; font-size:15px;
}
.modal input:focus{ border-color:rgba(227,178,58,.5); }
.modal-err{ color:var(--danger); font-size:13.5px; margin-bottom:10px; }
.modal-actions{ display:flex; gap:10px; align-items:center; margin-top:6px; }
.modal-actions .btn-primary{ margin-left:auto; }
/* взведённое подтверждение удаления истории (второе нажатие — уже действие) */
.s-btn.ghost.danger{ color:var(--danger); border-color:rgba(224,106,90,.55); }
/* окно с выданными логином и паролем */
.creds-box{ text-align:left; }
.creds-row{ display:flex; align-items:baseline; gap:12px; padding:6px 0; }
.creds-row + .creds-row{ border-top:1px solid var(--line); }
.creds-row span{ color:var(--muted); font-size:13px; min-width:64px; }
.creds-row b{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:18px; letter-spacing:.04em; color:var(--accent-text); user-select:all;
  word-break:break-all;
}
/* Кнопок здесь до четырёх, и «Закрыть» больше не отжимаем вправо: при переносе она
   уезжала на свою строку и выглядела оторванной от остальных. Порядок смысловой —
   сначала то, что делают с паролем, потом выход. */
.creds-actions{ flex-wrap:wrap; }
.creds-actions .btn-primary{ margin-left:0; }
.creds-actions #creds-close{ margin-left:0; }
#creds-mail-note{ margin-top:10px; font-size:13px; color:var(--muted); }

/* окно подтверждения важного действия */
#cf-body p{ margin-bottom:10px; }
#cf-body b{ color:var(--fg); }
.cf-hint{ font-size:13.5px; }
.cf-list{ margin:0 0 10px; padding-left:18px; font-size:13.5px; line-height:1.5; }
.cf-list li{ margin-bottom:4px; }
/* кнопка подтверждения опасного действия — красная вместо золотой */
.modal-actions .btn-primary.danger{
  background:var(--danger); color:#fff; border-color:var(--danger);
}

/* выданные логин/пароль: моноширинно, крупно, выделяются мышью целиком */
.creds-box{ text-align:left; padding:14px 16px; }
.creds-row{ display:flex; align-items:baseline; gap:12px; padding:5px 0; }
.creds-row span{ color:var(--muted); font-size:12.5px; min-width:64px; }
.creds-row b{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:17px; color:var(--accent-text); user-select:all; word-break:break-all;
}
.creds-actions{ flex-wrap:wrap; }
.creds-actions .btn-primary{ margin-left:0; order:-1; }

/* окно «бета / что нового» — шире мастера и с прокруткой: текста много */
.modal-wide{ width:min(600px,100%); max-height:86vh; display:flex; flex-direction:column; }
.notice-head{ display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; }
.notice-ver{ font-size:12.5px; color:var(--muted); margin-left:auto; }
.notice-body{ overflow-y:auto; margin:14px 0 4px; padding-right:6px; }
.notice-body .nb{ margin-bottom:16px; }
.notice-body h4{
  font-size:14px; font-weight:650; margin-bottom:7px; letter-spacing:-.01em;
  display:flex; align-items:center; gap:7px;
}
.notice-body p, .notice-body li{ color:var(--muted); font-size:14px; line-height:1.55; }
.notice-body ul{ margin:0; padding-left:18px; }
.notice-body li{ margin-bottom:4px; }
.notice-body b{ color:var(--fg); font-weight:600; }
.notice-body .nb-new{
  background:var(--bg-3); border:1px solid var(--line); border-left:3px solid var(--gold);
  border-radius:12px; padding:12px 14px;
}
.notice-body .nb-warn{ border-left:3px solid var(--danger); padding-left:12px; }
.notice-body a{ color:var(--accent-text); }
.code-box{ background:var(--bg-3); border:1px solid var(--line); border-radius:14px;
  padding:16px; margin-bottom:14px; text-align:center; }
.code{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:30px; font-weight:700; letter-spacing:.22em; color:var(--accent-text);
  margin-bottom:10px; user-select:all;
}
.code-box .side-hint{ margin-bottom:12px; }
.as-link{ display:inline-block; text-decoration:none; text-align:center; }
.tg-ok{ color:var(--ok); font-size:14px; margin-bottom:10px; }

@media (max-width: 860px){
  .section-area{ padding:16px 14px calc(24px + env(safe-area-inset-bottom)); }
  .section-head h2{ font-size:19px; }
  /* таблица → карточки */
  .utable thead{ display:none; }
  .utable, .utable tbody, .utable tr, .utable td{ display:block; width:100%; }
  .utable tr{ background:var(--bg-3); border:1px solid var(--line);
    border-radius:12px; padding:10px 12px; margin-bottom:10px; }
  /* подпись СВЕРХУ, значение под ней и по левому краю. Раньше было
     justify-content:space-between — подпись слева, значение справа, и длинные названия
     станций рвались посреди строки: на телефоне это и выглядело «съехавшим». */
  .utable td{ border:none; padding:6px 0; display:flex; flex-direction:column;
    align-items:flex-start; gap:3px; }
  .utable td::before{ content:attr(data-l); color:var(--muted-2); font-size:11px;
    text-transform:uppercase; letter-spacing:.06em; }
  .utable td:empty{ display:none; }              /* пустая ячейка не занимает место */
  .utable td.subs-cell{ flex-wrap:wrap; }
  .utable td.subs-cell::before{ width:100%; }
  .utable td.row-actions{ justify-content:flex-start; flex-wrap:wrap; gap:8px;
    white-space:normal; text-align:left; padding-top:8px; }
  .utable td.row-actions .btn-tiny{ margin-left:0; }
  .row-actions::before{ display:none; }
}

/* ---------- ПЕРЕКЛЮЧАТЕЛЬ ТЕМЫ ---------- */
/* Переключатель с ПОДВИЖНЫМ ползунком: он переезжает под выбранную кнопку, а не гаснет
   в одном месте и загорается в другом. Движение показывает, что это один и тот же выбор,
   а не три отдельные кнопки. Положение и ширину ставит JS (`segMove`) переменными, а
   двигается ползунок transform'ом — он считается на видеокарте и не пересчитывает
   раскладку, поэтому не грузит ни слабую машину, ни телефон. */
.seg{ display:flex; position:relative; background:var(--bg); border:1px solid var(--line);
  border-radius:12px; padding:4px; gap:4px; }
.seg::before{ content:""; position:absolute; top:4px; bottom:4px; left:0;
  width:var(--seg-w,0); transform:translateX(var(--seg-x,0));
  background:var(--brand); border-radius:9px; pointer-events:none;
  transition:transform .24s cubic-bezier(.2,.8,.3,1), width .24s cubic-bezier(.2,.8,.3,1);
  box-shadow:0 1px 3px rgba(3,26,70,.18); }
.seg button{ flex:1; position:relative; z-index:1; padding:8px 0; border-radius:9px;
  font-size:13.5px; color:var(--muted); transition:color .2s; }
.seg button:hover:not(.active){ color:var(--fg); }
.seg button.active{ background:none; color:#fff; font-weight:600; }
/* Пока ползунок ещё не встал на место (первая отрисовка), выбранная кнопка красит себя
   сама: без этого выбор на миг выглядит несделанным. */
.seg.seg-init::before{ transition:none; }
@media (prefers-reduced-motion:reduce){ .seg::before{ transition:none; } }

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

/* ---------- Сбор данных (вторая половина «Статистики») ---------- */
/* заголовок-разделитель: показывает, что ниже — другая тема того же раздела */
.sec-split{ margin:26px 0 4px; padding-top:16px; border-top:1px solid var(--line);
  font-size:15px; font-weight:600; color:var(--strong); }
.src-head{ display:flex; justify-content:space-between; align-items:flex-start; gap:12px;
  border-bottom:1px solid var(--line); padding-bottom:10px; margin-bottom:10px; }
.src-name{ font-size:16px; font-weight:600; color:var(--strong);
  display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.src-state{ text-align:right; font-size:13px; white-space:nowrap; }
.cs-ok{ color:var(--ok); }               /* тот же зелёный, что у хороших плиток статистики */
.cs-bad{ color:var(--danger); }
.cs-off{ color:var(--muted-2); }
.src-rows{ display:flex; flex-direction:column; gap:7px; font-size:14px; }
.src-rows .p-label{ display:inline-block; min-width:170px; }
.src-rows code{ font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:13px; background:var(--code-bg); border-radius:6px; padding:2px 6px; }
.src-rows a{ color:var(--accent-text); }


/* ---------- Выгрузка таблиц (раздел «Статистика») ---------- */
.exp-row{ display:flex; gap:14px; align-items:flex-end; flex-wrap:wrap; margin-top:10px; }
.exp-row label{ display:flex; flex-direction:column; gap:5px; font-size:13px; color:var(--muted); }
.exp-row select, .exp-row input{
  background:var(--bg-3); color:var(--fg); border:1px solid var(--line);
  border-radius:8px; padding:8px 10px; font:inherit; font-size:14px; min-width:170px;
}
.exp-row select:disabled{ opacity:.5; }
.exp-row .btn{ padding:9px 18px; }
#exp-note{ align-self:center; }

/* ---------- КАЛЕНДАРЬ ОСВЕЩЕНИЯ (раздел «УДК») ---------- */
.sc-sel{
  background:var(--bg); border:1px solid var(--line); border-radius:10px;
  padding:8px 11px; outline:none; font-size:13.5px; color:var(--fg);
}
.sc-sel:focus{ border-color:rgba(227,178,58,.5); }
.utable input[type=time]{
  background:var(--bg); border:1px solid var(--line); border-radius:9px;
  padding:6px 9px; font-size:13.5px; color:var(--fg); outline:none;
}
.utable input[type=time]:focus{ border-color:rgba(227,178,58,.5); }
.utable tr.row-today td{ background:rgba(227,178,58,.06); }
span.warn{ color:var(--accent-text); }

.warn-box{
  border:1px solid rgba(227,178,58,.45); border-radius:10px; padding:10px 12px;
  background:rgba(227,178,58,.07); color:var(--muted); margin-bottom:10px;
}
.warn-box b{ color:var(--accent-text); }

.mail-port{ max-width:110px; }
#mail-box .form-row input{ flex:1 1 180px; min-width:0; background:var(--bg);
  border:1px solid var(--line); border-radius:10px; padding:9px 12px; outline:none;
  font-size:14px; color:var(--fg); }
#mail-box .form-row input:focus{ border-color:rgba(227,178,58,.5); }

.day-pick{ display:inline-flex; align-items:center; gap:5px; padding:6px 10px;
  border:1px solid var(--line); border-radius:9px; font-size:13px; color:var(--muted);
  cursor:pointer; user-select:none; }
.day-pick:has(input:checked){ border-color:rgba(227,178,58,.55); color:var(--accent-text); }
.plain-list{ margin:6px 0 0; padding-left:18px; color:var(--muted); line-height:1.7; font-size:14px; }
.plain-list li{ margin-bottom:2px; }

/* подписки на объекты: строка «объект — состояние» и виды уведомлений под ней */
.sub-row{ margin-bottom:10px; color:var(--fg); }
.sub-row b{ color:var(--fg); }
.sub-kinds{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-top:4px; }
.chip-check{ display:inline-flex; align-items:center; gap:5px; padding:3px 9px;
  border:1px solid var(--line); border-radius:9px; font-size:12.5px; color:var(--muted);
  cursor:pointer; user-select:none; }
.chip-check:has(input:checked){ border-color:rgba(227,178,58,.55); color:var(--accent-text); }

/* ---------- ТЕЛЕФОН: правки разделов диспетчеризации ----------
   Живёт в КОНЦЕ файла осознанно: карточная раскладка таблиц (.utable td{display:flex})
   объявлена ниже блоков разделов и при равной специфичности перебивала их — из-за этого
   форма ввода координат схлопывалась в узкую колонку. */
@media (max-width:720px){
  /* переключатели в 4 кнопки не влезают в ширину телефона — переносим на две строки */
  .scope-head{ flex-direction:column; align-items:stretch; }
  .scope-head .seg{ flex-wrap:wrap; }
  .scope-head .seg button{ flex:1 1 45%; padding:8px 6px; font-size:12.5px; }
  /* flex:1 1 200px в КОЛОНОЧНОМ flex растит поле по ВЫСОТЕ — строка поиска превращалась
     в прямоугольник на треть экрана (та же болезнь, что с .form-row input; аудит
     27.08.2026). Ширину задаём явно, рост по главной оси отключаем. */
  .scope-head .sc-search{ width:100%; flex:0 0 auto; }
  .sc-narrow{ flex:0 0 auto; width:100%; }
  .scope-filters{ flex-direction:column; }
  .scope-filters select{ width:100%; flex:0 0 auto; }   /* см. .sc-search выше: рост в колонке = высота */

  /* развёрнутая строка (источники канона, редактор координат) — обычным блоком, а не таблицей */
  .utable .src-row td{ display:block; padding:12px 14px !important; }
  .utable .src-row td::before{ display:none; }
  .src-row .form-row{ flex-direction:column; }
  .src-row .form-row input, .src-row .form-row .btn-primary{ width:100%; }
  /* flex:1 1 150px у поля в КОЛОНОЧНОМ flex растягивает его по ВЫСОТЕ — на телефоне
     из-за этого поля ввода вытягивались в огромные прямоугольники. Ошибка повторяется
     каждый раз, когда ряд переводят в колонку, поэтому правило общее: любой ввод внутри
     разделов на телефоне растёт только по ширине (аудит 27.08.2026). */
  #sec-body .form-row input, #sec-body .form-row select{ flex:0 0 auto; }
  /* Общего правила «не расти» для всех полей здесь НЕ ставим, хотя соблазн есть: рост
     вреден только в КОЛОНОЧНОМ flex (растёт высота), а в обычном ряду он и держит ширину.
     Проверено делом: такая «страховка» сжала оба фильтра парного выбора (.mm-filters)
     до иконок. Чиним точечно — по контейнерам, которые переводятся в колонку. */
  /* Колонкой ставим ПОЛЯ ВВОДА. Строки с переключателем (:not(.sw-row)) исключены:
     иначе переключатель встаёт НАД подписью и обе половины разъезжаются по центру —
     «Почта», «Telegram», «В чате на сайте» в «Управлении рассылкой» выглядели так,
     будто это отдельные заголовки, а не подписи к тумблерам (аудит 27.08.2026). */
  .edit-box .form-row:not(.sw-row):not(.quiet-row){ flex-direction:column; }
  /* Тумблер и его подпись — ВСЕГДА в одну строку. Без nowrap длинная подпись
     («Telegram — не привязан, привяжите в профиле») переносится ЦЕЛИКОМ под тумблер и
     читается как отдельный заголовок; с min-width:0 она переносится внутри своей колонки,
     оставаясь справа от переключателя. */
  .sw-row{ flex-wrap:nowrap; }
  .sw-row > span{ min-width:0; }
  .edit-box .form-row input, .edit-box .form-row .btn-primary{ width:100%; }
  /* Кнопка с длинной надписью («Включить уведомления на этом устройстве») в 390 px не
     влезает: разрешаем перенос строки. Ширину не задаём — рядом стоит «Проверить»,
     и две кнопки во всю ширину растянулись бы каждая на свою строку без нужды. */
  .edit-box .btn-tiny, .edit-box .s-btn{ white-space:normal; line-height:1.35;
    max-width:100%; text-align:left; }
  .edit-block{ padding:10px 11px; }
  .src-title{ margin-bottom:10px; }

  .utable td.row-actions{ padding-top:10px; }

  /* вкладки внутри разделов («На объединение» / «Объединённые станции», периоды статистики).
     Именно в #sec-body — переключатель темы в профиле трогать не надо, он и так помещается. */
  #sec-body .seg:not(.s-seg){ flex-wrap:wrap; }
  #sec-body .seg:not(.s-seg) button{ flex:1 1 45%; }
  /* Переключатель-фильтр общего языка (.s-seg) переносить НЕЛЬЗЯ: ползунок двигается
     только по горизонтали, и перенесённая вниз кнопка остаётся без него, а сам он
     закрашивает соседа на первой строке. Сетка равными колонками держит один ряд. */
  #sec-body .s-seg{ display:grid; grid-auto-flow:column; grid-auto-columns:1fr; }
  #sec-body .s-seg button{ font-size:12px; padding:0 2px; min-height:44px;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

  /* Строки «подпись — значение» (Сервер, Инфо, Статистика): на широком экране значение
     прижато вправо и не переносится (tabular-nums, nowrap), но на телефоне длинное
     значение — имя дампа «ga_n8n_20260827_0300.sql.gz (42 МБ) — 3 ч назад» — уезжало
     за правый край и обрезалось. Ставим в столбик и разрешаем перенос. */
  ul.plain li{ flex-direction:column; align-items:flex-start; gap:2px; }
  ul.plain li b{ white-space:normal; overflow-wrap:anywhere; }

  /* «Сбор данных»: шапка карточки и подписи в столбик — адреса API длинные */
  .src-head{ flex-direction:column; gap:6px; }
  .src-state{ text-align:left; white-space:normal; }
  .src-rows .p-label{ min-width:0; display:block; margin-bottom:2px; }
  .src-rows code{ word-break:break-all; }
}

/* Кликабельная строка сводки (журнал: «кто и что именно менял») */
.plain li.row-click{ cursor:pointer; border-radius:8px; padding-left:6px; padding-right:6px;
  transition:background .15s; }
.plain li.row-click:hover{ background:var(--bg-3); }
.plain li.row-click:hover span{ color:var(--fg); }
.modal-wide{ max-width:min(920px, 94vw); }
.modal-wide .modal-text{ max-height:min(62vh, 560px); overflow:auto; }
.ev-table td{ font-size:13px; vertical-align:top; }

@media (max-width:720px){
  /* выгрузка: поля в столбик, кнопка во всю ширину */
  .exp-row{ flex-direction:column; align-items:stretch; }
  .exp-row label, .exp-row select, .exp-row input, .exp-row .btn{ width:100%; }
}

/* жалоба на ответ ассистента — неприметная строка под пузырём */
.msg-actions{ display:flex; gap:8px; align-items:center; margin-top:3px; padding-left:6px; }
.msg-actions input{ flex:1; min-width:120px; }
.link-btn{ background:none; border:0; padding:0; color:var(--muted); font-size:12px;
  cursor:pointer; opacity:.55; }
.link-btn:hover{ opacity:1; color:var(--accent-text); text-decoration:underline; }

/* Раздел «Управление рассылкой»: строки с переключателями.
   .form-row по умолчанию просто flex, из-за чего переключатель и подписи «с»/«до»
   висели по верхнему краю рядом с высокими селектами, а сами селекты растягивались
   во всю ширину (flex:1 1 150px). Здесь выравниваем по центру и даём времени
   фиксированную ширину — это выбор из 24 значений, а не поле ввода. */
.sw-row, .quiet-row{ align-items:center; }
.sw-row{ margin-bottom:10px; }             /* способы доставки шли слишком плотно */
.sw-row:last-of-type{ margin-bottom:0; }
.quiet-row .sc-sel{ flex:0 0 120px; }
.quiet-row > span{ color:var(--muted); }
.quiet-row button{ margin-left:auto; }      /* «Сохранить» прижата вправо, как в других формах */
@media(max-width:520px){
  .quiet-row .sc-sel{ flex:1 1 90px; }
  .quiet-row button{ margin-left:0; }
}

/* Жалоба на ответ ассистента: форма под пузырём и приложенная переписка в карточке заявки */
.complain{ width:100%; display:flex; flex-direction:column; gap:6px; }
.complain textarea{ width:100%; background:var(--bg); border:1px solid var(--line);
  border-radius:10px; padding:8px 10px; font-size:14px; outline:none; resize:vertical; }
.complain label{ font-size:12.5px; color:var(--muted); }
.complain .sc-sel{ flex:0 0 200px; }
.complain-chat{ margin:6px 0; font-size:13px; color:var(--muted); }
.complain-chat summary{ cursor:pointer; }
.complain-chat pre{ white-space:pre-wrap; margin-top:6px; padding:8px 10px;
  background:var(--code-bg); border-radius:8px; font-size:12.5px; line-height:1.5; }

/* Форма рассылки: четыре разных вопроса (кому/когда · дни · каналы · области) шли
   одинаковыми .form-row подряд и сливались в кашу. Каждый вопрос — своя группа
   с подписью и отделяющей линией, чтобы взгляд не искал границы блоков. */
.ml-group{ padding:12px 0 0; border-top:1px solid var(--line); margin-top:12px; }
.ml-group:first-of-type{ border-top:0; margin-top:0; padding-top:0; }
.ml-glab{ font-size:12px; color:var(--muted); margin-bottom:8px; }
.ml-group .side-hint{ margin:8px 0 0; }
/* дни/каналы/области — плитки, между ними воздуха больше, чем между полями ввода */
.pick-row{ gap:8px 10px; }
.ml-time{ flex:0 0 130px; }
.ml-foot{ align-items:center; gap:12px; padding-top:14px; margin-top:14px;
  border-top:1px solid var(--line); }
.ml-foot .btn-primary{ margin-left:auto; }
@media (max-width:720px){
  /* на телефоне плитки дней в один ряд не влезают — пусть переносятся, а кнопки
     сохранения занимают всю ширину (иначе margin-left:auto прижимает их в угол) */
  .edit-box .ml-foot{ flex-direction:row; flex-wrap:wrap; }
  .ml-foot .btn-primary{ margin-left:0; flex:1 1 100%; }
  .edit-box .ml-time{ width:100%; flex:1 1 auto; }
}

/* Восстановление доступа на экране входа: ссылка и раскрывающееся поле.
   Спрятано по умолчанию — нужно раз в полгода, а вход должен оставаться простым. */
#forgot-link{ display:block; margin:10px auto 0; }
#forgot-box{ margin-top:10px; display:flex; flex-direction:column; gap:8px; }
#forgot-box[hidden]{ display:none; }
.login-hint{ font-size:12.5px; color:var(--muted); line-height:1.5; }

/* Почта в профиле: та же логика, что у блока Telegram (состояние + действия столбиком) */
.mailp-form{ display:flex; flex-direction:column; gap:8px; }
.mailp-form input{ background:var(--bg); border:1px solid var(--line); color:var(--fg);
  border-radius:10px; padding:9px 11px; font-size:14px; outline:none; }
.mailp-form input:focus{ border-color:rgba(227,178,58,.5); }
.mailp-wait{ color:var(--muted); font-size:13.5px; margin-bottom:6px; word-break:break-all; }
#mail-box-profile .tg-ok{ word-break:break-all; }

/* Плитки-переключатели (дни, каналы, области, «кому»).
   Галочка внутри плитки — тоже input, поэтому под неё подпадало общее правило
   `.form-row input{flex:1 1 150px}`: квадратик растягивался и выдавливал подпись
   в три строки («просто на почту», «уведомления браузера»). Фиксируем размер галочки
   и запрещаем перенос подписи — плитка должна быть шириной со свой текст. */
.day-pick input[type="checkbox"], .day-pick input[type="radio"]{
  flex:0 0 auto; width:15px; height:15px; min-width:0; margin:0; padding:0;
}
.day-pick{ white-space:nowrap; gap:7px; padding:7px 12px; }
/* «Кому»: два варианта — отделяем их друг от друга заметнее, чем дни недели */
#ml-kind{ gap:12px; }
#ml-kind .day-pick{ font-size:13.5px; }
@media (max-width:720px){
  /* на узком экране подпись всё же может не влезть — там перенос лучше обрезки */
  .day-pick{ white-space:normal; }
}

/* Группы формы рассылки: между разными вопросами нужен заметный интервал —
   при плотной посадке «Кому» читалось как одна строка с «Во сколько». */
.ml-group{ padding-top:18px; margin-top:18px; }
.ml-group .form-row{ margin-top:2px; }
.ml-glab{ margin-bottom:10px; font-weight:600; color:var(--muted-2); }
.ml-foot{ padding-top:18px; margin-top:18px; }
/* строка «кому»: селект и поля получателя не должны липнуть к переключателю */
#ml-kind{ margin-bottom:12px; }
#ml-user-row, #ml-ext-row{ margin-top:2px; }

/* Пояснение под заголовком раздела (например, в «Выгрузке таблиц»): задумано как подпись,
   а стиля не было — текст шёл наравне с содержимым. */
.sec-note{ margin:0 0 14px; color:var(--muted); font-size:13px; line-height:1.6; }

/* Выбор объектов для слежения: кнопка «Следить» раньше стояла в потоке текста и «прыгала»
   вслед за длиной названия. Теперь имя и действие — две колонки, объекты сгруппированы
   по типу (без этого ТПИ было не найти: слова «ТПИ» в их названиях нет). */
.pick-group{ margin-top:12px; }
.pick-gtitle{ font-size:12px; color:var(--muted); text-transform:uppercase;
  letter-spacing:.04em; margin-bottom:4px; }
.pick-list{ list-style:none; margin:0; padding:0; }
.pick-list li{ display:flex; align-items:center; gap:10px; padding:5px 0;
  border-bottom:1px solid var(--line); }
.pick-list li:last-child{ border-bottom:0; }
.pick-name{ flex:1 1 auto; min-width:0; font-size:13.5px; }
.pick-name .dim{ margin-left:6px; }
.pick-act{ flex:0 0 auto; min-width:96px; text-align:center; }
@media (max-width:720px){
  .pick-list li{ flex-wrap:wrap; }
  .pick-act{ width:100%; }
}

/* Настройка отметок объекта в «Составе отчёта»: у каждого прибора точки свой блок
   (канон метео — один на оба источника, привязанная камера — отдельно). */
.mute-row td{ background:var(--bg); }
.mute-dev{ margin-top:10px; padding-top:8px; border-top:1px solid var(--line); }
.mute-dev:first-of-type{ border-top:0; padding-top:0; }
.mute-name{ font-size:13px; font-weight:600; margin-bottom:6px; }
.scope-cell{ white-space:nowrap; }
/* без vertical-align тумблер (inline-block без текста) встаёт нижним краем на базовую
   линию соседней кнопки и уезжает вверх; flex на td ставить нельзя — ломает строку */
.scope-cell .switch, .scope-cell .mute-btn{ vertical-align:middle; }
.scope-cell .mute-btn{ margin-left:8px; }
@media (max-width:720px){ .scope-cell .mute-btn{ margin-left:0; margin-top:6px; } }

/* ---------- РАЗДЕЛ «БАЗА ЗНАНИЙ» ---------- */
.kb-head{ display:flex; align-items:center; justify-content:space-between; gap:12px;
  flex-wrap:wrap; margin-bottom:10px; }
.kb-head h3{ margin:0; }
.kb-btns{ display:flex; gap:8px; flex-wrap:wrap; }
.kb-tblwrap{ overflow-x:auto; }              /* широкая таблица прокручивается сама, не страница */
.kb-group td{ background:var(--bg); font-weight:600; font-size:13px; color:var(--muted);
  padding:8px 10px !important; }
.kb-ok{ color:var(--ok); }
.kb-eqs{ display:flex; flex-wrap:wrap; gap:8px 18px; }
.kb-eq{ display:flex; align-items:center; gap:6px; font-size:13.5px; }
/* Формы базы знаний (карточка документа, модели, строки справочника) остались на прежних
   мелких кнопках: переписывать их разметку ради размера значило бы трогать логику правки
   текста. Размер задаём здесь — цель нажатия 40 px на мониторе и 44 на телефоне, где
   в эти кнопки целятся пальцем. */
#kb-editor .btn-tiny, #kb-eq-edit .btn-tiny, #kb-eq-edit .btn-primary,
#kb-fact-edit .btn-tiny, #kb-fact-edit .btn-primary{
  min-height:40px; padding:0 14px; }
#kb-editor .kb-eq{ min-height:40px; }
/* Форма правки открывается ПОД списком, и без отступа она прирастала к последней строке —
   было не видно, что это отдельное окно правки, а не ещё одна строка справочника. */
#kb-fact-edit > .card, #kb-eq-edit > .card, #kb-editor > .card{ margin-top:16px; }
/* Кнопки формы отделены от текста над ними: прижатые вплотную, они читаются как часть
   подписи, а не как действие (замечание Михаила 06.09.2026). */
#kb-fact-edit .kb-btns, #kb-eq-edit .kb-btns, #kb-editor .kb-btns,
.card > .s-acts{ margin-top:16px; }
/* Переключатель с подписью — свой блок, а не строка внутри текста: у него воздух сверху
   и снизу, иначе он теряется между двумя абзацами пояснений. */
.sw-row{ align-items:center; gap:12px; margin:16px 0 6px; }
@media (max-width:760px){
  #kb-editor .btn-tiny, #kb-eq-edit .btn-tiny, #kb-eq-edit .btn-primary,
  #kb-fact-edit .btn-tiny, #kb-fact-edit .btn-primary{
    min-height:44px; }
  #kb-editor .kb-eq{ min-height:44px; width:100%; }
  #kb-editor .kb-sec-head{ flex-wrap:wrap; }
  /* Поле ввода — тоже цель пальца: в поле шириной во весь экран и высотой 30 px попасть
     можно, а вот поставить курсор в нужное место — уже нет. */
  #kb-editor input:not([type="checkbox"]), #kb-eq-edit input:not([type="checkbox"]),
  #kb-fact-edit input:not([type="checkbox"]){ min-height:44px; }
}
.kb-check{ margin:8px 0 0 18px; font-size:13.5px; line-height:1.7; }
.kb-proposed td{ background:rgba(227,178,58,.07); }   /* строка, предложенная моделью */
.kb-books button{ font-size:13px; }
/* Формы разделов: подпись СВЕРХУ, поле во всю ширину. Иначе подпись и поле встают в строку,
   и форма читается как каша (замечание 07.08.2026). */
/* :not(...) обязателен: без него ширина 100% растягивала и ФЛАЖКИ — подпись уезжала от
   своего квадратика, и список оборудования рассыпался (поймано 07.08.2026). */
/* ВСЕ поля ввода внутри разделов выглядят одинаково (07.08.2026). Правило общее, а не
   для одной вкладки: поля живут в десятке разных экранов (база знаний, заявки, рассылки),
   и привязка вида к конкретному контейнеру каждый раз оставляла где-то «браузерные»
   поля — так и вышло с настройками генератора и ответом на заявку.
   ⚠️ Ширину здесь НЕ задаём: поля есть и внутри таблиц, там 100% ломает колонки. */
#sec-body input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
#sec-body textarea, #sec-body select{
  font-family:inherit; font-size:14px; background:var(--bg);
  border:1px solid var(--line); border-radius:10px; padding:9px 12px;
  outline:none; transition:border-color .15s;
}
#sec-body textarea{ width:100%; max-width:760px; display:block; line-height:1.55;
  resize:vertical; min-height:72px; }
#sec-body input:focus, #sec-body textarea:focus, #sec-body select:focus{
  border-color:rgba(227,178,58,.5);
}
/* поля-строки в формах правки — во всю ширину подписи над ними */
#kb-editor input:not([type="checkbox"]):not([type="file"]), #kb-editor select,
#kb-eq-edit input:not([type="checkbox"]), #kb-fact-edit input:not([type="checkbox"]),
#kb-fact-edit select, #kbg-model{
  width:100%; max-width:760px; display:block;
}
#kb-editor textarea, #kb-eq-edit textarea, #kb-fact-edit textarea{ max-width:none; }
#kbg-prompt{ max-width:none; }         /* правила разметки длинные — им нужна вся ширина */
#kb-editor input[readonly]{ color:var(--muted); }   /* имя файла у загруженного не меняется */
#kb-editor .kb-eqs input[type="checkbox"]{ width:auto; flex:0 0 auto; margin:0; }

/* ---- показ markdown по разделам ---- */
.md-view{ font-size:14px; line-height:1.6; }
.md-view h3, .md-view h4, .md-view h5{ margin:10px 0 6px; font-size:15px; }
.md-view p{ margin:6px 0; }
.md-view code{ background:var(--bg); padding:1px 5px; border-radius:5px;
  font-family:ui-monospace,Menlo,Consolas,monospace; font-size:13px; }
.md-view ul, .md-view ol{ margin:6px 0 6px 20px; }
.md-view li{ margin:3px 0; }
.md-view table{ width:100%; border-collapse:collapse; margin:8px 0; font-size:13px; }
.md-view th, .md-view td{ border:1px solid var(--line); padding:6px 8px; text-align:left;
  vertical-align:top; }
.md-view th{ background:var(--bg); font-weight:600; }
.kb-sec{ border:1px solid var(--line); border-radius:12px; padding:12px 14px; margin-bottom:10px; }
.kb-sec-head{ display:flex; align-items:center; justify-content:space-between; gap:10px;
  margin-bottom:4px; }
.kb-sec-title{ font-weight:600; font-size:14px; }
.kb-sec-title .muted{ font-weight:400; }
.kb-sec textarea{ font-family:ui-monospace,Menlo,Consolas,monospace; font-size:13px; }
#kb-editor textarea#kbe-md{ font-family:ui-monospace,Menlo,Consolas,monospace; font-size:13px; }
#kb-editor .ml-group, #kb-eq-edit .ml-group, #kb-fact-edit .ml-group{ margin-bottom:16px; }
#kb-editor .side-hint, #kb-eq-edit .side-hint, #kb-fact-edit .side-hint{ margin-top:6px; }
@media (max-width:720px){
  .kb-head{ align-items:flex-start; flex-direction:column; }
}
.kb-caret{ display:inline-block; width:14px; color:var(--muted); }
tr.kb-group{ cursor:pointer; }
tr.kb-group:hover td{ color:var(--fg); }
tr.kb-dirty td:first-child{ box-shadow:inset 3px 0 0 var(--gold); }
.kb-warnbar{ background:rgba(227,178,58,.10); border:1px solid rgba(227,178,58,.35);
  border-radius:10px; padding:8px 12px; font-size:13.5px; margin:0 0 12px; }
/* Предложения рабочих — не предупреждение, а работа, которую ждут: полоса слева,
   как у неопубликованных правок в списке документов. */
.kb-warnbar.kb-dirtybar{ box-shadow:inset 3px 0 0 var(--gold); }
.kb-sec-head{ cursor:pointer; }
/* Список документов свёрнут по группам: даём строкам реальный зазор, иначе группы
   слипаются в сплошную полосу (замечание 07.08.2026). */
.kb-doctable{ border-collapse:separate; border-spacing:0 6px; }
.kb-doctable tr.kb-group td{ background:var(--bg); border:1px solid var(--line);
  border-radius:10px; padding:12px 12px !important; font-size:13.5px; }
.kb-doctable tbody tr:not(.kb-group) td{ border-bottom:1px solid var(--line); }
.kb-doctable tbody tr:not(.kb-group):last-child td{ border-bottom:none; }
/* «Загрузить файл» — это label с невидимым input, но выглядеть должен как кнопка */

/* полоса хода преобразования текста моделью (раздел «База знаний») */
.kb-gen{margin:8px 0;padding:8px 10px;border-radius:8px;background:var(--bg-3);
  border:1px solid var(--line);font-size:.92em;color:var(--muted)}
.kb-gen-dot{display:inline-block;width:8px;height:8px;margin-right:6px;border-radius:50%;
  background:var(--gold);animation:kbpulse 1.2s ease-in-out infinite;vertical-align:middle}
@keyframes kbpulse{0%,100%{opacity:.25}50%{opacity:1}}
.kb-file{ display:flex; flex-wrap:wrap; gap:8px; align-items:baseline; margin:2px 0;
  font-size:13.5px; }
.kb-file a{ color:var(--accent-text); }

/* Строка справочника принята, но справочник ещё не пересобран — в базе знаний её нет. */
tr.kb-pending td:first-child{ box-shadow:inset 3px 0 0 var(--gold); }
/* История документа под заголовком редактора: кто составил, кто правил или публиковал. */
.kb-hist{ margin:-4px 0 14px; font-size:12.5px; color:var(--muted); }

/* ---- Заявки: карточка одинаково выглядит у автора и у диспетчера ---- */
.tk-list{ display:flex; flex-direction:column; gap:12px; }
.tk-card{ border-left:3px solid transparent; }
.tk-list .tk-card{ background:var(--bg-3); border-radius:12px; padding:12px 14px; }
.tk-new{ border-left-color:var(--gold); }
.tk-in_work{ border-left-color:#6ba3d6; }
.tk-done{ border-left-color:var(--ok); }
.tk-rejected{ border-left-color:var(--muted); }
.tk-head{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:2px;
  line-height:1.3; }
.tk-no{ font-weight:600; }
/* бейдж состояния: line-height:1 и inline-flex — иначе шрифт 12px внутри строки 15px
   опускал текст ниже центра относительно номера заявки */
.tk-state{ font-size:12px; line-height:1; padding:4px 9px; border-radius:20px;
  background:var(--bg-2); border:1px solid var(--line);
  display:inline-flex; align-items:center; }
.tk-state-new{ color:var(--gold); border-color:var(--gold); }
.tk-state-in_work{ color:#6ba3d6; border-color:#6ba3d6; }
.tk-state-done{ color:var(--ok); border-color:var(--ok); }
.tk-kind{ font-size:12px; color:var(--muted); }
.tk-track{ font-size:12.5px; color:var(--muted); margin-bottom:8px; }
.tk-obj{ font-size:13px; color:var(--muted); margin-bottom:4px; }
.tk-body{ margin:6px 0 10px; white-space:pre-wrap; }
/* ответ виден сразу: раньше он тонул в общей серой подсказке под текстом */
.tk-reply{ background:var(--bg-2); border-left:3px solid var(--gold); border-radius:8px;
  padding:9px 12px; margin:8px 0; font-size:14px; }
.tk-note{ font-size:13px; color:var(--muted); font-style:italic; margin:6px 0; }
.tk-acts{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:8px; }
/* у .side-hint снизу свой отступ 10px — рядом с кнопкой он утаскивал текст выше центра */
.tk-acts .side-hint{ margin:0; }
/* Слова, предложенные рабочим: ждут решения диспетчера, в поиск ещё не ушли. */
.kb-prop{ margin-top:5px; font-size:12.5px; color:var(--muted); }
.kb-prop .btn-tiny{ margin-left:6px; }

/* Сетка «что и куда присылать» (Управление рассылкой). Узкие столбцы каналов —
   иначе четыре колонки с галочками растягивают первую и текст вида ломается. */
.nt-matrix th.nt-ch { text-align: center; width: 90px; }
.nt-matrix th.nt-ch.off { opacity: .45; text-decoration: line-through; }
.nt-matrix td.nt-cell { text-align: center; }
.nt-matrix td.nt-cell input { width: 18px; height: 18px; accent-color: var(--gold); }
.nt-matrix td.nt-cell input:disabled { opacity: .35; }
.nt-matrix .side-hint { margin-top: 2px; }

/* Диагностика: строки «требует внимания» с цветной полосой — красная для сбоя,
   жёлтая для замечания (то же кодирование, что у неопубликованных правок). */
.hl-row { padding: 8px 0 8px 12px; border-left: 3px solid var(--line); margin-bottom: 8px; }
.hl-row.fail { border-left-color: var(--danger); }
.hl-row.warn { border-left-color: var(--gold); }

/* Диагностика: пометка «чинится сама» / «нужен человек» у проверки. Вторая намеренно
   приглушена и пунктиром — это не тревога, а факт: перезапуск тут не поможет. */
.chip.mini.off{ color:var(--muted-2); border-style:dashed; }
/* Состояние проверки: значок и слово — одной строкой. Колонка узкая, и без этого эмодзи
   переносился на строку выше слова (замечено на скриншоте 11.08.2026). */
.hl-state{ display:inline-flex; align-items:center; gap:6px; white-space:nowrap; }

/* «Пороги и опрос»: настройка = строка «название + поле + кнопки», под ней пояснение.
   Пояснение здесь не украшение: числа вроде «0.008 байт/пиксель» без него не поменяешь
   осознанно, а менять их будет человек, который проект не писал. */
/* Настройка живёт ВНУТРИ раскрытой строки списка, поэтому своей рамки и отступов сверху
   ей больше не нужно: их даёт `.s-row-x`. */
.tune{ display:flex; flex-direction:column; gap:8px; }
.tune-marks{ display:flex; gap:8px; flex-wrap:wrap; }
.tune-field input[type=number]{ min-height:44px; padding:0 12px; font-size:14px;
  color:var(--ink); background:var(--field); border:1px solid var(--line);
  border-radius:11px; outline:none; }
.tune-field input[type=number]:focus{ border-color:var(--brand); }
/* Справочные строки об источниках: подписи в столбик, чтобы на телефоне не резались. */
.tune-ref{ display:flex; flex-direction:column; gap:10px; margin:4px 0; }
.tune-ref-row{ display:flex; flex-direction:column; gap:3px; padding:10px 12px;
  background:var(--field); border-radius:11px; font-size:13px; color:var(--muted); }
.tune-ref-row b{ color:var(--ink); font-size:14px; }
.tune-ref-row.off{ opacity:.6; }
.tune-head{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
/* 🔴 `flex:1 1 260px` в КОЛОНОЧНОМ flex («.tune» стал колонкой) означает базовую ВЫСОТУ
   260 px, а не ширину: заголовок поля раздувался в пустой блок на четверть экрана
   (06.09.2026, та же грабля, что с полем поиска в «Дублях»). */
.tune-name{ flex:none; font-weight:600; font-size:15px; color:var(--strong);
  display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.tune-marks{ margin-left:auto; }
.tune-field{ display:flex; align-items:center; gap:8px; }
.tune-field input[type=number]{ width:110px; }
.tune-unit{ color:var(--muted-2); min-width:34px; }
.tune-state{ color:var(--muted-2); min-width:78px; }
.tune-warn{ color:var(--gold); }
/* Поле ввода и кнопки — ОДНОЙ строкой внизу блока, по левому краю, под пояснением.
   Одинаково в «Порогах» и в «Ассистенте». Раньше поле висело справа в шапке, а кнопка
   стояла отдельно: они относятся друг к другу, но глазом не связывались, и в длинном
   списке кнопки шли лесенкой — поля разной ширины (переключатель, число, список моделей). */
.tune-row{ display:flex; align-items:center; gap:8px; margin-top:10px; flex-wrap:wrap; }
/* У длинного текста поле идёт колонкой слева (max-width 900) — строку с кнопками
   держим в границах ЭТОЙ колонки. */
.asst-long .tune-row{ max-width:900px; }
/* Телефон: поле и кнопки во всю ширину, иначе уезжают за край. */
@media (max-width:720px){
  .tune-head{ flex-direction:column; align-items:stretch; gap:8px; }
  #sec-body .tune-row{ align-items:stretch; }
  #sec-body .tune-row .tune-field{ flex:1 1 100%; }
  #sec-body .tune-row button{ flex:1 1 auto; }
}

/* Ручные рамки нагрузки (УДК → «Проверка освещения»): две пары «от — до» ровной сеткой.
   Раньше это были .form-row с inline max-width, и поля разъезжались по ширине карточки. */
.light-manual{ margin:8px 0 4px; }
.lm-grid{ display:grid; grid-template-columns:auto 110px auto 110px;
          gap:8px 10px; align-items:center; }
.lm-grid input{ width:100%; }
.lm-dash{ text-align:center; }
/* Телефон: подпись своей строкой, поля парой под ней. */
@media (max-width:720px){
  .lm-grid{ grid-template-columns:1fr auto 1fr; }
  .lm-grid > .dim:not(.lm-dash){ grid-column:1 / -1; }
}

/* Вид уведомления, снятый диспетчером в «Составе отчёта»: галочка гаснет и зачёркивается —
   подписка на него всё равно не сработает, состав отчёта главнее личной настройки. */
.chip-check.chip-off{ opacity:.6; text-decoration:line-through; cursor:not-allowed; }
.chip-check.chip-off .dim{ text-decoration:none; }

/* Подсказка «похоже, плату уже поменяли» (УДК → «Замена контроллера»). У контроллеров
   есть GPS, поэтому «старый замолчал — рядом появился новый» видно точно, и замену
   предлагаем одной кнопкой. Полоса слева — как у прочих «требует внимания». */
.swap-hint{ margin:6px 0 10px; padding:8px 12px; border-left:3px solid var(--gold);
            background:var(--bg-3); border-radius:0 8px 8px 0; line-height:1.5; }
/* «Да, это замена» — своей строкой под текстом подсказки и по её левому краю: рядом с
   текстом кнопка вставала на уровень последней строки и выглядела как её продолжение. */
.swap-hint button{ display:block; margin:10px 0 2px; }
.chip.mini.warn{ color:var(--gold); border-color:var(--gold); }

/* Строка «кнопка + пояснение»: у .form-row нет выравнивания по центру (он общий и рассчитан
   на поля ввода разной высоты), поэтому подпись рядом с невысокой кнопкой уезжала вверх. */
.act-row{ display:flex; gap:10px; flex-wrap:wrap; align-items:center; }

/* Пояснение, стоящее МЕЖДУ карточками, получает их поля и фон: голый текст на фоне
   страницы начинался левее карточек и выглядел выпавшим из раздела. */
.hint-box{ background:var(--field); border:1px solid var(--line); border-radius:var(--radius);
  padding:12px 16px; margin:0 0 14px; }
.hint-box .side-hint{ margin:0; }

/* Обслуживание сервера («Сервер» → «Обслуживание сервера»): ряд кнопок и журнал операций.
   Пояснение под кнопками — отдельная вещь, а не их подпись: без отступа оно прирастало
   к ряду и читалось четвёртой кнопкой. */
.ho-row{ gap:8px; flex-wrap:wrap; margin-bottom:14px; }
.ho-row + details.s-note{ margin-top:14px; }
.ho-log{ margin-top:12px; border-top:1px solid var(--line); padding-top:10px;
         display:flex; flex-direction:column; gap:8px; font-size:14px; }

/* ── Раздел «Ассистент» ───────────────────────────────────────────────
   Поля здесь крупнее, чем в «Порогах»: тут правят не число, а промпт — длинный
   текст, который надо видеть целиком. */
.asst-field { padding: 10px 0; border-bottom: 1px solid var(--line); }
.asst-field:last-child { border-bottom: 0; }
/* Всё в разделе выровнено ПО ЛЕВОМУ КРАЮ, как в остальных разделах панели: раньше
   длинный текст и строка токена стояли по центру, и глаз прыгал между левыми
   заголовками и центрированными полями. */
.asst-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 6px; }
.asst-row input[type="text"] { flex: 0 1 320px; }
.asst-row select { flex: 0 1 320px; }
/* Длинный текст (промпт, напоминание): колонка слева. Ширину ограничиваем —
   строка во весь экран 1600 px читается тяжело. */
.asst-wide { margin: 8px 0 0; max-width: 900px; }
.asst-wide + .side-hint { max-width: 900px; }
.asst-text { width: 100%; display: block; font-family: ui-monospace, Menlo, Consolas, monospace;
             font-size: 13px; line-height: 1.45; resize: vertical; }

/* ТЕЛЕФОН: поля во всю ширину, иначе кнопки уезжают за край */
@media (max-width: 720px) {
  #sec-body .asst-row { flex-direction: column; align-items: stretch; }
  #sec-body .asst-row input[type="text"],
  #sec-body .asst-row select,
  #sec-body .asst-row button { width: 100%; }
}

/* Блок «Кто следит за объектами» (раздел «Рассылки отчётов»).
   Форма подписки живёт внутри карточки, поэтому ширину полей задаём здесь: без этого
   селект «Кому» растягивался во всю карточку, а поиск объекта уезжал в столбик. */
.sub-for-box{ max-width:560px; margin-top:14px; }
.sf-who-row{ display:flex; align-items:center; gap:10px; margin-bottom:4px; }
/* Метки фиксированной ширины: иначе «Имя» и «Почта» дают разный старт поля и
   форма выглядит съехавшей (замечание Михаила 26.08.2026). */
.sf-who-row label{ flex:0 0 68px; color:var(--muted); }
.sf-who-row select, .sf-who-row input{ flex:1 1 auto; max-width:340px; background:var(--bg);
  border:1px solid var(--line); border-radius:8px; padding:7px 10px; color:var(--fg); }
.sub-for-box .obj-picker{ border-top:none; margin-top:6px; padding-top:0; }
.sub-for-box .side-hint{ margin-top:10px; }

/* Имя наблюдателя — кнопка, раскрывающая его подписки. Выглядит ссылкой, а не кнопкой:
   это навигация внутри карточки, а не действие. */
.watch-list li{ padding:4px 0; }
.link-like{ background:none; border:none; padding:0; font:inherit; color:var(--accent-text);
  cursor:pointer; border-bottom:1px dashed rgba(227,178,58,.5); }
.link-like:hover{ color:var(--fg); }
.watch-body{ margin:6px 0 2px 14px; }
.watch-body .sub-row{ padding:6px 0; border-top:1px solid var(--line); }
/* Получатели без аккаунта — отдельный подраздел внутри карточки «Кто следит за объектами»:
   это не сотрудники, и мешать их в один список нельзя. */
.sub-sect{ margin-top:14px; padding-top:12px; border-top:1px solid var(--line); }
.card-subtitle{ font-size:14px; font-weight:600; margin-bottom:8px; color:var(--muted); }
.watch-list .btn-tiny{ margin-left:8px; }

/* ── Карточка объекта УДК ──────────────────────────────────────────────────────────
   Мнемосхема убрана 31.08.2026: нарисовать её одинаково честно для однофазного объекта,
   трёхфазного и платы 1.4 без модуля реле не вышло — картинка проигрывала тексту.
   Осталось главное: показания ввода крупно, состояние таблицей, управление рядом. */
.warn{ color:var(--accent-text); }        /* использовался в разметке, но не был описан */

.udk-card{ background:var(--bg-2); border:1px solid var(--line); border-radius:var(--radius);
  padding:14px 18px 16px; margin:8px 0 16px; }
/* Имя — заголовок карточки: по центру и заметно крупнее, чтобы взгляд цеплялся за него. */
.udk-card-name{ text-align:center; margin-bottom:2px; }
.udk-card-name .link-like{ font-size:17px; font-weight:650; color:var(--fg);
  border-bottom-color:rgba(227,178,58,.35); }
.udk-card-name .link-like:hover{ color:var(--accent-text); }
.udk-card-sub{ text-align:center; font-size:12.5px; color:var(--muted); margin-bottom:14px; }
.udk-card-sub .link-like{ font-size:12.5px; }

/* Показания ввода — плитками, в ряд: это те числа, на которые смотрят первыми.
   auto-fit сам решает, сколько поместится, поэтому на телефоне они складываются в две
   колонки без отдельного правила. */
.udk-tiles{ display:grid; gap:10px; margin-bottom:10px;
  grid-template-columns:repeat(auto-fit, minmax(120px, 1fr)); }
.udk-tile{ background:var(--bg-3); border:1px solid var(--line); border-radius:10px;
  padding:10px 12px; text-align:center; }
.udk-tile > b{ display:block; font-size:19px; font-weight:650; line-height:1.2;
  font-variant-numeric:tabular-nums; }
.udk-tile > span{ display:block; margin-top:3px; font-size:11.5px; color:var(--muted); }
.udk-meter-note{ font-size:12.5px; margin:0 0 14px; }

/* Состояние — в две колонки: одним столбцом это была длинная лестница на пол-экрана. */
/* Столбцы состояния собираются в разметке ЯВНО (см. udkCard): каждый отвечает на свой
   вопрос, и автопоток слева направо перемешал бы смысл, стоит одной строке пропасть. */
/* Одна сетка на все показатели: ячейки одного ряда выравниваются между собой, и перенос
   длинной подписи в одной колонке больше не сдвигает соседние (см. udkStateCells). */
.udk-state{ display:grid; gap:0 26px; grid-template-columns:repeat(auto-fit, minmax(240px, 1fr));
  align-items:stretch; }
.udk-kv-none{ border-bottom-color:transparent; }
/* Метка фиксированной ширины, значение сразу за ней: разгон «Статус ……… на связи» во всю
   ширину заставлял глаз перепрыгивать пустоту (замечание Михаила 31.08.2026). */
.udk-kv{ display:flex; gap:10px; padding:5px 0;
  border-bottom:1px solid var(--line); font-size:13px; line-height:1.35; }
.udk-kv > span{ color:var(--muted); flex:0 0 104px; }
.udk-kv > b{ min-width:0; overflow-wrap:anywhere; font-weight:600; }

/* Одиночная пара («Прошивка 2.12.3») тянуться по ширине не должна. */
.udk-pair{ display:inline-flex; align-items:baseline; gap:8px; font-size:13.5px; }
.udk-pair > span{ color:var(--muted); }
.udk-pair > b{ font-weight:650; }
.udk-pair > i{ font-style:normal; font-size:12px; }

.udk-ctrl{ display:flex; flex-wrap:wrap; gap:8px; align-items:center;
  justify-content:center;   /* кнопки по центру карточки, а не прижаты к левому краю */
  margin-top:14px; padding-top:14px; border-top:1px solid var(--line); }
.udk-ctrl .side-hint{ flex:1 0 100%; margin:0; }
.udk-ctrl-result:empty{ display:none; }
.udk-ctrl-result{ flex:1 0 100%; margin-top:8px; padding:8px 12px; border-radius:8px;
  font-size:13px; white-space:pre-wrap; background:var(--bg-3); }
.udk-ok{ border-left:3px solid var(--ok); }

.udk-busy{ display:flex; flex-wrap:wrap; align-items:baseline; gap:8px;
  background:var(--bg-3); border-left:3px solid var(--gold); border-radius:8px;
  padding:8px 12px; margin-bottom:12px; font-size:13.5px; }
.udk-busy .side-hint{ flex:1 0 100%; margin:2px 0 0; }
.spin{ width:12px; height:12px; border:2px solid var(--line); border-top-color:var(--gold);
  border-radius:50%; display:inline-block; animation:udk-spin 1s linear infinite; }
@keyframes udk-spin{ to{ transform:rotate(360deg); } }
/* Уважаем системную настройку: бесконечное вращение мешает части людей. */
@media (prefers-reduced-motion: reduce){ .spin{ animation:none; } }

.udk-failed{ background:var(--bg-3); border-left:3px solid var(--danger);
  border-radius:8px; padding:8px 12px; margin-bottom:12px;
  font-size:13px; color:var(--danger-text); }

/* Ход длительной операции. Три состояния различаются ЦВЕТОМ полосы, а не только словом:
   идёт (золотая), получилось (зелёная), сорвалось (красная) — человек, нажавший кнопку,
   должен понять исход раньше, чем дочитает текст. */
.udk-op{ background:var(--bg-3); border-left:3px solid var(--line); border-radius:8px;
  padding:9px 12px; margin-bottom:12px; font-size:13.5px; }
.udk-op.busy{ border-left-color:var(--gold); }
.udk-op.done{ border-left-color:var(--ok); }
.udk-op.failed{ border-left-color:var(--danger); }
.udk-op-head{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.udk-op-text{ margin-top:4px; color:var(--fg); }
.udk-op.failed .udk-op-text{ color:var(--danger-text); }
.udk-op .side-hint{ margin:4px 0 0; }
/* Шаги: пройденное приглушено, текущее выделено. Так видно не только «что сейчас»,
   но и сколько ещё осталось. */
.udk-steps{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-top:6px;
  font-size:12px; color:var(--muted); }
.udk-step.done{ color:var(--muted); text-decoration:line-through; }
.udk-step.now{ color:var(--fg); font-weight:650; }
.udk-step-sep{ color:var(--line); }
.udk-run{ display:inline-flex; align-items:center; gap:6px; }
/* Мини-статус под версией: там, где написано «доступна 2.12.4», — в том же месте, где
   нажали кнопку обновления. */
.udk-op-mini{ display:flex; align-items:center; flex-wrap:wrap; gap:8px; margin:6px 0 2px;
  padding:6px 10px; border-radius:7px; background:var(--bg-3); font-size:13px; }
.udk-op-mini.busy{ border-left:3px solid var(--gold); }
.udk-op-mini.done{ border-left:3px solid var(--ok); }
.udk-op-mini.failed{ border-left:3px solid var(--danger); color:var(--danger-text); }

/* Заголовок сортировки в списке правок: не строка журнала, а управление списком. */
/* Лента последних правок: время слева, подробности мелким под строкой. Это не таблица
   со счётчиками, а хронология — «кто и что сделал» читается сверху вниз. */
.feed li{ display:block; padding:7px 0; }
.feed b{ font-weight:600; }
.sort-head{ font-size:12.5px; color:var(--muted); cursor:default; }
.sort-head .link-like{ font-size:12.5px; }

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

   Раскладка — СЕТКА, а не растянутый flex: у названия своя колонка по ширине самого
   длинного, поле и кнопка идут сразу за ним. Растяжка `flex:1 1` отбрасывала поле к
   правому краю, и между настройкой и её значением зияла пустая полоса. */
.udk-settings{ margin-top:12px; }
.udk-setting{ padding:9px 0; border-top:1px solid var(--line); }
.udk-set-head{ display:grid; grid-template-columns:minmax(0,max-content) auto;
  align-items:center; gap:8px 12px; }
.udk-set-title{ font-size:13.5px; }
.udk-set-input{ display:inline-flex; align-items:center; gap:6px; }
/* Значение по центру поля: числа тут короткие, у правого края они выглядели прижатыми. */
.udk-set-num{ width:74px; text-align:center; }
.udk-unit{ min-width:26px; }
.udk-setting .side-hint{ margin:3px 0 0; }
/* Кнопка — ПОД пояснением и одного размера у всех: человек должен дочитать, чем грозит
   правка, прежде чем до неё дотянуться. */
.udk-setting .udk-set-save, .udk-main-save, .udk-ch-save{ margin-top:8px; min-width:120px; }
/* Опасные настройки: красная рамка и красное название — видно, не читая подписи. */
.udk-setting.danger{ border-left:3px solid var(--danger); padding-left:10px; }
.udk-setting.danger .udk-set-title{ color:var(--danger-text); font-weight:600; }

/* Каналы реле — выбор из тех выходов, что есть у модуля. Занятый «безопасным» каналом
   выход гаснет: интерфейс не должен позволять собрать запрещённое сочетание. */
.udk-chs{ display:flex; flex-wrap:wrap; gap:6px; margin-top:6px; }
.udk-ch{ display:inline-flex; align-items:center; gap:5px; padding:4px 9px;
  border:1px solid var(--line); border-radius:7px; font-size:13px; cursor:pointer; }
.udk-ch.off{ opacity:.4; cursor:not-allowed; }
.udk-ch-err{ margin-top:8px; }
/* Режим расписания: три взаимоисключающих варианта карточками. Выбранный подсвечен —
   человек должен видеть, по какому графику живёт объект, не вчитываясь в подписи. */
.udk-modes{ display:flex; flex-direction:column; gap:6px; margin-top:8px; }
.udk-mode{ display:flex; align-items:flex-start; gap:9px; padding:8px 10px;
  border:1px solid var(--line); border-radius:8px; cursor:pointer; }
.udk-mode.on{ border-color:var(--gold); background:var(--bg-3); }
.udk-mode b{ display:block; font-size:13.5px; font-weight:600; }
.udk-mode i{ display:block; font-style:normal; font-size:12.5px; color:var(--muted);
  margin-top:2px; }
/* «Команда ушла» и «контроллер подтвердил» — одна строка на оба случая: человеку важно
   не то, какое это событие, а что происходит с его правкой прямо сейчас. */
.udk-applied{ margin-top:6px; font-size:12.5px; color:var(--accent-text); }
/* Заводские группы свёрнуты: прятать совсем нельзя, выкладывать на виду — тоже. */
.udk-group{ margin-top:12px; padding-top:10px; border-top:1px solid var(--line); }
.udk-group-box{ margin-top:8px; }
.udk-group-warn{ background:var(--bg-3); border-left:3px solid var(--gold);
  border-radius:8px; padding:8px 12px; font-size:12.5px; color:var(--muted); }
.udk-manual{ margin-top:12px; padding-top:10px; border-top:1px solid var(--line); }
.udk-manual-box, .udk-ver-box{ margin-top:8px; }


/* ⚠️ flex-direction ОБЯЗАТЕЛЕН: класс висит на одном элементе с `.edit-box`, а тот задаёт
   `flex-direction: column`. Не переопределив направление, две колонки выстраивались одна
   под другой, и справа зияла пустота во всю ширину (31.08.2026). */
.udk-two-col{ display:flex; flex-direction:row; flex-wrap:wrap; gap:16px 26px;
  align-items:flex-start; }
.udk-col{ flex:1 1 330px; min-width:0; }
.udk-col-head{ font-size:12.5px; font-weight:600; letter-spacing:.05em; text-transform:uppercase;
  color:var(--muted); margin-bottom:10px; padding-bottom:6px; border-bottom:1px solid var(--line); }
/* Блоки правки стояли впритык и сливались в стену. Разводим их воздухом. */
.udk-col .edit-block{ margin-bottom:16px; }
.udk-col .edit-block:last-child{ margin-bottom:0; }

/* Строка часов: текст и кнопка на одной линии. По умолчанию `.form-row` тянет детей по
   высоте (align-items:stretch), и кнопка рядом с однострочным текстом вставала криво. */
.udk-clock-row{ align-items:center; }
.udk-mod{ margin-top:12px; padding-top:10px; border-top:1px solid var(--line); font-size:13px; }
.udk-mod-head{ margin-bottom:4px; }
.udk-serial{ font-weight:600; color:var(--fg); font-variant-numeric:tabular-nums; }
/* Кнопка и подпись «доступна 1.26.5» — на одной базовой линии, а не вразнобой. */
.udk-upd{ display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin-top:8px; }

/* Опасное — отдельной полосой внизу: замена платы и удаление не должны стоять в одном
   ряду с переименованием, их нажимают по ошибке. */
.udk-danger{ margin-top:18px; border-top:2px solid var(--line); padding-top:14px; }

@media (max-width: 720px){
  .udk-two-col{ display:block; }        /* телефон: колонки складываются в одну */
  .udk-col + .udk-col{ margin-top:20px; }
  .udk-ctrl .btn-tiny{ flex:1 1 auto; }
  .udk-kv > span{ flex:0 0 92px; }
}

/* Настройки контроллера на телефоне: название занимает всю ширину, поле и «Применить»
   встают под ним — в одну строку они не помещаются. Держим В КОНЦЕ файла: при равной
   специфичности побеждает то, что ниже. */
@media (max-width: 720px){
  .udk-set-head{ grid-template-columns:1fr auto; }
  .udk-set-title{ grid-column:1 / -1; }
}
