/* ==========================================================================
   Skyhotel Status — CSS thuần, không dùng framework
   --------------------------------------------------------------------------
   BẢNG MÀU dùng chung với dự án `skyhotel-admin` (SkyHotel Manager Account).
   Xem QUY_UOC_MAU.md trước khi thêm bất kỳ màu mới nào.
   Nguyên tắc: chỉ dùng biến trong :root, KHÔNG viết mã màu rời rạc trong
   từng thành phần. Giao diện chỉ có chế độ sáng, không làm dark mode.
   ========================================================================== */

:root {
  /* --- Nền và khung --- */
  --bg: #f4f5f7;          /* nền trang */
  --card: #ffffff;        /* nền thẻ, hộp thoại, thanh menu */
  --line: #e3e6ea;        /* đường viền, đường kẻ ngăn cách */

  /* --- Chữ --- */
  --text: #1b1f24;        /* chữ chính */
  --muted: #6b7280;       /* chữ phụ, nhãn, ghi chú */

  /* --- Màu thương hiệu (đỏ Skyhotel) --- */
  --brand: #c9433d;       /* nhạt hơn đỏ gốc #b3231f cho dịu mắt */
  --brand-dark: #a82f2a;  /* trạng thái hover của nút chính */

  /* --- Màu trạng thái --- */
  --ok: #1a7f45;          /* thành công / đang hoạt động */
  --warn: #b26a00;        /* cảnh báo / cần chú ý */
  --danger: #c62828;      /* lỗi / hành động nguy hiểm */
  --info: #1565c0;        /* thông tin / liên kết */

  /* --- Nền nhạt đi kèm màu trạng thái --- */
  --ok-soft: #e6f4ec;
  --warn-soft: #fff3e0;
  --danger-soft: #fdecec;
  --info-soft: #e8f1fd;
  --mute-soft: #eef0f2;

  /* --- Màu riêng của thanh nhịp ---
   * CỐ Ý không dùng --ok / --danger ở đây. Hai màu đó chọn để làm CHỮ trên nền
   * nhạt nên rất đậm; tô nguyên một khối đặc 5x16px thì nặng và tối, nhìn cả
   * dãy 50 vạch thành một mảng xanh rêu. Bảng dưới đây lấy đúng của Uptime
   * Kuma — thanh nhịp vốn là thứ mượn thẳng từ giao diện đó, dùng luôn màu gốc
   * thì mới ra đúng cái cảm giác nhẹ và sáng của nó. (Riêng màu xanh đã hạ
   * xuống một nấc so với Kuma, xem ghi chú ngay dưới.)
   * Đây là NGOẠI LỆ DUY NHẤT với quy ước màu, và vẫn khai ở :root chứ không
   * rải mã màu vào từng thành phần. */
  /* Kuma để #5cdd8b (rgb 92,221,139). Chủ dự án thấy nhạt quá nên hạ độ sáng
     đúng một nấc (L 61% -> 53%), giữ nguyên sắc và độ bão hoà. Đây là chỗ DUY
     NHẤT lệch khỏi bảng gốc của Kuma. */
  --nhip-tot: #3ad673;
  --nhip-chap: #f8a306;   /* Kuma: rgb(248, 163, 6) */
  --nhip-loi: #dc3545;    /* Kuma: rgb(220, 53, 69) */
  /* Kuma dùng #f0f8ff cho ô trống, nhưng trên nền thẻ trắng của mình thì gần
     như không thấy gì. Đậm hơn một nấc để vẫn đọc được là "chưa có số liệu". */
  --nhip-trong: #e9edf2;

  /* --- Hình khối --- */
  --radius: 10px;
  --shadow: 0 1px 3px rgba(16, 24, 40, .08);
  --tap: 44px;            /* chiều cao tối thiểu để bấm được trên điện thoại */
  --focus-ring: 0 0 0 3px rgba(201, 67, 61, .16);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "Helvetica Neue", Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
}

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: var(--info); }

code {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: .9em;
  background: var(--mute-soft);
  padding: 1px 5px;
  border-radius: 4px;
}

.hidden { display: none !important; }

/* Chống iOS tự phóng to trang khi chạm vào ô nhập có cỡ chữ nhỏ hơn 16px. */
@media (pointer: coarse) {
  input, select, textarea { font-size: 16px; }
}

/* ==========================================================================
   Trang đăng nhập
   ========================================================================== */

.login-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.login-card {
  width: 100%;
  max-width: 360px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: var(--shadow);
  padding: 26px 24px;
}
.login-card h1 { margin: 0 0 4px; font-size: 18px; }
.login-card .sub { margin: 0 0 20px; color: var(--muted); font-size: 13px; }

.field { margin-bottom: 12px; }
.field label {
  display: block;
  margin-bottom: 4px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted);
}
input[type="text"], input[type="password"] {
  width: 100%;
  min-height: var(--tap);
  padding: 8px 11px;
  color: var(--text);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px;
  -webkit-appearance: none;
  appearance: none;
}
/* Chỉ ô nhập chữ mới có vòng sáng khi focus (nút tròn xử lý riêng bên dưới). */
input[type="text"]:focus, input[type="password"]:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: var(--focus-ring);
}

/* ==========================================================================
   Nút
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 40px;
  padding: 0 14px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  color: var(--text);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.btn:hover:not(:disabled) { background: #f7f8f9; }
.btn:focus-visible { outline: none; border-color: var(--brand); box-shadow: var(--focus-ring); }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.btn-primary {
  color: #fff;
  background: var(--brand);
  border-color: var(--brand);
}
.btn-primary:hover:not(:disabled) { color: #fff; background: var(--brand-dark); border-color: var(--brand-dark); }

.btn-block { width: 100%; }
.btn-sm { min-height: 32px; padding: 0 10px; font-size: 13px; }

/* ---- Biểu tượng trong nút ----
   Vẽ thẳng bằng SVG, không kéo thư viện icon nào về — dự án vốn không dùng
   thư viện ngoài, mà ba cái hình này thì vài dòng path là xong.

   Nét 1.6 trên khung 24, thu về 16px là còn khoảng một pixel: đủ mảnh để nhẹ
   mắt nhưng vẫn sắc, không nhoè thành vệt xám như khi để 1.0.

   `fill: none` + `stroke: currentColor` nên biểu tượng tự đổi màu theo chữ của
   nút — kể cả nút trên thanh công cụ đỏ (chữ trắng) hay nút bị khoá (mờ đi). */
.btn-icon {
  flex: none;
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Vòng quay chờ nằm ĐÚNG chỗ biểu tượng, không chen thêm vào làm nút giật rộng
   ra rồi thụt lại. Bình thường ẩn; nút đang chạy thì đổi vai cho nhau. */
.btn .btn-quay { display: none; }
.btn.dang-chay .btn-icon { display: none; }
.btn.dang-chay .btn-quay { display: inline-block; }

/* ==========================================================================
   Thông báo trong luồng trang (dùng ở trang đăng nhập)
   ========================================================================== */

.msg {
  padding: 9px 12px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 12px;
}
.msg-error { color: var(--danger); background: var(--danger-soft); }
.msg-ok { color: var(--ok); background: var(--ok-soft); }
.msg-warn { color: var(--warn); background: var(--warn-soft); }

/* ==========================================================================
   Bố cục dashboard
   ========================================================================== */

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 16px;
  color: #fff;
  background: var(--brand);
}
.topbar .brand { font-size: 16px; font-weight: 700; letter-spacing: .3px; }
.topbar .brand span { font-size: 12px; font-weight: 400; opacity: .82; }
.topbar .right { display: flex; align-items: center; gap: 10px; font-size: 12.5px; }
.topbar #userInfo { opacity: .88; }
.topbar .btn {
  min-height: 34px;
  padding: 0 12px;
  font-size: 13px;
  color: #fff;
  background: rgba(255, 255, 255, .15);
  border-color: transparent;
}
.topbar .btn:hover:not(:disabled) { background: rgba(255, 255, 255, .28); }
.topbar .btn:focus-visible { border-color: #fff; box-shadow: 0 0 0 3px rgba(255, 255, 255, .3); }

/* Huy hiệu nằm trên nền đỏ phải tự đổi màu cho đọc được. */
.topbar .badge { color: #fff; background: rgba(255, 255, 255, .16); }
.topbar .badge-up .dot { background: #7ae2a5; }
.topbar .badge-down .dot { background: #ffc9c9; }
.topbar .badge-wait .dot { background: rgba(255, 255, 255, .7); }

/* ==========================================================================
   Thanh tab ngang
   --------------------------------------------------------------------------
   Nằm ngay dưới thanh công cụ đỏ và dính theo khi cuộn, nên lúc nào cũng
   chuyển được khu vực mà không phải cuộn ngược lên đầu trang.
   Tab đang mở đánh dấu bằng GẠCH CHÂN đỏ thương hiệu chứ không tô nền: thanh
   này nằm ngay dưới một dải đỏ lớn, tô thêm một mảng đỏ nữa thì nặng mắt.
   ========================================================================== */

.tabbar {
  position: sticky;
  /* Dính ngay dưới .topbar. Thanh công cụ có thể cao hai dòng trên màn hình
     hẹp nên không đặt được con số cố định — dùng 0 và để z-index thấp hơn
     topbar, thanh tab sẽ trượt xuống dưới thanh công cụ khi cuộn. */
  top: 0;
  z-index: 20;
  display: flex;
  gap: 2px;
  overflow-x: auto;
  padding: 0 16px;
  background: var(--card);
  border-bottom: 1px solid var(--line);
  /* Không hiện thanh cuộn ngang khi các tab tràn trên điện thoại. */
  scrollbar-width: none;
}
.tabbar::-webkit-scrollbar { display: none; }

.tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: none;
  min-height: 46px;
  padding: 0 14px;
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
  background: none;
  border: 0;
  border-bottom: 3px solid transparent;
  white-space: nowrap;
}
.tab:hover { color: var(--text); background: #fafbfc; }
.tab[aria-selected="true"] { color: var(--brand); border-bottom-color: var(--brand); }
.tab:focus-visible { outline: none; box-shadow: inset var(--focus-ring); }

/* Con số đỏ trên tab Monitor: bao nhiêu địa chỉ đang lỗi. Có nó thì đang ở tab
   khác vẫn biết bên kia có chuyện, không phải bấm sang xem. */
.tab-dem {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  background: var(--danger);
  border-radius: 20px;
}

.container { max-width: 1080px; margin: 0 auto; padding: 20px 16px 40px; }

/* Tiêu đề khu vực phụ nằm giữa trang (ví dụ "Nhật ký sự cố"), cần thở ra khỏi
   khối phía trên. */
.section-head-phu { margin-top: 22px; }

.section { margin-bottom: 26px; }
.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.section-head h2 { margin: 0; font-size: 15px; }
.section-head .hint { font-size: 12.5px; color: var(--muted); }

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
  margin-bottom: 12px;
}
.card h3 { margin: 0 0 2px; font-size: 15px; }
.card .desc { margin: 0 0 12px; font-size: 13px; color: var(--muted); }

/* Hàng tiêu đề của thẻ DNS: tên miền bên trái, trạng thái tự động chuyển
   hướng nằm ngay sau đó. Màn hình hẹp thì trạng thái tự xuống dòng dưới tên
   miền chứ không ép tên miền co lại. */
.dns-head {
  display: flex;
  /* Bám mép trên vì khối trạng thái bên phải có thể cao nhiều dòng — canh giữa
     thì tên miền bị trôi xuống lưng chừng. */
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin-bottom: 8px;
}

/* Tiêu đề thẻ DNS chính là tên miền, nên trình bày như một thẻ tag —
   cùng kiểu nền với thẻ <code> để người đọc thấy ngay đây là giá trị kỹ thuật. */
.dns-title {
  display: inline-block;
  margin: 0;
  padding: 4px 11px;
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 14.5px;
  font-weight: 700;
  color: var(--text);
  background: var(--mute-soft);
  border-radius: 6px;
  overflow-wrap: anywhere;
}

/* ==========================================================================
   Lựa chọn server
   ========================================================================== */

/* Nhãn nhỏ phía trên danh sách server, cho biết khối bên dưới là chỗ để CHỌN
   chứ không phải một bảng trạng thái chỉ để đọc. */
.options-label {
  margin: 0 0 6px 2px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--muted);
}

.server-options { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.server-option {
  display: flex;
  /* Bám mép trên để nút tròn nằm cùng dòng với tên server, không bị kéo
     xuống giữa khi ghi chú hay huy hiệu xuống dòng hai trên màn hình hẹp. */
  align-items: flex-start;
  gap: 10px;
  min-height: var(--tap);
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
}

/* Tên + IP + ghi chú + huy hiệu nằm chung một khối, tách khỏi nút tròn.
   Nhờ vậy khi màn hình hẹp và huy hiệu bị đẩy xuống dòng hai, nó thẳng
   hàng với tên server chứ không thụt ra sát mép trái của thẻ. */
.server-info {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px 10px;
}
.server-option:hover { border-color: #cfd5dc; background: #fafbfc; }
.server-option.active { border-color: var(--brand); background: #fdf4f4; }

/* Đang đọc hiện trạng DNS -> mờ đi và không bấm được. */
.server-option.is-locked { opacity: .5; cursor: progress; }
.server-option.is-locked:hover { border-color: var(--line); background: transparent; }
.server-option.is-locked.active:hover { border-color: var(--brand); background: #fdf4f4; }
.server-option .name { font-weight: 700; }
.server-option .ip {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 13px;
  color: var(--muted);
}

/* Nhãn vai trò trong cụm: server chính / server phụ. */
.role {
  padding: 1px 7px;
  font-size: 10px;
  font-weight: 700;
  border-radius: 20px;
  white-space: nowrap;
}
.role-main { color: var(--brand); background: #fdeeed; }
.role-secondary { color: var(--muted); background: var(--mute-soft); }

/* Ghi chú nằm ngay sau IP, không bị đẩy sang mép phải. */
.server-option .note { font-size: 12.5px; color: var(--muted); }
.server-option .note::before { content: '· '; }

/* Huy hiệu trạng thái trong thẻ server nhỏ hơn huy hiệu chung một chút,
   để không lấn át tên server. */
.server-option .badge { padding: 1px 7px; font-size: 10px; }
.server-option .badge .dot { width: 6px; height: 6px; }

/* Nút tròn: bỏ khung vuông mặc định của trình duyệt khi focus,
   thay bằng vòng sáng tròn cho vừa với hình dạng nút. */
.server-option input {
  /* 5px để tâm nút tròn (13px) trùng tâm dòng chữ tên server (~23px). */
  margin: 5px 0 0;
  accent-color: var(--brand);
  outline: none;
  border-radius: 50%;
}
.server-option input:focus { outline: none; box-shadow: none; }
.server-option input:focus-visible { box-shadow: 0 0 0 3px rgba(201, 67, 61, .26); }

/* Vòng quay nhỏ đặt lẫn trong dòng chữ "đang kiểm tra...". */
.inline-spin {
  display: inline-block;
  width: 12px;
  height: 12px;
  vertical-align: -2px;
  border: 2px solid var(--line);
  border-top-color: var(--muted);
  border-radius: 50%;
  animation: dlgSpin .7s linear infinite;
}

/* Trạng thái tự động chuyển hướng, nằm ngay sau tên miền trong .dns-head.
   min-width: 0 để phần chữ dài tự ngắt dòng trong ô của nó thay vì đẩy tràn
   cả hàng tiêu đề ra ngoài thẻ. */
.failover-line {
  display: flex;
  /* Bám mép trên: phần chữ có nhiều dòng, huy hiệu phải nằm ngang dòng đầu
     chứ không trôi xuống giữa khối. */
  align-items: flex-start;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
  /* Nhích xuống cho dòng đầu ngang tầm giữa thẻ tên miền (.dns-title cao hơn
     vì có padding). */
  padding-top: 3px;
  font-size: 12.5px;
  color: var(--muted);
}

/* Mỗi ý một dòng: đang đếm mấy lần, kết quả lượt cuối, lúc nào, chuyển lần cuối. */
.failover-line .fo-khoi {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.failover-line .fo-dong { display: block; }

/* "(3 phút trước)" phải đi liền một khối với giờ đứng trước nó. */
.failover-line .tuong-doi,
.current-line .tuong-doi { white-space: nowrap; }
.failover-line:empty { display: none; }
.failover-line .badge { padding: 1px 7px; font-size: 10px; }
.failover-line .dot { width: 6px; height: 6px; }

/* Lượt kiểm tra vừa rồi có phát hiện lỗi: phần chữ chuyển đỏ, có dấu chấm than
   phía trước. Huy hiệu "TỰ ĐỘNG: BẬT" vẫn xanh vì nó nói về tính năng, không
   nói về sức khoẻ server. */
.failover-line .fo-canh-bao {
  display: inline-flex;
  align-items: flex-start;
  gap: 6px;
  min-width: 0;
  color: var(--danger);
  font-weight: 600;
}
.failover-line .fo-cham {
  margin-top: 2px;   /* canh giữa với dòng chữ đầu tiên */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  font-style: normal;
  line-height: 1;
}

.head-actions { display: flex; gap: 8px; }

.current-line {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 9px 12px;
  background: var(--bg);
  border-radius: 8px;
  font-size: 13.5px;
  margin-bottom: 12px;
}

/* Một cụm có thể có nhiều tên miền: mỗi tên miền một dòng hiện trạng, còn thao
   tác thì dùng chung ở dưới. Khoảng cách giữa các dòng hẹp lại cho thấy rõ
   chúng là một nhóm. */
.mien-khoi + .mien-khoi { margin-top: -6px; }
.mien-khoi .current-line { margin-bottom: 8px; }

/* Tên miền đứng đầu dòng hiện trạng, trình bày như thẻ tag giống .dns-title
   nhưng nhỏ hơn vì ở đây nó là nhãn chứ không phải tiêu đề. */
.mien {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 12.5px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 5px;
  color: var(--text);
  background: var(--mute-soft);
  overflow-wrap: anywhere;
}

/* Mô tả riêng của từng tên miền. */
.mien-desc { margin: -4px 0 10px 12px; font-size: 12.5px; }

/* Các tên miền của cụm đang trỏ về khác nhau — trạng thái bất thường, cần
   người vào bấm một lần cho thống nhất. */
.canh-bao-lech {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 0 0 12px;
  padding: 9px 12px;
  border: 1px solid #f3d3d2;
  border-radius: 8px;
  background: var(--danger-soft);
  color: var(--danger);
  font-size: 12.5px;
  font-weight: 600;
}
.canh-bao-lech .fo-cham {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 14px;
  height: 14px;
  margin-top: 2px;
  border-radius: 50%;
  background: var(--danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  font-style: normal;
  line-height: 1;
}

/* ==========================================================================
   Tab Monitor
   ========================================================================== */

/* Dải nhắc nhở phía trên danh sách (chưa gắn KV, chưa khai Pushover...). */
.mon-nhac {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 12px;
  padding: 10px 13px;
  border-radius: 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--warn);
  background: var(--warn-soft);
  border: 1px solid #f0dfc0;
}

/* Thẻ một địa chỉ đang canh. Viền trái đổi màu theo trạng thái để lướt mắt
   xuống cả danh sách là thấy ngay cái nào đang đỏ, không phải đọc từng huy
   hiệu. */
.mon-the { border-left: 4px solid var(--line); }
.mon-the.mon-up { border-left-color: var(--ok); }
.mon-the.mon-down { border-left-color: var(--danger); }

/* Hàng đầu của thẻ — thứ duy nhất luôn hiện:
     [uptime % · OK/Lỗi]  tên  ......... thanh nhịp  [nút chi tiết]
   Thanh nhịp tự đẩy mình sang phải bằng `margin-left: auto`, nên dù tên dài
   ngắn thế nào các thanh của mọi thẻ vẫn thẳng hàng nhau. */
.mon-dau {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  /* Chừa chỗ cho nhãn thời gian treo bên dưới thanh nhịp. */
  padding-bottom: 18px;
}
.mon-ten { margin: 0; font-size: 15px; font-weight: 700; }

/* Nút gấp/mở. Kiểu chữ nhạt, viền mảnh — nó là lối đi phụ, không được tranh
   sự chú ý với huy hiệu trạng thái ngay bên cạnh. */
.mon-mo {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  padding: 4px 9px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted);
  background: none;
  border: 1px solid var(--line);
  border-radius: 20px;
}
.mon-mo:hover { color: var(--text); background: #f7f8f9; }
.mon-mo:focus-visible { outline: none; border-color: var(--brand); box-shadow: var(--focus-ring); }
.mon-mo-mui { transition: transform .25s ease; }
.mon-the.mo .mon-mo-mui { transform: rotate(180deg); }

/* Phần chi tiết gấp lại được.
   Dùng grid 0fr -> 1fr thay cho max-height: không phải đoán trước chiều cao,
   và mở ra bao nhiêu nội dung cũng chạy mượt. Trình duyệt cũ không hiểu `0fr`
   thì luật bị bỏ qua và phần chi tiết luôn mở — hỏng theo hướng an toàn, người
   dùng vẫn đọc được mọi thứ. */
.mon-chi-tiet {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .28s ease, opacity .2s ease;
  opacity: 0;
}
.mon-chi-tiet-trong { overflow: hidden; }
.mon-the.mo .mon-chi-tiet { grid-template-rows: 1fr; opacity: 1; }

/* Địa chỉ và ghi chú — nay nằm trong phần chi tiết. */
.mon-dia-chi {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  padding-top: 12px;
}

@media (prefers-reduced-motion: reduce) {
  .mon-chi-tiet, .mon-mo-mui { transition: none !important; }
}

/* Địa chỉ được canh: trình bày như giá trị kỹ thuật, giống .mien ở tab kia. */
.mon-url {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 12.5px;
  padding: 2px 8px;
  border-radius: 5px;
  color: var(--text);
  background: var(--mute-soft);
  overflow-wrap: anywhere;
}

/* Huy hiệu ĐẦU HÀNG — mang cả uptime 24 giờ lẫn trạng thái lúc này
   ("100% · OK", "99.8% · Lỗi"). Từ 1.19.1 nó thay luôn huy hiệu trạng thái
   riêng đứng sau tên: hai huy hiệu cạnh nhau nói về cùng một địa chỉ thì mắt
   phải đọc cả hai mới kết luận được, gộp lại còn đúng một chỗ để liếc.
   Đỏ ở đây luôn có nghĩa "đang hỏng", không phải "phần trăm thấp". */
.mon-uptime {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Đủ chỗ cho "100% · OK" mà không nhảy bề rộng khi đổi qua lại OK/Lỗi. */
  min-width: 94px;
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.mon-uptime-ok   { color: var(--ok);     background: var(--ok-soft); }
.mon-uptime-warn { color: var(--warn);   background: var(--warn-soft); }
.mon-uptime-down { color: var(--danger); background: var(--danger-soft); }
.mon-uptime-cho  { color: var(--muted);  background: var(--mute-soft); }

/* ---- Thanh lịch sử (heartbeat) ----
   Moi vach la mot luot kiem tra. Vach MOI NHAT nam ben PHAI: trong HTML no
   dung truoc, rồi row-reverse lật lại. Nhờ vậy khi màn hình hẹp, phần tràn ra
   ngoài và bị `overflow: hidden` cắt đi là mấy vạch CŨ ở mép trái — vạch mới
   nhất không bao giờ bị mất. */
.mon-nhip-khoi { margin: 12px 0; }

/* ---- Hình học lấy theo Uptime Kuma ----
   Vạch 5px, cao 16px, bo tròn hẳn thành viên thuốc, cách nhau 4px. Trước đây
   vạch chia đều bề ngang thẻ nên trên màn hình rộng mỗi vạch thành một khối
   17px — to như viên gạch, không còn ra dáng nhịp tim. Bề ngang cố định thì
   thanh dài đúng bằng số nhịp, dư bao nhiêu để trống bấy nhiêu. */

/* Thanh nhịp nằm ngay trên hàng đầu, bám mép phải, và chỉ rộng đúng bằng số
   nhịp — nhờ vậy mốc thời gian bên dưới nằm khít đầu phải của thanh chứ không
   trôi ra tận mép thẻ.

   `position: relative` để nhãn thời gian treo tuyệt đối bên dưới. Lý do: nhãn
   nằm trong luồng thì chiều cao khối = thanh + nhãn, mà hàng đầu canh giữa
   theo khối, nên THANH bị đẩy lên cao hơn mấy cái huy hiệu đứng trước nó. Treo
   nhãn ra ngoài luồng thì chiều cao khối đúng bằng chiều cao thanh, và thanh
   nằm ngang hàng với nhãn phía trước. Chỗ cho nhãn được chừa bằng
   `padding-bottom` của `.mon-dau`. */
.mon-nhip-khoi {
  position: relative;
  width: max-content;
  max-width: 100%;
  margin-left: auto;
}

/* Khung ngoài chỉ làm một việc: cắt phần tràn ra. Nó KHÔNG được dịch chuyển —
   khung dịch thì vùng cắt dịch theo, vạch mới sẽ thò hẳn ra ngoài thẻ.
   Đệm trên dưới 4px để lúc rê chuột vạch phóng to ra còn chỗ, không bị cắt cụt
   (đúng mẹo Kuma dùng). */
.mon-nhip {
  overflow: hidden;
  padding: 4px 0;
}

/* Dãy vạch bên trong. Đây mới là thứ trượt sang trái mỗi khi có nhịp mới. */
.mon-nhip-day {
  display: flex;
  flex-direction: row-reverse;
  /* row-reverse nên trục chính bắt đầu từ MÉP PHẢI: flex-start ở đây có nghĩa
     là cả dãy bám sát mép phải, đúng chỗ mắt tìm vạch mới nhất. */
  justify-content: flex-start;
  align-items: center;
  gap: 4px;
}

.nhip {
  flex: 0 0 5px;
  width: 5px;
  height: 16px;
  border-radius: 50rem;
  cursor: default;
  position: relative;          /* để z-index lúc rê chuột có tác dụng */
  transition: transform .15s ease-in-out, opacity .15s ease-in-out;
}

/* Rê chuột: phóng to cả hai chiều và mờ đi một chút — y như Kuma. Phóng được
   cả chiều dọc là nhờ 4px đệm của .mon-nhip ở trên. */
.nhip:hover {
  transform: scale(1.5);
  opacity: .8;
  z-index: 2;
}

.nhip-tot   { background: var(--nhip-tot); }
.nhip-chap  { background: var(--nhip-chap); }
.nhip-loi   { background: var(--nhip-loi); }
.nhip-trong { background: var(--nhip-trong); }

/* ---- Vạch vừa tới ----
   Hai chuyển động chồng lên nhau, cố ý:

     1. Cả dãy trượt sang trái một ô (truotDay() trong dashboard.js) — vạch mới
        theo đó trôi vào từ mép phải.
     2. Riêng vạch mới thì vươn cao lên rồi hạ về đúng cỡ.

   Chỉ có mỗi (1) thì quá kín đáo: một nhịp dịch đúng 9px, liếc vào đúng lúc
   vẫn không kịp thấy gì. (2) làm cho mắt bắt được chỗ vừa thêm vào. Hai
   transform này nằm ở hai phần tử khác nhau (dãy và vạch) nên nhân với nhau
   chứ không giành nhau. */
@keyframes nhipVao {
  0%   { opacity: 0; transform: scaleY(.25); }
  55%  { opacity: 1; transform: scaleY(1.45); }
  100% { opacity: 1; transform: scaleY(1); }
}
.nhip-moi {
  /* Vươn lên từ giữa cho cân, không mọc lệch về một đầu. */
  transform-origin: center;
  animation: nhipVao .45s cubic-bezier(.34, 1.4, .64, 1);
}

/* ---- Vạch mới nhất của địa chỉ đang sống ----
   Thở nhè nhẹ, như nhịp tim. Nhìn là biết trang vẫn đang theo dõi thật chứ
   không phải một ảnh chụp đứng yên từ lúc nào. Chỉ đổi opacity nên trình duyệt
   xử lý bằng GPU, không tốn gì đáng kể dù có hàng chục thẻ. */
@keyframes nhipTho {
  0%, 100% { opacity: 1; }
  50%      { opacity: .5; }
}
.nhip-song { animation: nhipTho 2.4s ease-in-out infinite; }
.nhip-song:hover { animation: none; }

/* Ghép hai nhãn về bên phải để đọc liền mốc mới nhất và hẹn cập nhật. */
.mon-nhip-nhan {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 4px;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--muted);
  white-space: nowrap;
}

/* Giữ mốc mới nhất đọc được ngay cả khi dòng nhãn thiếu chỗ. */
.mon-nhip-moc { flex: none; }

/* Đồng hồ đếm ngược. Nhạt hơn một bậc: mốc thời gian là số liệu thật, còn đây
   chỉ là chuyện trình duyệt bao giờ hỏi lại — cùng cỡ chữ mà cùng độ đậm thì
   hai thứ tranh nhau mắt người đọc.
   Màn hình hẹp thì NÓ là thứ chịu cắt, không phải mốc thời gian. */
.mon-hen {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: .72;
}



/* ==========================================================================
   Hiệu ứng khi dữ liệu vừa đổi
   --------------------------------------------------------------------------
   Trang tự cập nhật nên người dùng không bấm gì cả — nếu một con số lặng lẽ
   đổi thì không ai nhận ra. Hiệu ứng ở đây chỉ để TRẢ LỜI CÂU "vừa có gì đổi
   thế?", nên chúng đánh dấu đúng chỗ vừa đổi rồi tắt hẳn, không có gì chạy
   mãi ngoài nhịp thở của vạch mới nhất.
   ========================================================================== */

/* Một ô số vừa đổi giá trị: loé nền xanh thông tin rồi tắt dần. */
@keyframes oVuaDoi {
  from { background: var(--info-soft); color: var(--info); }
  60%  { background: var(--info-soft); color: var(--info); }
  to   { background: transparent; }
}
.mon-so .gt {
  display: inline-block;
  /* Nới ra rồi thụt vào đúng bằng chừng ấy: vệt sáng ôm lấy chữ cho dễ thấy
     mà không làm xê dịch bố cục khi hiệu ứng chạy. */
  padding: 1px 4px;
  margin: -1px -4px;
  border-radius: 4px;
}
.vua-doi { animation: oVuaDoi 1.6s ease-out; }

/* Huy hiệu / con số đáng chú ý: nảy lên một nhịp. Dùng cho lúc đổi trạng thái
   và cho con số đỏ trên nhãn tab. */
@keyframes nayLen {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.22); }
  100% { transform: scale(1); }
}
.nay-len { animation: nayLen .55s cubic-bezier(.34, 1.56, .64, 1); }

/* Cả thẻ đổi trạng thái: quét một vệt nền rồi tắt. Đây là chuyện đáng chú ý
   nhất có thể xảy ra trên thẻ nên nó được phép "to tiếng" hơn các ô số. */
@keyframes theHong {
  from { background: var(--danger-soft); box-shadow: 0 0 0 3px rgba(198, 40, 40, .18); }
  to   { background: var(--card); box-shadow: var(--shadow); }
}
@keyframes theHoiPhuc {
  from { background: var(--ok-soft); box-shadow: 0 0 0 3px rgba(26, 127, 69, .18); }
  to   { background: var(--card); box-shadow: var(--shadow); }
}
.the-hong     { animation: theHong 1.8s ease-out; }
.the-hoi-phuc { animation: theHoiPhuc 1.8s ease-out; }

/* Dòng nhật ký vừa thêm: trượt xuống từ trên. */
@keyframes dongMoi {
  from { opacity: 0; transform: translateY(-8px); background: var(--info-soft); }
  to   { opacity: 1; transform: none; background: transparent; }
}
.dong-moi { animation: dongMoi .5s ease-out; }

/* Viền trái của thẻ đổi màu mượt thay vì nhảy cái rụp. */
.mon-the { transition: border-left-color .35s ease; }

/* Ai đã tắt hiệu ứng chuyển động trong hệ điều hành thì tôn trọng: bỏ hết,
   kể cả nhịp thở. Dữ liệu vẫn cập nhật y nguyên, chỉ là không nhúc nhích. */
@media (prefers-reduced-motion: reduce) {
  .nhip-moi, .nhip-song, .vua-doi, .nay-len,
  .the-hong, .the-hoi-phuc, .dong-moi { animation: none !important; }
  .nhip, .mon-the { transition: none !important; }
}

/* Hàng số liệu: mã HTTP, thời gian phản hồi, lần kiểm tra cuối... */
.mon-so {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin-top: 10px;
  padding: 9px 12px;
  background: var(--bg);
  border-radius: 8px;
  font-size: 12.5px;
}
.mon-so div { min-width: 0; }
.mon-so .nhan { display: block; font-size: 11px; color: var(--muted); }
.mon-so .gt { font-weight: 700; overflow-wrap: anywhere; }

/* Dòng lý do khi đang lỗi. */
.mon-loi {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin-top: 10px;
  padding: 9px 12px;
  border: 1px solid #f3d3d2;
  border-radius: 8px;
  background: var(--danger-soft);
  color: var(--danger);
  font-size: 12.5px;
  font-weight: 600;
}
/* Lý do lỗi nay kèm cả đường đi chuyển hướng, nên phải cho phép ngắt giữa một
   địa chỉ dài thay vì để nó đẩy tràn thẻ ra ngoài. */
.mon-loi > span {
  min-width: 0;
  overflow-wrap: anywhere;
}
.mon-loi .fo-cham {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 14px;
  height: 14px;
  margin-top: 2px;
  border-radius: 50%;
  background: var(--danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  font-style: normal;
  line-height: 1;
}

/* Dòng ghi chú ngưỡng: thử mấy lần, cách nhau bao lâu. */
.mon-nguong { margin-top: 8px; font-size: 12px; color: var(--muted); }

/* Nhật ký sự cố — mỗi dòng một sự kiện, gọn như một bảng. */
.mon-nk { padding: 0; overflow: hidden; }
.mon-nk-dong {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 14px;
  font-size: 13px;
}
.mon-nk-dong + .mon-nk-dong { border-top: 1px solid var(--line); }
/* Cột thời gian: đủ rộng cho "13:42:16 16/9/2026 (vừa xong)" nằm gọn một dòng.
 *
 * VÌ SAO KHÔNG ĐỂ 128px NHƯ TRƯỚC: chỗ đó vừa đúng phần ngày giờ, nên cụm
 * "(vừa xong)" bị đẩy xuống và còn bị CẮT ĐÔI giữa hai chữ. Hai thẻ con dưới
 * đây giữ từng mẩu nguyên vẹn: màn hình hẹp thì xuống dòng giữa hai mẩu, không
 * bao giờ cắt ngang một cụm. */
.mon-nk-luc { flex: none; width: 200px; color: var(--muted); font-size: 12px; }
.mon-nk-luc .moc-tuyet-doi,
.mon-nk-luc .tuong-doi { white-space: nowrap; }
.mon-nk-noi { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.mon-nk-ten { font-weight: 700; }

@media (max-width: 560px) {
  .mon-nk-dong { flex-direction: column; gap: 4px; }
  .mon-nk-luc { width: auto; }
}

/* ==========================================================================
   Huy hiệu trạng thái
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: 20px;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}
.badge-up { color: var(--ok); background: var(--ok-soft); }
.badge-down { color: var(--danger); background: var(--danger-soft); }
.badge-wait { color: var(--muted); background: var(--mute-soft); }
.dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }

/* Dòng chú thích nhỏ, màu nhạt (thời điểm cập nhật, ghi chú quyền hạn...). */
.meta-note { font-size: 12.5px; color: var(--muted); overflow-wrap: anywhere; }

.footer-note { font-size: 12.5px; color: var(--muted); text-align: center; padding: 8px 0 24px; }

/* ==========================================================================
   Hộp thoại HTML (thay alert / confirm / prompt của trình duyệt)
   ========================================================================== */

.dlg-lock { overflow: hidden; }

.dlg-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  background: rgba(16, 24, 40, .52);
  animation: dlgFadeIn .14s ease-out;
}
.dlg-overlay.dlg-closing { animation: dlgFadeOut .13s ease forwards; }

/* Khung hộp thoại LUÔN mang tông đỏ thương hiệu cho đồng bộ với cả trang.
   Chỉ riêng biểu tượng đổi màu theo sắc thái để người dùng nhận ra ngay
   đây là tin vui, lời cảnh báo hay việc nguy hiểm.
     --dlg-color : màu biểu tượng
     --dlg-soft  : nền vòng tròn biểu tượng */
.dlg-box {
  --dlg-color: var(--info);
  --dlg-soft: var(--info-soft);

  width: min(440px, 100%);
  overflow: hidden;
  background: var(--card);
  border: 1px solid #f0cecc;
  border-top: 4px solid var(--brand);
  border-radius: 14px;
  box-shadow: 0 18px 50px rgba(16, 24, 40, .28);
  animation: dlgPopIn .16s ease-out;
}
.dlg-overlay.dlg-closing .dlg-box { animation: dlgPopOut .13s ease forwards; }

/* Sắc thái chỉ đổi màu biểu tượng, không đổi khung. */
.dlg-info,
.dlg-question { --dlg-color: var(--info);   --dlg-soft: var(--info-soft); }
.dlg-success  { --dlg-color: var(--ok);     --dlg-soft: var(--ok-soft); }
.dlg-warning  { --dlg-color: var(--warn);   --dlg-soft: var(--warn-soft); }
.dlg-danger   { --dlg-color: var(--danger); --dlg-soft: var(--danger-soft); }

.dlg-body { display: flex; gap: 12px; padding: 18px 18px 14px; }

.dlg-icon {
  flex: 0 0 34px;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: var(--dlg-color);
  background: var(--dlg-soft);
}
.dlg-icon svg { width: 20px; height: 20px; }

.dlg-content { min-width: 0; flex: 1; }
.dlg-title { margin: 0 0 5px; font-size: 16px; color: var(--text); }
.dlg-message { margin: 0; font-size: 14px; color: var(--text); line-height: 1.55; word-break: break-word; }

.dlg-details {
  margin-top: 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}
.dlg-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 12px;
  font-size: 13.5px;
  background: #fbfcfd;
}
.dlg-row + .dlg-row { border-top: 1px solid var(--line); }
.dlg-row-label { color: var(--muted); white-space: nowrap; }
.dlg-row-value { font-weight: 700; text-align: right; overflow-wrap: anywhere; }
.dlg-row-value.mono { font-family: ui-monospace, Consolas, monospace; font-weight: 600; }

.dlg-input {
  width: 100%;
  min-height: var(--tap);
  margin-top: 12px;
  padding: 8px 11px;
  font-family: inherit;
  color: var(--text);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.dlg-input:focus { outline: none; border-color: var(--brand); box-shadow: var(--focus-ring); }

/* Lỗi hiện ngay trong hộp thoại khi việc chạy nền thất bại. */
.dlg-error {
  margin-top: 12px;
  padding: 9px 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--danger);
  background: var(--danger-soft);
  border-radius: 8px;
}

/* Vòng quay chờ trên nút chính khi đang gọi API. */
.dlg-spin {
  width: 14px;
  height: 14px;
  border: 2px solid rgba(255, 255, 255, .45);
  border-top-color: #fff;
  border-radius: 50%;
  animation: dlgSpin .7s linear infinite;
}
@keyframes dlgSpin { to { transform: rotate(360deg) } }

.dlg-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px 16px;
  background: #fafbfc;
  border-top: 1px solid var(--line);
}
.dlg-actions .btn { min-height: 38px; min-width: 92px; }

/* Nút chính của hộp thoại giống hệt nút chính của trang: đỏ thương hiệu.
   Ghi lại đủ background + color ở hover, nếu không quy tắc .btn:hover phía
   trên sẽ đổi nền thành xám trong khi chữ vẫn trắng. */
.dlg-accent {
  color: #fff;
  background: var(--brand);
  border-color: var(--brand);
}
.dlg-accent:hover:not(:disabled) {
  color: #fff;
  background: var(--brand-dark);
  border-color: var(--brand-dark);
}
.dlg-accent:focus-visible {
  outline: none;
  border-color: var(--brand-dark);
  box-shadow: 0 0 0 3px rgba(201, 67, 61, .32);
}

@keyframes dlgFadeIn  { from { opacity: 0 } to { opacity: 1 } }
@keyframes dlgFadeOut { from { opacity: 1 } to { opacity: 0 } }
@keyframes dlgPopIn   { from { opacity: 0; transform: translateY(8px) scale(.98) } to { opacity: 1; transform: none } }
@keyframes dlgPopOut  { to { opacity: 0; transform: translateY(6px) scale(.98) } }

@media (max-width: 480px) {
  .dlg-actions .btn { flex: 1; }
}

/* ==========================================================================
   Tooltip riêng (thay thuộc tính title của trình duyệt)
   ========================================================================== */

.tip {
  position: fixed;
  z-index: 900;
  max-width: min(320px, calc(100vw - 16px));
  padding: 7px 10px;
  font-size: 12.5px;
  line-height: 1.45;
  color: #fff;
  background: #1b1f24;
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(10, 15, 30, .28);
  pointer-events: none;   /* không chắn chuột của phần tử bên dưới */
  opacity: 0;
  transform: translateY(2px);
  transition: opacity .12s ease, transform .12s ease;
}
.tip.tip-hien { opacity: 1; transform: none; }

.tip-dong { display: block; overflow-wrap: anywhere; }
.tip-dong + .tip-dong { color: rgba(255, 255, 255, .78); }

/* Mũi nhọn chỉ vào phần tử; --tip-mui do JavaScript đặt theo tâm phần tử. */
.tip::after {
  content: '';
  position: absolute;
  left: var(--tip-mui, 50%);
  margin-left: -5px;
  border: 5px solid transparent;
}
.tip::after { top: 100%; border-top-color: #1b1f24; }
.tip.tip-duoi::after { top: auto; bottom: 100%; border-top-color: transparent; border-bottom-color: #1b1f24; }

@media (prefers-reduced-motion: reduce) {
  .tip { transition: none; }
}

/* ==========================================================================
   Thông báo nổi (toast) — nền tối, nằm giữa mép dưới, giống skyhotel-admin
   ========================================================================== */

.toast-stack {
  position: fixed;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  z-index: 80;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: max-content;
  max-width: 90vw;
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 14px;
  font-size: 13.5px;
  color: #fff;
  background: #1b1f24;
  border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .25);
  animation: toastIn .18s ease-out;
}
.toast.toast-closing { animation: toastOut .18s ease forwards; }
.toast svg { flex: 0 0 auto; width: 17px; height: 17px; margin-top: 1px; }
.toast span { flex: 1; overflow-wrap: anywhere; }
.toast-close {
  flex: 0 0 auto;
  padding: 0 2px;
  font-size: 17px;
  line-height: 1;
  color: rgba(255, 255, 255, .6);
  background: none;
  border: 0;
}
.toast-close:hover { color: #fff; }

.toast-info { background: #1b1f24; }
.toast-success { background: var(--ok); }
.toast-warning { background: var(--warn); }
.toast-danger { background: var(--danger); }

@keyframes toastIn  { from { opacity: 0; transform: translateY(10px) } to { opacity: 1; transform: none } }
@keyframes toastOut { to { opacity: 0; transform: translateY(6px) } }

@media (prefers-reduced-motion: reduce) {
  .dlg-overlay, .dlg-box, .toast { animation: none !important; }
  .dlg-spin { animation-duration: 1.4s; }   /* vẫn quay để biết là đang chờ */
}

@media (max-width: 640px) {
  .container { padding: 16px 12px 32px; }
  .topbar { padding: 10px 12px; }
}
