/* XME Console — thành phần giao diện. Không Tailwind, không icon font.
   Chuyển động: chỉ một hiệu ứng mờ dần cho toast/hộp thoại. Không nâng khi rê
   chuột, không đổ bóng động, không skeleton nhấp nháy. */

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--text);
  background: var(--page);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3 { margin: 0; font-weight: 600; }
h1 { font-size: var(--fs-title); }
h2 { font-size: var(--fs-head); }
/* Vòng focus.
   Ô nhập / dropdown dùng `box-shadow` chứ KHÔNG dùng `outline`. Lý do: outline
   vẽ bán kính bằng border-radius + outline-offset, nên khi có offset thì vòng
   bo góc rộng hơn control và trông như một khung rời lơ lửng bên ngoài — đúng
   thứ vừa thấy. box-shadow bám chính xác border-radius nên không bao giờ lệch.
   Đổi luôn màu viền để vòng đọc ra là một phần của control.

   Nút và link vẫn dùng outline + `:focus-visible`: chúng có thể không có viền,
   và bấm chuột xong mà còn đọng vòng thì rối.

   Khai tường minh cho `<select>` là bắt buộc: dù đã `appearance: none`, Chrome
   vẫn tự vẽ vòng focus mặc định của hệ điều hành đè lên — dày, màu accent của
   máy, mỗi máy một kiểu. */
:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

.input:focus, .select:focus, textarea:focus,
input[type="date"]:focus, input[type="number"]:focus {
  outline: none;
  border-color: var(--focus);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 1px;
}

.mono { font-family: var(--font-mono); font-size: var(--fs-small); }
.num  { font-variant-numeric: tabular-nums; }
.muted { color: var(--text-3); }
.sec   { color: var(--text-2); }
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* ── Vỏ ───────────────────────────────────────────────────────────────── */
.shell { display: flex; min-height: 100vh; min-height: 100dvh; }

.sidebar {
  flex: 0 0 216px; background: var(--surface); border-right: 1px solid var(--border);
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; height: 100dvh;
}
.sidebar-brand {
  display: flex; align-items: center; gap: 9px;
  padding: 14px 16px; border-bottom: 1px solid var(--rule);
  font-weight: 600; letter-spacing: -0.01em;
}
/* ── Nhãn môi trường ───────────────────────────────────────────────────────
   Không chỉ là màu: mỗi nhãn có CHỮ riêng, để người mù màu và ảnh chụp màn hình
   đen trắng vẫn đọc được. Bản PROD thêm một vạch đỏ mảnh trên đầu sidebar — nhìn
   lướt qua từ xa là biết mình đang ở đâu, chưa cần đọc chữ. */
.env-badge {
  font-size: 10px; font-weight: 600; letter-spacing: .02em;
  padding: 2px 6px; border-radius: 4px; white-space: nowrap;
  border: 1px solid transparent;
}
.env-badge.env-test    { background: var(--warn-bg); color: var(--warn-text); border-color: var(--warn-text); }
.env-badge.env-prod    { background: var(--bad-bg);  color: var(--bad-text);  border-color: var(--bad-text); }
.env-badge.env-unknown { background: var(--surface-3); color: var(--text-2); border-color: var(--border-ctrl); }

/* Vạch cảnh báo chỉ ở bản PROD — chỗ duy nhất bấm nhầm là trả giá thật. */
.shell.env-prod .sidebar { box-shadow: inset 0 3px 0 0 var(--bad-text); }

.sidebar-brand { flex-wrap: wrap; }
/* Sidebar thu hẹp (<1150px) giấu chữ trong brand, nhưng nhãn môi trường PHẢI ở
   lại — xem `:not(.env-badge)` trong khối media ở cuối file. */

.sidebar-brand .tag, .login-brand .tag {
  font-size: 10px; font-weight: 600; letter-spacing: .06em;
  padding: 2px 5px; border-radius: 4px;
  background: var(--accent); color: var(--on-accent);
}
.nav { padding: 8px; overflow-y: auto; flex: 1; }
.nav-group {
  padding: 12px 8px 5px; font-size: var(--fs-label); font-weight: 600;
  color: var(--text-3); letter-spacing: .02em;
}
.nav a {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 9px; margin: 1px 0; border-radius: var(--r-ctrl);
  color: var(--text-2); text-decoration: none; font-size: var(--fs-table);
}
.nav a:hover { background: var(--surface-2); color: var(--text); }
.nav a.active { background: var(--surface-3); color: var(--text); font-weight: 500; }
.nav a svg { flex: none; width: 17px; height: 17px; }
.sidebar-foot { padding: 9px 12px; border-top: 1px solid var(--rule); }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.main-inner { width: 100%; max-width: var(--shell-max); margin-inline: auto; padding: 18px 22px 40px; }

.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-bottom: 16px;
}
.page-head p { margin: 3px 0 0; color: var(--text-3); font-size: var(--fs-small); }

/* ── Điều khiển ───────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: var(--h-ctrl); padding: 0 13px;
  font-family: inherit; font-size: var(--fs-table); font-weight: 500;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--border-ctrl); border-radius: var(--r-ctrl);
  cursor: pointer; white-space: nowrap;
}
.btn:hover { background: var(--surface-2); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn svg { width: 16px; height: 16px; flex: none; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-danger { color: var(--bad-text); border-color: var(--bad-text); background: var(--surface); }
.btn-danger:hover { background: var(--bad-bg); }
/* Lớp điều khiển nhỏ thống nhất 30px: .btn-sm, .select-sm, .pgbtn và các ô nhập
   nội dòng đều dùng chung con số này. Trước đó .btn-sm để 28px nên nút đứng cạnh
   ô nhập bị lệch 2px — nhìn thì tưởng nút cao hơn, thực ra là ô nhập cao hơn. */
.btn-sm { height: 30px; padding: 0 10px; font-size: var(--fs-small); }
.btn-icon { width: var(--h-ctrl); padding: 0; }
/* Nút chỉ có icon, không viền — dùng cho thao tác phụ nằm cạnh nội dung (sửa,
   copy). Có viền thì mỗi dòng lại thêm một hộp, bảng nhìn rối. */
.btn-plain {
  border-color: transparent; background: transparent;
  width: 26px; height: 26px; padding: 0; flex: none; color: var(--text-3);
}
.btn-plain:hover { background: var(--surface-2); color: var(--text); }
.btn-plain svg { width: 15px; height: 15px; }
.btn-plain-xs { width: 22px; height: 22px; }
.btn-plain-xs svg { width: 13px; height: 13px; }

.input, .select {
  height: var(--h-ctrl); padding: 0 10px;
  font-family: inherit; font-size: var(--fs-table); color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-ctrl); border-radius: var(--r-ctrl);
  min-width: 0;
}
.input::placeholder { color: var(--text-3); }
/* Ô bị khoá vẫn phải ĐỌC được — chỉ giảm nhẹ và đổi nền, không làm mờ tới mức
   không thấy giá trị. Người dùng cần biết con số đang là bao nhiêu trước khi
   quyết định có bật lên sửa hay không. */
.input:disabled, .select:disabled {
  background: var(--surface-2); color: var(--text-3); cursor: not-allowed;
}
.select { padding-right: 26px; cursor: pointer;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 12px center, right 7px center;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}
.field { display: flex; flex-direction: column; gap: 5px; }
.field > label { font-size: var(--fs-label); font-weight: 500; color: var(--text-2); }

/* Cả dòng nhãn là vùng bấm, không phải mỗi ô 16px */
.check {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 8px; margin: -6px -8px; border-radius: var(--r-ctrl);
  cursor: pointer; font-size: var(--fs-table); user-select: none;
}
.check:hover { background: var(--surface-2); }
.check input { width: 16px; height: 16px; margin: 0; accent-color: var(--accent); cursor: pointer; flex: none; }

.toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
.grow { flex: 1 1 200px; }

/* ── Thẻ & ô số ───────────────────────────────────────────────────────── */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-card); overflow: hidden;
}
.card-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 11px 14px; border-bottom: 1px solid var(--rule); flex-wrap: wrap;
}
.card-body { padding: 14px; }

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(142px, 1fr)); gap: 10px; margin-bottom: 16px; }
.tile {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-card); padding: 8px 11px;
}
.tile .k { font-size: var(--fs-label); color: var(--text-2); line-height: 1.35; }
.tile .v { font-size: 21px; font-weight: 600; line-height: 1.15; font-variant-numeric: tabular-nums; }
/* Nhãn phụ giữ trên MỘT dòng: một ô xuống dòng là cả hàng cao lên theo, nhìn
   so le. Dài quá thì cắt bằng ellipsis, chữ đầy đủ nằm ở title. */
.tile .s {
  font-size: var(--fs-label); color: var(--text-3); line-height: 1.35;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tile.is-warn { border-color: var(--warn-text); }
.tile.is-bad  { border-color: var(--bad-text); }
.tile.is-warn .v { color: var(--warn-text); }
.tile.is-bad  .v { color: var(--bad-text); }

/* ── Bảng ─────────────────────────────────────────────────────────────── */
.tw { overflow-x: auto; }
table.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-table); }
table.tbl th {
  position: sticky; top: 0; z-index: 1;
  text-align: left; padding: 8px 12px; white-space: nowrap;
  font-size: var(--fs-label); font-weight: 600; color: var(--text-2);
  background: var(--surface-2); border-bottom: 1px solid var(--border);
}
table.tbl th.sortable { cursor: pointer; user-select: none; }
table.tbl th.sortable:hover { color: var(--text); }
table.tbl th .ind { display: inline-block; width: 10px; color: var(--text-3); }
table.tbl td { padding: 0 12px; height: 42px; border-bottom: 1px solid var(--rule); vertical-align: middle; white-space: nowrap; }
table.tbl tbody tr:last-child td { border-bottom: none; }
table.tbl tbody tr.clickable { cursor: pointer; }
table.tbl tbody tr.clickable:hover td { background: var(--surface-2); }
table.tbl tbody tr.sel td { background: var(--surface-3); }
.r, table.tbl th.r { text-align: right; }
.empty { padding: 26px 12px; text-align: center; color: var(--text-3); font-size: var(--fs-table); }

/* ── Luồng nhiều bước ────────────────────────────────────────────────── */
.steps { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 16px; }
.step {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; border: 1px solid var(--border); border-radius: var(--r-ctrl);
  background: var(--surface); font-size: var(--fs-small); color: var(--text-3);
  flex: 1 1 150px; min-width: 0;
}
.step-n {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600;
  background: var(--surface-3); color: var(--text-2);
}
.step.cur { border-color: var(--accent); color: var(--text); }
.step.cur .step-n { background: var(--accent); color: var(--on-accent); }
.step.done { color: var(--ok-text); border-color: var(--ok-text); }
.step.done .step-n { background: var(--ok-bg); color: var(--ok-text); }
.step-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 767px) { .step { flex-basis: 100%; } }

/* ── Tab ─────────────────────────────────────────────────────────────── */
.tabs { display: flex; gap: 4px; flex-wrap: wrap; }
.tab {
  display: inline-flex; align-items: center; gap: 6px;
  height: 30px; padding: 0 11px;
  font-family: inherit; font-size: var(--fs-table); color: var(--text-2);
  background: transparent; border: 1px solid transparent; border-radius: var(--r-ctrl); cursor: pointer;
}
.tab:hover { background: var(--surface-2); color: var(--text); }
.tab.active { background: var(--surface-3); border-color: var(--border); color: var(--text); font-weight: 500; }
.tab-n { font-size: var(--fs-label); color: var(--text-3); font-variant-numeric: tabular-nums; }
.tab.active .tab-n { color: var(--text-2); }

/* ── Thanh mức + đồ thị nhỏ ──────────────────────────────────────────── */
.spark { color: var(--text-3); }

.hosts { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: 12px; margin-bottom: 16px; }

/* Ruột thẻ máy chủ: các con số bên trái, vòng mức bên phải.
   `auto` cho cột phải chứ không phải 1fr — vòng mức có bề ngang cố định, để nó
   co giãn chỉ tạo khoảng trắng giữa hai vòng. Cột trái nuốt phần dư.
   `minmax(0,…)` là bắt buộc: mặc định min-width của một track grid là `auto`,
   nên nội dung dài (chuỗi commit, tên file backup) sẽ đẩy giãn thay vì xuống dòng. */
/* Ruột thẻ máy chủ. Mốc chuyển bố cục phải đo THẺ, không đo cửa sổ — thẻ nằm
   trong lưới `.hosts` nên bề ngang của nó phụ thuộc SỐ MÁY: một máy thì ~1130px,
   hai máy thì ~565px, mà cửa sổ vẫn 1440 trong cả hai trường hợp. Media query
   theo viewport giữ nguyên ba cột ở mức 565px và ba cột chen nát nhau — đã vấp
   thật. `@container` là công cụ đúng cho việc này. */
.hosts > .card { container-type: inline-size; }

.host-cols {
  /* BỐN vùng bằng nhau: số liệu · RAM · tải CPU · đĩa. `minmax(0, 1fr)` chứ
     không phải `1fr`: min-width mặc định của track lưới là `auto`, nên nội dung
     dài (chuỗi commit, tên file backup) sẽ đẩy cột phình ra và phá thế cân bằng. */
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px 20px;
  /* stretch chứ không start: cột đồ thị phải cao bằng hàng để thanh ăn hết chỗ,
     nếu không nó co về chiều cao nội dung và chừa một dải trắng dưới đáy. */
  align-items: stretch;
}
/* Số liệu và vòng mức canh GIỮA theo chiều dọc. Hai đồ thị bên cạnh chiếm hết
   chiều cao hàng, nên bám mép trên làm hai khối kia treo lơ lửng phía trên và
   hàng trông lệch hẳn về một bên. */
.host-cols > .kv, .host-cols > .host-rings { align-self: center; }
/* Thẻ vừa (hai máy cạnh nhau): số liệu + vòng mức ở hàng trên, đồ thị xuống
   hàng dưới chiếm hết bề ngang — nó là thứ cần bề ngang nhất.
   Phải đặt Ô TƯỜNG MINH, không dựa vào tự xếp: trong DOM đồ thị nằm GIỮA số liệu
   và vòng mức, nên chỉ cho nó `grid-column: 1/-1` thì nó chiếm nguyên hàng 2 và
   đẩy vòng mức xuống hàng 3 — thành ba hàng chồng nhau thay vì hai. */
@container (max-width: 860px) {
  .host-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .host-cols > .kv         { grid-column: 1; grid-row: 1; }
  .host-cols > .host-rings { grid-column: 2; grid-row: 1; }
  /* Chỉ ghim HÀNG, để lưới tự xếp cột — như vậy vẫn đúng cả khi máy không có
     RAM (macOS không có /proc) và chỉ còn một đồ thị. */
  .host-cols > .daychart   { grid-row: 2; }
}
@container (max-width: 420px) {
  .host-cols { grid-template-columns: 1fr; }
  .host-cols > .kv, .host-cols > .host-rings, .host-cols > .daychart {
    grid-column: auto; grid-row: auto;
  }
}

/* Đồ thị 24 thanh. Chiều cao ĂN THEO Ô CHỨA, không đặt cứng: hàng lưới đã cao
   bằng cột cao nhất rồi, đặt cứng 46px chỉ tạo khoảng trắng dưới đáy. `flex: 1`
   trên vùng thanh + `height: 100%` trên cả khối là đủ; `min-height: 0` là bắt
   buộc, thiếu nó thì flex item không co nhỏ hơn nội dung và tràn ra ngoài. */
.daychart {
  min-width: 0; color: var(--text-2);
  display: flex; flex-direction: column;
  /* Cao bằng ô chứa, sàn 72px để vùng thanh không mỏng tới mức không đọc được.
     `height: 100%` ở đây an toàn vì hai đồ thị nằm CẠNH nhau (mỗi cái một ô
     lưới riêng), khác hẳn lúc xếp chồng — khi đó mỗi cái xin 100% chiều cao cha
     thì tổng thành 200% và tràn. */
  height: 100%; min-height: 96px;
}
.dc-top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  font-size: var(--fs-small); margin-bottom: 4px; flex: 0 0 auto;
}
.dc-top .dc-right { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.dc-top b { color: var(--text); font-variant-numeric: tabular-nums; font-weight: 600; }
.dc-delta {
  color: var(--text-3); font-variant-numeric: tabular-nums; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

/* Vùng vẽ = cột nhãn trục dọc + vùng thanh. Nhãn để RIÊNG một cột chứ không
   chồng lên thanh: chồng lên thì cột giờ 0h luôn bị chữ đè, mà đó lại là cột
   người ta đọc đầu tiên. */
.dc-plot { flex: 1 1 auto; min-height: 44px; display: flex; gap: 5px; }
.dc-y {
  flex: 0 0 auto; display: flex; flex-direction: column; justify-content: space-between;
  font-size: 9px; line-height: 1; color: var(--text-3);
  font-variant-numeric: tabular-nums; text-align: right; min-width: 15px;
  padding-bottom: 1px;
}

.dc-bars {
  position: relative; flex: 1 1 auto; min-width: 0;
  display: flex; align-items: flex-end; gap: 2px;
  border-bottom: 1px solid var(--border);
}
/* Vạch chia mỗi 10%. `pointer-events: none` để không nuốt tooltip của thanh. */
.dc-grid {
  position: absolute; left: 0; right: 0; height: 0;
  border-top: 1px solid var(--rule); pointer-events: none; opacity: .55;
}
.dc-grid.major { opacity: 1; }
.dc-grid[style*="bottom:0%"] { border-top-color: transparent; }
.dc-slot { position: relative; flex: 1 1 0; min-width: 0; height: 100%; display: flex; align-items: flex-end; }
/* Giờ chưa tới: nền mờ để phân biệt với giờ ĐÃ QUA mà không có mẫu — cái sau là
   dấu hiệu console từng dừng, cái trước là chuyện bình thường. */
.dc-slot.future { background: var(--surface-2); border-radius: 2px; }
/* Cột hẹp hơn ô của nó, canh giữa: 24 cột dính sát nhau đọc ra thành một khối
   liền, khe hở mới là thứ cho mắt đếm được từng giờ. */
.dc-bar {
  width: 60%; margin: 0 auto; background: currentColor;
  border-radius: 2px 2px 0 0; opacity: .82;
}

.dc-axis {
  /* Thụt trái đúng bằng cột nhãn trục dọc để số giờ thẳng cột với thanh của nó. */
  flex: 0 0 auto; display: flex; gap: 2px; margin-top: 3px; padding-left: 20px;
  font-size: 9px; color: var(--text-3); font-variant-numeric: tabular-nums;
}
.dc-tick { flex: 1 1 0; min-width: 0; text-align: left; }
.dc-empty { font-size: var(--fs-small); color: var(--text-3); margin-top: 4px; }

/* Vòng bên trái, chữ bên phải, chữ ăn hết chỗ còn lại — vòng đứng một mình canh
   trái để thừa gần 190px trong ô rộng 273px. */
.host-rings { display: flex; gap: 14px; align-items: center; min-width: 0; }
.host-rings > .kv { flex: 1 1 auto; min-width: 0; }

.donut { text-align: center; width: 84px; flex: 0 0 auto; }
.donut.bare { width: auto; }
.donut-label { font-size: var(--fs-label); color: var(--text-2); margin-top: 2px; }
.donut-value {
  font-size: var(--fs-small); color: var(--text-3);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}


/* ── Phân trang ──────────────────────────────────────────────────────── */
.pager {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  padding: 9px 14px; border-top: 1px solid var(--rule);
}
.pager-left { display: flex; align-items: center; gap: 10px; font-size: var(--fs-small); color: var(--text-2); }
.pager-right { display: flex; align-items: center; gap: 4px; }
.select-sm { height: 30px; font-size: var(--fs-small); padding: 0 24px 0 9px; }
.pgbtn {
  min-width: 30px; height: 30px; padding: 0 7px;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: inherit; font-size: var(--fs-small); font-variant-numeric: tabular-nums;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--border-ctrl); border-radius: var(--r-ctrl); cursor: pointer;
}
.pgbtn:hover:not(:disabled) { background: var(--surface-2); }
.pgbtn:disabled { opacity: .4; cursor: not-allowed; }
.pgbtn.cur { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 500; }
.pgdots { padding: 0 3px; color: var(--text-3); font-size: var(--fs-small); }
@media (pointer: coarse) { .pgbtn, .select-sm { min-width: 38px; height: 38px; } }

/* ── Nhãn trạng thái: luôn icon + chữ + màu, không bao giờ chỉ màu ────── */
.badge {
  display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; flex: none;
  font-size: var(--fs-label); font-weight: 500;
  padding: 2px 7px; border-radius: var(--r-ctrl);
  background: var(--neutral-bg); color: var(--neutral-text);
}
.badge svg { width: 13px; height: 13px; flex: none; }
.badge.ok   { background: var(--ok-bg);   color: var(--ok-text); }
.badge.warn { background: var(--warn-bg); color: var(--warn-text); }
.badge.bad  { background: var(--bad-bg);  color: var(--bad-text); }

.alert-row {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 14px; border-bottom: 1px solid var(--rule);
  font-size: var(--fs-table); cursor: pointer;
}
.alert-row:last-child { border-bottom: none; }
.alert-row:hover { background: var(--surface-2); }
.alert-row .who { color: var(--text-3); font-size: var(--fs-small); margin-left: auto; white-space: nowrap; }

/* ── Hộp thoại ───────────────────────────────────────────────────────── */
.modal {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: min(460px, calc(100vw - 32px)); max-height: calc(100vh - 48px);
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-card); z-index: 50; animation: fade var(--dur) ease-out;
}
.modal-head { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 13px 16px; border-bottom: 1px solid var(--rule); }
.modal-body { padding: 16px; overflow-y: auto; font-size: var(--fs-table); }
.modal-body p { margin: 0 0 8px; }
.modal-foot { display: flex; justify-content: flex-end; gap: 8px;
  padding: 12px 16px; border-top: 1px solid var(--rule); }

/* Khối nhắc nhở trong hộp thoại — dùng cho ràng buộc an toàn phải đọc kỹ */
.note { border-radius: var(--r-ctrl); padding: 10px 12px; font-size: var(--fs-small); }
.note.warn { background: var(--warn-bg); color: var(--warn-text); }
.note.bad  { background: var(--bad-bg);  color: var(--bad-text); }
.note.ok   { background: var(--ok-bg);   color: var(--ok-text); }

/* ── Ngăn chi tiết + lớp phủ ──────────────────────────────────────────── */
.scrim {
  position: fixed; inset: 0; background: rgba(10,12,16,.42); z-index: 40;
  animation: fade var(--dur) ease-out;
}
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(520px, 100%);
  background: var(--surface); border-left: 1px solid var(--border);
  z-index: 41; display: flex; flex-direction: column;
  animation: fade var(--dur) ease-out;
}
.drawer-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
  padding: 14px 16px; border-bottom: 1px solid var(--rule);
}
.drawer-body { padding: 16px; overflow-y: auto; flex: 1; }
/* Chân ngăn — nơi đặt nút hành động. Nằm ngoài vùng cuộn để nút luôn thấy được
   dù nội dung dài. */
.drawer-foot {
  display: flex; justify-content: flex-end; gap: 8px; flex: none;
  padding: 12px 16px; border-top: 1px solid var(--rule);
  padding-bottom: calc(12px + env(safe-area-inset-bottom));
}
/* margin: 0 là bắt buộc — trình duyệt mặc định cho <dl> margin 1em trên dưới,
   nên khối này bị đẩy xuống thêm ~15px và lệch hẳn với padding của khối tiêu đề
   ngay trên nó. */
.kv { display: grid; grid-template-columns: 128px minmax(0, 1fr); gap: 7px 12px; font-size: var(--fs-table); margin: 0; }
/* Bản gọn: nhãn ngắn hơn, dùng ở chỗ hẹp như ô đĩa. */
.kv-tight { grid-template-columns: auto minmax(0, 1fr); gap: 5px 10px; }
/* Trong thẻ máy chủ, cột nhãn hẹp lại: 128px + nội dung dài (chuỗi commit, tên
   file backup) làm bảng tràn khỏi ô lưới 273px. */
.host-cols > .kv { grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: 7px 10px; }
.kv dt { color: var(--text-2); }
.kv dd { margin: 0; }

.toast {
  position: fixed; right: 16px; bottom: 16px; z-index: 60;
  background: var(--accent); color: var(--on-accent);
  padding: 10px 14px; border-radius: var(--r-ctrl); font-size: var(--fs-table);
  animation: fade var(--dur) ease-out;
}
@keyframes fade { from { opacity: 0 } to { opacity: 1 } }

/* ── Ba mốc responsive, không hơn ─────────────────────────────────────── */
/* tablet: thu sidebar còn dải icon */
@media (max-width: 1279px) {
  .sidebar { flex-basis: 56px; }
  .sidebar .label, .sidebar-brand span:not(.tag):not(.env-badge), .nav-group { display: none; }
  .nav a { justify-content: center; padding: 9px 0; }
  .sidebar-brand { justify-content: center; padding: 14px 0; }
  .main-inner { padding: 16px 16px 40px; }
}
/* mobile: sidebar xuống dải đáy */
@media (max-width: 767px) {
  .shell { flex-direction: column; }
  .sidebar {
    position: fixed; bottom: 0; left: 0; right: 0; top: auto;
    height: auto; flex-basis: auto; flex-direction: row;
    border-right: none; border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom); z-index: 30;
  }
  .sidebar-brand, .sidebar-foot { display: none; }
  .nav { display: flex; overflow-x: auto; padding: 5px 6px; }
  .nav a { flex: 1; min-width: 54px; }
  .main-inner { padding: 14px 12px 76px; }
  .tiles { grid-template-columns: repeat(auto-fit, minmax(136px, 1fr)); }
  .drawer { width: 100%; }
}

/* ── Đăng nhập ─────────────────────────────────────────────────────────────
   Một cột hẹp giữa trang. Không ảnh nền, không gradient: đây là công cụ làm
   việc, và mọi pixel trang trí là một pixel phải tải trên đường truyền của
   khách sạn. */
.login {
  min-height: 100vh; min-height: 100dvh;
  display: flex; align-items: center; justify-content: center;
  padding: 24px; background: var(--page);
}
.login-card {
  width: 100%; max-width: 352px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-card); padding: 22px;
}
.login-brand {
  display: flex; align-items: center; gap: 9px;
  font-weight: 600; letter-spacing: -0.01em; margin-bottom: 6px;
}

/* Ai đang đăng nhập + nút ra. Ở đây chứ không ở đầu trang: đầu trang là chỗ của
   nội dung, còn danh tính là thứ tra một lần rồi thôi. */
.sidebar-user { display: flex; align-items: center; gap: 8px; }
.sidebar-user .label { min-width: 0; flex: 1; }
.sidebar-user .nm {
  font-size: var(--fs-table); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sidebar-user .rl { font-size: var(--fs-small); color: var(--text-3); }

/* Đang tải: một dòng chữ ĐỨNG YÊN, không skeleton nhấp nháy. */
.loading { padding: 40px 16px; text-align: center; color: var(--text-3); font-size: var(--fs-table); }
.page-err { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
