/* Outfit (auto-găzduit, variable) — sans geometric modern, terminale rotunjite. Două
   subset-uri: latin + latin-ext (ultimul aduce ș/ț/ă din Română, să nu cadă pe alt font). */
@font-face {
  font-family: "Outfit"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("/static/fonts/outfit-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, 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: "Outfit"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("/static/fonts/outfit-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* Fonturi ca TOKENI: `var(--font, inherit)` era folosit deja în cod, dar tokenul nu exista
     nicăieri — fallback-ul `inherit` moștenea exact fontul pe care regula voia să-l anuleze.
     Definite o dată aici; nu depind de temă. */
  --font: "Outfit", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  --bg: #0b0f17;
  --bg-2: #111725;
  --panel: #141c2b;
  --panel-2: #1a2434;
  --line: #232f45;
  --line-soft: #1b2536;
  /* ⚠ SEPARATOR DE SECTIUNE, distinct de `--line-soft`. Cerere Bogdan (08 sep): „vreau sa se
     vada mai strident acel separator intre CONTACT / ACCES si ACTIUNI PE CONT […] valabil
     general, pe tot portalul". NU s-a intarit `--line-soft` — el desparte RANDURI de tabel
     (opt locuri), unde o linie mai tare face tabelul zebrat si obositor. Un separator care
     spune „aici incepe alt subiect" si unul care spune „urmatorul rand" sunt doua lucruri
     diferite; pana acum aveau aceeasi culoare. */
  --line-sect: #38486a;
  --txt: #e6edf7;
  --txt-dim: #8a97ad;
  --txt-faint: #7b8aa4;   /* 4.89:1 pe carduri; era #5c6a82 = 3.12:1, sub pragul pentru text mic */
  /* brand DAB. --brand = roșul exact al logo-ului; --accent = variantă vie, lizibilă pe navy,
     pentru interactiv (butoane/focus/hover/activ). Distinct de --down (coral de „incident"). */
  --brand: #AE272F;
  --accent: #d0303c;
  --accent-bg: rgba(208,48,60,.15);
  --ok: #2ecc71;
  --ok-bg: rgba(46,204,113,.12);
  --warn: #f5a623;
  --warn-bg: rgba(245,166,35,.12);
  --down: #ff4d5e;
  --down-bg: rgba(255,77,94,.12);
  /* stale = probe tăcut / monitorizare oprită. Violet, distinct de roșu (cameră
     confirmată jos) — semnalează „nu știm", nu „e jos". */
  --stale: #b18aff;
  --stale-bg: rgba(177,138,255,.12);
  /* maint = echipament scos INTENȚIONAT din alerte (branșament / service). Gri neutru:
     vizibil, dar deliberat nealarmant — „știut și asumat", nu „problemă de rezolvat". */
  --maint: #8b97a8;
  --maint-bg: rgba(139,151,168,.14);
  --maint-line: rgba(139,151,168,.5);
  /* borduri de stare soft (uniforme, stil KPI) — pastile colorate pe toată marginea */
  --ok-line: rgba(46,204,113,.5);
  --warn-line: rgba(245,166,35,.5);
  --down-line: rgba(255,77,94,.55);
  --stale-line: rgba(177,138,255,.55);
  /* ---- SCARA DE SPATIERE (08 sep 2026) --------------------------------------------------
     Cerere Bogdan: „in unele zone am spatii prea mari, in alte zone n-am spatii deloc […]
     spatierea e general valabila pe tot portalul, fara spatii inutile si spatiere egala".
     Masurat inainte, in cele doua ferestre de utilizator: **noua** valori diferite (0, 5, 6,
     7, 9, 12, 13, 18, 20) pentru **patru** feluri de relatii. Una dintre ele era 0 — chiar
     locul unde se vedea ca lipseste spatiul.
     Patru trepte, fiecare pentru o RELATIE, nu pentru un loc:
       `--sp-1` eticheta si cimpul ei — cea mai strinsa, sint un singur lucru
       `--sp-2` un bloc si textul care-l explica · un titlu de sectiune si continutul lui
       `--sp-3` intre lucruri de acelasi rang (cimp linga cimp, bloc linga bloc)
       `--sp-4` intre SECTIUNI — singurul salt care se citeste ca „alt subiect"
     ⚠ Regula, la orice felie noua: daca un spatiu nou nu incape in niciuna din cele patru,
     intrebarea nu e „ce valoare pun", e „ce relatie e asta de fapt". */
  --sp-1: 5px;
  --sp-2: 9px;
  --sp-3: 14px;
  --sp-4: 20px;
  --radius: 12px;
  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.25);
  color-scheme: dark;
  /* tokeni de temă (valorile dark = culorile hardcodate ACTUALE → dark rămâne identic) */
  --line-strong: #2e3c56;                 /* era ORFAN (folosit la .modal-close:hover, nedefinit) — acum definit */
  --bg-glow: #16233b;
  --topbar-bg: rgba(11,15,23,.85);
  --overlay-panel: rgba(11,15,23,.82);
  --overlay-panel-strong: rgba(15,20,32,.97);
  --label-halo: rgba(11,15,23,.75);
  --uisp-fg: #ccd5e3; --uisp-border: rgba(255,255,255,.28); --uisp-bg: rgba(255,255,255,.07);
  --digi-fg: #8f8dff; --digi-border: rgba(0,43,254,.55); --digi-bg: rgba(0,43,254,.12);
  font-synthesis: none;

  /* ⚠ Iconițe DESENATE, nu caractere tipografice (07 sep 2026). Portalul avea 9 sageti scrise
     cu glife — `▾` la selectoare, iar la deschidere DOUA glife diferite pentru acelasi inteles
     (`▸` si `›`). Un caracter ia grosimea fontului, nu pe cea a desenelor din jur; de-aia
     aratau vechi langa SVG-urile din bara laterala si de pe harta.
     ⚠ Ca MASCA, nu ca imagine de fundal: masca ia `currentColor`, deci iconita urmeaza culoarea
     textului (hover, tema, stare) fara sa existe doua fisiere. Acelasi tipar ca la butonul
     „Straturi" de pe harta. */
  /* calendar — pentru `input[type=date]`, pus peste indicatorul nativ al WebKit */
  --ico-calendar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='16' rx='2.5'/%3E%3Cpath d='M3 10h18M8 3v4M16 3v4'/%3E%3C/svg%3E");
  --ico-jos: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  --ico-dreapta: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 6 6 6-6 6'/%3E%3C/svg%3E");
  --ico-bifa-alba: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  --ico-punct-alb: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='12' fill='%23fff'/%3E%3C/svg%3E");
  --ico-linie-alba: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 6'%3E%3Crect width='24' height='6' rx='3' fill='%23fff'/%3E%3C/svg%3E");
}

/* ---- temă LIGHT (M365: carduri albe pe canvas gri deschis). Overlay pe <html data-theme="light">.
   Doar tokeni + câteva overrides punctuale (harta) — restul componentelor se recolorează prin tokeni. ---- */
:root[data-theme="light"] {
  color-scheme: light;
  --bg:#eceff3; --bg-2:#e4e8ee;
  --panel:#ffffff; --panel-2:#eef1f5;
  --line:#d5dae1; --line-soft:#e6eaef; --line-strong:#b9c2cd; --line-sect:#bcc5d0;
  --txt:#1b2330; --txt-dim:#55606f; --txt-faint:#5f6975;
  --brand:#AE272F; --accent:#c62330; --accent-bg:rgba(198,35,48,.10);
  --ok:#0d7d42; --ok-bg:rgba(13,125,66,.12); --ok-line:rgba(13,125,66,.5);
  --warn:#a35f00; --warn-bg:rgba(163,95,0,.12); --warn-line:rgba(163,95,0,.5);
  --down:#d92d3a; --down-bg:rgba(217,45,58,.12); --down-line:rgba(217,45,58,.55);
  --stale:#7b52d6; --stale-bg:rgba(123,82,214,.12); --stale-line:rgba(123,82,214,.55);
  --maint:#5b6675; --maint-bg:rgba(91,102,117,.12); --maint-line:rgba(91,102,117,.5);
  --radius:12px; --shadow:0 1px 2px rgba(16,24,40,.06), 0 8px 24px rgba(16,24,40,.08);
  --bg-glow:#f4f7fb;
  --topbar-bg:rgba(255,255,255,.85);
  --overlay-panel:rgba(255,255,255,.82); --overlay-panel-strong:rgba(255,255,255,.97);
  --label-halo:rgba(255,255,255,.85);
  --uisp-fg:#3a4353; --uisp-border:rgba(58,67,83,.35); --uisp-bg:#e4e8ee;
  --digi-fg:#002BFE; --digi-border:rgba(0,43,254,.45); --digi-bg:rgba(0,43,254,.08);
}

/* Light: umbrele negre bespoke de pe carduri/panouri (nu backdrop-urile de modal) devin
   gri-reci blânde. Backdrop-urile (background: rgba(0,0,0,...)) rămân neatinse. Dark neschimbat. */
:root[data-theme="light"] .modal-dialog,
:root[data-theme="light"] .cred-card { box-shadow: 0 10px 34px rgba(16,24,40,.14), 0 2px 8px rgba(16,24,40,.08); }
:root[data-theme="light"] .site:hover { box-shadow: 0 6px 20px rgba(16,24,40,.12); }

* { box-sizing: border-box; }

body {
  margin: 0;
  background:
    radial-gradient(1200px 600px at 80% -10%, var(--bg-glow) 0%, transparent 60%),
    var(--bg);
  color: var(--txt);
  font: 15px/1.5 var(--font);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

a { color: inherit; text-decoration: none; }

.mono { font-family: var(--mono), "SF Mono", "JetBrains Mono", Menlo, monospace; font-size: .92em; }

/* ---- topbar ---- */
.topbar {
  display: flex; align-items: center; gap: 28px;
  height: var(--topbar-h); padding: 0 24px;
  background: var(--topbar-bg);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 10;
}
.brand { display: flex; align-items: center; gap: 9px; font-weight: 700; letter-spacing: .2px; }
/* Marca reală DAB, aceeași cu iconița de pe ecranul telefonului și cu cea de pe pagina de
   autentificare. Era un caracter text (`◉`), care nu semăna cu nimic din produs. */
.brand-mark { display: block; width: 22px; height: 22px; border-radius: 6px; flex: none; }
.brand-dim { color: var(--txt-dim); font-weight: 500; }
.topnav { display: flex; gap: 6px; }
.topnav a { padding: 6px 12px; border-radius: 8px; color: var(--txt-dim); font-weight: 500; }
.topnav a:hover { color: var(--txt); background: var(--panel); }
.topnav a.active { color: var(--txt); background: var(--panel-2); }
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.env-pill {
  font-size: 12px; color: var(--txt-dim);
  border: 1px solid var(--line); border-radius: 20px; padding: 4px 12px;
}
.live { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--txt-dim); font-variant-numeric: tabular-nums; }
.live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 3px var(--ok-bg); animation: livepulse 2.4s infinite; }
@keyframes livepulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .live-dot { animation: none; } }
/* refresh în pauză (modal deschis / câmp în focus): ceasul NU mai avansează, iar indicatorul
   o spune — altfel ora arată proaspătă peste date vechi */
.live.is-paused { opacity: .55; }
.live.is-paused .live-dot { background: var(--txt-faint); box-shadow: none; animation: none; }
@media (max-width: 620px) { .live { display: none; } }
.nav-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; margin-right: 4px;
  background: transparent; border: 1px solid var(--line); border-radius: 8px;
  color: var(--txt-dim); font-size: 16px; cursor: pointer; line-height: 1;
  transition: color .12s ease, background .12s ease, border-color .12s ease;
}
.nav-toggle:hover { color: var(--txt); background: var(--panel); border-color: var(--line); }
.nav-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- app shell: side-meniu (stânga) + zonă de conținut (dreapta) ---- */
.app { display: flex; align-items: stretch; min-height: 100vh; }
.sidebar {
  flex: 0 0 246px; width: 246px;
  background: var(--bg-2); border-right: 1px solid var(--line);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
  transition: flex-basis .18s ease, width .18s ease;
}
.sidebar-brand {
  /* aceeași înălțime ca banda de sus, ca marca din meniu să se alinieze cu ea */
  display: flex; align-items: center; gap: 9px; height: var(--topbar-h); padding: 0 18px;
  font-weight: 700; letter-spacing: .2px; white-space: nowrap; overflow: hidden;
  border-bottom: 1px solid var(--line);
}
.sidebar-nav { padding: 10px 8px; display: flex; flex-direction: column; gap: 2px; }
.sidebar-nav a {
  display: flex; align-items: center; gap: 11px; padding: 8px 10px;
  border-radius: 8px; color: var(--txt-dim); font-weight: 500;
  white-space: nowrap; overflow: hidden;
}
.sidebar-nav a:hover { color: var(--txt); background: var(--panel); }
.sidebar-nav a.active { color: var(--txt); background: var(--panel-2); box-shadow: inset 3px 0 0 var(--accent); }
.sidebar-nav a.nav-soon { opacity: .55; cursor: default; }
.sidebar-nav a.nav-soon:hover { background: transparent; color: var(--txt-dim); }
/* ⚠ SVG, nu emoji: emoji-urile se randează cu fontul sistemului (altfel pe iOS față de
   Android) și nu pot prelua culoarea stării active. Aceleași desene ca în bara de jos —
   aceeași destinație trebuie să arate la fel în ambele navigații. */
.sidebar-ico { flex: 0 0 22px; width: 22px; display: inline-flex; align-items: center; justify-content: center; }
.sidebar-ico svg { width: 19px; height: 19px; stroke: currentColor; }
.sidebar-nav a.active .sidebar-ico svg { color: var(--accent); }
.sidebar-label { overflow: hidden; text-overflow: ellipsis; }
.sidebar-sec {
  font-size: 11px; text-transform: uppercase; letter-spacing: .7px;
  color: var(--txt-faint); font-weight: 600; padding: 14px 12px 4px; white-space: nowrap;
}
.nav-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
/* ⚠ `isolation: isolate` pe conținut: fără el, orice `z-index` dinăuntru CONCUREAZĂ cu banda
   de sus (10) și cu bara de jos. Butonul ⋮ al unui card NVR avea 20 și trecea peste ele —
   se vedea plutind lângă ☰, deasupra antetului, fără nicio legătură cu cardul lui (semnalat
   de Bogdan cu poze). Izolarea îl închide în conținut, indiferent ce număr are.
   Numerele de dinăuntru rămân relative între ele: antet de comună 8 > panou de meniu 7 >
   antet de județ 5 > buton ⋮ 2. Un panou se deschide ÎN JOS, deci nu urcă niciodată peste
   antet — dar dacă rândul lui e derulat sub antet, se ascunde odată cu el, cum se cuvine. */
.app-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.app-main > main, .app-main > .content { isolation: isolate; }
.app-main .topbar { gap: 12px; }

/* retras (rail): doar iconuri; ascunde etichetele + brand-text + secțiuni */
.app.nav-collapsed .sidebar { flex-basis: 60px; width: 60px; }
.app.nav-collapsed .sidebar-label,
.app.nav-collapsed .sidebar-sec,
.app.nav-collapsed .brand-name { display: none; }
.app.nav-collapsed .sidebar-brand { padding: 0; justify-content: center; }
.app.nav-collapsed .sidebar-nav { padding: 10px 6px; }
.app.nav-collapsed .sidebar-nav a { justify-content: center; padding: 8px 0; }

/* ⚠ PLASĂ GLOBALĂ: orice regulă de autor cu `display` BATE `[hidden]{display:none}` din
   stylesheet-ul browserului (author > UA), deci un element ascuns din JS prin `.hidden = true`
   rămâne VIZIBIL dacă clasa lui setează `display`. Greșeala a apărut deja de trei ori în
   proiect: `.nav-scrim` (voalul fura toate tap-urile pe mobil), `.modal-overlay`, și
   `#ob-uisp-wrap` din wizard (câmpul UISP rămânea vizibil și editabil la comunele DIGI).
   Regula asta o închide o dată pentru tot portalul. */
[hidden] { display: none !important; }

/* scrim (doar mobil, când meniul e deschis peste conținut) */
.nav-scrim { display: none; }

@media (max-width: 820px) {
  /* mobil: sidebar off-canvas peste conținut; retras by default, toggle îl aduce */
  .sidebar {
    position: fixed; top: 0; left: 0; z-index: 40;
    flex-basis: 246px; width: 246px;
    transform: translateX(-100%); transition: transform .2s ease;
  }
  .app.nav-open .sidebar { transform: translateX(0); box-shadow: var(--shadow); }
  .app.nav-collapsed .sidebar { flex-basis: 246px; width: 246px; }  /* rail-ul e concept desktop */
  .app.nav-collapsed .sidebar-label, .app.nav-collapsed .sidebar-sec, .app.nav-collapsed .brand-name { display: revert; }
  /* scrim vizibil DOAR când meniul e deschis (nav-open). Fără scoparea asta, regula de autor
     .nav-scrim{display:block} suprascrie atributul [hidden] (author > UA) → voalul acoperă tot
     ecranul pe mobil și fură toate tap-urile (bug: „totul e gri, nu pot apăsa nimic"). */
  .app.nav-open .nav-scrim { display: block; position: fixed; inset: 0; z-index: 30; background: rgba(0,0,0,.5); }
  .wrap { padding: 20px 16px 48px; }
}
@media (prefers-reduced-motion: reduce) { .sidebar { transition: none; } }

/* conținutul umple zona din dreapta side-meniului (full-viewport, se adaptează la
   rezoluția displayului); fără coloană centrată fixă → fără spațiu mort pe laterale */
.wrap { width: 100%; max-width: none; margin: 0; padding: 24px 32px 60px; }
.foot { text-align: center; color: var(--txt-faint); font-size: 12px; padding: 24px; border-top: 1px solid var(--line-sect); }

.section-h { font-size: 13px; text-transform: uppercase; letter-spacing: .8px; color: var(--txt-dim); font-weight: 600; margin: 13px 0 8px; }
.section-h .count { color: var(--txt-faint); font-weight: 500; margin-left: 4px; }

/* pagina de nepotriviri de corelare (fleet-wide) */
.page-header { margin-bottom: 20px; }
.page-header h1 { font-size: 22px; font-weight: 700; letter-spacing: -.3px; margin: 0 0 8px; }
.corr-site { margin-top: 30px; padding-top: 18px; border-top: 1px solid var(--line); }
.corr-site:first-of-type { border-top: none; }
.corr-site-h { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; font-size: 17px; font-weight: 700; letter-spacing: -.2px; margin: 0 0 4px; }
.corr-site-h a { color: var(--txt); }
.corr-site-h a:hover { color: var(--accent); }
.corr-site-h .count { font-size: 12px; font-weight: 500; color: var(--txt-faint); text-transform: none; letter-spacing: 0; }

/* zona de nepotriviri de corelare, colapsabilă, în pagina de comună (doar admin) */
.corr-zone { margin-top: 18px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel-2); box-shadow: var(--shadow); }
.corr-zone-sum { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 10px; padding: 12px 16px; }
.corr-zone-sum::-webkit-details-marker { display: none; }
.corr-zone-sum::before { content: ""; width: 14px; height: 14px; flex: none; background: currentColor; color: var(--txt-faint); -webkit-mask: var(--ico-dreapta) center/contain no-repeat; mask: var(--ico-dreapta) center/contain no-repeat; transition: transform .15s; }
.corr-zone[open] > .corr-zone-sum::before { transform: rotate(90deg); }
.corr-zone[open] > .corr-zone-sum { border-bottom: 1px solid var(--line-sect); }
.corr-zone-sum:hover { background: var(--panel); border-radius: var(--radius); }
.corr-zone-title { font-size: 14px; font-weight: 700; color: var(--txt-dim); text-transform: uppercase; letter-spacing: .5px; }
.corr-zone-note { margin-left: auto; font-size: 12px; color: var(--txt-faint); font-style: oblique 8deg; font-synthesis: style; }
.corr-zone > h3, .corr-zone > p, .corr-zone > .panel { margin-left: 16px; margin-right: 16px; }
.corr-zone > .panel:last-child { margin-bottom: 16px; }
.corr-zone > h3.section-h:first-of-type { margin-top: 14px; }
.empty-state { display: flex; align-items: center; gap: 14px; padding: 20px 22px; color: var(--txt-dim); }
.empty-state strong { color: var(--txt); }

/* strip de pastile per comună (sub KPI) — click = filtrează la o singură comună */
.corr-strip { display: flex; flex-wrap: wrap; gap: 12px; margin: 20px 0 8px; }
.corr-pill { flex: 1 1 200px; max-width: 320px; background: var(--panel); border: 1.5px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px; cursor: pointer; box-shadow: var(--shadow); transition: transform .12s ease; }
.corr-pill:hover { transform: translateY(-1px); }
.corr-pill:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .corr-pill { transition: none; } .corr-pill:hover { transform: none; } }
.corr-pill.sev-dup { border-color: var(--down); background: linear-gradient(var(--down-bg), var(--down-bg)), var(--panel); }
.corr-pill.sev-warn { border-color: var(--warn); background: linear-gradient(var(--warn-bg), var(--warn-bg)), var(--panel); }
.corr-pill.corr-pill-sel { transform: translateY(-1px); }
.corr-pill.sev-dup.corr-pill-sel { box-shadow: 0 0 0 2.5px var(--down), var(--shadow); }
.corr-pill.sev-warn.corr-pill-sel { box-shadow: 0 0 0 2.5px var(--warn), var(--shadow); }
.corr-pill-head { display: flex; align-items: baseline; gap: 8px; }
.corr-pill-name { font-weight: 700; font-size: 14px; letter-spacing: .2px; }
.corr-pill-total { margin-left: auto; font-size: 13px; font-weight: 700; color: var(--txt-dim); font-variant-numeric: tabular-nums; }
.corr-pill-break { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 12px; font-variant-numeric: tabular-nums; }
.cp-dup { color: var(--down); }
.cp-noont { color: var(--warn); }
.cp-orphan { color: var(--txt-dim); }
.county-h { display: flex; align-items: baseline; gap: 11px; margin: 14px 0 9px; padding-bottom: 7px; border-bottom: 1px solid var(--line); }
.county-h:first-of-type { margin-top: 6px; }
.county-h .dot { align-self: center; }
.county-name { font-size: 17px; font-weight: 700; letter-spacing: .2px; margin: 0; }
.county-count { font-size: 12px; color: var(--txt-faint); font-weight: 500; }
.county-sum { margin-left: auto; font-size: 12.5px; color: var(--txt-dim); font-weight: 500; }
.county-sum-down { color: var(--down); }
.county-sum-stale { color: var(--stale); }
.county-sum-warn { color: var(--warn); }
.county-sum-ok { color: var(--ok); }

/* strip rezumat NVR (pagina de comună) */
/* pastilele cresc egal ca să umple TOATĂ lățimea (fără spațiu gol în dreapta);
   justify-content:center centrează când sunt puține (1–2 NVR nu se întind grotesc). */
.nvr-strip { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin: 12px 0 8px; }
.nvr-card { flex: 1 1 220px; max-width: 420px; background: var(--panel); border: 1.5px solid var(--line);
  border-radius: 10px; padding: 9px 13px; box-shadow: var(--shadow); cursor: pointer;
  transition: transform .08s ease, box-shadow .12s ease; }
/* ⚠ RIDICAREA LA HOVER, doar unde există cu adevărat un cursor. Pe iOS `:hover` RĂMÂNE LIPIT
   după o atingere, iar `transform` creează un CONTEXT DE STIVUIRE: panoul meniului ⋮ rămânea
   prins în cardul atins și era acoperit de cardul de dedesubt (semnalat de Bogdan cu poză —
   se vedea doar o fâșie din meniul lui NVR 1, sub NVR 3).
   Reprodus în WebKit punând `transform` pe card: panoul trece de la „deasupra" la „acoperit". */
@media (hover: hover) { .nvr-card:hover { transform: translateY(-1px); } }
.nvr-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* card selectat (filtru activ pe acel NVR): margine îngroșată ÎN CULOAREA DE STARE
   (nu albastru), + lift. Inelul de aceeași culoare dublează vizual grosimea bordurii. */
.nvr-card.nvr-card-sel { transform: translateY(-1px); }
.nvr-card-ok.nvr-card-sel { box-shadow: 0 0 0 2.5px var(--ok), var(--shadow); }
.nvr-card-warn.nvr-card-sel { box-shadow: 0 0 0 2.5px var(--warn), var(--shadow); }
.nvr-card-down.nvr-card-sel { box-shadow: 0 0 0 2.5px var(--down), var(--shadow); }
.nvr-card-stale.nvr-card-sel { box-shadow: 0 0 0 2.5px var(--stale), var(--shadow); }
@media (prefers-reduced-motion: reduce) { .nvr-card { transition: none; } .nvr-card:hover { transform: none; } }
/* culoarea de stare pe TOT conturul + fundal ușor tentat (compus peste panel) */
.nvr-card.nvr-card-ok { border-color: var(--ok); background: linear-gradient(var(--ok-bg), var(--ok-bg)), var(--panel); }
.nvr-card.nvr-card-warn { border-color: var(--warn); background: linear-gradient(var(--warn-bg), var(--warn-bg)), var(--panel); }
.nvr-card.nvr-card-down { border-color: var(--down); background: linear-gradient(var(--down-bg), var(--down-bg)), var(--panel); }
.nvr-card.nvr-card-maint { border-color: var(--maint); background: linear-gradient(var(--maint-bg), var(--maint-bg)), var(--panel); }
.nvr-card-maint.nvr-card-sel { box-shadow: 0 0 0 2.5px var(--maint), var(--shadow); }
.nvr-reason-maint { color: var(--maint); }
/* Meniul ⋮ din colțul cardului NVR. Cardul e el însuși buton de filtrare, deci meniul stă
   deasupra (z-index) și JS-ul îi oprește propagarea click-ului.
   ⚠ SPECIFICITATE, nu ordine: elementul poartă ȘI `.row-menu` (JS-ul îl caută cu
   `closest('.row-menu')` ca să-l închidă după acțiune), iar `.row-menu{position:relative}` e
   declarat mai jos în fișier — la specificitate egală câștiga el, meniul cădea în flux și
   apărea sus-STÂNGA, împingând conținutul cardului. Prefixul `.nvr-card` face regula imună la
   poziția în fișier, deci nu se mai strică la următoarea clasă adăugată la coadă. */
.nvr-card { position: relative; }
.nvr-card .nvr-menu { position: absolute; top: 6px; right: 6px; z-index: 2; }
.nvr-card .nvr-menu > summary { width: 26px; height: 26px; font-size: 16px; }
@media (max-width: 620px) { .nvr-card .nvr-menu > summary { width: 40px; height: 40px; } }
/* Butonul preia STAREA cardului pe bordură, textul rămâne neutru. ⚠ Culoarea de stare ca
   TEXT pică sub 4.5:1 pe light-warn (4.27) și light-down (4.00) — gliful ⋮ nu se califică
   drept „text mare". Bordura e element non-text (WCAG 1.4.11, prag 3:1) → trece confortabil. */
.nvr-card .nvr-menu > summary { background: transparent; color: var(--txt-dim); }
.nvr-card-ok    .nvr-menu > summary { border-color: var(--ok-line); }
.nvr-card-warn  .nvr-menu > summary { border-color: var(--warn-line); }
.nvr-card-down  .nvr-menu > summary { border-color: var(--down-line); }
.nvr-card-stale .nvr-menu > summary { border-color: var(--stale-line); }
.nvr-card-maint .nvr-menu > summary { border-color: var(--maint-line); }
.nvr-card .nvr-menu > summary:hover,
.nvr-card .nvr-menu[open] > summary { background: var(--panel); color: var(--txt); }
.nvr-card .nvr-menu > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nvr-card.nvr-card-stale { border-color: var(--stale); background: linear-gradient(var(--stale-bg), var(--stale-bg)), var(--panel); }
/* ⚠ Spațiu rezervat pentru meniul ⋮ (poziționat absolut în colțul din dreapta-sus): fără el,
   IP-ul (împins la margine cu `margin-left:auto`) și contorul de discuri (ultimul dintr-un
   `space-between`) ajung EXACT sub buton. Se aplică pe cele două rânduri de sus — rândul de
   motiv e sub butonul de 26px, deci nu-l atinge. Pe mobil butonul e 40px, deci și marja crește. */
.nvr-card .nvr-card-head, .nvr-card .nvr-card-count { padding-right: 32px; }
@media (max-width: 620px) {
  .nvr-card .nvr-card-head, .nvr-card .nvr-card-count { padding-right: 46px; }
}
.nvr-card-head { display: flex; align-items: center; gap: 8px; }
.nvr-card-head .dot { width: 8px; height: 8px; flex: none; }
.nvr-card-name { font-weight: 700; font-size: 14px; letter-spacing: .2px; }
/* ⚠ E acum LINK către interfața NVR-ului (2026-08-12), deci trebuie să arate ca un link, nu
   ca text șters: culoare de text obișnuit, subliniere doar la hover. `font-size` urcat de la
   12px — e o adresă pe care omul chiar o citește și o tastează, nu o notă de subsol. */
.nvr-card-host { margin-left: auto; font-size: 13.5px; color: var(--txt-dim);
  font-variant-numeric: tabular-nums; text-decoration: none; }
.nvr-card-host:hover { color: var(--accent); text-decoration: underline; }
.nvr-card-host:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px;
  border-radius: 3px; }
.nvr-card-count { margin-top: 6px; display: flex; justify-content: space-between; gap: 10px; font-size: 13px; color: var(--txt-dim); font-variant-numeric: tabular-nums; font-style: oblique 8deg; font-synthesis: style; }
.nvr-cell { white-space: nowrap; font-variant-numeric: tabular-nums; }
.ev-kind { font-size: 11px; font-weight: 700; padding: 2px 9px; border-radius: 999px; letter-spacing: .3px; }
.ev-kind-cam { color: var(--accent); background: var(--accent-bg); }
.ev-kind-ont { color: var(--txt-dim); background: var(--panel-2); border: 1px solid var(--line); }
.ev-kind-maint { color: var(--maint); background: var(--maint-bg); border: 1px solid var(--maint-line); }
.ev-detail { display: block; margin-top: 3px; color: var(--txt-faint); font-size: 12px; white-space: normal; }
.nvr-card-disks { color: var(--txt-faint); }
.nvr-card-disks.bad { color: var(--down); }
.nvr-card-reason { margin-top: 3px; font-size: 12px; line-height: 1.35; }
.nvr-card-reason.nvr-reason-down { color: var(--down); }
.nvr-card-reason.nvr-reason-warn { color: var(--warn); }
.nvr-card-reason.nvr-reason-stale { color: var(--stale); }

/* bară de filtre (tabel unificat) */
/* ⚠ `scroll-margin-top` = înălțimea barei de sus + puțin aer. Bara e `position: sticky`,
   deci fără offset `scrollIntoView` ar duce filtrele exact SUB ea — adică pastila te-ar
   trimite undeva unde tot nu vezi ce s-a filtrat. */
.filterbar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 10px 0 10px;
  scroll-margin-top: 70px; }
/* Rândul-țintă, venind din harta N3 (`/site/x#cam-R41`). Același offset pentru bara sticky.
   Evidențierea e un puls scurt, nu o culoare permanentă: spune «aici e», apoi se dă la o
   parte, ca să nu concureze cu bulinele de severitate ale rândului. */
.camrow { scroll-margin-top: 70px; }
.cam-tinta { animation: tinta 2.4s ease-out 1; }
@keyframes tinta {
  0%, 55% { background: var(--accent-bg); box-shadow: inset 3px 0 0 var(--accent); }
  100%    { background: transparent; box-shadow: none; }
}
@media (prefers-reduced-motion: reduce) { .cam-tinta { animation: none;
  background: var(--accent-bg); box-shadow: inset 3px 0 0 var(--accent); } }
/* Titlul comunei și numele camerei din panoul N3 = legături către pagina comunei.
   ⚠ FĂRĂ linie sub text: pusesem un `border-bottom` punctat ca semn de link, dar în capul
   panoului arată exact ca o subliniere scăpată (semnalat de Bogdan). Afordanța rămâne
   cursorul și culoarea la hover — destul într-un panou unde restul textului e static. */
.n3-panel-link, .n3-cam-link {
  color: inherit; font-weight: 700; text-decoration: none; cursor: pointer;
  transition: color .12s ease;
}
.n3-panel-link:hover, .n3-cam-link:hover { color: var(--accent); }
.n3-panel-link:focus-visible, .n3-cam-link:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px;
}
.filt { background: var(--panel-2); color: var(--txt); border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; font-size: 13px; font-family: inherit; }
.filt:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.filt-q { flex: 1; min-width: 160px; }
.filt-count { color: var(--txt-faint); font-size: 12px; margin-left: auto; font-variant-numeric: tabular-nums; }
.filt-dd { width: 178px; flex: none; }
.filt-dd .sel-current { overflow: hidden; text-overflow: ellipsis; }
/* ⛔ FĂRĂ plafon de lățime pe textele explicative. Măsurat, e drept, că la 1920 rândurile
   ajung la ~214 caractere — dar plafonul a fost încercat de TREI ori și respins de fiecare
   dată pentru același motiv: pe un panou lat, un paragraf îngust arată a text neterminat, și
   se rupe pe trei rânduri acolo unde ar încăpea pe unul. Ultima oară, 08 sep, Bogdan:
   „în loc să fie pe toată lățimea paginii, e pe o lățime fixă și se duce aiurea pe 3 rânduri".
   ⚠ Dacă revine ideea: problema NU e valoarea plafonului (70ch, 90ch, 38em — toate trei
   respinse), e faptul că textul introductiv stă într-un panou cât pagina. S-ar rezolva
   îngustând ZONA, nu paragraful. Până atunci, nu se pune la loc. */
/* ⚠ Un text explicativ APARȚINE blocului de deasupra (deci `--sp-2`, treapta „bloc și
   lămurirea lui") și e despărțit de ce urmează (deci `--sp-3`, rang diferit). Avea `margin-top`
   ZERO, deci stătea LIPIT de elementul de deasupra — măsurat 0px pe pagina de grupuri, în trei
   locuri. Semnalat de Bogdan: „spațierea inexistentă între textele gri și restul". */
.hint { color: var(--txt-dim); font-size: 12.5px; margin: var(--sp-2) 0 var(--sp-3); }

/* pop-up istoric (nu schimbă pagina) */
.modal-overlay { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center;
  padding: 20px; background: rgba(0,0,0,.6); backdrop-filter: blur(2px); }
.modal-overlay[hidden] { display: none; }
/* ⚠ `calc(100vh - 48px)`, nu `85vh`. Plafonul de 85% era o alegere arbitrară care lăsa 15%
   din fereastră nefolosiți TOCMAI cînd fereastra e mică — adică exact cînd contează. Cu 24px
   marjă sus și jos, aceeași fereastră care derula la un viewport de 840 acum încape de la 769
   în sus. ⚠ Contează fiindcă plafonul e din VIEWPORT, nu din ecran: Safari cu bară de file,
   de adrese și de semne de carte lasă dintr-un ecran de 1080 un viewport de ~850–940, iar
   diferența variază de la om la om. */
.modal-dialog { position: relative; width: 100%; max-width: 640px;
  max-height: calc(100vh - 48px); overflow-y: auto;
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px; box-shadow: 0 20px 60px rgba(0,0,0,.5);
  padding: 6px 22px 22px; }
.modal-close { position: absolute; top: 12px; right: 12px; width: 30px; height: 30px; border-radius: 8px; cursor: pointer;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--txt-dim); font-size: 14px; line-height: 1; }
.modal-close:hover { color: var(--txt); border-color: var(--line-strong); }
.modal-body .site-header { margin-top: 8px; }
.nvr-reason { margin-left: 10px; font-weight: 500; text-transform: none; letter-spacing: 0; font-size: 12px; }
.nvr-reason-down { color: var(--down); }
.nvr-reason-warn { color: var(--warn); }

/* ---- KPIs ---- */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr)); gap: 12px; margin-bottom: 6px; }
.kpi {
  position: relative; overflow: hidden;
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 18px; box-shadow: var(--shadow); text-align: center;
}
.kpi-val { font-size: 28px; font-weight: 800; line-height: 1; letter-spacing: -1px; font-variant-numeric: tabular-nums; }
.kpi-sub { font-size: 17px; color: var(--txt-faint); font-weight: 700; letter-spacing: -.5px; }
.kpi-lbl { margin-top: 4px; color: var(--txt-dim); font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; }
/* KPI-urile de stare: bordură colorată uniform (token) + un fir subțire sus, în aceeași culoare */
.kpi-warn { border-color: var(--warn-line); }
.kpi-warn .kpi-val { color: var(--warn); }
.kpi-stale { border-color: var(--stale-line); }
.kpi-stale .kpi-val { color: var(--stale); }
.kpi-down { border-color: var(--down-line); }
.kpi-down .kpi-val { color: var(--down); }
.kpi-warn::before, .kpi-stale::before, .kpi-down::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
}
.kpi-warn::before  { background: var(--warn); }
.kpi-stale::before { background: var(--stale); }
.kpi-down::before  { background: var(--down); }
.kpi-maint { border-color: var(--maint-line); }
.kpi-maint .kpi-val { color: var(--maint); }
.kpi-maint::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--maint); }

/* Pastile-filtru: afordanță de buton + stare activă, ca la cardurile NVR (`.nvr-card`).
   „Hard Disk"/„ONT" NU primesc `.kpi-f` — numără alte entități decât rândurile tabelului,
   deci au doar ancora `.kpi-jump` către secțiunea lor. */
.kpi-f { cursor: pointer; transition: transform .12s, box-shadow .12s; user-select: none; }
.kpi-f:hover { transform: translateY(-1px); box-shadow: 0 2px 4px rgba(0,0,0,.08), var(--shadow); }
.kpi-f:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.kpi-sel { box-shadow: 0 0 0 2.5px var(--accent), var(--shadow); }
/* ⚠ `.kpi-f:hover` (0-2-0) bate `.kpi-sel` (0-1-0) INDIFERENT de ordine → pe o pastilă
   selectată, hover-ul îi ștergea inelul de selecție. Combo explicit, cu specificitate ≥. */
.kpi-f.kpi-sel:hover { box-shadow: 0 0 0 2.5px var(--accent), var(--shadow); transform: translateY(-1px); }
.kpi-jump { color: var(--txt-dim); font-weight: 700; text-decoration: underline; margin-left: 4px; }
.kpi-jump:hover { color: var(--accent); }
.filt-clear { background: none; border: none; color: var(--accent); font: inherit; cursor: pointer; text-decoration: underline; padding: 0; }

/* ---- mentenanță: modal + formular ---- */
.maint-current { border: 1px solid var(--maint-line); background: var(--maint-bg); border-radius: var(--radius); padding: 10px 12px; margin-bottom: 12px; }
.maint-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }
.maint-tag { display: inline-block; padding: 2px 9px; border-radius: 20px; font-size: 11.5px; font-weight: 700; color: var(--maint); background: var(--panel); border: 1px solid var(--maint-line); }
.maint-progress { margin-top: 6px; font-size: 12.5px; font-weight: 700; color: var(--txt-dim); font-variant-numeric: tabular-nums; }
.maint-updated { margin-top: 3px; font-size: 12.5px; }
/* acțiunea de scoatere din meniul ⋮ = distructivă-ușoară, marcată ca atare */
.row-menu-panel .btn-maint-close { color: var(--down); }
.maint-hint { margin-top: 6px; }
.maint-notes { margin-bottom: 12px; }
.maint-types { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.maint-type { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius); cursor: pointer; }
.maint-type:hover { border-color: var(--line-strong); }
.maint-type:has(input:checked) { border-color: var(--accent); background: var(--accent-bg); }
.maint-type input { margin-top: 3px; flex: none; }
.maint-type-txt { display: flex; flex-direction: column; gap: 2px; }
.maint-type-txt em { font-style: normal; color: var(--txt-dim); font-size: 12px; }
.maint-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.maint-close { color: var(--down); }

/* ---- dots ---- */
.dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex: none; }
.dot-lg { width: 14px; height: 14px; }
/* verde = static (e ok, nu-ți fură atenția); galben/roșu/stale = pulsează (problemă) */
.dot-ok { background: var(--ok); box-shadow: 0 0 0 3px var(--ok-bg); }
.dot-warn { background: var(--warn); box-shadow: 0 0 0 3px var(--warn-bg); animation: pulse-warn 1.8s infinite; }
.dot-down { background: var(--down); box-shadow: 0 0 0 3px var(--down-bg); animation: pulse 1.8s infinite; }
.dot-stale { background: var(--stale); box-shadow: 0 0 0 3px var(--stale-bg); animation: pulse-stale 1.8s infinite; }
/* maint = static, ca `ok`: e o stare cunoscută și asumată, nu are voie să-ți fure atenția */
.dot-maint { background: var(--maint); box-shadow: 0 0 0 3px var(--maint-bg); }
/* fără stare: un stick băgat în NVR nu e nici bun, nici stricat — e doar prezent. Contur gol,
   ca să se vadă că rândul e informativ, nu o judecată despre sănătatea unui disc. */
.dot-none { background: transparent; box-shadow: inset 0 0 0 2px var(--txt-dim); }
@keyframes pulse { 0%,100% { box-shadow: 0 0 0 3px var(--down-bg); } 50% { box-shadow: 0 0 0 8px rgba(255,77,94,.20); } }
@keyframes pulse-warn { 0%,100% { box-shadow: 0 0 0 3px var(--warn-bg); } 50% { box-shadow: 0 0 0 8px rgba(245,166,35,.20); } }
@keyframes pulse-stale { 0%,100% { box-shadow: 0 0 0 3px var(--stale-bg); } 50% { box-shadow: 0 0 0 8px rgba(177,138,255,.20); } }
@media (prefers-reduced-motion: reduce) { .dot-warn, .dot-down, .dot-stale { animation: none; } }

/* ---- site grid ---- */
/* 320px minim → pe 1920 (lățime utilă 1610 = 1920 − 246 sidebar − 64 padding) intră exact 4
   carduri, fiindcă 5×320+48 = 1698 > 1610. Sub asta, auto-fill scade singur la 3 / 2 / 1. */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); gap: 12px; align-items: stretch; }
.site {
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 9px 14px; box-shadow: var(--shadow);
  min-height: 44px;
  display: flex; flex-direction: column;   /* fill pe înălțimea rândului → carduri egale */
  transition: transform .12s ease, border-color .12s ease;   /* border UNIFORM colorat pe stare */
}
.site:hover { transform: translateY(-2px); border-color: var(--accent); box-shadow: 0 6px 20px rgba(0,0,0,.35); }
.site.st-ok { border-color: var(--ok-line); }
.site.st-warn { border-color: var(--warn-line); }
.site.st-down { border-color: var(--down-line); }
.site.st-stale { border-color: var(--stale-line); }
.site-head { display: flex; align-items: center; gap: 9px; }
/* Numele comunei nu se rupe pe două rânduri, oricât de îngust ar fi cardul („Balta Doamnei",
   „Bretea Română"). Cardurile mai late de mai sus rezolvă cazul obișnuit; asta e garda care
   ține la orice lățime — dacă tot nu încape, se taie cu „…", nu strică înălțimea cardului. */
.site-name { font-weight: 600; font-size: 16px; white-space: nowrap; overflow: hidden;
             text-overflow: ellipsis; min-width: 0; }
.site-status { flex: none; }
.site-status { margin-left: auto; font-size: 12px; font-weight: 600; color: var(--txt-dim); }
.site.st-down .site-status { color: var(--down); }
.site.st-warn .site-status { color: var(--warn); }
.site.st-stale .site-status { color: var(--stale); }
.site.st-ok .site-status { color: var(--ok); }
/* rând stare secundar: motivul (stânga, colorat pe stare) + „actualizat acum X" (dreapta, sub status) */
.site-sub { display: flex; align-items: baseline; gap: 10px; margin-top: 4px; }
.site-reason { font-size: 12px; font-style: oblique 8deg; font-synthesis: style; color: var(--txt-dim); line-height: 1.3; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.site.st-down .site-reason { color: var(--down); }
.site.st-stale .site-reason { color: var(--stale); }
.site-body { display: flex; align-items: center; gap: 14px; margin: 6px 0; }
.metric-val { font-size: 19px; font-weight: 700; line-height: 1.1; }
.metric-sub { color: var(--txt-faint); font-size: 14px; font-weight: 500; }
.metric-lbl { font-size: 13px; color: var(--txt-dim); letter-spacing: .3px; margin-left: 6px; }
.cam-bar { flex: 1; display: flex; height: 8px; border-radius: 6px; overflow: hidden; background: var(--bg-2); }
.seg { display: block; }
.seg-ok { background: var(--ok); }
.seg-warn { background: var(--warn); }
.seg-down { background: var(--down); }
.seg-maint { background: var(--maint); }
/* semnul discret de pe cardul comunei din flotă */
.site-maint { font-size: 12px; line-height: 1; cursor: help; opacity: .85; }
.site-foot { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; margin-top: auto; }
.chip { font-size: 11px; color: var(--txt-dim); background: var(--bg-2); border: 1px solid var(--line-soft); border-radius: 20px; padding: 2px 8px; white-space: nowrap; }
.chip-alert { color: var(--warn); border-color: rgba(245,166,35,.3); }
.chip-disk-ok { color: var(--ok); border-color: rgba(46,204,113,.3); }
.chip-disk-bad { color: var(--down); border-color: rgba(255,77,94,.3); }
.chip-uisp { color: var(--uisp-fg); border-color: var(--uisp-border); }                 /* fibră proprie — alb-albăstrui UISP */
.chip-digi { color: var(--digi-fg); border-color: var(--digi-border); background: var(--digi-bg); }  /* transport DIGI — albastru logo #002BFE */

.empty { grid-column: 1/-1; text-align: center; color: var(--txt-dim); padding: 60px 20px; }
.empty code { background: var(--panel); padding: 2px 7px; border-radius: 5px; color: var(--txt); }
.empty h1 { font-size: 48px; margin: 0 0 8px; }

/* ---- site header ---- */
.crumbs { color: var(--txt-dim); font-size: 13px; margin-bottom: 10px; }
.crumbs a:hover { color: var(--txt); }
.crumbs span { margin: 0 6px; color: var(--txt-faint); }

/* zona de administrare: bară de tab-uri (segmented control) + conținut compact */
.admin-tabs { display: flex; flex-wrap: wrap; gap: 5px; margin: 2px 0 18px; padding: 5px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 12px; }
.admin-tab { padding: 8px 16px; border-radius: 8px; font-size: 13.5px; font-weight: 600;
  color: var(--txt-dim); text-decoration: none; white-space: nowrap; transition: background .12s, color .12s; }
.admin-tab:hover { background: var(--panel); color: var(--txt); }
.admin-tab.active { background: var(--accent); color: #fff; }
.admin-tab.active:hover { background: var(--accent); color: #fff; }
.admin-intro { margin: 0 0 14px; }

/* dialog „deconectat pentru inactivitate" (auto-refresh detectează sesiunea moartă) */
.idle-modal { position: fixed; inset: 0; z-index: 2000; display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.62); backdrop-filter: blur(3px); }
.idle-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 28px 34px; max-width: 400px; text-align: center; box-shadow: var(--shadow); }
.idle-ico { font-size: 34px; margin-bottom: 8px; }
.idle-card h3 { margin: 0 0 8px; font-size: 20px; }
.idle-card p { margin: 0 0 22px; color: var(--txt-dim); line-height: 1.5; }
.idle-card button { background: var(--accent); color: #fff; border: none; border-radius: 8px;
  padding: 11px 24px; font: inherit; font-weight: 600; font-size: 14px; cursor: pointer; }
.idle-card button:hover { filter: brightness(1.08); }

/* bară de progres la schimbarea paginii (feedback că se încarcă) */
#page-loader { position: fixed; top: 0; left: 0; height: 3px; width: 0; z-index: 3000; opacity: 0;
  background: var(--accent); box-shadow: 0 0 10px var(--accent), 0 0 4px var(--accent);
  transition: width .35s cubic-bezier(.2,.8,.3,1), opacity .25s ease; pointer-events: none; }
#page-loader.active { width: 92%; opacity: 1; }
@media (prefers-reduced-motion: reduce) { #page-loader { transition: opacity .2s ease; } }
.site-header {
  position: relative;
  display: flex; align-items: center; gap: 16px; margin-bottom: 13px;
  background: var(--panel); border: 1px solid var(--line); border-left: 4px solid var(--line);
  border-radius: var(--radius); padding: 13px 24px; box-shadow: var(--shadow);
}
/* Badge „necesită atenție — corelare": centrat sus pe pastila comunei, doar admin când există nepotriviri */
.corr-flag {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  display: inline-flex; align-items: center; gap: 6px; z-index: 3;
  background: var(--down); color: #fff; text-decoration: none;
  padding: 3px 13px; border-radius: 999px; white-space: nowrap;
  font-size: 12.5px; font-weight: 600; letter-spacing: .2px;
  box-shadow: 0 2px 8px rgba(255, 77, 94, .35);
}
.corr-flag:hover { filter: brightness(1.08); }
.site-header.st-ok { border-left-color: var(--ok); }
.site-header.st-warn { border-left-color: var(--warn); }
.site-header.st-down { border-left-color: var(--down); }
.site-header.st-stale { border-left-color: var(--stale); }
.site-header-main { display: flex; align-items: center; gap: 16px; }
.site-header h1 { margin: 0; font-size: calc(16px + 8px); }
.site-county { color: var(--txt-dim); font-weight: 400; }
.site-meta { color: var(--txt-dim); font-size: calc(12px + 1px); margin-top: 4px; }
.meta-age { font-style: oblique 8deg; font-synthesis: style; }
.site-header-status { margin-left: auto; text-align: right; }
.site-status-label { font-size: 19px; font-weight: 700; letter-spacing: .2px; }
.site-header.st-ok .site-status-label { color: var(--ok); }
.site-header.st-warn .site-status-label { color: var(--warn); }
.site-header.st-down .site-status-label { color: var(--down); }
.site-header.st-stale .site-status-label { color: var(--stale); }
.site-header-reason { display: block; font-size: calc(12px + 2px); margin-top: 4px; color: var(--txt-dim); }
.site-header.st-down .site-header-reason { color: var(--down); }
.site-header.st-stale .site-header-reason { color: var(--stale); }
.site-header.st-warn .site-header-reason { color: var(--warn); }

.badge { font-size: 11px; padding: 2px 8px; border-radius: 6px; margin-left: 6px; }
.badge-warn { background: var(--warn-bg); color: var(--warn); }
/* etichete de stare pe pastila comunei (Setări site-uri): litere mari, culori clare */
.badge-ct-ok, .badge-ct-no, .badge-uisp, .badge-digi-c { display: inline-flex; align-items: center; justify-content: center; line-height: 1; text-transform: uppercase; font-weight: 700; letter-spacing: .5px; font-size: 10px; border-radius: 5px; padding: 3px 9px; }
.badge-ct-ok { background: var(--ok-bg); color: var(--ok); border: 1px solid rgba(46,204,113,.4); }
.badge-ct-no { background: var(--warn-bg); color: var(--warn); border: 1px solid rgba(245,166,35,.4); }
.badge-uisp { background: var(--uisp-bg); color: var(--uisp-fg); border: 1px solid var(--uisp-border); }  /* neutru alb-gri, contrast cu DIGI albastru */
.badge-digi-c { background: var(--digi-bg); color: var(--digi-fg); border: 1px solid var(--digi-border); }   /* albastru DIGI #002BFE */

/* ---- alerts ---- */
.alerts { display: flex; flex-direction: column; gap: 6px; }
.alert { display: flex; align-items: center; gap: 12px; padding: 8px 16px; border-radius: 9px; border: 1px solid var(--line); background: var(--panel); }
.alert.al-down { border-color: rgba(255,77,94,.35); background: linear-gradient(90deg, var(--down-bg), var(--panel) 40%); }
.alert.al-warn { border-color: rgba(245,166,35,.3); background: linear-gradient(90deg, var(--warn-bg), var(--panel) 40%); }
.alert.al-stale { border-color: rgba(177,138,255,.35); background: linear-gradient(90deg, var(--stale-bg), var(--panel) 40%); }
.al-subj { font-weight: 600; min-width: 90px; font-family: ui-monospace, monospace; font-size: .92em; }
.al-msg { color: var(--txt-dim); font-size: 13.5px; }

/* ---- device tables ---- */
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); }
.dev { width: 100%; border-collapse: collapse; }
/* lățimi de coloană fixe → margini de coloană consecvente (fără goluri inegale). Conținutul
   prea lung se taie curat cu „…" (are tooltip oricum). */
.dev-fixed { table-layout: fixed; }
.dev-fixed tbody td, .dev-fixed thead th { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* prima coloană = bulina de stare: fără clip (altfel se taie punctul), fără ellipsis */
.dev-fixed tbody td:first-child, .dev-fixed td.row-actions { overflow: visible; }
.dev thead th { text-align: left; font-size: 14px; letter-spacing: .2px; color: var(--txt-dim); font-weight: 700; padding: 11px 14px; border-bottom: 1px solid var(--line); }
.dev tbody td { padding: 11px 14px; border-bottom: 1px solid var(--line-soft); }
.dev tbody tr:last-child td { border-bottom: none; }
.dev tbody tr:hover { background: var(--panel-2); }
.dev tbody tr td:first-child { width: 30px; padding-left: 18px; }
/* tabelul de camere (97 rânduri) = rânduri mai strânse, mai puțin scroll */
#cam-table tbody td, #cam-table thead th { padding-top: 6px; padding-bottom: 6px; }
.cam-panel { overflow: visible; }   /* meniul ⋮ pe ultimele rânduri să nu fie tăiat */
/* Același lucru pentru ORICE panou care conține un meniu de rând — regula generică previne
   repetarea bug-ului la tabelele viitoare (discurile au avut-o exact așa: meniul ultimului
   rând era tăiat de `overflow:hidden` al panoului). Clasa explicită de mai jos rămâne ca
   plasă pentru browserele fără `:has()`. */
.panel:has(.row-menu) { overflow: visible; }
.strong { font-weight: 600; }
.dim { color: var(--txt-dim); }
.tick { color: var(--ok); }
.cross { color: var(--down); }
.sev-down td:first-child { box-shadow: inset 3px 0 0 var(--down); }
.sev-warn td:first-child { box-shadow: inset 3px 0 0 var(--warn); }
.sev-maint td:first-child { box-shadow: inset 3px 0 0 var(--maint); }
/* ⚠ AICI a fost `opacity: .72` pe td, ca rândul să pară estompat. DOUĂ defecte reale:
   1. `opacity` randează tot SUBARBORELE într-un buffer și îl compune la alpha — iar panoul
      meniului ⋮ (`position:absolute`, dar descendent DOM al celulei) intra în același buffer
      → meniul apărea transparent, cu conținutul de sub el vizibil. Un `opacity:1` pe copil
      NU poate anula alpha-ul unui ancestor.
   2. în tema light scădea contrastul textului `--txt-dim` pe alb sub pragul WCAG AA.
   Distincția vizuală o poartă oricum punctul gri + badge-ul „🔧 Mentenanță", deci regula a
   fost scoasă complet în loc să fie doar restrânsă. */
/* coloana „Stare" = pastilă colorată pe stare (se scanează dintr-o privire);
   restul coloanelor (Rec/Motion/ONT) rămân text colorat */
.ok-mark, .verdict { display: inline-block; padding: 2px 9px; border-radius: 20px; font-size: 11.5px; font-weight: 600; line-height: 1.5; border: 1px solid transparent; }
.ok-mark { color: var(--ok); background: var(--ok-bg); border-color: rgba(46,204,113,.3); }
.v-ok { color: var(--txt-dim); background: var(--bg-2); border-color: var(--line-soft); }
.v-warn { color: var(--warn); background: var(--warn-bg); border-color: rgba(245,166,35,.3); }
.v-down { color: var(--down); background: var(--down-bg); border-color: rgba(255,77,94,.3); }
.v-stale { color: var(--stale); background: var(--stale-bg); border-color: var(--stale-line); }
.v-maint { color: var(--maint); background: var(--maint-bg); border-color: var(--maint-line); cursor: help; }
/* „NVR jos": cameră fantomă (NVR inaccesibil) — border punctat = stare inferată, nu verificată direct */
.v-ghost { border-style: dashed; }
.warn-tag { color: var(--warn); margin-left: 6px; cursor: help; }
.btn-hist {
  font-size: 11.5px; color: var(--txt-dim); text-decoration: none;
  border: 1px solid var(--txt-dim); border-radius: 5px; padding: 2px 8px;
  white-space: nowrap; opacity: .75;
}
.btn-hist:hover { opacity: 1; color: var(--txt); border-color: var(--txt); }
.row-actions { white-space: nowrap; }
.row-actions .btn-map { margin-left: 6px; }
/* buton „hartă": placeholder acum, va duce la coordonatele camerei (modul hartă) */
.btn-map {
  font-size: 11.5px; color: var(--txt-dim); background: transparent;
  border: 1px solid var(--txt-dim); border-radius: 5px; padding: 2px 8px;
  white-space: nowrap; opacity: .75; cursor: pointer; font-family: inherit; line-height: 1.4;
}
/* Iconițele desenate din `_macros.html`. `currentColor` ⇒ preiau culoarea butonului, deci și
   starea de hover/apăsare — ce nu putea face un emoji. */
.ico { display: block; flex: none; width: 15px; height: 15px; }
.row-actions .btn-map { display: inline-flex; align-items: center; gap: 6px; }
.btn-map:hover { opacity: 1; color: var(--accent); border-color: var(--accent); }
.btn-map:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Sub 1700px butonul rămâne DOAR icoană. Coloana de acțiuni are 139px iar conținutul cerea
   ~164 ⇒ `⋮` se revărsa în afara tabelului: 49px la 1280, 28 la 1440, 8 la 1600 (măsurat).
   ⚠ Pragul a fost întâi 1500 și a lăsat 1600 tot cu 8px afară — depășirea nu se stinge la
   1500, ci undeva sub 1920. Prima variantă a fost aleasă „la ochi"; a doua, după re-măsurare.
   Pinul e deja acolo, iar `title` și `aria-label` rămân pe buton — nu se pierde nimic la
   citirea cu voce.
   ⚠ COPIL DIRECT al `.row-actions`: meniul `⋮` e ÎNĂUNTRUL aceleiași celule și are propriul
   „hartă", unde textul e singurul indiciu. Scrisă cu descendent, regula l-ar fi golit și pe
   el. Aceeași capcană, în același loc, ca la ascunderea de pe telefon. */
@media (max-width: 1700px) {
  td.row-actions > .btn-map .btn-map-txt { display: none; }
  td.row-actions > .btn-map { padding: 2px 6px; }
}
/* varianta din alerte: pin compact, între nume și mesaj */
.al-map {
  padding: 1px 6px; font-size: 12px; border-color: var(--line); border-radius: 6px;
  flex: 0 0 auto; opacity: .85;
}

/* toast (placeholder buton hartă; se poate refolosi) */
.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translate(-50%, 12px);
  background: var(--panel-2); color: var(--txt); border: 1px solid var(--line);
  border-radius: 10px; padding: 11px 18px; font-size: 13.5px; box-shadow: var(--shadow);
  z-index: 60; max-width: min(92vw, 460px); opacity: 0;
  transition: opacity .2s ease, transform .2s ease;
}
.toast.toast-on { opacity: 1; transform: translate(-50%, 0); }

@media (max-width: 620px) {
  .wrap { padding: 20px 14px 40px; }
  .dev { font-size: 13px; }
  .dev thead th:nth-child(5), .dev tbody td:nth-child(5) { display: none; }
  .site-header { flex-wrap: wrap; }
  /* Tabelul de camere pe telefon: colegii sunt pe teren, iar acțiunea principală (⋮ →
     mentenanță) trebuie să fie la degetul lor, nu după scroll orizontal. Rămân dot / NVR /
     Cameră / Stare / acțiuni; IP, Model, Rec, Motion, ONT se ascund. Scopat pe #cam-table
     ca să nu atingă tabelul de discuri sau zona de corelare. */
  #cam-table { table-layout: auto; }
  #cam-table thead th:nth-child(4), #cam-table tbody td:nth-child(4),
  #cam-table thead th:nth-child(6), #cam-table tbody td:nth-child(6),
  #cam-table thead th:nth-child(7), #cam-table tbody td:nth-child(7),
  #cam-table thead th:nth-child(8), #cam-table tbody td:nth-child(8) { display: none; }
  #cam-table col { width: auto !important; }
  /* ⚠ Regulile de `.row-menu` pentru mobil NU stau aici — `.row-menu > summary` de bază e
     declarat mai jos în fișier și, la specificitate egală, câștiga el. Au fost mutate imediat
     după regula de bază (caută „ținte de atingere"). */
  .btn-hist, .btn-map { min-height: 34px; display: inline-flex; align-items: center; }
  .maint-actions button { flex: 1 1 auto; min-height: 44px; }
}

/* ==== auth (Felia 1 — zona de admin) ==== */
/* login = pagină standalone (fără shell), card centrat */
/* ⚠ Rămâne container flex pentru celelalte două pagini ale fluxului (cod 2FA / cod pe email),
   care n-au încă scenă. Pe pagina de autentificare singurul copil e scena, care e `fixed`,
   deci în afara fluxului — regulile de aici n-o ating. */
.login-body {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 30px; min-height: 100svh; padding: 24px; position: relative; overflow: hidden;
}
/* Scena: exact zona rămasă la vedere. `--vv`/`--vv-top` vin din `login-viewport.js`; până
   se încarcă el, `100svh` e o aproximare bună, deci pagina nu pâlpâie. */
.login-stage {
  position: fixed; left: 0; right: 0;
  top: var(--vv-top, 0px); height: var(--vv, 100svh);
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  gap: 20px; z-index: 1; overflow-y: auto;
  padding: max(20px, env(safe-area-inset-top)) 20px max(20px, env(safe-area-inset-bottom));
}
/* Semnătura DAB, deasupra cardului. Discretă: e identitatea firmei, nu a produsului.
   ⚠ CENTRARE SIGURĂ, nu `justify-content: center`: cu centrare clasică, un conținut mai înalt
   decât containerul se revarsă în AMÂNDOUĂ direcțiile, iar partea de sus ajunge sub bara de
   stare — imposibil de derulat înapoi. Marginile `auto` fac exact centrare cât timp e loc și
   se prăbușesc la zero când nu mai e, deci conținutul nu iese NICIODATĂ pe sus.
   ⚠ Înălțimea e SCRISĂ, nu `auto`: un SVG fără dimensiuni intrinseci lasă motorul s-o
   deducă, iar rezultatul diferă între Chromium și WebKit. (Am pus și atribute pe SVG.) */
.login-sig { flex: none; display: block; width: 208px; height: 68px; max-width: 62%;
  margin: auto auto 0; align-self: center; opacity: .55;
  transition: width .16s ease, height .16s ease, opacity .16s ease; }
.login-card { margin-bottom: auto; }

/* Varianta STRÂNSĂ — cu tastatura deschisă rămân ~470px din 852 pe un iPhone 15 Pro.
   ⚠ Nu se ascunde NIMIC: totul se micșorează, ca să rămână și logoul, și formularul întreg.
   Cifrele sunt alese ca suma să încapă în ~410px (înălțimea vizibilă minus zona sigură de sus). */
.login-strans .login-stage { gap: 8px; padding-bottom: 10px; }
.login-strans .login-sig   { width: 152px; height: 50px; opacity: .5; }
.login-strans .login-card  { padding: 16px 20px 14px; border-radius: 15px; }
.login-strans .login-brand { margin-bottom: 10px; gap: 9px; }
.login-strans .login-mark  { width: 26px; height: 26px; border-radius: 7px; }
.login-strans .login-title { font-size: 18px; margin-bottom: 8px; }
.login-strans .login-form  { gap: 8px; }
.login-strans .login-form label { gap: 5px; }
.login-strans .login-form button { margin-top: 4px; min-height: 44px; padding: 10px 14px; }
.login-strans .login-foot  { margin-top: 8px; font-size: 11.5px; }
.login-strans .login-error { margin-bottom: 10px; padding: 8px 11px; }
/* Atmosferă, în trei straturi foarte discrete: o aură caldă în roșul mărcii, un caroiaj fin
   care trimite la hărțile din produs, și o vinietă care închide marginile. Nimic din ele nu
   se citește ca „decor" — dau doar adâncime unui fundal care era plat. */
.login-bg {
  /* `fixed`, nu `absolute`: scena de deasupra e ea însăși fixată și se poate micșora la
     deschiderea tastaturii — fundalul trebuie să acopere ecranul indiferent de ea. */
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(ellipse 70% 46% at 50% 32%, rgba(174,39,47,.20), transparent 70%),
    repeating-linear-gradient(0deg, rgba(255,255,255,.022) 0 1px, transparent 1px 44px),
    repeating-linear-gradient(90deg, rgba(255,255,255,.022) 0 1px, transparent 1px 44px),
    radial-gradient(ellipse 120% 90% at 50% 50%, transparent 40%, rgba(0,0,0,.55));
}
.login-card {
  position: relative; z-index: 1;
  width: 100%; max-width: 372px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  border: 1px solid var(--line-strong);
  border-radius: 18px;
  box-shadow: 0 1px 0 rgba(255,255,255,.05) inset, 0 24px 60px rgba(0,0,0,.5),
              0 2px 10px rgba(0,0,0,.35);
  padding: 30px 26px 24px;
}
.login-brand { display: flex; align-items: center; gap: 11px; font-weight: 700; margin-bottom: 22px; }
.login-mark { display: block; width: 34px; height: 34px; border-radius: 9px; flex: none;
  box-shadow: 0 2px 8px rgba(0,0,0,.45); }
.login-brand .brand-name { font-size: 17px; letter-spacing: -.2px; }
.login-title { font-size: 26px; font-weight: 700; letter-spacing: -.6px; margin: 0 0 20px;
  line-height: 1.1; }
.login-form { display: flex; flex-direction: column; gap: 14px; }
.login-form label, .form-card label {
  display: flex; flex-direction: column; gap: 6px;
  font-size: 13.5px; color: var(--txt-dim); font-weight: 600;
}
/* ⚠ 16px, NU 15: sub 16px Safari pe iPhone MĂREȘTE pagina la atingerea câmpului, iar omul
   trebuie să dea zoom înapoi de fiecare dată. Se aplică la toate controalele portalului, mai
   jos în fișier — aici e doar cel de autentificare. */
.login-form input, .form-card input[type="text"], .form-card input[type="password"] {
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 10px;
  padding: 12px 13px; color: var(--txt); font: inherit; font-size: 16px;
  transition: border-color .12s ease, box-shadow .12s ease, background .12s ease;
}
.login-form input::placeholder { color: var(--txt-faint); }
/* ⚠ Focusul e un INEL, nu doar o bordură roșie: culoarea mărcii e roșie, iar culoarea de
   eroare tot roșie — un câmp focusat arăta identic cu unul greșit. Haloul spune „aici scrii
   acum"; eroarea rămâne banda de sus, care e altceva ca formă, nu doar ca nuanță. */
.login-form input:focus, .form-card input:focus {
  outline: none; border-color: var(--accent); background: var(--panel);
  box-shadow: 0 0 0 3px var(--accent-bg);
}
/* ⚠ `:not(.btn-salveaza)` — regula asta e (0,2,1) și bate `.btn-salveaza` (0,1,0), deci
   butonul de Salvează din Setări cont rămăsese TRANSPARENT și de 48px, singurul din portal
   care nu se colorase. A noua coliziune de cascadă din felia de design, și a doua oară cînd
   fix regula asta înghite o clasă explicită (prima dată `.btn-quiet`).
   ⚠ Regula generală, de aplicat: un selector generic pe `button[type=submit]` dintr-un
   container NU are voie să reclame butoanele care au deja o clasă proprie. */
.login-form button, .form-card button[type="submit"]:not(.btn-salveaza) {
  margin-top: 8px; color: #fff; border: none; border-radius: 10px;
  background: linear-gradient(180deg, #d8404b, var(--accent));
  box-shadow: 0 1px 0 rgba(255,255,255,.16) inset, 0 4px 14px rgba(174,39,47,.26);
  padding: 13px 14px; font: inherit; font-weight: 700; font-size: 16px; letter-spacing: .2px;
  cursor: pointer; min-height: 48px;
  transition: transform .1s ease, box-shadow .12s ease, filter .12s ease;
}
.login-form button:hover, .form-card button[type="submit"]:hover { filter: brightness(1.06); }
.login-form button:active { transform: translateY(1px);
  box-shadow: 0 1px 0 rgba(255,255,255,.12) inset, 0 2px 7px rgba(174,39,47,.22); }
.login-foot { margin: 20px 0 0; text-align: center; color: var(--txt-faint); font-size: 12px;
  letter-spacing: .2px; }
.login-error {
  display: flex; align-items: flex-start; gap: 9px;
  background: var(--down-bg); color: var(--down); border: 1px solid var(--down-line);
  border-radius: 10px; padding: 11px 13px; font-size: 13.5px; margin-bottom: 16px;
  line-height: 1.35;
}
/* Semnul de exclamare dă erorii o FORMĂ, nu doar o culoare — se deosebește de inelul de focus
   chiar și pentru cineva care nu distinge bine roșul. */
.login-error::before { content: "!"; flex: none; width: 18px; height: 18px; border-radius: 50%;
  background: var(--down); color: var(--panel); font-weight: 800; font-size: 12px;
  line-height: 18px; text-align: center; }

/* Pe telefon cardul urcă: la mijloc, cu tastatura deschisă, ajunge împins sub marginea de jos. */
@media (max-width: 620px) {
  .login-stage { gap: 18px; padding-left: 18px; padding-right: 18px; }
  .login-title { font-size: 23px; }
}

/* form card generic (schimbă parola, adaugă user) */
.form-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 20px 22px; max-width: 420px;
  display: flex; flex-direction: column; gap: 14px;
}
.hint-inline { font-weight: 500; color: var(--txt-faint); font-size: 12px; }
.check { flex-direction: row !important; align-items: center; gap: 8px; }
.check input { width: auto; }

/* 2FA / TOTP: subtitlu login, link-buton, înrolare (QR + cheie + pași) */
.login-sub { color: var(--txt-faint); font-size: 13px; margin: -6px 0 14px; }
.login-alt { margin-top: 14px; text-align: center; }
/* Bifă pe UN RÂND cu textul ei, cu ținta de atins ≥44px (colegii sunt pe telefon).
   ⚠ SELECTORUL TREBUIE SĂ NUMEASCĂ FIECARE CONTEXT ÎN CARE E FOLOSITĂ. Atât `.login-form label`
   cât și `.form-card label` au specificitate (0,1,1) și le pun în COLOANĂ; o clasă simplă
   (0,1,0) e bătută TĂCUT, iar bifa ajunge DEASUPRA textului ei.
   ⚠ S-a întâmplat de DOUĂ ori în aceeași zi (2026-08-10): întâi la login, apoi în Setări cont,
   unde regula scopată doar pe `.login-form` pur și simplu nu se aplica. Aceeași familie cu
   regula de 44px, care a stat moartă săptămâni. Când muți o bifă într-un formular nou, verifică
   dacă selectorul îl acoperă — nu presupune că „e aceeași clasă, deci arată la fel". */
.login-form label.login-check, .form-card label.login-check {
  display: flex; flex-direction: row; align-items: center; gap: 10px;
  min-height: 44px; font-size: 13px; color: var(--txt-dim);
  font-weight: 500; cursor: pointer;
}
.login-form .login-check input[type="checkbox"],
.form-card .login-check input[type="checkbox"] {
  width: 18px; height: 18px; margin: 0; flex: none; padding: 0;
}
.linklike { background: none; border: none; color: var(--txt-faint); font-size: 12px;
  text-decoration: underline; cursor: pointer; padding: 0; }
.totp-enroll { display: flex; flex-direction: column; gap: 18px; max-width: 460px; }
.totp-steps { margin: 0; padding-left: 20px; color: var(--txt); line-height: 1.7; font-size: 14px; }
.totp-key { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 6px; padding: 2px 6px;
  word-break: break-all; }
.totp-qr { background: #fff; padding: 12px; border-radius: 10px; width: max-content;
  box-shadow: var(--shadow); }
.totp-qr svg { width: 200px; height: 200px; display: block; }

/* Setări cont: coloană centrată (nu mai pare goală, aliniată la stânga) */
/* ⚠ 460px → 940px, pe DOUĂ coloane (Bogdan, 2026-08-10): parola și 2FA una lângă alta,
   dispozitivele late dedesubt, ca pagina să încapă pe un ecran fără scroll. Titlurile nu mai
   sunt centrate — într-o grilă, un titlu centrat peste un card aliniat la stânga arată strâmb. */
.acct-page { max-width: 940px; margin: 0 auto; }
.acct-page .form-card { max-width: none; }
/* `auto-fit` + `minmax` fac trecerea la o coloană pe telefon fără media query separat.
   `align-items: start` — altfel cardul scurt (2FA) s-ar întinde degeaba cât cel de parolă. */
.acct-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: 0 22px; align-items: start;
}
.acct-wide { max-width: none; }
/* Setări cont: secțiuni (parolă, 2FA) + buton-link + confirmare 2FA activ */
.acct-sec { margin: 0 0 26px; }
.acct-sec h2 { font-size: 15px; font-weight: 700; letter-spacing: -.2px; margin: 0 0 12px; }
.acct-ok { color: var(--ok); font-weight: 600; margin: 0 0 8px; }
/* ⚠ Folosit ȘI pe <a>, ȘI pe <button> (CTA-ul formularului de mentenanță, butonul din popup-ul
   hărții N3). Fără `border`/`cursor`/`font`, un <button> primea bordura nativă gri a browserului
   peste fundalul roșu și fontul de sistem în loc de Outfit. */
.btn-primary { display: inline-block; align-self: flex-start; background: var(--accent);
  color: #fff; border: none; border-radius: 8px; padding: 11px 16px; font: inherit;
  font-weight: 700; text-decoration: none; cursor: pointer; }
.btn-primary:hover { filter: brightness(1.08); }
.btn-primary:disabled { opacity: .55; cursor: not-allowed; filter: none; }

/* Dispozitive de încredere — listă, nu tabel: cardul de cont are 460px, cât un telefon.
   ⚠ SELECTORII ÎNCEP CU `.form-card`. `.form-card button[type="submit"]` (0,2,1) bate orice
   clasă simplă (0,1,0), deci un `.btn-quiet` singur ar fi ieșit buton roșu de CTA — aceeași
   familie de coliziuni din care regula de 44px a stat moartă săptămâni. */
.dev-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.dev-item {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  background: var(--bg-2); border: 1px solid var(--line-soft); border-radius: 9px;
  padding: 10px 12px;
}
/* `min-width: 0` = fără el, un nume lung împinge butonul afară din card (flex nu lasă
   elementele să scadă sub conținutul lor implicit). */
.dev-item-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.dev-item-name { font-weight: 600; color: var(--txt); overflow-wrap: anywhere; }
.dev-item-meta { font-size: 11.5px; color: var(--txt-faint); line-height: 1.5; overflow-wrap: anywhere; }
/* Dispozitivul de pe care te uiți ACUM. E singurul semn sigur când același browser, pe același
   calculator, apare de două ori (una din fereastră privată) — de-aia are ramă, nu doar text.
   ⚠ Prefixat cu `.form-card`, ca tot ce e în cardul ăsta: fără prefix, `.dev-item` (0,1,0) ar fi
   avut aceeași specificitate și ar fi decis ordinea din fișier, nu intenția. */
.form-card .dev-item-curent { border-color: var(--ok); background: color-mix(in srgb, var(--ok) 7%, var(--bg-2)); }
.form-card .dev-badge {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--ok) 18%, transparent); color: var(--ok);
  font-size: 10.5px; font-weight: 700; letter-spacing: .02em; vertical-align: 1px;
}
.form-card .btn-quiet {
  flex: none; margin: 0; background: none; border: 1px solid var(--line);
  border-radius: 8px; padding: 7px 12px; min-height: 36px;
  color: var(--txt-dim); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.form-card .btn-quiet:hover { border-color: var(--down); color: var(--down); filter: none; }
.form-card .linklike {
  align-self: flex-start; margin: 0; background: none; border: none; padding: 0;
  color: var(--txt-faint); font: inherit; font-size: 12px; font-weight: 500;
  text-decoration: underline; cursor: pointer;
}
.form-card .linklike:hover { color: var(--down); filter: none; }

/* grupuri RBAC = hub de acces (master-detail): listă grupuri + panou editare */
.grp-shell { display: flex; gap: 18px; align-items: flex-start; }
.grp-list { flex: 0 0 260px; display: flex; flex-direction: column; gap: 3px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 12px; box-shadow: var(--shadow); }
.grp-list-h { font-size: 11.5px; text-transform: uppercase; letter-spacing: .8px; color: var(--txt-faint); font-weight: 700; padding: 2px 6px 8px; }
.grp-item { display: flex; flex-direction: column; gap: 2px; padding: 9px 11px; border-radius: 9px; border: 1px solid transparent; transition: background .12s, border-color .12s; }
.grp-item:hover { background: var(--panel-2); }
.grp-item.active { background: var(--panel-2); border-color: var(--accent); }
.grp-item-name { font-weight: 600; color: var(--txt); }
.grp-item-sum { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 3px; font-size: 11px; }
.gis { background: var(--bg-2); border: 1px solid var(--line-soft); border-radius: 5px; padding: 1px 6px; color: var(--txt-dim); font-variant-numeric: tabular-nums; }
.grp-item.active .gis { border-color: rgba(208,48,60,.3); }
.grp-empty { padding: 10px 6px; }
/* Bifa de invitație: rând propriu sub grila de câmpuri — trimite o PAROLĂ pe email,
   deci nu se bifează scanând o listă. ⚠ Selector cu `.add-user-form` în față: `.check`
   simplu ar fi bătut de regulile de formular din jur. */
.add-user-form label.auf-invita {
  display: flex; flex-direction: row; align-items: flex-start; gap: 10px;
  margin: 6px 0 4px; padding: 11px 13px; border-radius: 9px;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  font-size: 13.5px; line-height: 1.5; cursor: pointer;
}
.add-user-form .auf-invita input { margin-top: 3px; flex: none; }
/* Audiențele de email ale grupului: separate vizual de comune/meniuri, ca bifa de
   administrare să nu se aprindă din reflex — trimite postura de securitate a serverului. */
.grp-notify { border-color: var(--warn); }
.grp-checks-col { display: flex; flex-direction: column; gap: 12px; }
.grp-checks-col .check { align-items: flex-start; gap: 10px; line-height: 1.5; }
.grp-checks-col .check input { margin-top: 3px; flex: none; }
.grp-warn { color: var(--warn); font-size: 12.5px; }
/* Comunele grupului: tabel cu DOUĂ coloane de bife — „vede" și „primește alerte". Sunt două
   întrebări diferite (2026-08-11), iar o grilă de pastile n-are unde să le țină pe amândouă.
   ⚠ Selectorii sunt prefixați cu `.grp-sites`: în cardul ăsta, `.check` și `td` au deja reguli,
   iar o clasă simplă pierde în fața lor (0,1,0 < 0,2,1) — coliziunea prinsă de trei ori pe 10 aug. */
.grp-sites { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 13.5px; }
.grp-sites th, .grp-sites td { padding: 6px 8px; border-bottom: 1px solid var(--line-soft); }
.grp-sites th { text-align: left; font-size: 11.5px; font-weight: 600; color: var(--txt-dim);
  text-transform: uppercase; letter-spacing: .03em; white-space: nowrap; }
/* Coloanele de bife: destul cât să încapă butonul „Deselectează" scurtat, cu numele comunei
   împins la stânga (cerut de Bogdan: „mută coloana mai în stânga ca să încapă butonul"). */
.grp-sites th:not(:first-child), .grp-sites td:not(:first-child) {
  text-align: center; width: 116px; vertical-align: bottom;
}
.grp-sites th { line-height: 1.35; padding-bottom: 4px; }
/* ⚠ Prefixat cu `.grp-sites`: `.grp-selall` singur (0,1,0) pierde în fața regulilor de buton
   din card. Aceeași coliziune de cascadă de trei ori pe 10 august. */
.grp-sites .grp-selall {
  margin: 2px 0 0; display: inline-block; font-size: 11px; font-weight: 600;
  text-transform: none; letter-spacing: 0; min-width: 58px;
}
.grp-sites tbody tr:hover { background: var(--bg-2); }
/* ⚠ Zona de atins de 44px NU se poate întinde de aici — vezi nota de la bifele desenate.
   Regula care încerca (`::after` cu `inset: -13px`) a fost ștearsă pe 08 sep: nu desena
   nimic. Rămâne doar cursorul; extinderea țintei e o felie separată. */
.grp-sites td input[type="checkbox"] { cursor: pointer; }
/* ---- pagina de GRUPURI, refăcută 09 sep 2026 ---------------------------------------------
   ⚠ A doua refacere în două zile. Prima (08 sep) a reparat aspectul, dar a fost respinsă pe
   STRUCTURĂ: „gruparea și afișarea și poziționarea și funcționalitatea în general, e gândită
   prost rău". Structura de acum e dictată de Bogdan: două zone de echipe la stânga (predefinit
   / personalizate), patru taburi la dreapta, bifare peste lista completă (fără butoane de
   adăugare-ștergere), adăugarea grupului într-o fereastră care cere doar numele.
   ⚠ „X meniuri" a fost scos din bara laterală, la cererea lui: „e irelevantă". */
/* ⚠ FĂRĂ plafon de lățime. Îl pusesem la 1180px ca să nu rămână bifele la ~1150px de numele
   comunei — dar am rezolvat cauza greșită: golul venea dintr-un rând lung, nu din pagina lată.
   Semnalat de Bogdan: „de ce nu e pe tot ecranul? la comune e destul loc cât să faci împărțirea
   în mai multe coloane, ca să nu mai fie ditamai scroll-ul". Corect: comunele stau acum pe
   carduri de județ care curg pe toată lățimea, deci lățimea e folosită ca să DISPARĂ derularea,
   nu ca să crească golul. */
.grp-shell { display: grid; grid-template-columns: 268px minmax(0, 1fr); gap: var(--sp-3);
  align-items: start; }

/* ---- stânga: echipele ---- */
.grp-list { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--sp-3); position: sticky; top: var(--sp-3); }
.grp-list-cap { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-bottom: var(--sp-3); }
.grp-list-t { font-size: 12px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--txt-dim); }
.grp-zona { margin: 0 0 var(--sp-2); font-size: 10.5px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--txt-faint); }
.grp-sep { height: 1px; background: var(--line-sect); margin: var(--sp-3) 0; }
.grp-card { display: block; padding: 10px 12px; border-radius: 9px; text-decoration: none;
  border: 1px solid transparent; margin-bottom: 6px; }
.grp-card:hover { background: var(--panel-2); }
.grp-card.activ { background: var(--accent-bg); border-color: var(--accent); }
.grp-card-n { display: block; font-size: 13.5px; font-weight: 600; color: var(--txt);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.grp-card.activ .grp-card-n { color: var(--accent); }
.grp-card-s { display: block; margin-top: 2px; font-size: 11.5px; color: var(--txt-faint); }
.grp-empty { font-size: 12.5px; margin: 0; }

/* ---- dreapta: panoul echipei ---- */
.grp-panou { background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--sp-4) var(--sp-4) var(--sp-3); min-width: 0; }
/* ⚠ Clasa se numește `.grp-editor`, NU `.grp-form`: numele ăla era deja luat de o regulă
   veche (`display: flex; align-items: flex-start`), iar formularul meu a moștenit-o — toate
   blocurile dinăuntru s-au strâns la conținut, lista de membri a ieșit 453px într-un panou de
   1328, iar bara de jos 137px. A douăsprezecea coliziune din felia de design, și a doua oară
   în două zile prin refolosirea unui nume existent (prima: `.grp-opt`). */
.grp-cap { display: flex; align-items: center; gap: 10px; margin-bottom: var(--sp-3); }
.grp-nume-txt { margin: 0; font-size: 19px; font-weight: 600; color: var(--txt); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ⚠ Numele NU mai e însoțit de „N membri · M comune" (Bogdan, 09 sep): cifrele stau sub
   fiecare echipă în stânga, iar repetarea lor aici doar ocupa rândul titlului. */
.grp-nume-cimp { flex: 1; min-width: 200px; height: 40px; font-size: 19px; font-weight: 600;
  color: var(--txt); background: var(--bg-2); border: 1px solid var(--accent); border-radius: 9px;
  padding: 0 11px; font-family: inherit; }
.grp-nume-cimp:focus { outline: none; }
.grp-edit-nume { width: 30px; height: 30px; flex: none; display: grid; place-items: center;
  border: 1px solid transparent; border-radius: 8px; background: transparent;
  color: var(--txt-faint); cursor: pointer; padding: 0; }
.grp-cap:hover .grp-edit-nume { color: var(--txt-dim); border-color: var(--line); }
.grp-edit-nume:hover { color: var(--accent); border-color: var(--accent); }
.grp-edit-nume:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.grp-insigna { flex: none; font-size: 10.5px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--txt-faint); border: 1px solid var(--line);
  border-radius: 20px; padding: 2px 9px; }

/* ---- taburi ---- */
/* ⚠ Taburile sunt VEDERI peste același formular, nu formulare separate. Ascunderea se face cu
   `display: none`, care NU scoate câmpurile din trimitere — ruta face validări încrucișate
   între taburi și are nevoie de tot conținutul. */
.grp-taburi { display: flex; gap: 2px; border-bottom: 1px solid var(--line-sect);
  margin-bottom: var(--sp-4); overflow-x: auto; }
.grp-tab { flex: none; padding: 10px 16px; border: none; background: none; font: inherit;
  font-size: 13.5px; font-weight: 600; color: var(--txt-dim); cursor: pointer;
  border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; }
.grp-tab:hover { color: var(--txt); }
.grp-tab.is-activ { color: var(--accent); border-bottom-color: var(--accent); }
.grp-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.grp-foaie { display: none; }
.grp-foaie.is-activ { display: block; }
.grp-foaie-h { margin: 0 0 var(--sp-3); font-size: 12.5px; color: var(--txt-dim);
  line-height: 1.55; }

/* ---- unelte de listă (căutare, filtru, contor) ---- */
/* ⚠ Discrete la 15 rânduri, necesare la 40. Parcul a crescut cu patru comune în șase
   săptămâni; o listă care merge azi și se rupe la dublu nu e gata. */
.grp-unelte { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-2);
  flex-wrap: wrap; }
.grp-cauta { position: relative; display: inline-flex; align-items: center; flex: 1;
  min-width: 180px; max-width: 320px; }
.grp-cauta .ico { position: absolute; left: 10px; width: 15px; height: 15px;
  color: var(--txt-faint); pointer-events: none; }
.grp-filtru { width: 100%; height: 34px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg-2); color: var(--txt); font: inherit; font-size: 13px;
  padding: 0 10px 0 32px; }
.grp-filtru:focus { outline: none; border-color: var(--accent); }
.grp-contor { margin-left: auto; font-size: 12px; color: var(--txt-faint);
  font-variant-numeric: tabular-nums; }
.grp-contor b { color: var(--txt-dim); font-weight: 600; }

/* ---- liste (membri / comune) ---- */
/* ⚠ Înălțime plafonată + derulare INTERNĂ: la 40 de comune lista ar împinge butonul de salvare
   dincolo de ecran. Cu plafon, panoul rămâne de aceeași înălțime indiferent cât crește parcul. */
.grp-lista { border: 1px solid var(--line); border-radius: 10px; background: var(--bg-2);
  max-height: 420px; overflow-y: auto; }
/* ⚠ Plafonul de înălțime a rămas DOAR pe lista de membri. La comune l-am scos: acolo derularea
   internă era chiar problema semnalată, iar cardurile pe județ o fac inutilă. */
.grp-rind { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: 9px 12px; cursor: pointer; }
.grp-rind + .grp-rind { border-top: 1px solid var(--line-soft); }
.grp-rind:hover { background: var(--panel-2); }
.grp-rind-nume { min-width: 0; font-size: 13.5px; color: var(--txt); display: flex;
  align-items: baseline; gap: 8px; flex-wrap: wrap; }
.grp-rind-sub { font-size: 12px; color: var(--txt-faint);
  font-family: ui-monospace, "SF Mono", Menlo, monospace; }
.grp-rind[hidden] { display: none; }

/* ---- comunele: carduri pe județ, care curg ---- */
/* ⚠ `auto-fill` cu minim 300px: la 1920 intră toate patru județele pe un rând (deci ZERO
   derulare), la 1280 intră trei, pe telefon unul. Nu e o alegere de aspect — e felul în care
   lățimea ecranului se transformă în mai puțină derulare, în loc de mai mult gol. */
.grp-acces { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: var(--sp-3); align-items: start; }
.grp-jcard { border: 1px solid var(--line); border-radius: 10px; background: var(--bg-2);
  overflow: hidden; min-width: 0; }
.grp-jcard[hidden] { display: none; }
/* Capul se repetă pe fiecare card — cu coloane care curg, un singur cap sus n-ar mai fi
   deasupra nimănui în afară de primul card. */
.grp-jcap { display: grid; grid-template-columns: minmax(0, 1fr) 54px 54px; gap: 8px;
  padding: 8px 12px; background: var(--panel-2); border-bottom: 1px solid var(--line-sect); }
.grp-jcap > span { font-size: 10.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--txt-faint); text-align: center; }
.grp-jcap > span:first-child { text-align: left; color: var(--txt-dim); }
.grp-tot-grup { font-size: 12px; color: var(--txt-faint); display: inline-flex;
  align-items: center; gap: 8px; }
.grp-tot { border: 1px solid var(--line); background: var(--bg-2); border-radius: 7px;
  color: var(--accent); font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
  padding: 4px 10px; }
.grp-tot:hover { border-color: var(--accent); }
.grp-rind-acces { display: grid; grid-template-columns: minmax(0, 1fr) 54px 54px; gap: 8px;
  padding: 8px 12px; }
.grp-rind-acces > .grp-rind-nume { overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; display: block; }
.grp-rind-acces > input { justify-self: center; }
/* Bifa de alerte, dezactivată când comuna n-are acces: regula serverului, arătată ÎNAINTE de
   salvare, nu aplicată tăcut după. */
.grp-rind-acces > input:disabled { opacity: .3; }

/* ---- Permisiuni: pastilele de meniu ---- */
.grp-pastile { display: flex; flex-wrap: wrap; gap: 8px; }
.grp-p { display: inline-flex; align-items: center; gap: 9px; padding: 10px 14px;
  border-radius: 9px; border: 1px solid var(--line); background: var(--bg-2);
  font-size: 13.5px; color: var(--txt-dim); cursor: pointer; white-space: nowrap; }
.grp-p:hover { border-color: var(--line-strong); color: var(--txt); }
.grp-p:has(input:checked) { border-color: var(--accent); color: var(--accent); font-weight: 600; }

/* ---- Notificări: trei rânduri, fiecare o CARTELĂ ---- */
/* ⚠⚠ Regulile astea LIPSEAU: le pierdusem când am rescris blocul paginii, iar `<label>` fără
   `display` e INLINE — cele trei opțiuni s-au scurs una în alta, într-un singur paragraf
   continuu. Semnalat de Bogdan: „în tabul notificări e haos". Nimic n-a dat eroare; textul era
   tot acolo, doar că nu mai era împărțit în nimic.
   ⚠ Lecția, dincolo de bug: la rescrierea unui bloc de CSS, ce se pierde nu se vede ca o
   eroare — se vede ca un aspect „ciudat", pe un tab pe care poate nu-l deschizi imediat. */
.grp-notif { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--sp-3); align-items: start; }
.grp-notif-r { display: flex; gap: 12px; align-items: flex-start; padding: 13px 14px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--bg-2);
  cursor: pointer; min-width: 0; }
.grp-notif-r:hover { border-color: var(--line-strong); }
.grp-notif-r:has(input:checked) { border-color: var(--accent); background: var(--accent-bg); }
.grp-notif-r > input { margin-top: 2px; flex: none; }
.grp-notif-r > span { min-width: 0; font-size: 12.5px; color: var(--txt-dim); line-height: 1.5; }
.grp-notif-r b { display: block; font-size: 13.5px; color: var(--txt); font-weight: 600;
  margin-bottom: 3px; }
/* ⚠ Avertismentul are cutie proprie, nu doar culoare: e singurul lucru dintre un grup de
   client și postura de securitate a serverului ajunsă în inboxul lui, iar într-un paragraf
   curgător se citea ca o continuare a descrierii. */
.grp-warn { display: flex; gap: 7px; margin-top: 8px; padding: 8px 10px;
  background: var(--warn-bg); border: 1px solid var(--warn-line); border-radius: 8px;
  color: var(--warn); font-size: 12px; line-height: 1.5; }

.grp-jos { margin-top: var(--sp-4); padding-top: var(--sp-4);
  border-top: 1px solid var(--line-sect); display: flex; justify-content: space-between;
  align-items: center; gap: var(--sp-3); }
.btn-sterge-grup { height: 34px; padding: 0 13px; border-radius: 8px; font: inherit;
  font-size: 13px; font-weight: 600; cursor: pointer; }
.modal-dialog.modal-mic { max-width: 460px; }

@media (max-width: 900px) {
  /* Pe ecran mic echipele trec SUS: o coloană de 268px ar lăsa panoului mai puțin decât îi
     trebuie tabului Acces. */
  .grp-shell { grid-template-columns: 1fr; }
  .grp-list { position: static; }
  .grp-panou { padding: var(--sp-3); }
  .grp-jcap, .grp-rind-acces { grid-template-columns: minmax(0, 1fr) 50px 50px; }
}

.grp-confirm { display: flex; align-items: flex-start; gap: 10px; margin: 4px 0 14px;
  padding: 11px 13px; border-radius: 9px; background: var(--warn-bg);
  border: 1px solid var(--warn); font-size: 13.5px; }
.grp-add { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; padding-top: 12px; border-top: 1px solid var(--line-sect); }
.grp-add input { background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; color: var(--txt); font: inherit; font-size: 13px; }
.grp-add input:focus { outline: none; border-color: var(--accent); }
.grp-detail { flex: 1 1 auto; min-width: 0; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 18px 20px; box-shadow: var(--shadow); }
.grp-detail-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.grp-detail-head h2 { margin: 0; font-size: 18px; }
.grp-none { padding: 24px 6px; }
/* `.grp-fieldset` — scos pe 08 sep: secțiunile paginii de grupuri nu mai sînt
   `<fieldset>`, ci `.grp-sect`, ca să poată ține comunele pe două coloane. */
.grp-fieldset legend { padding: 0; font-size: 13px; font-weight: 700; color: var(--txt); display: flex; align-items: baseline; gap: 8px; width: 100%; }
.grp-fieldset legend .dim { font-weight: 500; }
.grp-selall { margin-left: auto; font-size: 12px; font-weight: 600; color: var(--accent); background: none; border: none; cursor: pointer; font-family: inherit; padding: 0; }
.grp-selall:hover { text-decoration: underline; }
.grp-checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(188px, 100%), 1fr)); gap: 7px; margin-top: 10px; }
/* checkbox-uri în temă: pastilă care se aprinde (border+fundal accent) la bifare, bifă roșie */
.grp-checks .check { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 7px 10px;
  border: 1px solid var(--line-soft); border-radius: 8px; background: var(--bg-2); cursor: pointer;
  transition: border-color .12s, background .12s; }
.grp-checks .check:hover { border-color: var(--line); }
.grp-checks .check:has(input:checked) { border-color: var(--accent); background: var(--accent-bg); }
.grp-checks input[type="checkbox"] { accent-color: var(--accent); flex: none; }
.grp-edit button[type="submit"] { margin-top: 18px; background: var(--accent); color: #fff; border: none; border-radius: 8px; padding: 10px 18px; font: inherit; font-weight: 600; font-size: 14px; cursor: pointer; }
.grp-edit button[type="submit"]:hover { filter: brightness(1.08); }
/* apartenență compactă, pe pagina Utilizatori (păstrat) */
.grp-form { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.check-sm { font-size: 12px; gap: 6px; }
.check-sm input { transform: scale(0.9); }
.grp-form .btn-sm { margin-top: 4px; }
@media (max-width: 820px) { .grp-shell { flex-direction: column; } .grp-list { flex-basis: auto; width: 100%; } }

/* tag-uri de stare user */
.tag { display: inline-block; font-size: 11.5px; font-weight: 700; padding: 2px 8px; border-radius: 999px; }
.tag-admin { background: var(--accent-bg); color: var(--accent); }
/* ⚠ Proprietarul are insignă PROPRIE, nu „admin": e alt lucru, nu un admin mai important.
   Nu poate fi retrogradat, dezactivat, șters sau resetat de nimeni, iar dacă rândul lui ar
   arăta ca al oricărui admin, primul mod de a afla diferența ar fi că butoanele nu merg. */
.tag-owner { background: var(--warn-bg); color: var(--warn); }
.tag-ok { background: var(--ok-bg); color: var(--ok); }
.tag-warn { background: var(--warn-bg); color: var(--warn); }
.tag-off { background: var(--down-bg); color: var(--down); }
.tag-user { background: var(--ok-bg); color: var(--ok); }   /* rol „user" = pastilă (nu text gol); admin rămâne roșu */
/* nume utilizator: alb, ceva mai apăsat (nu mai pare „sec") */
.u-name { font-weight: 600; color: var(--txt); }

/* butoane mici de acțiune în tabel */
.inline { display: inline; margin: 0; }
.btn-sm {
  font-size: 12px; color: var(--txt-dim); background: transparent;
  border: 1px solid var(--line); border-radius: 6px; padding: 4px 10px;
  cursor: pointer; font-family: inherit;
}
.btn-sm:hover { color: var(--txt); border-color: var(--txt-dim); }

/* ---- BUTONUL DE SALVARE: o singura definitie, pentru tot portalul (08 sep 2026) ----------
   Cerere Bogdan: „la editare butoanele de tip salveaza, toate, vreau sa le faci cu rosu ca sa
   fie mai evidente. Si toate butoanele de salveaza din platforma sa fie asa".
   Inainte erau UNSPREZECE butoane de salvare cu PATRU stiluri: `<button>` gol (Cont, Grupuri),
   `.btn-sm` (Email, Setari, Site-uri, Notificari, ferestrele de utilizator) si `.btn-sm primary`
   — iar `.primary` era definit DOAR ca `.al-mrow .primary`, deci pe cele doua butoane de la
   Notificari nu facea absolut nimic. Inca un stil care exista in markup si nu se aplica.
   ⚠ Clasa asta duce TOATA infatisarea (marime, forma, umplere), nu doar culoarea: altfel
   butoanele ar fi ramas de trei marimi, rosii. Actiunea principala arata la fel peste tot.
   ⚠ Distinctul fata de `.btn-primary`: acela e butonul mare, de deschidere a unei actiuni
   („+ Adauga utilizator", „Creeaza utilizator"). Asta e cel care CONFIRMA o modificare
   dintr-o zona, deci sta la scara continutului din jur. */
.btn-salveaza {
  height: 34px; padding: 0 15px; border: none; border-radius: 8px;
  background: var(--accent); color: #fff; font: inherit; font-size: 13px; font-weight: 600;
  cursor: pointer; display: inline-flex; align-items: center; white-space: nowrap;
}
.btn-salveaza:hover { filter: brightness(1.08); }
.btn-salveaza:disabled { opacity: .55; cursor: not-allowed; filter: none; }
.btn-salveaza:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* ---- butoanele distructive: O SINGURA definitie, pentru tot portalul --------------------
   Cerere Bogdan (08 sep): „butoanele de dezactivare sa fie mai stridente putin, valabil
   general". Erau PATRU stiluri diferite pentru aceeasi idee — `.btn-danger` (colorat DOAR la
   hover, deci in repaus arata exact ca „Renunta"), `.um-actiuni button.danger` (text rosu,
   bordura neutra), `.srv-btn.danger` (bordura rosie) si intrarile din meniul ⋮. Un buton care
   dezactiveaza un cont sau sterge o comuna arata la fel ca unul care nu face nimic.
   Acum, in REPAUS: fundal colorat slab + bordura rosie + text rosu. La hover: plin.
   ⚠ Ramane secundar fata de butonul principal (`--accent`, plin): distructivul trebuie sa se
   VADA, nu sa fie cel mai atragator lucru de pe ecran.
   ⚠ Intrarile din meniul ⋮ NU intra aici, deliberat: sunt randuri de lista pe toata latimea,
   iar un fundal colorat in repaus ar face fiecare meniu de camera sa tipe. Acolo ramane text
   rosu + fundal la hover. */
.btn-danger, button.danger, .btn.danger, .srv-btn.danger {
  color: var(--down); border-color: var(--down-line); background: var(--down-bg);
}
.btn-danger:hover, button.danger:hover, .btn.danger:hover, .srv-btn.danger:hover {
  background: var(--down); border-color: var(--down); color: #fff;
}
.row-actions form.inline + form.inline { margin-left: 6px; }

/* flash cu parola temporară (o singură dată) */
.flash-ok {
  background: var(--ok-bg); border: 1px solid var(--ok); color: var(--txt);
  border-radius: 8px; padding: 11px 14px; font-size: 14px; margin-bottom: 16px;
}
.temp-pass {
  font-family: ui-monospace, monospace; font-size: 15px; font-weight: 700;
  background: var(--bg-2); padding: 2px 8px; border-radius: 6px; user-select: all;
}
.panel-h { font-size: 15px; margin: 0 0 12px; }

/* topbar: meniul contului — numele complet → dropdown (Setări cont + Deconectare) */
.acct-menu { position: relative; }
.acct-menu > summary { list-style: none; cursor: pointer; }
.acct-menu > summary::-webkit-details-marker { display: none; }
.acct { display: inline-flex; align-items: center; gap: 7px; color: var(--txt-dim); font-weight: 600; font-size: 13px; padding: 5px 9px; border-radius: 8px; border: 1px solid transparent; }
.acct:hover, .acct-menu[open] > .acct { color: var(--txt); background: var(--panel); border-color: var(--line); }
.acct-ico { font-size: 13px; opacity: .9; }
.acct-caret { font-size: 10px; color: var(--txt-faint); transition: transform .15s; }
.acct-menu[open] .acct-caret { transform: rotate(180deg); }
.acct-dropdown { position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; min-width: 190px; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow); padding: 6px; display: flex; flex-direction: column; gap: 2px; }
.acct-item { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; padding: 8px 10px; border-radius: 7px; color: var(--txt-dim); font: inherit; font-size: 13px; background: none; border: none; cursor: pointer; }
.acct-item:hover { background: var(--panel-2); color: var(--txt); }
.acct-logout:hover { color: var(--accent); }
@media (max-width: 620px) { .acct-name { display: none; } }

/* dropdown grupuri în tabelul Utilizatori (nativ <details>, integrat în temă) */
.grp-dd { position: relative; display: inline-block; }
.grp-dd > summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border: 1px solid var(--line); border-radius: 7px; background: var(--bg-2); color: var(--txt-dim); font-size: 12.5px; white-space: nowrap; }
.grp-dd > summary::-webkit-details-marker { display: none; }
.grp-dd > summary::after { content: ""; width: 15px; height: 15px; flex: none; background: currentColor; color: var(--txt-faint); -webkit-mask: var(--ico-jos) center/contain no-repeat; mask: var(--ico-jos) center/contain no-repeat; }
.grp-dd[open] > summary { border-color: var(--accent); color: var(--txt); }
.grp-dd-panel { position: absolute; top: calc(100% + 5px); left: 0; z-index: 20; min-width: 200px; max-width: 280px; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow); padding: 10px; display: flex; flex-direction: column; gap: 8px; }
.grp-dd-panel .btn-sm { margin-top: 2px; align-self: flex-start; }
/* grupuri = PASTILE selectabile (nu checkbox-uri native) — dot gri/roșu + fill brand când e activ */
.grp-pills { display: flex; flex-wrap: wrap; gap: 7px; }
.grp-pill { display: inline-flex; align-items: center; gap: 7px; padding: 5px 11px; border-radius: 20px; border: 1px solid var(--line); background: var(--bg-2); color: var(--txt-dim); font-size: 12.5px; cursor: pointer; user-select: none; transition: background .12s, border-color .12s, color .12s; }
/* ⚠⚠ `input[type="checkbox"]` PE SELECTOR, nu doar `input`. Bifele ascunse vizual de aici
   sunt înlocuite de un buline desenat (`.pop-dot`), iar regula veche era `.grp-opt input`
   = (0,1,1) — EXACT specificitatea regulii globale care desenează bifele, adăugată pe 08
   sep și aflată mai jos în fișier. Egalitate ⇒ decide ORDINEA ⇒ bifa a redevenit vizibilă
   și se suprapunea peste numele grupului. Semnalat de Bogdan („apare o bifă în dreptul
   Administrare CCTV"). A șaptea coliziune de felul ăsta în felia de design, și prima în
   care regula nouă a stricat una veche, nu invers. */
.grp-pill input[type="checkbox"] { position: absolute; opacity: 0; width: 0; height: 0; }
.grp-pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--txt-faint); flex: none; transition: background .12s; }
.grp-pill:hover:not(:has(input:disabled)) { border-color: var(--txt-faint); }
.grp-pill:has(input:checked) { background: var(--accent-bg); border-color: var(--accent); color: var(--accent); font-weight: 600; }
.grp-pill:has(input:checked)::before { background: var(--accent); }
.grp-pill:has(input:disabled) { cursor: default; opacity: .8; }
/* dropdown custom single-select (Rol) — opțiuni în popup dark, ca celelalte dropdown-uri */
/* dropdown custom single-select CANONIC (Rol, tip conexiune, filtre) — trigger input-like + popup dark */
.sel-dd { position: relative; }
.sel-dd > summary { list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; height: 36px; padding: 0 11px; box-sizing: border-box; border: 1px solid var(--line); border-radius: 8px; background: var(--bg-2); color: var(--txt); font: inherit; font-size: 13.5px; white-space: nowrap; }
.sel-dd > summary::-webkit-details-marker { display: none; }
.sel-dd > summary::after { content: ""; width: 15px; height: 15px; flex: none; background: currentColor; color: var(--txt-faint); -webkit-mask: var(--ico-jos) center/contain no-repeat; mask: var(--ico-jos) center/contain no-repeat; flex: none; }
.sel-dd[open] > summary { border-color: var(--accent); }
.sel-panel { position: absolute; top: calc(100% + 5px); left: 0; z-index: 30; min-width: 190px; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow); padding: 6px; display: flex; flex-direction: column; gap: 2px; }
/* item de popup (rând cu dot) — stil UNIFICAT: Rol (single, JS) + Grupuri (multi, checkbox) */
.sel-opt, .grp-opt { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 8px 10px; border-radius: 7px; border: none; background: none; color: var(--txt-dim); font: inherit; font-size: 13px; cursor: pointer; }
.sel-opt:hover, .grp-opt:hover { background: var(--panel-2); color: var(--txt); }
.sel-dot, .grp-opt .pop-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--txt-faint); flex: none; }
.sel-opt.sel-active, .grp-opt:has(input:checked) { color: var(--accent); font-weight: 600; }
.sel-opt.sel-active .sel-dot, .grp-opt:has(input:checked) .pop-dot { background: var(--accent); }
.grp-opt input[type="checkbox"] { position: absolute; opacity: 0; width: 0; height: 0; }
.grp-opt:has(input:disabled) { opacity: .7; cursor: default; }

/* „Adaugă utilizator" = card unificat, FULL-WIDTH; 4 câmpuri EGALE (aceeași înălțime), compacte */
.add-user-panel { margin-top: 24px; padding: 18px 20px; overflow: visible; }  /* dropdown Rol/Grupuri nu se taie sub card */
.add-user-panel .panel-h { margin: 0 0 4px; }
.add-user-panel .hint { margin: 0 0 16px; }
.add-user-form { width: 100%; }
.add-user-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); gap: 14px; }
.auf-f { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.auf-lbl { font-size: 12.5px; color: var(--txt-dim); font-weight: 600; }
/* TOATE controalele (input, select, dropdown grupuri) — identice ca dimensiune */
.add-user-grid input, .add-user-grid select, .add-grp-dd > summary {
  width: 100%; box-sizing: border-box; height: 36px;
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px;
  padding: 0 11px; color: var(--txt); font: inherit; font-size: 13.5px; font-weight: 400;
}
.add-user-grid input::placeholder { color: var(--txt-faint); }
.add-user-grid select {
  appearance: none; -webkit-appearance: none; cursor: pointer; padding-right: 30px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%235c6a82' stroke-width='1.5' fill='none'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 11px center;
}
.add-user-grid input:focus, .add-user-grid select:focus { outline: none; border-color: var(--accent); }
.add-grp-dd { width: 100%; }
.add-grp-dd > summary { display: flex; align-items: center; justify-content: space-between; gap: 8px; color: var(--txt-dim); cursor: pointer; list-style: none; }
.add-grp-dd > summary::-webkit-details-marker { display: none; }
.add-grp-dd > summary::after { content: ""; width: 15px; height: 15px; flex: none; background: currentColor; color: var(--txt-faint); -webkit-mask: var(--ico-jos) center/contain no-repeat; mask: var(--ico-jos) center/contain no-repeat; }
.add-user-foot { margin-top: 16px; }
.add-user-foot button[type="submit"] { background: var(--accent); color: #fff; border: none; border-radius: 8px; padding: 9px 20px; font: inherit; font-weight: 600; font-size: 13.5px; cursor: pointer; }
.add-user-foot button[type="submit"]:hover { filter: brightness(1.08); }
/* buton „+ Adaugă site nou" (e un <a>) — aceeași înfățișare ca butonul de adăugare utilizator */
.btn-add-site {
  display: inline-block; background: var(--accent); color: #fff; text-decoration: none;
  border: none; border-radius: 8px; padding: 9px 20px; font: inherit; font-weight: 600;
  font-size: 13.5px; cursor: pointer;
}
.btn-add-site:hover { filter: brightness(1.08); }

/* ---- tabelul Utilizatori -----------------------------------------------------------------
   ⚠ Măsurat pe 07 sep: cu formularele ÎN tabel avea 1513px lățime naturală și nu încăpea nici
   la 1600 (disponibil: 970px la 1280 cu meniul deschis, 1156 cu el strâns). Coloanele „Email"
   și „Grupuri" găzduiau câmpuri, deci nu se puteau scurta cu „…" — sunt lucruri în care scrii.
   Editarea a trecut în pop-up (`modale-admin.js`), tabelul doar AFIȘEAZĂ, iar lățimile de mai
   jos îl duc la ~1138px. Varianta B, aleasă de Bogdan: toate cele 10 coloane rămân vizibile,
   dar pe un laptop de 1280 se cere strânsă bara laterală (butonul ☰, care există deja).
   ⚠ Lățimile stau pe `nth-child`, fără clase în HTML — ordinea coloanelor într-un singur loc. */
/* ⚠⚠ Lățimile erau PROCENTE FIXE, alese de mine, și produceau exact ce a semnalat Bogdan pe
   08 sep: „prea mult spațiu între Login și Activitate […] și la mail îmi sare pe al doilea
   rând". Măsurat pe 1920 (tabel 1608px), lățimea CERUTĂ de fiecare coloană — clonând
   conținutul într-un element `nowrap`, nu estimând pe caractere:

     Nume 258 · Utilizator 178 · Rol 70 · Stare 65 · 2FA 112 · Email 300
     · Login 100 · Activitate 98 · Grupuri 227 · Acțiuni 87   ⇒ TOTAL 1495

   Deci tot conținutul încape, cu 113px de REZERVĂ — iar trei coloane tăiau (Email −107,
   Grupuri −66, Nume −49) în timp ce alte șapte aveau surplus. Nu era o problemă de spațiu,
   ci de împărțire.

   Lățimile de mai jos sunt calculate DIN cifrele alea: fiecare coloană primește cât cere,
   iar rezerva de 113px se duce în cele trei care chiar au ce arăta (nume, email, grupuri).

   ⚠ Prima încercare a fost `table-layout: auto` + `width: 1%` — idiomul „strânge la conținut".
   L-am ȘI aplicat, ȘI măsurat: pe pagina de utilizatori e GREȘIT. Cu `nowrap`, lățimea
   minimă a tabelului devine suma textelor întregi (**1513px**), deci tabelul nu mai poate
   scădea: la 1600 și 1700 ieșea 192px, respectiv 92px în afara paginii, iar sub 1550
   panoul are `overflow-x: auto` și apărea derulare orizontală — exact ce a fost RESPINS.
   Idiomul e bun acolo unde tabelul are voie să nu încapă (tabelele din Server, care oricum
   nu taie nimic); aici conținutul TREBUIE să se poată scurta.
   ⚠ Coloana de acțiuni are lățime în PIXELI, nu procent: butonul „Editează" are 69px + 24
   padding, iar 6% din 1128 (la 1440) însemna 68 — buton tăiat. O coloană cu conținut de
   lățime fixă nu se exprimă în procente. */
.tbl-users { table-layout: auto; width: 100%; }
/* ⚠ Padding SIMETRIC. Era `11px 14px 11px 18px`: intre coloane iesea 32px (14 dreapta +
   18 stinga), iar retragerea de la marginea panoului doar 14 — deci continutul incepea mai
   aproape de margine decit erau coloanele intre ele. Acum 28px intre coloane si 18px la
   AMBELE capete. */
.tbl-users th, .tbl-users td { padding: 11px 14px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.tbl-users th:first-child, .tbl-users td:first-child { padding-left: 18px; }
.tbl-users th:last-child,  .tbl-users td:last-child  { padding-right: 18px; }
/* Coloanele cu conținut de lungime FIXĂ — insigne, date, buton — se strâng la EXACT cât le
   trebuie (`width: 1%` = „cât îți trebuie"; browserul urcă valoarea la minimul de conținut).
   Semnalat de Bogdan: „între Stare și 2FA am ditamai spațiul" — venea din faptul că le
   dădusem procente calculate pe cazul cel mai lung posibil („parolă temporară", 111px), deși
   în tabel scrie „activ" (43px). Acum spațiul dintre coloane e același peste tot, fiindcă e
   dat de padding, nu de o lățime aleasă. */
.tbl-users th:nth-child(3),  .tbl-users td:nth-child(3),
.tbl-users th:nth-child(4),  .tbl-users td:nth-child(4),
.tbl-users th:nth-child(5),  .tbl-users td:nth-child(5),
.tbl-users th:nth-child(7),  .tbl-users td:nth-child(7),
.tbl-users th:nth-child(8),  .tbl-users td:nth-child(8),
.tbl-users th:nth-child(10), .tbl-users td:nth-child(10) { width: 1%; }
/* ⚠⚠ `max-width: 0` pe coloanele de TEXT. Fără el, `nowrap` face ca lățimea minimă a
   tabelului să fie suma textelor întregi (măsurat: 1513px) — tabelul nu mai poate scădea și
   iese din pagină la 1600/1700, sau apare derulare orizontală sub 1550. Cu `max-width: 0`
   au voie să se scurteze cu „…", iar procentele de mai jos spun doar CUM se împarte între
   ele spațiul rămas. Prima încercare a fost fără el și a stricat exact asta. */
.tbl-users td:nth-child(1), .tbl-users td:nth-child(2),
.tbl-users td:nth-child(6), .tbl-users td:nth-child(9) { max-width: 0; }
.tbl-users th:nth-child(1), .tbl-users td:nth-child(1) { width: 24%; }
.tbl-users th:nth-child(2), .tbl-users td:nth-child(2) { width: 16%; }
.tbl-users th:nth-child(6), .tbl-users td:nth-child(6) { width: 34%; }
.tbl-users th:nth-child(9), .tbl-users td:nth-child(9) { width: 26%; }
.tbl-users .u-email { white-space: nowrap; }
/* ⚠ Fără „…" pe celula cu buton: se aprindea la o depășire de UN pixel și desena trei puncte
   lângă un buton complet — arătau a al doilea comutator. Într-o celulă cu un CONTROL, nu cu
   text, scurtarea n-are înțeles. */
.tbl-users td.row-actions { text-overflow: clip; }
/* ⚠ Emailul rămâne pe UN rând. Ruperea în interiorul cuvântului era plasa pentru coloana
   prea îngustă; acum coloana e destul de lată la 1920, iar sub lățimea la care n-ar mai
   încăpea se scurtează cu „…", ca celelalte — întregul e în `title`. */
.tbl-users .u-email { white-space: nowrap; }
.u-bell { font-size: 11px; opacity: .75; }
.tbl-users .u-grupuri { color: var(--txt-dim); }
/* Bara de deasupra tabelului, cu butonul de adăugare. */
.tbl-bara { display: flex; justify-content: flex-end; margin: 0 0 12px; }
.u-fullname { font-weight: 600; color: var(--txt); }
/* coloane de timp (ultimul login / ultima activitate): cifre tabulare = aliniere verticală
   perfectă între rânduri + puțin mai compacte */
.tbl-users .u-time { font-variant-numeric: tabular-nums; font-size: 12.5px; letter-spacing: -.1px; }
.users-panel { overflow: visible; }   /* dropdown-ul de grupuri + meniul de acțiuni nu se taie */

/* Formularul de email + notificări + „acces până la" — acum trăiește în pop-up, nu în tabel. */
.u-email-form {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; white-space: normal;
}
.u-email-form input[type="email"] {
  width: 170px; max-width: 100%; background: var(--bg-2); border: 1px solid var(--line);
  border-radius: 8px; padding: 6px 9px; color: var(--txt); font: inherit; font-size: 13px;
}
.u-email-form input[type="email"]:focus { outline: none; border-color: var(--accent); }
.u-email-form input::placeholder { color: var(--txt-faint); }
.u-email-form .check { font-size: 12.5px; color: var(--txt-dim); white-space: nowrap; }
/* Sub 1500px tabelul devine mai lat decât ecranul: scroll pe TABEL, nu pe pagină.
   ⚠ `overflow-x` cere `overflow-y` calculabil, care ar tăia dropdown-urile — de-aia se aplică
   doar pe ecrane înguste, unde oricum e singura variantă utilizabilă. */
@media (max-width: 1500px) {
  .users-panel { overflow-x: auto; }
}

/* Acțiuni pe rând = buton „⋮" → mini-popup (nu mai 4 butoane care ies din tabel) */
.row-menu { position: relative; display: inline-block; }
.row-menu > summary { list-style: none; cursor: pointer; width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; border-radius: 7px; border: 1px solid var(--line); background: var(--bg-2); color: var(--txt-dim); font-size: 18px; line-height: 1; }
.row-menu > summary::-webkit-details-marker { display: none; }
.row-menu[open] > summary, .row-menu > summary:hover { border-color: var(--accent); color: var(--txt); }
.row-menu-panel { position: absolute; top: calc(100% + 5px); right: 0; z-index: 7; min-width: 170px; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow); padding: 0; overflow: hidden; display: flex; flex-direction: column; gap: 0; }
.row-menu-panel form { margin: 0; }
.row-menu-panel button { width: 100%; text-align: left; padding: 8px 10px; border-radius: 7px; border: none; background: none; color: var(--txt-dim); font: inherit; font-size: 13px; cursor: pointer; }
/* ținte de atingere pe mobil: 30px cu mănuși de lucru = mis-tap pe rândul vecin, adică
   mentenanță pusă pe camera greșită. ⚠ Blocul TREBUIE să stea AICI, după regulile de bază de
   mai sus — la specificitate egală câștigă ultima declarată, iar în blocul de mobil de la
   începutul fișierului era anulat tăcut și nu s-a aplicat niciodată. */
@media (max-width: 620px) {
  .row-menu > summary { width: 44px; height: 44px; }
  .row-menu-panel { right: 0; min-width: min(220px, 78vw); }
  .row-menu-panel button { min-height: 44px; }
}
.row-menu-panel button:hover { background: var(--panel-2); color: var(--txt); }
.row-menu-panel button.danger { color: var(--down); }
.row-menu-panel button.danger:hover { background: var(--down-bg); color: var(--down); }
/* acțiuni de rând: istoric + hartă + meniul ⋮ în același stil (buton solid, ca ⋮), aliniate */
.row-actions .btn-hist, .row-actions .btn-map, .row-actions .row-menu { vertical-align: middle; }
.row-actions .row-menu { margin-left: 6px; }
.row-actions .btn-hist, .row-actions .btn-map {
  height: 30px; box-sizing: border-box; display: inline-flex; align-items: center;
  padding: 0 12px; border: 1px solid var(--line); border-radius: 7px;
  background: var(--bg-2); color: var(--txt-dim); font-size: 12.5px; line-height: 1;
  text-decoration: none; opacity: 1; cursor: pointer;
}
.row-actions .btn-hist:hover, .row-actions .btn-map:hover { border-color: var(--accent); color: var(--txt); }
.grp-dd-note { font-size: 11.5px; color: var(--txt-faint); margin: 0 0 2px; }

/* popup credențiale la crearea unui user (copiabil) */
.cred-modal { position: fixed; inset: 0; z-index: 3000; display: flex; align-items: center; justify-content: center; padding: 24px; background: rgba(5,8,14,.62); backdrop-filter: blur(2px); }
.cred-card { width: min(460px, 100%); background: var(--panel); border: 1px solid var(--line); border-radius: 14px; box-shadow: 0 18px 50px rgba(0,0,0,.55); padding: 22px 24px; }
.cred-card h3 { margin: 0 0 8px; font-size: 17px; }
.cred-sub { color: var(--txt-dim); font-size: 13px; margin: 0 0 16px; line-height: 1.5; }
.cred-rows { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.cred-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; background: var(--bg-2); border: 1px solid var(--line-soft); border-radius: 8px; padding: 9px 12px; }
.cred-k { font-size: 12.5px; color: var(--txt-dim); }
.cred-v { font-family: ui-monospace, "SF Mono", monospace; font-size: 14px; color: var(--txt); user-select: all; }
.cred-actions { display: flex; gap: 10px; }
.add-user { margin-top: 24px; }
.admin-users .panel + .add-user { margin-top: 24px; }

/* ==== setări (Felia 2) ==== */
/* ⚠ `min(560px, 100%)`, nu `560px`: o pistă `minmax(Npx, 1fr)` NU SE POATE STRÂNGE sub N.
   Pe un ecran de 390, cardul de setare rămânea 560 și împingea PAGINA ÎNTREAGĂ să deruleze
   lateral cu ~350px — pe trei pagini deodată (Setări, Notificări, Email).
   ⚠ Simptomul e înșelător: elementul care nu încape nu e cel care „arată" greșit, ci
   containerul lui. Măsurasem `.setting-head` și `.setting` de trei ori înainte să urc un
   nivel. Aceeași corecție s-a aplicat tuturor declarațiilor de acest fel din fișier. */
.settings-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(560px, 100%), 1fr)); gap: 12px; align-items: stretch; }
.settings-cat { font-size: 12px; text-transform: uppercase; letter-spacing: .8px; color: var(--txt-dim); font-weight: 700; margin: 20px 0 10px; }
.settings-cat:first-of-type { margin-top: 4px; }

/* pagina Securitate acces: headere discrete + timp tabular pe un rând + formulare aliniate */
.sec-h { margin-top: 26px; }
.sec-time { white-space: nowrap; font-variant-numeric: tabular-nums; }
.sec-form { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; align-items: center; }
.sec-form input[type="text"] { background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 11px; color: var(--txt); font: inherit; font-size: 13.5px; min-width: 200px; }
.sec-form input[type="text"]:focus { outline: none; border-color: var(--accent); }
.setting {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 16px 18px;
  display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 12px 16px;
}
.setting-err { border-color: var(--down); }
.setting-head { grid-column: 1 / -1; display: flex; align-items: center; gap: 8px; }
.setting-label { font-weight: 600; }
.setting-form { display: contents; }
.setting-num { display: flex; align-items: center; gap: 8px; }
/* `time` alături de `number`: controlul nativ de oră arăta brut lângă câmpuri stilizate. */
.setting-num input[type="number"], .setting-num input[type="time"] {
  width: 96px; background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 10px; color: var(--txt); font: inherit;
}
/* controlul nativ de oră are pictograma de ceas în plus — 96px l-ar tăia */
.setting-num input[type="time"] { width: 130px; }
.setting-num input:focus { outline: none; border-color: var(--accent); }
.unit { color: var(--txt-dim); font-size: 13px; }
.setting-choices { display: flex; flex-wrap: wrap; gap: 8px; }
/* opțiuni (ex. moduri recording) = pastile în temă, ca la Grupuri */
.setting-choices .check { padding: 7px 12px; border: 1px solid var(--line-soft); border-radius: 8px;
  background: var(--bg-2); cursor: pointer; transition: border-color .12s, background .12s; }
.setting-choices .check:hover { border-color: var(--line); }
.setting-choices .check:has(input:checked) { border-color: var(--accent); background: var(--accent-bg); }
.setting-choices input[type="checkbox"], .setting-choices input[type="radio"] { accent-color: var(--accent); }
.setting-actions { display: flex; }
.setting-reset { margin: 0; }

/* buton info ⓘ — tooltip afișat de CSS la hover/focus, AȘEZAT de `tooltip-pozitie.js`.
   ⚠ `position: fixed`, NU `absolute` (2026-09-07, semnalat de Bogdan: „la unele dialogul iese
   din pagină, dacă e prea în dreapta, nu se poate citi"). Ancorat de ⓘ — 18px — cu `left: 0`,
   un tooltip de până la 320px iese din ecran cu tot ce nu încape: MĂSURAT 253px pe
   /admin/sites la toate lățimile (1280→1920) și 27px pe /admin/settings la 1600.
   Depășirea nu depinde de lățimea ferestrei, fiindcă iconița stă tot la ~67px de marginea
   dreaptă — deci nu se rezolvă „pe un ecran mai mare".
   ⚠ Fără `top`/`left` aici: un element `fixed` cu offseturi `auto` rămâne la poziția lui
   STATICĂ, deci dacă JS-ul n-ar rula, tooltipul apare tot lângă iconiță — nu în colțul
   ecranului. Fallback-ul unei apărări trebuie să fie inteligibil.
   ⚠ Aceeași soluție ca la `.ont-tip` (stâlpii DIGI), acum cu un singur poziționator. */
.ib {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%; cursor: help;
  color: var(--accent); font-size: 13px; position: relative; user-select: none;
}
.ib:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ib-tip {
  /* ⚠ 50, nu 60: `.acces-pop` (modalul „accesul tău expiră") e la 60, iar el trebuie să rămână
     deasupra. Nu se bazează pe faptul că modalul blochează oricum hover-ul — ierarhia se scrie,
     nu se deduce dintr-un efect lateral. */
  position: fixed; z-index: 50;
  width: max-content; max-width: 320px; padding: 10px 12px;
  background: var(--panel-2); color: var(--txt); border: 1px solid var(--line);
  border-radius: 8px; box-shadow: var(--shadow); font-size: 13px; line-height: 1.45;
  opacity: 0; visibility: hidden; transform: translateY(-3px); transition: .12s;
  font-weight: 400; text-align: left; pointer-events: none;
}
.ib:hover .ib-tip, .ib:focus .ib-tip, .ib:focus-within .ib-tip {
  opacity: 1; visibility: visible; transform: translateY(0);
}
@media (max-width: 820px) {
  .setting { grid-template-columns: 1fr; }
  .ib-tip { max-width: 78vw; }
}

/* ==== notițe de teren (corelare §12) ==== */
/* ⚠ Marginea de jos e `--sp-2`: ce urmează după titlu (insignele de stare sau textul
   introductiv) îl EXPLICĂ, deci e treapta de „bloc și lămurirea lui", nu una liberă. */
.modal-title { margin: 4px 44px var(--sp-2) 4px; font-size: 16px; }
.note-btn {
  background: transparent; border: 1px solid var(--line); border-radius: 6px;
  padding: 3px 8px; cursor: pointer; font: inherit; font-size: 13px; line-height: 1.3;
  color: var(--txt-dim); display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
}
.note-btn:hover { color: var(--txt); border-color: var(--txt-dim); }
.note-btn.has-notes { border-color: var(--accent); color: var(--accent); }
.note-count {
  background: var(--accent); color: #fff; font-size: 11px; font-weight: 700;
  min-width: 16px; height: 16px; border-radius: 999px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
}
.note-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }
.note-item { background: var(--bg-2); border: 1px solid var(--line-soft); border-radius: 8px; padding: 10px 12px; }
.note-meta { display: flex; gap: 10px; margin-bottom: 4px; font-size: 12.5px; }
.note-author { font-weight: 700; color: var(--accent); }
.note-date { color: var(--txt-faint); }
.note-body { white-space: pre-wrap; font-size: 14px; }
.note-empty { padding: 8px 2px; }
.note-form, .maint-form { display: flex; flex-direction: column; gap: 10px; }
.note-label { display: flex; flex-direction: column; gap: 6px; font-size: 13.5px; color: var(--txt-dim); font-weight: 600; }
/* ⚠ selectorul acoperă AMBELE formulare: scopat doar pe `.note-form`, textarea-ul din
   formularul de mentenanță rămânea complet nestilizat (default de browser). */
.note-form textarea, .maint-form textarea {
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px;
  padding: 10px 12px; color: var(--txt); font: inherit; font-size: 14px; resize: vertical;
  width: 100%; box-sizing: border-box;
}
.note-form textarea:focus, .maint-form textarea:focus { outline: none; border-color: var(--accent); }
.note-form button { align-self: flex-start; }
.note-err { margin-bottom: 10px; }

/* ==== model cameră scris de om (meniul ⋮, 2026-09-10) ==== */
/* ⚠ Spațierea vine din scara `--sp-1..4`, nu din valori alese la ochi: eticheta lipită de
   câmpul ei (`--sp-1`), textul care explică blocul (`--sp-2`), între secțiuni (`--sp-4`). */
.mm-stare { margin-bottom: var(--sp-4); }
.mm-cam { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: baseline; }
.mm-live {
  margin-top: var(--sp-2); padding: 10px 12px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--bg-2);
  font-size: 13px; color: var(--txt-dim); line-height: 1.5;
}
.mm-form { display: flex; flex-direction: column; }
.mm-eticheta { font-size: 13.5px; color: var(--txt-dim); font-weight: 600; margin-bottom: var(--sp-1); }
/* ⚠ Se scrie prin EXCLUDERE, nu enumerând tipuri: un selector care listează `type="text"`
   lasă pe dinafară orice tip adăugat mai târziu, tăcut (precedentul câmpului de dată). */
.mm-form input:not([type="checkbox"]):not([type="radio"]) {
  height: 40px; border: 1px solid var(--line); border-radius: 9px; background: var(--bg-2);
  color: var(--txt); font: inherit; font-size: 13.5px; padding: 0 12px;
  width: 100%; box-sizing: border-box;
}
.mm-form input:focus { outline: none; border-color: var(--accent); }
.mm-hint { margin-top: var(--sp-2); line-height: 1.5; }
.mm-actiuni { margin-top: var(--sp-4); }
.mm-urma { margin-top: var(--sp-3); font-size: 12.5px; }

/* ==== overlay config admin: site-uri + excepții camere ==== */
/* carduri pliabile per site (details/summary) — aliniate cu .setting */
.site-ov {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); overflow: hidden;
}
.site-ov + .site-ov { margin-top: 0; }
.site-sum {
  /* ⚠ TREI coloane, nu cinci (2026-08-11). Aveam o coloană fixă per pastilă (132px pentru
     contract, 76px pentru conexiune); orice pastilă nouă trecea pe rândul următor, în stânga
     jos, iar una cu text mai lung era strivită în lățimea fixă. Acum pastilele stau într-un
     singur container flexibil, deci se pot adăuga oricâte fără să se mute nimic. */
  display: grid; grid-template-columns: 18px minmax(180px, 260px) 1fr;
  align-items: center; gap: 8px 12px; padding: 14px 16px;
  cursor: pointer; list-style: none; user-select: none;
}
.site-sum-badges { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
/* `margin-left` de pe `.badge` ar fi dublat `gap`-ul; aici distanța o dă containerul. */
.site-sum-badges .badge { margin-left: 0; }
.site-sum::-webkit-details-marker { display: none; }
.site-sum::before {
  content: ""; width: 14px; height: 14px; background: currentColor;
  color: var(--txt-faint); -webkit-mask: var(--ico-dreapta) center/contain no-repeat;
  mask: var(--ico-dreapta) center/contain no-repeat;
  transition: transform .15s ease; display: inline-block;
}
.site-ov[open] > .site-sum::before { transform: rotate(90deg); }
.site-sum:hover { background: var(--panel-2); }
/* listă pe O coloană (details colapsabile) — grid-ul de 2 coloane strica expand-ul */
.site-ov-list { display: flex; flex-direction: column; gap: 8px; }
.site-sum-name { font-size: 15px; font-weight: 600; color: var(--txt); }
/* `.site-sum-nvr` (numărul de NVR-uri) a fost SCOS din antet la cererea lui Bogdan: ajunsese
   pe rândul doi din cauza grid-ului de mai sus și arăta ca un text rătăcit. IP-urile NVR se
   văd oricum în panoul comunei, la un click. */
.pill-ov {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
  padding: 2px 8px; border-radius: 999px; background: var(--accent-bg); color: var(--accent);
}
.site-ov[open] .ov-form { border-top: 1px solid var(--line-sect); }

/* formular overlay (site-uri + adaugă excepție cameră) */
.ov-form { padding: 16px; }
.ov-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px 18px; margin-bottom: 16px;
}
/* ⚠ Stilul de CÂMP nu mai cere ancestorul `.ov-grid` (corecție audit 2026-08-03). Legat de
   părinte, s-a rupt tăcut de două ori în aceeași zi: câmpurile din /admin/email (container
   `.mail-grid`) și „Pauză între colectări" din /admin/sites (așezat DUPĂ închiderea `.ov-grid`)
   au rămas cu controale brute de browser, fără fundal/bordură/font din temă. Un câmp trebuie să
   arate la fel oriunde e pus. */
.ov-grid label:not(.ov-check), .ov-field {
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 7px 8px;
  font-size: 13px; color: var(--txt-dim); font-weight: 600;
}
/* eticheta + ⓘ pe primul rând; controlul (input/dropdown) pe rândul de dedesubt, full-width */
.ov-grid label > input[type="text"], .ov-field > input, .ov-field > .sel-dd,
.ov-field > select { grid-column: 1 / -1; }
.ov-check input[type="checkbox"] { accent-color: var(--accent); }
.ov-grid label.ov-wide { grid-column: 1 / -1; }
.ov-grid input[type="text"], .ov-grid select,
.ov-field input[type="text"], .ov-field input[type="number"],
.ov-field input[type="password"], .ov-field input[type="email"], .ov-field select {
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px;
  padding: 9px 11px; color: var(--txt); font: inherit; font-size: 14px;
  width: 100%; box-sizing: border-box;
}
.ov-grid input[type="text"]:focus, .ov-grid select:focus,
.ov-field input:focus, .ov-field select:focus { outline: none; border-color: var(--accent); }
.ov-grid input::placeholder, .ov-field input::placeholder { color: var(--txt-faint); }
/* rând checkbox contract (pe toată lățimea, inline) */
.ov-check {
  grid-column: 1 / -1; flex-direction: row !important; align-items: center;
  gap: 8px; color: var(--txt); font-size: 13.5px; font-weight: 600;
}
.ov-check input { width: auto; }
/* ---- Profil de brand, un rând per NVR (2026-08-13) ---------------------------------- */
/* ⚠ Selectorii sunt prefixați cu `.ov-brands`: în interiorul unui card cu formular, o clasă
   simplă e bătută de orice selector element+clasă (`.ov-grid input[type="text"]` = 0,2,1).
   Regula asta ne-a costat de trei ori într-o singură zi (10 aug). */
.ov-brands { grid-column: 1 / -1; display: grid; gap: 8px; }
.ov-brands .ov-brands-head {
  display: grid; grid-template-columns: 1fr auto; align-items: center;
  font-size: 13px; color: var(--txt-dim); font-weight: 600;
}
.ov-brands .ov-brand-row {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 10px;
}
.ov-brands .ov-brand-name {
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px;
  padding: 7px 10px; color: var(--txt); font: inherit; font-size: 13.5px;
  width: 100%; box-sizing: border-box; min-height: 34px;
}
.ov-brands .ov-brand-name:focus { outline: none; border-color: var(--accent); }
.ov-brands .ov-brand-name::placeholder { color: var(--txt-faint); }
.ov-brands .ov-brand-host {
  font-size: 13.5px; color: var(--txt-dim); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ov-brands .ov-brands-note { margin: 2px 0 0; font-size: 12px; color: var(--txt-faint); }
.ov-brands .seg {
  display: inline-flex; border: 1px solid var(--line); border-radius: 8px;
  overflow: hidden; background: var(--bg-2);
}
.ov-brands .seg .seg-opt {
  background: none; border: 0; padding: 7px 13px; font: inherit; font-size: 13px;
  color: var(--txt-dim); cursor: pointer; min-height: 34px;
}
.ov-brands .seg .seg-opt + .seg-opt { border-left: 1px solid var(--line); }
.ov-brands .seg .seg-opt.seg-on { background: var(--accent); color: #fff; font-weight: 600; }
.ov-brands .seg .seg-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ⚠ CINCI coloane, nu patru. Rindul are cinci elemente vizibile — adresa, numele, comutatorul
   de brand, „Verifica" si „✕" — iar grila declara patru: al cincilea cadea pe un rind nou,
   intr-o caseta de 120px, si arata a cimp gol stricat. Butonul „✕" a fost adaugat pe 13 aug
   (`652a634`) fara sa fie actualizata grila din `2697090`, din aceeasi zi; a stat asa trei
   saptamini, fiindca se vede doar cu o comuna DESFACUTA. Gasit pe o captura, 08 sep.
   ⚠ De retinut: o grila cu numar FIX de coloane e o afirmatie despre cite elemente are rindul.
   Cine adauga al n+1-lea element trebuie sa treaca si pe aici — nimic nu da eroare. */
/* ⚠ ȘASE coloane de la 09 sep: adresa · numele · brandul · modurile de înregistrare ·
   „Verifică" · „✕". A cincea a fost adăugată pe 08 sep, tot fiindcă lipsea din grilă.
   O grilă cu număr FIX de coloane e o afirmație despre cîte elemente are rîndul. */
.ov-brands .ov-brand-row { grid-template-columns: minmax(120px, auto) 1fr auto auto auto auto; }
/* Comutatorul de moduri: aceleași dimensiuni ca cel de brand, ca rîndul să rămînă aliniat. */
.ov-brands .seg-rec .seg-opt { padding: 0 10px; }
.ov-brands .ov-brand-host-nou {
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px;
  padding: 7px 10px; color: var(--txt); font: inherit; font-size: 13.5px;
  min-height: 34px; box-sizing: border-box; width: 100%;
}
.ov-brands .ov-brand-host-nou:focus { outline: none; border-color: var(--accent); }
.ov-brands .ov-brand-add { display: flex; align-items: center; gap: 10px; margin-top: 2px; }
.ov-brands .ov-add-hint { font-size: 12px; color: var(--txt-faint); }
/* Rezultatul recunoașterii — sub rândul lui, pe toată lățimea. */
.ov-brands .ov-check-out { margin: -2px 0 4px; font-size: 12.5px; }
.ov-brands .ov-check-wait { color: var(--txt-faint); }
.ov-brands .ov-check-ok { color: var(--ok, #3fb950); }
.ov-brands .ov-check-fail { color: var(--warn, #d29922); }

/* badge DIGI (informativ, neutru — nu e nici problemă, nici privilegiu) */
.badge-digi { background: var(--panel-2); color: var(--txt-dim); border: 1px solid var(--line); }
/* filtru fleet: bifa „afișare comune fără contract" */
/* Comutatorul din banda de sus, imediat după butonul de meniu.
   ⚠ Distanța cerută de Bogdan: „cam cât alinierea de la margine la buton", adică cei 24px de
   padding ai barei. Butonul are `margin-right: 4px`, bara are `gap: 12px` ⇒ 16px; cei 8px de
   aici completează până la 24, ca stânga butonului și stânga comutatorului să pară simetrice.
   ⚠ `margin: 0` explicit: `.fleet-filter` avea marginile de bloc de dinainte, care într-o bară
   flex de 58px ar fi împins comutatorul în sus. */
.topbar-filter { margin: 0 0 0 8px; flex: none; }
.topbar-filter .switch { padding: 4px 0; white-space: nowrap; }
/* Pe ecran îngust rămâne comutatorul + numărul; eticheta lungă ar fi împins meniul contului
   afară din bară. Numărul rămâne, ca să știi CÂTE comune ascunzi. */
@media (max-width: 900px) {
  /* ⚠ Nu se ASCUNDE eticheta, se SCURTEAZĂ: cu `font-size: 0` rămâneau un întrerupător gol
     și „(4)", adică un buton despre care nu poți ști ce face. */
  .topbar-filter .switch-lbl-lung { display: none; }
  .topbar-filter .switch-lbl-scurt { display: inline; }
}
/* switch on/off (persistat în cont) — înlocuiește vechea bifă */
.switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; background: none; border: none; padding: 4px 2px; font: inherit; }
.switch-track { position: relative; width: 40px; height: 22px; flex: none; border-radius: 999px; background: var(--panel-2); border: 1px solid var(--line); transition: background .18s, border-color .18s; }
.switch-thumb { position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--txt-faint); transition: transform .18s, background .18s; }
.switch-on .switch-track { background: var(--ok); border-color: var(--ok); }   /* verde = on (roșul părea off) */
.switch-on .switch-thumb { transform: translateX(18px); background: #fff; }
.switch-lbl { font-size: 13.5px; color: var(--txt-dim); user-select: none; }
.switch-lbl-scurt { display: none; }
.switch:hover .switch-track { border-color: var(--accent); }
.switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
/* butonul principal de salvare din formularele overlay */
.ov-form .setting-actions button[type="submit"],
.ov-form > button[type="submit"] {
  background: var(--accent); color: #fff; border: none; border-radius: 8px;
  padding: 9px 16px; font: inherit; font-weight: 700; cursor: pointer; font-size: 13.5px;
}
.ov-form .setting-actions button[type="submit"]:hover,
.ov-form > button[type="submit"]:hover { filter: brightness(1.08); }
.site-ov > .setting-reset { margin: 0; padding: 0 16px 16px; }

@media (max-width: 720px) {
  .ov-grid { grid-template-columns: 1fr; }
}

/* ============================================================================
   Onboarding wizard (Adaugă site nou) — pași 1..5, punte web→worker.
   Markup + clase fixate în templates/admin_onboard.html (NU se editează).
   Totul e prefixat .ob-* / scopat sub .ob-step ca să nu atingă alte pagini.
   Tokeni, nu culori: stările = --ok/--warn/--down; accentul/pasul curent = --accent.
   ============================================================================ */

/* wizardul se întinde full-width, ca restul zonei admin (preferință Bogdan 2026-07-31) */

/* ---- listă drafturi în lucru (reluare) ---- */
.ob-resume {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 14px 16px; margin-bottom: 20px;
}
.ob-resume > .hint { margin-bottom: 10px; }
.ob-resume-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 12px; border: 1px solid var(--line-soft); border-radius: 9px; background: var(--bg-2);
}
.ob-resume-row + .ob-resume-row { margin-top: 8px; }
.ob-resume-link { display: flex; flex-direction: column; gap: 2px; text-decoration: none; min-width: 0; }
.ob-resume-link strong { color: var(--txt); font-weight: 600; }
.ob-resume-link .dim { font-size: 12px; }
.ob-resume-link:hover strong { color: var(--accent); }

/* ---- indicator de pași (1..5) — .is-active / .is-done puse de JS pe .ob-pip ---- */
.ob-steps {
  list-style: none; margin: 0 0 24px; padding: 0;
  display: flex; align-items: flex-start;
}
.ob-pip {
  flex: 1 1 0; min-width: 0; position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 7px; text-align: center;
}
/* linia de legătură spre pasul din stânga (sub cerc) */
.ob-pip::before {
  content: ""; position: absolute; top: 15px; left: -50%; width: 100%; height: 2px;
  background: var(--line); z-index: 0;
}
.ob-pip:first-child::before { display: none; }
.ob-pip-n {
  position: relative; z-index: 1;
  width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
  background: var(--bg-2); border: 1px solid var(--line); color: var(--txt-faint);
  transition: background .15s, border-color .15s, color .15s, box-shadow .15s;
}
.ob-pip-t { font-size: 12px; font-weight: 600; line-height: 1.25; color: var(--txt-faint); }
/* pas trecut: marcat subtil (accent estompat), linia din stânga = accent */
.ob-pip.is-done .ob-pip-n { background: var(--accent-bg); border-color: var(--accent); color: var(--accent); }
.ob-pip.is-done .ob-pip-t { color: var(--txt-dim); }
.ob-pip.is-done::before, .ob-pip.is-active::before { background: var(--accent); }
/* pas curent: accent plin + inel */
.ob-pip.is-active .ob-pip-n {
  background: var(--accent); border-color: var(--accent); color: #fff;
  box-shadow: 0 0 0 4px var(--accent-bg);
}
.ob-pip.is-active .ob-pip-t { color: var(--txt); }

/* ---- cardul unui pas ---- */
.ob-step {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 22px 24px;
}
.ob-h { font-size: 17px; font-weight: 700; letter-spacing: -.2px; margin: 0 0 6px; color: var(--txt); }
.ob-step > .hint { margin-bottom: 18px; }
.ob-step input[type="radio"], .ob-step input[type="checkbox"] { accent-color: var(--accent); }

/* ---- grilă de câmpuri (pași 1+2), analog cu .ov-grid ---- */
/* ⚠ `align-items: start` pe grilă + `align-content: start` pe câmp: câmpul „Identificator" are
   un rând în plus (mesajul de disponibilitate), deci e mai înalt. Fără astea, grila întindea
   ambele coloane la aceeași înălțime, iar spațiul în plus se distribuia peste rândurile AUTO ale
   câmpului vecin — îi cobora input-ul cu ~1em față de al slug-ului. */
.ob-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 18px; margin-bottom: 16px;
  align-items: start; }
.ob-field {
  display: grid; grid-template-columns: 1fr auto; align-items: center; align-content: start;
  gap: 7px 8px; font-size: 13px; color: var(--txt-dim); font-weight: 600;
}
.ob-field.ob-wide { grid-column: 1 / -1; }
/* eticheta + ⓘ pe rândul 1; controlul + mesajul inline full-width dedesubt */
.ob-field > input, .ob-field > .ob-inline-msg { grid-column: 1 / -1; }
.ob-grid input {
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px;
  padding: 9px 11px; color: var(--txt); font: inherit; font-size: 14px;
}
.ob-grid input:focus { outline: none; border-color: var(--accent); }
.ob-grid input:read-only { color: var(--txt-dim); background: var(--bg); cursor: default; }
.ob-grid input::placeholder { color: var(--txt-faint); }
.ob-inline-msg { font-size: 12px; font-weight: 500; color: var(--txt-faint); min-height: 1em; }
.ob-inline-msg.is-ok { color: var(--ok); }
.ob-inline-msg.is-bad { color: var(--down); }

/* ---- grup „tip conexiune" (radio) + bifă contract ---- */
.ob-conn { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; margin-bottom: 16px; }
.ob-conn-lbl {
  width: 100%; font-size: 13px; font-weight: 600; color: var(--txt-dim);
  display: inline-flex; align-items: center; gap: 6px;
}
.ob-conn .check { font-size: 13.5px; color: var(--txt); font-weight: 500; }
.ob-contract { margin-bottom: 16px; color: var(--txt); font-size: 13.5px; }

/* ---- rândul de acțiuni (Înapoi / Continuă) ---- */
.ob-actions {
  display: flex; align-items: center; gap: 10px;
  margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line-sect);
  justify-content: flex-end;
}
.ob-actions .ob-back { margin-right: auto; }  /* Înapoi la stânga, acțiunea principală la dreapta */
/* blocul de chei are propriul ob-actions intern (fără linie sus) */
.ob-block .ob-actions { margin-top: 14px; padding-top: 0; border-top: none; }

/* ---- butoane principale ale pașilor (accent plin); restul rămân .btn-sm outline ---- */
#ob-next-1, #ob-next-2, #ob-next-3, #ob-next-4,
#ob-gen-keys, #ob-provision, #ob-verify, #ob-ack-keys, #ob-set-cred, #ob-dry-run, #ob-activate {
  background: var(--accent); color: #fff; border-color: var(--accent);
  font-weight: 700; font-size: 13px; padding: 8px 16px;
}
#ob-next-1:hover:not(:disabled), #ob-next-2:hover:not(:disabled),
#ob-next-3:hover:not(:disabled), #ob-next-4:hover:not(:disabled),
#ob-gen-keys:hover:not(:disabled), #ob-provision:hover:not(:disabled),
#ob-ack-keys:hover:not(:disabled), #ob-set-cred:hover:not(:disabled),
#ob-dry-run:hover:not(:disabled), #ob-activate:hover:not(:disabled) {
  filter: brightness(1.08); color: #fff; border-color: var(--accent);
}
/* dezactivat = clar inactiv (estompat + cursor not-allowed), fără hover */
.ob-step button:disabled { opacity: .55; cursor: not-allowed; filter: none; }

/* ---- blocuri de sub-pași (3/4/5): generare chei, tunel, credential, camere ---- */
.ob-block {
  background: var(--bg-2); border: 1px solid var(--line-soft); border-radius: 10px;
  padding: 16px; margin-bottom: 14px;
}
.ob-block-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px;
}
.ob-block-head strong { font-size: 14px; font-weight: 700; color: var(--txt); }

/* ---- pastile de status (busy / ok / err) ---- */
.ob-status { font-size: 12px; font-weight: 600; }
.ob-status:empty { display: none; }
.ob-status-busy, .ob-status-ok, .ob-status-err {
  padding: 3px 10px; border-radius: 999px; border: 1px solid transparent; white-space: nowrap;
}
.ob-status-busy { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-line); }
.ob-status-ok { color: var(--ok); background: var(--ok-bg); border-color: var(--ok-line); }
.ob-status-err { color: var(--down); background: var(--down-bg); border-color: var(--down-line); }
/* spinner discret pe „se lucrează" */
.ob-status-busy::before {
  content: ""; display: inline-block; width: 9px; height: 9px; margin-right: 6px; vertical-align: -1px;
  border-radius: 50%; border: 2px solid var(--warn); border-top-color: transparent;
  animation: ob-spin .7s linear infinite;
}
@keyframes ob-spin { to { transform: rotate(360deg); } }

/* ---- blocuri de cod copiabile (config UDM + regulă ZBF) ---- */
.ob-pre-wrap { margin: 12px 0; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.ob-pre-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 12px; background: var(--panel-2); border-bottom: 1px solid var(--line);
  font-size: 12px; font-weight: 600; color: var(--txt-dim);
}
.ob-pre {
  margin: 0; padding: 12px 14px; background: var(--bg);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; line-height: 1.55;
  color: var(--txt); white-space: pre; overflow-x: auto;
}

/* ---- linie de stare (handshake / nr. camere) + listă NVR ---- */
.ob-state-line { font-size: 13px; font-weight: 600; margin: 10px 0 8px; color: var(--txt-dim); }
.ob-state-line.is-ok { color: var(--ok); }
.ob-state-line.is-bad { color: var(--down); }
.ob-nvr-list { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.ob-nvr {
  display: flex; align-items: center; gap: 9px; font-size: 13px; font-variant-numeric: tabular-nums;
  padding: 7px 11px; border-radius: 8px; border: 1px solid var(--line-soft); background: var(--panel);
}
.ob-nvr.is-ok { border-color: var(--ok-line); }
.ob-nvr.is-bad { border-color: var(--down-line); }
.ob-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; background: var(--txt-faint); }
.ob-nvr.is-ok .ob-dot { background: var(--ok); }
.ob-nvr.is-bad .ob-dot { background: var(--down); }

/* ---- tabelul de camere descoperite ---- */
.ob-table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: 10px; margin: 10px 0; }
.ob-cam-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.ob-cam-table th {
  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--txt-faint); font-weight: 700; padding: 9px 12px;
  background: var(--panel-2); border-bottom: 1px solid var(--line);
}
.ob-cam-table td {
  padding: 8px 12px; border-bottom: 1px solid var(--line-soft);
  color: var(--txt); font-variant-numeric: tabular-nums;
}
.ob-cam-table tbody tr:last-child td { border-bottom: none; }
.ob-cam-table td.is-ok { color: var(--ok); font-weight: 600; }
.ob-cam-table td.is-bad { color: var(--down); font-weight: 600; }
.ob-cam-table tr:has(td.is-bad) { background: var(--down-bg); }  /* tent subtil pe rândul căzut */
#ob-cam-count { color: var(--txt); font-weight: 700; }

/* ---- lista de erori de la descoperire ---- */
.ob-errs {
  list-style: none; margin: 10px 0 0; padding: 12px 14px; border-radius: 8px;
  background: var(--down-bg); border: 1px solid var(--down-line); color: var(--down); font-size: 12.5px;
}
.ob-errs li { margin: 2px 0; }

/* ---- rezumat (pas 5) ---- */
.ob-summary { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin: 0 0 18px; }
.ob-summary > div {
  background: var(--bg-2); border: 1px solid var(--line-soft); border-radius: 10px; padding: 12px 14px;
}
.ob-summary dt {
  font-size: 11px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--txt-faint); font-weight: 700; margin: 0 0 4px;
}
.ob-summary dd { margin: 0; font-size: 15px; font-weight: 600; color: var(--txt); font-variant-numeric: tabular-nums; }

/* ---- „gata" + abandonare draft ---- */
#ob-done p { margin: 4px 0; }
#ob-done a { color: var(--accent); font-weight: 600; }
.ob-abandon { display: block; margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--line-sect); }
.ob-abandon-btn:hover { color: var(--down); border-color: var(--down); }

@media (max-width: 720px) {
  .ob-grid, .ob-summary { grid-template-columns: 1fr; }
  .ob-step { padding: 18px 16px; }
  .ob-pip-t { font-size: 11px; }
  .ob-block-head { flex-wrap: wrap; }
}

/* ---- pagina Email (SMTP) ---- */
.mail-form { display: flex; flex-direction: column; gap: 12px; }
.mail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: 12px; }
.mail-result { margin-top: 10px; padding: 10px 12px; border-radius: 9px; font-size: 14px; }
.mail-ok  { background: var(--ok-bg); border: 1px solid var(--ok); color: var(--ok); }
.mail-err { background: var(--down-bg); border: 1px solid var(--down); color: var(--down); }
/* test în curs: nici verde, nici roșu — încă nu știm nimic */
.mail-info { background: var(--panel-2); border: 1px solid var(--line); color: var(--txt-dim); }
p.hint.mail-err { background: none; border: none; padding: 0; }

/* ---- Rețea & fibră (Setări site): OLT + router per sat ------------------------------
   ⚠ Supraveghere DIN UMBRĂ — zona asta apare DOAR în /admin/sites, niciodată în pagina
   comunei (decizia Bogdan 2026-08-03). */
.infra-zone { margin: 14px 0 4px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--bg-2); }
.infra-zone > summary {
  cursor: pointer; padding: 10px 14px; font-size: 13px; font-weight: 600; color: var(--txt-dim);
  display: flex; align-items: center; gap: 10px; list-style: none;
}
.infra-zone > summary::-webkit-details-marker { display: none; }
.infra-zone > summary::before { content: ""; width: 14px; height: 14px; flex: none; background: currentColor; color: var(--txt-faint); -webkit-mask: var(--ico-dreapta) center/contain no-repeat; mask: var(--ico-dreapta) center/contain no-repeat; transition: transform .15s; }
.infra-zone[open] > summary::before { transform: rotate(90deg); }
.infra-zone > summary:hover { color: var(--txt); }
.infra-bad {
  font-size: 11.5px; font-weight: 700; color: var(--down);
  border: 1px solid var(--down); border-radius: 20px; padding: 1px 8px;
}
/* tabelul poate depăși lățimea pe ecrane mici → scroll pe TABEL, nu pe pagină */
.infra-zone .scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tbl-infra { width: 100%; border-collapse: collapse; font-size: 13px; min-width: 640px; }
.tbl-infra th, .tbl-infra td {
  text-align: left; padding: 7px 12px; border-top: 1px solid var(--line-soft); vertical-align: top;
}
.tbl-infra th {
  font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--txt-faint);
  font-weight: 600; white-space: nowrap;
}
.tbl-infra tbody tr:hover { background: var(--panel); }
.infra-row-bad td:first-child { box-shadow: inset 3px 0 0 var(--down); }
/* problemă apărută, dar încă neconfirmată în timp (anti-flapping) — se vede, nu alarmează */
.infra-row-new td:first-child { box-shadow: inset 3px 0 0 var(--warn); }
.tbl-infra .ok-t  { color: var(--ok); font-weight: 600; }
.tbl-infra .bad-t { color: var(--down); font-weight: 600; }
.infra-zone > .hint { padding: 4px 14px 12px; margin: 0; }
/* numele satului — câmp editabil în tabelul „Rețea & fibră" */
.tbl-infra .infra-name { min-width: 150px; }
.tbl-infra .infra-name input {
  width: 100%; box-sizing: border-box; background: var(--bg-2); border: 1px solid var(--line);
  border-radius: 7px; padding: 5px 8px; color: var(--txt); font: inherit; font-size: 13px;
}
.tbl-infra .infra-name input:focus { outline: none; border-color: var(--accent); }
.tbl-infra .infra-name input::placeholder { color: var(--txt-faint); }
/* numele satelor în wizard — o coloană per subrețea, aliniate cu restul câmpurilor */
.ob-lbl-row { font-size: 12.5px; }
/* ⚠ `var(--font, inherit)` era inutil: tokenul NU există în proiect, iar `inherit` moștenea
   exact monospace-ul de pe rând, pe care regula voia să-l anuleze. Numele satului se scrie cu
   fontul temei; doar CIDR-ul rămâne mono. */
.ob-lbl-row input[type="text"] { font-family: var(--font); font-size: 14px; }
.ob-lbl-row { font-family: var(--mono); }

/* ---- wizard onboarding: subrețele repetabile ---------------------------------------- */
.ob-sub-row { display: flex; gap: 8px; align-items: center; margin-bottom: 6px; }
.ob-sub-row input {
  flex: 1 1 auto; min-width: 0;
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px;
  padding: 9px 11px; color: var(--txt); font: inherit; font-size: 14px;
}
.ob-sub-row input:focus { outline: none; border-color: var(--accent); }
.ob-sub-row input::placeholder { color: var(--txt-faint); }
.ob-sub-del { flex: 0 0 auto; }
.ob-add-sub { margin-top: 4px; }

/* ---- wizard onboarding: pașii manuali de pe UDM (pasul 3) ---------------------------- */
.ob-manual {
  margin: 14px 0; padding: 12px 14px; border: 1px solid var(--line);
  border-left: 3px solid var(--warn); border-radius: 0 10px 10px 0; background: var(--bg-2);
  font-size: 13.5px; color: var(--txt-dim); line-height: 1.55;
}
.ob-manual-h {
  font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--txt); margin: 10px 0 4px;
}
.ob-manual-h:first-child { margin-top: 0; }
.ob-manual p { margin: 4px 0; }
.ob-manual code {
  background: var(--panel); border: 1px solid var(--line-soft); border-radius: 5px;
  padding: 1px 5px; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: .92em;
  color: var(--txt);
}
.ob-manual-warn { color: var(--warn); font-weight: 600; }
.ob-manual-list { margin: 4px 0 0 18px; display: grid; gap: 6px; }
.ob-manual-list strong { color: var(--txt); }

/* ---- Onboarding: reparații din auditul de design (2026-08-04) ----------------------- */
/* `.check` fixa flex-direction dar NU declara `display: flex` — se baza pe ce moștenea de la
   `.ob-grid label`. Mutată la pasul 1, `.ob-contract` a ieșit din grilă și bifa a căzut sub text. */
.check { display: flex; }

/* Clasă folosită în șablon fără nicio regulă: blocul de rezultat al verificării tunelului
   apărea lipit de buton, fără separare vizuală de restul pasului. */
.ob-tunnel-out {
  display: flex; flex-direction: column; gap: 8px;
  margin-top: 14px; padding: 12px 14px;
  background: var(--panel-2); border: 1px solid var(--line-soft); border-radius: 8px;
}
/* Eticheta „provizionat" din lista de drafturi (butonul de ștergere dispare acolo). */
.ob-locked {
  font-size: 12px; padding: 3px 9px; border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--line-soft);
}

/* Antetul blocurilor de cod se sprijinea pe `--panel-2` peste `--panel`; în tema light
   diferența e prea mică pentru a citi unde începe blocul. O linie explicită o rezolvă în
   ambele teme, fără să atingem tokenii globali. */
.ob-pre-head { border-bottom-width: 2px; }

/* Inele de focus vizibile — wizardul se parcurge mult de la tastatură (Tab între câmpuri). */
.ob-form :focus-visible, .ob-actions :focus-visible,
.ob-pip:focus-visible, .ob-copy:focus-visible {
  outline: 2px solid var(--accent, #4c8dff); outline-offset: 2px; border-radius: 4px;
}
/* Un buton dezactivat trebuie să ARATE dezactivat (pașii se deblochează pe rând). */
.ob-form :disabled, .ob-actions :disabled, button:disabled {
  opacity: .55; cursor: not-allowed;
}
/* Ținte de atins pe telefon — colegii sunt pe teren. Vezi ⚠ din CLAUDE.md: regula de 44px a
   stat moartă săptămâni din cauza unei coliziuni de cascadă, de aceea e scopată explicit. */
@media (max-width: 620px) {
  .ob-actions .btn-sm, .ob-copy, .ob-abandon-btn { min-height: 44px; padding-inline: 14px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Registrul de alerte (/admin/notificari) — tabel ierarhic pe trei niveluri:
   categorie (details) → tip → variante. Design aprobat de Bogdan (preview CSS).
   ═══════════════════════════════════════════════════════════════════════════ */
.al-cat { background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  margin-bottom: 10px; overflow: hidden; }
.al-cat > summary { cursor: pointer; list-style: none; padding: 12px 14px; display: flex;
  align-items: center; gap: 10px; font-weight: 620; user-select: none; }
.al-cat > summary::-webkit-details-marker { display: none; }
.al-cat > summary::before {
  content: ""; width: 14px; height: 14px; background: currentColor;
  color: var(--txt-faint); -webkit-mask: var(--ico-dreapta) center/contain no-repeat;
  mask: var(--ico-dreapta) center/contain no-repeat;
  transition: transform .15s ease; display: inline-block;
}
.al-cat[open] > summary::before { transform: rotate(90deg); }
.al-cat > summary:hover { background: var(--panel-2); }
.al-count { font-family: var(--mono, ui-monospace, monospace); font-size: 11px;
  color: var(--txt-faint); font-weight: 500; }
.al-chans { margin-left: auto; display: flex; gap: 5px; flex-wrap: wrap; }
.al-cc { font-family: var(--mono, ui-monospace, monospace); font-size: 10px; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 20px; border: 1px solid var(--line); color: var(--txt-dim);
  white-space: nowrap; }
.al-cc.on { border-color: var(--ok); color: var(--ok); }

.al-scroll { overflow-x: auto; }              /* 9 coloane nu încap pe telefon: derulare proprie,
                                                 pagina NU se mișcă lateral */
.al-tab { border-collapse: collapse; width: 100%; font-size: 13px; min-width: 62rem; }
.al-tab thead th { font-size: 10px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--txt-faint); font-weight: 600; text-align: center; padding: 7px 6px;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  background: var(--panel-2); white-space: nowrap; }
.al-tab thead th.l, .al-tab tbody td.l { text-align: left; padding-left: 14px; }
.al-tab tbody td { padding: 7px 6px; border-bottom: 1px solid var(--line-soft);
  vertical-align: middle; text-align: center; }
.al-tab tbody tr:hover { background: var(--panel-2); }
.al-tab tbody tr:last-child td { border-bottom: none; }
.al-tip { font-weight: 560; }
.al-sub { display: block; color: var(--txt-faint); font-size: 11.5px; font-weight: 400;
  margin-top: 2px; max-width: 42rem; }

.al-ch { display: inline-flex; align-items: center; gap: 5px; }
.al-ch input { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; margin: 0; }
.al-t { border: 1px solid var(--line); background: transparent; color: var(--txt-faint);
  border-radius: 5px; font-family: var(--mono, ui-monospace, monospace); font-size: 10px;
  padding: 2px 5px; cursor: pointer; line-height: 1.4; }
.al-t:hover { border-color: var(--accent); color: var(--accent); }
.al-t.edited { border-color: var(--ok); color: var(--ok); }   /* verde = text propriu */
.al-ch input:disabled { opacity: .35; cursor: not-allowed; }
.al-ch input:disabled + .al-t { opacity: .3; pointer-events: none; }

.al-sev { background: var(--panel-2); color: var(--txt); border: 1px solid var(--line);
  border-radius: 6px; padding: 3px 6px; font-size: 12px; font-family: inherit; }
.al-prag { font-family: var(--mono, ui-monospace, monospace); font-size: 11.5px;
  color: var(--txt-dim); white-space: nowrap; }
.al-prag .na { color: var(--txt-faint); opacity: .55; }
.al-kebab { border: 1px solid transparent; background: transparent; color: var(--txt-faint);
  font-size: 17px; line-height: 1; border-radius: 6px; padding: 2px 6px; cursor: pointer; }
.al-kebab:hover { border-color: var(--line); color: var(--txt); background: var(--panel-2); }
.al-pill { font-family: var(--mono, ui-monospace, monospace); font-size: 10px; padding: 2px 7px;
  border-radius: 20px; border: 1px solid var(--line-soft); color: var(--txt-faint);
  white-space: nowrap; }
.al-pill.lock { border-color: var(--maint, var(--txt-faint)); color: var(--maint, var(--txt-faint)); }

.al-grp td { background: var(--panel-2); }
.al-grp-sum { display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  font-weight: 600; user-select: none; background: none; border: 0; color: inherit;
  font-family: inherit; font-size: inherit; padding: 0; }
.al-grp-sum::before {
  content: ""; width: 14px; height: 14px; background: currentColor;
  color: var(--txt-faint); -webkit-mask: var(--ico-dreapta) center/contain no-repeat;
  mask: var(--ico-dreapta) center/contain no-repeat;
  transition: transform .15s ease; display: inline-block;
}
.al-grp-sum.open::before { transform: rotate(90deg); }
.al-var { display: none; }
.al-var.show { display: table-row; }
.al-var td.l { padding-left: 36px; }
.al-var td.l::before { content: "└"; color: var(--txt-faint); margin-right: 6px;
  font-family: var(--mono, ui-monospace, monospace); }

.al-save { display: flex; align-items: center; gap: 12px; margin: 14px 0 26px; }

/* ---- ferestrele (text / praguri) ---- */
.al-ov { position: fixed; inset: 0; background: rgba(0,0,0,.55); display: none;
  align-items: center; justify-content: center; padding: 16px; z-index: 50; }
.al-ov.show { display: flex; }
.al-modal { background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  width: min(46rem, 100%); max-height: 88vh; overflow: auto; padding: 18px 20px;
  display: block; }
.al-modal h3 { margin: 0 0 3px; font-size: 17px; }
.al-msub { color: var(--txt-dim); font-size: 13px; margin: 0 0 14px; }
.al-modal textarea { width: 100%; min-height: 5.5rem; background: var(--panel-2);
  color: var(--txt); border: 1px solid var(--line); border-radius: 8px; padding: 10px 11px;
  font-family: var(--mono, ui-monospace, monospace); font-size: 13px; line-height: 1.55;
  resize: vertical; }
.al-vars { display: flex; flex-wrap: wrap; gap: 5px; margin: 10px 0 4px; }
.al-vr { font-family: var(--mono, ui-monospace, monospace); font-size: 11px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 5px;
  padding: 2px 6px; cursor: pointer; color: var(--txt-dim); }
.al-vr:hover { border-color: var(--accent); color: var(--accent); }
.al-prev { margin-top: 11px; background: var(--bg); border: 1px solid var(--line-soft);
  border-radius: 8px; padding: 10px 12px; font-size: 13px; color: var(--txt-dim); }
.al-prev b { color: var(--txt-faint); font-family: var(--mono, ui-monospace, monospace);
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase; display: block;
  margin-bottom: 5px; }
.al-inherit { font-size: 12px; color: var(--txt-faint); margin-top: 6px; }
.al-mrow { display: flex; gap: 8px; margin-top: 16px; align-items: center; }
.al-mrow .sp { margin-left: auto; }
/* `.al-mrow .primary` — sters pe 08 sep: cele doua butoane au trecut pe
   `.btn-salveaza`, iar clasa `.primary` nu mai are niciun consumator. */
.al-fld { display: flex; align-items: center; gap: 10px; margin: 11px 0; flex-wrap: wrap; }
.al-fld label { font-size: 13px; min-width: 15rem; }
.al-fld input[type=number] { width: 6rem; background: var(--panel-2); color: var(--txt);
  border: 1px solid var(--line); border-radius: 6px; padding: 5px 7px;
  font-family: var(--mono, ui-monospace, monospace); }

@media (max-width: 620px) {
  .al-fld label { min-width: 0; }
  .al-t, .al-kebab { min-height: 34px; min-width: 34px; }
  .al-cat > summary { padding: 14px; }
}

/* ---- alerta de SAT în lista de probleme: meniu ⋮ + rândul de mentenanță (2026-08-08) ----
   Satele n-au rând de tabel (supravegherea e „din umbră"), deci meniul de acțiuni trăiește
   pe alertă. ⚠ `.alerts` NU are `overflow:hidden` — dacă i se adaugă vreodată, meniul
   ultimului rând se taie (precedent: `.panel:has(.row-menu)`). */
.alerts:has(.row-menu) { overflow: visible; }
.al-menu { margin-left: auto; flex: 0 0 auto; }
.al-menu > summary { cursor: pointer; list-style: none; color: var(--txt-faint);
  padding: 0 6px; border-radius: 6px; }
.al-menu > summary::-webkit-details-marker { display: none; }
.al-menu > summary:hover { color: var(--txt); background: var(--panel-2); }
.alert.al-maint { border-left-color: var(--maint); }   /* `.dot-maint` există deja mai sus */

/* ---- anunț de expirare a accesului (2026-08-11) ----
   Popup peste conținut, o dată per sesiune. ⚠ Selectorii sunt proprii și specifici; regula de
   cascadă a proiectului: în zone cu butoane deja stilizate, clasa simplă pierde. */
.acces-pop { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center;
  justify-content: center; padding: 20px; background: rgba(0,0,0,.55); backdrop-filter: blur(2px); }
.acces-pop[hidden] { display: none; }
.acces-pop-box { max-width: 430px; width: 100%; background: var(--panel); color: var(--txt);
  border: 1px solid var(--line); border-radius: 14px; padding: 26px 24px 22px; text-align: center;
  box-shadow: 0 18px 50px rgba(0,0,0,.45); }
.acces-pop-icon { font-size: 34px; line-height: 1; margin-bottom: 10px; }
.acces-pop-box h2 { margin: 0 0 10px; font-size: 19px; font-weight: 650; text-wrap: balance; }
.acces-pop-box p { margin: 0 0 10px; font-size: 14px; line-height: 1.55; color: var(--txt-dim); }
.acces-pop-sub { font-size: 13px !important; color: var(--txt-faint) !important; }
.acces-pop-box .btn-primary { margin-top: 6px; min-height: 44px; padding: 0 26px; }

/* ── Sortarea tabelului de camere (2026-08-12) ────────────────────────────────
   ⚠ Prefixate cu `#cam-table`, nu clase simple: într-un tabel cu `thead th`
   deja stilat, o clasă simplă (0,1,0) e bătută de selectorul de element cu
   clasă. Aceeași familie cu coliziunile din `.form-card`. */
#cam-table th.sortable { cursor: pointer; user-select: none; }
#cam-table th.sortable:hover { color: var(--txt); }
#cam-table th.sortable::after { content: "↕"; opacity: .35; margin-left: 6px; font-size: .9em; }
#cam-table th.sortable[aria-sort="ascending"]::after  { content: "↑"; opacity: 1; color: var(--accent); }
#cam-table th.sortable[aria-sort="descending"]::after { content: "↓"; opacity: 1; color: var(--accent); }
#cam-table th.sortable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ── Discurile, sub NVR-ul lor (2026-08-12) ───────────────────────────────────
   Au înlocuit tabelul separat de la subsolul paginii: te uiți la un NVR și vrei
   discurile LUI. Pastila rămâne buton de filtrare; săgeata deschide discurile. */
.nvr-strip { align-items: flex-start; }   /* un card deschis nu mai întinde vecinii */
/* ⚠ UN SINGUR RÂND pentru discuri + motiv (Bogdan, 2026-08-12). Prima variantă punea pastila
   de discuri pe rândul ei, iar motivul („2 camere offline") era împins mai jos — cardurile din
   bandă ieșeau inegale: unul cu un rând, restul cu două. Grila `auto 1fr` le ține pe același
   rând, discurile la stânga, alerta la dreapta. Când discurile se DESCHID, `<details>` trece
   pe toată lățimea (`1 / -1`), ca lista să aibă loc, iar motivul coboară — schimbare cerută
   explicit de utilizator, nu accidentală. */
.nvr-card-foot { display: grid; grid-template-columns: auto 1fr; align-items: start;
  column-gap: 10px; margin-top: 4px; }
.nvr-card-foot > .nvr-card-reason { grid-column: 2; justify-self: end; text-align: right; }
.nvr-card .nvr-discuri { grid-column: 1; }
.nvr-card .nvr-discuri[open] { grid-column: 1 / -1; }
.nvr-card .nvr-discuri > summary { list-style: none; cursor: pointer; display: inline-flex;
  align-items: center; gap: 5px; }
.nvr-card .nvr-discuri > summary::-webkit-details-marker { display: none; }
.nvr-card .nvr-discuri > summary::before { content: ""; width: 14px; height: 14px; flex: none; background: currentColor; color: var(--txt-faint); -webkit-mask: var(--ico-dreapta) center/contain no-repeat; mask: var(--ico-dreapta) center/contain no-repeat; transition: transform .15s ease; display: inline-block; }
.nvr-card .nvr-discuri[open] > summary::before { transform: rotate(90deg); }
.nvr-card .nvr-discuri > summary:focus-visible { outline: 2px solid var(--accent);
  outline-offset: 2px; border-radius: 4px; }
.nvr-disk-list { margin-top: 6px; border-top: 1px solid var(--line-sect); padding-top: 6px;
  display: flex; flex-direction: column; gap: 3px; }
/* ⚠ Prefixat cu `.nvr-card`: `.sev-*` există deja pentru RÂNDURI de tabel, iar aici sunt
   div-uri. Fără prefix, regulile de tabel ar fi bătut fondul cardului. */
.nvr-card .nvr-disk { display: flex; align-items: center; gap: 8px; font-size: .82rem;
  padding: 2px 0; position: relative; }
.nvr-card .nvr-disk-vendor { flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; color: var(--txt-dim); }
.nvr-card .nvr-disk-health { white-space: nowrap; }
.nvr-card .nvr-disk-menu { position: static; }
@media (prefers-reduced-motion: reduce) {
  .nvr-card .nvr-discuri > summary::before { transition: none; }
}
.kpi-hint { font-size: .72rem; color: var(--txt-faint); }

/* ── Detaliile stâlpului DIGI, în coloana ONT (2026-08-12) ────────────────────
   ⚠ `position: fixed`, NU `absolute`: `.cam-panel` / containerul tabelului
   decupează (overflow), iar tooltipul ar apărea tăiat — exact ce a semnalat
   Bogdan pe artefact. Coordonatele le pune JS-ul, cu întoarcere lângă marginile
   ecranului. Aceeași familie cu meniul ⋮ tăiat de `.panel{overflow:hidden}`. */
.ont-digi { position: relative; display: inline-block; cursor: help; }
.ont-digi > .tick { border-bottom: 1px dotted currentColor; }
/* ⚠ Aici era `.ont-digi.gw-mut > .tick`, care înroșea eticheta DIGI când gateway-ul de pe
   stâlp nu răspundea. ȘTEARSĂ pe 2026-09-01 odată cu senzorul: adresa pinguită era a
   routerului DIGI, nu a ONT-ului, deci starea aia nu însemna nimic. Roșul e o afirmație —
   iar noi nu aveam ce afirma. */

.ont-digi:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px;
  border-radius: 3px; }
.ont-tip { position: fixed; z-index: 50; width: 270px; max-width: calc(100vw - 24px);
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: var(--shadow); padding: 10px 12px; display: none;
  white-space: normal; font-size: .8rem; line-height: 1.5; text-align: left; }
.ont-digi:hover .ont-tip, .ont-digi:focus .ont-tip, .ont-digi:focus-within .ont-tip {
  display: block; }
.ont-tip-h { display: block; font-weight: 650; margin-bottom: 6px; }
.ont-tip-r { display: flex; justify-content: space-between; gap: 12px; }
.ont-tip-r > span:first-child { color: var(--txt-faint); font-size: .76rem; }
.ont-tip-n { display: block; margin-top: 8px; padding-top: 6px;
  border-top: 1px dashed var(--line); color: var(--txt-faint); font-size: .74rem; }

/* ---- Import poziții din KMZ (felia 4, 2026-09-02) ---------------------------------------
   ⚠ TOATE regulile sunt prefixate cu `.kmz-import`, care stă pe cardul `.ob-step`. Înăuntrul
   lui există deja reguli element+clasă — `.ob-step button:disabled`, `.ob-grid input` — care
   au specificitate (0,2,1) și ar bate o clasă simplă (0,1,0). Precedentul e regula de 44px,
   moartă săptămâni din exact cauza asta.
   ⚠ Zero atribute `style=` în șablon: bara primește `--kmz-pct` din JS, prin CSSOM. */
.kmz-import .kmz-err { margin: 0 0 14px; }
.kmz-import .kmz-sinteza { margin: 4px 0 8px; font-size: 1.05rem; font-weight: 600; }
.kmz-import .kmz-sinteza.is-blocat { color: var(--down); }
.kmz-import .kmz-dif { margin: 0 0 10px; color: var(--muted); font-size: .92rem; }
.kmz-import .kmz-avertisment { margin: 0 0 10px; color: var(--warn); }

.kmz-import .kmz-bar { height: 10px; border-radius: 5px; background: var(--panel-2);
                       overflow: hidden; margin: 0 0 12px; --kmz-pct: 0%; }
.kmz-import .kmz-bar > i { display: block; height: 100%; width: var(--kmz-pct);
                           background: var(--ok); transition: width .25s ease; }
.kmz-import .kmz-bar.is-blocat > i { background: var(--down); }

.kmz-import .kmz-fld { margin-top: 8px; }
.kmz-import .kmz-fld-list { display: flex; flex-direction: column; gap: 4px; margin: 8px 0; }
.kmz-import .kmz-fld-list label { display: block; font-weight: 400; cursor: pointer; }
.kmz-import .kmz-fld-list .lvl-1 { padding-left: 22px; }
.kmz-import .kmz-fld-list .lvl-2 { padding-left: 44px; }
.kmz-import .kmz-fld-list .lvl-3 { padding-left: 66px; }

.kmz-import .kmz-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
                        gap: 16px; align-items: start; }
/* ⚠ Înălțime EXPLICITĂ: o hartă într-un container fără înălțime iese 0px. */
.kmz-import .kmz-preview { height: 380px; border-radius: 8px; overflow: hidden;
                           border: 1px solid var(--line); }
.kmz-import .kmz-grupuri { display: flex; flex-direction: column; gap: 6px; }
.kmz-import .kmz-grup > summary { cursor: pointer; padding: 6px 8px; border-radius: 6px;
                                  background: var(--panel-2); font-weight: 600; }
.kmz-import .kmz-grup[data-gol] > summary { opacity: .55; font-weight: 400; }
.kmz-import .kmz-lista { max-height: 240px; overflow-y: auto; margin: 6px 0 0;
                         padding-left: 20px; font-size: .9rem; color: var(--muted); }
.kmz-import .kmz-lista li { margin-bottom: 3px; }
.kmz-import .kmz-gata p { margin: 4px 0; }

/* Markere de previzualizare — elemente HTML proprii (`ol.Overlay`), nu icoane din bibliotecă.
   ⚠ Motivul e vechi și rămâne valabil: o bibliotecă de hartă vendorizată parțial cere imagini
   care nu există (Leaflet cerea `marker-icon.png`, 404 din iulie). Ce desenăm noi nu se strică. */
.kmz-import .kmz-pin { display: block; width: 12px; height: 12px; border-radius: 50%;
                       border: 2px solid #0b0f17; background: var(--ok); }
.kmz-import .kmz-pin.is-nou { background: var(--ok); }
.kmz-import .kmz-pin.is-mutat { background: var(--warn); }
.kmz-import .kmz-pin.is-la_fel { background: var(--accent, #5aa9e6); }

/* Controalele OpenLayers ale previzualizării — aceeași grafică cu restul portalului.
   ⚠ Prefixate cu `.kmz-import`, regula casei: o clasă simplă e bătută de orice selector cu
   element, iar `ol.css` are destule. */
.kmz-import .ol-control { background: none; padding: 0; }
.kmz-import .ol-control button {
  background: var(--panel); color: var(--txt); border: 1px solid var(--line);
  border-radius: 6px; margin: 0; width: 28px; height: 28px; font: 600 15px/1 inherit;
  cursor: pointer;
}
.kmz-import .ol-control button:hover { background: var(--panel-2); }
.kmz-import .ol-zoom { top: 8px; left: 8px; display: grid; gap: 3px; }
.kmz-import .ol-attribution {
  left: 0; right: auto; bottom: 0; background: var(--panel); border-radius: 0 6px 0 0;
  padding: 1px 6px; max-width: 100%;
}
.kmz-import .ol-attribution ul { font-size: 10px; text-shadow: none; color: var(--muted); }
.kmz-import .ol-attribution button { display: none; }

@media (max-width: 720px) {
  .kmz-import .kmz-cols { grid-template-columns: 1fr; }
}

/* Lista de acoperire din „Setări hartă" */
.cov-list { width: 100%; border-collapse: collapse; margin: 8px 0 18px; }
.cov-list th { text-align: left; font-size: .78rem; text-transform: uppercase;
               letter-spacing: .04em; color: var(--muted); padding: 4px 8px; }
.cov-list td { padding: 6px 8px; border-top: 1px solid var(--line); }
.cov-list .cov-bar { display: inline-block; width: 90px; height: 8px; border-radius: 4px;
                     background: var(--panel-2); overflow: hidden; vertical-align: middle;
                     --kmz-pct: 0%; }
.cov-list .cov-bar > i { display: block; height: 100%; width: var(--kmz-pct);
                         background: var(--ok); }
.cov-list tr.is-gol td { font-weight: 600; }

/* ---- Instalare pe telefon (PWA) — zonele de siguranță, 2026-09-04 ----------------------
   Când portalul rulează ca aplicație (fără chenarul browserului), conținutul ajunge SUB
   ceasul de sus și SUB bara de gesturi de jos. `env(safe-area-inset-*)` are valori reale doar
   dacă `<meta viewport>` are `viewport-fit=cover` — altfel e 0 și regulile de aici n-au niciun
   efect, TĂCUT. Cele două se schimbă împreună.
   ⚠ Pe desktop și în browser normal toate valorile sunt 0, deci regulile nu schimbă nimic —
   de-aia nu sunt puse într-un `@media`. */
.topbar { padding-top: env(safe-area-inset-top);
  height: calc(var(--topbar-h) + env(safe-area-inset-top)); }
.sidebar { padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom); }
/* ⚠ `--botnav-h` e SURSĂ UNICĂ: o folosesc și bara de jos (înălțime), și rezervarea de spațiu
   de dedesubt. Erau două numere scrise separat, iar cel care rezerva locul (`.wrap`, 72px) era
   BĂTUT TĂCUT de regula asta: `.app-main > main` are specificitatea (0,1,2), `.wrap` are
   (0,1,0). Rezultatul: bara acoperea ultimul card al listei — 3 elemente clicabile de neatins,
   fix cele la care ajungi derulând. Aceeași familie cu `body textarea` de ieri. */
.app-main > .content, .app-main > main {
  padding-bottom: calc(16px + var(--botnav-h, 0px) + var(--safe-b, 0px));
}
/* Marginile laterale contează în peisaj, unde decupajul intră din stânga sau din dreapta. */
.app-main { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); }

/* În modul aplicație nu există bara browserului, deci nici „trage ca să reîmprospătezi" —
   dar rămâne bounce-ul de la capătul listei, care arată ca un ecran care se dezlipește. */
@media (display-mode: standalone) {
  body { overscroll-behavior-y: none; }
}

/* ---- Pagina comunei pe telefon: tabelul de camere devine RÂNDURI-CARD (2026-09-04) -------
   Tabelul are ZECE coloane. Pe 390px nu încap nici pe departe, iar până acum nu exista nicio
   regulă pentru el — se derula orizontal, adică exact ce nu faci cu o mână, afară.
   Sub 720px rândul devine un card cu patru informații: starea (punctul), identul, IP-ul și
   NVR-ul, plus meniul de acțiuni. Model / Rec / Motion / ONT rămân pe desktop — nu se pierd,
   dar nu concurează cu ce se citește dintr-o privire pe teren.
   ⚠ Se aplică DOAR pe `tr.camrow`: rândul de „niciun rezultat" are o singură celulă și ar fi
   fost stâlcit de grilă.
   ⚠ Mărimile vin din tokenii de DENSITATE, deci rândul crește singur în modul teren. */
@media (max-width: 720px) {
  .cam-panel #cam-table, .cam-panel #cam-table tbody { display: block; }
  .cam-panel #cam-table thead, .cam-panel #cam-table colgroup { display: none; }
  .cam-panel #cam-table tr.camrow {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    align-items: center;
    gap: 1px 10px;
    padding: 9px 14px;
    margin-bottom: 12px;
    min-height: 44px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--panel);
  }
  .cam-panel #cam-table tr.camrow > td { display: block; border: 0; padding: 0; }
  /* punctul de stare ține toată înălțimea, ca bara de severitate de pe cardul de comună */
  .cam-panel #cam-table tr.camrow > td:nth-child(1) { grid-column: 1; grid-row: 1 / span 2; }
  .cam-panel #cam-table tr.camrow > td:nth-child(3) { grid-column: 2; grid-row: 1;
    font-size: 16px; font-weight: 700; }
  .cam-panel #cam-table tr.camrow > td:nth-child(9) { grid-column: 3; grid-row: 1;
    font-size: 12px; white-space: nowrap; }
  .cam-panel #cam-table tr.camrow > td:nth-child(4) { grid-column: 2; grid-row: 2;
    font-size: 12px; }
  .cam-panel #cam-table tr.camrow > td:nth-child(2) { grid-column: 3; grid-row: 2;
    font-size: 12px; white-space: nowrap; }
  .cam-panel #cam-table tr.camrow > td:nth-child(5),
  .cam-panel #cam-table tr.camrow > td:nth-child(6),
  .cam-panel #cam-table tr.camrow > td:nth-child(7),
  .cam-panel #cam-table tr.camrow > td:nth-child(8) { display: none; }
  .cam-panel #cam-table tr.camrow > td.row-actions { grid-column: 4; grid-row: 1 / span 2; }
  /* Rândul de „niciun rezultat" rămâne un bloc simplu, centrat. */
  .cam-panel #cam-table tr:not(.camrow) > td { display: block; text-align: center;
    padding: 18px 8px; }
}

/* ---- Navigație de jos, DOAR pe telefon (2026-09-04) --------------------------------------
   Bara laterală de 246px iese peste conținut pe mobil (la ☰), deci lista de comune rămâne
   accesibilă — dar cele patru destinații principale nu mai cer un gest ca să apară.
   Pragul e 820px, ACELAȘI cu cel la care bara laterală devine off-canvas: două praguri
   diferite ar lăsa o fâșie de lățimi cu ambele navigații, sau cu niciuna. */
:root { --botnav-h: 0px; --safe-b: 0px; }
.botnav { display: none; }

@media (max-width: 820px) {
  /* 50 conținut + 1 bordură. ⚠ `--safe-b` e zona sigură de jos, TRIMISĂ: iPhone-ul raportează
     34px, dar indicatorul de acasă are ~5px și stă centrat în ea — 34px de gol sub butoane e
     ecran pierdut degeaba (semnalat de Bogdan: bara ieșea 91px). Cu −14px rămâne degajare
     amplă față de indicator, iar bara scade la ~71px.
     ⚠ Expresia stă ÎNTR-UN SINGUR LOC: o folosesc și bara (ca spațiu interior), și rezerva de
     sub conținut. Scrisă de două ori, ar fi exact defectul de la `--topbar-h`, unde a patra
     copie a lui „58" a lăsat o fâșie prin care se vedea derularea. */
  :root { --botnav-h: 51px; --safe-b: max(6px, calc(env(safe-area-inset-bottom) - 14px)); }
  .botnav {
    display: flex;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
    background: var(--bg-2);
    border-top: 1px solid var(--line);
    padding-bottom: var(--safe-b);   /* degajare față de indicatorul de acasă */
  }
  .botnav-i {
    flex: 1; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    min-height: 50px; padding: 5px 2px 4px;
    color: var(--txt-faint); text-decoration: none;
    font-size: 10.5px; font-weight: 500; letter-spacing: .2px;
  }
  .botnav-i svg { width: 22px; height: 22px; flex: none; }
  .botnav-i.active { color: var(--txt); font-weight: 600; }
  .botnav-i.active svg { color: var(--accent); }
  /* ⚠ Feedback LA DEGET, nu în capul paginii. Bara de încărcare există, dar are 3px și stă
     SUS — apeși jos și semnalul apare la 800px distanță, unde nu te uiți. De-aia butonul se
     aprinde singur, instant, iar pagina confirmă când ajunge.
     ⚠ `touch-action: manipulation` scoate așteptarea după al doilea tap (zoom), iar
     `-webkit-tap-highlight-color` scoate dreptunghiul gri al iOS-ului, ca să nu se bată cu
     starea noastră. */
  .botnav-i { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
  .botnav-i:active, .botnav-i.apasat { background: var(--panel); }
  .botnav-i.apasat { color: var(--txt); }
  .botnav-i.apasat svg { color: var(--accent); }

  /* Bara e ancorată de fereastră, deci trebuie să stea DEASUPRA voalului meniului lateral,
     altfel rămâne colorată sub el și pare activă când nu e. */
  .app.nav-open .botnav { z-index: 25; }
}

/* Pe ecran mic, antetul comunei se așază pe verticală: la 390px numele și verdictul se
   înghesuiau pe același rând.
   ⚠ Regulile de KPI care stăteau AICI au fost ȘTERSE (05 sep): declarau aceeași grilă ca
   blocul de mai jos, cu alte valori (3 coloane în loc de 6). Erau moarte — al doilea bloc
   vine mai târziu în fișier, deci câștiga la specificitate egală — dar o a doua declarație
   a aceluiași lucru e exact cum se strică ceva tăcut peste trei luni. Grila de KPI se
   declară ÎNTR-UN SINGUR LOC. */
@media (max-width: 620px) {
  .site-header { flex-direction: column; align-items: flex-start; gap: 8px; padding: 12px 16px; }
  .site-header-status { margin-left: 0; text-align: left; }
  .site-header h1 { font-size: 21px; }
}

/* ---- Pagina comunei pe telefon: ierarhie, nu înșiruire (2026-09-04) ----------------------
   Problema, în cuvintele lui Bogdan: „totul e împrăștiat și prea mare și vin toate peste tine".
   Corect — înainte de a ajunge la camere treceai prin PATRU blocuri pe toată lățimea (probleme,
   alerte, cardurile NVR, filtrele), fiecare la dimensiuni de desktop. Pe 390px asta e un turn.
   Nu se ascunde nimic: se strâng, ca ordinea de citire să fie starea → problemele → camerele. */
@media (max-width: 620px) {
  /* Ritm vertical mai strâns: mai puțin derulat până la ce cauți. */
  .section-h { font-size: 11.5px; margin: 14px 0 6px; }
  .alerts { gap: 5px; }
  .alert { padding: 9px 12px; gap: 9px; font-size: 13px; align-items: flex-start;
           flex-wrap: wrap; line-height: 1.35; }

  /* Cardurile NVR: erau `flex: 1 1 220px`, deci pe 390px fiecare ocupa un rând întreg — la
     patru NVR-uri, un perete înainte de camere. Două pe rând, compacte: rămân un instrument
     de triaj (click = filtrează tabelul), nu conținutul principal al paginii. */
  .nvr-strip { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
               gap: 8px; margin: 10px 0 6px; }
  .nvr-card { flex: none; max-width: none; padding: 8px 10px; border-radius: 10px; }
  .nvr-card .nvr-card-head, .nvr-card .nvr-card-count { padding-right: 40px; }
  .nvr-card-name { font-size: 13px; }
  .nvr-card-host { font-size: 12px; }
  /* ⚠ Meniul ⋮ rămâne la 40px (regula de atins), dar cardul e acum îngust — deci textul are
     nevoie de spațiu rezervat, altfel intră sub buton. */
  .nvr-card .nvr-menu > summary { width: 34px; height: 34px; }

  /* ⚠ Regulile de BARĂ DE FILTRE care stăteau aici au fost ȘTERSE (05 sep). Descriau un
     layout care nu mai există: un rând care derulează orizontal, cu căutarea dedesubt. E
     chiar layoutul din care venea defectul semnalat de Bogdan — câmpul de căutare ajungea
     la 995px, deci trebuia tras lateral ca să-l vezi. Blocul de mai jos îl înlocuiește cu o
     grilă. A doua oară azi când găsesc același lucru declarat în două locuri (vezi `.kpis`).
     Ce era util acolo — ținta de 44px — s-a mutat cu el. */

  /* KPI-urile sunt butoane de filtrare — trebuie să se simtă ca atare la atingere. */
  .kpi-f { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
  .kpi-f:active { border-color: var(--accent); }

  /* Zona de corelare e diagnostic, nu triaj: rămâne strânsă, cu un cap de secțiune clar. */
  .corr-zone > summary { min-height: 48px; display: flex; align-items: center; }
}

/* ---- Rândul de cameră pe telefon: reparațiile de după PRIMA privire reală (2026-09-04) ----
   Până azi deduceam cum arată. Cu o captură la 390px s-au văzut deodată patru lucruri, toate
   confirmate de Bogdan:
     · identul camerei era tăiat la „F." și IP-ul la „1…" — ilizibile;
     · bulina de severitate se ÎNTINDEA pe cele două rânduri ale grilei și devenea o BARĂ roșie
       verticală (bug al meu, din `grid-row: 1 / span 2`);
     · „istoric" și „hartă" ocupau peste jumătate din lățime;
     · cardul NVR portocaliu era mai înalt decât vecinii, fiindcă textul de motiv se rupea pe
       trei rânduri — de aia „sărea" grila. */
@media (max-width: 720px) {
  /* Bulina rămâne bulină: ocupă ambele rânduri, dar NU se întinde.
     ⚠ Și are nevoie de LOC: pulsul camerelor cu probleme e un halou care crește până la 8px
     în jurul unei buline de 9px — pe o coloană `auto` de 9px, haloul era retezat și bulina
     apărea ca un „D". Se vedea doar la stările care pulsează, adică exact la cele care contează.
     Coloana primește lățime fixă, cât încape haloul întreg. */
  .cam-panel #cam-table tr.camrow { grid-template-columns: 26px minmax(0, 1fr) auto auto; }
  .cam-panel #cam-table tr.camrow > td:nth-child(1) { align-self: center; text-align: center; }
  .cam-panel #cam-table tr.camrow > td:nth-child(1) .dot { width: 9px; height: 9px; }

  /* „istoric" și „hartă" trec în meniul ⋮ — pe rând rămâne doar el. Ce câștigăm e exact ce
     lipsea: loc pentru numele camerei și pentru stare. */
  /* ⚠ COPIL DIRECT al `.row-actions`, nu descendent. Scrisă cu descendent, regula prindea și
     „Istoric" din MENIUL ⋮ — în clipa în care i-am dat clasa `btn-hist` (ca să deschidă
     fereastra în loc să navigheze), a devenit invizibil pe telefon. Cele două aveau deja
     domenii diferite în aceeași regulă: `.btn-map` era scopat cu `>`, `.btn-hist` nu.
     Găsit de auditul funcțional, la o oră după ce l-am introdus. */
  .cam-panel #cam-table tr.camrow > td.row-actions > .btn-hist,
  .cam-panel #cam-table tr.camrow > td.row-actions > .btn-map { display: none; }
  .cam-panel #cam-table tr.camrow > td:nth-child(3) { white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis; }
  .cam-panel #cam-table tr.camrow > td:nth-child(4) { white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis; }
  .cam-panel #cam-table tr.camrow .row-menu > summary { width: 40px; height: 40px; }

  /* Cardul NVR: fără IP (nu deschizi interfața aparatului de pe telefon, din teren), fără
     motivul care rupea înălțimea, iar lista de discuri rămâne doar contorul. Culoarea plus
     „29/30" spun deja ce trebuie, iar problemele sunt oricum listate mai sus. */
  .nvr-card .nvr-card-host,
  .nvr-card .nvr-card-reason,
  .nvr-card .nvr-discuri > summary::-webkit-details-marker { display: none; }
  .nvr-card .nvr-discuri[open] > *:not(summary) { display: none; }
  .nvr-card .nvr-discuri > summary { pointer-events: none; list-style: none; }
  .nvr-card .nvr-card-head { padding-right: 38px; }
}

/* Intrările „doar pe telefon" din meniul ⋮ — pe desktop butoanele proprii sunt deja pe rând. */
.mob-doar { display: none; }
@media (max-width: 720px) { .row-menu-panel .mob-doar { display: block; } }

/* ============================================================================
   Controale de formular pe telefon: minimum 16px
   ⚠ Safari pe iPhone MĂREȘTE pagina la atingerea oricărui câmp cu text sub 16px, iar omul
   trebuie apoi să dea zoom înapoi manual. Erau 13 reguli sub prag (13–15px), pe autentificare,
   grupuri, email, securitate, notițe, overlay-uri, onboarding, infra și alerte — deci practic
   fiecare formular al portalului sărea la atingere.

   ⚠ `!important` e DELIBERAT, și e chiar folosirea lui corectă: asta nu e o preferință de
   stil, e o limită a dispozitivului, care trebuie să bată orice regulă de componentă. Fără el
   nu ține: `body textarea` are specificitate (0,0,2) și e bătut de `.al-modal textarea` (0,1,1),
   deci prima variantă a regulii era scrisă, corectă și MOARTĂ pe textarea — exact familia în
   care ținta de 44px a stat moartă săptămâni. O variantă pe specificitate ar fi cerut selectori
   umflați artificial (`:not([readonly])` pus doar ca să crească numărul), adică o minciună
   semantică pusă în locul unei intenții clare.
   Doar sub 620px: pe desktop nu există zoom automat, iar densitatea mică rămâne cum e.
   Păzit de `tests/test_campuri_mobil.py`.
   ============================================================================ */
@media (max-width: 620px) {
  input:not([type="checkbox"]):not([type="radio"]),
  select,
  textarea { font-size: 16px !important; }
}

/* ============================================================================
   FLOTA pe telefon (audit vizual 04 sep, aprobat de Bogdan)
   Ce s-a schimbat și DE CE — fiecare punct vine dintr-un defect măsurat, nu dintr-o preferință.
   ============================================================================ */
@media (max-width: 620px) {

  /* --- KPI: rândurile umplu lățimea -------------------------------------------------------
     Erau 5 casete pe o grilă `auto-fit` de 150px ⇒ 3 + 2, cu un gol cât o casetă pe rândul doi.
     Acum grila are 6 coloane, iar cele două familii ocupă câte un rând întreg:
       3 stări → totalurile 3+3, stările 2+2+2
       4 stări (când apare „Fără date") → totalurile 3+3, stările 3+3 pe două rânduri
     ⚠ `order` în loc de reordonare în șablon: desktopul rămâne exact cum era. */
  /* ⚠ REGULA DE BAZĂ E PE DOUĂ COLOANE, nu pe șase. Prima variantă punea 6 coloane pe ORICE
     `.kpis`, iar clasa e folosită și de pagina comunei — unde casetele n-au clasele semantice
     de mai jos, deci fiecare primea 1/6 din lățime: eticheta „HARD DISKURI ONLINE SUB NVR"
     se rupea pe ȘASE rânduri. Am stricat o pagină reparând alta.
     Grila de 6 coloane se aplică DOAR flotei, care își declară numărul de stări. */
  /* ⚠ Grila de 6 coloane se aplică DOAR unde șablonul își declară familiile (`kpis-Ntot`).
     Prima variantă o punea pe orice `.kpis` și a stâlcit pagina comunei — care folosește
     aceeași clasă — rupând eticheta „HARD DISK-URI ONLINE SUB NVR" pe șase rânduri.
     A doua variantă a greșit invers: spanuri de 3 într-o grilă de 2 coloane ⇒ fiecare casetă
     pe rândul ei. Coloanele și spanurile trebuie declarate ÎMPREUNĂ. */
  .kpis { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 1fr; gap: 8px; }
  .kpis-1tot, .kpis-2tot { grid-template-columns: repeat(6, 1fr); }
  /* `grid-auto-rows: 1fr` egalizează rândurile: altfel rândul stărilor e mai înalt, fiindcă
     „PROBLEME ACTIVE" se rupe pe două rânduri, și cele două benzi arată nealiniate. */
  .kpis-3stari, .kpis-4stari { grid-template-columns: repeat(6, 1fr); }
  .kpi { padding: 9px 8px; }
  /* Grilă de 6 coloane, cu fiecare familie umplând rânduri ÎNTREGI. Numărul de casete vine
     din șablon (CSS-ul nu poate număra elemente după clasă), deci aceleași reguli servesc și
     flota (2 totaluri, 3-4 stări), și pagina comunei (1-2 totaluri, 2-3 stări). */
  .kpi-total { order: 1; }
  .kpi-stare { order: 2; }
  .kpis-1tot .kpi-total { grid-column: span 6; }
  .kpis-2tot .kpi-total { grid-column: span 3; }
  .kpis-2stari .kpi-stare { grid-column: span 3; }
  .kpis-3stari .kpi-stare { grid-column: span 2; }
  .kpis-4stari .kpi-stare { grid-column: span 3; }
  .kpi-val { font-size: 23px; }
  .kpi-sub { font-size: 14px; }
  .kpi-lbl { font-size: 10px; letter-spacing: .4px; }

  /* --- Antetul de județ rămâne la vedere --------------------------------------------------
     Pe un ecran de telefon cu 10 comune, derulând pierzi în ce județ ești. Se lipește sub
     banda de sus (58px, `position: sticky`), cu z-index SUB ea. */
  .county-h {
    position: sticky; top: var(--topbar-h); z-index: 5;
    margin: 12px 0 8px; padding: 7px 2px;
    background: var(--bg);
  }

  /* --- Cardul comunei: mai compact --------------------------------------------------------
     Era 124px × 10 comune. Acum ~86px, fără să dispară nimic din ce ajută la decizie:
     numele, starea, câte camere merg și cum arată bara. */
  .grid { gap: 8px; }
  .site { padding: 10px 12px; }
  .site-name { font-size: 15px; }
  .site-head { gap: 8px; }
  .site-sub { margin-top: 3px; }
  .site-reason { font-size: 11.5px; }
  .site-body { margin: 5px 0 0; gap: 10px; }
  .metric-val { font-size: 17px; }
  .metric-sub { font-size: 13px; }
  .metric-lbl { font-size: 11.5px; margin-left: 4px; }

  /* Starea devine o PASTILĂ, nu text colorat: se prinde din ochi la derulare rapidă. */
  .site-status {
    font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
    padding: 3px 9px; border-radius: 20px; border: 1px solid currentColor; line-height: 1.35;
  }
  .site.st-ok    .site-status { background: var(--ok-bg); }
  .site.st-warn  .site-status { background: var(--warn-bg); }
  .site.st-down  .site-status { background: var(--down-bg); }
  .site.st-stale .site-status { background: var(--stale-bg); }

  /* Rândul de chip-uri — SCOS pe telefon (decizia Bogdan, 04 sep). Pe o comună funcțională,
     `16 discuri OK` + `UISP` repetau ce spun deja pastila verde și bara plină; iar când chiar
     e o problemă, ea se vede în pagina comunei, la locul ei. */
  .site-foot { display: none; }

  /* --- Ținte de atins: minimum 44px ------------------------------------------------------
     Măsurate înainte: ☰ 34×34 · contul 48×32 · comutatorul 65×30 · rândurile din meniul
     lateral 39–42px. Toate sub pragul la care degetul nimerește din prima. */
  .nav-toggle { min-width: 44px; min-height: 44px; }
  .acct { min-height: 44px; padding: 5px 10px; }
  .topbar-filter .switch { min-height: 44px; padding: 4px 0; }
  .sidebar-nav a { min-height: 44px; }
  .acct-item { min-height: 44px; }   /* rândurile din meniul contului erau 36px */
}

/* ============================================================================
   PAGINA COMUNEI pe telefon (audit vizual 05 sep, aprobat de Bogdan)
   Pagina avea 8 277px de derulat, din care 7 277 (88%) doar lista de camere.
   ============================================================================ */
@media (max-width: 620px) {

  /* --- Antetul comunei, mai strâns: ajungeai la prima cameră după 700px ----------------- */
  .site-header { padding: 11px 14px; }
  .site-header h1 { font-size: 20px; }
  .site-header-sub { font-size: 12px; }
  .site-status-label { font-size: 16px; }
  .site-header-reason { font-size: 12.5px; margin-top: 2px; }
  .crumbs { margin-bottom: 6px; }

  /* --- KPI-ul de discuri: SCOS (decizia Bogdan) ------------------------------------------
     Un disc defect apare oricum în „Probleme" (alerta `discuri.defect`, `web/alerts/eval.py`).
     Verificat în cod înainte de a-l scoate, nu presupus. */
  .kpi-disc { display: none; }

  /* --- Pastilele NVR: desfășurătorul de discuri, SCOS (decizia Bogdan) ------------------- */
  .nvr-card .nvr-discuri { display: none; }
  .nvr-strip { gap: 8px; }
  .nvr-card { padding: 9px 11px; }

  /* --- Bara de filtre: pe rânduri, nu pe orizontală --------------------------------------
     ⚠ Avea `overflow-x: auto`, deci câmpul de căutare ajungea la 995px — trebuia s-o tragi
     lateral ca să-l vezi. Acum se așază pe rânduri, și rămâne LIPITĂ la derulare: cu 97 de
     rânduri, filtrul e singurul mod rezonabil de a ajunge la o cameră anume. */
  /* ⚠ Bara NU mai e lipită (Bogdan, 05 sep): funcția e bună, dar nu pentru ea. A trecut pe
     antetul comunei, care spune UNDE ești — vezi mai jos. */
  .filterbar {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
    overflow: visible; padding: 0; margin: 0 0 8px;
  }
  /* CELE TREI LISTE SUNT SCOASE (decizia Bogdan, 05 sep): NVR-ul se alege apăsând cardul
     NVR de deasupra, starea apăsând pastila KPI — ambele se și evidențiază, deci filtrul
     activ se vede. Listele erau al treilea control pentru aceleași două lucruri, pe ecranul
     unde spațiul costă cel mai mult.
     ⚠ O listă REAPARE cât timp chiar filtrează (`.filt-activ`, pusă din JS). Fără asta,
     un filtru de MODEL venit dintr-un link făcut pe desktop ar rămâne aplicat fără nicio
     cale de a-l scoate — modelul n-are alt control în pagină. */
  .filterbar .filt-dd { display: none; }
  .filterbar .filt-dd.filt-activ { display: block; order: 1; grid-column: span 3;
    width: 100%; min-width: 0; }
  .filterbar .filt-q { order: 2; grid-column: span 2; width: 100%; min-width: 0; }
  /* Contorul e ASCUNS cât timp doar repetă totalul din antet („97 camere") — antetul spune
     deja „4 NVR-uri — 97 camere". Reapare când filtrează („24 din 97"), fiindcă atunci e
     singurul semn că lista nu e întreagă. Clasa vine din JS, ca la listele de filtru. */
  .filterbar .filt-q { min-height: 44px; }
  .filterbar .filt-count { display: none; }
  .filterbar .filt-count.filt-activ { display: block; order: 3; margin: 0; align-self: center;
    text-align: right; white-space: nowrap; }
  /* Cu contorul ascuns, căutarea ia lățimea întreagă. */
  .filterbar .filt-q { grid-column: 1 / -1; }
  .filterbar .filt-count.filt-activ ~ .filt-q,
  .filterbar:has(.filt-count.filt-activ) .filt-q { grid-column: span 2; }
  /* Opțiunile din liste erau la 30px — sub pragul la care degetul nimerește din prima. */
  .filterbar .sel-opt { min-height: 44px; }
  /* ⚠ Panoul listei se ancorează la DREAPTA: a doua listă e lipită de marginea dreaptă, iar
     un panou care crește spre dreapta iese din ecran (măsurat: 437px pe un ecran de 390).
     Aceeași greșeală ca la meniul ⋮ de pe ultima coloană. */
  .filterbar .filt-dd .sel-panel { right: 0; left: auto; min-width: 0;
    width: max-content; max-width: calc(100vw - 24px); }
  .filterbar .sel-dd > summary { min-height: 44px; display: flex; align-items: center; }

  /* --- Lista de camere: rânduri dese, nu carduri separate --------------------------------
     Fiecare rând avea card propriu (bordură + fundal + 12px între ele) = 75px pentru patru
     informații. Acum e o listă cu linii despărțitoare, într-un singur panou. */
  .cam-panel #cam-table tr.camrow {
    margin-bottom: 0;
    padding: 6px 12px;
    border: 0;
    border-bottom: 1px solid var(--line-soft);
    border-radius: 0;
    background: transparent;
    gap: 0 10px;
  }
  .cam-panel #cam-table tr.camrow:last-child { border-bottom: 0; }
  /* ⚠ LINIA VERTICALĂ ROȘIE, semnalată de Bogdan. `.sev-down td:first-child` desena dunga de
     gravitate pe PRIMA CELULĂ — corect pe desktop, unde celula începe chiar la marginea
     rândului. Pe telefon rândul e un card cu spațiu interior, iar dunga devenea o bară care
     plutește lângă bulină. Se mută pe RÂND, ca margine din stânga. */
  .cam-panel #cam-table tr.camrow > td:first-child { box-shadow: none; }
  .cam-panel #cam-table tr.sev-down  { box-shadow: inset 3px 0 0 var(--down); }
  .cam-panel #cam-table tr.sev-warn  { box-shadow: inset 3px 0 0 var(--warn); }
  .cam-panel #cam-table tr.sev-maint { box-shadow: inset 3px 0 0 var(--maint); }

  /* IP-ul iese din rând (a trecut în meniul ⋮): de pe telefon n-ai rută spre `192.168.5.x`,
     deci linkul nu putea funcționa oricum. Numele camerei ocupă acum rândul întreg. */
  .cam-panel #cam-table tr.camrow > td:nth-child(4) { display: none; }
  /* Rândul se reașază după ce au ieșit IP-ul și pastila „OK": numele și NVR-ul/canalul devin
     un BLOC la stânga, iar coloana din dreapta rămâne liberă pentru starea de problemă,
     centrată pe verticală. Înainte, cu pastila scoasă, rămâneau două goluri în diagonală —
     numele sus-stânga, canalul jos-dreapta, mijlocul gol. */
  .cam-panel #cam-table tr.camrow > td:nth-child(3) { grid-column: 2; grid-row: 1;
    font-size: 15px; line-height: 1.25; }
  .cam-panel #cam-table tr.camrow > td:nth-child(2) { grid-column: 2; grid-row: 2;
    font-size: 11.5px; line-height: 1.25; text-align: left; }
  .cam-panel #cam-table tr.camrow > td:nth-child(9) { grid-column: 3; grid-row: 1 / span 2;
    align-self: center; font-size: 11.5px; line-height: 1.25; }
  /* Pastila verde „OK" — SCOASĂ pe telefon (decizia Bogdan, 05 sep). La Bărcănești sunt 152
     de camere: cu pastilă pe fiecare rând, ochiul primea 150 de confirmări identice și 2
     semnale reale, la aceeași greutate vizuală. Starea rămâne în bulina din stânga, în
     filtrul „Toate stările", în KPI-uri și în „Probleme".
     ⚠ Se scoate DOAR `.ok-mark`. `.verdict` (Offline / Atenție / Mentenanță / NVR jos) rămâne
     neatins — el e chiar semnalul pe care vrem să cadă ochiul. */
  .cam-panel #cam-table tr.camrow .ok-mark { display: none; }


  /* --- Meniul ⋮ ieșea din ecran cu 13px --------------------------------------------------
     ⚠ Se ancorează la DREAPTA, nu la stânga: pe ultima coloană un panou care crește spre
     dreapta iese garantat afară. */
  .row-menu-panel { right: 0; left: auto; max-width: calc(100vw - 24px); }
  /* Rândurile din meniul ⋮ erau la 36px — sub pragul la care degetul nimerește din prima. */
  .row-menu-panel > a, .row-menu-panel > button { min-height: 44px; display: flex;
    align-items: center; }
  .nvr-card .row-menu > summary { min-height: 44px; min-width: 40px; display: flex;
    align-items: center; justify-content: center; }
  .cam-panel #cam-table tr.camrow > td.row-actions .row-menu > summary { min-height: 44px;
    min-width: 40px; display: flex; align-items: center; justify-content: center; }
}

/* ---- Antetul comunei pe telefon: mai strâns, fără verdictul scris (Bogdan, 05 sep) ------
   Cuvântul „Funcțional" / „Atenție" / „Incident" trecea pe rândul lui și înălța cardul, ca
   să repete ce spun deja bulina colorată și dunga din stânga. Rămâne MOTIVUL — „o cameră
   offline" — care e singura parte ce chiar adaugă ceva.
   ⚠ `display: contents` pe containerul verdictului, NU `display: none`: motivul trebuie să
   rămână vizibil, iar containerul gol (la comunele funcționale, care n-au motiv) nu trebuie
   să lase un gol de flex în urmă. Cu `none` ar fi dispărut și motivul; cu container păstrat,
   comunele fără motiv ar fi avut un rând gol.  */
@media (max-width: 620px) {
  /* ANTETUL RĂMÂNE LA VEDERE la derulare (Bogdan, 05 sep): la 97 de rânduri de cameră, cel
     mai ușor lucru de pierdut e în ce comună ești. Se lipește sub banda de sus (58px).
     ⚠ `z-index` SUB banda de sus (10) și sub meniurile ⋮ (40), dar peste conținut — altfel
     ar acoperi meniul unui rând deschis chiar sub el.
     ⚠ Umbra de deasupra (`0 -12px 0 var(--bg)`) acoperă fâșia dintre bandă și card: cardul
     are colțuri rotunjite și marjă, deci fără ea rândurile s-ar vedea trecând prin gol.
     Nu e `background` pe un părinte, fiindcă elementul lipit e chiar cardul. */
  .site-header { position: sticky; top: var(--topbar-h); z-index: 8;
    box-shadow: 0 -12px 0 var(--bg), 0 8px 14px rgba(0,0,0,.35);
    padding: 10px 14px; gap: 5px; }
  .site-header-main { gap: 11px; }
  .site-header h1 { font-size: 19px; line-height: 1.2; }
  .site-county { font-size: 15px; }
  .site-meta { font-size: 11.5px; margin-top: 1px; }
  .site-header-status { display: contents; }
  .site-status-label { display: none; }
  /* Motivul se ascunde — îl repetă pastilele KPI și secțiunea „Probleme". Rămâne DOAR când
     comuna n-are niciun rând de problemă, ca să nu existe stare roșie fără explicație. */
  .site-header-reason { display: none; }
  .site-header-reason.reason-solo { display: block; font-size: 12.5px; margin-top: 0; }
}

/* ============================================================================
   Meniul ⋮ — un singur stil pentru toate elementele (05 sep)
   ⚠ Măsurat înainte, în panoul unei camere: cinci elemente, TREI stiluri. „Istoric" era un
   `<a>` cu `padding: 0` și culoare deschisă, deci textul stătea lipit de marginea panoului,
   nealiniat cu restul; „Vezi pe hartă" moștenea aspectul de pe desktop — pastilă cu bordură,
   fundal propriu și alt corp de literă (12,5 vs 13); butoanele de mentenanță aveau al treilea.
   Regulile de mai jos sunt PREFIXATE cu `.row-menu-panel`: altfel `.row-actions .btn-map`
   (0,2,0) le-ar bate, exact coliziunea din [[coliziuni-de-cascada-css]].
   Se aplică la toate cele cinci meniuri din portal, nu doar la cel de cameră.
   ============================================================================ */
.row-menu-panel > a,
.row-menu-panel > button,
.row-menu-panel .btn-map,
.row-menu-panel .btn-hist {
  display: flex; align-items: center; width: 100%; box-sizing: border-box;
  /* Linie între FIECARE element, ca să se citească drept butoane separate (Bogdan, 05 sep).
     ⚠ Full-bleed: panoul n-are spațiu orizontal propriu, iar retragerea vine din elemente —
     altfel liniile s-ar opri înainte de margini și ar arăta ca niște crăpături. */
  /* ⚠ `margin: 0` explicit: butonul de hartă din meniu e ȘI în `.row-actions` (panoul e
     înăuntru), iar `.row-actions .btn-map { margin-left: 6px }` — pus pentru spațierea
     butoanelor de pe rând, la desktop — îl decala cu 6px față de restul elementelor din
     meniu. Un stil scris pentru un context se moștenește în cel imbricat. */
  margin: 0;
  /* ⚠⚠ `height: auto` EXPLICIT — a treia moștenire străină prinsă în același panou, după
     `margin-left` și `border`. `.row-actions .btn-hist, .row-actions .btn-map` declară
     `height: 30px` pentru butoanele de pe RÂNDUL tabelului; meniul ⋮ stă ÎNĂUNTRUL aceleiași
     celule, deci regula îl prinde. Specificitatea e EGALĂ (0,2,0), iar regula asta vine mai
     jos în fișier — dar nu declara `height`, deci n-avea ce anula: „Vezi pe hartă" și
     „Istoric" ieșeau 30px, restul 41px, în același meniu (semnalat de Bogdan, măsurat 10 sep).
     ⚠ Tiparul de reținut: la specificitate egală câștigi doar proprietățile pe care le
     DECLARI. O regulă „mai jos în fișier" nu neutralizează ce nu numește. */
  height: auto;
  /* ⚠ `border: 0` ÎNAINTE de `border-bottom`: îl scosesem când am adăugat liniile, iar
     „Vezi pe hartă" și-a recăpătat conturul de pastilă moștenit de pe desktop — un singur
     element cu chenar, între șase fără. */
  /* Retragerea verticală e cea CERUTĂ de Bogdan: „spațiul de la vezi hartă și istoric este
     ideal" — adică rândul strâns, nu cel de 41px. Ținta comună iese ~31px. */
  padding: 6px 13px; border: 0; border-bottom: 1px solid var(--line-soft);
  border-radius: 0; background: none;
  color: var(--txt-dim); font: inherit; font-size: 13.5px; font-weight: 500;
  text-align: left; text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: background .1s ease, color .1s ease;
}
.row-menu-panel > a:hover, .row-menu-panel > button:hover,
.row-menu-panel > a:active, .row-menu-panel > button:active {
  background: var(--panel-2); color: var(--txt);
}
/* Ultimul element n-are linie sub el — panoul se termină acolo. */
.row-menu-panel > *:last-child { border-bottom: 0; }
/* Capul meniului: PE CE acționezi. Nu e un element de atins, deci nu ia stilul de mai sus. */
/* Capul meniului: PE CE acționezi. Fundal propriu, ca să se distingă de rândurile apăsabile
   fără să mai fie nevoie de încă o linie. */
.row-menu-panel .rm-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 9px 13px; background: var(--bg-2);
  border-bottom: 1px solid var(--line);
}
.rm-head .rm-nume { font-size: 14px; font-weight: 700; color: var(--txt); letter-spacing: -.1px; }
.rm-head .rm-ip { font-size: 12px; color: var(--txt-faint); }
/* ⚠ Aici era o A DOUA regulă de despărțire, rămasă din prima variantă a meniului (când
   elementele n-aveau linii între ele). Adăuga `border-top`, care se aduna peste linia de jos
   a elementului precedent: 2px lipiți între „Istoric" și „Mentenanță branșament" — bordura
   dublată semnalată de Bogdan. Despărțirea de grup se declară ÎNTR-UN SINGUR LOC, mai jos.
   A treia oară în două zile când găsesc același lucru declarat în două locuri. */
.row-menu-panel .btn-maint-close { color: var(--down); }
.row-menu-panel .btn-maint-close:hover { background: var(--down-bg); color: var(--down); }

/* ⚠ Meniul ⋮ de pe cardul NVR ieșea 36px DIN ECRAN, pe stânga (măsurat: panoul la x −36).
   Panoul e ancorat la dreapta declanșatorului și are 220px minim — potrivit pentru meniul unei
   camere, dar cardurile NVR stau două pe rând, deci sunt de 177px: 220 la stânga dintr-un
   declanșator aflat la x=184 cad în afara ecranului.
   ⚠ Nici ancorarea pe stânga n-ar merge: ar rezolva coloana din stânga și ar strica-o pe cea
   din dreapta. Soluția e să iasă din logica „lângă buton": `position: static` pe `.row-menu`
   mută blocul de ancorare pe CARD, iar panoul se întinde între marginile lui. Cardul e deja
   `position: relative`, deci nu se schimbă nimic altundeva.
   ⚠ Și `white-space: normal`: la lățimea cardului, „Mentenanță branșament" trebuie să aibă
   voie să treacă pe două rânduri, altfel ar depăși din nou — de data asta pe dinăuntru. */
@media (max-width: 620px) {
  /* ⚠ `position: static` mută blocul de ancorare pe CARD — dar `.nvr-menu` era ea însăși
     `position: absolute; top: 6px; right: 6px`, adică EA plasa butonul în colț. Anulând-o,
     butonul ⋮ a sărit în colțul de sus-stânga, peste numele NVR-ului (prins pe captură).
     Plasarea se mută deci pe `<summary>`, care e chiar butonul: cardul rămâne blocul de
     ancorare pentru panou, iar butonul stă unde stătea. */
  /* ⚠ `height: 0` + `display: block`: cu `position: static`, `<details>` INTRĂ ÎN FLUX și,
     fiind `inline-block`, creează un rând de text gol în capul cardului — ~25px de nimic, pe
     care i-am adăugat chiar eu ancorând panoul de card. Butonul și panoul dinăuntru sunt
     amândouă poziționate absolut, deci elementul n-are nevoie de nicio înălțime proprie. */
  .nvr-card .nvr-menu { position: static; display: block; height: 0; }
  /* ⚠ A DOUA plasă, independentă de prima: cardul cu meniul deschis urcă el însuși peste
     vecini. Chiar dacă mâine ceva creează din nou un context de stivuire pe card (o animație,
     un filtru, un `will-change`), panoul nu mai poate fi acoperit — se ridică tot cardul.
     ⚠ 6, nu 9: rămâne SUB antetul lipit al comunei (8), ca să nu iasă peste el la derulare. */
  .nvr-card:has(.row-menu[open]) { z-index: 6; }
  .nvr-card .nvr-menu > summary { position: absolute; top: 5px; right: 5px; z-index: 2; }
  .nvr-card .row-menu-panel {
    top: calc(100% - 4px); left: 8px; right: 8px;
    min-width: 0; max-width: none; white-space: normal;
  }
  .nvr-card .row-menu-panel > button { white-space: normal; line-height: 1.3; }
}

/* ⚠ BORDURI DUBLATE, semnalat de Bogdan și confirmat prin măsurare: „Istoric" avea linie jos
   ȘI „Mentenanță branșament" avea linie sus — două linii lipite, 2px, între aceleași două
   rânduri. Când FIECARE element are deja o linie sub el, un `border-top` pus pe următorul se
   adună la ea; granița de grup trebuie să MODIFICE linia existentă, nu să adauge una nouă.
   Deci: linia de dinaintea grupului devine mai apăsată (`--line`), iar aerul vine din
   `padding-top` pe primul element al grupului.
   ⚠ Selectorul e pe `a + button`: ultimul element de navigare e „Istoric", un `<a>`. */
@media (max-width: 620px) {
  .row-menu-panel > a:has(+ .btn-maint),
  .row-menu-panel > a:has(+ .btn-maint-close) { border-bottom-color: var(--line); }
  .row-menu-panel > a + .btn-maint,
  .row-menu-panel > a + .btn-maint-close { padding-top: 16px; }
}

/* Fereastra de alegere a aplicației de navigație. Butoane mari: se apasă cu mănuși, în grabă.
   ⚠ Ordinea butoanelor e FIXĂ. Ultima folosită se marchează, dar nu se mută sus: pe teren se
   apasă din reflex, iar un buton care își schimbă locul e mai rău decât unul nemarcat. */
.nav-dialog { max-width: 380px; padding: 20px 20px 18px; }
.nav-titlu { margin: 0 0 15px; font-size: 17px; font-weight: 700; letter-spacing: -.2px; }
.nav-cam { color: var(--accent); }
.nav-optiuni { display: grid; gap: 10px; }
.nav-opt {
  position: relative; min-height: 54px; padding: 13px 16px; border-radius: 11px;
  border: 1px solid var(--line-strong); background: var(--panel-2); color: var(--txt);
  font: inherit; font-size: 16px; font-weight: 600; cursor: pointer;
  transition: border-color .12s ease, background .12s ease;
}
.nav-opt:hover, .nav-opt:active { border-color: var(--accent); background: var(--accent-bg); }
.nav-opt-ultima::after {
  content: "ultima folosită"; position: absolute; right: 14px; top: 50%;
  transform: translateY(-50%);
  font-size: 11px; font-weight: 600; color: var(--txt-faint); letter-spacing: .2px;
}

/* ---- Banda de sus, mai joasă pe telefon (Bogdan, 05 sep) --------------------------------
   Avea 58px pentru două butoane de 44 — 14px de aer pe verticală, pe ecranul unde spațiul e
   cel mai scump. Scade la 50, iar butoanele își păstrează ținta de atins de 44px.
   ⚠ Înălțimea e un TOKEN (`--topbar-h`), nu un număr repetat: de ea depind și antetul lipit
   al comunei, și antetele de județ de pe flotă. Erau trei „58" scrise separat — schimbarea
   unuia singur ar fi lăsat o fâșie prin care se vede conținutul derulând, sau o suprapunere.
   Exact clasa de defect din `--botnav-h`, unde bara acoperea ultimul rând. */
:root { --topbar-h: 58px; }
@media (max-width: 620px) {
  :root { --topbar-h: 50px; }
  .topbar { padding: 0 14px; gap: 10px; }
  .nav-toggle { width: 44px; height: 44px; }
  .acct { min-height: 44px; }
}

/* ---- Alerta și pastila NVR, mai compacte (Bogdan, 05 sep) --------------------------------
   ⚠ Alerta era un RÂND flex: bulină, nume, pin, mesaj, meniu. La 390px nu încap pe un rând,
   deci se rupea — iar pinul rămânea plutind între nume și mesaj, ca un element fără loc.
   Devine o GRILĂ de două rânduri: identificarea sus, explicația dedesubt, aliniată sub nume.
   ============================================================================ */
@media (max-width: 620px) {
  .alerts { gap: 6px; }
  /* Varianta E, aleasă de Bogdan (05 sep): bulina în dreptul numelui, mesajul dedesubt în
     aceeași coloană, iar butoanele PE ORIZONTALĂ în dreapta, centrate pe înălțime.
     ⚠ Mesajul are DOUĂ RÂNDURI REZERVATE (`min-height` + tăiere la 2 rânduri). Textele reale
     au 29–80 de caractere, iar 80 încap fix în două rânduri la lățimea asta — deci toate
     pastilele ies IDENTICE ca înălțime, fără să scurtăm vreun text. Fără rezervare, fiecare
     alertă avea altă înălțime, iar lista arăta dințată. */
  .alert {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center; gap: 0 8px; padding: 6px 9px; border-radius: 9px;
  }
  .alert > .dot      { grid-column: 1; grid-row: 1; }
  .alert > .al-subj  { grid-column: 2; grid-row: 1; min-width: 0; font-size: 13px;
    line-height: 1.3; }
  .alert > .al-msg   { grid-column: 1 / 3; grid-row: 2; font-size: 11.5px; line-height: 1.28;
    min-height: calc(2 * 1.28em);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; }
  /* Butoanele: un grup singur, centrat pe toată înălțimea pastilei. */
  .alert > .al-act   { grid-column: 3; grid-row: 1 / span 2; display: flex; gap: 6px;
    align-self: center; }
  .alert > .al-menu  { grid-column: 3; grid-row: 1 / span 2; align-self: center; }
  /* ⚠ Butoanele arată de 32px, dar ținta de atins e de 44: `::after` întinde zona sensibilă
     dincolo de chenar, fără să miște nimic vizual. Altfel, ca să respect pragul de 44px, ar fi
     trebuit să umflu pastila la loc — exact ce mi-a cerut Bogdan să evit.
     ⚠ Zonele NU se suprapun: 32 + 2×6 = 44, iar distanța dintre butoane e tot 6, deci se
     ating exact, nu se calcă. */
  .al-map .ico, .al-drive .ico { width: 16px; height: 16px; }
  /* ⚠ `min-height: 0` explicit: butonul de hartă poartă ȘI clasa `.btn-map`, care are
     `min-height: 34px` dintr-o regulă de mai sus — ieșea 32×34 lângă unul de 32×32, adică
     exact asimetria pe care felia asta o repară. Măsurat, nu presupus. */
  .al-map, .al-drive { position: relative; width: 32px; height: 32px; min-height: 0;
    padding: 0; margin: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: 13px; line-height: 1; border-radius: 8px; opacity: 1;
    border: 1px solid var(--line); background: var(--bg-2); color: var(--txt-dim);
    cursor: pointer; flex: none; }
  .al-map::after, .al-drive::after { content: ""; position: absolute; inset: -6px; }
  .al-map:active, .al-drive:active { border-color: var(--accent); color: var(--accent); }
  .alert > .al-act { gap: 6px; }
  .alert .row-menu > summary { width: 32px; height: 32px; }

  /* Pastila NVR: numele și contorul pe ACELAȘI rând. Erau două rânduri plus spațiu, adică
     ~76px pentru „NVR 1" și „24/24 online" — pe un ecran unde cele patru pastile mâncau
     jumătate din primul cadru. */
  .nvr-card { padding: 8px 10px; border-radius: 9px; }
  .nvr-card .nvr-card-head { padding-right: 34px; gap: 7px; }
  .nvr-card-name { font-size: 13.5px; }
  .nvr-card .nvr-card-count { margin-top: 2px; padding-right: 34px; font-size: 12px;
    font-style: normal; }
  .nvr-card .nvr-menu > summary { width: 30px; height: 30px; font-size: 15px;
    top: 5px; right: 5px; }
  .nvr-strip { gap: 7px; }
}

/* Meniul ⋮ deschis în SUS (clasa pusă din `meniu-pozitie.js`, când n-are loc în jos). */
.row-menu.sus > .row-menu-panel { top: auto; bottom: calc(100% + 5px); }

/* ============================================================================
   Istoricul unei camere pe telefon (05 sep) — grupat pe zile, o linie per eveniment
   ⚠ Măsurat: R31 la Cerna are 380 de evenimente în 30 de zile = 22 721px, adică 27 de ecrane.
   Data completă se repeta pe fiecare rând, iar coloana „Durată" — singura care chiar spune
   cât a fost jos — era strivită la LĂȚIME ZERO, deci invizibilă. Tăcut.
   ============================================================================ */
.ev-ore { display: none; }
.ev-zi { display: none; }
@media (max-width: 620px) {
  #hist-body .panel { overflow: visible; }
  #hist-body .dev, #hist-body .dev tbody { display: block; }
  #hist-body .dev thead { display: none; }

  /* Antetul de zi: se scrie O DATĂ, și rămâne la vedere cât derulezi prin ziua respectivă. */
  #hist-body .ev-zi { display: block; position: sticky; top: var(--topbar-h); z-index: 4; }
  #hist-body .ev-zi td { display: block; padding: 7px 12px 5px; border: 0;
    background: var(--bg-2); color: var(--txt-dim);
    font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px; }

  #hist-body .dev tbody tr:not(.ev-zi) {
    display: grid; grid-template-columns: auto 1fr auto;
    align-items: center; gap: 0 10px; padding: 7px 12px;
    border-bottom: 1px solid var(--line-soft);
  }
  #hist-body .dev tbody tr:not(.ev-zi) > td { display: block; border: 0; padding: 0; }
  /* ⚠ Aceeași dungă parazită ca pe pagina comunei: `.sev-down td:first-child` desenează
     gravitatea pe PRIMA CELULĂ — corect într-un tabel, unde celula începe la marginea
     rândului; aici rândul e o grilă cu spațiu interior, iar dunga devenea un ciot roșu lipit
     de bulină. Se mută pe RÂND. A doua oară același defect, în alt tabel — de-aia are test. */
  #hist-body .dev tbody tr > td:first-child { box-shadow: none; }
  #hist-body .dev tbody tr.sev-down  { box-shadow: inset 3px 0 0 var(--down); }
  #hist-body .dev tbody tr.sev-maint { box-shadow: inset 3px 0 0 var(--maint); }
  #hist-body .dev tbody tr > td:nth-child(1) { grid-column: 1; grid-row: 1 / span 2; }
  #hist-body .dev tbody tr > td:nth-child(2) { grid-column: 2; grid-row: 1; }
  /* Durata trece în DREAPTA, mare: e informația pentru care deschizi pagina. */
  #hist-body .dev tbody tr > td.ev-durata { grid-column: 3; grid-row: 1 / span 2;
    font-size: 14px; font-weight: 700; white-space: nowrap; }
  /* Data completă iese; rămân orele scurte, sub tip. */
  #hist-body .dev tbody tr > td:nth-child(3) { grid-column: 2; grid-row: 2; font-size: 0; }
  #hist-body .dev tbody tr > td:nth-child(4) { display: none; }
  #hist-body .ev-ore { display: inline; font-size: 12px; color: var(--txt-dim); }
  /* „în curs" e o stare, nu o oră — se arată în locul intervalului. */
  #hist-body .dev tbody tr.sev-down > td:nth-child(3) .ev-ore::after { content: " → în curs"; }
}

/* Antetul de zi din istoric: rezumat + săgeată de strâns/desfăcut (doar pe telefon). */
@media (max-width: 620px) {
  /* ⚠ `width: 100%` explicit: un `<td>` pus pe `display: flex` într-un `<tr>` care e `block`
     se strânge la conținut — ieșea 30px din 360, iar textul se rupea pe verticală, literă cu
     literă. `colspan` nu mai înseamnă nimic odată ce tabelul e `display: block`. */
  #hist-body .ev-zi td { display: flex; align-items: center; gap: 9px; min-height: 44px;
    width: 100%; box-sizing: border-box; cursor: pointer; }
  .ez-zi { font-size: 12px; }
  .ez-n { font-weight: 500; text-transform: none; letter-spacing: 0; font-size: 11.5px;
    color: var(--txt-faint); }
  .ez-jos { margin-left: auto; font-weight: 600; text-transform: none; letter-spacing: 0;
    font-size: 11.5px; color: var(--warn); }
  .ez-sg { flex: none; color: var(--txt-faint); font-size: 11px; transition: transform .15s ease; }
  .ev-zi.ez-deschis .ez-sg { transform: rotate(180deg); }
}

/* ============================================================================
   TABELELE DE ADMINISTRARE pe telefon (06 sep) — fiecare rând devine o fișă
   ⚠ Măsurat pe toate cele 12 pagini: tabelele au între 397 și 1361px pe un ecran de 390.
   Etichetele vin din `<thead>`, puse pe celule de `tabele-mobil.js` — o singură sursă.
   ⚠ Se aplică DOAR tabelelor marcate de script (`.tbl-fise`), nu oricărui `<table>`: tabelul
   de camere din pagina comunei are deja tratamentul lui, iar două ar intra în conflict.
   ============================================================================ */
@media (max-width: 620px) {
  .tbl-fise, .tbl-fise tbody { display: block; width: 100%; }
  .tbl-fise thead { display: none; }
  .tbl-fise tbody tr {
    display: block; margin-bottom: 8px; padding: 10px 12px;
    background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  }
  .tbl-fise tbody td {
    display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
    padding: 3px 0; border: 0; font-size: 12.5px; text-align: right;
  }
  .tbl-fise tbody td::before {
    content: attr(data-eticheta); flex: none; text-align: left;
    color: var(--txt-faint); font-size: 11.5px; font-weight: 500;
  }
  /* Primul rând al fișei e TITLUL — numele omului, IP-ul, comuna. Fără etichetă, mai mare. */
  .tbl-fise tbody td:first-child, .tbl-fise tbody td.td-fara-eticheta:first-child {
    font-size: 14.5px; font-weight: 700; text-align: left; padding-bottom: 6px;
  }
  .tbl-fise tbody td:first-child::before { content: none; }
  /* Coloanele fără cap (bulină, acțiuni) rămân fără etichetă. */
  .tbl-fise tbody td.td-fara-eticheta::before { content: none; }
  .tbl-fise tbody td.td-fara-eticheta { justify-content: flex-end; }
  /* Un rând care ocupă tot tabelul („niciun rezultat") rămâne un bloc simplu. */
  .tbl-fise tbody tr:has(td[colspan]) { padding: 16px 12px; text-align: center; }
  .tbl-fise tbody td[colspan] { display: block; text-align: center; }
  .tbl-fise tbody td[colspan]::before { content: none; }

  /* --- barele de taburi: derulabile, cu ținte de 44px ------------------------------------
     ⚠ Aveau 7 taburi de 34px, rupte pe două rânduri pe fiecare pagină de administrare. */
  .admin-tabs, .srv-tabs {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .admin-tabs::-webkit-scrollbar, .srv-tabs::-webkit-scrollbar { display: none; }
  .admin-tab, .srv-tab { flex: none; min-height: 44px; display: flex; align-items: center;
    white-space: nowrap; }
}

/* ---- Blocurile de setare pe telefon ------------------------------------------------------
   ⚠ `.setting` e o grilă `1fr auto auto`, iar antetul ei nu se putea îngusta sub 522px: pe
   trei pagini (Setări, Notificări, Email) PAGINA ÎNTREAGĂ derula lateral cu ~300px. Un
   element care nu se poate strânge împinge tot documentul, nu doar cutia lui. */
@media (max-width: 620px) {
  .setting { grid-template-columns: minmax(0, 1fr); padding: 12px 14px; gap: 10px; }
  .setting > *, .setting-head, .setting-form, .setting-num { min-width: 0; }
  .setting-head { flex-wrap: wrap; }
  .setting-form, .setting-reset { justify-self: start; }
  .setting-num input[type="number"], .setting-num input[type="time"] { max-width: 130px; }
  /* Tooltipul de lângă ⓘ: `position: absolute`, deci nu împinge pagina — dar ieșea din ecran
     și rămânea necitibil. Se plafonează la lățimea ecranului. */
  /* ⚠ Tooltipul e `position: absolute` FAȚĂ DE ICONIȚĂ, deci cu cât ⓘ e mai la dreapta, cu
     atât iese mai mult — până la 306px dincolo de ecran, ceea ce făcea PAGINA să deruleze
     lateral. Un element absolut nu împinge layoutul, dar CONTRIBUIE la `scrollWidth`.
     Pe telefon se ancorează de CARD, nu de iconiță: `.ib` devine `static`, iar tooltipul se
     întinde între marginile cardului. Un text de ajutor n-are de ce să fie mai îngust. */
  .setting { position: relative; }
  .setting .ib { position: static; }
  /* ⚠ `position: absolute` REPUS explicit: din 2026-09-07 regula de bază e `fixed`, iar aici
     ancorarea de CARD e deliberată și validată pe telefon. Poziționatorul din JS o respectă —
     sare peste orice tooltip care nu e `fixed`, deci decizia rămâne într-un singur loc: aici. */
  .setting .ib-tip { position: absolute; left: 12px; right: 12px; max-width: none; width: auto;
    white-space: normal; }
  /* Restul (ex. /admin/sites, unde ⓘ nu stă într-un `.setting`) rămân `fixed` și sunt
     întoarse de JS lângă marginea ecranului — acolo se măsurau 309px pe dinafară la 390px. */
  .ib-tip { max-width: calc(100vw - 32px); white-space: normal; }
  /* Tabelul de acoperire a hărții n-are cap, deci nu poate deveni fișe — se strânge în loc. */
  .cov-list { table-layout: fixed; width: 100%; }
  .cov-list td, .cov-list th { overflow: hidden; text-overflow: ellipsis; }
  /* ⚠ Panoul de alegere a grupurilor ieșea 114px din ecran. `max-width` singur n-a fost de
     ajuns: e ancorat `left: 0` față de un declanșator aflat în dreapta rândului, deci se
     deschide spre dreapta din start. Se ancorează la DREAPTA — aceeași reparație ca la meniul
     ⋮ al cardului NVR, a treia oară în proiect. */
  .grp-dd-panel { left: auto; right: 0; max-width: calc(100vw - 28px); }
}

/* ---- Ținte de atins în administrare (06 sep) --------------------------------------------
   ⚠ Măsurate pe cele 12 pagini: peste 700 de controale sub 44px — bife de 14px, butoane mici
   de 28, comutatoare de canal câte 125 pe o pagină. Colegii apasă cu mănuși, pe teren.
   ⚠ Bifele NU se măresc vizual: se mărește ZONA SENSIBILĂ, prin eticheta care le însoțește.
   O bifă de 28px arată greoi într-o listă de 41; una de 14px într-o etichetă de 44 se apasă
   la fel de ușor și arată la fel. Aceeași soluție ca la butoanele din pastila de alertă. */
@media (max-width: 620px) {
  /* ⚠ `.admin-page` / `.srv-page` sunt ADĂUGATE în cele două layouturi: scrisesem regulile
     pe ele înainte să existe, iar toate erau literă moartă — zero efect, zero eroare.
     Verificat că se aplică, nu presupus. */
  .admin-page label.check, .admin-page .u-until, .admin-page .ov-check,
  .srv-page label.check {
    display: flex; align-items: center; gap: 10px; min-height: 44px;
  }
  .admin-page .btn-sm, .srv-page .srv-btn, .srv-page .srv-more, .srv-page .srv-chip,
  .admin-page .seg-opt, .admin-page .al-t, .admin-page .al-kebab, .admin-page .btn-add-site {
    min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  }
  /* Butoanele de trimitere ale formularelor („Creează utilizator", „Salvează accesul") —
     prinse de trecerea finală, nu de auditul pe pagini: sunt jos de tot, sub formulare lungi. */
  .admin-page button[type="submit"], .srv-page button[type="submit"] { min-height: 44px; }
  .admin-page select, .admin-page input[type="text"], .admin-page input[type="number"],
  .admin-page input[type="email"], .admin-page input[type="time"],
  .admin-page input[type="search"], .srv-page input[type="text"] { min-height: 44px; }
  /* Bifele și radiourile: puțin mai mari pe telefon. Ținta de 44px o dă eticheta
     înfășurătoare (`min-height: 44px` mai jos), NU bifa — vezi nota de la bifele desenate. */
  .admin-page input[type="checkbox"], .admin-page input[type="radio"] {
    width: 22px; height: 22px; flex: none; background-size: 14px;
  }
}

/* Restul controalelor din administrare, prinse la a doua măsurare (bifele fără etichetă
   înfășurătoare, comutatoarele segmentate, listele proprii `.sel-dd`). */
@media (max-width: 620px) {
  .admin-page .grp-opt, .admin-page .al-grp-sum,
  .admin-page .sel-opt, .admin-page .sel-dd > summary,
  .srv-page .sel-opt, .srv-page .sel-dd > summary,
  .admin-page details > summary, .srv-page details > summary {
    min-height: 44px; display: flex; align-items: center; gap: 9px;
  }
  /* ⚠ PREFIXAT cu `.ov-brands .seg`: regula de bază are (0,3,0), iar `.admin-page .seg-opt`
     (0,2,0) era bătută TĂCUT — comutatoarele de brand NVR rămâneau la 34px, câte 54 pe
     pagină. A șasea coliziune de felul ăsta în felia de design. */
  .admin-page .ov-brands .seg .seg-opt { min-height: 44px; padding-top: 0; padding-bottom: 0; }
  /* Numele de comună din tabelul de acoperire se rupe, nu se taie: „iordacheanu" ieșea
     „iordachean…", iar o comună tăiată e greu de recunoscut. */
  .cov-list td, .cov-list th { white-space: normal; overflow: visible; text-overflow: clip;
    word-break: break-word; }
}

/* ---- utilitare pentru închiderea lui `style-src` din CSP (07 sep 2026) -------------------
 * Portalul avea 68 de atribute `style="…"` STATICE în șabloane. Fiecare e cod inline pentru
 * CSP, deci `style-src` trebuia să rămână `'unsafe-inline'` — adică jumătate din politică
 * dezactivată. Mutate aici, ca reguli.
 * ⚠ Numele sunt VERIFICATE ca libere înainte de a fi alese: `.num` era deja folosit în 5 locuri
 * din șabloane FĂRĂ nicio regulă CSS, deci a-i da una acum le-ar fi schimbat tăcut pe toate.
 * ⚠ Nu sunt „utilitare de framework": sunt exact valorile care existau deja inline, cu nume care
 * spun ce sunt. Nu se adaugă altele fără motiv. */
.val-num    { text-align: right; font-variant-numeric: tabular-nums; }
.val-centru { text-align: center; }
.txt-slab   { color: var(--txt-faint); }
.gros       { font-weight: 600; }
.gros-7     { font-weight: 700; }
/* Mesajele de „se încarcă / n-am putut încărca" din modale — construite în JS prin `innerHTML`.
   ⚠ Și ALEA sunt atribute de stil în markup pentru CSP, chiar dacă șirul vine din JS. */
.mesaj-panou { padding: 1rem; }

/* Lățimile coloanelor tabelului de camere (pagina comunei). ⚠ Aici NU e nevoie de clase în
   HTML: `<col>`-urile se pot ținti pe poziție, iar tabelul e unul singur, cu `id`. Mai puțin
   markup și, mai important, ordinea coloanelor trăiește într-un singur loc. */
#cam-table col:nth-child(1) { width: 40px; }
#cam-table col:nth-child(2) { width: 13%; }
#cam-table col:nth-child(3) { width: 8%; }
#cam-table col:nth-child(4) { width: 13%; }
#cam-table col:nth-child(5) { width: 18%; }
#cam-table col:nth-child(6) { width: 8%; }
#cam-table col:nth-child(7) { width: 9%; }
#cam-table col:nth-child(8) { width: 8%; }
#cam-table col:nth-child(9) { width: 12%; }
#cam-table col:nth-child(10) { width: 13%; }

/* Restul atributelor `style=` statice, mutate din șabloane (vezi nota de mai sus). */
.crumbs-jos      { margin-bottom: 12px; }
.hint-sus        { margin-top: 12px; }
.srv-empty-strans { padding: 8px 0; }
.col-rest        { width: 42%; }
.al-ascuns       { color: var(--warn); display: none; }
.srv-pill-mic    { padding: 3px 10px; font-size: 11.5px; }
.srv-grid-sus    { margin-top: 18px; }
.srv-card-link   { text-decoration: none; color: inherit; display: block; }
.srv-hint-sus    { margin: 8px 0 0; }
.srv-hint-mic    { margin: 3px 0 0; }
.srv-wrap-pad    { padding: 14px 16px; }
.srv-wrap-sus    { margin-top: 8px; }
.srv-rand        { display: flex; justify-content: space-between; align-items: center;
                   gap: 14px; flex-wrap: wrap; }
.srv-titlu       { font-weight: 700; font-size: 14px; }
.txt-ok          { color: var(--ok); }
.txt-down        { color: var(--down); }
.dim-stanga  { margin-left: .6rem; }
.hint-sub    { margin-top: .6rem; }
.ajutor      { cursor: help; }
.gauge-unit  { font-size: 15px; }
.txt-warn   { color: var(--warn); }
.txt-normal { color: var(--txt); }

/* ⚠ COLIZIUNE DE CASCADĂ, prinsă la mutarea stilurilor inline în clase (07 sep 2026).
   `.srv-tbl th` (0,1,1) — regulă din blocul `<style>` al lui `server_base.html`, deci INVIZIBILĂ
   pentru un grep prin fișierele `.css` — bate `.val-num` (0,1,0). Șase antete de coloană numerică
   din zona Server rămâneau aliniate la stânga.
   ⚠ Tiparul e general și e chiar riscul mutării inline→clasă: un stil inline bate ORICE selector,
   deci orice conversie poate pierde o luptă pe care înainte n-o avea. Verificarea nu e „există
   clasa?", ci „ce spune `getComputedStyle`?" — browserul a fost cel care a numit regula. */
.srv-tbl th.val-num { text-align: right; }


/* ---- pop-up-urile din administrare (modale-admin.js) ------------------------------------- */
.um-modal .modal-dialog { max-width: 560px; }
.modal-title .um-user { display: block; font-size: 12.5px; font-weight: 400; color: var(--txt-dim);
  margin-top: 2px; }
.um-sect { margin-top: var(--sp-4); padding-top: var(--sp-4); border-top: 1px solid var(--line-sect); }
/* Prima secțiune n-are linie deasupra (nu desparte de nimic), dar păstrează treapta de
   secțiune — altfel era singurul spațiu din fereastră care nu se potrivea în scară (6px). */
.um-sect:first-of-type { margin-top: var(--sp-4); padding-top: 0; border-top: none; }
.um-h-nota { margin-left: 7px; font-weight: 400; text-transform: none; letter-spacing: 0;
  color: var(--txt-faint); font-size: 11.5px; }
.um-h { margin: 0 0 var(--sp-2); font-size: 12px; font-weight: 700; color: var(--txt-dim);
  text-transform: uppercase; letter-spacing: .5px; }
.um-grupuri { display: flex; flex-direction: column; gap: 2px; }
.um-grupuri > .btn-sm { align-self: flex-start; margin-top: 8px; }
/* Acțiunile: fiecare e un formular propriu (rute separate, CSRF propriu), deci butoanele se
   așază pe o grilă, nu într-un formular comun. */
.um-actiuni { display: flex; flex-wrap: wrap; gap: 7px; }
.um-actiuni form { display: contents; }
/* ⚠ `:not(.danger)` pe AMBELE. Regula asta e (0,1,1), exact ca `button.danger` de mai sus,
   dar sta mai jos in fisier ⇒ ar fi cistigat si ar fi anulat TACUT culoarea distructiva —
   fundal `--panel-2`, text `--txt`, adica exact butonul neutru pe care tocmai l-am schimbat.
   A opta coliziune de cascada din felia de design, si a doua gasita CAUTIND-O inainte sa
   apara, nu dupa. */
.um-actiuni button:not(.danger) {
  border: 1px solid var(--line); background: var(--panel-2); color: var(--txt);
  border-radius: 8px; padding: 7px 12px; font: inherit; font-size: 13px; cursor: pointer;
}
.um-actiuni button.danger {
  border: 1px solid var(--down-line); border-radius: 8px; padding: 7px 12px;
  font: inherit; font-size: 13px; cursor: pointer;
}
.um-actiuni button:not(.danger):hover { border-color: var(--accent); color: var(--accent); }

/* ---- BIFE ȘI RADIOURI, desenate de noi (07 sep 2026) --------------------------------------
 * ⚠ De ce nu doar `accent-color`: portalul îl punea în 8 locuri, dar NU peste tot — unele bife
 * erau roșii, altele albastrul implicit al browserului. Iar mărimea nu se putea corecta:
 * `width`/`height` pe o bifă NATIVĂ nu e onorat de WebKit. Cu `appearance: none` este.
 *
 * ⚠⚠ SEMNUL SE DESENEAZĂ CU `background-image`, NU CU `::before`. Măsurat în WebKit:
 * `getComputedStyle(input, '::before').content` întoarce **`none`** — motorul NU generează
 * pseudo-elemente pe `<input>`, care e element înlocuit. O bifă cu semnul pus pe `::before`
 * ar fi ieșit un pătrat roșu GOL, iar nimic n-ar fi semnalat-o.
 * ⚠ Din același motiv, zona de atins nu se poate întinde cu `::after` pe input — vezi nota de
 * la `.grp-sites` mai jos.
 */
input[type="checkbox"], input[type="radio"] {
  appearance: none; -webkit-appearance: none;
  width: 18px; height: 18px; flex: none;
  border: 1.5px solid var(--line-strong); border-radius: 5px;
  background: var(--bg-2) center/12px no-repeat; cursor: pointer;
  vertical-align: -4px; transition: background-color .12s ease, border-color .12s ease;
}
input[type="radio"] { border-radius: 50%; }
input[type="checkbox"]:hover, input[type="radio"]:hover { border-color: var(--accent); }
input[type="checkbox"]:checked {
  background-color: var(--accent); border-color: var(--accent);
  background-image: var(--ico-bifa-alba);
}
input[type="radio"]:checked {
  background-color: var(--accent); border-color: var(--accent);
  background-image: var(--ico-punct-alb); background-size: 8px;
}
input[type="checkbox"]:indeterminate {
  background-color: var(--accent); border-color: var(--accent);
  background-image: var(--ico-linie-alba); background-size: 10px;
}
input[type="checkbox"]:disabled, input[type="radio"]:disabled {
  opacity: .45; cursor: not-allowed; border-color: var(--line);
}
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* ---- ferestrele de administrare: câmpuri, comutator, subsol ------------------------------- */
.um-intro { margin: var(--sp-2) 0 0; color: var(--txt-dim); font-size: 12.5px; line-height: 1.5; }
.um-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3) 16px; }
.um-lat { grid-column: 1 / -1; }
.um-f { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
/* ⚠ UN SINGUR stil de etichetă în toată fereastra. Înainte erau două: `.auf-lbl` (12,5px,
   normal) la adăugare și `.um-h` (12px, majuscule) la editare — în aceeași pagină. */
.um-lbl { font-size: 12.5px; font-weight: 500; color: var(--txt-dim); }
/* ⚠ EXCLUDE, nu enumera. Regula asta a fost `input[type="text"], input[type="email"]`, si
   exact de-aia cimpul de data a ajuns in productie complet nestilizat (fundal rgb(30,30,30),
   font de sistem 16px, inaltime 27 in loc de 40) — nu figura in lista. Un selector care
   ENUMERA tipuri lasa pe dinafara orice tip adaugat dupa el si NU da nicio eroare: arata doar
   altfel. Cu forma de mai jos, un `number`, un `tel` sau un `search` adaugat miine e imbracat
   din prima. Bifele si radiourile sint scoase fiindca le desenam noi, separat. */
.um-f > input:not([type="checkbox"]):not([type="radio"]) {
  height: 40px; border: 1px solid var(--line); border-radius: 9px; background: var(--bg-2);
  color: var(--txt); font: inherit; font-size: 13.5px; padding: 0 12px; width: 100%;
}
.um-f > input:focus { outline: none; border-color: var(--accent); }
.um-f .sel-dd > summary, .um-f .grp-dd > summary { height: 40px; border-radius: 9px; }

/* Opțiunea de la creare: comutator, nu bloc gri. Blocul gri arăta ca o zonă dezactivată,
   când de fapt e o alegere pe care o faci. */
/* ⚠ Avea 0px fata de grila de deasupra — chiar „nu am spatii deloc" din sesizare.
   Comutatorul e de acelasi rang cu cimpurile, deci `--sp-3`. */
.um-optiune { display: flex; gap: 12px; align-items: flex-start; cursor: pointer;
  margin-top: var(--sp-3); }
.um-optiune > span { font-size: 12.5px; color: var(--txt-dim); line-height: 1.5; }
.um-optiune b { display: block; font-weight: 600; font-size: 13.5px; color: var(--txt);
  margin-bottom: 2px; }
/* Comutatorul: aceeași bifă, desenată altfel. Rămâne un `input[type=checkbox]`, deci
   trimiterea formularului și tastatura funcționează neschimbat.
   ⚠ Butonul rotund e `background-image`, mutat cu `background-position` — NU un `::before`.
   Motivul e același ca la semnul de bifat: WebKit nu generează pseudo-elemente pe `<input>`
   (`getComputedStyle(el, '::before').content` → `none`). Cu `::before`, comutatorul ieșea o
   pastilă goală, fără buton — arăta ca un câmp stricat, nu ca un întrerupător. */
input[type="checkbox"].sw {
  width: 38px; height: 22px; border-radius: 20px; border: none; flex: none;
  background: var(--line-strong) var(--ico-punct-alb) 3px center / 16px no-repeat;
  transition: background-color .15s ease, background-position .15s ease;
  margin-top: 1px; vertical-align: 0;
}
input[type="checkbox"].sw:hover { border: none; }
input[type="checkbox"].sw:checked {
  background-color: var(--accent);
  background-image: var(--ico-punct-alb);
  background-position: calc(100% - 3px) center;
}

/* Acțiunile stau dreapta-jos, cu cea secundară alături — convenția pe care o folosesc deja
   celelalte modale ale portalului. Înainte era un buton singur, stânga-jos, iar ieșirea din
   fereastră se putea face doar din ✕. */
/* ---- fereastra de EDITARE: aceleasi piese ca la adaugare -------------------------------- */
/* Insignele de stare din antet: rol · activ/dezactivat · al doilea factor. */
/* ---- fereastra de utilizator: densitate proprie (09 sep 2026) ------------------------------
   ⚠ Fereastra e o suprafață de 560×918px cu patru secțiuni; densitatea unui rând de tabel nu
   i se potrivește. Cerere Bogdan: „acum e puțin mai mic decât maximul încadrabil […] fă-l la
   limita maximă admisă și adaptează-l prin spațiere egală. Dacă tot mai e loc, hai să
   profităm de el."
   ⚠⚠ Plafonul e 85% din VIEWPORT, nu din ecran. Reglasem întâi pe 1080 (plafon 918px), dar
   Safari-ul lui Bogdan are și bara de semne de carte: între bara de meniu a sistemului, bara
   de file, cea de adrese și cea de semne, un ecran de 1080 lasă un viewport de ~850–940. Deci
   ținta NU e „918", e „să nu deruleze la ~820 de viewport" — adică un conținut sub ~690px.
   ⚠ Lecția: „rezoluția ecranului" și „înălțimea în care desenez" sunt două lucruri diferite,
   iar diferența dintre ele e mobilierul browserului, care variază de la om la om.
   ⚠ NU e o a doua scară, e ACEEAȘI scară cu pasul mai mare, scopată pe fereastră: relațiile
   rămân aceleași patru (etichetă↔cîmp · bloc↔lămurirea lui · rang egal · între secțiuni), doar
   distanțele cresc proporțional. Regula „ce relație e asta?" se aplică neschimbată.
   ⚠ Rezerva de sub plafon e DIMENSIONATĂ, nu întâmplătoare: trebuie să acopere un rând în
   plus de pastile de grup (~50px). Cu patru grupuri în loc de trei, pastilele trec pe al
   doilea rând — iar o fereastră reglată la 13px sub plafon ar fi început să deruleze exact
   atunci, adică la prima creștere a parcului. */
.um-modal .modal-dialog {
  --sp-1: 6px; --sp-2: 10px; --sp-3: 16px; --sp-4: 22px;
}
.um-modal .um-f > input:not([type="checkbox"]):not([type="radio"]),
.um-modal .dt-cimp .dt-text { height: 44px; }
.um-modal .um-actiuni button { padding: 9px 13px; }
.um-modal .grp-dd > summary { height: 44px; }

.um-cip { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin: calc(var(--sp-2) - 4px) 0 0; }
.um-cip .um-user { display: block; font-size: 12.5px; color: var(--txt-dim); margin-right: 4px; }
/* Textul de ajutor de sub un cimp. Acelasi rol ca `.hint`, dar legat de UN cimp anume. */
.um-ajut { font-size: 11.5px; color: var(--txt-faint); line-height: 1.45;
  margin: var(--sp-2) 0 var(--sp-3); display: block; }
/* Butonul unei sectiuni sta la DREAPTA ei, ca sa se citeasca „asta salveaza ce e deasupra". */
.um-sect-foot { display: flex; justify-content: flex-end; margin-top: var(--sp-3); }
/* ⚠ Controlul la stinga, butonul lui de salvare la DREAPTA, pe acelasi rind. Ideea lui Bogdan,
   ca fereastra sa incapa fara derulare: un buton pe rind propriu costa 48px (34 butonul + 14
   marja), iar la doua sectiuni asta e o optime din inaltimea ferestrei.
   ⚠ `align-items: flex-end` la selector: eticheta sta DEASUPRA cimpului, deci alinierea la
   mijloc ar ridica butonul in dreptul etichetei, nu al cimpului. La comutator, unde nu exista
   eticheta deasupra, se trece la mijloc. */
.um-rand-actiune { display: flex; align-items: flex-end; gap: var(--sp-3); }
.um-rand-actiune > .um-crestere { flex: 1; min-width: 0; }
.um-rand-actiune > .btn-salveaza { flex: none; }
.um-rand-actiune > .um-optiune { flex: 1; min-width: 0; margin-top: 0; }
.um-rand-actiune:has(> .um-optiune) { align-items: center; }
/* Textul comutatorului e o propozitie: plafonat, se rupe pe doua rinduri si ramine loc de buton. */
.um-rand-actiune > .um-optiune > span { max-width: 42ch; }
/* Cimpul de data nu are nevoie de toata latimea, dar textul de ajutor de sub el DA — altfel
   se ingusteaza la o coloana si iese un bloc inalt si subtire. Deci cimpul e ingust, campul
   care-l contine e lat. */
/* ---- „Acces pina la": cimpul de data + calendarul propriu (08 sep 2026) ------------------
   ⚠ Cel nativ a fost inlocuit la cererea lui Bogdan: „calendarul ala […] e urit rau", n-avea
   alegere de luna si an (un an de valabilitate = douasprezece clicuri pe sageata) si nu se
   putea scrie data de la tastatura. Vezi `web/static/calendar.js` pentru contract. */
.um-data-rand { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.dt-cimp { position: relative; display: inline-flex; }
.dt-cimp .dt-text {
  width: 150px; height: 40px; border: 1px solid var(--line); border-radius: 9px;
  background: var(--bg-2); color: var(--txt); font: inherit; font-size: 13.5px;
  padding: 0 38px 0 12px; font-variant-numeric: tabular-nums; letter-spacing: .2px;
}
.dt-cimp .dt-text:focus { outline: none; border-color: var(--accent); }
.dt-cimp .dt-text::placeholder { color: var(--txt-faint); }
/* ⚠ Cimpul necitibil se vede ROSU si trimiterea e oprita din JS. O data gresita care ar
   pleca tacut ca „fara limita" ar fi exact opusul a ce voia omul. */
.dt-cimp.rau .dt-text { border-color: var(--down); }
.dt-buton {
  position: absolute; right: 5px; top: 50%; transform: translateY(-50%);
  width: 30px; height: 30px; border: none; background: transparent; border-radius: 7px;
  cursor: pointer; display: grid; place-items: center; padding: 0; color: var(--txt-faint);
}
.dt-buton:hover { background: var(--panel-2); color: var(--txt-dim); }
.dt-buton svg { width: 17px; height: 17px; }
.dt-buton:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.um-data-ro { font-size: 13px; color: var(--txt-dim); font-weight: 500; }
.um-data-ro.dt-eroare { color: var(--down); }
.um-data-sterge { flex: none; }

/* ---- calendarul ---- */
/* ⚠ `position: fixed` si coordonate din JS: fereastra are `overflow-y: auto` si ar taia un
   element absolut. Aceeasi capcana cu tooltipul de stilp DIGI si cu meniul ⋮. */
.cal {
  position: fixed; z-index: 90; width: 288px; background: var(--panel);
  border: 1px solid var(--line-sect); border-radius: 13px; padding: 12px;
  box-shadow: 0 10px 34px rgba(0,0,0,.4), 0 2px 6px rgba(0,0,0,.25);
}
:root[data-theme="light"] .cal { box-shadow: 0 10px 30px rgba(20,30,50,.16), 0 2px 6px rgba(20,30,50,.08); }
.cal[hidden] { display: none; }
.cal-cap { display: flex; align-items: center; gap: 6px; margin-bottom: var(--sp-2); }
/* ⚠ Luna si anul sint LISTE, nu sageti — asta era cererea. Sagetile raman, pentru pasi mici. */
.cal-sel {
  flex: 1; min-width: 0; height: 34px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg-2); color: var(--txt); font: inherit; font-size: 13px;
  padding: 0 26px 0 8px; cursor: pointer; appearance: none; -webkit-appearance: none;
  background-image: var(--ico-jos); background-repeat: no-repeat;
  background-position: right 7px center; background-size: 13px;
}
.cal-sel:focus { outline: none; border-color: var(--accent); }
.cal-an { flex: 0 0 76px; }
.cal-sag {
  width: 32px; height: 34px; flex: none; border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg-2); cursor: pointer; display: grid; place-items: center; padding: 0;
  color: var(--txt-dim);
}
.cal-sag:hover { border-color: var(--accent); }
.cal-sag svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round; }
.cal-zile, .cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-zile span { text-align: center; font-size: 10.5px; font-weight: 600; color: var(--txt-faint);
  letter-spacing: .04em; text-transform: uppercase; padding: 4px 0 6px; }
.cal-grid button {
  height: 34px; border: none; background: transparent; color: var(--txt); font: inherit;
  font-size: 13px; border-radius: 8px; cursor: pointer; font-variant-numeric: tabular-nums;
}
.cal-grid button:hover { background: var(--panel-2); }
.cal-grid button.afara { color: var(--txt-faint); opacity: .5; }
.cal-grid button.azi { box-shadow: inset 0 0 0 1.5px var(--line-strong); font-weight: 600; }
.cal-grid button.ales, .cal-grid button.ales:hover { background: var(--accent); color: #fff;
  font-weight: 600; }
.cal-grid button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.cal-jos { display: flex; justify-content: space-between; gap: 8px; margin-top: var(--sp-2);
  padding-top: var(--sp-2); border-top: 1px solid var(--line-soft); }
.cal-jos button { border: none; background: transparent; color: var(--txt-dim); font: inherit;
  font-size: 12.5px; font-weight: 500; cursor: pointer; padding: 5px 8px; border-radius: 7px; }
.cal-jos button:hover { background: var(--panel-2); color: var(--txt); }
.cal-jos button.sterge:hover { color: var(--down); }

/* Pe telefon: ACELASI calendar, cu tinte mari — nu cel nativ. Doua interfete pentru acelasi
   lucru e chiar ce s-a eliminat la selectorul de grupuri. */
@media (max-width: 620px) {
  .cal { width: min(320px, calc(100vw - 20px)); }
  .cal-grid button { height: 42px; font-size: 14px; }
  .cal-sel, .cal-sag { height: 42px; }
  .cal-jos button { padding: 10px 10px; }
  .dt-cimp .dt-text { height: 44px; }
}
/* Selectorul de grupuri, cit toata fereastra — ca in fereastra de adaugare. */
.um-modal .grp-dd { width: 100%; }
/* ⚠ `display: flex` + `space-between`, nu `inline-flex`: regula de baza (`.grp-dd > summary`)
   e gindita pentru un rezumat scurt intr-un rind de tabel, unde sageata sta lipita de text.
   Intr-un cimp de formular lat, sageata trebuie sa fie la marginea din dreapta, ca la toate
   celelalte selectoare din portal. Aceeasi clasa, doua contexte. */
.um-modal .grp-dd > summary { width: 100%; height: 40px; border-radius: 9px; padding: 0 12px;
  display: flex; justify-content: space-between; font-size: 13.5px; color: var(--txt);
  overflow: hidden; }
/* ⚠ Operatiunile care TAIE accesul, pe rind propriu, sub o linie. Cerere Bogdan (08 sep):
   „butoanele de dezactivare sa fie mai stridente putin". Culoarea vine din regula comuna
   `button.danger`; aici se adauga SEPARAREA — un buton ireversibil nu are ce cauta in acelasi
   rind cu „Reset parola". */
.um-periculoase { margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--line-sect); }

.um-foot { margin-top: var(--sp-4); padding-top: var(--sp-4); border-top: 1px solid var(--line-sect);
  display: flex; justify-content: flex-end; gap: 9px; }
