/* ============================================================================
   Backup Portal — "Operations Deck"
   Стримано-індустріальний термінальний control-plane. Темна шарувата база,
   blueprint-сітка + зерно, IBM Plex Sans/Mono, один азуровий акцент,
   статуси як сигнальні лампи. Самохостимі шрифти (woff2, latin+cyrillic).
   ========================================================================== */

/* ---- Fonts (self-hosted) ------------------------------------------------- */
@font-face{font-family:'Plex';font-style:normal;font-weight:400;font-display:swap;src:url(/static/fonts/plex-sans-latin-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Plex';font-style:normal;font-weight:400;font-display:swap;src:url(/static/fonts/plex-sans-cyr-400.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:'Plex';font-style:normal;font-weight:600;font-display:swap;src:url(/static/fonts/plex-sans-latin-600.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+2074,U+20AC,U+2122,U+2212,U+FEFF,U+FFFD}
@font-face{font-family:'Plex';font-style:normal;font-weight:600;font-display:swap;src:url(/static/fonts/plex-sans-cyr-600.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:'Plex';font-style:normal;font-weight:700;font-display:swap;src:url(/static/fonts/plex-sans-latin-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+2074,U+20AC,U+2122,U+2212,U+FEFF,U+FFFD}
@font-face{font-family:'Plex';font-style:normal;font-weight:700;font-display:swap;src:url(/static/fonts/plex-sans-cyr-700.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:'PlexMono';font-style:normal;font-weight:400;font-display:swap;src:url(/static/fonts/plex-mono-latin-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+2074,U+20AC,U+2122,U+2212,U+FEFF,U+FFFD}
@font-face{font-family:'PlexMono';font-style:normal;font-weight:400;font-display:swap;src:url(/static/fonts/plex-mono-cyr-400.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:'PlexMono';font-style:normal;font-weight:600;font-display:swap;src:url(/static/fonts/plex-mono-latin-600.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+2000-206F,U+2074,U+20AC,U+2122,U+2212,U+FEFF,U+FFFD}
@font-face{font-family:'PlexMono';font-style:normal;font-weight:600;font-display:swap;src:url(/static/fonts/plex-mono-cyr-600.woff2) format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}

/* ---- Tokens -------------------------------------------------------------- */
:root{
  --bg:#0a0d11; --bg-2:#0d1115;
  --panel:#13181e; --panel-2:#191f27; --panel-3:#212934;
  --line:#232b35; --line-2:#2e3743; --line-glow:#3a4654;
  /* Втоплена поверхня для ВКЛАДЕНИХ блоків. --panel-3 тут не годиться:
     він скрізь означає ПІДВЕДЕНИЙ дрібний елемент (чип, кнопка, тег), і як
     заливка контейнера перевертав ієрархію — вкладене світилось яскравіше
     за те, що його містить, а приглушені підказки на ньому губили контраст. */
  --sunken:rgba(0,0,0,.22);
  /* --faint: ≥4.5:1 до --bg (WCAG AA для дрібних підказок .hint/nav-label) */
  --text:#e7edf3; --dim:#93a1b0; --faint:#78879a;
  --accent:#5cc8ff; --accent-2:#8fdcff; --accent-press:#34a8e6;
  --accent-tint:rgba(92,200,255,.12); --accent-line:rgba(92,200,255,.35);
  --ok:#46d588; --fail:#ff6363; --warn:#f5b14c; --info:var(--accent);
  /* --miss: «джоба немає на NAS» — окремий стан, не fail і не stale. */
  --miss:#b98cff;
  --ok-tint:rgba(70,213,136,.12); --fail-tint:rgba(255,99,99,.12); --warn-tint:rgba(245,177,76,.12);
  --miss-tint:rgba(185,140,255,.12);
  --r-card:12px; --r-ctl:8px; --r-pill:999px; --ctl-h:38px;
  --sh-1:0 1px 2px rgba(0,0,0,.4); --sh-2:0 8px 28px -8px rgba(0,0,0,.6);
  --sans:'Plex',-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif;
  --mono:'PlexMono',ui-monospace,'SF Mono',Menlo,Consolas,monospace;
  --sb:236px;
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; font-family:var(--sans); font-size:14px; line-height:1.55;
  color:var(--text); background:var(--bg);
  font-feature-settings:" cv05" 1,"ss01" 1; letter-spacing:.005em;
  background-image:
    radial-gradient(1100px 600px at 78% -8%, rgba(92,200,255,.06), transparent 60%),
    linear-gradient(rgba(124,143,168,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(124,143,168,.045) 1px, transparent 1px);
  background-size:auto, 46px 46px, 46px 46px;
  background-position:0 0, -1px -1px, -1px -1px;
}
/* тонке зерно поверх усього */
body::before{
  content:""; position:fixed; inset:0; pointer-events:none; z-index:9999; opacity:.025;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
a{color:var(--accent); text-decoration:none} a:hover{color:var(--accent-2)}
code,.mono{font-family:var(--mono); font-size:.92em}
code{background:var(--panel-3); padding:.06em .42em; border-radius:5px; color:var(--accent-2); border:1px solid var(--line)}
/* довгі ключі/токени: переносимо по символах, не ламаємо select/copy зайве */
.break{overflow-wrap:anywhere; word-break:normal}
ul.keylines{margin:4px 0 8px; padding-left:1.1em; font-family:var(--mono); font-size:.78rem} ul.keylines li{margin:2px 0; overflow-wrap:anywhere}
/* секрет (приватний ключ / токен) — моноблок, що легко виділити й скопіювати */
pre.secret{margin:.5rem 0; padding:10px 12px; background:var(--bg-2); border:1px solid var(--line-2); border-radius:var(--r-ctl); overflow-wrap:anywhere; white-space:pre-wrap; user-select:all}
pre.secret code{background:none; border:none; padding:0; color:var(--accent-2)}
/* recipients inline-блок */
.recipline{display:flex; gap:10px; align-items:flex-start}
.recip-list{flex:1; min-width:0; display:flex; flex-direction:column; gap:3px}
.recip-list code{display:inline-block; max-width:100%}
h1,h2,h3,h4{font-weight:600; letter-spacing:-.01em; margin:.2rem 0}
.muted{color:var(--dim)} .faint{color:var(--faint)}
.hint{color:var(--faint); font-size:.78em; font-weight:400; font-family:var(--sans)}
/* Текст лише для скрінрідера (лічильники видачі, статуси живих регіонів):
   лишається у дереві доступності, з екрана прибраний. */
.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
.svg-sprite{position:absolute;width:0;height:0;overflow:hidden}
.ic{width:1.05em;height:1.05em;flex:none;vertical-align:-.16em;stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}

/* ---- App shell ----------------------------------------------------------- */
.shell{display:flex; min-height:100vh}
.side{
  width:var(--sb); flex:none; position:sticky; top:0; align-self:flex-start; height:100vh;
  display:flex; flex-direction:column; gap:2px; padding:18px 12px;
  background:linear-gradient(180deg,var(--bg-2),rgba(13,17,21,.6));
  border-right:1px solid var(--line); backdrop-filter:blur(2px);
}
.brand{display:flex; align-items:center; gap:10px; padding:4px 10px 16px; font-weight:700; letter-spacing:-.02em; font-size:1.06rem}
.brand .glyph{width:30px;height:30px;border-radius:9px;display:grid;place-items:center;
  background:linear-gradient(150deg,var(--accent),var(--accent-press)); color:#04121c; box-shadow:0 0 0 1px var(--accent-line),0 6px 16px -6px var(--accent)}
.brand .glyph .ic{width:18px;height:18px;stroke-width:2.2}
.nav-label{padding:14px 12px 6px; font-family:var(--mono); font-size:.66rem; letter-spacing:.18em; text-transform:uppercase; color:var(--faint)}
.side a{display:flex; align-items:center; gap:11px; padding:9px 12px; border-radius:var(--r-ctl); color:var(--dim); font-weight:500; position:relative; transition:background .14s,color .14s}
.side a:hover{background:var(--panel); color:var(--text); text-decoration:none}
.side a.active{background:var(--accent-tint); color:var(--accent-2)}
.side a.active::before{content:""; position:absolute; left:-12px; top:9px; bottom:9px; width:3px; border-radius:0 3px 3px 0; background:var(--accent); box-shadow:0 0 10px var(--accent)}
.side .spacer{flex:1}
.side .foot{display:flex; flex-direction:column; gap:8px; padding-top:10px; border-top:1px solid var(--line)}
.safebadge{display:flex; align-items:center; gap:8px; font-family:var(--mono); font-size:.68rem; letter-spacing:.12em; color:var(--warn);
  background:var(--warn-tint); border:1px solid rgba(245,177,76,.28); border-radius:var(--r-ctl); padding:7px 10px}
.mutedbadge{color:#ff9a9a; background:var(--fail-tint); border-color:rgba(255,99,99,.3); text-decoration:none}
.mutedbadge:hover{filter:brightness(1.1)}
/* events-ref — перелік подій під полем «Події» (Settings → Сповіщення): згорнутий довідник «назва — що означає». */
details.events-ref{margin:4px 0 12px; border:1px solid var(--line); border-radius:var(--r-ctl); background:var(--panel)}
details.events-ref summary{padding:8px 12px; cursor:pointer; color:var(--dim); font-size:.85rem}
details.events-ref summary:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
details.events-ref h5{margin:12px 12px 4px; font-size:.72rem; letter-spacing:.08em; text-transform:uppercase; color:var(--dim)}
table.events-ref-table{width:100%; border-collapse:collapse; font-size:.82rem; table-layout:fixed}
table.events-ref-table td{padding:5px 12px; border-top:1px solid var(--line); vertical-align:top}
table.events-ref-table col.ev-name{width:15rem}
table.events-ref-table td:first-child{overflow-wrap:anywhere}
table.events-ref-table .tag{margin-left:6px; font-size:.7rem}
@media (max-width:640px){table.events-ref-table, table.events-ref-table tbody, table.events-ref-table tr, table.events-ref-table td{display:block; width:auto}
  table.events-ref-table td + td{border-top:0; padding-top:0}}
.qstate{margin:16px 0; padding:12px 14px; border:1px solid var(--line); border-radius:var(--r-ctl); background:var(--panel)}
.qstate h4{margin:0 0 8px; font-size:.82rem; letter-spacing:.06em; text-transform:uppercase; color:var(--dim)}
.who{display:flex; align-items:center; gap:9px; padding:4px 6px}
.avatar{width:28px;height:28px;border-radius:8px;flex:none;display:grid;place-items:center;font-weight:700;font-size:.8rem;color:#04121c;background:linear-gradient(150deg,var(--accent-2),var(--accent))}
.who .nm{font-weight:600;font-size:.86rem;line-height:1.1}
.who .role{font-family:var(--mono);font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.linkbtn{background:none;border:1px solid var(--line);color:var(--dim);cursor:pointer;padding:7px 10px;border-radius:var(--r-ctl);font:inherit;font-size:.82rem;display:flex;align-items:center;gap:8px;width:100%;transition:.14s}
.linkbtn:hover{border-color:var(--line-2);color:var(--text);background:var(--panel)}
/* Перемикач мови (Task 12 плану i18n) — той самий "pill"-патерн, що й .psz
   (розмір сторінки в пагінації): дві компактні кнопки, активна підсвічена
   акцентом. Свідомо малий: він живе в шапці/сайдбарі на КОЖНІЙ сторінці. */
.langsw{display:flex; gap:4px; padding:0 6px 2px}
.langbtn{min-width:25px; height:24px; padding:0 6px; border-radius:6px; font-family:var(--mono); font-size:.7rem; letter-spacing:.03em; border:1px solid var(--line-2); color:var(--dim); background:var(--panel-3)}
.langbtn:hover{border-color:var(--line-glow); color:var(--text)}
.langbtn.on{background:var(--accent-tint); border-color:var(--accent-line); color:var(--accent-2); font-weight:600}

.content{flex:1; min-width:0; display:flex; flex-direction:column}
.topbar{position:sticky; top:0; z-index:20; display:flex; align-items:center; justify-content:space-between; gap:1rem;
  padding:16px 30px; border-bottom:1px solid var(--line); background:rgba(10,13,17,.72); backdrop-filter:blur(10px) saturate(1.2)}
.topbar h1{font-size:1.18rem; margin:0}
.topbar .meta{font-family:var(--mono); font-size:.74rem; letter-spacing:.06em; color:var(--faint); display:flex; align-items:center; gap:8px}
.topbar .meta .live{width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok);animation:pulse 2.4s ease-in-out infinite}
/* .err — зв'язок втрачено: пульс зупинено, дот жовтий (js: htmx error/recovery) */
.topbar .meta .live.err{background:var(--warn);box-shadow:0 0 8px var(--warn);animation:none}
main{padding:26px 30px 48px; max-width:1320px; width:100%}

@media (max-width:880px){
  :root{--sb:64px}
  .side{padding:14px 8px}
  /* .sr-only, а НЕ display:none (audit-2026-09-04): згорнутий сайдбар ховав
     підписи з дерева доступності — скрінрідер озвучував кожне посилання і
     кнопку виходу без назви взагалі, а «Audit» і бейдж SAFE-MODE ставали
     двома однаковими безіменними щитами в одній колонці (тепер у них ще й
     різні гліфи). Текст лишається доступним, з екрана прибраний. */
  .brand span,.nav-label,.side a span,.who .info,.safebadge span,.linkbtn span{
    position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
  .side a{justify-content:center} main,.topbar{padding-left:18px;padding-right:18px}
  /* Згорнутий сайдбар (64px): .who ховає підпис (лишає лише аватар), а
     перемикач мови тексту-без-іконки не має, тож замість ховання (сховане =
     непрацююче) стискаємо пару кнопок, щоб влізла в ту саму колонку. */
  .langsw{padding:0 2px 2px; justify-content:center}
  .langbtn{min-width:20px; padding:0 3px; font-size:.62rem}
}

/* ---- Reveal on load ------------------------------------------------------ */
@keyframes rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
/* fill-mode саме backwards, НЕ both: both лишає анімацію «застосованою» після
   завершення, а анімація transform/opacity у такому стані НАЗАВЖДИ створює
   контекст накладання на кожній картці. Через це z-index випадайки (.combo-list)
   замикався всередині своєї картки, і наступна картка малювалася поверх неї.
   backwards дає той самий вступний ефект (from-стан до старту), але після
   завершення елемент повертається до звичайних стилів — і z-index знову
   глобальний. Не міняй на both/forwards, не перевіривши оверлеї. */
main>*{animation:rise .5s cubic-bezier(.2,.7,.2,1) backwards}
main>*:nth-child(1){animation-delay:.02s} main>*:nth-child(2){animation-delay:.08s}
main>*:nth-child(3){animation-delay:.14s} main>*:nth-child(4){animation-delay:.2s}
main>*:nth-child(n+5){animation-delay:.24s}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* ---- Cards / layout ------------------------------------------------------ */
.card{background:linear-gradient(180deg,var(--panel),var(--bg-2)); border:1px solid var(--line); border-radius:var(--r-card); padding:20px 22px; margin-bottom:20px; box-shadow:var(--sh-1)}
.card>h3:first-child{margin-top:0}
.card h3{font-size:1rem} .card h4{font-size:.86rem;color:var(--dim);margin:1.1rem 0 .4rem;font-family:var(--mono);letter-spacing:.04em;text-transform:uppercase}
.grid2{display:grid; grid-template-columns:1fr 1fr; gap:20px}
@media (max-width:900px){.grid2{grid-template-columns:1fr}}
.toolbar{display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin:0 0 16px}
.toolbar h3{margin:0; flex:1} .toolbar form{display:inline}

/* ---- Stat tiles ---------------------------------------------------------- */
.tiles{display:grid; grid-template-columns:repeat(6,1fr); gap:14px; margin-bottom:22px}
@media (max-width:1420px){.tiles{grid-template-columns:repeat(3,1fr)}}
@media (max-width:760px){.tiles{grid-template-columns:repeat(2,1fr)}}
.tile{position:relative; background:linear-gradient(180deg,var(--panel),var(--bg-2)); border:1px solid var(--line); border-radius:var(--r-card); padding:16px 18px; overflow:hidden}
.tile::before{content:"";position:absolute;left:0;top:0;height:3px;width:100%;background:var(--line-2)}
.tile .n{font-family:var(--mono); font-size:2rem; font-weight:600; line-height:1; letter-spacing:-.02em}
.tile .l{font-family:var(--mono); font-size:.68rem; letter-spacing:.16em; text-transform:uppercase; color:var(--faint); margin-top:8px; display:flex; align-items:center; gap:7px}
.tile.ok::before{background:var(--ok)} .tile.ok .n{color:var(--ok)}
.tile.fail::before{background:var(--fail)} .tile.fail .n{color:var(--fail)}
.tile.stale::before{background:var(--warn)} .tile.stale .n{color:var(--warn)}
.tile.missing::before{background:var(--miss)} .tile.missing .n{color:var(--miss)}
.tile.disabled::before{background:var(--faint)} .tile.disabled .n{color:var(--dim)}
/* «/M» — скільки вимкнених джобів у порталі проти скількох видно у фліті. */
.tile .n .of{font-size:.5em; color:var(--faint); letter-spacing:0}
.legend{margin:-14px 0 18px}
.tile.total::before{background:var(--accent)} .tile.total .n{color:var(--text)}
/* плитки-фільтри: клік = ?status=, активний фільтр підсвічено */
a.tile{display:block; color:inherit; transition:border-color .14s, background .14s}
a.tile:hover{border-color:var(--line-glow); color:inherit; text-decoration:none}
.tile.active{border-color:var(--accent-line); box-shadow:0 0 0 1px var(--accent-line), var(--sh-1)}
.tile.active .l{color:var(--dim)}

/* ---- Tables -------------------------------------------------------------- */
/* широкі таблиці (fleet/activity/hosts): горизонтальний скрол замість зрізання */
.table-wrap{overflow-x:auto; -webkit-overflow-scrolling:touch}
table.jobs{width:100%; border-collapse:collapse; background:transparent; border:1px solid var(--line); border-radius:var(--r-card); overflow:hidden}
table.jobs th{text-align:left; padding:11px 14px; background:var(--panel-2); font-family:var(--mono); font-size:.68rem; font-weight:600; text-transform:uppercase; letter-spacing:.12em; color:var(--dim); border-bottom:1px solid var(--line)}
/* Сортовані заголовки: лінк успадковує th-стиль, активна колонка — акцент + стрілка */
table.jobs th a{color:inherit; display:inline-flex; align-items:center; gap:4px}
table.jobs th a:hover{color:var(--text); text-decoration:none}
table.jobs th a.sorted{color:var(--accent-2)}
table.jobs th a .dir{font-size:.9em; line-height:1}
table.jobs td{padding:11px 14px; border-top:1px solid var(--line); vertical-align:middle}
table.jobs tbody tr{transition:background .12s}
table.jobs tbody tr:hover{background:var(--panel)}
table.jobs.small td,table.jobs.small th{padding:8px 12px; font-size:.85rem}
table.jobs td.err{font-family:var(--mono); color:#ff9a9a; font-size:.8rem}
.txt-fail{color:#ff9a9a}
.txt-warn{color:var(--warn)}
table.jobs td.num,table.jobs td .num{font-family:var(--mono)}
.empty,td.empty{color:var(--dim); text-align:center; padding:30px 14px}
.empty .ic{display:block;margin:0 auto 8px;width:26px;height:26px;color:var(--faint)}
table.kv{width:100%} table.kv th{text-align:left;color:var(--dim);font-weight:500;padding:5px 14px 5px 0;white-space:nowrap;vertical-align:top;font-size:.86rem}
table.kv td{padding:5px 0;font-family:var(--mono);font-size:.86rem}
.row-fail{background:linear-gradient(90deg,var(--fail-tint),transparent 60%)}
.row-stale{background:linear-gradient(90deg,var(--warn-tint),transparent 60%)}
.row-missing{background:linear-gradient(90deg,var(--miss-tint),transparent 60%)}
/* Стан хоста на NAS не прочитано: той самий червоний, що fail — потрібна дія. */
.row-unreadable{background:linear-gradient(90deg,var(--fail-tint),transparent 60%)}
/* Вимкнений джоб: НІЯКОЇ заливки — він не проблема. Приглушений текст, щоб
   рядок читався як «свідомо зупинений», а не як «щось із ним не так». */
.row-disabled{color:var(--dim)}
.row-disabled a{color:var(--dim)}

/* ---- Status pill --------------------------------------------------------- */
.st{display:inline-flex; align-items:center; gap:7px; font-family:var(--mono); font-size:.74rem; letter-spacing:.04em; text-transform:uppercase; color:var(--dim)}
.st .dot{width:8px;height:8px;border-radius:50%;background:var(--faint);box-shadow:0 0 0 3px rgba(255,255,255,.02)}
.st.ok{color:var(--ok)} .st.ok .dot{background:var(--ok);box-shadow:0 0 9px var(--ok)}
.st.fail{color:var(--fail)} .st.fail .dot{background:var(--fail);box-shadow:0 0 9px var(--fail)}
.st.stale{color:var(--warn)} .st.stale .dot{background:var(--warn);box-shadow:0 0 9px var(--warn)}
.st.warn{color:var(--warn)} .st.warn .dot{background:var(--warn);box-shadow:0 0 9px var(--warn)}
.st.missing{color:var(--miss)} .st.missing .dot{background:var(--miss);box-shadow:0 0 9px var(--miss)}
/* Без світіння: сигнальна лампа вимкненого джоба не блимає. */
.st.disabled{color:var(--faint)} .st.disabled .dot{background:var(--faint)}
.st.running{color:var(--accent)} .st.running .dot{background:var(--accent);box-shadow:0 0 9px var(--accent);animation:pulse 1.4s ease-in-out infinite}
/* Індикатор кроків видалення (Р-BD2-15): пілюлі .st рядком зі стрілками. */
.steps{display:flex;flex-wrap:wrap;align-items:center;gap:8px 4px;list-style:none;margin:6px 0 12px;padding:0}
.steps li+li::before{content:"→";color:var(--faint);margin:0 8px 0 4px}
.steps li[aria-current]{font-weight:600}
.lag{white-space:pre-line}
.tag{display:inline-flex;align-items:center;gap:5px;font-family:var(--mono);font-size:.68rem;letter-spacing:.06em;color:var(--dim);background:var(--panel-3);border:1px solid var(--line);border-radius:var(--r-pill);padding:2px 9px}
.tag-ok{color:var(--ok);background:var(--ok-tint);border-color:rgba(70,213,136,.3)}
.tag-warn{color:var(--warn);background:var(--warn-tint);border-color:rgba(245,177,76,.3)}
.tag-fail{color:#ff9a9a;background:var(--fail-tint);border-color:rgba(255,99,99,.3)}
.tag-info{color:var(--accent-2);background:var(--accent-tint);border-color:var(--accent-line)}

/* ---- Archive mode badges (full/inc) --------------------------------------- */
.badge{display:inline-block;padding:0 .4em;border-radius:.4em;font-size:.85em}
.badge-full{color:var(--ok);background:var(--ok-tint)}
.badge-inc{color:var(--warn);background:var(--warn-tint)}
th.pick, td.pick{width:2.2em;text-align:center}
.warn{color:var(--warn)}

/* ---- Form controls (global — однакові у формах, таблицях, будь-де) ------- */
input:not([type=checkbox]):not([type=radio]), textarea, select{
  font:inherit; font-size:.9rem; color:var(--text); background:var(--bg-2);
  border:1px solid var(--line-2); border-radius:var(--r-ctl);
  height:var(--ctl-h); padding:0 11px; transition:border-color .14s, box-shadow .14s; max-width:100%}
textarea{height:auto; min-height:66px; padding:9px 11px; line-height:1.5; font-family:var(--mono); font-size:.85rem; resize:vertical; width:100%}
select{appearance:none; -webkit-appearance:none; padding-right:32px; cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none' stroke='%2393a1b0' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 11px center}
input::placeholder, textarea::placeholder{color:var(--faint)}
input:focus, textarea:focus, select:focus{outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-tint)}
input[readonly]{color:var(--dim); background:var(--panel); cursor:default}
/* Стрілки-спінери у type=number малює ОС — усередині нашого поля вони єдина
   деталь чужого вигляду (і на hover ще й зсувають текст). Ховаємо саме
   спінер, а не тип: number лишає числову клавіатуру на мобільному і
   браузерну валідацію min/step, на яких тримаються stale_threshold_hours,
   agent_gone_minutes, local_keep_count і notify_smtp_port. */
input[type=number]{-moz-appearance:textfield; appearance:textfield}
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none; appearance:none; margin:0}
/* Поле з помилкою валідації (aria-invalid ставить хендлер при error-рендері).
   :not([type=hidden]) — НЕ декоративний (audit-2026-09-06): глобальне правило
   полів вище має специфічність (0,2,1) через два :not(), а input.invalid і
   input[aria-invalid] — лише (0,1,1), тож рамка ніколи не червоніла: ні від
   серверної помилки поля, ні від клієнтської валідації. Зайвий :not() підіймає
   до (0,2,1), і як пізніше правило воно виграє. Стереже TestInvalidFieldBorderBeatsGlobalInputRule. */
input.invalid:not([type=hidden]), textarea.invalid, input[aria-invalid="true"]:not([type=hidden]), textarea[aria-invalid="true"]{border-color:var(--fail)}
input.invalid:focus, textarea.invalid:focus, input[aria-invalid="true"]:focus, textarea[aria-invalid="true"]:focus{border-color:var(--fail); box-shadow:0 0 0 3px var(--fail-tint)}
form{margin:0}

/* ---- Утиліти ------------------------------------------------------------
   Свідомо три. Усе, що має зміст, отримує ім'я за змістом; ці закривають
   випадки, де в розмітці стояв інлайн-стиль рівно заради відступу — і через
   це не піддавався ні темі, ні медіа-запиту, ні перевизначенню з CSS.
   [hidden] з !important: приховування атрибутом мусить перемагати правило
   класу, яке ставить display (напр. .modal-typed{display:block}). */
[hidden]{display:none !important}
.mt{margin-top:12px}
.toolbar.mt{margin:12px 0 0}
.center{text-align:center}

/* Форма пінування версії: поле-комбо тягнеться, кнопка лишається при ньому. */
.pin-form{display:flex; gap:8px; flex-wrap:wrap; margin-top:6px}
.pin-form > .combo{flex:1 1 220px; min-width:200px}
/* Перемикач поруч зі значенням у kv-таблиці (шифрування) — відступ від тега. */
table.kv td > form.inline{margin-left:.5rem}

/* ---- Buttons ------------------------------------------------------------- */
button,.btn{display:inline-flex; align-items:center; justify-content:center; gap:7px; font:inherit; font-size:.86rem; font-weight:500; cursor:pointer;
  height:var(--ctl-h); padding:0 14px; background:var(--panel-3); color:var(--text); border:1px solid var(--line-2); border-radius:var(--r-ctl); transition:.14s; white-space:nowrap}
button:hover,.btn:hover{border-color:var(--line-glow); background:var(--panel-2); text-decoration:none}
button:active,.btn:active{transform:translateY(1px)}
button:focus-visible,.btn:focus-visible{outline:2px solid var(--accent); outline-offset:1px}
/* Primary-дефолт лише для НЕстилізованих submit-кнопок: інакше button[type=submit]
   (0,1,1) перебивав би .btn-danger/.btn-warn/.mini/.linkbtn/.langbtn (0,1,0) на
   деструктивних діях і на перемикачі мови (Task 12 плану i18n). */
.btn-primary,button[type=submit]:not(.btn):not(.mini):not(.btn-warn):not(.btn-danger):not(.linkbtn):not(.langbtn){background:linear-gradient(180deg,var(--accent),var(--accent-press)); color:#04121c; border-color:transparent; font-weight:600; box-shadow:0 3px 9px -5px var(--accent)}
.btn-primary:hover,button[type=submit]:not(.btn):not(.mini):not(.btn-warn):not(.btn-danger):not(.linkbtn):not(.langbtn):hover{background:var(--accent-2); color:#04121c}
.btn-warn{background:var(--warn-tint);color:var(--warn);border-color:rgba(245,177,76,.35)} .btn-warn:hover{background:rgba(245,177,76,.2)}
.btn-danger{background:var(--fail-tint);color:#ff9a9a;border-color:rgba(255,99,99,.35)} .btn-danger:hover{background:rgba(255,99,99,.18)}
.mini{height:30px; padding:0 11px; font-size:.76rem; border-radius:6px}
/* Запит у польоті: htmx-request (hx-запити) та .busy (submit-guard звичайних форм)
   — кнопка пригасає, отримує спінер і не приймає повторних кліків. */
button:disabled{opacity:.55; cursor:not-allowed}
button.htmx-request,.btn.htmx-request,button.busy,.btn.busy{opacity:.55; pointer-events:none}
button.htmx-request::after,button.busy::after{content:""; width:11px; height:11px; flex:none;
  border:2px solid currentColor; border-right-color:transparent; border-radius:50%; animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.actions{display:flex; gap:8px; flex-wrap:wrap; align-items:center}
.actions form{display:inline-flex; gap:6px; align-items:center}
.inline{display:inline-flex; gap:6px; align-items:center}
/* Панель фільтра списку (Audit): та сама .toolbar, але підпис і поле стоять
   парою, а поле не займає всю ширину, як у формах-картках. */
.filterbar label{font-size:.8rem; color:var(--dim); gap:8px}
.filterbar input{width:20ch}
.filterbar .sel{min-width:14ch}
.danger-zone{margin-top:24px; padding-top:16px; border-top:1px dashed rgba(255,99,99,.25)}

/* ---- Forms (вертикальні: підпис над полем) ------------------------------ */
.form label{display:block; margin:14px 0; color:var(--dim); font-size:.84rem; font-weight:500}
.form label > input, .form label > textarea, .form label > select, .form label > .sel{width:100%; margin-top:6px}
/* .field — підпис + контрол, який НЕ можна класти всередину <label>
   (у .combo скрипт доліплює ul з опціями: всередині лейбла вони потрапили б
   в його accessible name, а клік по опції активував би сам лейбл). Ритм той
   самий, що й у звичайного поля: 14px між полями, 6px підпис→контрол. */
.form .field{margin:14px 0}
.form .field > label{margin:0}
.form .field > .combo{margin-top:6px}
/* Поле у .field (підпис окремо від контрола) тримає той самий ритм, що й
   поле всередині <label>: 6px підпис→контрол, підказка — під контролом. */
.form .field > input, .form .field > textarea, .form .field > select, .form .field > .sel{width:100%; margin-top:6px}
.form .field > .hint{margin:6px 0 0}
.form .row{display:flex; gap:14px; flex-wrap:wrap; align-items:flex-end}
.form .row > label{flex:1 1 170px; margin:0}
.form .row > button, .form .row > .btn{flex:0 0 auto; margin:0}
.form label.check{display:flex; align-items:center; gap:9px; color:var(--text); margin:14px 0}
.form label.check input{width:16px; height:16px; margin:0; padding:0; accent-color:var(--accent)}
/* Підказка чекбокса — під написом, з відступом на ширину самого чекбокса
   (16px + gap 9px): у flex-рядку .check довгий текст розтягував би рядок. */
.form .check-hint{display:block; margin:-10px 0 14px 25px; max-width:78ch}
fieldset{border:1px solid var(--line); border-radius:var(--r-ctl); margin:16px 0; padding:8px 16px 16px}
legend{font-family:var(--mono); font-size:.68rem; letter-spacing:.14em; text-transform:uppercase; color:var(--accent-2); padding:0 8px}
.plugin-cfg{border-top:1px dashed var(--line-2); margin-top:10px; padding-top:12px}
/* Секції конфігу плагіна (Field.Group) — вкладені у fieldset «Конфіг плагіна»,
   тож рамка легша за зовнішню: групування має читатись, а не сперечатися з
   нею за увагу. */
.cfg-group{border:1px solid var(--line-2); background:var(--sunken); margin:12px 0; padding:6px 14px 14px}
.cfg-group legend{color:var(--faint); font-size:.64rem}

/* ---- Pagination --------------------------------------------------------- */
.pager{display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:14px; flex-wrap:wrap}
.pager .pinfo{font-family:var(--mono); font-size:.76rem; color:var(--faint)}
.pager .pnav{display:flex; align-items:center; gap:10px}
.pager .ppage{font-family:var(--mono); font-size:.76rem; color:var(--dim)}
.pager .btn.disabled{opacity:.4; pointer-events:none}
.pager .psize{margin-left:auto; display:inline-flex; align-items:center; gap:4px; font-family:var(--mono); font-size:.72rem; letter-spacing:.04em; text-transform:uppercase; color:var(--faint)}
.psz{display:inline-flex; align-items:center; justify-content:center; min-width:26px; height:26px; padding:0 7px; margin-left:2px; border-radius:6px; font-family:var(--mono); font-size:.78rem; border:1px solid var(--line-2); color:var(--dim); background:var(--panel-3)}
a.psz:hover{border-color:var(--line-glow); color:var(--text); text-decoration:none}
.psz.active{background:var(--accent-tint); border-color:var(--accent-line); color:var(--accent-2); font-weight:600}

/* ---- Контроли в таблицях — компактні, по висоті з mini-кнопками ---------- */
table.jobs td input:not([type=checkbox]), table.jobs td select, table.jobs td .sel-btn{height:30px; font-size:.82rem; margin:0}
table.jobs td .actions input{max-width:170px}

/* ---- Conn-lost banner (js: htmx responseError/sendError) ------------------ */
.conn-banner{position:fixed; top:14px; left:50%; transform:translateX(-50%); z-index:10001;
  display:flex; align-items:center; gap:10px; max-width:min(92vw,560px); padding:9px 10px 9px 14px;
  font-size:.85rem; color:#ffdca0; background:var(--panel-2);
  border:1px solid rgba(245,177,76,.35); border-left:3px solid var(--warn);
  border-radius:var(--r-ctl); box-shadow:var(--sh-2)}
.conn-banner .conn-close{height:24px; padding:0 8px; font-size:1rem; line-height:1;
  background:none; border:none; color:var(--dim); cursor:pointer}
.conn-banner .conn-close:hover{color:var(--text); background:var(--panel-3); border:none}

/* ---- Message boxes ------------------------------------------------------- */
.vbox{border-radius:var(--r-ctl); padding:12px 14px; margin:12px 0; font-size:.88rem; border-left:3px solid var(--line-2); background:var(--panel)}
.vbox-ok{border-left-color:var(--ok); background:var(--ok-tint); color:#aef0c8}
.vbox-err{border-left-color:var(--fail); background:var(--fail-tint); color:#ffc1c1}
.vbox-warn{border-left-color:var(--warn); background:var(--warn-tint); color:#ffdca0}
.vbox ul{margin:.4rem 0 0; padding-left:1.2rem} .vbox li{margin:2px 0}

/* ---- Releases (pending-картки) ------------------------------------------- */
.rel-list{display:flex; flex-direction:column; gap:10px; margin:10px 0}
/* fold — довгий машинний текст (stderr агента, помилка прогону) у комірці
   таблиці: прев'ю лишається однорядковим, повний текст розгортається на
   місці. До audit-2026-09-01 повного тексту не було ніде в UI. */
details.fold summary{cursor:pointer; list-style:none; display:block; overflow-wrap:anywhere}
details.fold summary::-webkit-details-marker{display:none}
details.fold summary::after{content:" ▸"; color:var(--faint); font-size:.85em}
details.fold[open] summary::after{content:" ▾"}
details.fold summary:hover{text-decoration:underline dotted}
details.fold summary:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
/* pre.err — блок тексту помилки (пуш на NAS, poll, self-update, карантин,
   черга алертів). Голий <pre> лишає дефолтний white-space:pre, тож ОДИН довгий
   рядок від rsync/ssh робить елемент ширшим за картку і розсуває всю сторінку
   вправо разом із рештою верстки (скарга оператора 2026-09-03). Перенос +
   власна прокрутка тримають довгий текст усередині його ж блоку. */
pre.err{margin:6px 0 0; padding:8px 10px; max-height:14rem; overflow:auto;
  background:var(--panel-2); border:1px solid var(--line); border-radius:var(--r-ctl);
  white-space:pre-wrap; overflow-wrap:anywhere; font-size:.78rem}
pre.err code{background:none; border:none; padding:0; color:var(--text)}
details.fold pre{margin:6px 0 0; padding:8px 10px; max-height:16rem; overflow:auto;
  background:var(--panel-2); border:1px solid var(--line); border-radius:var(--r-ctl);
  white-space:pre-wrap; font-size:.78rem}

details.rel-pending{background:var(--panel-2); border:1px solid var(--line); border-radius:var(--r-ctl); overflow:hidden}
details.rel-pending summary{padding:10px 14px; cursor:pointer; list-style:none; display:flex; align-items:center; gap:8px; flex-wrap:wrap; transition:background .14s}
details.rel-pending summary::before{content:"▸"; color:var(--faint); font-size:.8em; transition:transform .14s}
details.rel-pending[open] summary::before{transform:rotate(90deg)}
details.rel-pending summary::-webkit-details-marker{display:none}
details.rel-pending summary:hover{background:var(--panel-3)}
details.rel-pending summary:focus-visible{outline:2px solid var(--accent); outline-offset:-2px}
details.rel-pending .sign-form{padding:4px 14px 14px; border-top:1px dashed var(--line-2)}

/* ---- Print: світла схема, лише контент ----------------------------------- */
@media print{
  :root{--bg:#fff; --bg-2:#fff; --panel:#fff; --panel-2:#f2f2f2; --panel-3:#eee; --sunken:#f7f7f7;
    --text:#111; --dim:#444; --faint:#666; --line:#bbb; --line-2:#999}
  body{background:#fff}
  body::before, .side, .topbar .meta, .pager, .conn-banner, .toolbar form, .actions{display:none!important}
  main>*{animation:none!important}
  .card, table.jobs{box-shadow:none}
}

/* ---- Auth (login/setup) -------------------------------------------------- */
main.centered{display:grid; place-items:center; min-height:100vh; padding:24px}
.auth{width:100%; max-width:392px; border:1px solid var(--line-2); box-shadow:var(--sh-2); position:relative; overflow:hidden}
.auth::before{content:"";position:absolute;inset:0;height:3px;background:linear-gradient(90deg,transparent,var(--accent),transparent)}
.auth .brand-lg{display:flex;flex-direction:column;align-items:center;gap:12px;margin-bottom:8px}
.auth .glyph{width:52px;height:52px;border-radius:14px;display:grid;place-items:center;background:linear-gradient(150deg,var(--accent),var(--accent-press));color:#04121c;box-shadow:0 0 0 1px var(--accent-line),0 10px 30px -8px var(--accent)}
.auth .glyph .ic{width:30px;height:30px;stroke-width:2.1}
.auth h2{margin:0;font-size:1.15rem;text-align:center}
.auth .tag-line{font-family:var(--mono);font-size:.7rem;letter-spacing:.18em;text-transform:uppercase;color:var(--faint);text-align:center;margin-bottom:18px}
.auth form{margin-top:6px} .auth button{width:100%; height:44px; margin-top:14px; font-size:.92rem}

/* ── combo: поле з випадайкою і пошуком (вибір версії self-update) ──────────
   Без JS це звичайний input — випадайка вмикається скриптом (data-combo). */
.combo{position:relative; display:block; width:100%}
/* :not()-и НЕ декоративні: глобальне правило полів
   input:not([type=checkbox]):not([type=radio]) (0,2,1) — має вищу
   специфічність за просте .combo > input (0,1,1) і перебивало padding-right
   своїм padding:0 11px. Текст заїжджав під шеврон. */
.combo > input:not([type=checkbox]):not([type=radio]){width:100%; padding-right:34px}
.combo-toggle{
  position:absolute; right:1px; top:1px; height:calc(var(--ctl-h) - 2px); width:32px;
  display:none; align-items:center; justify-content:center;
  background:none; border:0; border-radius:0 var(--r-ctl) var(--r-ctl) 0;
  color:var(--dim); cursor:pointer; padding:0}
.combo[data-ready] .combo-toggle{display:flex}
/* background/transform — не косметика: глобальні button:hover / button:active
   (0,1,1) перебивають background:none у .combo-toggle (0,1,0) і малюють
   світлий прямокутник із прямим краєм усередині заокругленого поля, а на
   кліку ще й совають іконку. */
.combo-toggle:hover{color:var(--text); background:none}
.combo-toggle:active{transform:none}
.combo-toggle .ic{width:12px; height:8px; transition:transform .14s}
.combo[data-open] .combo-toggle .ic{transform:rotate(180deg)}
.combo-list{
  position:absolute; z-index:40; left:0; right:0; top:calc(100% + 4px);
  margin:0; padding:4px; list-style:none; max-height:264px; overflow-y:auto;
  background:var(--panel-2); border:1px solid var(--line-2); border-radius:var(--r-ctl);
  box-shadow:var(--sh-2)}
.combo:not([data-open]) .combo-list{display:none}
.combo-list li{
  display:flex; align-items:baseline; gap:8px; padding:7px 9px; border-radius:6px;
  cursor:pointer; font-size:.86rem; line-height:1.35}
.combo-list li:hover{background:var(--panel-3)}
/* Клавіатурна підсвітка — ОКРЕМИЙ і помітний стан. --panel-3 на --panel-2 дає
   1.13:1: для миші вистачало (позицію показує курсор), але ↑/↓ іншого
   індикатора не мають узагалі (WCAG 1.4.11 просить 3:1). Смуга кольору
   --accent дає 8.8:1 до тла списку; тінт відрізняє фокус від :hover. */
.combo-list li[aria-selected="true"]{background:var(--accent-tint); box-shadow:inset 2px 0 0 var(--accent)}
.combo-list li .v{font-family:var(--mono); color:var(--text)}
/* --dim, не --faint: обіцянка «--faint ≥4.5:1» тримається до --bg, а мета
   рядка під курсором/фокусом сидить на --panel-3 (--faint там 4.00:1) — і
   найгірше читається саме той рядок, який зараз обирають. --dim: 5.57:1. */
.combo-list li .meta{margin-left:auto; font-size:.74rem; color:var(--dim); white-space:nowrap}
.combo-list li.combo-empty-opt .v{font-family:var(--sans); color:var(--dim); font-style:italic}
.combo-note{
  padding:7px 9px; font-size:.76rem; color:var(--dim); font-family:var(--sans);
  border-top:1px solid var(--line)}
/* Нота — інформаційний рядок без хендлера: скидаємо cursor/hover від
   .combo-list li, як це вже зроблено для .combo-none нижче. */
.combo-list li.combo-note{cursor:default}
.combo-list li.combo-note:hover{background:none}
.combo-list li.combo-none{cursor:default; color:var(--dim); font-size:.8rem}
.combo-list li.combo-none:hover{background:none}

/* ── field-err: повідомлення валідації під полем ───────────────────────────
   Нативна бульбашка required/minlength — останнє системне вікно поверх
   дизайну, що вціліло після аудиту верстки: біла, зі системним шрифтом, з
   текстом мовою браузера. app.js гасить її (invalid + preventDefault) і
   малює це замість неї — тим самим патерном, що серверна помилка поля
   (aria-invalid + aria-describedby). Без JS нативна валідація лишається. */
.field-err{margin:6px 0 0; font-size:.8rem; line-height:1.4; color:var(--fail)}
.field-err:empty{display:none}

/* ── modal: власне вікно підтвердження ─────────────────────────────────────
   Замість window.confirm/prompt/alert — системних вікон поверх дизайну, у
   яких текст не форматується і не влазить пояснення, чому дія незворотна.
   Основа — нативний <dialog>: фокус-пастка, Escape і backdrop даром, а
   виглядає він так, як ми скажемо. Без підтримки <dialog> app.js падає назад
   на window.confirm — краще старе вікно, ніж дія без підтвердження. */
dialog.modal{
  padding:0; border:1px solid var(--line-2); border-radius:var(--r-card);
  background:var(--panel); color:var(--text); box-shadow:var(--sh-2);
  width:min(520px, calc(100vw - 32px)); max-height:calc(100vh - 64px)}
dialog.modal::backdrop{background:rgba(5,8,11,.66)}
.modal-card{display:flex; flex-direction:column; gap:12px; padding:18px 20px 16px}
.modal-title{margin:0; font-size:1rem; font-weight:600}
.modal-body{margin:0; font-size:.9rem; line-height:1.5; color:var(--dim); overflow-wrap:anywhere; white-space:pre-line}
.modal-body strong{color:var(--text)}
.modal-typed{display:block; font-size:.85rem; color:var(--dim)}
.modal-typed input{width:100%; margin-top:6px; font-family:var(--mono)}
/* Помилка введення — тут же, а не окремим alert-ом поверх: вона стосується
   поля, яке видно, і вікно не має закриватись, щоб її показати. */
.modal-err{margin:0; font-size:.82rem; color:var(--fail)}
.modal-err:empty{display:none}
.modal-actions{display:flex; justify-content:flex-end; gap:8px; margin-top:2px}
/* Небезпечна дія: підтвердження червоне, а фокус за замовчуванням — на
   «Скасувати» (див. app.js), щоб Enter навмання нічого не видалив. */
.modal-actions .btn-danger{font-weight:600}

/* ── sel: наша випадайка поверх нативного <select> ─────────────────────────
   Закритий <select> ми стилізували давно, але СПИСОК, що розкривався,
   малювала ОС: білий фон, системний шрифт, свій скролбар — посеред темної
   сторінки він читався як гола HTML-вставка. Тепер app.js робить із кожного
   <select> кнопку + той самий .combo-list, що й у combo; сам <select>
   лишається в DOM носієм значення (форма, onchange, автотести).
   Без JS нічого не відбувається — нативний select працює як працював. */
.sel{position:relative; display:inline-block; max-width:100%}
/* Нативний select ховаємо ЛИШЕ коли заміна вже стоїть (data-ready): інакше
   вимкнений/зламаний скрипт лишив би форму без жодного видимого контролу. */
.sel[data-ready] > select{position:absolute; width:1px; height:1px; margin:-1px;
  padding:0; overflow:hidden; clip-path:inset(50%); border:0; opacity:0; pointer-events:none}
/* Кнопка повторює геометрію поля: висота/радіус/шеврон — з тих самих змінних,
   що й глобальний select, щоб у ряду з input-ами нічого не «стрибало». */
.sel-btn{width:100%; justify-content:space-between; gap:8px; padding:0 11px;
  background:var(--bg-2); font-weight:400; font-size:.9rem; text-align:left}
.sel-btn:hover{background:var(--bg-2); border-color:var(--line-glow)}
.sel-btn:active{transform:none}
.sel-btn .ic{width:12px; height:8px; flex:none; color:var(--dim); transition:transform .14s}
/* Помилка поля: select схований, тож app.js переносить invalid/aria-invalid на
   кнопку (mirrorInvalid). Відкритий список лишає акцентну рамку — він пізніше. */
.sel-btn.invalid, .sel-btn[aria-invalid="true"]{border-color:var(--fail)}
.sel-btn.invalid:focus-visible, .sel-btn[aria-invalid="true"]:focus-visible{border-color:var(--fail); outline-color:var(--fail); box-shadow:0 0 0 3px var(--fail-tint)}
.sel[data-open] .sel-btn{border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-tint)}
.sel[data-open] .sel-btn .ic{transform:rotate(180deg)}
.sel-val{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* Порожня опція (`<option value="">`) у нативному списку — просто порожній
   рядок: незрозуміло, це «не задано» чи збій рендера. Показуємо тире. */
.sel-val.empty, .combo-list li.sel-empty .v{color:var(--dim); font-style:italic}
.sel:not([data-open]) .combo-list{display:none}
.sel .combo-list li .v{font-family:var(--sans)}
/* .combo-list прибитий до ширини поля (left:0;right:0) — комбо стоїть у формі
   на всю ширину. Наші ж select-и бувають вузькі (роль у комірці таблиці), і в
   такий список «operator» переносився на два рядки. Тут список росте під
   вміст, але не вужче за кнопку і не ширше за стелю. */
.sel .combo-list{right:auto; min-width:100%; width:max-content; max-width:min(360px, 80vw)}
/* Довгі імена архівів у restore ріжемо по стелі, а не розсовуємо випадайку. */
.sel .combo-list li{overflow-wrap:anywhere}

/* Поки випадайка відкрита, її картку піднімаємо над сусідніми: протягом
   програвання вступної анімації контекст накладання ще існує, тож самого
   z-index на .combo-list не досить. Клас вішає app.js на найближчого
   нащадка main. Значення СВІДОМО менше за .topbar (z-index:20): сусідні
   картки мають z-index:auto, тож 10 їх перебиває, а 30 піднімало картку ще й
   над липкою шапкою — на невисокому вікні заголовок і годинник зникали під
   карткою (audit-2026-09-01). Обидва елементи в кореневому контексті
   накладання: .content — звичайний flex без position/z-index/transform. */
.combo-raise{position:relative; z-index:10}

/* Таблиця витягів. Без явних ширин колонки «пливуть»: сирий RFC3339 із
   наносекундами, довгі абсолютні шляхи й текст помилки тягнуть кожен у свій
   бік, і жодна колонка не тримає форму (скарга оператора 2026-09-03).
   Час і розмір НЕ переносяться, шляхи ламаються де завгодно, помилка сидить
   у власному <details>. */
table.extracts{table-layout:fixed}
table.extracts td{vertical-align:top}
table.extracts .when{width:11rem; white-space:nowrap}
table.extracts .what{width:22rem; overflow-wrap:anywhere}
table.extracts .state{width:12rem}
table.extracts td.num,table.extracts th.num{width:6rem; text-align:right; white-space:nowrap;
  font-variant-numeric:tabular-nums}
table.extracts .result{overflow-wrap:anywhere}
table.extracts ul.paths{margin:4px 0 0; padding-left:1.1em; font-family:var(--mono); font-size:.78rem}
table.extracts ul.paths li{margin:1px 0}
table.extracts .arch{margin-top:4px; font-family:var(--mono); font-size:.72rem}
table.extracts .nas{margin-top:6px; font-size:.72rem}
table.extracts .nas code{overflow-wrap:anywhere}
table.extracts form.inline{display:inline}
.btn-sm{padding:4px 10px; font-size:.78rem}

/* Дерево гостьової ФС kvm (задача 9): імена — моноширинним, бо «\xff» і
   керівні символи показуються кодами, і їх треба читати побайтово. */
.gtree .gtree-meta{font-family:var(--mono); font-size:.76rem; overflow-wrap:anywhere}
.gtree .gtree-crumbs{font-family:var(--mono); font-size:.82rem; margin:10px 0; overflow-wrap:anywhere}
.gtree .gtree-crumbs .sep{color:var(--faint); margin:0 2px}
.gtree table.gtree-list td.pick, .gtree table.gtree-list th.pick{width:2rem}
.gtree table.gtree-list td.name{font-family:var(--mono); font-size:.8rem; overflow-wrap:anywhere}
.gtree table.gtree-list td.num{width:6rem; text-align:right; white-space:nowrap; font-variant-numeric:tabular-nums}
.gtree table.gtree-list td.when{width:9rem; white-space:nowrap}
.gtree .gtree-picked ul.paths{font-family:var(--mono); font-size:.78rem; margin:6px 0 0}
/* Вибране поза сторінкою — label.check (галочка 16px поруч з іменем, як
   перемикач режимів), а не загальне «.form label > input» на всю ширину. */
.gtree .gtree-picked label.check{margin:4px 0}
.guest-modes textarea, .guest-modes input[name=index_root]{margin-top:4px}

/* ---- Сторінки з боковим меню: Settings (спека 2026-10-13-settings-pages §3.2), хост і джоб (спека
   2026-10-18-host-job-pages §3.2) — одна рамка .subpages на три групи сторінок ---- */
.subpages{display:grid; grid-template-columns:220px minmax(0,1fr); gap:24px; align-items:start}
.subpages-body{min-width:0}
/* top: висота .topbar (≈57px: padding 16px×2, h1 1.18rem, рамка) + верхній відступ main (26px) */
.subpages-nav{position:sticky; top:84px; display:flex; flex-direction:column; gap:2px; padding:8px;
  background:linear-gradient(180deg,var(--panel),var(--bg-2)); border:1px solid var(--line); border-radius:var(--r-card)}
.subpages-nav a{display:block; padding:8px 12px; border-radius:var(--r-ctl); color:var(--dim); font-weight:500; font-size:.88rem; transition:background .14s,color .14s}
.subpages-nav a:hover{background:var(--panel-2); color:var(--text); text-decoration:none}
.subpages-nav a.active{background:var(--accent-tint); color:var(--accent-2)}
.subpages-nav a:focus-visible{outline:2px solid var(--accent); outline-offset:1px}
@media (max-width:880px){
  .subpages{grid-template-columns:1fr; gap:14px}
  .subpages-nav{position:static; flex-direction:row; overflow-x:auto; padding:6px; gap:4px}
  .subpages-nav a{flex:none; white-space:nowrap}
}
/* Шапка сторінок хоста й джоба (спека 2026-10-18-host-job-pages §2.2): над меню й тілом, назва й теги в рядок. */
.subpages-head{display:flex; align-items:center; flex-wrap:wrap; gap:8px; margin:0 0 18px}
.subpages-head h2{font-size:1.1rem; margin:0 6px 0 0}
.subpages-head h2 a{color:var(--text)}
.subpages-head h2 a:hover{color:var(--accent-2); text-decoration:none}
.settings-cards{display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:16px}
.settings-cards .card{margin:0}
.settings-card h3 a{color:var(--text)}
.settings-card h3 a:hover{color:var(--accent-2); text-decoration:none}
.settings-card .facts{display:flex; flex-wrap:wrap; gap:6px; margin:0}

/* Огляди хоста й джоба (план 2026-10-18 HJ-5): картки розділів — та сама сітка, що на огляді Settings. */
.page-cards{display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:16px; margin-top:16px}
.page-cards .card{margin:0}
.page-card h3 a{color:var(--text)}
.page-card h3 a:hover{color:var(--accent-2); text-decoration:none}
.page-card .facts{display:flex; flex-wrap:wrap; gap:6px; margin:0}
