/* ============================================================================
   up-bull admin — дизайн-система панели. Без сборки: обычный CSS, никаких CDN.
   Слои: 1) токены → 2) база → 3) каркас → 4) компоненты → 5) экраны.
   Правило: цвета/отступы/радиусы берём ТОЛЬКО из токенов — иначе экраны
   расходятся по виду (так и появился «черновик» с разнобоем инлайн-стилей).
   ========================================================================== */

/* ── 1. Токены ───────────────────────────────────────────────────────────── */
:root{
  /* Поверхности: 4 уровня глубины. Раньше фон и карточка почти совпадали —
     интерфейс выглядел плоской простынёй. Теперь карточка реально «лежит» на фоне. */
  --bg:#0B0D11; --panel:#0F1217; --card:#15191F; --card2:#1A1F27; --sunken:#0D1014;
  --line:#232830; --line2:#2C333D; --lineSoft:#1B2027;

  --ink:#F4F6F8; --ink2:#DCE1E8; --sub:#98A1AD; --dim:#697180;

  --accent:#FF6B4A; --accent2:#FF8A6B; --accentInk:#1A0904;
  --accentBg:#2A1611; --accentLine:#4A2418; --accentSoft:rgba(255,107,74,.12);

  --g:#4ADE80; --y:#F0B849; --r:#F87171; --b:#6296F5; --p:#A78BFA;
  --gbg:#122019; --ybg:#231C0E; --rbg:#241315; --bbg:#131E33; --pbg:#1C1830;
  --gline:#1F3A2C; --yline:#3D3117; --rline:#42232A; --bline:#22355C;
  --bord:#2C333D;

  /* Шкала отступов — вместо произвольных 5/7/9/11px по всему коду. */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:24px; --s7:32px;
  --r-sm:8px; --r-md:10px; --r-lg:14px; --r-xl:18px; --r-pill:999px;

  --sh-1:0 1px 2px rgba(0,0,0,.32);
  --sh-2:0 4px 14px rgba(0,0,0,.34);
  --sh-3:0 18px 48px rgba(0,0,0,.52);
  --ring:0 0 0 3px rgba(255,107,74,.24);
  --t:.14s cubic-bezier(.4,0,.2,1);
  --mono:'IBM Plex Mono',ui-monospace,monospace;
}

/* ── 2. База ─────────────────────────────────────────────────────────────── */
*{box-sizing:border-box}
html,body{margin:0;padding:0;height:100%;background:var(--bg);color:var(--ink2);
  font-family:'Geologica',system-ui,sans-serif;font-size:14px;line-height:1.45;
  -webkit-font-smoothing:antialiased}
a{color:var(--accent2);text-decoration:none;transition:color var(--t)} a:hover{color:var(--accent)}
input,select,textarea,button{font-family:inherit;font-size:inherit}
input::placeholder,textarea::placeholder{color:var(--dim)}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:#282E37;border-radius:var(--r-pill);border:2px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:#39414D;background-clip:content-box}
:focus-visible{outline:none;box-shadow:var(--ring);border-radius:var(--r-sm)}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
.mono{font-family:var(--mono);font-feature-settings:'tnum' 1}
.muted{color:var(--dim)} .sub{color:var(--sub)}
.h2{font-size:13.5px;font-weight:600;color:var(--ink);margin-bottom:var(--s3);
  display:flex;align-items:center;gap:var(--s2);letter-spacing:-.01em}

/* ── 3. Каркас ───────────────────────────────────────────────────────────── */
.app{display:flex;height:100vh;min-height:600px;overflow:hidden}

.side{width:236px;flex-shrink:0;display:flex;flex-direction:column;
  border-right:1px solid var(--line);background:var(--panel);color:var(--sub);overflow-y:auto}
.brand{display:flex;align-items:center;gap:var(--s3);padding:var(--s5) var(--s4) var(--s4)}
.logo{width:32px;height:32px;border-radius:var(--r-md);background:linear-gradient(140deg,var(--accent2),var(--accent));
  display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-weight:600;
  font-size:13px;color:var(--accentInk);box-shadow:var(--sh-1)}
.navgrp{font-size:10px;font-weight:600;letter-spacing:.14em;color:var(--dim);
  padding:var(--s5) var(--s5) var(--s2);text-transform:uppercase}
.navgrp:first-of-type{padding-top:var(--s2)}
.nav{position:relative;display:flex;align-items:center;gap:var(--s3);margin:1px var(--s3);
  padding:var(--s2) var(--s3);border-radius:var(--r-sm);cursor:pointer;font-size:13.5px;
  font-weight:500;color:var(--sub);transition:background var(--t),color var(--t)}
.nav:hover{background:#181C23;color:var(--ink2)}
.nav.on{background:var(--accentSoft);color:var(--accent2)}
.nav.on::before{content:'';position:absolute;left:-6px;top:50%;transform:translateY(-50%);
  width:3px;height:16px;border-radius:0 3px 3px 0;background:var(--accent)}
.nav .cnt{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--dim)}
.nav .badge{margin-left:auto;font-family:var(--mono);font-size:10.5px;font-weight:600;
  background:var(--accent);color:var(--accentInk);border-radius:var(--r-pill);padding:1px 7px;display:none}
.me{margin-top:auto;padding:var(--s3)}
.mecard{display:flex;align-items:center;gap:var(--s3);padding:var(--s3);border-radius:var(--r-md);
  background:var(--card);border:1px solid var(--line)}
.avatar{width:30px;height:30px;border-radius:50%;background:#252C36;display:flex;align-items:center;
  justify-content:center;font-size:11px;font-weight:600;color:#B9C2CE;flex-shrink:0}

.main{flex:1;display:flex;flex-direction:column;min-width:0;min-height:0}
.top{height:58px;flex-shrink:0;display:flex;align-items:center;gap:var(--s4);padding:0 var(--s6);
  border-bottom:1px solid var(--line);background:var(--panel)}
.title{font-size:16px;font-weight:600;color:var(--ink);letter-spacing:-.01em}
.topmeta{margin-left:auto;display:flex;align-items:center;gap:var(--s3);min-width:0}
/* Кнопка меню и затемнение — только для узкого экрана (см. слой 6). */
.navbtn{display:none;width:34px;height:34px;margin-left:-6px;flex-shrink:0;border-radius:var(--r-sm);
  align-items:center;justify-content:center;font-size:17px;color:var(--sub);cursor:pointer;
  transition:background var(--t),color var(--t)}
.navbtn:hover{background:#181C23;color:var(--ink2)}
.navscrim{display:none}
.core{display:flex;align-items:center;gap:var(--s2);padding:5px var(--s3);border-radius:var(--r-pill);
  background:var(--gbg);border:1px solid var(--gline);font-size:12px;color:var(--g);white-space:nowrap}
.dot{width:7px;height:7px;border-radius:50%;display:inline-block;flex-shrink:0}
.content{flex:1;overflow-y:auto;padding:var(--s6);min-height:0}
/* Экран во всю высоту без внешней прокрутки (чат): панели скроллятся сами. */
.content.flush{overflow:hidden;display:flex;flex-direction:column;padding-bottom:var(--s6)}

/* ── 4. Компоненты ───────────────────────────────────────────────────────── */
/* Карточки */
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s5);box-shadow:var(--sh-1)}
.grid4{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s4);margin-bottom:var(--s4)}
@media (max-width:1240px){.grid4{grid-template-columns:repeat(2,minmax(0,1fr))}}
/* Парные блоки. Классом, а не инлайном, — иначе на узком экране их не свернуть. */
.g2{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4)}
.g2w{display:grid;grid-template-columns:1.4fr 1fr;gap:var(--s4)}
/* Строка «Здоровья системы»: название · состояние · чем питает · возраст. */
.hrow{display:grid;grid-template-columns:170px 120px 1fr auto;gap:10px;align-items:center;
  padding:8px 0;border-top:1px solid var(--line);font-size:12.5px}

/* KPI-плитка: подпись → значение → расшифровка. Источник данных виден в чипе. */
.kpi .l{font-size:12px;color:var(--sub);margin-bottom:var(--s2);display:flex;
  align-items:center;gap:var(--s2);flex-wrap:wrap}
.kpi .src{font-family:var(--mono);font-size:9.5px;color:var(--dim);background:var(--sunken);
  border:1px solid var(--line);border-radius:5px;padding:1px 5px;cursor:help}
.kpi .v{font-size:27px;font-weight:600;color:var(--ink);letter-spacing:-.02em;line-height:1.15}
.kpi .s{font-size:12px;margin-top:5px;color:var(--sub)}

/* Кнопки */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:9px var(--s4);
  border-radius:var(--r-md);background:var(--accent);color:var(--accentInk);font-weight:600;
  font-size:13px;cursor:pointer;border:1px solid transparent;white-space:nowrap;
  transition:background var(--t),border-color var(--t),color var(--t),transform var(--t)}
.btn:hover{background:var(--accent2)}
.btn:active{transform:translateY(1px)}
.btn.sm{padding:5px 10px;font-size:12px;font-weight:500;border-radius:var(--r-sm);min-width:30px}
.btn.gh{background:transparent;border-color:var(--line2);color:var(--ink2)}
.btn.gh:hover{background:var(--card2);border-color:#39414D;color:var(--ink2)}
.btn.dz{background:var(--rbg);border-color:var(--rline);color:var(--r)}
.btn.dz:hover{background:#2E181B;border-color:#5A2D34}
.ghost{padding:9px var(--s4);border-radius:var(--r-md);background:transparent;
  border:1px solid var(--line2);color:var(--sub);cursor:pointer;font-size:13px;
  transition:background var(--t),color var(--t)}
.ghost:hover{background:var(--card2);color:var(--ink2)}
.arrowbtn{cursor:pointer;user-select:none;padding:2px 7px;border-radius:6px;background:var(--card2);
  color:var(--sub);font-size:11px;line-height:1.4;transition:background var(--t),color var(--t)}
.arrowbtn:hover{background:#333B46;color:var(--ink)}

.rowbar{display:flex;align-items:center;gap:var(--s3);margin-bottom:var(--s5);flex-wrap:wrap}

/* Поля ввода */
.inp{padding:9px var(--s3);border-radius:var(--r-md);border:1px solid var(--line2);
  background:var(--sunken);color:var(--ink2);font-size:13px;outline:none;
  transition:border-color var(--t),box-shadow var(--t)}
.inp:hover{border-color:#39414D}
.inp:focus{border-color:var(--accent);box-shadow:var(--ring)}
textarea.inp{line-height:1.5}
/* Селект: свой указатель — системный в тёмной теме выглядит инородно. */
select.inp{appearance:none;-webkit-appearance:none;padding-right:30px;cursor:pointer;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%2398A1AD' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 11px center}
select.inp option{background:var(--card);color:var(--ink2)}

/* Чекбоксы: свои. Системные (браузерные) и делали ощущение недоделанной формы. */
.chk{display:flex;align-items:center;gap:var(--s2);font-size:12.5px;color:var(--ink2);
  cursor:pointer;padding:5px 6px;border-radius:var(--r-sm);transition:background var(--t)}
.chk:hover{background:var(--card2)}
.chk input[type=checkbox],.selrow input[type=checkbox]{appearance:none;-webkit-appearance:none;
  width:17px;height:17px;flex-shrink:0;margin:0;border:1.5px solid var(--line2);border-radius:5px;
  background:var(--sunken);cursor:pointer;position:relative;
  transition:background var(--t),border-color var(--t)}
.chk input[type=checkbox]:hover,.selrow input[type=checkbox]:hover{border-color:var(--accent2)}
.chk input[type=checkbox]:checked,.selrow input[type=checkbox]:checked{background:var(--accent);border-color:var(--accent)}
.chk input[type=checkbox]:checked::after,.selrow input[type=checkbox]:checked::after{
  content:'';position:absolute;left:5px;top:1.5px;width:4px;height:9px;
  border:solid var(--accentInk);border-width:0 2px 2px 0;transform:rotate(43deg)}
.chk input[type=checkbox]:focus-visible,.selrow input[type=checkbox]:focus-visible{box-shadow:var(--ring)}
.chk input[type=checkbox]:disabled{opacity:.35;cursor:not-allowed}
.checks{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:2px var(--s2);
  padding:var(--s2);border:1px solid var(--line2);border-radius:var(--r-md);background:var(--sunken)}
.selrow{display:flex;align-items:center;gap:var(--s2)}

/* Тумблер */
.toggle{width:36px;height:20px;border-radius:var(--r-pill);position:relative;flex-shrink:0;
  cursor:pointer;transition:background var(--t)}
.toggle>i{width:16px;height:16px;border-radius:50%;background:#fff;position:absolute;top:2px;
  transition:left var(--t);box-shadow:var(--sh-1)}

/* Пилюли и чипы */
.pill{padding:3px 9px;border-radius:var(--r-pill);font-size:11.5px;font-weight:500;
  display:inline-block;white-space:nowrap;line-height:1.5}
.platchips{display:flex;gap:var(--s1);flex-wrap:wrap}
.platchip{font-size:10.5px;padding:2px 7px;border-radius:6px;background:var(--card2);color:var(--sub)}
.livebadge{font-size:10px;font-weight:600;padding:2px 7px;border-radius:6px;white-space:nowrap}
.livebadge.on{background:var(--gbg);color:var(--g)} .livebadge.off{background:var(--ybg);color:var(--y)}

/* Таблицы */
.tbl{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  overflow:hidden;box-shadow:var(--sh-1)}
table{width:100%;border-collapse:collapse}
.thead{display:grid;gap:var(--s3);padding:var(--s3) var(--s5);border-bottom:1px solid var(--line);
  font-size:10.5px;font-weight:600;letter-spacing:.09em;color:var(--dim);
  text-transform:uppercase;background:var(--panel)}
/* Липкая шапка таблицы: длинные матрицы (Блокировки) без неё превращались в столбцы безымянных чипов */
.thead.stick{position:sticky;top:0;z-index:3;background:var(--card);box-shadow:0 1px 0 var(--line)}
.vstrip{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:0 0 10px}

.trow{display:grid;gap:var(--s3);padding:var(--s3) var(--s5);border-bottom:1px solid var(--lineSoft);
  align-items:center;transition:background var(--t)}
.trow:last-child{border-bottom:none}
.trow:hover{background:var(--card2)}
.bar{height:5px;border-radius:3px;background:#242A33;overflow:hidden}
.bar>i{display:block;height:100%;border-radius:3px;transition:width var(--t)}
.empty{padding:var(--s7) var(--s5);text-align:center;color:var(--dim);font-size:13px}
/* Подвал таблицы («Всего: N») — отделён линией, а не висит как лишняя строка. */
.tfoot{padding:var(--s3) var(--s5);font-size:12px;color:var(--dim);border-top:1px solid var(--line);background:var(--panel)}

/* Модалка: шапка с крестиком, прокручиваемое тело, закреплённый низ. */
.modal{position:fixed;inset:0;background:rgba(6,8,11,.72);backdrop-filter:blur(3px);
  display:flex;align-items:center;justify-content:center;z-index:40;padding:var(--s5);
  animation:mfade .16s ease}
@keyframes mfade{from{opacity:0}to{opacity:1}}
.modalbox{width:520px;max-width:100%;max-height:88vh;display:flex;flex-direction:column;
  background:var(--card);border:1px solid var(--line2);border-radius:var(--r-xl);
  box-shadow:var(--sh-3);animation:mrise .18s cubic-bezier(.2,.8,.3,1);overflow:hidden}
@keyframes mrise{from{opacity:0;transform:translateY(10px) scale(.99)}to{opacity:1;transform:none}}
.modalbox.wide{width:720px}
.modalhead{display:flex;align-items:center;gap:var(--s3);padding:var(--s5) var(--s5) var(--s4);
  border-bottom:1px solid var(--line)}
.modalbox h3{margin:0;font-size:15.5px;font-weight:600;color:var(--ink);letter-spacing:-.01em}
.mclose{margin-left:auto;width:28px;height:28px;border-radius:var(--r-sm);display:flex;
  align-items:center;justify-content:center;cursor:pointer;color:var(--dim);font-size:17px;
  line-height:1;transition:background var(--t),color var(--t)}
.mclose:hover{background:var(--card2);color:var(--ink)}
.modalbody{padding:var(--s5);overflow-y:auto;flex:1;min-height:0}
.modalfoot{display:flex;gap:var(--s3);justify-content:flex-end;align-items:center;
  padding:var(--s4) var(--s5);border-top:1px solid var(--line);background:var(--panel)}
.modactions{display:flex;gap:var(--s3);justify-content:flex-end;margin-top:var(--s2)}
/* Поля: сетка в две колонки — узкие поля не растягиваются на всю ширину. */
.mfields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s2) var(--s4)}
.field{margin-bottom:var(--s3);grid-column:span 2;min-width:0}
.field.half{grid-column:span 1}
.field.mfull{grid-column:span 2}

/* Справка в диалоге: что панель УЖЕ знает об объекте. Раньше карточку абонента правили
   вслепую — ни расхода, ни uuid, ни числа устройств в диалоге не было, и за каждым таким
   фактом приходилось закрывать окно и искать строку в списке. */
.facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--s3);
  background:var(--sunken);border:1px solid var(--lineSoft);border-radius:var(--r-md);
  padding:var(--s3) var(--s4)}
.facts>div{min-width:0}
.facts .k{font-size:10.5px;color:var(--dim);letter-spacing:.3px;text-transform:uppercase}
.facts .v{font-size:12.5px;color:var(--ink2);margin-top:2px;overflow-wrap:anywhere}
.facts .v.mono{font-size:11.5px}
.copyable{cursor:pointer;border-bottom:1px dashed var(--line2)}
.copyable:hover{color:var(--accent2);border-bottom-color:var(--accent)}

/* Ускорители: продлить на N дней, не набирая дату руками. */
.quick{display:flex;flex-wrap:wrap;align-items:center;gap:6px;font-size:12px;color:var(--sub)}
.quick>span[data-ext]{padding:4px 10px;border:1px solid var(--line2);border-radius:var(--r-pill);
  color:var(--ink2);cursor:pointer;transition:background var(--t),border-color var(--t)}
.quick>span[data-ext]:hover{background:var(--card2);border-color:#39414D}

/* Список устройств абонента. Своя сетка, а не .trow: у диалога другие колонки, и раньше
   она задавалась инлайном — на телефоне это схлопывало имя устройства. */
.drow{display:grid;grid-template-columns:minmax(0,2.2fr) .9fr .9fr .8fr auto;gap:var(--s3);
  align-items:center;padding:9px 2px;border-top:1px solid var(--lineSoft)}
.drow.dhead{border-top:0;font-size:10.5px;letter-spacing:.4px;color:var(--dim);padding-bottom:6px}
.drow.idle{opacity:.55}
.drow .dname{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:13px;color:var(--ink2)}
.dfoot{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;margin-top:var(--s3);
  padding-top:var(--s3);border-top:1px solid var(--line);font-size:12px;color:var(--sub)}
@media (max-width:640px){
  .drow{grid-template-columns:minmax(0,1fr) auto;row-gap:4px}
  .drow.dhead{display:none}          /* без колонок шапка бессмысленна */
  .drow>div:nth-child(2),.drow>div:nth-child(3),.drow>div:nth-child(4){grid-column:1;text-align:left}
  .drow>.btn{grid-row:1;grid-column:2}
}
.field label{display:block;font-size:11.5px;color:var(--sub);margin-bottom:5px;line-height:1.35}
.field .inp,.field select,.field textarea{width:100%}
/* Заголовок секции внутри формы — группирует поля вместо сплошной простыни. */
.fsec{grid-column:span 2;font-size:10.5px;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--dim);padding-top:var(--s3);margin-bottom:2px;
  border-top:1px solid var(--lineSoft)}
.fsec:first-child{border-top:none;padding-top:0}
.fhint{grid-column:span 2;font-size:11.5px;color:var(--dim);margin:-6px 0 var(--s3)}
.err{color:var(--r);font-size:12.5px;min-height:18px;margin-top:6px}

/* Вход */
.login{position:fixed;inset:0;background:radial-gradient(1000px 520px at 50% -10%,#161B22,var(--bg));
  display:flex;align-items:center;justify-content:center;z-index:50;padding:var(--s5)}
.loginbox{width:360px;max-width:100%;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-xl);padding:var(--s7) var(--s6);box-shadow:var(--sh-3)}
.loginbox h1{font-size:19px;margin:0 0 var(--s1);color:var(--ink);letter-spacing:-.01em}
.loginbox p{margin:0 0 var(--s5);color:var(--dim);font-size:13px}
.loginbox .inp{width:100%;margin-bottom:var(--s3)}
.loginbox .btn{width:100%;margin-top:var(--s1);padding:11px}

/* Тосты */
.toasts{position:fixed;right:var(--s5);bottom:var(--s5);display:flex;flex-direction:column;
  gap:var(--s2);z-index:60;max-width:380px}
.toast{padding:11px var(--s4);border-radius:var(--r-md);font-size:13px;background:var(--card2);
  border:1px solid var(--line2);color:var(--ink2);box-shadow:var(--sh-3);animation:tin .2s ease}
.toast.ok{border-color:var(--gline);background:var(--gbg);color:#B7F0CC}
.toast.err{border-color:var(--rline);background:var(--rbg);color:#FBCACA}
.toast.info{border-color:var(--bline);background:var(--bbg);color:#C6D8FA}
@keyframes tin{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* Индикатор живого обновления */
.livepill{display:flex;align-items:center;gap:6px;padding:5px var(--s3);border-radius:var(--r-pill);
  background:var(--card);border:1px solid var(--line2);font-size:11.5px;color:var(--sub);
  cursor:default;user-select:none;white-space:nowrap;transition:opacity var(--t)}
.livepill.off{opacity:.45}

/* ── 5. Экраны ───────────────────────────────────────────────────────────── */
/* Карточка ноды */
.ncard{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s5);position:relative;box-shadow:var(--sh-1);transition:border-color var(--t)}
.ncard:hover{border-color:var(--line2)}
.ncard .nhead{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s1);flex-wrap:wrap}
.ncard .nname{font-family:var(--mono);font-size:14px;font-weight:500;color:var(--ink)}
.ncard .nstat{margin-left:auto}
.nstats{display:flex;gap:var(--s5);margin-top:var(--s4);padding-top:var(--s3);
  border-top:1px solid var(--lineSoft);font-size:12px;flex-wrap:wrap}

/* Паспорт машины в карточке: строка «ОС · CPU · аптайм» под адресом. Держим её
   отдельным классом, а не инлайном, чтобы перенос на узком экране был один на всё. */
/* Адрес/версия/свежесть — списком сегментов, а не одной длинной строкой: в узкой
   карточке она переносилась посреди фразы («серт: не / проверялся»). */
.nmeta{display:flex;flex-wrap:wrap;gap:2px var(--s3);font-family:var(--mono);font-size:11.5px;
  color:var(--dim);margin-bottom:5px}
.nmeta>span{white-space:nowrap}
.nspec{display:flex;flex-wrap:wrap;gap:2px var(--s3);font-size:11.5px;color:var(--dim);
  margin-bottom:var(--s4)}
.nspec>span{white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}
.nspec b{font-weight:500;color:var(--sub)}
.nspec:empty{display:none}   /* нода без паспорта не должна оставлять пустой отступ */

/* Меню действий над нодой. Восемь кнопок в ряд ломались на две строки и превращали
   карточку в приборную панель. В ряду остаются два частых действия, остальные —
   под «⋯»: они разрушительные (рестарт ядра, перепровижен, удаление) и не должны
   быть в одном клике от мышки. */
.amenu{position:relative;margin-left:auto}
.amenu>.apop{position:absolute;right:0;bottom:calc(100% + 6px);z-index:30;min-width:200px;
  background:var(--card2);border:1px solid var(--line2);border-radius:var(--r-md);
  box-shadow:var(--sh-3);padding:5px;display:none}
.amenu.open>.apop{display:block}
.apop>div{padding:7px 10px;border-radius:var(--r-sm);font-size:12.5px;color:var(--ink2);
  cursor:pointer;white-space:nowrap;transition:background var(--t)}
.apop>div:hover{background:var(--card)}
.apop>div.dz{color:var(--r)}
.apop>hr{border:0;border-top:1px solid var(--lineSoft);margin:5px 2px}

/* Живые скорости интерфейса — самое подвижное число на карточке, поэтому даём ему
   собственную плашку, а не строчку в общем списке: глаз находит её мгновенно. */
.nspeed{display:flex;gap:var(--s2);margin-bottom:var(--s3)}
.nspeed>div{flex:1;background:var(--sunken);border:1px solid var(--lineSoft);
  border-radius:var(--r-sm);padding:7px var(--s3);min-width:0}
.nspeed .sl{font-size:10.5px;color:var(--dim);letter-spacing:.3px}
.nspeed .sv{font-family:var(--mono);font-size:13px;color:var(--ink2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* Полоса нагрузки: подпись — шкала — значение. Три колонки фиксированной ширины,
   иначе CPU/RAM/Диск/Квота расходятся по вертикали и читаются как разные таблицы. */
.meters{display:flex;flex-direction:column;gap:9px}
.meter{display:grid;grid-template-columns:42px 1fr auto;align-items:center;gap:10px;font-size:11.5px}
.meter>.ml{color:var(--dim)}
.meter>.mv{font-family:var(--mono);color:var(--sub);text-align:right;white-space:nowrap}

/* Паспорт сервера в диалоге: определения в две колонки. */
.speclist{display:grid;grid-template-columns:auto 1fr;gap:7px var(--s4);font-size:12.5px;
  align-items:baseline}
.speclist dt{color:var(--dim);white-space:nowrap}
.speclist dd{margin:0;color:var(--ink2);font-family:var(--mono);font-size:12px;
  overflow-wrap:anywhere}
.specgrid{display:grid;grid-template-columns:1fr 1fr;gap:var(--s5)}
.specgrid h4{margin:0 0 var(--s3);font-size:11px;letter-spacing:.5px;color:var(--dim);
  font-weight:600;text-transform:uppercase}
@media (max-width:640px){ .specgrid{grid-template-columns:1fr} .nspeed{flex-direction:column} }
.actbar{display:flex;gap:6px;flex-wrap:wrap;margin-top:var(--s4);padding-top:var(--s3);
  border-top:1px solid var(--lineSoft)}
.joblog{margin:var(--s2) 0 0;max-height:170px;overflow:auto;background:var(--sunken);
  border:1px solid var(--line);border-radius:var(--r-sm);padding:var(--s2) var(--s3);
  font-size:10.5px;line-height:1.55;color:var(--sub);white-space:pre-wrap;
  overflow-wrap:anywhere;font-family:var(--mono)}

/* Строка входа. Раньше сетка задавалась инлайном (4px 24px 24px 1fr auto auto) — а инлайн
   медиазапросом не перебить, и на телефоне колонка 1fr схлопывалась в НОЛЬ: имя входа и его
   адрес пропадали целиком, а кнопки уезжали за край карточки. Поэтому классом. */
.inbrow{display:grid;grid-template-columns:4px 24px 24px minmax(0,1fr) auto auto;gap:11px;
  align-items:center;padding:10px 14px 10px 0}
.inbrow.off{opacity:.62}
.inbrow>.ibar{align-self:stretch;border-radius:3px;min-height:40px}
.inbrow>.isel,.inbrow>.iord{justify-self:center}
.inbrow>.iord{display:flex;flex-direction:column;gap:2px}
.inbrow>.imain{min-width:0}
.inbrow .iname{font-size:14px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.inbrow .imeta{font-size:10.5px;color:var(--dim);margin-top:3px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.inbrow .iproto{padding:1px 6px;border-radius:5px;background:var(--accentBg);color:var(--accent2)}
.inbrow .istate{display:flex;align-items:center;gap:8px}
.inbrow .iacts{display:flex;gap:6px;padding-right:14px}
/* На широком экране обёртка «состояние + кнопки» прозрачна: обе половины остаются
   самостоятельными колонками сетки. На узком она становится второй строкой. */
.inbrow>.iside{display:contents}
@media (max-width:860px){
  .inbrow{grid-template-columns:4px 24px 24px minmax(0,1fr);row-gap:10px;padding-right:12px;
    grid-template-areas:"bar sel ord main" "bar side side side"}
  .inbrow>.ibar{grid-area:bar} .inbrow>.isel{grid-area:sel}
  .inbrow>.iord{grid-area:ord} .inbrow>.imain{grid-area:main}
  .inbrow>.iside{display:flex;grid-area:side;flex-wrap:wrap;align-items:center;
    gap:var(--s2);justify-content:space-between}
  .inbrow .imeta{white-space:normal;overflow:visible}   /* лучше две строки, чем обрезка */
  .inbrow .iacts{padding-right:0}
}

/* Превью списка серверов «как в приложении» */
.prevrow{display:flex;align-items:center;gap:var(--s2);padding:var(--s2) var(--s3);
  border-radius:var(--r-sm);background:var(--sunken);border:1px solid var(--lineSoft);margin-bottom:5px}
.grouphdr{font-size:10.5px;font-weight:600;color:var(--accent2);letter-spacing:.08em;
  text-transform:uppercase;margin:var(--s4) 0 6px}

/* ── Чат ─────────────────────────────────────────────────────────────────
   Ключевое: КАЖДАЯ панель скроллится сама, страница — никогда. Раньше
   огромный лог растягивал .msgs (flex-элемент без min-height:0 не сжимается
   меньше контента), из-за чего скроллилась вся страница и список диалогов
   уезжал за экран. min-height:0 по всей цепочке это чинит. */
.chatwrap{display:grid;grid-template-columns:minmax(280px,340px) 1fr;gap:var(--s4);
  flex:1;min-height:0;overflow:hidden}
/* Колонка диалогов: поле поиска закреплено над прокручиваемым списком. */
.threadcol{display:flex;flex-direction:column;gap:var(--s2);min-height:0;min-width:0}
.threadlist{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  overflow-y:auto;min-height:0;box-shadow:var(--sh-1)}
.threadcol>.threadlist{flex:1}
.thread{padding:var(--s3) var(--s4);border-bottom:1px solid var(--lineSoft);cursor:pointer;
  transition:background var(--t);border-left:2px solid transparent}
.thread:hover{background:var(--card2)}
.thread.on{background:var(--card2);border-left-color:var(--accent)}
.thread.unread{background:#171C24}
.thread.unread .prev{color:var(--ink2)}
.chatpane{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;box-shadow:var(--sh-1)}
.chathead{padding:var(--s3) var(--s5);border-bottom:1px solid var(--line);flex-shrink:0;background:var(--panel)}
.msgs{flex:1;min-height:0;overflow-y:auto;padding:var(--s5);display:flex;flex-direction:column;gap:var(--s3)}
.msg{max-width:min(74%,620px);padding:var(--s2) var(--s3);border-radius:var(--r-md);
  font-size:13px;line-height:1.5;min-width:0}
.msg.in{align-self:flex-start;background:var(--card2);border:1px solid var(--line)}
.msg.out{align-self:flex-end;background:var(--accentBg);border:1px solid var(--accentLine);color:#FFD9CC}
.msg .meta{font-size:10.5px;color:var(--dim);margin-top:6px;font-family:var(--mono)}
.msg .msgbody{white-space:pre-wrap;overflow-wrap:anywhere}
/* Диагностические логи из приложения: компактная карточка вместо простыни на весь экран. */
.msg.islog{max-width:min(88%,760px);background:var(--sunken);border:1px solid var(--line2)}
.logmsg>summary{cursor:pointer;font-size:12.5px;color:var(--sub);list-style:none;user-select:none;
  display:flex;align-items:center;gap:6px;transition:color var(--t)}
.logmsg>summary::-webkit-details-marker{display:none}
.logmsg>summary:hover{color:var(--ink2)}
.logmsg[open]>summary{margin-bottom:var(--s2);color:var(--ink2)}
.logmsg>pre{margin:0;max-height:300px;overflow:auto;background:#090B0E;border:1px solid var(--line);
  border-radius:var(--r-sm);padding:var(--s3);font-size:10.5px;line-height:1.55;color:var(--sub);
  white-space:pre-wrap;overflow-wrap:anywhere;font-family:var(--mono)}
.msg.pending{opacity:.55}
.msg.new{animation:msgin .3s ease}
@keyframes msgin{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.composer{display:flex;gap:var(--s2);padding:var(--s3) var(--s4);border-top:1px solid var(--line);
  flex-shrink:0;background:var(--panel)}
.composer .inp{flex:1}
.chatinfo{display:flex;flex-wrap:wrap;gap:5px var(--s4);margin-top:var(--s2);font-size:11.5px;color:var(--sub)}
.chatinfo b{color:var(--ink2);font-weight:500}
.relts{color:var(--dim);font-size:11px}

/* Вложения в чате поддержки: превью с фиксированной высотой, чтобы вертикальный
   скриншот телефона не растягивал ленту на весь экран. Оригинал — по клику. */
.msg .att{display:block;margin:0 0 6px;border-radius:var(--r-sm);overflow:hidden;line-height:0;
  border:1px solid var(--line);background:var(--sunken)}
.msg .att img{max-height:260px;max-width:100%;object-fit:contain;cursor:zoom-in}
.msg .att:hover{border-color:var(--accentLine)}
.composer .btn.ghost{padding:0 10px;font-size:15px}

/* Кликабельный счётчик устройств в строке абонента */
.devlink{cursor:pointer;border-bottom:1px dashed var(--line2)}
.devlink:hover{color:var(--accent);border-bottom-color:var(--accent)}

/* ── 6. Узкий экран ──────────────────────────────────────────────────────────
   Панелью пользуются с телефона, а вёрстка была рассчитана только на десктоп:
   боковое меню шириной 236px на экране 393px съедало 60% ширины, и контент
   уезжал за край. Здесь меню превращается в выдвижную панель, таблицы (у них
   инлайновые grid-колонки по 5–7 штук) получают собственную горизонтальную
   прокрутку вместо того, чтобы ломать страницу, а модалки — нижние шторки.
   ------------------------------------------------------------------------ */

/* Высота вьюпорта на мобильных браузерах «дышит» вместе с панелями браузера:
   100vh там больше видимой области, и низ экрана оказывается под адресной
   строкой. dvh считает по фактически видимому. Строкой ниже — фолбэк. */
.app{height:100vh;height:100dvh}

@media (max-width:900px){
  /* Меню — поверх контента, а не в потоке. .main занимает всю ширину сам:
     .side выпадает из потока, а у .main уже стоит flex:1. */
  .side{position:fixed;z-index:50;top:0;bottom:0;left:0;width:min(276px,80vw);
    transform:translateX(-100%);transition:transform var(--t);box-shadow:var(--sh-3)}
  .app.navopen .side{transform:none}
  .navscrim{display:block;position:fixed;inset:0;z-index:45;background:rgba(6,8,11,.62);
    opacity:0;pointer-events:none;transition:opacity var(--t)}
  .app.navopen .navscrim{opacity:1;pointer-events:auto}
  .navbtn{display:flex}
  /* На телефоне в альбомной ориентации высота ~390px: min-height:600px заставил бы
     страницу скроллиться целиком, а панели перестали бы скроллиться сами. */
  .app{min-height:0}

  .top{padding:0 var(--s3);gap:var(--s3)}
  .title{font-size:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .topmeta{gap:var(--s2)}
  /* Счётчик онлайна есть на дашборде — в тесной шапке он лишний. */
  .toponline{display:none}
  .core{padding:4px var(--s2);font-size:11px}
  .content{padding:var(--s3)}
  .content.flush{padding-bottom:var(--s3)}

  .g2,.g2w{grid-template-columns:1fr}
  /* Название и состояние в первой строке, «питает» и возраст — во второй. */
  .hrow{grid-template-columns:1fr auto;gap:2px var(--s3)}

  /* Колонки таблиц задаются инлайном в screens.js (по 5–7 fr-долей). Ужимать их
     до 393px нечитаемо, поэтому таблица скроллится вбок внутри своей рамки —
     страница при этом стоит на месте. */
  .tbl{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}
  .thead,.tbl .trow{min-width:680px}
  .ptbl .thead,.ptbl .trow{min-width:var(--ptbl-min,900px)}   /* матрица блокировок: столбец на точку замера */
  /* Подвал («Показано N из M») и «пусто» не уезжают вместе со строками: они
     прилипают к левому краю видимой части, иначе их приходится искать прокруткой. */
  .tfoot,.tbl .empty{position:sticky;left:0}

  /* Модалка — нижняя шторка: до верха экрана тянуться незачем, а большой палец
     достаёт до кнопок внизу. Поля в одну колонку. */
  .modal{align-items:flex-end;padding:0}
  .modalbox,.modalbox.wide{width:100%;max-width:100%;max-height:92dvh;
    border-radius:var(--r-xl) var(--r-xl) 0 0}
  .mfields{grid-template-columns:1fr}
  .field.half{grid-column:span 2}
  .modalfoot{padding-bottom:calc(var(--s4) + env(safe-area-inset-bottom,0px))}

  .toasts{left:var(--s3);right:var(--s3);bottom:var(--s3);max-width:none}

  /* Чат: список диалогов сверху со своей прокруткой, переписка под ним.
     Обе панели скроллятся сами — страница по-прежнему никогда. */
  .chatwrap{grid-template-columns:1fr;grid-template-rows:minmax(96px,30dvh) 1fr;gap:var(--s3)}
  .msgs{padding:var(--s3)}
  .msg{max-width:88%}
}

/* Телефон: KPI держим в два столбца — так на экран влезает вчетверо больше,
   чем колонкой карточек в полную ширину. Значение и подпись ужимаем под ширину. */
@media (max-width:640px){
  .kpi .v{font-size:22px}
  .kpi .l{font-size:11.5px;gap:5px}
  .kpi .s{font-size:11px}
  .grid4{gap:var(--s3);margin-bottom:var(--s3)}
  .card{padding:var(--s4)}
}
/* Совсем узкие экраны (примерно iPhone SE и уже) — там два столбца уже не читаются. */
@media (max-width:359px){
  .grid4{grid-template-columns:1fr}
  .checks{grid-template-columns:1fr}
  .loginbox{padding:var(--s5)}
}
