/* host24 Portal - Design System v2 "Clarity" (1Panel-inspired: light, crisp, enterprise) */
@property --p { syntax: '<number>'; inherits: true; initial-value: 0; }

:root{
  color-scheme:light;
  --bg:#f6f8fc; --card:#ffffff; --border:#eceff5; --border-strong:#d4dae3;
  --ink:#1c2434; --text:#4b5563; --muted:#8a94a6;
  --primary:#3d6ef7; --primary-deep:#2b5cf0; --primary-soft:#eef3ff;
  --ok:#16a34a; --ok-soft:#e8f7ee; --warn:#d97706; --warn-soft:#fdf3e3;
  --bad:#dc2626; --bad-soft:#fdecec; --info:#0891b2; --info-soft:#e6f7fb;
  /* Surfaces and lines that used to be literals. Every colour a theme has to
     change lives here, so a dark palette is one block below and not eighty
     edits scattered through the file. --surface is the white that panels,
     inputs and the top bar sit on; --surface-2 is the barely-grey hover and
     header shade; --track is the empty part of a ring or a bar. */
  --surface:#ffffff; --surface-2:#f3f5fa; --track:#e9edf3;
  --primary-line:#c7d7fe; --ok-line:#bfe3cd; --warn-line:#f0d3a4; --bad-line:#f5c2c2;
  --ok-ink:#14683a; --warn-ink:#7c4a06; --warn-ink-deep:#5c3604; --lbl:#475569;
  --r:12px; --shadow:0 1px 2px rgba(16,24,40,.05),0 1px 3px rgba(16,24,40,.08);
  --shadow-lg:0 8px 24px rgba(16,24,40,.10);
}
/* Dark. Chosen by the switch in the top bar and remembered per browser; the
   pre-paint script in each shell's head sets data-theme before the first
   paint so a dark page never flashes white. The accent, the status colours and
   white-on-accent text are the same in both: only surfaces, lines and ink
   move. color-scheme makes native controls (selects, scrollbars) follow. */
[data-theme="dark"]{
  color-scheme:dark;
  --bg:#0f141c; --card:#161c26; --border:#232b38; --border-strong:#33405a;
  --ink:#e8edf5; --text:#b7c0cf; --muted:#7f8a9b;
  --primary-soft:#1a2440;
  --ok-soft:#10261a; --warn-soft:#2b2010; --bad-soft:#2d1717; --info-soft:#0f2530;
  --surface:#161c26; --surface-2:#1c2431; --track:#243040;
  --primary-line:#2b3d6b; --ok-line:#1f4a30; --warn-line:#4a3512; --bad-line:#4d2222;
  --ok-ink:#7fd3a0; --warn-ink:#e3b56b; --warn-ink-deep:#f0c98a; --lbl:#9aa6b8;
  --shadow:0 1px 2px rgba(0,0,0,.35),0 1px 3px rgba(0,0,0,.4);
  --shadow-lg:0 8px 24px rgba(0,0,0,.5);
}
/* The switch itself: moon in light mode (what you would go to), sun in dark. */
.theme-toggle .ico-sun{display:none}
[data-theme="dark"] .theme-toggle .ico-sun{display:block}
[data-theme="dark"] .theme-toggle .ico-moon{display:none}
[data-theme="dark"] img.qr{background:#fff}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:var(--bg);color:var(--text);font-size:13.5px;line-height:1.45;min-height:100vh}
a{color:var(--primary);text-decoration:none}
.sub{color:var(--muted);font-size:12px}
.ink{color:var(--ink)}

/* ---------- top bar ---------- */
.topbar{position:sticky;top:0;z-index:50;display:flex;height:52px;background:var(--surface);border-bottom:1px solid var(--border)}
.topbar-inner{display:flex;align-items:center;gap:14px;width:100%;max-width:1760px;margin:0 auto;padding:0 18px}
.logo{display:flex;align-items:center;gap:9px;font-weight:800;color:var(--ink);font-size:15.5px;letter-spacing:.01em}
.logo b{color:var(--primary)}
.logo-dot{width:22px;height:22px;border-radius:7px;background:var(--primary);display:inline-block;position:relative}
.logo-dot::after{content:"24";position:absolute;inset:0;display:grid;place-items:center;color:#fff;font-size:9.5px;font-weight:800}
.topright{margin-left:auto;display:flex;align-items:center;gap:12px}
/* .row was used in thirteen places before it was ever defined. Every one of
   them sets gap, align-items, flex-wrap or justify-content inline -- properties
   that do nothing at all without this line -- so every one of those rows has
   been stacking vertically since the day it was written, including the QR and
   the code field on the two-step sign-in card. Deliberately just the display:
   each site already brings its own spacing. */
.row{display:flex}
/* Three more names that read as design and produced nothing. Found by the
   ratchet in tests/css_classes_test.php after `.pill` and `.row`, and each is
   the same shape: markup that states an intention no rule ever answered.

   .mailoff is a web domain with no email set up, listed among domains that
   have it. Nothing distinguished the two, so a row saying "nothing accepts
   mail for it here" looked exactly like a working one -- the fleet board's
   fault again, on another page.

   .grid2 wraps two cards that were meant to sit side by side and stacked.
   .pagehead-actions holds the buttons in a page header. */
.mailoff td{opacity:.62}
.mailoff td:last-child{opacity:1}
.grid2{display:grid;grid-template-columns:repeat(2,1fr);gap:14px}
@media(max-width:1020px){.grid2{grid-template-columns:1fr}}
.pagehead-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.server-chip{display:flex;align-items:center;gap:7px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:5px 11px;font-size:12px;color:var(--text)}
.server-chip .pulse{width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 0 3px var(--ok-soft)}
.avatar{width:30px;height:30px;border-radius:50%;background:var(--primary);display:grid;place-items:center;color:#fff;font-weight:700;font-size:12.5px}

/* ---------- layout ---------- */
.shell{display:flex;min-height:calc(100vh - 52px);max-width:1760px;margin:0 auto}
.side{width:216px;padding:14px 10px;background:var(--surface);border-right:1px solid var(--border);flex-shrink:0}
.nav-label{font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);font-weight:700;padding:8px 12px 4px}
.nav a{display:flex;align-items:center;gap:10px;padding:9px 12px;border-radius:9px;color:var(--text);font-weight:600;font-size:13px;margin-bottom:2px;cursor:pointer;transition:.13s}
.nav a:hover{background:var(--bg);color:var(--ink)}
.nav a.on{background:var(--primary-soft);color:var(--primary);box-shadow:inset 3px 0 0 var(--primary)}
.nav .ico{width:17px;height:17px;flex-shrink:0;opacity:.85}
.main{flex:1;padding:22px 26px;min-width:0}
.crumb{color:var(--muted);font-size:12px;margin-bottom:3px}
.crumb b{color:var(--text);font-weight:600}
h1{color:var(--ink);font-size:19px;font-weight:700;margin-bottom:16px;letter-spacing:-.01em}
h1 small{color:var(--muted);font-weight:400;font-size:12.5px;margin-left:8px}

/* ---------- cards ---------- */
.grid{display:grid;gap:14px}
.g4{grid-template-columns:repeat(4,1fr)}
.g3{grid-template-columns:repeat(3,1fr)}
.g2{grid-template-columns:repeat(2,1fr)}
.card{background:var(--card);border:1px solid var(--border);border-radius:var(--r);padding:16px 18px;box-shadow:var(--shadow)}
.card h3{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.07em;margin-bottom:12px;font-weight:700;display:flex;align-items:center;gap:6px}
.card .big{color:var(--ink);font-size:21px;font-weight:800;letter-spacing:-.02em}

/* stat card */
.stat{display:flex;align-items:center;gap:14px}
.stat .meta{min-width:0}
.statbar{margin-top:10px}

/* rings - thin, blue, on white */
.ring{--p:0;width:64px;height:64px;border-radius:50%;background:conic-gradient(var(--primary) calc(var(--p)*1%),var(--track) 0);display:grid;place-items:center;flex-shrink:0;position:relative;transition:--p 1s ease}
.ring::before{content:"";position:absolute;width:52px;height:52px;border-radius:50%;background:var(--card)}
.ring b{position:relative;color:var(--ink);font-size:13.5px;font-weight:700}
.ring.warn{background:conic-gradient(var(--warn) calc(var(--p)*1%),var(--track) 0)}

/* bars */
.bar{height:6px;border-radius:99px;background:var(--border);overflow:hidden}
.bar i{display:block;height:100%;border-radius:99px;background:var(--primary);width:0;transition:width .9s ease}
.bar i.warn{background:var(--warn)}
.bar i.bad{background:var(--bad)}

/* ---------- tags (status = tinted bg + icon + label) ---------- */
.tag{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:6px;font-size:11.5px;font-weight:700}
.tag.ok{background:var(--ok-soft);color:var(--ok)}
.tag.warn{background:var(--warn-soft);color:var(--warn)}
.tag.bad{background:var(--bad-soft);color:var(--bad)}
.tag.info{background:var(--info-soft);color:var(--info)}
.tag.plain{background:var(--bg);color:var(--text);border:1px solid var(--border)}

/* ---------- tables ---------- */
.tblcard{padding:0;overflow:hidden}
.tblhead{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid var(--border);flex-wrap:wrap}
.tblhead h2{color:var(--ink);font-size:14.5px;font-weight:700;margin-right:auto}
table{width:100%;border-collapse:collapse}
th{color:var(--muted);font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;text-align:left;padding:9px 16px;background:var(--surface-2);border-bottom:1px solid var(--border);font-weight:700}
td{padding:12px 16px;border-bottom:1px solid var(--border);color:var(--text);vertical-align:middle}
tr:last-child td{border-bottom:0}
tbody tr{transition:.12s}
tbody tr:hover{background:var(--surface-2)}
.rowid{display:flex;align-items:center;gap:10px}
.rowav{width:32px;height:32px;border-radius:8px;display:grid;place-items:center;font-weight:700;font-size:13px;background:var(--primary-soft);color:var(--primary);flex-shrink:0}

/* ---------- buttons & inputs ---------- */
.btn{border:0;border-radius:9px;padding:8px 14px;font-weight:600;font-size:13px;cursor:pointer;background:var(--primary);color:#fff;box-shadow:var(--shadow);transition:.13s}
.btn:hover{background:var(--primary-deep)}
.btn.ghost{background:var(--surface);color:var(--text);border:1px solid var(--border-strong);box-shadow:none}
.btn.ghost:hover{border-color:var(--primary);color:var(--primary);background:var(--primary-soft)}
.btn.sm{padding:5.5px 10px;font-size:12px;border-radius:8px}
.inp,select.inp{background:var(--surface);border:1px solid var(--border-strong);border-radius:9px;color:var(--ink);padding:8px 12px;font-size:13px;outline:none;transition:.13s}
.inp:focus{border-color:var(--primary);box-shadow:0 0 0 3px var(--primary-soft)}

/* ---------- services grid ---------- */
.svc{display:flex;flex-wrap:wrap;gap:8px}

/* ---------- login ---------- */
.loginwrap{min-height:100vh;display:grid;place-items:center;padding:20px;background:var(--bg)}
.loginbox{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:34px 30px;width:min(92vw,380px);box-shadow:var(--shadow-lg);text-align:center}
.loginbox .logo{justify-content:center;font-size:20px;margin-bottom:6px}
.loginbox p{color:var(--muted);font-size:12.5px;margin-bottom:20px}
.loginbox .inp{width:100%;margin-bottom:10px;text-align:left}
.loginbox .btn{width:100%;margin-top:6px;padding:10px}
.alertbox{background:var(--bad-soft);border:1px solid var(--bad-line);color:var(--bad);border-radius:9px;padding:9px 12px;font-size:12.5px;margin-bottom:12px;text-align:left}

.note{margin-top:20px;font-size:11px;color:var(--muted);text-align:center}

@media(max-width:860px){
  .shell{flex-direction:column}
  .side{width:100%;border-right:0;border-bottom:1px solid var(--border);padding:8px 10px}
  .nav{display:flex;gap:4px;overflow-x:auto;-webkit-overflow-scrolling:touch}
  .nav-label{display:none}
  .nav a{white-space:nowrap;padding:8px 12px}
  .nav a.on{box-shadow:inset 0 -2px 0 var(--primary)}
  .g4,.g3{grid-template-columns:repeat(2,1fr)}
  .g2{grid-template-columns:1fr}
  .main{padding:14px}
  .server-chip{display:none}
}
@media(max-width:470px){.g4,.g3{grid-template-columns:1fr 1fr}}

.ringwrap{display:flex;align-items:center;gap:14px}

/* ===== v2.1 - calibration against the real 1Panel reference =====
 * (accent/bg/border tokens folded into the :root block at the top of this file) */
.side{background:var(--surface);border-right:1px solid var(--border)}
.nav a.on{background:var(--surface);border:1px solid var(--primary-line);box-shadow:0 1px 4px rgba(61,110,247,.14)}
.nav a{border:1px solid transparent}
.card{border-color:var(--border);box-shadow:0 1px 2px rgba(16,24,40,.04)}

.sect{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:700;color:var(--ink);margin:0 0 14px}
.sect::before{content:"";width:4px;height:14px;border-radius:2px;background:var(--primary)}

.cols{display:grid;grid-template-columns:1fr 330px;gap:14px;align-items:start}
.qrow{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;text-align:center}
.qstat .lbl{color:var(--lbl);font-size:13px;margin-bottom:6px}
.qstat .bignum{color:var(--primary);font-size:26px;font-weight:700;letter-spacing:-.02em}

.ringrow{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;text-align:center;justify-items:center}
.ring{width:96px;height:96px;background:conic-gradient(var(--primary) calc(var(--p)*1%),var(--track) 0)}
.ring::before{width:84px;height:84px}
.ring b{font-size:17px;font-weight:700}
.ring b .pct{font-size:11px;color:var(--muted);font-weight:600;margin-left:1px}
.ringlabel{margin-top:9px;color:var(--ink);font-weight:600;font-size:13px}
.ringsub{color:var(--muted);font-size:11.5px;margin-top:2px}

.chipline{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
.chipline .tag{background:var(--surface);border:1px solid var(--primary-line);color:var(--primary)}
.tabs{display:inline-flex;background:var(--surface-2);border-radius:9px;padding:3px;margin-left:auto}
.tabs b{padding:5px 14px;border-radius:7px;font-size:12.5px;font-weight:600;color:var(--text);cursor:default}
.tabs b.on{background:var(--primary);color:#fff}
.monhead{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:10px}

.chart line.grid{stroke:var(--track);stroke-width:1}
.chart .axis{fill:var(--muted);font-size:10px}
.chart .line{fill:none;stroke:var(--primary);stroke-width:1.8;stroke-linejoin:round}
.chart .fill{fill:#3d6ef7;opacity:.14}

.kv{width:100%}
.kv div{display:flex;gap:16px;padding:7.5px 0;border-bottom:1px solid var(--surface-2);font-size:12.5px}
.kv div:last-child{border-bottom:0}
.kv dt{color:var(--muted);min-width:100px;flex-shrink:0}
.kv dd{color:var(--ink);font-weight:600;margin:0;word-break:break-all}

.approw{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid var(--surface-2)}
.approw:last-child{border-bottom:0}
.appic{width:36px;height:36px;border-radius:9px;background:var(--primary-soft);color:var(--primary);display:grid;place-items:center;font-weight:800;font-size:14px;flex-shrink:0}
.appmeta{min-width:0;margin-right:auto}
.appmeta b{color:var(--ink);font-size:13px;display:block}
.applinks{font-size:11.5px;color:var(--muted)}

@media(max-width:1020px){.cols{grid-template-columns:1fr}}
@media(max-width:640px){.qrow,.ringrow{grid-template-columns:repeat(2,1fr)}}

/* ===== v2.2 - design kit page + missing primitives ===== */
.btn.danger{background:var(--bad);color:#fff}
.btn.danger:hover{background:#b91c1c}
.btn[disabled]{opacity:.5;cursor:not-allowed}
.toast{background:var(--surface);border:1px solid var(--border-strong);border-left:4px solid var(--primary);color:var(--ink);padding:10px 14px;border-radius:10px;box-shadow:var(--shadow-lg);font-size:13px;display:inline-block}
.toast.ok{border-left-color:var(--ok)} .toast.bad{border-left-color:var(--bad)}
.empty{text-align:center;padding:34px 16px;color:var(--muted)}
/* An empty state with something to READ, as against .empty, which centres one
   short line under a glyph. Centring three lines of prose makes every line
   start in a different place, so here the glyph sits BESIDE the text and the
   text ranges left. Bounded width, because a sentence that crosses a 1700px
   card is not a sentence anyone finishes. */
.blankstate{display:flex;gap:16px;align-items:flex-start;padding:24px 20px;max-width:780px}
.blankstate .eico{width:42px;height:42px;border-radius:12px;background:var(--bg);display:grid;place-items:center;flex:none;margin-top:1px}
.blankstate .eico .eico-svg{width:21px;height:21px;color:var(--muted)}
.blankstate h3{color:var(--ink);font-size:14px;font-weight:700;margin:0 0 6px;text-transform:none;letter-spacing:normal;display:block}
.blankstate p{color:var(--muted);font-size:12.5px;line-height:1.6;margin:0 0 8px}
.blankstate p:last-child{margin:0}
@media(max-width:560px){.blankstate{flex-direction:column;gap:12px}}
.empty .eico{width:44px;height:44px;border-radius:12px;background:var(--bg);display:grid;place-items:center;margin:0 auto 10px}
/* The empty-state glyph. Was a font-size, because these used to be emoji drawn
   by the operating system; it is a stroke-drawn icon now, so it takes the
   accent like every other mark in the panel. */
.empty .eico .eico-svg{width:22px;height:22px;color:var(--muted)}
.switch{position:relative;width:36px;height:20px;display:inline-block;vertical-align:middle}
.switch input{opacity:0;width:0;height:0}
.switch i{position:absolute;inset:0;background:var(--border-strong);border-radius:99px;transition:.15s;cursor:pointer}
.switch i::after{content:"";position:absolute;left:3px;top:3px;width:14px;height:14px;border-radius:50%;background:var(--surface);transition:.15s;box-shadow:0 1px 2px rgba(0,0,0,.2)}
.switch input:checked+i{background:var(--primary)}
.switch input:checked+i::after{transform:translateX(16px)}
/* kit page helpers */
.kit-note{font-size:12px;color:var(--muted);margin:8px 0 0;padding-left:10px;border-left:2px solid var(--border-strong)}
.swatches{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px}
.swatch{border:1px solid var(--border);border-radius:10px;overflow:hidden;background:var(--surface)}
.swatch .c{height:44px}
.swatch .m{padding:7px 9px;font-size:11px}
.swatch .m b{display:block;color:var(--ink);font-size:11.5px}
.kitrow{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.kitgrid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(max-width:900px){.kitgrid{grid-template-columns:1fr}}

/* ===== service rows: name+state on top, buttons one line below ===== */
/* One line per service. The actions used to sit on a second line under the
   name, so eight services filled the card with sixteen rows and the thing being
   scanned for — the name — was never on a line of its own. */
.svc-row{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid var(--surface-2)}
.svc-top{flex:1;min-width:0}
.svc-row:last-child{border-bottom:0}
.svc-top{display:flex;align-items:center;gap:10px}
.svc-name{color:var(--ink);font-size:13px;margin-right:auto}
.svc-actions{display:flex;gap:6px;flex:none;align-items:center}

/* Which of start/stop exists at all is decided here, from one attribute on the
   row, so the live poll swaps them by setting data-state and never has to
   rebuild a button. Both were previously always on screen and one of them could
   only ever fail: a Start button on a running service does nothing but produce
   an error. */
.svc-row[data-state="running"] .svc-start,
.svc-row[data-state="stopped"] .svc-stop,
.svc-row[data-state="unknown"] .svc-start,
.svc-row[data-state="unknown"] .svc-stop,
.svc-row:not([data-state="unknown"]) .svc-unknown{display:none}
.svc-unknown{font-size:11.5px;color:var(--warn);white-space:nowrap;align-self:center}
/* status: a coloured dot only (green running / red down / amber acting); title on hover */
.svc-dot{width:9px;height:9px;border-radius:50%;flex-shrink:0;display:inline-block}
.svc-dot.ok{background:var(--ok);box-shadow:0 0 0 3px var(--ok-soft)}
.svc-dot.bad{background:var(--bad);box-shadow:0 0 0 3px var(--bad-soft)}
.svc-dot.warn{background:var(--warn);box-shadow:0 0 0 3px var(--warn-soft)}
/* actions: icon-only, colour-coded; native title shows on hover */
.svc-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:30px;flex:0 0 auto;
  border:1px solid var(--border-strong);background:var(--surface);color:var(--muted);border-radius:8px;cursor:pointer;transition:.12s}
.svc-btn svg{width:15px;height:15px;flex-shrink:0}
.svc-btn[data-a="start"]{color:var(--ok)}
.svc-btn[data-a="stop"]{color:var(--bad)}
.svc-btn[data-a="restart"]{color:var(--warn)}
.svc-btn[data-a="logs"]{color:var(--muted)}
.svc-btn:hover{background:var(--bg);border-color:var(--muted)}
.svc-btn[data-a="start"]:hover{background:var(--ok-soft);border-color:var(--ok)}
.svc-btn[data-a="stop"]:hover{background:var(--bad-soft);border-color:var(--bad)}
.svc-btn[data-a="restart"]:hover{background:var(--warn-soft);border-color:var(--warn)}
.svc-id{display:flex;flex-direction:column;gap:1px;margin-right:auto;min-width:0}
.svc-id .svc-name{margin:0}
.svc-sub{color:var(--muted);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.appic.php{background:var(--primary-soft);color:#4f46e5;font-size:12.5px}
.svc-viewonly{display:inline-flex;align-items:center;justify-content:center;width:34px;height:30px;flex:0 0 auto;border:1px dashed var(--border-strong);color:var(--muted);border-radius:8px}
.svc-viewonly svg{width:15px;height:15px;flex-shrink:0}
.php-row .svc-actions{flex-wrap:wrap}
.svc-btn.php-info,.svc-btn.php-config{flex:0 0 auto}
.svc-btn.php-config:hover,.svc-btn.php-info:hover{border-color:var(--primary);color:var(--primary);background:var(--primary-soft)}
.chip-k{color:var(--muted);font-weight:600}

/* destination tab strip: exactly one selected, unlike a checkbox group */
.dtabs{display:flex;flex-wrap:wrap;gap:6px;border-bottom:1px solid var(--border);padding-bottom:0;margin-bottom:22px}
.dtab{display:inline-flex;align-items:center;gap:7px;padding:9px 14px;border:1px solid var(--border);border-bottom:none;
  border-radius:9px 9px 0 0;background:var(--bg);color:var(--muted);font-size:13px;font-weight:600;cursor:pointer;
  position:relative;top:1px}
.dtab:hover{color:var(--ink);border-color:var(--border-strong)}
.dtab.on{background:var(--card);color:var(--primary);border-color:var(--border);box-shadow:0 -1px 0 var(--primary) inset}
.dtab.on::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:2px;background:var(--card)}
.dtag{font-size:10.5px;font-weight:700;padding:2px 6px;border-radius:5px;background:var(--border);color:var(--muted)}
.dtag.ok{background:var(--ok-soft);color:var(--ok)}
/* Forms are easier to read at a sane measure than stretched across a 1900px
   card, and each pane needs air under the tab strip. */
/* Inside a padded .card the pane needs no side padding of its own, but it does
   need air under the tab strip and after the last control. */
.dpane{padding:2px 0 4px;max-width:760px}
.dpane .fgrid{gap:14px 18px;margin-top:6px}
.dpane .note-soft{margin-top:12px;margin-bottom:0}
.dpane > .sub{line-height:1.5}
/* The explanatory line under a field, and the row of buttons after it. */
/* Was `.dpane .fieldhint`. No view has emitted .dpane since the backup-jobs
   page was rewritten, so every fieldhint on every page rendered as a plain
   paragraph at body size -- including the "without this the report is public"
   warning on the Visitors card. Unscoped. */
.fieldhint{font-size:11.5px;color:var(--muted);line-height:1.45;margin-top:7px}
.dpane .actions{display:flex;gap:8px;margin-top:18px;margin-bottom:2px;flex-wrap:wrap}
@media(max-width:640px){.dtabs{border-bottom:0}.dtab{border-bottom:1px solid var(--border);border-radius:9px;top:0}}

/* removable chips (domain aliases) */
.chiprow{display:flex;flex-wrap:wrap;gap:6px;min-height:30px;align-items:flex-start}
.chip{display:inline-flex;align-items:center;gap:6px;padding:4px 6px 4px 10px;border:1px solid var(--border);border-radius:999px;font-size:12px;color:var(--ink);background:var(--bg)}
.chip-x{display:inline-flex;align-items:center;justify-content:center;width:17px;height:17px;border:0;border-radius:50%;background:transparent;color:var(--muted);font-size:14px;line-height:1;cursor:pointer;padding:0}
.chip-x:hover{background:var(--bad-soft);color:var(--bad)}

/* .modal.logmodal, not .logmodal.
   Both are single-class selectors, and .modal is defined LATER in this file —
   so its width:min(94vw,440px) won every time and the log opened at 440px with
   every line wrapped three ways. Raising the specificity fixes it wherever the
   rules end up sitting.

   Wider and taller than before as well: a log line is long, and a window that
   shows eight of them is a window you scroll rather than read. */
.modal.logmodal{width:min(96vw,1180px);padding:0;overflow:hidden}
.logmodal-head{display:flex;align-items:center;justify-content:space-between;padding:13px 16px;border-bottom:1px solid var(--border)}
.logmodal-head b{color:var(--ink);font-size:14px}
.logbody{margin:0;padding:14px 16px;max-height:74vh;overflow:auto;background:#0d1420;color:var(--border-strong);
  font-family:"SF Mono",Menlo,Consolas,monospace;font-size:12px;line-height:1.55;white-space:pre-wrap;word-break:break-word}

/* ===== overlay + modal + hidden (were missing from v2 - caused inline render) ===== */
.hidden{display:none!important}
.overlay{position:fixed;inset:0;z-index:120;background:rgba(15,22,34,.5);backdrop-filter:blur(3px);display:grid;place-items:center;padding:20px}
/* A modal taller than the window was unreachable, not merely ugly. The overlay
   is position:fixed — it never scrolls — and a grid item centred inside one
   overflows off BOTH ends, so the plan editor's Advanced limits section had a
   Save button nothing could scroll to. The modal carries its own scrollbar now;
   overscroll-behavior keeps that scroll from chaining to the page behind it. */
.modal{background:var(--card);border:1px solid var(--border);border-radius:16px;box-shadow:var(--shadow-lg);width:min(94vw,440px);padding:20px;animation:pop .18s ease;max-height:calc(100vh - 40px);max-height:calc(100dvh - 40px);overflow-y:auto;overscroll-behavior:contain}
/* The footer is the reason for the scrollbar, so it must not scroll away with
   the rest: Cancel and Save stay on screen at the bottom of a long form. */
.modal .modal-foot{position:sticky;bottom:0;background:var(--card);padding-top:14px}
@keyframes pop{from{transform:translateY(8px);opacity:.6}to{transform:none;opacity:1}}

/* svc icon hardening + mobile row layout */
@media(max-width:860px){
  /* Narrow: the actions drop below the name rather than squeezing it to
     nothing. The one-line arrangement is for the widths where it fits. */
  .svc-row{flex-wrap:wrap}
  .svc-top{flex:1 0 100%}
  .svc-row .svc-state{margin-left:auto}
  .svc-actions{width:100%;margin-top:8px}
  .svc-btn,.svc-viewonly{width:40px;height:34px}
}

/* ===== client row actions + modal + toasts ===== */
.row-actions{display:flex;gap:4px;justify-content:flex-end}
.ibtn{display:inline-grid;place-items:center;width:30px;height:30px;border:1px solid var(--border-strong);background:var(--surface);color:var(--muted);border-radius:8px;font-size:14px;cursor:pointer;transition:.12s;text-decoration:none}
.ibtn:hover{border-color:var(--primary);color:var(--primary);background:var(--primary-soft)}
.ibtn[data-a="suspend"]:hover{border-color:var(--warn);color:var(--warn);background:var(--warn-soft)}
.ibtn[data-a="unsuspend"]:hover{border-color:var(--ok);color:var(--ok);background:var(--ok-soft)}
.ibtn[disabled]{opacity:.5;cursor:wait}
.modal-h{color:var(--ink);font-size:16px;margin-bottom:14px}
.lbl{display:block;color:var(--muted);font-size:12px;margin:14px 0 6px;font-weight:600}
/* A hint inside a label must not read as part of the label. .lbl and .sub were
   both 12px/muted, so "Directory" + "hestia.conf BACKUP" ran together as one
   phrase. Lighter, spaced, and slightly smaller separates them. */
.lbl .sub{font-weight:400;font-size:11.5px;opacity:.85;margin-left:7px}
.lbl .sub::before{content:"· "}
.inp.w{width:100%}
.modal-foot{display:flex;gap:10px;justify-content:flex-end;margin-top:18px}
#toasts{position:fixed;bottom:18px;right:18px;display:flex;flex-direction:column;gap:8px;z-index:200}
.toast{background:var(--surface);border:1px solid var(--border-strong);border-left:4px solid var(--ok);color:var(--ink);padding:10px 14px;border-radius:10px;box-shadow:var(--shadow-lg);font-size:13px;animation:pop .2s ease}
.toast.bad{border-left-color:var(--bad)}

/* ===== client detail ===== */
.detail-head{display:flex;align-items:center;gap:14px;margin-bottom:18px;flex-wrap:wrap}
.rowav.lg{width:52px;height:52px;border-radius:14px;font-size:22px}
.detail-actions{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}
.detail-head h1 .tag{margin-left:8px;vertical-align:middle}

/* ===== nav text icons, packages, restore options ===== */
.ico.ni{display:inline-grid;place-items:center;width:17px;font-size:13px;opacity:.7}
.pkg{display:flex;flex-direction:column}
.pkg-name{font-weight:700;color:var(--ink);font-size:14px}
.pkg-disk{color:var(--primary);font-size:22px;font-weight:800;margin-top:6px}
.pkg-add{cursor:pointer;border-style:dashed;justify-content:center}
.pkg-add:hover{border-color:var(--primary);background:var(--primary-soft)}
.ropt{display:flex;align-items:center;gap:9px;padding:9px 11px;border:1px solid var(--border);border-radius:9px;margin-bottom:7px;cursor:pointer;font-size:13px}
.ropt:hover{border-color:var(--primary);background:var(--primary-soft)}

/* inline button icons (replace emoji) */
.bi{width:14px;height:14px;flex-shrink:0;vertical-align:-2px}
.ibtn .bi{width:15px;height:15px}
.tag .bi{width:12px;height:12px}
.btn .bi,.btn.ghost .bi{margin-right:2px}
.ibtn{gap:0}
.livedot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--ok);margin-right:2px;vertical-align:0}

/* ===== top bar: hamburger, bell, user menu, drawer sidebar ===== */
.hamb{display:none;background:none;border:0;cursor:pointer;color:var(--ink);padding:6px;margin-right:-4px;border-radius:8px}
.hamb svg{width:22px;height:22px}
.hamb:hover{background:var(--bg)}
.bell-wrap,.user-wrap{position:relative}
.bell{position:relative;background:none;border:1px solid transparent;cursor:pointer;color:var(--text);padding:6px;border-radius:9px;display:grid;place-items:center}
.bell svg{width:19px;height:19px}
.bell:hover{background:var(--bg);color:var(--ink)}
.bell-badge{position:absolute;top:0;right:0;min-width:15px;height:15px;padding:0 3px;background:var(--bad);color:#fff;border-radius:99px;font-size:9.5px;font-weight:800;display:grid;place-items:center;line-height:1;border:2px solid #fff}
.bell-panel,.user-panel{position:absolute;top:calc(100% + 8px);right:0;width:280px;background:var(--surface);border:1px solid var(--border);border-radius:12px;box-shadow:var(--shadow-lg);z-index:80;overflow:hidden}
.user-panel{width:210px}
.bell-head{padding:11px 14px;border-bottom:1px solid var(--border);font-weight:700;color:var(--ink);font-size:13px;display:flex;justify-content:space-between;align-items:center}
.bell-list{max-height:340px;overflow:auto}
.bell-item{display:flex;align-items:center;gap:10px;padding:10px 14px;border-bottom:1px solid var(--border);color:var(--text);font-size:12.5px}
.bell-item:last-child{border-bottom:0}
.bell-item:hover{background:var(--bg)}
.bell-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.bell-dot.bad{background:var(--bad)} .bell-dot.warn{background:var(--warn)} .bell-dot.ok{background:var(--ok)}
.bell-empty{padding:22px 14px;text-align:center;color:var(--muted);font-size:12.5px}
.avatar{border:0;cursor:pointer}
.user-head{padding:12px 14px;border-bottom:1px solid var(--border)}
.user-item{display:flex;align-items:center;gap:9px;padding:10px 14px;color:var(--text);font-size:13px;font-weight:600;border-bottom:1px solid var(--border)}
.user-item:last-child{border-bottom:0}
.user-item:hover{background:var(--bg);color:var(--ink)}
.user-item.danger{color:var(--bad)} .user-item.danger:hover{background:var(--bad-soft)}
.scrim{position:fixed;inset:0;background:rgba(15,22,34,.4);z-index:60}
.scrim.hidden{display:none}

/* drawer behavior on small screens */
@media(max-width:860px){
  .hamb{display:grid;place-items:center}
  .side{position:fixed;top:52px;bottom:0;left:0;width:240px;z-index:70;transform:translateX(-100%);transition:transform .22s ease;border-right:1px solid var(--border)}
  .side.open{transform:none;box-shadow:20px 0 50px rgba(0,0,0,.15)}
  .nav{display:block}
  .nav a{white-space:normal}
  .nav-label{display:block}
}
@media(min-width:861px){ .scrim{display:none!important} }

/* ===== confirm modal ===== */
.confirm-modal{text-align:center;width:min(94vw,400px)}
.confirm-icon{width:48px;height:48px;border-radius:12px;display:grid;place-items:center;margin:0 auto 14px}
.confirm-icon svg{width:26px;height:26px}
.confirm-icon.danger{background:var(--bad-soft);color:var(--bad)}
.confirm-icon.info{background:var(--primary-soft);color:var(--primary)}
.confirm-modal .modal-h{text-align:center}
.confirm-body{color:var(--text);font-size:13px;margin-bottom:4px;white-space:pre-line;
  word-break:break-word;max-height:34vh;overflow:auto;text-align:left}
/* A one-line body reads as a sentence and belongs centred with the heading; a
   list of paths does not. Centring a list makes it unreadable. */
.confirm-body:not(:empty){text-align:left}
.confirm-modal .modal-foot{justify-content:center}
/* The acknowledgement on a destructive confirm. A second click is a reflex; a
   tick is a decision, and the confirm button stays disabled until it is made.
   Left-aligned inside a centred modal on purpose — it is a sentence to read,
   not a heading to glance at. */
.confirm-ack{display:flex;gap:10px;align-items:flex-start;text-align:left;margin:14px 0 2px;
  padding:11px 12px;border:1px solid var(--bad);border-radius:9px;background:var(--bad-soft);
  color:var(--bad);font-size:12.5px;line-height:1.5;cursor:pointer}
.confirm-ack input{flex:none;margin-top:1px;width:15px;height:15px;accent-color:var(--bad)}
.confirm-modal .btn[disabled]{opacity:.5;cursor:not-allowed}

/* ===== sortable table headers ===== */
th.th-sort{cursor:pointer;user-select:none;white-space:nowrap;position:relative}
th.th-sort:hover{color:var(--ink);background:var(--surface-2)}
th.th-sort::after{content:"";display:inline-block;width:0;height:0;margin-left:5px;vertical-align:middle;
  border-left:3.5px solid transparent;border-right:3.5px solid transparent;opacity:.35;
  border-bottom:5px solid currentColor}
th.th-sort.sorted{color:var(--primary)}
th.th-sort.sorted::after{opacity:1;border-bottom-color:var(--primary)}
th.th-sort.sorted[data-dir="desc"]::after{border-bottom:0;border-top:5px solid var(--primary)}

/* buttons never wrap label + icon */
.btn{white-space:nowrap}
.btn .bi{flex:0 0 auto}
td .btn.ghost.sm, td .ibtn{vertical-align:middle}

/* ===== dashboard server-info panel ===== */
.srvinfo .kv dt{min-width:92px}
.srvinfo .kv dd{word-break:normal;overflow-wrap:anywhere}
/* key-value cards need full width on phones/tablets, not the 2-up g3 grid */
@media(max-width:820px){.srvinfo{grid-template-columns:1fr}}

/* dashboard: storage-by-client, recent activity, attention */
.usg{margin-bottom:12px}
.usg:last-child{margin-bottom:0}
.usg-top{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:5px;font-size:13px}
.usg-top a{color:var(--ink);font-weight:600}
.usg-top a:hover{color:var(--primary)}
.actrow{display:flex;gap:10px;align-items:flex-start;padding:9px 0;border-bottom:1px solid var(--surface-2)}
.actrow:last-child{border-bottom:0}
.actdot{width:8px;height:8px;border-radius:50%;margin-top:5px;flex-shrink:0;background:var(--ok)}
.actdot.bad{background:var(--bad)}
.actmeta{min-width:0;font-size:13px}
.alrow{display:flex;align-items:center;gap:9px;padding:8px 0;border-bottom:1px solid var(--surface-2);color:var(--text);font-size:13px}
.alrow:last-child{border-bottom:0}
.alrow:hover{color:var(--ink)}
.svctabs b{cursor:pointer}
.tabdot{display:none;width:7px;height:7px;border-radius:50%;background:var(--bad);margin-left:6px;vertical-align:middle}
.tabdot.show{display:inline-block}
.tabs b.on .tabdot{background:#fff} /* on the accent: white in both themes, the ONE literal the theme test allows */

/* dashboard compact rows */
.drow{display:grid;gap:14px;margin-bottom:14px;align-items:start}
.drow.two{grid-template-columns:1fr 1fr}
.drow.mon{grid-template-columns:1.7fr 1fr}
.drow.three{grid-template-columns:1fr 1fr 1fr}
.drow.qarow{grid-template-columns:1.6fr 1fr}
.drow .card{margin:0!important}
@media(max-width:1100px){.drow.three{grid-template-columns:1fr 1fr}}
@media(max-width:900px){.drow.two,.drow.mon,.drow.three,.drow.qarow{grid-template-columns:1fr}}

/* quick actions */
.qa{display:flex;gap:10px;flex-wrap:wrap}
.qa-btn{flex:1;min-width:122px;display:flex;flex-direction:column;gap:1px;padding:9px 12px;border:1px solid var(--border-strong);border-radius:10px;text-decoration:none;background:var(--surface);transition:.12s}
.qa-btn:hover{border-color:var(--primary);background:var(--primary-soft)}
.qa-btn .qa-ic{width:26px;height:26px;border-radius:7px;background:var(--primary-soft);color:var(--primary);display:grid;place-items:center;margin-bottom:4px}
.qa-btn .qa-ic svg{width:15px;height:15px}
.qa-btn b{color:var(--ink);font-size:13px}
/* overview stat tiles (clickable, icon) + equal-height paired rows */
.drow.eq{align-items:stretch}
.drow.eq .card{display:flex;flex-direction:column}
.drow.eq .qrow,.drow.eq .ringrow{flex:1;align-content:center}
a.qstat{text-decoration:none;border-radius:10px;padding:8px 4px;transition:.12s}
a.qstat:hover{background:var(--primary-soft)}
.qic{display:grid;place-items:center;width:34px;height:34px;border-radius:9px;background:var(--primary-soft);color:var(--primary);margin:0 auto 8px}
.qic svg{width:18px;height:18px}
/* status ring hover popover */
.ringcell{cursor:default;border-radius:12px;padding:6px 4px}
.statpop{position:fixed;z-index:130;background:var(--surface);border:1px solid var(--border);border-radius:12px;box-shadow:var(--shadow-lg);padding:14px 16px;min-width:250px;max-width:min(92vw,460px);font-size:12.5px;color:var(--text)}
.sp-title{font-weight:800;color:var(--ink);font-size:14px;margin-bottom:10px}
.sp-sub{font-weight:700;color:var(--ink);margin:10px 0 6px;font-size:12.5px}
.sp-row{display:flex;gap:18px;margin-bottom:8px;flex-wrap:wrap}
.sp-row:last-child{margin-bottom:0}
.sp-row>div{min-width:60px}
.sp-k{color:var(--muted);font-size:11px;margin-bottom:2px}
.sp-v{color:var(--ink);font-weight:600}
.sp-cores{display:flex;flex-wrap:wrap;gap:5px 14px}
.sp-core{font-size:12px;color:var(--text)}
.sp-core b{color:var(--primary)}
/* monitoring chart crosshair + tooltip */
#chartbox{cursor:crosshair}
.chart .chcross{stroke:var(--primary);stroke-width:1;stroke-dasharray:3 3;opacity:.55}
.chart .chdot{fill:var(--primary);stroke:#fff;stroke-width:1.5}
.chart .fill.up{fill:var(--ok);opacity:.15}.chart .line.up{stroke:var(--ok)}
.chart .fill.down{fill:var(--primary);opacity:.15}.chart .line.down{stroke:var(--primary)}
.chtip{position:fixed;z-index:130;display:none;background:var(--ink);color:#fff;border-radius:8px;padding:7px 11px;font-size:12px;pointer-events:none;box-shadow:var(--shadow-lg);white-space:nowrap}
.chtip b{margin-right:8px;color:#fff}
.chtip span{opacity:.7}
.chtip .cht-t{font-weight:700;margin-bottom:4px}
.chtip .cht-r{display:flex;align-items:center;gap:7px;margin-top:2px}
.chtip .cht-r b{margin:0 0 0 4px}
.chtip .cht-dot{width:8px;height:8px;border-radius:50%}
.chtip .cht-dot.up{background:#7bd88f}
.chtip .cht-dot.down{background:#8ab4ff}
/* top processes */
.proctbl{width:100%;font-size:12.5px}
.proctbl th{padding:6px 10px;font-size:10px}
.proctbl td{padding:7px 10px}
.mono{font-family:"SF Mono",Menlo,Consolas,monospace;font-size:12px;color:var(--ink)}
/* ssl / mail health rows */
.ssl-row{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--surface-2);text-decoration:none}
.ssl-row:last-child{border-bottom:0}
.ssl-row:hover .ink{color:var(--primary)}
.ssl-dom{margin-right:auto;min-width:0;font-size:13px}
/* panel status */
.qstat2{display:grid;grid-template-columns:1fr 1fr;gap:10px;text-align:center;margin-bottom:12px}
.qstat2.q4{grid-template-columns:repeat(4,1fr);gap:8px}
.qstat2 .bignum{color:var(--primary);font-size:24px;font-weight:800;letter-spacing:-.02em}
.qstat2 .bignum.bn-sm{font-size:16px}
.qstat2 .bignum .pct{font-size:14px;color:var(--muted);font-weight:600;margin-left:1px}
.pm-head{margin-left:auto;display:inline-flex;gap:6px;align-items:center;flex-wrap:wrap;justify-content:flex-end}
.pm-updbtn{cursor:pointer;border:1px solid transparent;font:inherit;line-height:inherit}
.pm-updbtn:hover{filter:brightness(.97)}
.pm-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 20px}
.pm-cell{display:flex;flex-direction:column;gap:3px;padding:9px 0;border-top:1px solid var(--border)}
.pm-k{color:var(--muted);font-size:12px}
.pm-v{font-size:13px;color:var(--ink);display:flex;align-items:center;gap:6px;flex-wrap:wrap}
@media(max-width:520px){.qstat2.q4{grid-template-columns:1fr 1fr}.pm-grid{grid-template-columns:1fr}}
.upd-row{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:9px;font-size:13px}
.upd-row.ok{background:var(--ok-soft)}
.upd-row.warn{background:var(--warn-soft)}
.upd-row .btn{margin-left:auto;flex:0 0 auto}

/* ===== packages ===== */
.toolbtns{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.fgrid{display:grid;grid-template-columns:1fr 1fr;gap:10px 12px;margin-top:4px}
.inp.ro{background:var(--bg);color:var(--muted);cursor:not-allowed}
.ibtn[data-a="delete"]:hover{border-color:var(--bad);color:var(--bad);background:var(--bad-soft)}
.ibtn[disabled]{opacity:.45;cursor:not-allowed}
@media(max-width:520px){.fgrid{grid-template-columns:1fr}}

/* ===== backup center ===== */
.cfg-health{display:flex;align-items:center;gap:9px;padding:11px 13px;border-radius:10px;margin-bottom:14px;font-weight:600;font-size:13px}
.cfg-health.ok{background:var(--ok-soft);color:var(--ok)}
.cfg-health.warn{background:var(--warn-soft);color:var(--warn)}
.cfg-health.bad{background:var(--bad-soft);color:var(--bad)}
.cfg-health svg{width:18px;height:18px;flex-shrink:0}
.bk-note{color:var(--muted);font-size:12px;max-width:320px}
.cfg-toggle{display:flex;align-items:center;gap:10px;margin-top:12px;font-size:13px;color:var(--text)}
.php-ini-head{font-weight:700;color:var(--ink);font-size:11px;margin:15px 0 4px;text-transform:uppercase;letter-spacing:.04em}
.phpinfo-body{max-height:60vh;overflow:auto}
.phpinfo-body .kv dd.mono{font-family:"SF Mono",Menlo,Consolas,monospace;font-size:12.5px}
.h24toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%) translateY(10px);background:#0d1420;color:#fff;padding:11px 18px;border-radius:10px;font-size:13px;font-weight:600;box-shadow:var(--shadow-lg);z-index:200;opacity:0;transition:.25s}
.h24toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
/* global stacked toasts (window.h24toast) */
.toast-wrap{position:fixed;right:18px;bottom:18px;z-index:400;display:flex;flex-direction:column;gap:9px;pointer-events:none;max-width:min(92vw,380px)}
.toast{background:#111a2b;color:#fff;padding:11px 15px;border-radius:11px;font-size:13px;font-weight:600;line-height:1.4;box-shadow:0 10px 30px rgba(15,22,34,.28);border-left:3px solid var(--primary);opacity:0;transform:translateY(12px) scale(.98);transition:.24s cubic-bezier(.2,.7,.3,1)}
.toast.show{opacity:1;transform:none}
.toast.ok{border-left-color:var(--ok)}
.toast.bad{border-left-color:var(--bad)}
.toast.warn{border-left-color:var(--warn)}
.toast.info{border-left-color:var(--primary)}
@media(max-width:560px){.toast-wrap{right:10px;left:10px;bottom:10px;max-width:none}}

/* ===== Dashboard v3 — "Triage board" ===== */
/* Zone 2: six rings + live vitals one-liner */
.ringrow.r6{grid-template-columns:repeat(6,1fr)}
.ring.bad{background:conic-gradient(var(--bad) calc(var(--p)*1%),var(--track) 0)}
.vitals-line{font-weight:600;font-variant-numeric:tabular-nums}
/* Zone 3: honest empty chart */
.chart-empty{display:flex;align-items:center;gap:12px;padding:26px 14px;color:var(--muted)}
.chart-empty svg{width:34px;height:34px;flex-shrink:0;opacity:.7}
/* Zone 4: overcommit gauge (a touch stronger than plan bars) */
.usg.overcommit{padding:10px 12px;background:var(--bg);border-radius:10px;margin-bottom:14px}
/* Zone 3: compact estate widget beside Top processes */
.invgrid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.invgrid a{display:flex;align-items:center;gap:11px;padding:12px 13px;border:1px solid var(--border);border-radius:11px;background:var(--card);transition:.14s}
.invgrid a:hover{border-color:var(--primary);background:var(--primary-soft);transform:translateY(-1px);box-shadow:0 4px 12px rgba(61,110,247,.10)}
.invgrid .iv-ic{width:36px;height:36px;border-radius:9px;background:var(--primary-soft);color:var(--primary);display:grid;place-items:center;flex-shrink:0}
.invgrid a:hover .iv-ic{background:var(--surface)}
.invgrid .iv-ic svg{width:19px;height:19px}
.invgrid .iv-t{display:flex;flex-direction:column;line-height:1.05;min-width:0}
.invgrid .iv-n{color:var(--ink);font-size:21px;font-weight:800;letter-spacing:-.02em}
.invgrid .iv-k{color:var(--muted);font-size:12px;margin-top:1px}
/* low-confidence data notes (SSL / mail health) */
.note-soft{font-size:11.5px;color:var(--muted);background:var(--bg);border-radius:8px;padding:7px 10px;margin-bottom:10px;line-height:1.4}
/* navbar alerts / panel-update status chips */
.bell.alerts.has-bad{color:var(--bad)}
.bell.alerts.has-warn{color:var(--warn)}
.bell-badge.warn{background:var(--warn)}
.bell-badge.bad{background:var(--bad)}
.navupd{display:inline-flex;align-items:center;gap:5px;padding:5px 11px;border-radius:8px;font-size:12px;font-weight:700;border:1px solid transparent;white-space:nowrap}
.navupd .bi{width:14px;height:14px}
.navupd.ok{color:var(--ok);background:var(--ok-soft)}
.navupd.avail{color:var(--warn);background:var(--warn-soft);border-color:rgba(217,119,6,.25);cursor:pointer;transition:.12s}
.navupd.avail:hover{background:var(--warn);color:#fff}
@media(max-width:900px){.srvinfo.g3{grid-template-columns:1fr}}
@media(max-width:900px){.ringrow.r6{grid-template-columns:repeat(3,1fr)}}
@media(max-width:640px){.ringrow.r6{grid-template-columns:repeat(2,1fr)}.invgrid{grid-template-columns:1fr}.navupd.ok{display:none}}

/* session-expired bar (layout.php fetch guard) - sits above everything, because
   the operator's next click is otherwise wasted on an already-dead session */
.sessionbar{position:fixed;left:0;right:0;bottom:0;z-index:9999;display:flex;
  align-items:center;justify-content:center;gap:14px;flex-wrap:wrap;
  padding:13px 18px;background:var(--warn-soft);color:var(--warn-ink);
  border-top:2px solid var(--warn);font-size:13px;
  box-shadow:0 -6px 22px rgba(0,0,0,.10);
  transform:translateY(100%);transition:transform .22s ease}
.sessionbar.show{transform:none}
.sessionbar .btn{flex:none}

/* ---- DNS server page (views/dns-server.php) ---------------------------------
   Nameservers are edited per account, so each account is its own bordered block
   rather than a row in a table: the four NS fields belong together visually, and
   a table would put NS3/NS4 in columns that are empty for most accounts. */
.nsblock{border:1px solid var(--border);border-radius:10px;padding:12px 13px;margin-bottom:10px}
.nshead{display:flex;align-items:center;gap:10px;margin-bottom:9px;flex-wrap:wrap}
.nshead .sub{margin-left:auto}
.nsfoot{display:flex;align-items:center;gap:10px;margin-top:10px}
.nsfoot .sub{margin-right:auto}
.nstop{margin-top:18px}
.nsgap{margin:8px 0 0}
.svtbl{width:100%;border-collapse:collapse;font-size:12.5px;margin-bottom:4px}
.svtbl th{text-align:left;color:var(--muted);font-weight:600;font-size:11.5px;
  text-transform:uppercase;letter-spacing:.04em;padding:6px 8px;border-bottom:1px solid var(--border)}
.svtbl td{padding:9px 8px;border-bottom:1px solid var(--surface-2);vertical-align:middle}
.svtbl tr:last-child td{border-bottom:0}
.svacts{display:flex;gap:6px;justify-content:flex-end;flex-wrap:wrap}
@media(max-width:700px){.svtbl thead{display:none}
  .svtbl td{display:block;border:0;padding:3px 0}
  .svtbl tr{display:block;padding:10px 0;border-bottom:1px solid var(--border)}
  .svacts{justify-content:flex-start;margin-top:6px}}

/* ---- zone configuration panel (views/dns-zone.php) ---- */
.zcfg label{display:flex;flex-direction:column;gap:4px}
.zcfg .sub{font-size:11.5px;line-height:1.35}
.zfoot{display:flex;align-items:center;gap:12px;margin-top:12px;flex-wrap:wrap}
.zfoot .zserial{margin-left:auto}
.zacts{display:flex;gap:7px;margin-top:11px;flex-wrap:wrap}
.zhelp{margin:9px 0 0;line-height:1.45}
.btn.danger-txt{color:var(--bad)}
.btn.danger-txt:hover{background:var(--bad-soft);border-color:var(--bad)}
.dnssl{display:flex;flex-direction:column;gap:4px;justify-content:flex-start}

/* A zone BIND refuses to load resolves for nobody - it outranks every other
   warning on the page, so it gets the strongest treatment. */
.zbroken{margin-bottom:14px;border-left:3px solid var(--bad)}
.zbroken p{font-size:12.5px;line-height:1.5;color:var(--ink);margin:0 0 10px}
.zbroken .alertbox{margin-bottom:10px}
.zbroken .sub{margin:0 0 10px}

/* ---- danger zone -------------------------------------------------------------
   A reusable pattern for the small number of places where the panel hands the
   operator something that can genuinely break the server. It has to LOOK unlike
   the rest of the panel, so nobody arrives here by muscle memory: red rule down
   the side, an explicit badge, the consequences written out before the control,
   and the destructive button last and visually separated. */
.dz{border:1px solid var(--bad);border-left-width:3px;margin-top:14px}
.dz-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:10px}
.dz-badge{background:var(--bad);color:#fff;font-size:10.5px;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;padding:3px 8px;border-radius:6px}
.dz-title{margin:0}
.dz-title::before{background:var(--bad)}
.dz-warn{background:var(--bad-soft);border-radius:9px;padding:11px 13px;margin-bottom:12px;
  font-size:12.5px;line-height:1.5;color:var(--ink)}
.dz-warn p{margin:0 0 6px}
.dz-warn ul{margin:0;padding-left:18px}
.dz-warn li{margin-bottom:5px}
.dz-warn li:last-child{margin-bottom:0}
.dz-edit{width:100%;min-height:340px;resize:vertical;background:var(--surface-2);
  border:1px solid var(--border-strong);border-radius:9px;padding:11px 12px;
  font-size:12px;line-height:1.55;color:var(--ink);outline:none;white-space:pre;overflow:auto}
.dz-edit:focus{border-color:var(--bad);box-shadow:0 0 0 3px var(--bad-soft)}
.dz-result{margin-top:10px;padding:10px 12px;border-radius:9px;font-size:12px;
  font-family:"SF Mono",Menlo,Consolas,monospace;white-space:pre-wrap;line-height:1.5}
.dz-result.ok{background:var(--ok-soft);color:var(--ok-ink);border:1px solid var(--ok-line)}
.dz-result.bad{background:var(--bad-soft);color:var(--bad);border:1px solid var(--bad-line)}
.dz-result.warn{background:var(--warn-soft);color:var(--warn-ink);border:1px solid var(--warn-line)}
.dz-acts{display:flex;align-items:center;gap:8px;margin-top:11px;flex-wrap:wrap}
.dz-sp{flex:1}
.dz-hint{margin:7px 0 0}
.dz-bk{margin-top:18px;padding-top:14px;border-top:1px solid var(--border)}
.dz-bktitle{margin-bottom:9px}
.dz-bktitle::before{background:var(--bad)}

/* ---- expandable nav groups (sidebar) -----------------------------------------
   The parent row matches a normal nav link exactly, so the sidebar keeps one
   visual rhythm; only the chevron and the indented children mark the level. */
.navparent{display:flex;align-items:center;gap:10px;width:100%;padding:9px 12px;
  border-radius:9px;border:1px solid transparent;background:none;color:var(--text);
  font-weight:600;font-size:13px;font-family:inherit;margin-bottom:2px;cursor:pointer;
  text-align:left;transition:.13s}
.navparent:hover{background:var(--bg);color:var(--ink)}
.navparent span{flex:1}
.navparent .ico{width:17px;height:17px;flex-shrink:0;opacity:.85}
.navparent .chev{width:14px;height:14px;flex-shrink:0;opacity:.55;transition:transform .16s}
.navgroup.open > .navparent .chev{transform:rotate(90deg)}
/* The parent highlights only as a section marker - the CHILD carries the strong
   "you are here", so two rows never claim to be the current page. */
.navgroup.open > .navparent.on{color:var(--primary)}
.navgroup.open > .navparent.on .ico{opacity:1}

.navsub{display:none;margin:1px 0 4px 8px;padding-left:9px;border-left:1px solid var(--border)}
.navgroup.open > .navsub{display:block}
.navsub a{padding:7px 10px;font-size:12.5px;font-weight:600;gap:0}
.navsub a.on{background:var(--primary-soft);color:var(--primary);box-shadow:inset 3px 0 0 var(--primary)}

/* ---- advisory before a risky create (the "riskbox" pattern) -------------------
   Distinct from .dz: the danger zone guards an action that can break the server,
   this guards one that is merely INADVISABLE. So it warns and offers the better
   path first, but the second button is a real way through - the operator decides,
   and the API enforces the same rule so the decision cannot be bypassed. */
.riskbox{border:1px solid var(--bad);border-left-width:3px;margin-bottom:14px}
.riskbox.accepted{border-color:var(--warn);border-left-color:var(--warn);opacity:.85}
.riskbox-h{display:flex;gap:12px;align-items:flex-start}
.riskbox-ico{flex:none;width:26px;height:26px;border-radius:50%;background:var(--bad);
  color:#fff;font-weight:800;font-size:15px;display:grid;place-items:center;margin-top:1px}
.riskbox.accepted .riskbox-ico{background:var(--warn)}
.riskbox-t{font-size:14px;font-weight:700;color:var(--ink);margin-bottom:4px}
.riskbox-b{font-size:12.5px;line-height:1.55;color:var(--text);margin:0}
.riskbox-acts{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap;padding-left:38px}
@media(max-width:600px){.riskbox-acts{padding-left:0}}

/* the "before you rely on it" checklist on the add-zone page */
.zchk{margin:0;padding-left:18px;font-size:12.5px;line-height:1.55;color:var(--text)}
.zchk li{margin-bottom:8px}
.zchk li:last-child{margin-bottom:0}

/* ---- stale-data bar (layout.php) ---------------------------------------------
   Sits above the page content, not fixed to a corner: it qualifies everything
   below it, so it has to be read before the data is - and it must not be
   dismissible, because the condition it reports does not go away by clicking. */
.stalebar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  background:var(--warn-soft);border:1px solid var(--warn);border-left-width:3px;
  border-radius:10px;padding:11px 14px;margin-bottom:14px;font-size:12.5px;line-height:1.5}
.stalebar-ico{flex:none;width:22px;height:22px;border-radius:50%;background:var(--warn);
  color:#fff;font-weight:800;font-size:13px;display:grid;place-items:center}
.stalebar-t{flex:1;min-width:220px;color:var(--warn-ink)}
.stalebar-t b{color:var(--warn-ink-deep)}
.stalebar .btn{flex:none}

/* ---- messages inside the notification bell ----
   The customer's message feed lives in the panel now, not on a page of its own,
   so the panel has to be wide enough to hold a topic, a body and two actions —
   and scroll rather than grow. */
.bell-panel.wide{width:min(92vw,420px)}
.bell-panel.wide .bell-list{max-height:min(60vh,440px)}
/* Scoped to the wide panel: the stock .bell-head is space-between, which every
   other bell relies on. Here the head carries three things, and the third is
   pushed right by its own margin. */
.bell-panel.wide .bell-head{justify-content:flex-start;gap:8px}
.msg{padding:11px 13px;border-bottom:1px solid var(--border)}
.msg:last-child{border-bottom:0}
.msg.unread{background:var(--primary-soft)}
.msg-t{display:flex;align-items:center;gap:8px;margin-bottom:3px}
.msg-t b{font-size:13px;line-height:1.35}
.msg-b{line-height:1.5;word-break:break-word}
.msg-f{display:flex;align-items:center;gap:7px;margin-top:7px}
.msg-f .sub{flex:1;font-size:11.5px}

/* ---- icon-only buttons ----
   The file manager's row actions. Text buttons there took more width than the
   filename they applied to, so the row wrapped and the name stopped being the
   widest thing on the line. Square, so five of them read as one control strip.
   Every one carries a title and an aria-label — an icon with no name is a guess,
   and the guess about a trash can next to somebody's files must not have to be
   right. */
/* .btn.icobtn, not .icobtn: `.btn.sm` sets padding and is TWO classes, so a
   one-class reset lost to it and every square button kept 5.5px/10px of it.
   With border-box that leaves a 10px content box holding a 15px glyph, which
   overflows to the right of the grid cell — the icon looked nudged off centre
   in every icon row on the panel, and it was. */
.btn.icobtn{padding:0;width:30px;height:28px;display:inline-grid;place-items:center;flex:none}
.icobtn svg{width:15px;height:15px}
/* Also .btn.danger, which paints the SOLID red button — combined with the red
   glyph below it drew a red icon on a red field, and the delete button became
   an empty red square. The icon was always there; nothing could see it. */
.btn.icobtn.danger{background:var(--surface);color:var(--bad);border-color:var(--bad-soft)}
.btn.icobtn.danger:hover:not([disabled]){background:var(--bad-soft);border-color:var(--bad)}
/* Busy: the icon stays, because replacing it with a word deletes the SVG and
   leaves an empty square that never comes back. */
.icobtn.working{opacity:.45}
.icobtn.working svg{animation:h24spin .9s linear infinite}
@keyframes h24spin{to{transform:rotate(360deg)}}

/* cron fields sit in a row and are read as a unit, so they share a width */
.cronrow{display:grid;grid-template-columns:repeat(5,1fr);gap:7px;max-width:520px}
.cronrow .inp{text-align:center;padding-left:6px;padding-right:6px}

/* ---------- page header with actions ----------
   The primary action for a page belongs beside its title, not buried in the
   first card's header - a customer looking for "add a website" scans the top
   right of the page, which is where every other panel puts it. */
.pagehead{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap;margin-bottom:14px}
.pagehead .crumb,.pagehead h1{margin-bottom:0}
.pagehead h1{margin-top:2px}
.pageactions{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap;align-items:center}
@media(max-width:560px){.pageactions{margin-left:0;width:100%}}

/* ---------- the repair screen ----------
   A row here is a decision, not a setting: the switch says "do this when I press
   the button", and the sentence beside it says exactly what "this" is. The
   description is the same weight as the label on purpose - a customer who
   cannot tell what a fix will do to their site is right not to press it. */
.fixrow{display:flex;gap:14px;align-items:flex-start;padding:14px 2px;border-top:1px solid var(--border)}
.fixrow:first-of-type{border-top:0}
.fixrow>.switch{flex:none;margin-top:2px}
.fixrow-t{flex:1;min-width:0}
.fixrow-t b{display:block;color:var(--ink);font-size:13.5px}
.fixrow-t .sub{margin-top:3px;line-height:1.55}
.fixrow-extra{margin-top:9px;display:flex;gap:16px;flex-wrap:wrap;align-items:center;font-size:12.5px}
.fixrow-extra label{display:flex;gap:6px;align-items:center;cursor:pointer}

/* One block per operation. The counts are the whole point of the panel, so they
   sit on the same line as the name rather than inside a paragraph. */
.fixres{margin-top:2px}
.fxs{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;padding:11px 2px;border-top:1px solid var(--border)}
.fxs:first-child{border-top:0}
.fxs-n{flex:1;min-width:160px;color:var(--ink);font-weight:600;font-size:13px}
.fxs-d{width:100%;margin:2px 0 0;padding-left:0;list-style:none}
.fxs-d li{font-size:12.5px;color:var(--muted);line-height:1.65;display:flex;gap:8px}
.fxs-d li b{color:var(--ink);font-variant-numeric:tabular-nums;min-width:44px;text-align:right;font-weight:600}
.fxs-d li.on b{color:var(--warn)}

/* --- hint tooltip: an explanation that is there when wanted and gone otherwise
   Written for the Services note, which was a paragraph nobody reads sitting
   under a list everybody reads. Hover OR keyboard focus opens it, so it is not
   mouse-only, and the panel is aria-described rather than title="" — a native
   title cannot carry the bold runs that make this text scannable. */
.hinttip{position:relative;display:inline-flex;align-items:center;vertical-align:middle}
.hinttip>.hinttip-btn{display:inline-flex;align-items:center;justify-content:center;
  width:18px;height:18px;padding:0;border:0;background:none;cursor:help;color:var(--muted);
  border-radius:50%;line-height:0}
.hinttip>.hinttip-btn:hover,.hinttip>.hinttip-btn:focus-visible{color:var(--ink);outline:none}
.hinttip>.hinttip-btn:focus-visible{box-shadow:0 0 0 2px var(--accent,#4f6ef7)}
.hinttip>.hinttip-body{position:absolute;top:calc(100% + 8px);left:-6px;z-index:80;
  width:max(280px,min(440px,70vw));background:var(--card);color:var(--ink);
  border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow-lg);
  padding:12px 14px;font-size:12.5px;line-height:1.6;font-weight:400;text-transform:none;
  letter-spacing:normal;opacity:0;visibility:hidden;transform:translateY(-3px);
  transition:opacity .12s ease,transform .12s ease,visibility .12s;pointer-events:none}
.hinttip:hover>.hinttip-body,.hinttip:focus-within>.hinttip-body{
  opacity:1;visibility:visible;transform:translateY(0)}
.hinttip>.hinttip-body b{color:var(--ink);font-weight:700}
/* Near the right edge the panel would hang off the card; this flips it. */
.hinttip.tip-right>.hinttip-body{left:auto;right:-6px}
