@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/geist-400-vietnamese.woff2?v=8fa40e5d') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/geist-400-latin.woff2?v=19f9c925') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/geist-500-vietnamese.woff2?v=8fa40e5d') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/geist-500-latin.woff2?v=19f9c925') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/geist-600-vietnamese.woff2?v=8fa40e5d') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/geist-600-latin.woff2?v=19f9c925') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/geist-700-vietnamese.woff2?v=8fa40e5d') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/geist-700-latin.woff2?v=19f9c925') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/inter-400-vietnamese.woff2?v=5c66f9e0') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/inter-400-latin.woff2?v=3100e775') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/inter-500-vietnamese.woff2?v=5c66f9e0') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/inter-500-latin.woff2?v=3100e775') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/inter-600-vietnamese.woff2?v=5c66f9e0') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/inter-600-latin.woff2?v=3100e775') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Noto Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/noto-sans-400-vietnamese.woff2?v=791e3f21') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Noto Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/noto-sans-500-vietnamese.woff2?v=791e3f21') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Noto Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/noto-sans-600-vietnamese.woff2?v=791e3f21') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Noto Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/noto-sans-700-vietnamese.woff2?v=791e3f21') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Material Symbols Outlined';
  font-style: normal;
  font-weight: 100 700;
  font-display: block;
  src: url('/static/fonts/material-symbols-outlined.woff2?v=3482a76b') format('woff2');
}
/* ══════════════════════════════════════════════════════════════════════
   VPAY — hệ giao diện của ví điện tử FIN-05
   Dựng theo bản bàn giao "Wallet VPAY All" của Claude Design.

   Ba điều ràng buộc, đọc trước khi sửa:

   1. Tên lớp là HỢP ĐỒNG với app.js và 16 template. app.js bắt tay vào khoảng
      40 tên lớp/id (chat-thread, bubble, trace, chuong-*, toast-*, bang-*,
      vat-*, ma-gd, md-table, foc…). Đổi vẻ ngoài thì tuỳ, đổi TÊN là gãy chức
      năng — và gãy im lặng, không có lỗi nào hiện ra.

   2. Nhãn tiếng Việt nằm ở labels.py, không nằm ở đây. CSS chỉ tô màu cho các
      lớp tone-* mà labels.py chỉ định.

   3. Số tiền dùng tabular-nums ở mọi chỗ. Cột tiền nhảy chữ số khi đổi giá trị
      là thứ khiến bảng tài chính trông như đang sai.
   ══════════════════════════════════════════════════════════════════════ */

:root {
  /* ── Bảng màu VPAY ──────────────────────────────────────────────── */
  /*  Khối đặc dùng CHUNG một mã với nền tối. Bản chàm cũ dùng hai sắc khác
      nhau cho hai nền, nên thẻ số dư đổi màu khi bật/tắt nền tối và hai bên
      đọc ra như hai sản phẩm. Một mã cho cả hai thì thẻ trông y hệt dù đang
      ở nền nào — đổi lại, nền sáng mất quyền chỉnh riêng độ đậm. */
  --vp-primary: #0b6f68;        /* khối đặc: nút chính, thẻ số dư, sidebar đang chọn */
  --vp-primary-2: #0f877e;      /* hover, cột biểu đồ thường */
  --vp-primary-text: #0a5f59;   /* CHỮ màu thương hiệu — đậm hơn khối đặc để đọc được trên mặt sáng */
  --vp-on-primary: #ffffff;
  --vp-on-primary-soft: #c2efe9;
  --vp-on-primary-soft-2: #ddf6f3;

  /* Tím = trợ lý AI. Xanh tím = ví. Hai màu không được lẫn: khách phải phân
     biệt được "ví nói" với "trợ lý nói" mà không cần đọc chữ. */
  /*  Tím VẪN là màu của trợ lý — nó đánh dấu "thứ trợ lý nói ra", và đó là
      thông tin, không phải trang trí. Nhưng hai tông NHẠT phải dựng lại.

      Bộ cũ (#e9ddff / #d0bcff) là hoa oải hương thuần, không một chút xanh nào,
      vì nó được pha cho nền chàm ngày trước. Đặt lên mặt bạc hà mới thì nó
      không đọc ra là "vùng của trợ lý" nữa mà đọc ra là mảng sót lại của một
      bảng màu khác — đúng chỗ trang trợ lý trông như chưa được làm mới.

      Bộ mới pha tím vào chính `--vp-surface` mới, nên nó nằm cùng họ với mọi
      mặt phẳng khác trong khi vẫn giữ nguyên sắc tím. */
  --vp-ai: #5a34c4;
  --vp-ai-2: #7050d8;
  --vp-ai-soft: #eae6f6;
  --vp-ai-line: #cec3ea;
  /*  CHỮ nằm trên khối đặc màu trợ lý. Phải là token riêng vì `--vp-ai` LẬT
      SÁNG ở nền tối (#571ac0 → #b79dff): ghi cứng `color: #fff` thì nền tối
      thành ra trắng trên tím nhạt, tỉ lệ tương phản ~1,6 — đọc không nổi.
      Đây là cặp song sinh của `--vp-on-primary`, chỉ khác là bên kia không cần
      lật vì `--vp-primary` vẫn đậm ở cả hai nền. */
  --vp-on-ai: #ffffff;

  --vp-ink: #0f1e1d;
  --vp-muted: #40514f;
  /*  #777587 của bản chàm chỉ đạt 4,35:1 trên mặt thẻ — dưới ngưỡng AA, và
      chưa từng có gì kêu vì không ai đo. Bản này đo rồi: 5,05:1. */
  --vp-faint: #5c6a68;
  --vp-line: #b4c7c4;
  --vp-line-soft: #cee0dd;

  /*  KHÔNG CÓ MÀU TRẮNG NÀO TRONG THANG NÀY, và đó là chủ ý.

      Bộ trước đặt `--vp-surface: #f9fdfc` và `--vp-surface-2: #ffffff`, tức là
      thẻ — thứ phủ gần hết màn hình — gần như trắng tinh. Trên màn sáng trong
      phòng thường, mảng đó loá đúng theo nghĩa đen: mắt phải nheo lại để đọc
      chữ đen trên nó, và cảm giác ấy không hiện ra ở bất kỳ phép đo tương phản
      nào — vì tương phản CAO mới là thứ gây loá.

      Bản này hạ cả thang xuống khoảng 4% độ sáng và giữ NGUYÊN khoảng cách
      giữa các bậc, nên quan hệ nền → mặt → mặt nổi không đổi: mặt vẫn sáng hơn
      nền, mặt nổi vẫn sáng hơn mặt. Đây là đối xứng của luật bên nền tối (xem
      khối `prefers-color-scheme: dark`): nền tối không được đen xì, nền sáng
      không được trắng xoá — cả hai đều là cách bóp thang tầng cho phẳng lì.

      Hạ mặt xuống thì `--vp-faint` mất chút tương phản, nên nó đậm lên theo:
      #616f6d trên #f9fdfc là 5,05:1, còn #5c6a68 trên #f1f7f5 là 5,21:1. Đổi
      một cái mà không đổi cái kia là lặng lẽ trượt xuống dưới ngưỡng AA. */
  --vp-bg: #e3ecea;
  --vp-surface: #f1f7f5;
  --vp-surface-2: #f8fcfb;
  --vp-tint: #e9f2f0;
  --vp-tint-2: #deebe8;
  --vp-tint-3: #d0e4e0;
  --vp-avatar-bg: #cfe0dd;

  --vp-danger: #ba1a1a;
  /*  CHỮ nằm trên một khối đặc tô bằng MÀU CHỮ của tông trạng thái — nút "Duyệt"
      (xanh lá), nút "Chuyển" khi có cảnh báo (hổ phách), nút xoá (đỏ).

      Phải là token riêng vì các `--tone-*-fg` LẬT SÁNG ở nền tối: nền sáng
      #137333 là xanh lá đậm, nền tối #7fdca4 là xanh lá nhạt. Ghi cứng
      `color: #fff` thì nền tối thành ra trắng trên xanh nhạt, tỉ lệ ~1,4 —
      không đọc nổi chữ trên chính cái nút mà người ta sắp bấm.

      Cùng họ với `--vp-on-primary` và `--vp-on-ai`. */
  --vp-on-tone: #ffffff;

  /* ── Màu phân loại cho biểu đồ tròn ────────────────────────────
     Không lấy 4 sắc chàm/tím của thương hiệu: cạnh nhau trên một hình tròn
     thì chúng gần như một màu. Bộ dưới đây tách nhau cả về TÔNG lẫn ĐỘ SÁNG,
     nên vẫn phân biệt được khi in đen trắng hoặc với người khó phân biệt màu.

     Cũng KHÔNG mượn 5 tông trạng thái (xanh lá = thành công, đỏ = thất bại):
     ở đây các lát là LOẠI giao dịch, mượn màu trạng thái là mời người đọc
     hiểu nhầm "lát xanh = giao dịch thành công". */
  --phan-1: #0b6f68;   /* xanh ngọc — màu thương hiệu, lát lớn nhất */
  --phan-2: #6f3dd9;   /* tím */
  --phan-3: #c2185b;   /* hồng sen đậm */
  --phan-4: #e0952b;   /* hổ phách */
  --phan-5: #6b7a99;   /* xám xanh — nhóm còn lại */

  /* ── 5 tông trạng thái. Nền nhạt + chữ đậm, không viền — badge phải đọc
        được ở cỡ 11px nên độ tương phản nằm ở chữ, không ở khung. ── */
  --tone-ok-bg: #e6f4ea;      --tone-ok-fg: #137333;
  --tone-warn-bg: #fff4d8;    --tone-warn-fg: #8a5a12;
  --tone-bad-bg: #ffdad6;     --tone-bad-fg: #93000a;
  --tone-wait-bg: #d6ebe8;    --tone-wait-fg: #0a5f59;
  --tone-neutral-bg: #e4f0ed; --tone-neutral-fg: #40514f;

  /* ── Bo góc ─────────────────────────────────────────────────────── */
  --r-xs: 4px;   --r-sm: 10px;  --r-md: 12px;  --r-lg: 16px;
  --r-xl: 20px;  --r-2xl: 24px; --r-3xl: 28px; --r-full: 9999px;

  /* ── Khoảng cách ────────────────────────────────────────────────── */
  --s-1: 4px;  --s-2: 6px;  --s-3: 8px;  --s-4: 12px;
  --s-5: 16px; --s-6: 20px; --s-7: 24px; --s-8: 32px; --s-9: 44px;

  /* ── Bóng đổ. Rất nhạt: mặt phẳng phân tầng bằng nền và viền, bóng chỉ
        để nhấc thẻ nổi (popover, toast, khung máy) lên trên. ── */
  --sh-1: 0 2px 4px rgba(15, 23, 42, .04);
  --sh-2: 0 4px 12px rgba(15, 23, 42, .06);
  --sh-3: 0 8px 26px rgba(15, 23, 42, .13);
  --sh-pop: 0 8px 24px rgba(15, 23, 42, .14);

  /* ── Chữ ────────────────────────────────────────────────────────── */
  /* 'Noto Sans' đứng cuối MỌI ngăn xếp: Geist và Inter không có ký tự ₫
     (U+20AB). Thiếu nó thì mọi số tiền hiện ô vuông đậu. */
  --font-display: 'Geist', 'Noto Sans', system-ui, -apple-system, sans-serif;
  --font-body: 'Inter', 'Noto Sans', system-ui, -apple-system, sans-serif;
  --font-mono: ui-monospace, 'SFMono-Regular', 'Cascadia Mono', Consolas, monospace;

  --side-w: 216px;
  --topbar-h: 64px;
  /*  Chiều cao nút trợ lý nổi.

      Từng có thời khay toast phải đọc con số này để lùi lên cho khỏi đè lên nút.
      Không còn nữa: toast đã dời hẳn lên góc phải TRÊN, nút ở góc phải DƯỚI, hai
      thứ hết đường gặp nhau — và đó là cách gỡ đúng, thay vì bắt bên này nhớ
      kích thước của bên kia. Biến giữ lại vì chiều cao cố định là thứ giúp nút
      không co giãn theo cỡ chữ của hệ thống. */
  --nut-ai-cao: 54px;

  /* Bí danh cho hai chỗ template gọi thẳng var() */
  --brand: var(--vp-primary);
  --line: var(--vp-line);
}

/* ══ Nền tối ═══════════════════════════════════════════════════════════
   Bản bàn giao chỉ có nền sáng. Bảng tối dưới đây do mình dựng, giữ đúng
   quan hệ giữa các lớp: nền → mặt → mặt nổi, và giữ tím-AI tách khỏi
   xanh-ví. Chữ thương hiệu sáng lên (--vp-primary-text) trong khi khối đặc
   (--vp-primary) chỉ đậm vừa đủ để chữ trắng trên nó vẫn đạt tương phản. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --vp-primary: #0b6f68;
    --vp-primary-2: #0f877e;
    --vp-primary-text: #4fd6c8;
    --vp-on-primary-soft: #c2efe9;
    --vp-on-primary-soft-2: #ddf6f3;

    --vp-ai: #b79dff;
    --vp-ai-2: #9b7af0;
    /* Cùng cách dựng như nền sáng: pha tím vào mặt thẻ tối, không lấy tím thuần. */
    --vp-ai-soft: #35304f;
    --vp-ai-line: #514874;
    --vp-on-ai: #1a1130;

    --vp-ink: #e3edee;
    --vp-muted: #a9babb;
    --vp-faint: #90a4a5;
    --vp-line: #3b4a4c;
    --vp-line-soft: #334143;

    /*  Nền KHÔNG phải đen. Sáu mức dưới đây bắt đầu từ xám đá hơi ngả xanh chứ
        không từ gần đen: một mảng lớn gần đen đọc ra như cái hố chứ không như một
        mặt phẳng, và quan trọng hơn, sáu mức phân tầng không đủ chỗ tách nhau ở
        đáy thang. Đổi lại, thẻ số dư bớt chói hơn so với nền — vẫn hơn 3 lần nên
        vẫn là thứ mắt rơi vào trước tiên. */
    --vp-bg: #1e292b;
    --vp-surface: #283638;
    --vp-surface-2: #314042;
    --vp-tint: #2c3a3c;
    --vp-tint-2: #344446;
    --vp-tint-3: #3d4f50;
    --vp-avatar-bg: #35474a;

    --vp-danger: #ff8f86;
    --vp-on-tone: #14201f;

    /* Sáng hơn bản nền sáng để nổi trên mặt tối, vẫn giữ khoảng cách tông. */
    --phan-1: #2fb3a6;
    --phan-2: #8f7dff;
    --phan-3: #ff6f9c;
    --phan-4: #f0b45c;
    --phan-5: #93a1bd;

    /*  Nền viên nhạt hơn hẳn bản cũ. Nền cũ (#123726...) dựng cho một trang gần
        đen; đặt lên mặt thẻ #283638 thì chúng tối hơn cả mặt thẻ và viên trạng
        thái đọc ra như lỗ thủng thay vì như nhãn dán. */
    --tone-ok-bg: #1c3d2d;      --tone-ok-fg: #7fdca4;
    --tone-warn-bg: #43341a;    --tone-warn-fg: #f4c66a;
    --tone-bad-bg: #4b2226;     --tone-bad-fg: #ffb4ab;
    /*  "Đang chờ" mượn màu thương hiệu, y như nền sáng mượn chàm. Đổi ví sang
        xanh ngọc mà để tông chờ ở chàm cũ thì trên màn có hai màu nhấn không liên
        quan gì nhau. */
    --tone-wait-bg: #1a4449;    --tone-wait-fg: #7fd9d0;
    --tone-neutral-bg: #33403f; --tone-neutral-fg: #a9babb;

    --sh-1: 0 2px 4px rgba(0, 0, 0, .3);
    --sh-2: 0 4px 12px rgba(0, 0, 0, .34);
    --sh-3: 0 8px 26px rgba(0, 0, 0, .46);
    --sh-pop: 0 8px 24px rgba(0, 0, 0, .5);
  }
}

:root[data-theme="dark"] {
  --vp-primary: #0b6f68;
  --vp-primary-2: #0f877e;
  --vp-primary-text: #4fd6c8;
  --vp-on-primary-soft: #c2efe9;
  --vp-on-primary-soft-2: #ddf6f3;

  --vp-ai: #b79dff;
  --vp-ai-2: #9b7af0;
  /* Cùng cách dựng như nền sáng: pha tím vào mặt thẻ tối, không lấy tím thuần. */
  --vp-ai-soft: #35304f;
  --vp-ai-line: #514874;
  --vp-on-ai: #1a1130;

  --vp-ink: #e3edee;
  --vp-muted: #a9babb;
  --vp-faint: #90a4a5;
  --vp-line: #3b4a4c;
  --vp-line-soft: #334143;

  /*  Nền KHÔNG phải đen. Sáu mức dưới đây bắt đầu từ xám đá hơi ngả xanh chứ
      không từ gần đen: một mảng lớn gần đen đọc ra như cái hố chứ không như một
      mặt phẳng, và quan trọng hơn, sáu mức phân tầng không đủ chỗ tách nhau ở
      đáy thang. Đổi lại, thẻ số dư bớt chói hơn so với nền — vẫn hơn 3 lần nên
      vẫn là thứ mắt rơi vào trước tiên. */
  --vp-bg: #1e292b;
  --vp-surface: #283638;
  --vp-surface-2: #314042;
  --vp-tint: #2c3a3c;
  --vp-tint-2: #344446;
  --vp-tint-3: #3d4f50;
  --vp-avatar-bg: #35474a;

  --vp-danger: #ff8f86;
  --vp-on-tone: #14201f;

  /* Sáng hơn bản nền sáng để nổi trên mặt tối, vẫn giữ khoảng cách tông. */
  --phan-1: #2fb3a6;
  --phan-2: #8f7dff;
  --phan-3: #ff6f9c;
  --phan-4: #f0b45c;
  --phan-5: #93a1bd;

  /*  Nền viên nhạt hơn hẳn bản cũ. Nền cũ (#123726...) dựng cho một trang gần
      đen; đặt lên mặt thẻ #283638 thì chúng tối hơn cả mặt thẻ và viên trạng
      thái đọc ra như lỗ thủng thay vì như nhãn dán. */
  --tone-ok-bg: #1c3d2d;      --tone-ok-fg: #7fdca4;
  --tone-warn-bg: #43341a;    --tone-warn-fg: #f4c66a;
  --tone-bad-bg: #4b2226;     --tone-bad-fg: #ffb4ab;
  /*  "Đang chờ" mượn màu thương hiệu, y như nền sáng mượn chàm. Đổi ví sang
      xanh ngọc mà để tông chờ ở chàm cũ thì trên màn có hai màu nhấn không liên
      quan gì nhau. */
  --tone-wait-bg: #1a4449;    --tone-wait-fg: #7fd9d0;
  --tone-neutral-bg: #33403f; --tone-neutral-fg: #a9babb;

  --sh-1: 0 2px 4px rgba(0, 0, 0, .3);
  --sh-2: 0 4px 12px rgba(0, 0, 0, .34);
  --sh-3: 0 8px 26px rgba(0, 0, 0, .46);
  --sh-pop: 0 8px 24px rgba(0, 0, 0, .5);
}

/* ══ Chuyển cảnh mở/đóng ═══════════════════════════════════════════
   Mọi tấm nổi lên hay biến đi đều đi qua một trong các khung hình dưới đây.
   Hiện/ẩn tức thì làm người dùng mất dấu: một tấm bật ra giữa màn mà không có
   chuyển động thì mắt phải quét lại cả trang để hiểu vừa có gì đổi.

   Thời lượng cố ý ngắn (0,18–0,26s) — đủ để mắt bắt được hướng, chưa đủ để
   thành thứ phải ngồi chờ. Toàn bộ tắt sạch ở prefers-reduced-motion cuối file.

   `both` để giữ trạng thái cuối: thiếu nó thì tấm đang đóng nhảy lại nguyên
   hình đúng một khung hình trước khi bị gỡ khỏi DOM. */
@keyframes panel-vao { from { opacity: 0; transform: translateY(10px) scale(.985); } }
@keyframes panel-ra  { to   { opacity: 0; transform: translateY(-6px) scale(.985); } }
@keyframes tam-len   { from { transform: translateY(100%); } }
@keyframes tam-xuong { to   { transform: translateY(100%); } }
@keyframes hop-vao   { from { opacity: 0; transform: translateY(12px) scale(.97); } }
@keyframes hop-ra    { to   { opacity: 0; transform: translateY(8px) scale(.98); } }
@keyframes xo-vao    { from { opacity: 0; transform: translateY(-8px); } }
@keyframes xo-ra     { to   { opacity: 0; transform: translateY(-6px); } }
@keyframes mo-nhe    { from { opacity: 0; } }
@keyframes tat-nhe   { to   { opacity: 0; } }

/* ══ Nền tảng ══════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; }

/* Bất kỳ quy tắc nào đặt `display` đều thắng `display:none` mặc định của thuộc
   tính [hidden] — số trên chuông và thanh nhòm đều rơi vào bẫy này, hiện ra
   ngay cả khi app.js đã ẩn. Chốt lại một lần cho cả file. */
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--vp-bg);
  color: var(--vp-ink);
  font: 400 15px/1.55 var(--font-body);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--vp-primary-text); text-underline-offset: 3px; }
a:hover { color: var(--vp-primary-2); }

:focus-visible {
  outline: 2px solid var(--vp-primary-text);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

/*  ── Thanh cuộn ────────────────────────────────────────────────────
    Thanh cuộn mặc định của trình duyệt là mảng màu DUY NHẤT trên trang không
    đi theo bảng màu: ở nền tối nó vẫn xám sáng của hệ điều hành, cắt một vệt
    dọc mép phải. Ở đây có tới bốn vùng cuộn riêng (khung hội thoại, bảng làm
    việc, băng chuyền ưu đãi, bảng rộng) nên nó lộ ra khắp nơi.

    Khai ở `:root` là đủ cho Firefox và Chrome đời mới — `scrollbar-width` và
    `scrollbar-color` đều là thuộc tính DI TRUYỀN. Khối `::-webkit-scrollbar`
    bên dưới lo cho Safari và Chrome cũ, nơi hai thuộc tính kia chưa có.

    `background-clip: content-box` cộng viền trong suốt là mẹo chừa lề cho con
    trượt: `::-webkit-scrollbar-thumb` không nhận `margin`. */
:root {
  scrollbar-width: thin;
  scrollbar-color: var(--vp-line) transparent;
}
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--vp-line);
  border: 3px solid transparent;
  border-radius: var(--r-full);
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--vp-faint); background-clip: content-box; }
::-webkit-scrollbar-corner { background: transparent; }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ══ Icon ══════════════════════════════════════════════════════════
   Material Symbols đã cắt còn đúng bộ icon template dùng (xem
   scripts/cat_font_icon.py). Thêm icon mới ở template mà quên chạy lại
   script thì ô đó hiện ra TÊN icon bằng chữ — không phải hình.

   ICON KHÔNG ĐƯỢC PHÉP HIỆN RA THÀNH CHỮ, KỂ CẢ TRONG LÚC FONT ĐANG TẢI.
   Material Symbols vẽ icon bằng ligature: nội dung ô là TÊN icon ("check_circle"),
   font dịch tên đó thành hình. Font chưa về thì font dự phòng vẽ nguyên chữ.

   `font-display: block` bên dưới cho một khoảng chặn ~3 giây — chữ vô hình
   trong khoảng đó, đúng ý. Nhưng quá 3 giây mà font chưa về thì trình duyệt BỎ
   CHẶN và vẽ bằng font dự phòng, tức là in "check_circle" lù lù giữa giao diện.
   Font nặng 52 KB nên mạng bình thường không bao giờ chạm ngưỡng; mạng chậm,
   cache lạnh, hay CDN đang chập chờn thì chạm. Hỏng hiếm nhưng có thật, và
   không để lại dấu vết nào trong console.

   Chốt chặn: giấu ô icon cho tới khi font THẬT SỰ dùng được, do
   `document.fonts.load()` trong <head> của base.html báo về. Không còn cửa sổ
   3 giây nào để lọt.

   `visibility` chứ không phải `display`: ô vẫn chiếm đúng chỗ, nên bố cục không
   nhảy một nhịp khi icon hiện ra.

   PHẢI LIỆT KÊ CẢ CÁC ::before DÙNG FONT ICON, không chỉ `.ms`. Một số icon đặt
   bằng `content:` trên pseudo-element (bong bóng báo lỗi, dấu bước, nhãn bảng
   làm việc) — chúng không mang class `.ms` nào nên không được chốt chặn che cho.
   Bỏ sót đúng chỗ này một lần rồi: ảnh chụp màn hình bắt được chữ "warning"
   hiện lù lù cạnh một hộp lỗi. Danh sách này có phép thử canh — xem
   `test_moi_selector_dung_font_icon_deu_duoc_che` trong tests/test_web_icons.py. */
.ms,
.steps li::before,
.msg-bad::before,
.trace > ol li::before,
.bang-nhan::before,
.nut-bang::before { visibility: hidden; }

html.font-ms-ok .ms,
html.font-ms-ok .steps li::before,
html.font-ms-ok .msg-bad::before,
html.font-ms-ok .trace > ol li::before,
html.font-ms-ok .bang-nhan::before,
html.font-ms-ok .nut-bang::before { visibility: visible; }

.ms {
  font-family: 'Material Symbols Outlined';
  font-weight: normal; font-style: normal;
  font-size: 20px; line-height: 1; letter-spacing: normal;
  text-transform: none; display: inline-block; white-space: nowrap;
  direction: ltr; -webkit-font-feature-settings: 'liga';
  -webkit-font-smoothing: antialiased;
  font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
  flex: none;
  user-select: none;
}
.msf { font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24; }

/* ══ Khung ứng dụng ════════════════════════════════════════════════
   Desktop: cột trái 256px cố định + cột phải cuộn. Điện thoại: cột trái
   biến thành thanh tab dưới đáy. Trang chưa đăng nhập (trang chủ, hai màn
   đăng nhập) không có cột trái — .shell-tron. */

/*  Khai KIỂU cho bề rộng cột, để chính cái biến trượt được.

    Biến CSS mặc định không có kiểu — với trình duyệt nó chỉ là một chuỗi, nên
    nó nhảy thẳng sang giá trị mới và không có quãng giữa nào để nội suy.
    `@property` khai `--side-w` là `<length>`, thế là bản thân cái biến thành
    thứ nội suy được, và MỌI thuộc tính đọc nó tự đi theo từng khung hình.

    Cách khác là đặt `transition` thẳng lên `grid-template-columns` — cũng chạy,
    vì hai đầu (`216px minmax(0,1fr)` và `84px minmax(0,1fr)`) cùng cấu trúc
    track nên nội suy được. Chọn `@property` vì nó giữ một nguồn sự thật duy
    nhất: thêm bất kỳ thứ gì đọc `--side-w` sau này là thứ đó tự trượt theo, còn
    cách kia thì phải nhớ khai thêm một dòng `transition` nữa cho từng nơi.

    Trình duyệt không hiểu `@property` thì bỏ qua cả khối này lẫn phần chuyển
    cảnh: cột nhảy cái một, không hỏng thêm gì. */
@property --side-w {
  syntax: "<length>";
  inherits: true;
  initial-value: 216px;
}

.shell {
  display: grid;
  grid-template-columns: var(--side-w) minmax(0, 1fr);
  min-height: 100vh;
  /*  Chuyển cảnh đặt trên CHÍNH cái biến, không phải trên `grid-template-columns`.
      Cả nhãn điều hướng lẫn chữ thương hiệu mờ và hẹp lại theo cùng nhịp (xem
      `.nav-chu`), nên không có chữ nào bật tắt giữa chừng. */
  transition: --side-w .26s cubic-bezier(.4, 0, .2, 1);
}
.shell-tron { grid-template-columns: minmax(0, 1fr); }

/* ── Cột điều hướng ────────────────────────────────────────────── */
.side {
  grid-column: 1;
  display: flex; flex-direction: column;
  padding: var(--s-6) 0;
  background: var(--vp-surface);
  border-right: 1px solid var(--vp-line);
  position: sticky; top: 0; height: 100vh;
  /* Xén phần chữ đang co lại. Thiếu dòng này thì trong lúc trượt, nhãn còn thò
     ra ngoài mép cột và đè lên nội dung trang. */
  overflow: hidden;
}

.brand {
  display: flex; align-items: center; gap: var(--s-4);
  padding: 0 var(--s-6) var(--s-7);
  text-decoration: none; color: inherit;
}
.brand:hover { color: inherit; }
.brand-mark {
  display: grid; place-items: center;
  width: 34px; height: 34px; flex: none;
  border-radius: 11px;
  filter: drop-shadow(0 3px 7px color-mix(in srgb, var(--vp-primary) 22%, transparent));
}
.brand-mark img { display: block; width: 34px; height: 34px; }
.brand-ten {
  display: block;
  font: 700 19px/23px var(--font-display);
  letter-spacing: .045em;
  color: var(--vp-primary-text);
}
.brand-phu { display: block; font: 500 12px/16px var(--font-display); color: var(--vp-muted); }

.nav {
  display: flex; flex-direction: column; gap: 2px;
  padding: 0 var(--s-4);
  flex: 1;
}
.nav a {
  /*  `justify-content: center` ở CẢ HAI trạng thái, và nhãn `flex: 1`.

      Mở rộng: nhãn chiếm hết chỗ thừa nên icon bị đẩy sát trái — đúng dáng cột
      điều hướng thường thấy, và canh giữa thành ra vô nghĩa.
      Thu gọn: nhãn co về 0 nên icon tự về giữa dải.

      Nhờ vậy không thuộc tính nào phải đổi giữa hai trạng thái. Bản trước đổi
      `flex-direction` từ hàng sang cột, mà cái đó không nội suy được — nó bật
      cái một giữa lúc cột đang trượt, và chính cú bật đó làm cả chuyển cảnh
      trông như hỏng. */
  display: flex; align-items: center; justify-content: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  color: var(--vp-muted);
  font: 400 14px var(--font-body);
  text-decoration: none;
}
.nav a .ms { font-size: 19px; flex: none; }
.nav-chu {
  flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden;
  max-width: 200px; opacity: 1;
  transition: max-width .26s cubic-bezier(.4, 0, .2, 1), opacity .16s ease, margin-left .26s cubic-bezier(.4, 0, .2, 1);
}
/*  Nhãn KHÔNG dùng `display: none` khi thu gọn — `display` không nội suy được,
    chữ sẽ biến mất tức thì trong lúc cột còn đang trượt. Co bề rộng về 0 và mờ
    đi thì nó rút vào cùng nhịp với cột.

    Chữ vẫn nằm trong DOM nên trình đọc màn hình vẫn đọc đủ tên mục; chuột thì
    có `title`. Ẩn bằng `display: none` mới là thứ lấy mất tên mục của cả hai. */
.shell-gon .nav-chu,
.shell-gon .brand > span:not(.brand-mark) {
  max-width: 0; opacity: 0;
  /* Triệt tiêu `gap` để icon về đúng giữa dải chứ không lệch nửa khoảng hở. */
  margin-left: calc(var(--s-3) * -1);
}
.nav a:hover { background: var(--vp-tint); color: var(--vp-ink); }
.nav a.on {
  background: color-mix(in srgb, var(--vp-primary) 10%, transparent);
  border-right: 4px solid var(--vp-primary);
  color: var(--vp-primary-text);
  font-weight: 600;
}
.nav a.on .ms { font-variation-settings: 'FILL' 1; }

/* Ghi chú "trợ lý chỉ đọc" ở chân cột — lời hứa phạm vi của agent, đặt ở
   chỗ luôn nhìn thấy chứ không giấu trong trang chat. */
.side-ghi {
  margin: var(--s-5) var(--s-7) 0;
  padding: 14px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  background: var(--vp-tint);
}
.side-ghi-dau {
  display: flex; align-items: center; gap: var(--s-3);
  color: var(--vp-ai);
  font: 600 12px var(--font-display);
}
.side-ghi-dau .ms { font-size: 18px; }
.side-ghi p { margin: var(--s-2) 0 0; font: 400 12px/1.5 var(--font-body); color: var(--vp-muted); }

/* ── Cột điều hướng thu gọn thành dải icon ─────────────────────
   Cùng hình dạng với biến thể 76px ở khổ tablet bên dưới, nhưng do NGƯỜI bật
   chứ không do bề rộng màn. Viết lại thay vì gộp chung một khối: một cái là
   media query, một cái là class — CSS không cho nhóm hai thứ đó vào một
   selector, mà tách ra thì mỗi bên sửa được độc lập khi thiết kế đổi.

   Chỉ đổi cách BÀY, không ẩn mục nào: bốn lối đi vẫn còn đủ, chỉ mất phần chữ
   dài. Giấu bớt mục khi thu gọn là làm người dùng mất đường mà không biết. */
/*  84px chứ không phải 76px: ở 76px thì dấu thương hiệu (34) + khoảng hở (8) +
    hamburger (32) vừa đúng 74px và không còn chỗ cho đệm hai bên, nên hàng đầu
    cột phải xếp dọc lại — lại một cú bật giữa chuyển cảnh. Rộng thêm 8px là
    hàng đó giữ nguyên dáng ở cả hai trạng thái. */
.shell-gon { --side-w: 84px; }
.shell-gon .nav { padding: 0 var(--s-3); }
.shell-gon .nav a { padding: var(--s-3) 0; }
.shell-gon .nav a.on { border-right: 0; }
.shell-gon .side-ghi { display: none; }

/* Hàng đầu cột: thương hiệu bên trái, hamburger bên phải. */
.side-dau {
  display: flex; align-items: flex-start; justify-content: center; gap: var(--s-3);
  padding: 0 var(--s-5) var(--s-6) var(--s-6);
  transition: padding .26s cubic-bezier(.4, 0, .2, 1);
}
/* Cùng thủ thuật với `.nav a`: thương hiệu `flex: 1` nên khi mở rộng nó chiếm
   hết chỗ và hamburger nằm sát phải; khi thu gọn chữ co về 0 và hai vật thể tự
   xúm về giữa. Không phải đổi `flex-direction` ở bất kỳ trạng thái nào. */
.side-dau .brand { flex: 1; min-width: 0; padding: 0; gap: var(--s-3); }
.side-dau .brand > span:not(.brand-mark) {
  overflow: hidden; white-space: nowrap;
  max-width: 140px; opacity: 1;
  transition: max-width .26s cubic-bezier(.4, 0, .2, 1), opacity .16s ease, margin-left .26s cubic-bezier(.4, 0, .2, 1);
}

/* Hamburger đóng/mở. Vẫn hiện khi đã thu gọn — nếu không thì bật vào rồi
   không có đường bật ra. */
.side-gon-nut {
  display: grid; place-items: center;
  width: 32px; height: 32px; flex: none;
  border: 1px solid transparent; border-radius: var(--r-sm);
  background: none; color: var(--vp-muted);
  cursor: pointer;
}
.side-gon-nut:hover { background: var(--vp-tint); color: var(--vp-ink); }
.side-gon-nut:focus-visible { outline: 2px solid var(--vp-primary); outline-offset: 2px; }
.side-gon-nut .ms { font-size: 21px; }
.shell-gon .side-dau { padding: 0 var(--s-3) var(--s-6); }

/* Dưới 1025px cột đã tự ở dạng icon — nút không còn việc gì để làm. */
@media (max-width: 1024px) { .side-gon-nut { display: none; } }

/* ── Thanh trên ───────────────────────────────────────────────── */
.main-cot { grid-column: 2; display: flex; flex-direction: column; min-width: 0; }
.shell-tron .main-cot { grid-column: 1; }

.topbar {
  height: var(--topbar-h); flex: none;
  display: flex; align-items: center; gap: var(--s-5);
  padding: 0 var(--s-7);
  background: var(--vp-surface);
  border-bottom: 1px solid var(--vp-line);
  position: sticky; top: 0; z-index: 30;
}
.topbar-end { margin-left: auto; display: flex; align-items: center; gap: var(--s-4); }
.topbar-end form { margin: 0; }

/* Ô tìm nhanh theo mã biên lai. Là <form> thật, gõ Enter là sang trang
   giao dịch đã lọc — không phải ô trang trí. */
.tim-nhanh { position: relative; width: 280px; max-width: 40vw; }
/* CON TRỰC TIẾP, không phải mọi hậu duệ. Bản trước viết `.tim-nhanh .ms` cho
   cái kính lúp, và từ lúc bảng gợi ý mọc ra bên trong form thì mọi icon trong
   bảng cũng bị hút về góc trái ô nhập, chồng lên nhau. */
.tim-nhanh > .ms {
  position: absolute; left: var(--s-4); top: 50%;
  transform: translateY(-50%); font-size: 20px; color: var(--vp-muted);
  pointer-events: none;
}
.tim-nhanh input {
  height: 40px; width: 100%;
  padding: 0 var(--s-4) 0 40px;
  border: 1px solid var(--vp-line); border-radius: var(--r-md);
  background: var(--vp-tint);
  font: 400 14px var(--font-body); color: var(--vp-ink);
}

/* ── Bảng gợi ý dưới ô tìm ───────────────────────────────────────────
   RỘNG HƠN Ô NHẬP. Ô tìm chỉ 280px, còn một dòng gợi ý phải chở tên dịch vụ,
   một câu mô tả và có khi cả mã biên lai — nhét vào 280px thì dòng nào cũng
   cụt bằng dấu ba chấm và bảng mất tác dụng.

   Neo theo MÉP TRÁI của ô nhập chứ không canh giữa: mắt vừa đọc xong từ khoá ở
   mép trái thì dòng gợi ý đầu tiên phải bắt đầu ngay dưới chỗ đó. */
.tim-bang {
  position: absolute; z-index: 60;
  top: calc(100% + 6px); left: 0;
  width: min(440px, 86vw);
  max-height: min(70vh, 560px); overflow-y: auto;
  padding: var(--s-2);
  background: var(--vp-surface-2);
  border: 1px solid var(--vp-line); border-radius: var(--r-lg);
  box-shadow: var(--sh-pop);
}
.tim-muc {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  color: inherit; text-decoration: none;
}
/* Chuột và bàn phím dùng CHUNG một lớp `on`. Để chuột dùng `:hover` còn bàn
   phím dùng lớp riêng thì có lúc hai dòng cùng sáng — con trỏ đứng yên ở một
   dòng trong khi mũi tên đã đi xuống dòng khác — và không dòng nào là dòng sẽ
   mở khi bấm Enter. */
.tim-muc.on { background: var(--vp-tint-2); }
.tim-muc-ico {
  flex: none; display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: var(--r-md);
  background: var(--vp-tint-2); color: var(--vp-primary-text);
}
.tim-muc-ico .ms { font-size: 19px; }
.tim-muc-chu { min-width: 0; flex: 1; }
.tim-muc-chu b { display: block; font: 500 13.5px/19px var(--font-body); }
.tim-muc-chu em {
  display: block; font-style: normal; font-size: 12px; line-height: 17px;
  color: var(--vp-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tim-muc-nhom {
  flex: none; font: 500 11px var(--font-display); color: var(--vp-faint);
}

/* Thẻ trợ lý: luôn là dòng đầu, và tô tím vì tím là màu của trợ lý trong cả
   ứng dụng — khách nhận ra nó trước khi đọc chữ. */
.tim-muc-ai .tim-muc-ico { background: var(--vp-ai-soft); color: var(--vp-ai); }
.tim-muc-ai .tim-muc-chu b { color: var(--vp-ai); }
.tim-muc-ai.on { background: var(--vp-ai-soft); }

.tim-bang-vach { height: 1px; margin: var(--s-2) var(--s-4); background: var(--vp-line-soft); }
.tim-bang-trong {
  padding: var(--s-4) var(--s-4) var(--s-5);
  font: 400 12.5px/18px var(--font-body); color: var(--vp-muted);
}

/* Thẻ trợ lý ở đầu trang kết quả. Là một <a> mang lớp `.card` chứ không phải
   một card có nút bên trong: cả tấm chỉ dẫn tới đúng một chỗ, nên cả tấm phải
   bấm được — chừa lại một nút nhỏ ở góc là bắt người ta nhắm. */
.tk-ai {
  display: flex; align-items: center; gap: var(--s-4);
  text-decoration: none; color: inherit;
  border-color: var(--vp-ai-line);
  transition: border-color .15s, transform .15s;
}
.tk-ai:hover { border-color: var(--vp-ai); transform: translateY(-1px); }
.tk-ai-ico {
  flex: none; display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: var(--r-md);
  background: var(--vp-ai-soft); color: var(--vp-ai);
}
.tk-ai-ico .ms { font-size: 22px; }
.tk-ai-chu { min-width: 0; flex: 1; }
.tk-ai-chu b { display: block; font: 600 15px/21px var(--font-display); color: var(--vp-ai); }
.tk-ai-chu em { display: block; font-style: normal; font-size: 12.5px; color: var(--vp-muted); }

/* Tiêu đề nhóm kết quả tìm kiếm: icon + tên loại + số lượng. */
.tk-dau { display: flex; align-items: center; gap: var(--s-3); }
.tk-dau .ms { font-size: 20px; color: var(--vp-primary-text); }
.tk-dau .badge { margin-left: auto; }

.who {
  display: inline-flex; align-items: center; gap: var(--s-3);
  font: 500 14px var(--font-body);
}
.ava {
  display: grid; place-items: center;
  width: 40px; height: 40px; flex: none;
  border-radius: var(--r-full);
  color: #fff;
  font: 600 14px var(--font-display);
}
.ava-lg { width: 52px; height: 52px; font-size: 18px; }

.role {
  padding: 3px 9px; border-radius: var(--r-full);
  font: 500 11.5px var(--font-display); font-style: normal;
  white-space: nowrap;
}
.role-cskh { background: var(--vp-tint-3); color: var(--vp-primary-text); }
.role-supervisor { background: var(--vp-ai-soft); color: var(--vp-ai); }

/* ── Vùng nội dung ────────────────────────────────────────────── */
.wrap {
  flex: 1; min-width: 0;
  width: 100%; max-width: 1180px;
  margin: 0 auto;
  padding: var(--s-7) var(--s-7) var(--s-9);
  display: flex; flex-direction: column; gap: var(--s-6);
}
.wrap-narrow { max-width: 560px; }
.wrap-rong { max-width: none; }

.foot {
  /* Chừa chỗ cho nút trợ lý nổi ở góc phải dưới. Không chừa thì dòng cuối trang
     nằm ngay dưới nút và bị che mất một góc. */
  padding: var(--s-6) var(--s-7) 84px;
  text-align: center;
  font: 400 12px var(--font-body);
  color: var(--vp-faint);
}

/* ── Thanh tab dưới (chỉ điện thoại) ──────────────────────────── */
.tabbar { display: none; }

/* ── Nút trợ lý nổi — MỌI bề ngang màn ────────────────────────────
      Trước đây nút này chỉ hiện dưới 768px; trên máy, trợ lý là một ô vuông
      nằm lẫn giữa các lối đi nhanh ở trang ví. Nghĩa là đang xem một giao dịch
      lạ mà muốn hỏi thì phải quay về trang ví trước — đúng lúc thắc mắc thì lại
      là lúc không có nút.

      Nút nổi đi theo mọi trang, ở một chỗ cố định, và tự nhắc mình tồn tại bằng
      một vòng sáng lan ra rồi tắt. Một nút im lìm ở góc màn thì mắt lọc mất sau
      vài giây — đó là "banner blindness", và nó xảy ra với cả những nút hữu ích.

      Nhịp thở TỰ TẮT khi rê chuột hoặc khi tiêu điểm rơi vào: nó chỉ có việc
      thu hút ánh nhìn, mà nhìn thấy rồi thì nó thành ra phiền. Với người bật
      `prefers-reduced-motion` thì tắt hẳn (khai ở cuối file), và nút vẫn còn
      nguyên màu tím + đổ bóng để nổi khỏi nền. */
.nut-ai {
  position: fixed; right: var(--s-6);
  bottom: calc(var(--s-6) + env(safe-area-inset-bottom));
  z-index: 61;
  display: inline-flex; align-items: center; gap: 10px;
  height: var(--nut-ai-cao); padding: 0 20px 0 10px;
  border: 0; border-radius: var(--r-full);
  /*  MÀU THƯƠNG HIỆU, không phải màu trợ lý.

      Chỗ này cố ý đi ngược quy ước "tím = trợ lý" khai ở đầu file, nên phải nói
      rõ ranh giới: tím vẫn đánh dấu THỨ TRỢ LÝ NÓI RA — bong bóng hội thoại,
      chip bảng làm việc, ô đang được trợ lý chỉ vào. Còn nút này không phải lời
      của trợ lý, nó là một lối đi trong khung ứng dụng, cùng hạng với thẻ số dư
      và nút "Chuyển tiền".

      Cái giá: mất một tín hiệu màu ở lối vào. Đổi lại, nút thôi chọi màu với cả
      trang — tím đặc trên nền bạc hà là cặp màu gắt nhất trong cả giao diện.

      Dùng token chứ không ghi cứng, nên nền sáng và nền tối cùng đổi. Để một
      thành phần đổi sắc theo nền là kiểu hỏng mà `test_web_theme.py` sinh ra để
      chặn — chỉ hiện ở một trong hai đường, và gần như không ai thử đủ cả hai. */
  background: var(--vp-primary); color: var(--vp-on-primary);
  box-shadow: var(--sh-pop);
  text-decoration: none;
  font: 600 14px var(--font-display);
}
.nut-ai:hover { background: var(--vp-primary-2); color: var(--vp-on-primary); translate: 0 -2px; }
.nut-ai.on { display: none; }   /* đang ở trang trợ lý thì không cần nút vào trang trợ lý */

.nut-ai-ico {
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: var(--r-full);
  background: color-mix(in srgb, var(--vp-on-primary) 22%, transparent);
}
.nut-ai-ico .ms { font-size: 21px; }

/* Vòng sáng lan ra. Là `::before` phủ kín nút chứ không phải một thẻ riêng —
   `box-shadow` với spread lớn dần vẽ được đúng cái vòng đó mà không cần layer
   thứ hai, và nó không bao giờ chắn chuột vì `pointer-events: none`. */
.nut-ai::before {
  content: ""; position: absolute; inset: 0;
  border-radius: inherit; pointer-events: none;
  animation: nut-ai-tho 3.4s ease-out infinite;
}
.nut-ai:hover::before, .nut-ai:focus-visible::before { animation: none; }

@keyframes nut-ai-tho {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--vp-primary) 55%, transparent); }
  45%  { box-shadow: 0 0 0 16px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* ══ Chữ ═══════════════════════════════════════════════════════════ */

h1 {
  margin: 0;
  font: 600 32px/40px var(--font-display);
  letter-spacing: -.02em;
}
h2 { margin: 0; font: 600 22px/28px var(--font-display); letter-spacing: -.01em; }
h3 { margin: 0; font: 600 18px/26px var(--font-display); }

.lede { margin: var(--s-1) 0 0; font: 400 16px/24px var(--font-body); color: var(--vp-muted); }
.hint { margin: 0; font: 400 13px/1.6 var(--font-body); color: var(--vp-muted); }
.small { font-size: 12.5px; }
.muted { color: var(--vp-muted); }
.mono { font-family: var(--font-mono); font-size: .92em; }
.num { font-family: var(--font-display); font-variant-numeric: tabular-nums; }

/* ══ Thẻ, lưới ═════════════════════════════════════════════════════ */

.card {
  background: var(--vp-surface);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-2xl);
  padding: var(--s-6) 22px 22px;
  box-shadow: var(--sh-2);
}
.card > h2, .card > h3 { margin-bottom: var(--s-4); }

.grid { display: grid; gap: var(--s-5); }
.grid-2 { display: grid; gap: var(--s-5); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { display: grid; gap: var(--s-5); grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { display: grid; gap: var(--s-4); grid-template-columns: repeat(4, minmax(0, 1fr)); }
.split { display: grid; gap: var(--s-6); grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); align-items: start; }
/* Cột chứa NHIỀU thẻ chồng lên nhau — .wrap dùng gap nên thẻ trong cột con
   không tự có khoảng cách, phải khai riêng. */
.cot-doc, .cot-thao-tac { display: flex; flex-direction: column; gap: var(--s-6); min-width: 0; }
.cot-thao-tac { position: sticky; top: calc(var(--topbar-h) + var(--s-6)); }
.row { display: flex; gap: var(--s-4); flex-wrap: wrap; align-items: flex-end; }
.row > .field { flex: 1 1 180px; }

/* ── Thẻ số dư: khối đặc duy nhất ở màn ví. Số dư là thứ khách mở ví ra
      để xem, nên nó là vật thể nổi nhất trang.

      Bố cục hai tầng: TẦNG TRÊN là số dư + trạng thái, TẦNG DƯỚI là mã ví +
      tiền đang tạm giữ. Trước đây mã ví bị nối vào cuối dòng nhãn ("Số dư khả
      dụng · 900000000086") nên nó đọc như một phần của cái nhãn, và không ai
      nghĩ tới việc lấy nó ra dùng — trong khi đây chính là thứ khách phải gửi
      cho người khác để nhận tiền. Tách xuống một tầng riêng, có nhãn riêng, và
      bấm được. ── */
.balance {
  position: relative; overflow: hidden;
  border-radius: var(--r-2xl);
  padding: var(--s-7);
  background: var(--vp-primary); color: var(--vp-on-primary);
  box-shadow: var(--sh-2);
  min-height: 196px;
  display: flex; flex-direction: column; justify-content: space-between;
  gap: var(--s-6);
}
.balance::before {
  content: ""; position: absolute; right: -80px; top: -80px;
  width: 256px; height: 256px; border-radius: var(--r-full);
  background: rgba(255, 255, 255, .1); filter: blur(40px);
  pointer-events: none;
}
.balance > *:not(.vi-nen) { position: relative; }

/*  Mặt thẻ vẽ bằng SVG, neo vào góc phải dưới và cho phép tràn ra ngoài mép —
    chính chỗ bị xén đó tạo cảm giác "một tấm thẻ thật đang thò ra khỏi khung"
    thay vì một hình minh hoạ dán vào giữa. `.balance` đã `overflow: hidden`.

    Không dùng ảnh nền qua `background-image`: hình cần tô theo màu chữ của thẻ
    (thẻ chính nền đặc, ví phụ nền nhạt) và một file ảnh thì không đổi màu được. */
.vi-nen {
  position: absolute; right: -28px; bottom: -26px;
  width: 300px; height: 200px;
  /*  Từng nét trong hình đã tự đặt `stroke-opacity` riêng (viền .22, con chip
      .55, sóng NFC .7/.45/.25) để ba lớp chi tiết chìm dần ra xa. Con số ở đây
      chỉ hạ toàn bộ xuống một nấc cho khỏi tranh chỗ với số dư — hạ sâu hơn nữa
      là hình biến mất hẳn trên nền xanh, đúng lỗi đã gặp với hình chìm ở trang
      Ưu đãi. */
  opacity: .85;
  pointer-events: none;
}
.vi-nen-phu { opacity: .16; color: var(--vp-ink); }

.balance-dau { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-4); }
.balance-chan {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap;
}
.balance .cap { font: 400 13px var(--font-body); color: var(--vp-on-primary-soft); }
.balance .amount {
  margin-top: var(--s-2);
  font: 600 44px/52px var(--font-display);
  letter-spacing: -.02em; font-variant-numeric: tabular-nums;
}
.balance-tt {
  display: inline-flex; align-items: center; gap: 6px;
  padding: var(--s-2) 12px;
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .18);
  font: 500 12px var(--font-display);
  white-space: nowrap;
}
.balance-tt .ms { font-size: 15px; }
/*  Ví không hoạt động: nền vàng cảnh báo chứ không phải cùng một viên trắng mờ
    như ví bình thường. Trạng thái này là lý do số dư không tiêu được, nên nó
    phải khác biệt được ngay từ đuôi mắt. */
.balance-tt.la { background: rgba(255, 255, 255, .32); font-weight: 600; }

.balance-ma { display: flex; flex-direction: column; gap: 4px; }

/*  Viên "đang tạm giữ" bấm được — dẫn sang danh sách giao dịch. Con số này là
    thứ khách thấy rồi mới đi tìm hiểu, nên chỗ để tìm hiểu phải nằm ngay dưới
    ngón tay chứ không bắt họ tự mò sang menu. */
.balance .st {
  display: inline-flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .2);
  font: 500 12px var(--font-display);
  color: inherit; text-decoration: none;
}
.balance a.st:hover { background: rgba(255, 255, 255, .32); color: inherit; }
.balance .st .ms { font-size: 16px; }

/*  Ô mã ví bấm để chép. Trông như một viên nhãn nhưng là <button> thật: bàn
    phím Tab tới được, Enter bấm được, trình đọc màn hình đọc ra là nút. */
.chep {
  display: inline-flex; align-items: center; gap: var(--s-3);
  padding: 6px 10px 6px 12px;
  border: 1px solid rgba(255, 255, 255, .28);
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, .12);
  color: inherit; cursor: pointer;
  font: 500 14px var(--font-mono);
  letter-spacing: .04em;
}
.chep:hover { background: rgba(255, 255, 255, .24); border-color: rgba(255, 255, 255, .5); }
.chep-hinh, .chep-xong { width: 17px; height: 17px; flex: none; }
/*  Hai hình chồng đúng một chỗ, đổi vai bằng `display`. Không dùng opacity vì
    hình mờ vẫn chiếm chỗ và vẫn nhận được chuột. */
.chep-xong { display: none; }
.chep.xong .chep-hinh { display: none; }
.chep.xong .chep-xong { display: block; }
.chep.xong { background: rgba(255, 255, 255, .3); }
/* Trình duyệt chặn bộ nhớ tạm — dãy số đã được bôi đen sẵn, chỉ còn Ctrl+C. */
.chep.chep-loi { border-color: var(--vp-danger); border-style: dashed; }

/* Ví phụ / ví tạm khoá — cùng bố cục, nền nhạt. Không giấu ví khoá: khách
   phải thấy nó tồn tại thì mới hiểu vì sao số dư tổng không khớp. */
.balance-phu {
  background: var(--vp-surface); color: var(--vp-ink);
  border: 1px solid var(--vp-line); box-shadow: none; min-height: 0;
}
.balance-phu::before { display: none; }
.balance-phu .cap { color: var(--vp-muted); }
.balance-phu .amount { font-size: 26px; line-height: 32px; color: var(--vp-muted); }
.balance-phu .st,
.balance-phu .balance-tt { background: var(--tone-neutral-bg); color: var(--tone-neutral-fg); }
.balance-phu .balance-tt.la { background: var(--tone-warn-bg); color: var(--tone-warn-fg); }
.balance-phu .chep {
  border-color: var(--vp-line); background: var(--vp-surface-2); color: var(--vp-ink);
}
.balance-phu .chep:hover { background: var(--vp-tint); border-color: var(--vp-primary); }

/* Năm thẻ đếm ở hàng chờ. Xuống 3 rồi 2 cột theo bề ngang màn. */
.the-dem {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(5, minmax(0, 1fr));
}
@media (max-width: 1100px) { .the-dem { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 640px) { .the-dem { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.stat {
  background: var(--vp-surface);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  padding: var(--s-5);
  box-shadow: var(--sh-1);
  color: inherit; text-decoration: none;
  display: block;
}
a.stat:hover { border-color: var(--vp-primary); color: inherit; }
a.stat.on {
  background: var(--vp-primary); border-color: var(--vp-primary);
  color: var(--vp-on-primary);
}
a.stat.on .k { color: var(--vp-on-primary-soft); }
.stat .cap { display: block; font: 400 12.5px var(--font-body); color: var(--vp-muted); }
.stat .amount {
  display: block; margin-top: var(--s-1);
  font: 600 26px/32px var(--font-display); font-variant-numeric: tabular-nums;
}
.stat .k { display: flex; align-items: center; gap: var(--s-2); }
.stat .k .ms { font-size: 15px; }
.stat .sub { margin-top: var(--s-2); font: 400 11.5px/1.45 var(--font-body); color: var(--vp-muted); }
/* Chỉ số của AI mặc màu tím như mọi thứ khác trợ lý đụng tới — nhìn là biết
   nó KHÔNG phải tỉ lệ duyệt chung của cả bộ phận. */
.stat-ai { border-color: var(--vp-ai-line); background: var(--vp-ai-soft); }
.stat-ai .k, .stat-ai .sub { color: var(--vp-ai); }
.stat-ai .v { color: var(--vp-ai); }

/* Nút hành động nhanh ở màn ví — ô vuông icon + nhãn. */
.go {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-3);
  padding: 14px;
  background: var(--vp-surface);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  color: inherit; text-decoration: none;
  text-align: center;
}
.go:hover { border-color: var(--vp-primary); color: inherit; }
.go span.ico {
  display: grid; place-items: center;
  width: 44px; height: 44px; border-radius: var(--r-full);
  background: color-mix(in srgb, var(--vp-primary) 10%, transparent);
  color: var(--vp-primary-text);
}
.go .ms { font-size: 22px; }
.go b { font: 500 12px var(--font-display); }
/*  Nút chính trong hàng lối đi nhanh — đúng MỘT cái. Ba nút còn lại dẫn tới
    chỗ để XEM, nút này là chỗ duy nhất làm tiền đổi chủ, nên nó là thứ duy
    nhất tô đặc. Tô đặc hai cái là không tô cái nào. */
.go-chinh {
  background: var(--vp-primary); border-color: var(--vp-primary);
  color: var(--vp-on-primary);
}
.go-chinh:hover { background: var(--vp-primary-2); border-color: var(--vp-primary-2); color: var(--vp-on-primary); }
.go-chinh span.ico { background: rgba(255, 255, 255, .2); color: inherit; }

/*  Bốn lối đi nhanh xuống hai cột khi hẹp. Nhãn dài nhất là "Giao dịch & biên
    lai" — hai dòng ở 2 cột thì vừa, ở 4 cột trên màn hẹp thì vỡ chữ. */
@media (max-width: 720px) {
  .vi-loi { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ══ Chuyển tiền ═══════════════════════════════════════════════════
      Trang hẹp (`wrap-narrow`), một cột, không có gì để so sánh hay lướt qua.
      Đây là màn khách phải ĐỌC chứ không phải màn khách quét mắt, nên mọi thứ
      xếp dọc theo đúng thứ tự cần quyết định: gửi cho ai → bao nhiêu → bấm. */

.ct-dau { text-align: center; }
.ct-dau .lede { margin-inline: auto; max-width: 42ch; }

/* Tranh minh hoạ SVG. Tô bằng màu nhấn ở độ mờ vừa phải — nó là trang trí, và
   một hình đậm bằng chữ thì tranh mất chỗ với thứ khách cần đọc. */
.vi-tranh {
  display: block; width: 200px; max-width: 100%; height: auto;
  margin: 0 auto var(--s-4);
  color: var(--vp-primary); opacity: .75;
}

/*  Ví nguồn — một dòng, không phải một thẻ. Khách đã biết số dư của mình từ
    trang trước; ở đây nó chỉ cần có mặt để đối chiếu, không cần giành sự chú ý
    với ô nhập số tiền ngay bên dưới. */
.ct-nguon {
  display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap;
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--vp-line); border-radius: var(--r-lg);
  background: var(--vp-tint);
}
.ct-nguon .cap { font: 400 13px var(--font-body); color: var(--vp-muted); }
.ct-nguon-so { font: 600 20px var(--font-display); margin-left: auto; }

/*  Dòng hạn mức ngày. Là chú thích, không phải cảnh báo — nên nền nhạt và nằm
    ngay dưới ví nguồn, cùng chỗ mắt đang đọc số dư. Chỉ khi hết hạn mức nó mới
    đổi sang tông cảnh báo, vì lúc đó nó thôi là chú thích mà thành lý do form
    biến mất. */
.ct-han-muc {
  display: flex; align-items: center; gap: var(--s-3);
  margin: 0; padding: var(--s-4) var(--s-5);
  border-radius: var(--r-lg);
  background: var(--vp-tint-2);
  font: 400 13px/1.5 var(--font-body); color: var(--vp-muted);
}
.ct-han-muc b { color: var(--vp-ink); font-weight: 600; }
.ct-han-muc .ms { font-size: 18px; flex: none; color: var(--vp-primary-text); }
.ct-han-muc-het {
  background: var(--tone-warn-bg); color: var(--tone-warn-fg);
}
.ct-han-muc-het b, .ct-han-muc-het .ms { color: inherit; }

.ct-form { display: flex; flex-direction: column; gap: var(--s-5); }
.ct-form .btn { margin-top: var(--s-2); }

.ct-goi-y-ds { list-style: none; margin: var(--s-4) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.ct-goi-y-ds .demo-chip { width: 100%; justify-content: flex-start; }
.ct-goi-y-ds .badge { margin-left: auto; }

.ct-xn { display: flex; flex-direction: column; gap: var(--s-5); }
.ct-xn-tien {
  text-align: center;
  font: 600 40px/48px var(--font-display);
  letter-spacing: -.02em;
}
.ct-xn-nut { display: flex; gap: var(--s-4); }
.ct-xn-nut .btn { flex: 1; justify-content: center; }

/*  Cảnh báo ví người nhận không nhận được tiền. Đặt TRƯỚC nút bấm, không phải
    sau — một cảnh báo nằm dưới nút thì người đã quyết bấm sẽ không bao giờ đọc. */
.ct-canh {
  display: flex; gap: var(--s-4); align-items: flex-start;
  padding: var(--s-5);
  border: 1px solid var(--tone-warn-fg); border-radius: var(--r-lg);
  background: var(--tone-warn-bg); color: var(--tone-warn-fg);
}
.ct-canh p { margin: var(--s-2) 0 0; font: 400 13px/1.6 var(--font-body); }
.ct-canh-hinh { width: 56px; margin: 0; flex: none; color: inherit; opacity: .9; }

.empty {
  padding: var(--s-8) var(--s-7);
  border: 1px dashed var(--vp-line);
  border-radius: var(--r-2xl);
  background: var(--vp-tint);
  text-align: center;
  color: var(--vp-muted);
}
.empty .ms { font-size: 34px; color: var(--vp-faint); }
.empty b { display: block; margin-top: var(--s-3); font: 600 20px/28px var(--font-display); color: var(--vp-ink); }
.empty p { margin: var(--s-2) auto 0; max-width: 44ch; font: 400 14px/20px var(--font-body); }

/* ══ Bảng ══════════════════════════════════════════════════════════ */

.table-wrap {
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--vp-surface-2);
}
.table-wrap + .hint { margin-top: var(--s-4); }

table { width: 100%; border-collapse: collapse; }
th {
  padding: var(--s-4) 14px;
  background: var(--vp-tint);
  border-bottom: 1px solid var(--vp-line);
  text-align: left;
  font: 600 12px var(--font-display);
  color: var(--vp-muted);
  white-space: nowrap;
}
td {
  padding: var(--s-4) 14px;
  border-bottom: 1px solid var(--vp-line-soft);
  font: 400 14px var(--font-body);
  vertical-align: middle;
}
tbody tr:last-child td { border-bottom: 0; }
td.num, th.num {
  text-align: right;
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
tbody tr[data-href] { cursor: pointer; }
tbody tr[data-href]:hover { background: var(--vp-tint); }

.open-link { color: var(--vp-primary-text); font: 500 13px var(--font-display); text-decoration: none; }

/* Đầu một khối: tiêu đề bên trái, một đường dẫn/nút bên phải, cùng đường chân.
   `align-items: baseline` chứ không phải `center` — tiêu đề và đường dẫn lệch
   nhau khá nhiều về cỡ chữ, canh giữa thì đường dẫn trôi lên trông như bị đẩy. */
.card-dau {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap;
  margin-bottom: var(--s-4);
}
.card-dau > h2, .card-dau > h3 { margin: 0; }
.card-dau .open-link { display: inline-flex; align-items: center; gap: 2px; }
.card-dau .open-link .ms { font-size: 17px; }
.sap-xep { color: inherit; text-decoration: none; white-space: nowrap; }
.sap-xep:hover { color: var(--vp-primary-text); }

/* ══ Badge ═════════════════════════════════════════════════════════ */

.badge {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 3px 11px;
  border-radius: var(--r-full);
  font: 500 12px var(--font-display);
  white-space: nowrap;   /* nhãn trạng thái KHÔNG được xuống dòng giữa chừng */
}
.tone-ok { background: var(--tone-ok-bg); color: var(--tone-ok-fg); }
.tone-bad { background: var(--tone-bad-bg); color: var(--tone-bad-fg); }
.tone-warn { background: var(--tone-warn-bg); color: var(--tone-warn-fg); }
.tone-wait { background: var(--tone-wait-bg); color: var(--tone-wait-fg); }
.tone-neutral { background: var(--tone-neutral-bg); color: var(--tone-neutral-fg); }

/* ══ Biểu mẫu ══════════════════════════════════════════════════════ */

/* Biểu mẫu xếp dọc có khoảng cách sẵn. Trước đây .card không có gap nên hai ô
   nhập và nút bấm dính liền nhau thành một khối liền mạch — mắt không tách
   được đâu là trường nào, và nút "Đăng nhập" trông như phần của ô mật khẩu. */
form.card, .card > form { display: flex; flex-direction: column; gap: var(--s-5); }

.field { display: flex; flex-direction: column; gap: var(--s-3); }
.field label, label {
  font: 500 13px var(--font-display);
  color: var(--vp-muted);
}
input, select, textarea {
  width: 100%;
  min-height: 44px;   /* chạm được bằng ngón tay */
  padding: var(--s-4) 14px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-md);
  background: var(--vp-surface-2);
  color: var(--vp-ink);
  font: 400 15px var(--font-body);
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--vp-primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--vp-primary) 18%, transparent);
}
input:disabled, select:disabled, textarea:disabled {
  background: var(--vp-tint); color: var(--vp-muted); cursor: not-allowed;
}
textarea { min-height: 96px; resize: vertical; line-height: 1.55; }

/* Ô nhập có icon dẫn ở đầu (số điện thoại, mật khẩu). Icon nằm ngoài luồng
   nhập nên không ăn cú chạm — con trỏ vẫn vào ô. */
.o-icon { position: relative; display: flex; align-items: center; }
.o-icon > .ms {
  position: absolute; left: 14px;
  color: var(--vp-faint); pointer-events: none;
}
.o-icon input { padding-left: 44px; }
.o-icon:has(.o-hien) input { padding-right: 48px; }
.o-hien {
  position: absolute; right: var(--s-3);
  display: grid; place-items: center;
  width: 34px; height: 34px;
  border: 0; border-radius: var(--r-full);
  background: transparent; color: var(--vp-faint);
  cursor: pointer;
}
.o-hien:hover { background: var(--vp-tint-2); color: var(--vp-ink); }

/* Ô OTP: 6 hộp rời. Một ô dài 6 số đọc thành một khối, khách đánh máy sai
   một số là phải xoá hết dò lại. */
.otp { display: flex; gap: 7px; }
.otp input {
  flex: 1; min-width: 0;
  height: 52px; min-height: 52px;
  padding: 0; text-align: center;
  border-radius: var(--r-md);
  font: 600 20px var(--font-display); font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}
.otp input:not(:placeholder-shown), .otp input[data-co] { border-color: var(--vp-primary); }

/* Chỉ dấu 2 bước của màn xác thực phiên. */
.buoc-2 {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: center; gap: 9px;
}
.buoc-2 li { display: contents; }
.buoc-2 li > * {
  display: grid; place-items: center;
  width: 26px; height: 26px; flex: none;
  border-radius: var(--r-full);
  background: var(--vp-tint-2); color: var(--vp-muted);
  font: 600 12px var(--font-display);
}
.buoc-2 li + li::before { content: ""; height: 2px; flex: 1; background: var(--vp-line); }
.buoc-2 li.dang > * { background: var(--vp-primary); color: var(--vp-on-primary); }
.buoc-2 li.xong > * { background: var(--tone-ok-fg); color: var(--vp-on-tone); }
.buoc-2 li.xong + li::before { background: var(--tone-ok-fg); }
.buoc-2 .ms { font-size: 15px; }

.actions { display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: center; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-3);
  min-height: 44px;
  padding: 0 18px;
  border: 1px solid var(--vp-primary);
  border-radius: var(--r-md);
  background: var(--vp-primary); color: var(--vp-on-primary);
  font: 600 15px var(--font-display);
  text-decoration: none;
  cursor: pointer;
}
.btn:hover { background: var(--vp-primary-2); border-color: var(--vp-primary-2); color: var(--vp-on-primary); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn-ghost {
  background: var(--vp-surface-2); color: var(--vp-primary-text);
  border-color: var(--vp-line);
}
.btn-ghost:hover { background: var(--vp-tint); border-color: var(--vp-primary); color: var(--vp-primary-text); }
.btn-ok { background: var(--tone-ok-fg); border-color: var(--tone-ok-fg); color: var(--vp-on-tone); }
.btn-ok:hover { filter: brightness(1.12); background: var(--tone-ok-fg); border-color: var(--tone-ok-fg); }
.btn-warn { background: var(--tone-warn-fg); border-color: var(--tone-warn-fg); color: var(--vp-on-tone); }
.btn-warn:hover { filter: brightness(1.12); background: var(--tone-warn-fg); border-color: var(--tone-warn-fg); }
.btn-bad { background: var(--vp-danger); border-color: var(--vp-danger); color: var(--vp-on-tone); }
.btn-bad:hover { filter: brightness(1.12); background: var(--vp-danger); border-color: var(--vp-danger); }
.btn-wide { width: 100%; }
.btn-nho { min-height: 34px; padding: 0 12px; font-size: 13px; }

/* ══ Cảnh báo ══════════════════════════════════════════════════════ */

.alert {
  padding: 14px var(--s-5);
  border-radius: var(--r-lg);
  font: 400 14px/1.6 var(--font-body);
  background: var(--tone-neutral-bg); color: var(--tone-neutral-fg);
}
.alert strong { font-weight: 600; }
.alert-bad { background: var(--tone-bad-bg); color: var(--tone-bad-fg); }
.alert-ok { background: var(--tone-ok-bg); color: var(--tone-ok-fg); }
.alert-warn { background: var(--tone-warn-bg); color: var(--tone-warn-fg); }
.alert-info { background: var(--tone-wait-bg); color: var(--tone-wait-fg); }

/* ══ Danh sách khoá–giá trị ════════════════════════════════════════ */

.kv { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--s-3) var(--s-6); margin: 0; }
.kv dt { font: 400 13px var(--font-body); color: var(--vp-muted); }
.kv dd { margin: 0; font: 400 14px var(--font-body); }

/* ══ Diễn biến (dọc) ═══════════════════════════════════════════════ */

/* Mỗi mốc = một huy hiệu tròn + hai dòng chữ. Bỏ đường kẻ dọc nối các mốc:
   nó gợi ý một quy trình tuyến tính đều đặn, trong khi diễn biến thật có mốc
   cách nhau vài giây và mốc cách nhau nhiều ngày. */
.steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-4); }
.steps li { display: flex; gap: 11px; }
.steps li::before {
  content: "flag";
  font-family: 'Material Symbols Outlined';
  font-size: 15px; line-height: 1;
  display: grid; place-items: center;
  width: 26px; height: 26px; flex: none;
  border-radius: var(--r-full);
  background: var(--tone-neutral-bg); color: var(--vp-faint);
}
.steps li.done::before { content: "check"; font-variation-settings: 'FILL' 1; background: var(--tone-ok-bg); color: var(--tone-ok-fg); }
.steps li.now::before { content: "hourglass_top"; font-variation-settings: 'FILL' 1; background: var(--vp-ai-soft); color: var(--vp-ai); }
.steps li:not(.done):not(.now) { opacity: .6; }
.steps li.now .st { color: var(--vp-ai); }
.steps .st { display: block; font: 500 13.5px var(--font-body); }
.steps .mo-ta { display: block; font: 400 13px/1.5 var(--font-body); color: var(--vp-muted); }
.steps .xuong { display: block; margin-top: 3px; font: 400 12px var(--font-body); color: var(--vp-faint); }

/* ══ Diễn biến (ngang, 4 cột) ══════════════════════════════════════ */

/* Dùng cho ba dải ở trang chi tiết ca: diễn biến giao dịch, quá trình điều tra
   của AI, nhật ký xử lý. Mỗi mốc là một thẻ bấm được mở hộp thoại chi tiết.

   Lưới xuống dòng (repeat(4,…)) chứ không phải grid-auto-flow: column — nhật ký
   một ca dài có thể tới hàng chục mốc, dồn hết vào một hàng thì mỗi mốc còn
   vài chục pixel và không đọc được gì. */
.steps-ngang {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-4);
}
.steps-ngang li {
  display: block;
  padding: 14px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  background: var(--vp-surface-2);
  opacity: 1;
}
.steps-ngang li::before { display: none; }
.steps-ngang li[data-tieu-de] { cursor: pointer; }
.steps-ngang li[data-tieu-de]:hover { border-color: var(--vp-primary); background: var(--vp-tint); }
.steps-ngang li:not(.done) { background: var(--vp-tint); }
/* Bước điều tra LỖI phải nhìn ra ngay giữa dải toàn bước xanh: cái quản lý cần
   biết trước tiên là AI có tra hụt chỗ nào không. */
.steps-ngang li.loi, .steps-ngang li.loi:hover {
  border-color: var(--tone-bad-fg);
  background: var(--tone-bad-bg);
  color: var(--tone-bad-fg);
}
.steps-ngang li.loi .by, .steps-ngang li.loi .at { color: inherit; opacity: .8; }
.steps-ngang .st { display: block; font: 600 14px var(--font-display); }
.steps-ngang .by { margin-top: 4px; }
.steps-ngang .at { margin-top: 2px; }
.steps-ngang .note { margin-top: var(--s-3); font-size: 12.5px; }
.steps-ngang .xem-them { margin-top: var(--s-3); }

/* Dòng "ai làm / lúc nào" dưới mỗi mốc diễn biến. */
.by { font: 400 12.5px var(--font-body); color: var(--vp-muted); }
.at { font: 400 12px var(--font-body); color: var(--vp-faint); font-variant-numeric: tabular-nums; }
.note {
  margin-top: var(--s-3);
  padding: var(--s-4) 14px;
  border-radius: var(--r-md);
  background: var(--vp-tint);
  font: 400 13.5px/1.6 var(--font-body);
  color: var(--vp-muted);
  white-space: pre-wrap;
}

/* ══ Chip lọc chương trình ═════════════════════════════════════════
   KHÔNG dùng lại `.bang-chip`: chip đó tô `--vp-ai`, mà tím trong ví có nghĩa
   cố định là "thuộc về trợ lý AI". Bộ lọc khuyến mãi không dính gì tới AI, tô
   tím là phá đúng quy ước màu mà giao diện đang giữ. */
.loc-chips { display: flex; gap: var(--s-3); flex-wrap: wrap; }
.loc-chip {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 6px 14px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-full);
  background: var(--vp-surface-2);
  color: var(--vp-muted);
  font: 500 13px var(--font-display);
  text-decoration: none;
}
.loc-chip:hover { border-color: var(--vp-primary); color: var(--vp-primary-text); }
.loc-chip.on {
  background: var(--vp-primary); border-color: var(--vp-primary); color: var(--vp-on-primary);
}
.loc-so { font-variant-numeric: tabular-nums; opacity: .7; }

/* ══ Thẻ trang Ưu đãi ══════════════════════════════════════════════
   Bố cục cột: đầu thẻ → con số lớn → vài chỉ số ngắn → nút mở popup. Nút luôn
   nằm đáy nhờ `margin-top:auto`, nên các thẻ trong một hàng có nút thẳng nhau
   dù nội dung dài ngắn khác nhau. */
.ud-the { display: flex; flex-direction: column; }
.ud-dau { display: flex; gap: var(--s-3); align-items: center; flex-wrap: wrap; }
.ud-dau h3 { margin: 0; }

.ud-so { margin-top: var(--s-5); }
.ud-so-v {
  display: block;
  font: 600 30px/36px var(--font-display);
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  color: var(--vp-primary-text);
}
.ud-so-k { display: block; margin-top: 2px; font: 400 12.5px var(--font-body); color: var(--vp-muted); }
.ud-so-nho .ud-so-v { font-size: 24px; line-height: 30px; }

/* Chỉ số phụ: nhãn nhỏ trên, giá trị dưới. Xuống hàng được để thẻ hẹp không vỡ. */
.ud-mini { display: flex; gap: var(--s-6); flex-wrap: wrap; margin: var(--s-5) 0; }
.ud-mini dt { font: 400 12px var(--font-body); color: var(--vp-muted); }
.ud-mini dd { margin: 2px 0 0; font: 500 14px var(--font-display); }

.ud-loi { list-style: none; margin: var(--s-5) 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.ud-loi li { display: grid; grid-template-columns: auto 1fr; gap: var(--s-3); align-items: center; font: 400 13.5px/1.45 var(--font-body); }
.ud-loi .ms { font-size: 19px; color: var(--vp-muted); }

.ud-km-tieu { margin: var(--s-4) 0 0; font: 600 16px/1.4 var(--font-display); }
.ud-chan { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin-top: auto; padding-top: var(--s-5); }
.ud-dem { display: inline-flex; align-items: center; gap: var(--s-2); font: 500 13px var(--font-display); color: var(--vp-muted); }
.ud-dem .ms { font-size: 18px; }
/* Khoảng hở phía trên nút nằm ở margin-bottom của khối đứng trước, KHÔNG phải
   padding-top của chính nút. Để trên nút thì khoảng hở đó nằm TRONG khung viền
   của nút: chữ bị đẩy xuống sát đáy khung (padding 16px trên / 0 dưới), nhìn như
   nút bị lệch. `margin-top: auto` vẫn giữ nút dính đáy thẻ khi thẻ bị kéo cao
   bằng thẻ cạnh nó. */
.ud-nut { margin-top: auto; align-self: flex-start; }

/* ── Dải hạng Túi+ ────────────────────────────────────────────────────
   Một dòng thay cho việc bắt khách tự đối chiếu ba thẻ bên dưới với hoàn cảnh
   của mình. Ray nằm ở giữa hàng mốc theo CHIỀU DỌC, nên `.hang-ray` là lớp nền
   còn `.hang-moc` chồng lên trên — không dùng grid hai hàng vì chấm phải nằm ĐÈ
   lên ray, không phải bên dưới nó. */
/* Nay là nửa phải của `.hang-banner`, nên lề ngoài do tấm lo. Là gốc định vị
   cho ray: ray nằm ĐÈ dưới hàng mốc chứ không phải một hàng riêng, vì chấm phải
   nằm TRÊN ray. Hàng mốc thì luôn dựng (danh mục hạng là thông tin công khai),
   còn ray chỉ dựng khi ví đo được hạng của khách — nên hai thứ phải tách rời,
   không lồng nhau như bản trước. */
.hang-dai { position: relative; margin: 0; min-width: 0; padding-top: 6px; }
/* `top` tính từ tâm HUY HIỆU: 6px đệm của `.hang-dai` + 3px đệm của đế + 34px
   chiều cao huy hiệu / 2, trừ nửa chiều dày dải. Lệch con số này là dải chạy
   qua cằm hoặc qua đỉnh huy hiệu thay vì qua giữa. */
.hang-ray { position: absolute; left: 0; right: 0; top: 24px; height: 4px; }
.hang-ray::before {
  content: ""; position: absolute; inset: 0;
  border-radius: var(--r-full); background: var(--vp-line-soft);
}
/*  Dải LỘ TRÌNH — dùng khi ví chưa đo được hạng của khách.

    Nét đứt chứ không phải nét liền mờ: nét liền mờ vẫn đọc ra là một thanh tiến
    độ đang rỗng, tức là vẫn khẳng định "bạn ở mốc 0" — điều mà ví không biết.
    Nét đứt là quy ước của đường đi, của "còn tiếp", nên nó nối ba bậc lại thành
    một tiến trình mà không gán cho khách vị trí nào.

    Vẽ bằng gradient lặp chứ không bằng `border: dashed`: viền nét đứt của trình
    duyệt không chỉnh được chiều dài vạch và khoảng hở, mỗi trình duyệt vẽ một
    kiểu, còn ở đây cần vạch dài hơn hở để đọc ra là một đường liên tục. */
.hang-ray-lo::before {
  background: repeating-linear-gradient(
    90deg,
    var(--vp-line) 0 10px,
    transparent 10px 18px
  );
  border-radius: 0;
}

.hang-ray-day {
  position: absolute; left: 0; top: 0; height: 100%;
  border-radius: var(--r-full);
  background: linear-gradient(90deg, var(--vp-primary), var(--vp-ai));
  /* Tối thiểu 4px để khách ở mốc 0% vẫn thấy vạch bắt đầu từ đâu. */
  min-width: 4px;
  transition: width .35s ease;
}
.hang-moc {
  position: relative; list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(var(--hang-cot, 3), 1fr);
}
/* `> li` chứ không phải `li`: hộp quyền lợi bên trong mốc có <ul class="ud-loi">
   với ba <li> con, mà chúng KHÔNG phải mốc hạng. Bỏ dấu `>` là ba dòng quyền lợi
   cũng được `position: relative` và ăn cả mấy luật `:first-child`/`:hover` dưới. */
.hang-moc > li { position: relative; }
/* Mốc là NÚT chứ không phải chữ chết: rê vào hiện hộp quyền lợi, bấm mở popup
   điều kiện đạt hạng. Bàn phím và màn cảm ứng không rê chuột được nên đường bấm
   mới là đường chính; hộp rê chỉ là lối tắt cho chuột. */
.hang-nut {
  display: grid; justify-items: center; gap: 2px;
  width: 100%; padding: 0 2px 4px;
  border: 0; background: none; cursor: pointer;
  text-align: center;
  font: 400 12.5px var(--font-body); color: var(--vp-muted);
}
.hang-nut:focus-visible { outline: 2px solid var(--vp-primary); outline-offset: 2px; border-radius: var(--r-md); }

/* Hộp quyền lợi bật ra khi rê vào mốc — chỗ ở mới của ba thẻ hạng cũ.
   Ba thẻ đó bày sẵn chín dòng quyền lợi cho ba hạng trong khi khách chỉ đứng ở
   một hạng; tám dòng còn lại là nhiễu. Đưa vào hộp thì mỗi lúc chỉ hiện đúng
   hạng đang được hỏi tới. */
.hang-hop {
  --dx: -50%;
  position: absolute; top: calc(100% + 6px); left: 50%;
  z-index: 6; width: max-content; max-width: 250px;
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--vp-line); border-radius: var(--r-lg);
  background: var(--vp-surface); box-shadow: var(--sh-2);
  text-align: left;
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translate(var(--dx), -4px);
  transition: opacity .14s ease, transform .14s ease, visibility .14s;
}
/* Mốc đầu và mốc cuối neo theo mép của chính nó: canh giữa thì hộp thò ra khỏi
   tấm và đẩy cả trang cuộn ngang. */
.hang-moc > li:first-child .hang-hop { --dx: 0; left: 0; }
.hang-moc > li:last-child .hang-hop { --dx: 0; left: auto; right: 0; }
/* `pointer-events: auto` khi đã hiện: hộp nằm ngay dưới nút, không cho nhận
   chuột thì vừa rê xuống đọc là mất hộp. */
.hang-moc > li:hover .hang-hop,
.hang-moc > li:focus-within .hang-hop {
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: translate(var(--dx), 0);
}
.hang-hop .ud-loi { margin: 0; gap: var(--s-2); }
.hang-hop-goi {
  display: block; margin-top: var(--s-3); padding-top: var(--s-3);
  border-top: 1px solid var(--vp-line-soft);
  font: 500 12px var(--font-display); color: var(--vp-primary-text);
}
/*  Đế tròn dưới huy hiệu. Việc duy nhất của nó là CẮT dải chạy phía sau — dải
    nằm đè dưới hàng mốc, không có đế thì nét đứt xuyên qua giữa huy hiệu.

    Tô `--vp-surface` chứ không tô trong suốt. Nền tấm là gradient, nên đế không
    khớp tuyệt đối; nhưng nhánh cuối của gradient CHÍNH LÀ `--vp-surface` và hàng
    mốc nằm ở cột phải của tấm, tức đúng vùng gradient đã về tới màu đó. */
.hang-huy {
  position: relative;
  display: grid; place-items: center;
  padding: 3px; margin-bottom: var(--s-2);
  border-radius: var(--r-full);
  background: var(--vp-surface);
}
.hh-moc { width: 28px; height: 34px; }
/*  Chưa đạt: huy hiệu mờ và bạc màu. Vẫn nhận ra là huy hiệu — đó là điểm của
    cả thay đổi này — nhưng không ai nhìn nhầm thành đã có. Bạc màu chứ không
    chỉ giảm độ mờ: dải kim loại vàng/bạc ở độ mờ thấp vẫn còn ám màu, và cái
    ám màu đó mới là thứ mắt đọc ra "đã đạt". */
.hang-moc > li:not(.dat) .hh-moc { opacity: .55; filter: grayscale(.5); }
/* Dấu tích chồng lên góc huy hiệu đã đạt. Viền cùng màu đế để nó tách khỏi
   huy hiệu bên dưới thay vì dính vào thành một khối. */
.hang-tick {
  position: absolute; right: -1px; bottom: 1px;
  display: grid; place-items: center;
  width: 16px; height: 16px; border-radius: var(--r-full);
  background: var(--vp-primary); color: var(--vp-on-primary);
  border: 2px solid var(--vp-surface);
}
.hang-tick .ms { font-size: 9px; }
.hang-moc > li b { font: 600 13.5px var(--font-display); color: var(--vp-muted); }
.hang-bps { font: 500 12px var(--font-body); }
/* Đã qua mốc: chấm đặc. Đang đứng ở mốc: thêm quầng để phân biệt với các mốc đã
   qua — "đã đạt Bạc" và "đang ở Bạc" là hai trạng thái khác nhau trên cùng dải. */
.hang-moc > li.dat b { color: var(--vp-ink); }
/* Đang đứng ở mốc: thêm quầng quanh đế để phân biệt với các mốc ĐÃ QUA — "đã
   đạt Bạc" và "đang ở Bạc" là hai trạng thái khác nhau trên cùng một dải. */
.hang-moc > li.dang .hang-huy { box-shadow: 0 0 0 4px var(--vp-tint); }
.hang-moc > li.dang b { color: var(--vp-primary-text); }
.hang-cau {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  margin: 0; font: 400 13.5px/1.5 var(--font-body); color: var(--vp-muted);
}

/* Tiêu đề mục kèm bộ lọc cùng hàng; hàng hẹp thì lọc rơi xuống dưới. */
.ud-dau-muc { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; }

/* ── Hình vẽ trang Ưu đãi ─────────────────────────────────────────────
   Xem `templates/customer/_hinh_uu_dai.html` để biết vì sao là SVG nội tuyến
   chứ không phải ảnh. Ở đây chỉ lo kích thước và màu.

   `--hinh-mau` là màu tranh, khai ở từng tấm khuyến mãi. Tranh đọc biến này
   qua `var(--hinh-mau, currentColor)` nên đặt nhầm chỗ cũng không mất hình,
   chỉ là nó lấy màu chữ. */
.hinh-kho { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Tỷ lệ khung huy hiệu là 48:58 — sai tỷ lệ thì lục giác méo, mà méo huy hiệu
   thì nhìn ra ngay kể cả khi không biết nó đáng lẽ trông thế nào. */
.hh { flex: none; color: var(--vp-muted); }
.hh-to { width: 46px; height: 56px; }
.hh-nho { width: 22px; height: 27px; }

/* Huy hiệu đứng cạnh dòng nhãn, không đứng cạnh tên hạng: tên hạng là dòng chữ
   lớn nhất tấm, kèm thêm huy hiệu vào là hai thứ tranh nhau chỗ. */
.hang-danh { display: flex; align-items: center; gap: var(--s-3); }

.hang-hop-dau {
  display: flex; align-items: center; gap: var(--s-3);
  margin-bottom: var(--s-3); padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--vp-line-soft);
}
.hang-hop-dau b { font: 600 13px var(--font-display); }

/* ── Tấm hạng Túi+ ────────────────────────────────────────────────────
   Một tấm lớn mở đầu trang, gộp mọi thứ về hạng của khách: đang ở đâu, được
   cộng bao nhiêu, còn thiếu gì, và cả bậc thang trên cùng một dải.

   Bản trước rải ra: dải mảnh nằm lọt giữa hai lưới thẻ, câu tóm tắt in nhỏ bên
   dưới. Đúng thông tin nhưng không ai dừng mắt lại — mấy con số này chỉ có
   nghĩa khi đọc CÙNG NHAU, "+0,3%/năm" chẳng nói lên gì nếu không biết đó là
   hạng nào và hạng kế còn cách bao xa.

   Nền pha từ chính hai màu của vạch tiến độ bên trong (`--vp-primary` và
   `--vp-ai`) nên tấm và vạch nhìn ra cùng một họ. Mảng màu chứ không phải ảnh:
   ví chạy offline, không có CDN, chèn <img> chỉ để lại ô vỡ. Pha bằng
   `color-mix` với `--vp-surface` nên nền tối tự đi theo, khỏi khai bộ biến thứ
   hai chỉ dùng ở một chỗ. */
.hang-banner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: var(--s-6) var(--s-8);
  align-items: center;
  margin: var(--s-5) 0 var(--s-6);
  padding: var(--s-7);
  border: 1px solid var(--vp-line-soft);
  border-radius: var(--r-xl);
  background:
    radial-gradient(120% 140% at 100% 0%, color-mix(in srgb, var(--vp-ai) 13%, transparent), transparent 62%),
    linear-gradient(135deg, color-mix(in srgb, var(--vp-primary) 10%, var(--vp-surface)), var(--vp-surface) 62%);
}
.hang-banner-tin { display: flex; flex-direction: column; gap: var(--s-3); min-width: 0; }
.hang-eyebrow {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font: 600 12px/1 var(--font-display); letter-spacing: .06em; text-transform: uppercase;
  color: var(--vp-primary-text);
}
.hang-ten {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  margin: 0; font: 600 26px/32px var(--font-display); letter-spacing: -.01em;
}
.hang-lon { margin: 0; }
.hang-lon-v {
  display: block;
  font: 600 40px/46px var(--font-display);
  letter-spacing: -.02em; font-variant-numeric: tabular-nums;
  color: var(--vp-primary-text);
}
/* Ca không có số thì chữ thay vào chỗ của số, và phải NHỎ lại: để nguyên 40px
   là một câu tiếng Việt chiếm hai dòng, tấm cao vọt lên so với ca thường. */
.hang-lon-trong { font-size: 19px; line-height: 26px; color: var(--vp-muted); }
.hang-lon-k { display: block; margin-top: 2px; font: 400 12.5px var(--font-body); color: var(--vp-muted); }
.hang-vi {
  display: flex; gap: var(--s-5); flex-wrap: wrap;
  margin-top: var(--s-2);
  font: 400 13px var(--font-body); color: var(--vp-muted);
}
.hang-vi b {
  font: 600 14px var(--font-display); color: var(--vp-ink);
  font-variant-numeric: tabular-nums;
}

/* ── Marquee chương trình mới mở ──────────────────────────────────────
   Ba trang khổ lớn, mỗi trang trọn bề ngang, chạy độc lập với băng chuyền bên
   dưới. Dùng lại hàng lái (`.km-lai`) của băng chuyền để hai thứ trên cùng một
   trang không có hai kiểu nút bấm khác nhau.

   Nền đậm hơn tấm băng chuyền (16% so với 7%): đây là khối quảng cáo, nó ĐƯỢC
   phép giành sự chú ý, còn tấm băng chuyền thì phải nhường chỗ cho nội dung. */
.mq { position: relative; margin: var(--s-5) 0 var(--s-7); }
.mq-ray {
  display: flex; gap: var(--s-5);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  padding-block: 4px; margin-block: -4px;
  scrollbar-width: none;
}
.mq-ray::-webkit-scrollbar { display: none; }
.mq-ray:focus-visible { outline: 2px solid var(--vp-primary); outline-offset: 2px; border-radius: var(--r-2xl); }
.mq-trang {
  flex: 0 0 100%; scroll-snap-align: start; min-width: 0;
  /* Hai cột: chữ co giãn, tranh giữ nguyên khổ. `<template>` popup cũng là con
     của tấm nhưng trình duyệt không dựng nó, nên không sinh ra cột thứ ba. */
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: var(--s-8);
  min-height: 236px; padding: var(--s-8);
  border: 1px solid var(--vp-line-soft); border-radius: var(--r-2xl);
}
.mq-noi { display: flex; flex-direction: column; gap: var(--s-4); min-width: 0; }
/* 120:104 — cùng tỷ lệ với viewBox của mọi tranh. Tranh vẽ bằng `currentColor`
   nên màu do `color` ở đây quyết định; marquee dùng thẳng màu nhấn vì tranh là
   phần tử tiền cảnh, không phải hoạ tiết nền. */
.mq-tranh { width: 190px; height: 165px; color: var(--hinh-mau); }
.mq-trang-1 {
  --hinh-mau: var(--phan-1);
  background: linear-gradient(120deg, color-mix(in srgb, var(--phan-1) 16%, var(--vp-surface)), var(--vp-surface) 68%);
}
.mq-trang-2 {
  --hinh-mau: var(--phan-2);
  background: linear-gradient(120deg, color-mix(in srgb, var(--phan-2) 16%, var(--vp-surface)), var(--vp-surface) 68%);
}
.mq-trang-3 {
  --hinh-mau: var(--phan-3);
  background: linear-gradient(120deg, color-mix(in srgb, var(--phan-3) 16%, var(--vp-surface)), var(--vp-surface) 68%);
}
.mq-dau { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.mq-moi {
  display: inline-flex; align-items: center; gap: 2px;
  font: 600 12px var(--font-display); color: var(--vp-primary-text);
}
.mq-moi .ms { font-size: 16px; }
.mq-tieu { margin: 0; font: 600 30px/38px var(--font-display); letter-spacing: -.02em; }
/* Bó chiều dài dòng: câu tóm tắt trải hết 1000px là mắt lạc dòng khi xuống hàng. */
.mq-mo { margin: 0; max-width: 62ch; font: 400 14.5px/1.6 var(--font-body); color: var(--vp-muted); }
.mq-chan { display: flex; align-items: center; gap: var(--s-6); flex-wrap: wrap; margin-top: var(--s-2); }
.mq-so { display: grid; }
.mq-so b {
  font: 600 28px/34px var(--font-display);
  font-variant-numeric: tabular-nums; color: var(--vp-primary-text);
}
.mq-so span { font: 400 12.5px var(--font-body); color: var(--vp-muted); }
.mq-han { display: inline-flex; align-items: center; gap: var(--s-2); font: 400 13px var(--font-body); color: var(--vp-muted); }
.mq-han .ms { font-size: 18px; }
/* Nút gọi hành động của marquee — chỗ khách bấm để nhận quà.

   Vòng sáng lan ra rồi tắt, 3,2 giây một nhịp. Nhịp CHẬM và vòng NHỎ (tối đa
   9px) là cố ý: thứ này phải gợi được mà không cướp mắt khỏi tiêu đề ngay bên
   trên nó — nhấp nháy nhanh trên một khối quảng cáo tự chạy là quá nhiều
   chuyển động cho một màn hình.

   Chỉ marquee có, tấm băng chuyền thì không: marquee hiện mỗi lúc một trang nên
   chỉ một nút nhấp, còn băng chuyền bày hai tấm rưỡi cùng lúc — ba vòng sáng
   cùng đập là nhiễu, không phải gợi ý.

   Dừng khi chuột đã tới nơi: người đang trỏ vào nút thì không cần rủ nữa. */
@keyframes mq-goi-bam {
  0%   { box-shadow: 0 0 0 0 var(--mq-goi-mau); }
  60%  { box-shadow: 0 0 0 9px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
.mq-nut {
  margin-left: auto;
  --mq-goi-mau: color-mix(in srgb, var(--hinh-mau, var(--vp-primary)) 55%, transparent);
  animation: mq-goi-bam 3.2s ease-out infinite;
}
.mq-nut:hover, .mq-nut:focus-visible { animation-play-state: paused; }
/* Người hạn chế chuyển động vẫn cần biết nút này đáng bấm: đổi nhịp đập thành
   một quầng đứng yên chứ không bỏ trắng. */
@media (prefers-reduced-motion: reduce) {
  .mq-nut { animation: none; box-shadow: 0 0 0 4px var(--mq-goi-mau); }
}

/* ── Băng chuyền khuyến mãi ───────────────────────────────────────────
   Cuộn ngang là của CSS, không phải của JS: `overflow-x` + `scroll-snap` cho
   sẵn kéo bằng chuột, vuốt trên cảm ứng và cuộn ngang bằng bàn di. JS chỉ thêm
   phần TỰ chạy cùng mấy nút bấm, nên trang không phụ thuộc JS để đọc được.

   Thanh cuộn ẩn đi vì đã có chấm chỉ vị trí ngay dưới; hai thứ cùng nói một
   điều thì bỏ bớt cái xấu hơn. */
.km-bang { position: relative; margin-top: var(--s-5); }
.km-ray {
  display: flex; gap: var(--s-5);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /*  NHOÈ MÉP THAY VÌ CẮT CỤT. `overflow` xén tấm khuất bằng một đường thẳng
      đứng, và chỗ cắt rơi giữa chữ thì đọc như lỗi hiển thị chứ không như "còn
      nữa, cuộn đi". Mặt nạ chuyển dần sang trong suốt ở 32px cuối nói đúng điều
      cần nói.

      Hai biến do app.js đặt theo VỊ TRÍ CUỘN, không nhoè cứng hai bên: cuộn hết
      về đầu mà mép trái vẫn mờ là bảo có thứ ở bên trái trong khi không có gì
      cả. Bằng 0px thì hai mốc gradient trùng nhau và không nhoè tí nào — không
      cần luật riêng cho ca đó. Không có JS thì cả hai giữ 0px: mất phần nhoè,
      không mất gì khác. */
  --lo-trai: 0px; --lo-phai: 0px;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--lo-trai),
                      #000 calc(100% - var(--lo-phai)), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 var(--lo-trai),
                      #000 calc(100% - var(--lo-phai)), transparent 100%);
  /* Chỉ đệm TRÊN–DƯỚI, không đệm hai bên: `scroll-snap` neo mép tấm vào mép
     hộp cuộn, nên đệm ngang chỉ làm mốc dừng lệch đi đúng ngần ấy pixel so với
     chỗ JS tính. Lề âm bù lại phần đệm để mép dải vẫn thẳng hàng với các mục
     khác trên trang. */
  padding-block: 4px; margin-block: -4px;
  scrollbar-width: none;
}
.km-ray::-webkit-scrollbar { display: none; }
.km-ray:focus-visible { outline: 2px solid var(--vp-primary); outline-offset: 2px; border-radius: var(--r-xl); }
/* `flex: 0 0` — tấm không được co lại cho vừa khung, vì co lại đúng là cái
   "đổ hết lên một lưới" mà băng chuyền sinh ra để tránh. Chừa 16% để mép tấm
   kế hé ra: đó là thứ nói cho khách biết còn chương trình phía sau. */
.km-tam {
  flex: 0 0 min(400px, 84%); scroll-snap-align: start; min-width: 0;
  /* `isolation` dựng tầng riêng cho tấm, để hình chìm bên dưới (z-index: -1)
     nằm TRÊN nền tấm chứ không tụt xuống dưới nó và biến mất. */
  position: relative; overflow: hidden; isolation: isolate;
  transition: border-color .16s ease, box-shadow .16s ease, translate .16s ease;
}
/* Tấm nhấc lên và viền ăn màu chương trình khi rê vào: đây là thứ nói cho khách
   biết tấm này BẤM ĐƯỢC. Bắt cả `focus-within` để đường bàn phím có đúng phản
   hồi ấy, không phải chỉ chuột mới được biết.

   `translate` CHỨ KHÔNG PHẢI `transform`. `.card` đang chạy `animation: ui-vao
   … both`, mà hoạt cảnh có fill-mode thắng mọi khai báo thường ở tầng xếp
   chồng — kể cả sau khi nó chạy xong, `transform: none` của khung hình cuối vẫn
   được giữ và đè lên `transform` viết ở đây. Bản đầu của tôi dùng `transform`
   và cái nhấc KHÔNG BAO GIỜ chạy; đo mới thấy, vì nhìn thì chỉ thấy "hình như
   nó không nhúc nhích".

   `translate` là thuộc tính riêng, độc lập với `transform`, nên hoạt cảnh kia
   không đụng tới. Bóng cũng phải đậm hơn hẳn (`--sh-3`): `.card` vốn đã mang
   sẵn `--sh-2`, đổi sang đúng giá trị đang có là không đổi gì cả. */
/* Cả tấm bấm được, nên con trỏ phải nói ra điều đó ở mọi chỗ trong tấm — kể cả
   trên chữ. Đây là nửa còn lại của tín hiệu; nửa kia là cái nhấc ở dưới. */
[data-tam-bam] { cursor: pointer; }

.km-tam:hover,
.km-tam:focus-within {
  translate: 0 -3px;
  border-color: color-mix(in srgb, var(--hinh-mau, var(--vp-primary)) 48%, var(--vp-line));
  box-shadow: var(--sh-3);
}
.km-tam-1 { --hinh-mau: var(--phan-1); background: color-mix(in srgb, var(--phan-1) 7%, var(--vp-surface)); }
.km-tam-2 { --hinh-mau: var(--phan-2); background: color-mix(in srgb, var(--phan-2) 7%, var(--vp-surface)); }
.km-tam-3 { --hinh-mau: var(--phan-3); background: color-mix(in srgb, var(--phan-3) 7%, var(--vp-surface)); }
/* Tràn ra ngoài mép rồi bị `overflow: hidden` xén — hình chìm là hoạ tiết nền,
   không phải minh hoạ; xén một góc làm nó hoà vào tấm thay vì đứng riêng.

   ĐỘ ĐẬM đo bằng tỷ lệ tương phản giữa nét và nền tấm, không ước lượng bằng mắt.
   Bản đầu để 10% cho ra 1,19 — gần như không tồn tại.

   NÉT PHA VỚI MÀU CHỮ, KHÔNG DÙNG THẲNG MÀU NHẤN. Ba tông `--phan-*` lệch nhau
   rất xa về độ sáng: cùng phủ 34% thì chàm cho 1,86 còn mòng két chỉ 1,49 —
   nhìn ra ngay là ba tấm đậm nhạt không đều. Tệ hơn, thứ tự lệch ĐẢO CHIỀU giữa
   hai chủ đề (nền sáng mòng két cần đậm nhất, nền tối lại cần nhạt nhất), nên
   chỉnh tay sáu con số là sáu chỗ để sai.

   Pha 60% màu nhấn với 40% màu chữ thì thành phần "màu chữ" tự kéo mọi tông ra
   xa nền đúng chiều ở cả hai chủ đề, mà sắc thì vẫn đọc ra được. Kết quả đo:
   nền sáng 2,23 / 1,89 / 2,14 — nền tối 2,25 / 2,63 / 2,46. Một con số, ba tông,
   hai chủ đề.

   Phủ tới 40% được vì chữ nằm TRÊN hình (hình ở z-index -1): con số này chỉ
   quyết định độ ồn của nền, không quyết định độ đọc của chữ. Rê vào tấm thì lên
   56% và nhích lên, thành một phần của phản hồi "tấm này bấm được" ở trên. */
.km-hinh {
  position: absolute; z-index: -1; right: -12px; bottom: -10px;
  width: 150px; height: 130px;
  color: color-mix(in srgb, var(--hinh-mau, var(--vp-primary)) 60%, var(--vp-ink));
  opacity: .4; pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}
.km-tam:hover .km-hinh,
.km-tam:focus-within .km-hinh { opacity: .56; transform: translateY(-3px) scale(1.05); }

/* Nút trên tấm cũng ăn màu theo, để cả tấm phản hồi như MỘT khối chứ không
   phải mấy mảnh rời nhau cùng động đậy. */
.km-tam:hover .btn-ghost,
.km-tam:focus-within .btn-ghost {
  border-color: color-mix(in srgb, var(--hinh-mau, var(--vp-primary)) 55%, var(--vp-line));
  color: var(--vp-primary-text);
}


.km-nb {
  display: inline-flex; align-items: center; gap: 2px;
  font: 600 12px var(--font-display); color: var(--vp-primary-text);
}
.km-nb .ms { font-size: 15px; }

/* Hàng lái ẩn cho tới khi JS gỡ `km-cam`: không có JS thì dải vẫn cuộn được
   bằng tay, còn nút thì bấm không ra gì — thà đừng hiện. */
.km-lai { display: flex; align-items: center; justify-content: center; gap: var(--s-4); margin-top: var(--s-4); }
.km-cam { display: none; }
.km-mui {
  display: grid; place-items: center;
  width: 34px; height: 34px; padding: 0;
  border: 1px solid var(--vp-line); border-radius: var(--r-full);
  background: var(--vp-surface); color: var(--vp-ink);
  cursor: pointer;
}
.km-mui:hover { border-color: var(--vp-primary); color: var(--vp-primary-text); }
.km-mui .ms { font-size: 20px; }
.km-cham { display: flex; align-items: center; gap: var(--s-2); list-style: none; margin: 0; padding: 0; }
.km-cham button {
  display: block; width: 8px; height: 8px; padding: 0;
  border: 0; border-radius: var(--r-full);
  background: var(--vp-line); cursor: pointer;
  transition: width .2s ease, background .2s ease;
}
.km-cham button:hover { background: var(--vp-muted); }
.km-cham button[aria-current="true"] { width: 22px; background: var(--vp-primary); }

/* Hộp thoại chi tiết rộng hơn hộp xem-bước: nội dung ở đây là đoạn văn + danh
   sách điều kiện, bó hẹp như hộp kia thì chữ vỡ thành cột rất dài. */
.modal-rong { max-width: 560px; }
.modal-rong .modal-than > * + * { margin-top: var(--s-5); }
.modal-rong h4 { margin: 0; font: 600 14px var(--font-display); }
/* Tên đầy đủ của chương trình, mở đầu hộp chi tiết — thứ mà câu quảng cáo ngoài
   tấm đã lược đi. Đậm hơn thân bài để đọc ra ngay là "tên", không phải một câu
   mô tả nữa. */
.popup-ten { margin: 0; font: 600 15px/1.5 var(--font-display); }
.modal-rong h4 + * { margin-top: var(--s-3); }
.modal-rong p { margin: 0; font: 400 14px/1.6 var(--font-body); }
.modal-rong .alert { display: flex; gap: 10px; }

/* ══ Điều kiện nhận thưởng (trang Ưu đãi) ══════════════════════════
   Danh sách icon + câu điều kiện. KHÔNG dùng lại .chat-nguon-ds dù nhìn
   na ná: class đó là lưới 2 cột cỡ 12.5px của khung chat, đổi nó vì trang
   này là sửa luôn phần trích nguồn của trợ lý.

   `align-items: start` chứ không phải `baseline`: câu điều kiện dài xuống
   hai dòng thì icon phải neo ở dòng đầu, baseline sẽ kéo nó xuống giữa. */
.dk-ds { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--s-2); }
.dk-ds li {
  display: grid; grid-template-columns: auto 1fr; gap: var(--s-3);
  align-items: start;
  font: 400 14px/1.55 var(--font-body);
}
.dk-ds .ms { font-size: 19px; color: var(--vp-muted); }

/* ══ Thẻ đếm (hàng chờ, báo cáo) ═══════════════════════════════════
   .k là nhãn, .v là con số. Ở hàng chờ cả thẻ là <a> lọc được — nên nó
   phải trông bấm được, không phải một ô số tĩnh. */
.k { font: 400 12.5px var(--font-body); color: var(--vp-muted); }
.v {
  margin-top: var(--s-1);
  font: 600 30px/36px var(--font-display);
  font-variant-numeric: tabular-nums;
}

/* ══ Lối vào khiếu nại ═════════════════════════════════════════════ */

.gates { display: flex; flex-direction: column; gap: var(--s-3); }
.gate {
  display: flex; align-items: center; gap: var(--s-4);
  padding: 14px var(--s-5);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  background: var(--vp-surface-2);
  color: inherit; text-decoration: none;
  font: 500 14px var(--font-body);
  text-align: left;
  cursor: pointer;
  width: 100%;
}
.gate:hover { border-color: var(--vp-primary); background: var(--vp-tint); color: inherit; }
.gate .ms { color: var(--vp-primary-text); }
.gate .mui { margin-left: auto; color: var(--vp-faint); }

/* Đỉnh màn chi tiết giao dịch: icon trạng thái, số tiền cỡ lớn, badge, biên lai. */
.gd-dinh { text-align: center; padding: var(--s-5) 0 var(--s-1); }
.gd-dinh-ico {
  display: inline-grid; place-items: center;
  width: 60px; height: 60px;
  border-radius: var(--r-xl);
}
.gd-dinh-ico .ms { font-size: 30px; }
.gd-dinh h1 { margin: var(--s-4) 0 var(--s-3); font-size: 34px; line-height: 40px; font-variant-numeric: tabular-nums; }
.gd-dinh .badge { font-size: 13px; padding: var(--s-2) 13px; }
.gd-dinh .muted { margin-top: var(--s-3); }

/* ══ Hộp thoại ═════════════════════════════════════════════════════ */

.modal, dialog {
  width: min(560px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-2xl);
  background: var(--vp-surface);
  color: var(--vp-ink);
  box-shadow: var(--sh-pop);
}
/* <dialog> chỉ chạy hoạt cảnh khi đã có [open] — trước đó nó là display:none
   nên không có gì để diễn. Lúc đóng thì ngược lại: app.js phải giữ hộp lại
   đúng thời lượng rồi mới gọi close(), nếu không hộp biến mất tức thì. */
dialog[open] { animation: hop-vao .2s cubic-bezier(.2, .7, .3, 1) both; }
dialog.dang-dong { animation: hop-ra .16s ease-in both; }
dialog[open]::backdrop { animation: mo-nhe .2s ease-out both; }
dialog.dang-dong::backdrop { animation: tat-nhe .16s ease-in both; }
dialog::backdrop { background: rgba(19, 27, 46, .45); }
.modal-dau {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-5) var(--s-6);
  border-bottom: 1px solid var(--vp-line);
}
.modal-dau h3 { flex: 1; }
.modal-dong {
  display: grid; place-items: center;
  width: 34px; height: 34px; flex: none;
  border: 0; border-radius: var(--r-full);
  background: var(--vp-tint); color: var(--vp-muted);
  font-size: 20px; line-height: 1; cursor: pointer;
}
.modal-dong:hover { background: var(--vp-tint-2); color: var(--vp-ink); }
.modal-than { padding: var(--s-6); }

/* ══ Toast ═════════════════════════════════════════════════════════ */

/*  Khay toast ở GÓC PHẢI TRÊN, ngay dưới thanh trên.

    Trước đây nó ở góc phải dưới và tranh chỗ với nút trợ lý nổi — toast thắng vì
    z-index cao hơn, nên đang có giao dịch mới thì nút trợ lý bị che, đúng lúc
    khách muốn hỏi. Đưa lên đỉnh là hai thứ hết đường gặp nhau: nút ở đáy, toast
    ở đỉnh, không cần ai phải né ai bằng một con số chừa chỗ.

    Đỉnh còn đúng hơn ở một điểm nữa: đáy màn là nơi ngón tay và con trỏ hoạt
    động nhiều nhất, còn đỉnh thì gần như chỉ để đọc.

    `pointer-events: none` ở khay và `auto` ở từng toast: khoảng hở giữa các
    toast không chắn chuột, chỉ bản thân tấm toast mới chắn. */
.toast-viewport {
  position: fixed; right: var(--s-6);
  top: calc(var(--topbar-h) + var(--s-4)); bottom: auto;
  z-index: 90; width: min(360px, calc(100vw - 32px));
  pointer-events: none;
}
/*  Các tấm XẾP CHỒNG lên nhau, không xếp thành cột — lối bày của Sonner.

    Mọi tấm cùng neo vào một mép của khay và chồng lên nhau; app.js đặt `--y` và
    `--co` cho từng tấm theo thứ hạng của nó trong chồng, rồi đổi lại khi cả
    chồng bung ra. Nhờ vậy ba tấm chỉ chắn đúng diện tích của một tấm cộng phần
    thò ra, thay vì gần 300px chiều cao như bản xếp cột.

    Chiều cao `.toast-list` do app.js đặt, KHÔNG để nó tự cao theo nội dung: các
    tấm đều `position: absolute` nên nội dung không còn đẩy chiều cao, mà khay
    lại chính là vùng chắn chuột — sai chiều cao ở đây là chắn nhầm một vùng
    trống, đúng cái vấn đề mà xếp chồng sinh ra để giải. */
.toast-list {
  position: relative; list-style: none; margin: 0; padding: 0;
  transition: height .3s cubic-bezier(.22, 1, .36, 1);
}
.toast {
  pointer-events: auto;
  /* Neo ĐỈNH ở mọi bề ngang màn, nên chồng đổ xuống dưới và tấm thu nhỏ co về
     phía đỉnh — mép trên của cả chồng vì thế thẳng hàng. */
  position: absolute; left: 0; right: 0; top: 0;
  /* Tấm phía sau bị ép cao bằng tấm trước khi đang xếp chồng (app.js đặt
     `height`); không xén thì phần nội dung thừa tràn ra ngoài viền bo góc. */
  overflow: hidden;
  padding: 14px var(--s-5);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  background: var(--vp-surface);
  box-shadow: var(--sh-pop);
  transform-origin: top center;
  transform: translateY(var(--y, 0)) scale(var(--co, 1));
  transition: transform .3s cubic-bezier(.22, 1, .36, 1), opacity .26s ease,
              height .3s cubic-bezier(.22, 1, .36, 1);
}
/* Tấm đang đóng: mờ đi và trượt tiếp ra ngoài mép. `--y` lúc này do app.js giữ
   nguyên, chỉ opacity đổi — nên nó tan tại chỗ chứ không nhảy về đầu chồng. */
.toast[data-state="closed"] { opacity: 0 !important; }
.toast-title { display: block; font: 600 14px var(--font-display); }
.toast-desc { display: block; margin-top: 2px; font: 400 12.5px/1.5 var(--font-body); color: var(--vp-muted); }
.toast-action {
  display: inline-block; margin-top: var(--s-3);
  font: 600 12.5px var(--font-display);
  color: var(--vp-primary-text); text-decoration: none;
}
.toast-close {
  position: absolute; top: var(--s-3); right: var(--s-3);
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border: 0; border-radius: var(--r-full);
  background: transparent; color: var(--vp-faint);
  font-size: 16px; line-height: 1; cursor: pointer;
}
.toast-close:hover { background: var(--vp-tint); color: var(--vp-ink); }

/*  Băng báo câu trả lời bị bỏ dở vì khách rời trang giữa chừng.

    Không dùng bong bóng của trợ lý: đây không phải lời trợ lý nói, mà là ví
    thuật lại chuyện đã xảy ra. Cho nó dáng khác hẳn — nằm giữa, nền cảnh báo,
    có nút — để không ai đọc nhầm thành câu trả lời. */
.chat-ngat {
  display: flex; align-items: center; justify-content: center;
  gap: var(--s-4); flex-wrap: wrap;
  margin: var(--s-4) auto 0; max-width: 46ch;
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--tone-warn-fg);
  border-radius: var(--r-lg);
  background: var(--tone-warn-bg); color: var(--tone-warn-fg);
  font: 400 13px/1.5 var(--font-body);
  text-align: center;
}

/* ══ Chuông thông báo ══════════════════════════════════════════════ */

.chuong-boc { position: relative; }
.chuong {
  display: grid; place-items: center;
  width: 40px; height: 40px;
  border: 0; border-radius: var(--r-full);
  background: transparent; color: var(--vp-muted);
  cursor: pointer;
}
.chuong:hover { background: var(--vp-tint); color: var(--vp-ink); }
.chuong-so {
  position: absolute; top: 4px; right: 2px;
  min-width: 18px; height: 18px; padding: 0 5px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--vp-danger); color: #fff;
  font: 600 11px var(--font-display);
}
.chuong-bang {
  position: absolute; top: calc(100% + 8px); right: 0;
  z-index: 40; width: 320px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  background: var(--vp-surface);
  box-shadow: var(--sh-pop);
  overflow: hidden;
  transform-origin: top right;
  animation: xo-vao .17s cubic-bezier(.2, .7, .3, 1) both;
}
.chuong-bang.dang-dong { animation: xo-ra .14s ease-in both; }
.chuong-dau {
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--vp-line);
  font: 600 13px var(--font-display);
}
.chuong-ds { list-style: none; margin: 0; padding: 0; max-height: 320px; overflow-y: auto; }
.chuong-ds li { border-bottom: 1px solid var(--vp-line-soft); }
.chuong-ds li:last-child { border-bottom: 0; }
.chuong-ds a {
  display: block; padding: var(--s-4) var(--s-5);
  color: inherit; text-decoration: none;
  font: 400 13px/1.5 var(--font-body);
}
.chuong-ds a:hover { background: var(--vp-tint); color: inherit; }
/* app.js gắn `ct vao` / `ct ra` cho dòng tiền vào / tiền ra trong danh sách. */
.chuong-ds .ct { display: block; font: 600 13px var(--font-display); font-variant-numeric: tabular-nums; }
.chuong-ds .vao { color: var(--tone-ok-fg); }
.chuong-ds .ra { color: var(--vp-muted); }
.chuong-rong { padding: var(--s-5); font: 400 13px var(--font-body); color: var(--vp-muted); }

/* ══ Nút đổi nền ═══════════════════════════════════════════════════ */

.theme-toggle {
  display: grid; place-items: center;
  width: 40px; height: 40px;
  border: 0; border-radius: var(--r-full);
  background: transparent; color: var(--vp-muted);
  cursor: pointer;
}
.theme-toggle:hover { background: var(--vp-tint); color: var(--vp-ink); }
.ico-sang, .ico-toi { display: none; }
:root[data-theme="dark"] .ico-sang { display: block; }
:root[data-theme="light"] .ico-toi { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ico-sang { display: block; }
  :root:not([data-theme="light"]) .ico-toi { display: none; }
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .ico-toi { display: block; }
  :root:not([data-theme="dark"]) .ico-sang { display: none; }
}

/* ══ Chip tài khoản thử (UAT) ══════════════════════════════════════ */

.demo-hint { margin: 0 0 var(--s-3); font: 400 12.5px var(--font-body); color: var(--vp-muted); }
.demo-row { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.demo-chip {
  display: inline-flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) 14px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-full);
  background: var(--vp-surface-2);
  color: var(--vp-primary-text);
  font: 500 12.5px var(--font-display);
  cursor: pointer;
}
.demo-chip:hover { border-color: var(--vp-primary); background: var(--vp-tint); }
.seed-box {
  padding: var(--s-5);
  border: 1px dashed var(--vp-line);
  border-radius: var(--r-lg);
  background: var(--vp-tint);
}

/* ══ Trang đầu — đăng nhập trước, câu chuyện tích hợp đứng sau ══════ */

.home-page {
  --home-bg: #ffffff;
  --home-ink: #102321;
  --home-body: #49605d;
  --home-faint: #6d817e;
  --home-line: #d7e5e2;
  --home-soft: #f1f7f5;
  --home-walletcare: #1967e8;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .home-page {
    --home-bg: var(--vp-bg);
    --home-ink: var(--vp-ink);
    --home-body: var(--vp-muted);
    --home-faint: var(--vp-faint);
    --home-line: var(--vp-line);
    --home-soft: var(--vp-surface);
  }
}
:root[data-theme="dark"] .home-page {
  --home-bg: var(--vp-bg);
  --home-ink: var(--vp-ink);
  --home-body: var(--vp-muted);
  --home-faint: var(--vp-faint);
  --home-line: var(--vp-line);
  --home-soft: var(--vp-surface);
}
.home-page .topbar { background: var(--home-bg); }
.home-page .main-cot { min-height: 100dvh; }
.home-page .wrap-home {
  width: 100%; max-width: none; min-height: calc(100dvh - var(--topbar-h));
  margin: 0; padding: 0;
}
.home-page .foot { display: none; }
.home-hero {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100dvh - var(--topbar-h));
  overflow: hidden;
  padding: clamp(36px, 5vw, 72px);
  background:
    radial-gradient(circle at 50% 0%, color-mix(in srgb, var(--vp-on-primary-soft) 36%, transparent), transparent 42%),
    var(--home-bg);
  color: var(--home-ink);
}
.home-hero::after {
  content: "";
  position: absolute;
  width: 320px; height: 320px;
  right: -160px; bottom: -190px;
  border: 1px solid color-mix(in srgb, var(--vp-primary) 12%, transparent);
  border-radius: var(--r-full);
  box-shadow: 0 0 0 54px color-mix(in srgb, var(--vp-primary) 3%, transparent), 0 0 0 108px color-mix(in srgb, var(--vp-primary) 2%, transparent);
  pointer-events: none;
}
.home-hero-content {
  position: relative; z-index: 1;
  width: min(100%, 920px);
  text-align: center;
}
.home-hero h1 {
  margin: var(--s-6) auto 0;
  font: 700 clamp(42px, 5vw, 68px)/1.02 var(--font-display);
  letter-spacing: -.045em;
  color: var(--home-ink);
}
.home-intro { max-width: 620px; margin: var(--s-5) auto 0; font: 400 clamp(15px, 1.6vw, 18px)/1.6 var(--font-body); color: var(--home-body); }
.home-access { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-3); max-width: 680px; margin: var(--s-7) auto 0; text-align: left; }
.home-access-card {
  display: grid; grid-template-columns: 40px minmax(0, 1fr) 20px;
  align-items: center; gap: var(--s-3);
  min-height: 76px; padding: 14px 16px;
  border: 1px solid var(--home-line);
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--home-bg) 92%, transparent);
  color: var(--home-ink); text-decoration: none;
  box-shadow: 0 10px 28px color-mix(in srgb, var(--home-ink) 5%, transparent);
  transition: transform .18s ease, border-color .18s ease, background .18s ease, box-shadow .18s ease;
}
.home-access-card:hover { transform: translateY(-2px); border-color: var(--vp-primary); background: var(--home-soft); color: var(--home-ink); box-shadow: 0 14px 34px color-mix(in srgb, var(--home-ink) 9%, transparent); }
.home-access-customer { border-color: var(--vp-primary); background: var(--vp-primary); color: var(--vp-on-primary); }
.home-access-customer:hover { background: var(--vp-primary-2); color: var(--vp-on-primary); }
.home-access-icon { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 12px; background: rgba(255, 255, 255, .16); }
.home-access-staff .home-access-icon { background: var(--home-soft); color: var(--vp-primary-text); }
.home-access-icon .ms { font-size: 19px; }
.home-access-card strong, .home-access-card small { display: block; }
.home-access-card strong { font: 650 14px/19px var(--font-display); }
.home-access-card small { margin-top: 2px; font: 450 11px/15px var(--font-body); opacity: .68; }
.home-access-arrow { justify-self: end; font-size: 18px; }
.home-uat-inline { display: flex; align-items: center; justify-content: center; gap: 7px; margin: var(--s-4) 0 0; font: 450 11px/16px var(--font-body); color: var(--home-faint); }
.home-uat-inline .ms { font-size: 15px; color: var(--tone-warn-fg); }

.home-brand-pair { display: grid; grid-template-columns: 88px 84px 88px; align-items: center; width: 260px; margin: 0 auto; }
.home-brand-product { display: grid; justify-items: center; gap: 6px; }
.home-brand-product > strong { font: 700 13px/17px var(--font-display); letter-spacing: -.015em; white-space: nowrap; }
.home-brand-name-vpay { color: var(--vp-primary-text); font-weight: 700; }
.home-brand-name-walletcare { font-weight: 700; white-space: nowrap; }
.home-brand-name-walletcare > span:first-child { color: var(--home-ink); }
.home-brand-name-walletcare > span:last-child { color: var(--home-walletcare); }
.home-intro strong { font-family: var(--font-display); }
.home-brand-logo { display: grid; place-items: center; width: 56px; height: 56px; }
.home-brand-logo img { display: block; width: 56px; height: 56px; object-fit: contain; }
.home-brand-logo-vpay img { width: 52px; height: 52px; }
.home-brand-logo-wc img { width: 56px; height: 56px; }
.home-brand-logo-wc { background: transparent; border-radius: 0; overflow: visible; }
.home-brand-bridge { display: flex; flex-direction: column; align-items: center; gap: 2px; color: var(--home-faint); text-align: center; }
.home-brand-bridge small { font: 550 9px/13px var(--font-display); letter-spacing: .04em; white-space: nowrap; }
.home-brand-bridge i { color: var(--vp-primary-text); font: 400 22px/24px var(--font-display); font-style: normal; }
.home-partner-link { display: inline-flex; align-items: center; gap: var(--s-3); margin-top: var(--s-5); color: var(--vp-primary-text); font: 600 12px/18px var(--font-display); text-decoration: none; }
.home-partner-link:hover { color: var(--home-ink); }
.home-partner-link .home-brand-name-walletcare { font: 700 12px/18px var(--font-display); }
.home-partner-link .ms { font-size: 17px; }

@media (max-width: 859px) {
  .home-hero { min-height: calc(100dvh - var(--topbar-h)); }
}
@media (max-width: 640px) {
  .home-hero { padding: 36px var(--s-5) 48px; }
  .home-hero h1 { font-size: clamp(38px, 11vw, 52px); }
  .home-access { grid-template-columns: 1fr; }
  .home-brand-pair { grid-template-columns: 76px 76px 76px; width: 228px; }
}

/* ══════════════════════════════════════════════════════════════════
   TRỢ LÝ — hội thoại
   ══════════════════════════════════════════════════════════════════ */

.chat {
  display: flex; flex-direction: column;
  min-height: 0;
  background: var(--vp-surface-2);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-2xl);
  overflow: hidden;
}

/* Thanh tên trợ lý: một dòng, gọn hết mức. Trước đây có thêm dòng phụ "Chỉ đọc
   dữ liệu của bạn" — bỏ đi vì lời hứa đó đã nằm ở thẻ trong cột điều hướng, để
   ở cả hai chỗ chỉ tốn chiều cao của phần hội thoại. */
.chat-dau {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-5);
  border-bottom: 1px solid var(--vp-line);
  flex: none;
}
.chat-dau .ico {
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: var(--r-full);
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--vp-ai) 24%, var(--vp-line));
  background: var(--vp-ai-soft);
  box-shadow: var(--sh-1);
}
.chat-dau .ico img { width: 100%; height: 100%; object-fit: cover; transform: scale(1.08); }
.chat-dau b { font: 600 15px/20px var(--font-display); }

.chat-thread {
  flex: 1; min-height: 0;
  overflow-y: auto;
  padding: var(--s-5);
  display: flex; flex-direction: column; gap: var(--s-5);
  scrollbar-width: thin;
}
.chat-thread::-webkit-scrollbar { width: 8px; }
.chat-thread::-webkit-scrollbar-thumb { background: var(--vp-line); border-radius: var(--r-full); }
.chat-thread::-webkit-scrollbar-track { background: transparent; }

.msg { display: flex; align-items: flex-start; gap: var(--s-3); }
.msg-me { justify-content: flex-end; }
.msg-me .bubble {
  max-width: 88%;
  padding: var(--s-4) 14px;
  border-radius: var(--r-lg) var(--r-lg) var(--r-xs) var(--r-lg);
  background: var(--vp-primary); color: var(--vp-on-primary);
  font: 400 13px/19px var(--font-body);
}
/* Mascot VPAY là một asset thật, không phụ thuộc font icon. Dùng pseudo-element
   giữ DOM bong bóng gọn và giúp mọi đường dựng/khôi phục chat có cùng avatar. */
.msg-bot::before {
  content: "";
  display: block;
  width: 36px; height: 36px; flex: none;
  margin-top: 1px;
  border-radius: var(--r-full);
  border: 1px solid color-mix(in srgb, var(--vp-ai) 24%, var(--vp-line));
  background: var(--vp-ai-soft) url("/static/vpay-crane-mascot.webp") center / cover no-repeat;
  box-shadow: var(--sh-1);
}
.msg-bad::before {
  content: "warning";
  font-family: 'Material Symbols Outlined';
  font-variation-settings: 'FILL' 1;
  font-size: 17px; line-height: 1;
  display: grid; place-items: center;
  width: 32px; height: 32px; flex: none;
  margin-top: 2px;
  border-radius: var(--r-sm);
  background: var(--tone-bad-bg); color: var(--tone-bad-fg);
}
.msg-stack {
  display: flex;
  flex: 0 1 min(720px, calc(100% - 44px));
  min-width: 0;
  max-width: min(720px, calc(100% - 44px));
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}
.msg-bad > .bubble { min-width: 0; flex: 1; }
.msg-bot .bubble {
  width: fit-content;
  max-width: 100%;
  padding: var(--s-4) 14px;
  border-radius: var(--r-xs) var(--r-lg) var(--r-lg) var(--r-lg);
  border: 1px solid var(--vp-line-soft);
  background: var(--vp-surface-2); color: var(--vp-ink);
  box-shadow: var(--sh-1);
  font: 400 13px/20px var(--font-body);
}
.msg-bad .bubble { background: var(--tone-bad-bg); color: var(--tone-bad-fg); }
.bubble > *:first-child { margin-top: 0; }
.bubble > *:last-child { margin-bottom: 0; }
.bubble p { margin: 0 0 var(--s-3); }
.bubble strong { font-weight: 600; }

.chat-tips { display: flex; flex-direction: column; gap: var(--s-3); padding: 0 var(--s-5) var(--s-4); flex: none; }
.tip {
  text-align: left;
  padding: 9px var(--s-4);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-md);
  background: var(--vp-surface-2);
  color: var(--vp-primary-text);
  font: 500 12.5px var(--font-display);
  cursor: pointer;
}
.tip:hover { border-color: var(--vp-primary); background: var(--vp-tint); }

.chat-composer {
  position: relative;
  display: flex; align-items: center;
  padding: var(--s-5);
  border-top: 1px solid var(--vp-line);
  flex: none;
}
.chat-composer textarea {
  width: 100%;
  min-height: 46px; max-height: 160px;
  padding: 13px 52px 13px var(--s-5);
  border-radius: var(--r-xl);   /* bo tròn hẳn chỉ hợp lúc một dòng */
  background: var(--vp-tint);
  font: 400 14px/20px var(--font-body);
  resize: none;
  /* Ẩn thanh cuộn, KHÔNG tắt cuộn. Trình duyệt vẫn tự kéo theo con trỏ nên gõ
     dài vẫn thấy chỗ mình đang gõ; chỉ là không có cái vạch xám cắt ngang ô. */
  scrollbar-width: none;
}
.chat-composer textarea::-webkit-scrollbar { width: 0; height: 0; }
.chat-composer .btn {
  /* Ghim ĐÁY phải, không phải giữa. Ô nhập cao dần theo số dòng; canh giữa thì
     nút trôi xuống theo và lệch dần so với dòng đang gõ. */
  position: absolute;
  right: calc(var(--s-5) + 6px);
  bottom: calc(var(--s-5) + 6px);
  width: 34px; height: 34px; min-height: 34px;
  padding: 0;
  border-radius: var(--r-full);
  background: var(--vp-ai); border-color: var(--vp-ai);
}
.chat-composer .btn:hover { background: var(--vp-ai-2); border-color: var(--vp-ai-2); }
.chat-composer .btn .ms { font-size: 18px; }

/* ── Phiếu xác nhận khiếu nại (HITL) ──────────────────────────────
   Đây là một hồ sơ chuẩn bị gửi, không phải lời chat của agent. Tách nó thành
   card sáng có nhãn/trường rõ ràng để khách kiểm tra được sai mã, sai vấn đề
   trước khi bấm; nền đặc cho cả một đoạn dài làm mất hierarchy và khó đọc. */
.msg-bot .bubble:has(> .hitl) {
  width: 100%;
  max-width: 640px;
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
}
.hitl {
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--vp-line);
  border-top: 3px solid var(--vp-primary);
  border-radius: var(--r-lg);
  background: var(--vp-surface-2);
  color: var(--vp-ink);
  box-shadow: var(--sh-2);
}
.hitl-dau {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-5) var(--s-5) var(--s-4);
}
.hitl-ico {
  display: grid;
  flex: none;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--r-md);
  background: var(--vp-ai-soft);
  color: var(--vp-ai);
  overflow: hidden;
}
.hitl-ico svg { display: block; width: 21px; height: 21px; }
.hitl-tieu-de { min-width: 0; flex: 1; }
.hitl-loai {
  display: block;
  margin-bottom: 2px;
  color: var(--vp-muted);
  font: 600 10.5px/14px var(--font-display);
  letter-spacing: .08em;
  text-transform: uppercase;
}
.hitl-ten {
  margin: 0;
  color: var(--vp-ink);
  font: 600 17px/23px var(--font-display);
}
.hitl-trang-thai {
  flex: none;
  padding: 5px 9px;
  border-radius: var(--r-full);
  background: var(--tone-warn-bg);
  color: var(--tone-warn-fg);
  font: 600 10.5px/14px var(--font-body);
}
.hitl .hitl-dan {
  margin: 0;
  padding: 0 var(--s-5) var(--s-4);
  color: var(--vp-muted);
  font: 400 12.5px/18px var(--font-body);
}
.hitl-chi-tiet {
  margin: 0;
  border-top: 1px solid var(--vp-line-soft);
  border-bottom: 1px solid var(--vp-line-soft);
}
.hitl-dong {
  display: grid;
  grid-template-columns: minmax(116px, 150px) minmax(0, 1fr);
  gap: var(--s-4);
  padding: 11px var(--s-5);
}
.hitl-dong + .hitl-dong { border-top: 1px solid var(--vp-line-soft); }
.hitl-dong dt {
  color: var(--vp-muted);
  font: 500 11.5px/17px var(--font-body);
}
.hitl-dong dd {
  min-width: 0;
  margin: 0;
  color: var(--vp-ink);
  overflow-wrap: anywhere;
  font: 500 12.5px/18px var(--font-body);
}
.hitl .hitl-noi-dung {
  margin: 0 var(--s-5);
  padding: var(--s-4);
  border: 1px solid var(--vp-line-soft);
  border-radius: var(--r-md);
  background: var(--vp-tint);
  color: var(--vp-ink);
  white-space: pre-wrap;
  font: 400 12.5px/18px var(--font-body);
}
.hitl-luu-y {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  padding: var(--s-4) var(--s-5) 0;
  color: var(--vp-muted);
  font: 400 11.5px/17px var(--font-body);
}
.hitl-luu-y .ms { flex: none; margin-top: 1px; color: var(--vp-primary-text); font-size: 15px; }
.hitl-hanh-dong {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s-2);
  padding: var(--s-4) var(--s-5) var(--s-5);
}
.hitl-nut {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 40px;
  padding: 0 var(--s-4);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  font: 600 12.5px var(--font-display);
  cursor: pointer;
  transition: background .15s, border-color .15s, transform .15s;
}
.hitl-nut .ms { font-size: 17px; }
.hitl-nut:hover { transform: translateY(-1px); }
.hitl-nut:focus-visible { outline: 2px solid var(--vp-primary-text); outline-offset: 2px; }
.hitl-nut-chinh {
  order: 2;
  background: var(--vp-primary);
  border-color: var(--vp-primary);
  color: var(--vp-on-primary);
}
.hitl-nut-chinh:hover { background: var(--vp-primary-2); border-color: var(--vp-primary-2); }
.hitl-nut-phu {
  order: 1;
  background: transparent;
  border-color: var(--vp-line);
  color: var(--vp-muted);
}
.hitl-nut-phu:hover { background: var(--vp-tint); border-color: var(--vp-primary); color: var(--vp-ink); }

@media (max-width: 520px) {
  .hitl-dau { align-items: flex-start; }
  .hitl-trang-thai { margin-left: auto; }
  .hitl-dong { grid-template-columns: 1fr; gap: 2px; }
  .hitl-hanh-dong { align-items: stretch; flex-direction: column; }
  .hitl-nut { width: 100%; }
  .hitl-nut-chinh { order: 1; }
  .hitl-nut-phu { order: 2; }
}

.chat-quick { display: flex; flex-direction: column; gap: var(--s-3); margin-top: var(--s-4); }
.chat-quick .tip { text-align: center; }

.chat-cta {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: var(--s-3);
  padding: 9px 14px;
  border-radius: var(--r-md);
  background: var(--vp-primary); color: var(--vp-on-primary);
  font: 600 13px var(--font-display);
  text-decoration: none;
}
.chat-cta:hover { background: var(--vp-primary-2); color: var(--vp-on-primary); }

/* ── Reasoning / thinking — cùng interaction với Platform frontend ─────
   Đang chạy: chỉ một dòng shimmer, bước mới thay bước cũ. Đã xong: co lại
   thành disclosure; người cần kiểm chứng vẫn mở được timeline đầy đủ. */
.trace { color: var(--vp-muted); }
.trace-tick {
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 16px;
  height: 16px;
}
.trace-tick svg { display: block; width: 100%; height: 100%; overflow: visible; }
.trace-tick circle,
.trace-tick path {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}
.trace-tick circle { stroke: var(--vp-line); }
.trace-tick path {
  stroke: var(--tone-ok-fg);
  stroke-dasharray: 14;
  stroke-dashoffset: 14;
  opacity: 0;
}
.trace-tick-live circle { animation: trace-tick-ring .32s ease-out .1s forwards; }
.trace-tick-live path { animation: trace-tick-draw .34s cubic-bezier(.22, 1, .36, 1) .16s forwards; }
.trace-tick-done circle { stroke: var(--tone-ok-fg); }
.trace-tick-done path { stroke-dashoffset: 0; opacity: 1; }
.trace-tick-small { width: 13px; height: 13px; }
@keyframes trace-tick-ring {
  to { stroke: var(--tone-ok-fg); }
}
@keyframes trace-tick-draw {
  to { stroke-dashoffset: 0; opacity: 1; }
}
.trace-live {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 24px;
  max-width: 100%;
  padding: 1px 4px;
  font: 500 12px/20px var(--font-body);
}
.trace-live-step {
  display: flex;
  min-width: 0;
  animation: trace-step-in .35s cubic-bezier(.22, 1, .36, 1);
}
.trace-shimmer {
  overflow: hidden;
  max-width: 100%;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: linear-gradient(
    90deg,
    var(--vp-muted) 0%,
    var(--vp-muted) 35%,
    var(--vp-ink) 50%,
    var(--vp-muted) 65%,
    var(--vp-muted) 100%
  );
  background-size: 200% 100%;
  background-clip: text;
  color: transparent;
  -webkit-background-clip: text;
  animation: trace-shimmer 2.4s ease-in-out infinite;
}
@keyframes trace-shimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}
@keyframes trace-step-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

.trace-done {
  overflow: hidden;
  width: 100%;
  margin: 0;
}
.trace-done > summary {
  display: flex;
  align-items: center;
  gap: 5px;
  width: fit-content;
  max-width: 100%;
  padding: 4px;
  border-radius: var(--r-xs);
  color: var(--vp-muted);
  cursor: pointer;
  list-style: none;
  font: 400 11px/16px var(--font-mono);
  transition: color .15s ease, background-color .15s ease;
}
.trace-done > summary::-webkit-details-marker { display: none; }
.trace-done > summary:hover {
  background: var(--vp-tint);
  color: var(--vp-primary-text);
}
.trace-done > summary:focus-visible {
  outline: 2px solid var(--vp-ai);
  outline-offset: 2px;
}
.trace-done > summary > .ms {
  flex: none;
  font-size: 13px;
}
.trace-done > summary > .ms:first-child {
  transition: transform .2s ease;
}
.trace-done[open] > summary > .ms:first-child { transform: rotate(90deg); }
.trace-done > summary > span:last-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.trace-timeline {
  position: relative;
  padding: 4px 10px 10px 10px;
}
.trace-timeline::before {
  content: "";
  position: absolute;
  top: 5px;
  bottom: 12px;
  left: 17px;
  width: 1px;
  background: var(--vp-line-soft);
}
.trace-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.trace-list > li {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
  font: 400 11px/1.4 var(--font-body);
}
.trace-node {
  position: relative;
  z-index: 1;
  display: grid;
  flex: none;
  place-items: center;
  width: 15px;
  height: 15px;
  margin-top: 1px;
}
.trace-node .trace-tick { background: var(--vp-surface-2); border-radius: var(--r-full); }
.trace-step-content { display: grid; min-width: 0; gap: 2px; }
.trace-step-label { color: var(--vp-ink); font-weight: 500; overflow-wrap: anywhere; }
.trace-detail { display: block; color: var(--vp-muted); overflow-wrap: anywhere; }

@media (prefers-reduced-motion: reduce) {
  .trace-tick-live circle,
  .trace-tick-live path { animation-delay: 0s; }
}

.dots { display: inline-flex; flex: none; }
.dots i {
  display: inline-block; width: 5px; height: 5px;
  border-radius: var(--r-full);
  background: var(--vp-ai);
  margin-right: 4px;
  animation: dot 1.1s infinite;
}
.dots i:nth-child(2) { animation-delay: .15s; }
.dots i:nth-child(3) { animation-delay: .3s; }
@keyframes dot { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }

/* Mã giao dịch trong câu trả lời — bấm được, sang thẳng chi tiết. */
.ma-gd {
  font-weight: 600;
  color: var(--vp-primary-text);
  text-decoration: none;
  border-bottom: 1px solid var(--vp-on-primary-soft);
}
.ma-gd:hover { border-bottom-color: var(--vp-primary); }

.md-table-wrap { overflow-x: auto; margin: var(--s-3) 0; }
.md-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.md-table th, .md-table td { padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--vp-line-soft); text-align: left; }
.md-table th { background: var(--vp-tint); font: 600 12px var(--font-display); }

/* ══════════════════════════════════════════════════════════════════
   TRỢ LÝ — bảng làm việc
   Vật thể trợ lý dựng từ dữ liệu nó vừa tra. Bốn loại: lịch, bảng, biểu
   đồ, tiến độ. Không loại nào được vẽ từ số không có nguồn.
   ══════════════════════════════════════════════════════════════════ */

/* Trang trợ lý đi TRÀN VIỀN, không nằm trong .wrap có lề như các trang khác:
   khu làm việc là mặt bàn, nó phải chiếm trọn khoảng giữa hai mép. */
/* Viết `.wrap.wrap-agent` chứ không `.wrap-agent`: hai media query bên dưới có
   khai lại `.wrap { padding: … }`, cùng độ đặc hiệu nhưng đứng sau nên chúng
   thắng. Trang trợ lý bị đẩy xuống 20px và tràn đúng chừng đó ở đáy. */
.wrap.wrap-agent { max-width: none; padding: 0; gap: 0; }
/* Khung đã cao đúng bằng phần còn lại của màn; thêm chân trang vào là sinh
   thanh cuộn cho một dòng chữ. Dòng đó đã có ở mọi trang khác. */
.wrap-agent ~ .foot { display: none; }

.agent {
  display: grid;
  /* Cột trợ lý rộng 380–560px. Bản vẽ đầu chốt 320px, nhưng tiếng Việt dài hơn
     tiếng Anh nên ở mức đó câu trả lời vỡ dòng liên tục và mấy nút gợi ý phải
     xuống hàng — đọc rất mệt.

     Trần tính bằng PHẦN TRĂM chứ không phải vw: phần trăm đo trên chỗ CÒN LẠI
     sau cột điều hướng, nên thu gọn cột thì khung trợ lý rộng ra theo. Dùng vw
     thì chỗ dôi ra chảy hết vào bảng làm việc, mà cột chật mới là cái cần chữa. */
  grid-template-columns: minmax(0, 1fr) clamp(380px, 36%, 560px);
  height: calc(100vh - var(--topbar-h));
  min-height: 0;
}

/* Cột trợ lý DÍNH SÁT mép phải, cao trọn khung, chỉ có một đường kẻ ngăn —
   không bo góc, không đổ bóng, không khe hở. Nó là một phần của khung ứng
   dụng chứ không phải một thẻ trôi nổi trong trang. */
.agent > .chat {
  height: 100%;
  min-height: 0;
  border: 0;
  border-left: 1px solid var(--vp-line);
  border-radius: 0;
  box-shadow: none;
}

/* Khu làm việc: MỘT MÀN, không cuộn. Ba bảng biểu phải nhìn được cùng lúc —
   đó là toàn bộ lý do có cái bảng này. Bắt cuộn để xem cái thứ ba thì chẳng
   khác gì quay lại lối cũ, mỗi lúc một bảng.
   Panel nào có nội dung dài hơn ô của nó thì CHÍNH NÓ cuộn, không kéo cả mặt
   bàn dài ra theo. */
.bang {
  display: flex; flex-direction: column; gap: var(--s-4);
  min-width: 0; min-height: 0;
  padding: var(--s-6) var(--s-7);
  overflow: hidden;
}
/* Lưới 2×2 CỐ ĐỊNH, cao đúng bằng chỗ còn lại của mặt bàn. Ba cỡ là ba cách
   chiếm ô trong lưới đó, nên cộng lại luôn vừa khít một màn:
     · toàn khung  → 2 cột × 2 hàng
     · nửa         → 2 cột × 1 hàng
     · một phần tư → 1 cột × 1 hàng
   1/2 + 1/4 + 1/4 = 1 màn. */
.bang-than {
  flex: 1; min-height: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(0, 1fr));
  gap: var(--s-5);
}
/* Bảng trống nở hết chỗ còn lại, không co thành một hộp nhỏ lọt thỏm giữa màn. */
.bang-rong { grid-area: 1 / 1 / 3 / 3; }
.bang-dau { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.bang-nhan {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font: 600 12px var(--font-display); color: var(--vp-ai);
}
.bang-nhan::before {
  content: "headset_mic";
  font-family: 'Material Symbols Outlined';
  font-variation-settings: 'FILL' 1;
  font-size: 16px;
}
.bang-chips { display: flex; gap: var(--s-3); flex-wrap: wrap; }
.bang-chip {
  padding: 5px 12px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-full);
  background: var(--vp-surface-2);
  color: var(--vp-muted);
  font: 500 12px var(--font-display);
  cursor: pointer;
}
.bang-chip:hover { border-color: var(--vp-ai); color: var(--vp-ai); }
.bang-chip.on { background: var(--vp-ai); border-color: var(--vp-ai); color: #fff; }
/* Sức chứa của bảng, hiện dưới dạng "2/3". Nói trước con số này thì lúc một
   panel bị đẩy ra người dùng hiểu ngay vì sao, không tưởng là mất dữ liệu. */
.bang-dem {
  margin-left: auto;
  font: 500 11.5px var(--font-mono);
  color: var(--vp-faint);
  white-space: nowrap;
}
.bang-dem.day { color: var(--vp-ai); }
/* Ba thứ chỉ tồn tại ở bản màn hẹp: hai nút quay lại và thanh nhòm.
   .bang-nhom PHẢI tắt ở đây chứ không chỉ dựa vào thuộc tính [hidden] mà
   app.js bật/tắt — nó là con thứ hai của lưới .agent, nên hễ có vật thể là nó
   hiện ra và chiếm mất ô cột phải, đẩy khung hội thoại xuống hàng dưới. */
.bang-lui, .bang-ve, .bang-nhom { display: none; }
.bang-rong {
  min-height: 300px;
  display: grid; place-items: center;
  padding: var(--s-8);
  border: 1px dashed var(--vp-line);
  border-radius: var(--r-2xl);
  background: var(--vp-tint);
  text-align: center;
}
.bang-rong-tieu { margin: var(--s-3) 0 0; font: 600 20px/28px var(--font-display); }
.bang-rong-mo { margin: var(--s-2) auto 0; max-width: 44ch; font: 400 14px/20px var(--font-body); color: var(--vp-muted); }

/* ── Khung một vật thể ────────────────────────────────────────── */
.vat-the {
  grid-column: span 2; grid-row: span 2;   /* mặc định: trọn mặt bàn */
  min-width: 0; min-height: 0;
  /* KHÔNG `overflow: hidden`: popover chi tiết ngày phải thoát ra ngoài panel.
     Trần cứng của panel do lưới 2×2 áp; phần vẽ bên trong có nhiệm vụ tự gọn
     lại cho vừa (bớt dòng, bớt cột) thay vì đẩy ra thanh cuộn. */
  overflow: visible;
  /* Ruột panel co theo KÍCH THƯỚC PANEL, không theo màn hình. Cùng một màn
     1440px, panel toàn khung rộng 1200px cao 690px còn panel một-phần-tư chỉ
     380×330 — media query không phân biệt được hai chỗ đó.

     `size` chứ không phải `inline-size`: lịch cần biết panel CAO bao nhiêu để
     quyết bỏ bớt gì. Dùng được vì ô trong lưới 2×2 có chiều cao xác định. Trên
     màn hẹp panel cao theo nội dung nên phải đổi lại inline-size — khai `size`
     ở đó là nội dung tự sập về 0. */
  container-type: size;
  container-name: panel;
  display: flex; flex-direction: column;
  background: var(--vp-surface-2);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-2xl);
  padding: var(--s-6) 22px 22px;
  box-shadow: var(--sh-2);
  animation: panel-vao .24s cubic-bezier(.2, .7, .3, 1) both;
}
/* Khai cả `co-full` cho đủ ba cỡ, dù nó trùng mặc định — một lớp có trong JS
   mà không có quy tắc nào là thứ người sửa sau tưởng đã chết rồi xoá đi. */
.vat-the.co-full { grid-column: span 2; grid-row: span 2; }
.vat-the.co-nua { grid-column: span 2; grid-row: span 1; }
.vat-the.co-tu { grid-column: span 1; grid-row: span 1; }
/* Panel đang mở popover phải nằm TRÊN các panel bên cạnh, nếu không popover
   chui xuống dưới panel kề. */
.vat-the:hover, .vat-the:focus-within { z-index: 5; }
/* Đã nằm trên bảng từ lượt trước thì đừng diễn lại hoạt cảnh vào — vẽ lại
   toàn bộ bảng mỗi lượt hỏi mà panel nào cũng nhấp nháy thì rất nhức mắt. */
.vat-the.da-ve { animation: none; }
.vat-the.dang-dong { animation: panel-ra .2s ease-in both; pointer-events: none; }

.vat-dau { display: flex; align-items: baseline; gap: var(--s-4); flex-wrap: wrap; }
.vat-tieu { margin: 0; font: 600 20px/26px var(--font-display); }
.vat-the.co-tu .vat-tieu, .vat-the.co-nua .vat-tieu { font-size: 17px; line-height: 23px; }
.vat-nguon { font: 400 12px var(--font-body); color: var(--vp-muted); white-space: nowrap; }
.vat-luc { font: 400 12px var(--font-body); color: var(--vp-muted); white-space: nowrap; }

/* Chỉ còn nút bỏ panel khỏi bảng. Cỡ panel do giao diện tự xếp theo số bảng
   biểu trợ lý dựng — bắt người dùng kéo từng ô cho vừa là đẩy việc của giao
   diện sang cho họ. Bỏ một panel thì cả bảng tự xếp lại ngay. */
.vat-nut { margin-left: auto; display: flex; align-items: center; gap: 2px; }
.vat-nut button {
  display: grid; place-items: center;
  width: 28px; height: 28px;
  border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--vp-faint);
  cursor: pointer;
}
.vat-nut button:hover { background: var(--vp-tint-2); color: var(--vp-ink); }
.vat-nut .ms { font-size: 17px; }
.vat-nut .vat-dong:hover { background: var(--tone-bad-bg); color: var(--tone-bad-fg); }
/* KHÔNG cuộn. Mỗi phần vẽ phải tự gọn cho vừa ô: lịch co hàng, bảng giao dịch
   cắt bớt dòng và NÓI RÕ còn bao nhiêu, biểu đồ tròn thì co theo ô.
   Cuộn ở đây là cách né việc gọn lại — và nó cắt luôn popover của lịch. */
.vat-than { margin-top: var(--s-5); flex: 1; min-width: 0; min-height: 0; overflow: visible; }
.vat-chan { margin: 14px 0 0; font: 400 12px/1.55 var(--font-body); color: var(--vp-muted); }
.vat-the.co-tu .vat-chan { display: none; }   /* cỡ nhỏ nhất: nhường chỗ cho số liệu */

.vat-rong { padding: var(--s-6); font: 400 13px var(--font-body); color: var(--vp-muted); }
.vat-ds { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.vat-ds li {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-4);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  background: var(--vp-surface-2);
}
/* Dòng giao dịch trong panel: hẹp thì tên nội dung cắt bớt bằng dấu ba chấm
   thay vì xuống bốn dòng làm vỡ ô. */
.vat-ds-gd li { padding: 0; }
.vat-ds-gd .vat-ds-tieu,
.vat-ds-gd .vat-ds-mo { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vat-ds-gd .gd-dong { padding: var(--s-3) var(--s-4); }
.vat-ds-gd li.foc { background: var(--vp-ai); border-color: var(--vp-ai); }
.vat-ds-gd li.foc .gd-dong,
.vat-ds-gd li.foc .vat-ds-mo { color: #fff; }
.vat-ds-gd li.foc .badge { background: rgba(255, 255, 255, .22); color: #fff; }

/* Vùng cuộn dọc cho các vật thể dạng DANH SÁCH (giao dịch, khiếu nại). Đặt ở
   đây chứ không ở .vat-than: lịch phải giữ overflow visible cho popover thoát
   ra, mà danh sách thì cần cuộn. Hai nhu cầu ngược nhau nên tách hẳn.
   Cắt bớt danh sách là giấu mất giao dịch; cuộn chỉ tốn một cú vuốt.
   Thanh cuộn ẩn — ẩn chứ không tắt, vẫn vuốt/lăn được như thường. */
.cuon-doc {
  height: 100%;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;   /* cuộn hết danh sách thì dừng, không kéo cả trang */
  scrollbar-width: none;
}
.cuon-doc::-webkit-scrollbar { width: 0; height: 0; }

@container panel (max-width: 460px) {
  .vat-ds-gd .gd-ico { width: 32px; height: 32px; }
  .vat-ds-gd .gd-ico .ms { font-size: 17px; }
  .vat-ds-gd .vat-ds-tieu { font-size: 13px; }
  .vat-ds-gd .gd-tien .num { font-size: 13.5px; }
}

.vat-ds-kn li { display: block; }
.kn-dau { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.kn-dau .vat-ds-tieu { font: 600 16px/22px var(--font-display); }
.vat-ds-kn .vat-ds-mo { margin-top: 4px; }
.vat-ds-kn .vat-tien-do { margin-top: var(--s-4); }
.vat-ds-kn li.foc { background: var(--vp-ai-soft); border-color: var(--vp-ai); }

.vat-ds-tieu { display: block; font: 500 14px var(--font-body); }
.vat-ds-mo { display: block; font: 400 12px var(--font-body); color: var(--vp-muted); }

/* Dòng giao dịch trong danh sách: icon trạng thái · nội dung · tiền + badge.
   Cả dòng là một <a> — vùng chạm bằng cả thẻ, không phải một chữ "Chi tiết". */
.vat-ds li:has(.gd-dong) { padding: 0; }
.gd-dong {
  display: flex; align-items: center; gap: var(--s-4);
  width: 100%; padding: var(--s-4);
  color: inherit; text-decoration: none;
  border-radius: var(--r-lg);
}
.gd-dong:hover { background: var(--vp-tint); color: inherit; }
.gd-ico {
  display: grid; place-items: center;
  width: 40px; height: 40px; flex: none;
  border-radius: var(--r-full);
}
.gd-noi { min-width: 0; flex: 1; }
.gd-tien { margin-left: auto; text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
.gd-tien .num { font: 600 15px var(--font-display); font-variant-numeric: tabular-nums; }
.gd-tien .badge { font-size: 11px; padding: 2px 9px; }

/* Ô đang được chỉ vào. Nhịp thở chứ không nhấp nháy — nhấp nháy là mẫu
   hình gây động kinh, và prefers-reduced-motion tắt hẳn ở cuối file. */
.foc { animation: nhip 1.9s ease-in-out infinite; }
@keyframes nhip {
  0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--vp-ai) 50%, transparent); }
  50% { box-shadow: 0 0 0 8px color-mix(in srgb, var(--vp-ai) 12%, transparent); }
}

/* ── Vật thể: LỊCH CHI TIÊU ─────────────────────────────────────
   Lịch phải VỪA KHÍT panel, không đẻ ra thanh cuộn. Chiều cao cả khối bị chặn,
   hàng đầu (T2…CN) cao theo nội dung, các hàng tuần chia đều phần còn lại
   (`grid-auto-rows: minmax(0,1fr)`) — nên tháng 4 hàng hay 6 hàng đều lấp kín
   đúng chừng ấy chỗ.

   Trước đây mỗi ô cố định 78px: tháng 6 hàng thành ~500px, tràn khỏi panel và
   người dùng phải cuộn trong lòng một bảng biểu — đọc lịch mà phải cuộn thì
   mất luôn cái lợi duy nhất của lịch là nhìn cả tháng một lượt. */
.lich {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  /* Hàng 1 (T2…CN) cao theo chữ; mọi hàng tuần sinh sau chia đều chỗ còn lại. */
  grid-template-rows: auto;
  grid-auto-rows: minmax(0, 1fr);
  gap: 5px;
  /* Cao đúng bằng ô panel cấp cho, không phải một con số cố định: cùng cái
     lịch, panel trọn mặt bàn cho nó ~700px còn panel nửa chỉ ~330px.
     KHÔNG đặt min-height: ô thấp mà có sàn là lịch lòi ra ngoài panel. */
  height: 100%;
}
.lich-thu {
  padding: 0 0 2px;
  text-align: center;
  font: 500 11px var(--font-display); color: var(--vp-muted);
}
.day {
  position: relative;
  min-height: 0;
  overflow: hidden;
  padding: 5px 7px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-sm);
  background: var(--vp-surface-2);
}
/* Ô ngày phải cho popover thoát ra ngoài, nếu không chi tiết bị cắt cụt. */
.day:hover, .day:focus-within { overflow: visible; }
.day-ngoai {
  background: repeating-linear-gradient(45deg,
    var(--vp-tint) 0, var(--vp-tint) 5px,
    var(--vp-surface-2) 5px, var(--vp-surface-2) 10px);
  border-style: dashed; opacity: .55;
}
.day-so { display: flex; align-items: baseline; justify-content: space-between; gap: 4px; }
.day-so b { font: 500 11.5px var(--font-display); opacity: .7; }
.day-so span { font: 500 9.5px var(--font-display); opacity: .75; white-space: nowrap; }
.day-tien {
  margin-top: 2px;
  font: 600 12px/1.25 var(--font-display); font-variant-numeric: tabular-nums;
}
/* Ngày có tiền chưa rời ví: đổi TÔNG cả ô thay vì viết thêm một dòng chữ. Ô
   chỉ cao ~54px vì cả tháng phải lọt trong panel; con số đầy đủ nằm ở popover.
   Màu không phải kênh thông tin duy nhất — popover luôn nói bằng chữ. */
.day-loi { border-color: var(--tone-bad-fg); background: var(--tone-bad-bg); }
.day-loi .day-tien { color: var(--tone-bad-fg); }
.day-giu { border-color: var(--tone-warn-fg); background: var(--tone-warn-bg); }
.day-giu .day-tien { color: var(--tone-warn-fg); }
.day-nhan .day-tien { color: var(--tone-ok-fg); }
/* Ô trợ lý đang chỉ vào thắng mọi tông khác — nó là câu trả lời cho câu hỏi. */
.day.foc { background: var(--vp-ai); border-color: var(--vp-ai); color: #fff; z-index: 2; }
.day.foc .day-tien, .day.foc .day-so b, .day.foc .day-so span { color: #fff; }
.day:hover { outline: 2px solid var(--vp-ai); outline-offset: -2px; z-index: 5; }

/* Chi tiết từng ngày: hiện khi rê chuột, VÀ khi ô được focus bằng bàn phím
   — chỉ hover là bỏ rơi người dùng bàn phím. */
.day-pop { display: none; }
.day:hover .day-pop, .day:focus-within .day-pop {
  display: block;
  position: absolute; top: calc(100% + 4px); left: -2px;
  z-index: 20; min-width: 252px;
  padding: var(--s-4) 14px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  background: var(--vp-surface); color: var(--vp-ink);
  box-shadow: var(--sh-pop);
  text-align: left;
}
.day-pop-tieu { font: 500 12px var(--font-display); color: var(--vp-muted); }
.day-pop-tong { margin-top: 3px; font: 600 20px/26px var(--font-display); font-variant-numeric: tabular-nums; }
.day-pop-ds {
  margin-top: 9px; padding-top: 9px;
  border-top: 1px solid var(--vp-line-soft);
  display: flex; flex-direction: column; gap: 5px;
  font: 400 12px/1.45 var(--font-body);
}
.day-pop-ghi { margin-top: 7px; font: 500 11.5px var(--font-body); color: var(--vp-ai); }

/* ── Vật thể: BẢNG GIAO DỊCH ──────────────────────────────────── */
.vat-bang { border: 1px solid var(--vp-line); border-radius: var(--r-lg); overflow: hidden; }
.vat-bang table { font-size: 13px; }
.vat-bang tr.foc td { background: var(--vp-ai); color: #fff; }
.vat-bang tr.foc td .badge { background: rgba(255, 255, 255, .22); color: #fff; }
.vat-bang tr.foc td a { color: #fff; }

/* ── Vật thể: BIỂU ĐỒ TRÒN CHI THEO LOẠI ────────────────────────
   Vành khuyên + chú giải. Ở ô rộng thì nằm cạnh nhau, ô hẹp thì xếp dọc. */
.tron-boc { position: relative; display: flex; align-items: center; gap: var(--s-6); height: 100%; min-height: 0; }
.tron { width: 150px; height: 150px; flex: none; overflow: visible; }
.tron-phan { transition: opacity .15s, stroke-width .15s; cursor: pointer; }
.tron-phan:focus { outline: none; }
/* Lát đang rê: dày lên và nhô ra khỏi vành. Các lát khác mờ đi — trên nền
   nhiều màu thì "làm mờ phần còn lại" nhận ra nhanh hơn "làm sáng phần này". */
.tron-phan.sang { stroke-width: 32; }
.tron-boc:has(.tron-phan.sang) .tron-phan:not(.sang) { opacity: .3; }
/* Cột bên phải hình: dòng tổng, rồi danh sách các phần cộng thành nó.
   `min-width: 0` là thứ giữ cho cả khối này co được — thiếu nó thì một tên
   kênh dài đẩy cột phình ra và hình tròn bị bóp méo. */
.tron-ben { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--s-4); }
/* Tổng tiền — số lớn nhất trong panel, và là số duy nhất ô chú thích nổi khi
   rê chuột KHÔNG nói được (nó chỉ nói về từng lát). Cho phép xuống dòng
   (`flex-wrap`) thay vì tràn: đây chính là chỗ bản trước đã hỏng. */
.tron-tom { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; }
.tron-tom b {
  font: 700 19px/25px var(--font-display); font-variant-numeric: tabular-nums;
}
.tron-tom em { font: 400 12px var(--font-body); font-style: normal; color: var(--vp-muted); }

.tron-chu { list-style: none; margin: 0; padding: 0; flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.tron-chu li {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr) max-content 34px;
  align-items: baseline; gap: var(--s-3);
  padding: 4px 6px;
  border-radius: var(--r-sm);
}
.tron-cham { width: 10px; height: 10px; border-radius: 3px; }
.tron-ten { font: 500 13px var(--font-body); min-width: 0; }
.tron-so { font: 600 13.5px var(--font-display); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tron-ti { text-align: right; font: 500 12px var(--font-mono); color: var(--vp-muted); }
/* Dòng phụ chiếm trọn hàng dưới của chính mục đó. */
.tron-treo {
  grid-column: 2 / -1;
  font: 400 11px var(--font-body); color: var(--tone-warn-fg);
}
/* Lát bánh trợ lý đang chỉ vào. */
.tron-chu li.foc { background: var(--vp-ai-soft); }
.tron-boc:has(.foc) .tron-phan:not(.foc) { opacity: .28; }
/* Dòng chú thích đang rê (hoặc đang rê lát tương ứng). */
.tron-chu li { cursor: default; transition: background .12s; }
.tron-chu li.sang { background: var(--vp-tint-2); }

/* Ô chú thích nổi khi rê vào lát bánh. Ghim trên đầu hình tròn, không bám con
   trỏ: bám con trỏ thì nó nhấp nháy mỗi lần chuột đi qua ranh giới hai lát. */
.tron-goi {
  position: absolute; left: 0; top: 0; z-index: 20;
  display: flex; flex-direction: column; gap: 1px;
  max-width: 240px;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-md);
  background: var(--vp-surface);
  box-shadow: var(--sh-pop);
  pointer-events: none;   /* đừng chặn chuột đang rê trên lát bánh */
}
.tron-goi b { font: 600 13px var(--font-display); }
.tron-goi span { font: 600 14px var(--font-display); font-variant-numeric: tabular-nums; color: var(--vp-primary-text); }
.tron-goi em { font: 400 11px var(--font-body); font-style: normal; color: var(--tone-warn-fg); }

@container panel (max-width: 520px) {
  .tron-boc { flex-direction: column; gap: var(--s-4); align-items: stretch; }
  .tron { width: 108px; height: 108px; align-self: center; }
  .tron-chu li { grid-template-columns: 10px minmax(0, 1fr) max-content; padding: 2px 4px; }
  .tron-ti { display: none; }
  .tron-ten { font-size: 12.5px; }
}
@container panel (max-height: 300px) {
  .tron { width: 96px; height: 96px; }
  .tron-treo { display: none; }
  /* Nhỏ lại chứ KHÔNG ẩn: đây là con số chính của cả biểu đồ, và khi panel
     thấp thì nó cũng là thứ duy nhất đọc được mà không phải rê chuột. */
  .tron-tom b { font-size: 16px; line-height: 22px; }
  .tron-ben { gap: var(--s-3); }
}

/* ══ Ruột panel theo bề rộng PANEL ═════════════════════════════════ */

/* Panel hẹp (nửa / một phần tư): thanh không còn đủ chỗ nằm giữa hai cột chữ
   nên xuống hẳn một dòng riêng — nhãn và số tiền chia nhau dòng trên. Thà cao
   thêm một nhịp còn hơn mất cái thanh, vì thanh mới là thứ để so sánh. */
@container panel (max-width: 560px) {

  /* Tiến độ khiếu nại: 4 bước cạnh nhau trong panel hẹp thì mỗi bước còn vài
     chục pixel. Xuống 2 cột, rồi 1 cột ở panel hẹp nhất. */
  .vat-tien-do { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kn-dau .vat-ds-tieu { font-size: 15px; }
  .vat-dau { gap: var(--s-3); }
  .vat-luc { display: none; }   /* giờ dựng là thông tin phụ, nhường chỗ cho tiêu đề */
}

/* Lịch trong panel hẹp: bảy cột trên ~340px thì mỗi ô còn ~44px — không cách
   nào nhét "2.630.000 ₫" vào đó mà không xuống ba dòng và tràn ô. Bỏ số tiền,
   để lại số ngày và một CHẤM báo có phát sinh; con số đầy đủ nằm ở popover khi
   chạm. Chấm đổi màu theo tông của ngày nên vẫn phân biệt được tạm giữ/thất
   bại mà không cần đọc chữ. */
@container panel (max-width: 560px) {
  .day-tien { display: none; }
  .day-so span { display: none; }
  .day:has(.day-tien)::after {
    content: "";
    position: absolute; left: 50%; bottom: 6px;
    transform: translateX(-50%);
    width: 5px; height: 5px; border-radius: var(--r-full);
    background: var(--vp-primary);
  }
  .day-giu:has(.day-tien)::after { background: var(--tone-warn-fg); }
  .day-loi:has(.day-tien)::after { background: var(--tone-bad-fg); }
  .day.foc:has(.day-tien)::after { background: #fff; }
}

@container panel (max-width: 380px) {
  .vat-tien-do { grid-template-columns: minmax(0, 1fr); }
  .vat-nguon { display: none; }
}

/* Panel THẤP (nửa hoặc một phần tư của mặt bàn ~330px): sáu hàng lịch chỉ còn
   ~30px mỗi hàng. Bỏ hết chữ phụ, giữ số ngày và một chấm — cả tháng vẫn nhìn
   được một lượt, chi tiết vẫn ở popover. Bỏ luôn dòng chú thích để lấy thêm
   một hàng cho lịch. */
@container panel (max-height: 430px) {
  .vat-the:has(.lich) .vat-chan { display: none; }
  .vat-the:has(.lich) .vat-than { margin-top: var(--s-4); }
  .lich { gap: 4px; }
  .day { padding: 3px 5px; }
  .day-tien { display: none; }
  .day-so span { display: none; }
  .day-so b { font-size: 11px; }
  .day:has(.day-tien)::after {
    content: "";
    position: absolute; left: 50%; bottom: 4px;
    transform: translateX(-50%);
    width: 4px; height: 4px; border-radius: var(--r-full);
    background: var(--vp-primary);
  }
  .day-giu:has(.day-tien)::after { background: var(--tone-warn-fg); }
  .day-loi:has(.day-tien)::after { background: var(--tone-bad-fg); }
  .day.foc:has(.day-tien)::after { background: #fff; }
}

/* ── Vật thể: TIẾN ĐỘ KHIẾU NẠI ───────────────────────────────── */
.vat-tien-do { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-4); }
.vat-tien-do .buoc {
  padding: 14px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
  background: var(--vp-surface-2);
}
.vat-tien-do .buoc.todo { background: var(--vp-tint); opacity: .7; }
.vat-tien-do .buoc.foc { background: var(--vp-ai); border-color: var(--vp-ai); color: #fff; z-index: 1; position: relative; }
.vat-tien-do .ico {
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: var(--r-full);
  background: var(--tone-neutral-bg); color: var(--vp-faint);
}
.vat-tien-do .buoc.done .ico { background: var(--tone-ok-bg); color: var(--tone-ok-fg); }
.vat-tien-do .buoc.now .ico { background: var(--vp-ai-soft); color: var(--vp-ai); }
.vat-tien-do .buoc.foc .ico { background: rgba(255, 255, 255, .22); color: #fff; }
.vat-tien-do .st { display: block; margin-top: 9px; font: 600 15px var(--font-display); }
.vat-tien-do .mo-ta { display: block; margin-top: 5px; font: 400 12px/1.45 var(--font-body); opacity: .75; }
.vat-tien-do .xuong { display: block; margin-top: var(--s-3); font: 400 11.5px var(--font-body); opacity: .6; }

/* Nút "↖ Xem trên bảng" trong bong bóng trả lời */
.nut-bang {
  display: flex; gap: var(--s-3); align-items: flex-start;
  margin-top: var(--s-3);
  padding: 9px 11px;
  border: 1px solid var(--vp-line);
  border-radius: var(--r-md);
  background: var(--vp-tint);
  color: var(--vp-primary-text);
  font: 400 12px/1.45 var(--font-body);
  text-align: left;
  cursor: pointer;
}
.nut-bang:hover { border-color: var(--vp-primary); background: var(--vp-tint-2); }
.nut-bang::before {
  content: "my_location";
  font-family: 'Material Symbols Outlined';
  font-size: 15px; flex: none;
}

.bang-dang-ve { display: flex; flex-direction: column; gap: var(--s-3); }
.bang-dang-ve-dau { font: 600 12px var(--font-display); color: var(--vp-ai); }

.xem-them {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font: 500 12.5px var(--font-display);
  color: var(--vp-primary-text); text-decoration: none;
}

/* ══════════════════════════════════════════════════════════════════
   MÀN HẸP
   ══════════════════════════════════════════════════════════════════ */

/* ── Dưới 900px: hội thoại chiếm cả khung, bảng thành tấm gập ────
   KHÔNG xếp chồng hai cột. Xếp chồng cho ra một biến thể thứ ba không có
   trong bản vẽ, và nó đọc như hai khung rời nhau: cuộn qua hết hội thoại mới
   thấy bảng thì mất hẳn liên hệ "trợ lý đang chỉ vào ô kia".

   Hai đầu của bản vẽ là: máy tính = bảng giữa + trợ lý phải; điện thoại =
   trợ lý toàn màn + bảng trượt lên. Chỗ ở giữa đi theo đầu điện thoại. */
@media (max-width: 899px) {
  .agent { grid-template-columns: minmax(0, 1fr); }
  .agent > .chat { border-left: 0; }

  .bang {
    position: fixed; inset: 0; z-index: 70;
    display: none;
    padding: var(--s-5) var(--s-5) calc(var(--s-5) + env(safe-area-inset-bottom));
    background: var(--vp-bg);
    overflow-y: auto;
  }
  body.xem-bang { overflow: hidden; }
  body.xem-bang .bang { display: flex; animation: tam-len .26s cubic-bezier(.2, .7, .3, 1) both; }
  body.xem-bang .bang-nhom { display: none; }
  /* Đóng: app.js GIỮ NGUYÊN `xem-bang` (để trang nền vẫn khoá cuộn), chỉ thêm
     `dang-dong`, hết hoạt cảnh mới bỏ cả hai. Ba lớp để thắng quy tắc mở ở
     trên — hai lớp là hoà, và hoà thì quy tắc đứng sau (mở) thắng. */
  body.xem-bang .bang.dang-dong { animation: tam-xuong .22s ease-in both; }

  /* Tấm gập chỉ rộng bằng điện thoại: mọi panel về một cột.
     Phải kể ĐỦ cả `co-full` — `.vat-the.co-full` có hai lớp nên thắng
     `.vat-the` một lớp, và `grid-column: span 2` trên lưới một cột lại đẻ ra
     một cột ngầm thứ hai, làm các panel rộng hẹp lung tung. */
  /* Màn hẹp bỏ hẳn mô hình 2×2: một cột, mỗi panel một hàng cao theo nội dung,
     và tấm gập thì CUỘN. Nhét ba bảng biểu vào một màn điện thoại thì mỗi cái
     còn 200px — không đọc được gì. */
  .bang { overflow-y: auto; }
  .bang-than {
    /* `flex: none` là chỗ dễ quên nhất: để nguyên `flex: 1` thì thân bảng bị
       ép đúng chiều cao màn, ba hàng `auto` chia nhau còn ~130px mỗi cái và
       nội dung tràn đè lên nhau. Ở đây thân phải CAO THEO NỘI DUNG rồi để
       tấm gập cuộn. */
    flex: none;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    grid-auto-rows: auto;
    align-content: start;
  }
  .bang-rong { grid-area: auto; }
  .vat-the,
  .vat-the.co-full,
  .vat-the.co-nua,
  .vat-the.co-tu { grid-column: span 1; grid-row: span 1; overflow: visible; }
  .vat-than { overflow: visible; }
  /* Tấm gập đã cuộn cả trang rồi. Lồng thêm một vùng cuộn nữa bên trong là
     ngón tay vuốt không biết cái nào sẽ chạy. */
  .cuon-doc { height: auto; overflow: visible; }
  .lich { height: min(46vh, 320px); }

  /* HAI đường quay lại: nút ở đầu và nút lớn ở đáy. Bảng chiếm trọn màn hình
     — một lối thoát duy nhất ở trên cùng là quá dễ mắc kẹt. */
  .bang-lui {
    display: grid; place-items: center;
    width: 40px; height: 40px; flex: none;
    border: 1px solid var(--vp-line); border-radius: var(--r-full);
    background: var(--vp-surface-2); color: var(--vp-ink);
    cursor: pointer;
  }
  .bang-ve { display: inline-flex; width: 100%; margin-top: var(--s-4); }

  .bang-nhom {
    position: fixed; left: var(--s-5); right: var(--s-5);
    bottom: calc(var(--s-5) + env(safe-area-inset-bottom));
    z-index: 59;
    display: flex; align-items: center; gap: var(--s-3);
    min-height: 44px;
    padding: var(--s-3) var(--s-5);
    border: 1px solid var(--vp-line); border-radius: var(--r-full);
    background: var(--vp-surface); color: var(--vp-ink);
    box-shadow: var(--sh-pop);
    font: 500 13px var(--font-display);
    cursor: pointer;
  }
  .bang-nhom-nhan { color: var(--vp-ai); }
  .bang-nhom-mo { color: var(--vp-muted); font-weight: 400; font-size: 12px; }
  .bang-nhom > .ms { margin-left: auto; }
}

@media (min-width: 768px) and (max-width: 1024px) {
  /* Cùng cơ chế với `.shell-gon` — chỉ khác là do BỀ NGANG MÀN quyết, không do
     người bấm. Lệch cách làm giữa hai đường là hai dáng cột khác nhau ở cùng
     một bề rộng. */
  :root { --side-w: 84px; }
  .side-dau { padding: 0 var(--s-3) var(--s-6); }
  .side-dau .brand { padding: 0; }
  .nav-chu { max-width: 0; opacity: 0; margin-left: calc(var(--s-3) * -1); }
  .brand > span:not(.brand-mark) { max-width: 0; opacity: 0; overflow: hidden; white-space: nowrap; }
  .nav { padding: 0 var(--s-3); }
  .nav a { padding: var(--s-3) 0; }
  .nav a.on { border-right: 0; }
  .side-ghi { display: none; }
  .wrap { padding: var(--s-6) var(--s-6) var(--s-8); }
  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .split { grid-template-columns: minmax(0, 1fr); }
  .steps-ngang, .vat-tien-do { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── Điện thoại ────────────────────────────────────────────────── */
@media (max-width: 767px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  .side { display: none; }
  .main-cot { grid-column: 1; }

  .topbar { padding: 0 var(--s-5); gap: var(--s-3); }
  .tim-nhanh { display: none; }
  .topbar .who span:not(.ava) { display: none; }
  .topbar-end form .btn { padding: 0 var(--s-4); }

  /* Chỗ cho thanh tab + vùng an toàn (tai thỏ / thanh gạt) */
  .wrap { padding: var(--s-5) var(--s-5) calc(96px + env(safe-area-inset-bottom)); gap: var(--s-5); }
  .foot { display: none; }

  h1 { font-size: 24px; line-height: 30px; }
  .lede { font-size: 14px; line-height: 20px; }
  .card { padding: var(--s-5); border-radius: var(--r-xl); }
  .grid-2, .grid-3 { grid-template-columns: minmax(0, 1fr); }
  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Tấm hạng xếp dọc: dải bậc thang cần cả bề ngang mới đọc được tên hạng, nên
     dưới 900px nó xuống hàng riêng thay vì bị bóp còn nửa khung. */
  .hang-banner { grid-template-columns: minmax(0, 1fr); padding: var(--s-6); }
  .hang-ten { font-size: 22px; line-height: 28px; }
  .hang-lon-v { font-size: 32px; line-height: 38px; }
  /* Hẹp thì chừa ít hơn — 88% vẫn đủ hé mép tấm kế, mà tấm hiện tại không bị
     bóp tới mức tiêu đề vỡ thành ba dòng. */
  .km-tam { flex-basis: 88%; }

  /* Một cột và bỏ tranh: bóp tranh xuống 80px thì nó thành một vệt mực, mà chỗ
     đó trên màn 375px đang phải dành cho tiêu đề. */
  .mq-trang { grid-template-columns: minmax(0, 1fr); min-height: 200px; padding: var(--s-6); gap: var(--s-3); }
  .mq-tranh { display: none; }
  /* Tấm hẹp lại nhưng hình chìm thì không — thu theo để nó không trùm nửa tấm. */
  .km-hinh { width: 110px; height: 95px; }
  .mq-tieu { font-size: 22px; line-height: 28px; }
  .mq-mo { font-size: 13.5px; }
  /* Đẩy nút sang phải trên màn 375px là nút bị ép còn một nửa và chữ vỡ đôi. */
  .mq-chan { gap: var(--s-4); }
  .mq-nut { margin-left: 0; }
  /* Hộp quyền lợi rộng gần bằng cả cột mốc trên màn hẹp; bó theo bề ngang màn
     hình để nó không thò ra khỏi tấm và đẩy cả trang cuộn ngang. */
  .hang-hop { max-width: min(250px, calc(100vw - 72px)); }
  .split { grid-template-columns: minmax(0, 1fr); }
  .steps-ngang, .vat-tien-do { grid-template-columns: minmax(0, 1fr); }
  .balance .amount { font-size: 32px; line-height: 40px; }

  .kv { grid-template-columns: minmax(0, 1fr); gap: 2px var(--s-4); }
  .kv dd { margin-bottom: var(--s-3); }

  /*  Điện thoại: toast lên ĐỈNH màn, không ở đáy.

      Đáy màn hẹp đã có ba thứ chồng nhau — thanh tab, nút trợ lý nổi, và vùng
      vuốt về của hệ điều hành. Nhét thêm toast vào đó là toast hoặc đè lên nút,
      hoặc bị đẩy lên cao tới mức che mất nửa nội dung. Đỉnh màn đang trống, và
      đó cũng là chỗ ứng dụng điện thoại thường đặt thông báo. */
  /* Điện thoại: cùng góc, chỉ trải rộng hết bề ngang vì màn hẹp. */
  .toast-viewport { right: var(--s-4); left: var(--s-4); width: auto; }

  /* Trên điện thoại thứ che dòng chân trang là THANH TAB, không phải nút nổi —
     nên chừa theo chiều cao thanh tab chứ không theo chiều cao nút. */
  .foot { padding-bottom: calc(84px + env(safe-area-inset-bottom)); }

  /* Bảng rộng cuộn ngang trong khung của nó, không đẩy cả trang lệch. */
  .table-wrap { overflow-x: auto; }
  .table-wrap table { min-width: 560px; }
  /* Điện thoại: bảy cột trên ~330px thì mỗi ô chỉ còn ~45px. Chỉ còn chỗ cho
     số ngày và số tiền viết tắt; chi tiết vẫn ở popover khi chạm. */
  .lich { gap: 3px; height: min(46vh, 320px); }
  .day { padding: 4px; }
  .day-so b { font-size: 10.5px; }
  .day-so span { display: none; }
  .day-tien { font-size: 10px; }

  /* ── Thanh tab dưới + nút trợ lý nổi ────────────────────────── */
  .tabbar {
    position: fixed; left: 0; right: 0; bottom: 0;
    z-index: 60;
    /* Số cột PHẢI khớp số <a> dựng trong base.html. Lệch một tab là các tab co
       lại lệch nhau hoặc tràn xuống hàng hai, đè lên nút trợ lý nổi. */
    display: grid; grid-template-columns: repeat(4, 1fr);
    padding: var(--s-2) var(--s-3) calc(var(--s-2) + env(safe-area-inset-bottom));
    background: var(--vp-surface);
    border-top: 1px solid var(--vp-line);
  }
  .tabbar a {
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    min-height: 44px;
    padding: var(--s-2) 0;
    color: var(--vp-muted); text-decoration: none;
    font: 500 10.5px var(--font-display);
  }
  .tabbar a.on { color: var(--vp-primary-text); }
  .tabbar a.on .ms { font-variation-settings: 'FILL' 1; }

  /*  Điện thoại: bỏ chữ, thu về vòng tròn, và nâng lên khỏi thanh tab dưới.
      Giữ nguyên viên thuốc có chữ ở đây thì nó chiếm gần nửa bề ngang màn và
      đè lên chính nội dung mà khách đang định hỏi về. */
  .nut-ai {
    right: var(--s-5);
    bottom: calc(76px + env(safe-area-inset-bottom));
    display: grid; place-items: center;
    width: 56px; height: 56px;
    padding: 0; gap: 0;
  }
  .nut-ai-chu { display: none; }
  .nut-ai-ico { width: auto; height: auto; background: none; }
  .nut-ai .ms { font-size: 26px; }

  /* Thanh nhòm và tấm gập (khai ở breakpoint 899px) phải nhường chỗ cho
     thanh tab dưới và nút trợ lý nổi. */
  .bang-nhom { bottom: calc(76px + env(safe-area-inset-bottom)); }
  .bang { padding-bottom: calc(96px + env(safe-area-inset-bottom)); }
  /* Khung hội thoại lùi lên đúng chiều cao thanh tab, nếu không ô nhập nằm
     dưới thanh tab và không bấm được. */
  .agent { height: calc(100vh - var(--topbar-h) - 68px - env(safe-area-inset-bottom)); }
}

/* Ưu tiên hệ thống: tắt mọi chuyển động trang trí. Nhịp thở của ô "đang chỉ
   vào" cũng tắt — thông tin đó đã có ở màu nền và ở băng chữ, không phụ
   thuộc chuyển động. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* Tiêu đề cột bấm được để sắp xếp. Mũi tên chỉ hiện ở cột đang sắp — hiện ở mọi
   cột thì mắt không bắt được cột nào đang có hiệu lực. */
.sap-xep { color: inherit; display: inline-flex; align-items: center; gap: 3px; }
.sap-xep:hover { color: var(--brand); text-decoration: none; }

/* ══ Chuyển động & hiệu ứng ═════════════════════════════
   Một lớp phủ đặt CUỐI file để không phải sửa rải rác phía trên. Nguyên tắc:
   nhanh (120–200ms), một kiểu easing, chỉ động vào thứ người dùng vừa chạm —
   để giao diện có phản hồi mà không thành sân khấu. Mọi transform/entrance đều
   được gỡ ở khối prefers-reduced-motion cuối section, giữ đúng cam kết trợ năng
   của cả file. */

/* ── Micro-transition: mọi thứ bấm/hover mượt vào-ra ──── */
a, .btn, .nav a, .tip, .demo-chip, .chuong, .theme-toggle, .modal-dong,
.gate, tr[data-href], .badge, .trace > summary, .sap-xep, .ma-gd, .open-link,
.chuong-ds a, .toast-action, .toast-close, .steps li,
input[type=text], input[type=password], input[type=number], input[type=date],
select, textarea {
  transition: background-color .16s ease, border-color .16s ease, color .16s ease,
              box-shadow .16s ease, transform .12s ease, opacity .16s ease;
}

/* ── Hover nhấc nhẹ + nhấn xuống (chỉ thứ bấm được) ───── */
.btn:hover { transform: translateY(-1px); box-shadow: 0 3px 10px rgba(13, 116, 206, .18); }
.btn:active { transform: translateY(0); box-shadow: none; }
.btn-ghost:hover { box-shadow: none; }
.gate:hover { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(0, 0, 0, .08); }
.tip:hover, .demo-chip:hover { transform: translateY(-1px); }
.ma-gd:active, .tip:active, .demo-chip:active { transform: translateY(0); }

/* ── Card hiện dần khi tải trang (server render nên chạy mỗi lần mở) ── */
@keyframes ui-vao { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
.card { animation: ui-vao .24s ease both; }
/* Lưới nhiều thẻ: lệch pha một nhịp cho mắt lướt theo, tối đa 5 để không thành chờ đợi. */
.grid > :nth-child(2), .gates > :nth-child(2) { animation-delay: .04s; }
.grid > :nth-child(3), .gates > :nth-child(3) { animation-delay: .08s; }
.grid > :nth-child(4), .gates > :nth-child(4) { animation-delay: .12s; }
.grid > :nth-child(5), .gates > :nth-child(5) { animation-delay: .16s; }

/* ── Popup <dialog>: mở/đóng scale + mờ, kèm backdrop ───
   Dùng @starting-style (trạng thái đầu khi vừa mở) + transition-behavior:
   allow-discrete trên display/overlay để native <dialog> animate được CẢ chiều
   đóng — không cần đụng JS showModal()/close() đang có. */
.modal {
  opacity: 0; transform: translateY(10px) scale(.97);
  transition: opacity .18s ease, transform .18s ease,
              overlay .18s ease allow-discrete, display .18s ease allow-discrete;
}
.modal[open] { opacity: 1; transform: none; }
@starting-style { .modal[open] { opacity: 0; transform: translateY(10px) scale(.97); } }

.modal::backdrop {
  background: rgba(0, 0, 0, .45); opacity: 0;
  transition: opacity .18s ease, overlay .18s ease allow-discrete, display .18s ease allow-discrete;
}
.modal[open]::backdrop { opacity: 1; }
@starting-style { .modal[open]::backdrop { opacity: 0; } }

/* ── Dropdown chuông: bật ra pop-in ───────────────────
   JS bật/tắt bằng thuộc tính [hidden] (display:none). CSS animate được chiều
   HIỆN (khi bỏ hidden trình duyệt chạy animation), chiều ẩn vẫn tức thì — đủ
   để cảm giác "bật ra" mà không phải sửa JS. */
.chuong-bang:not([hidden]) { animation: pop-xuong .16s ease both; transform-origin: top right; }
@keyframes pop-xuong { from { opacity: 0; transform: translateY(-6px) scale(.97); } to { opacity: 1; transform: none; } }

/* Con số trên chuông nảy nhẹ mỗi khi xuất hiện — bắt mắt đúng lúc có cái mới. */
.chuong-so:not([hidden]) { animation: pop-so .22s ease both; }
@keyframes pop-so { 0% { transform: scale(.4); opacity: 0; } 60% { transform: scale(1.15); } 100% { transform: scale(1); opacity: 1; } }

/* ── Đổi nền sáng/tối: các mặt lớn chuyển màu mượt ──────
   Transition KHÔNG chạy ở lần vẽ đầu (data-theme đã gắn trong <head> trước khi
   trang paint), nên chỉ thấy hiệu ứng lúc bấm nút — không tái sinh cú chớp nền
   mà phần <head> đã cố công tránh. */
body, .topbar, .card, .modal, .chuong-bang, .toast, .md-table th, th, td,
input, select, textarea, .nav a, .badge {
  transition: background-color .22s ease, border-color .22s ease, color .22s ease;
}

/* ── Trợ năng: ai chọn "giảm chuyển động" thì bỏ transform + entrance,
   vẫn giữ chuyển màu (không phải "chuyển động") — đồng bộ với các khối
   reduced-motion đã có phía trên. */
@media (prefers-reduced-motion: reduce) {
  .card { animation: none; }
  .btn:hover, .gate:hover, .tip:hover, .demo-chip:hover { transform: none; }
  .btn:hover { box-shadow: none; }
  .chuong-bang:not([hidden]), .chuong-so:not([hidden]) { animation: none; }
  .modal, .modal[open] { transform: none; }
  .modal, .modal::backdrop { transition: opacity .14s ease; }
  @starting-style { .modal[open], .modal[open]::backdrop { opacity: 0; } }
}

@media print {
  .side, .topbar, .tabbar, .nut-ai, .foot, .toast-viewport, .chat-composer, .chat-tips { display: none; }
  body { background: #fff; }
  .card, .vat-the { box-shadow: none; break-inside: avoid; }
}

/* ── Trang xem nguồn trợ lý đã dẫn ─────────────────────────────
   Mở từ link trong khối "Nguồn" của một câu trả lời. Bày như một trang giấy:
   chữ to, dòng thưa, bề ngang hẹp — người vào đây để ĐỌC một đoạn văn bản, chứ
   không phải để thao tác. */
.nguon-dau {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s-5); flex-wrap: wrap; margin-bottom: var(--s-6);
}
.nguon-dau h1 { margin: var(--s-1) 0 0; }

.nguon-mach {
  display: flex; align-items: center; gap: var(--s-3);
  margin: 0 0 var(--s-5);
  padding-bottom: var(--s-5);
  border-bottom: 1px solid var(--vp-line-soft);
  color: var(--vp-muted); font: 400 13px var(--font-body);
}
.nguon-mach .ms { font-size: 19px; color: var(--vp-ai); }

/* `pre-wrap` giữ nguyên xuống dòng của tài liệu gốc — markdown/plain text nào
   cũng đọc được mà không cần diễn giải lại. Không dựng markdown ở đây: mốc
   `start`/`end` platform gửi đếm theo KÝ TỰ của văn bản gốc, dựng lại thành thẻ
   HTML là lệch hết vị trí, tô nhầm đoạn. */
.nguon-chu {
  white-space: pre-wrap; word-break: break-word;
  font: 400 15px/1.75 var(--font-body); color: var(--vp-ink);
  max-width: 72ch;
}
.nguon-chu mark {
  background: var(--vp-ai-soft); color: inherit;
  padding: 2px 1px; border-radius: 3px;
  box-shadow: 0 0 0 3px var(--vp-ai-soft);
}

/* ── Khối "Nguồn" trong bong bóng trợ lý ───────────────────────
   Thu gọn mặc định vì [1], [2] trong câu đã mở thẳng đúng nguồn. Khi bung ra,
   danh sách dàn theo cột gọn và không lặp tên file hay chữ "Mở". */
/* Số hiệu nguồn ngay trong câu trả lời — bấm được, mở đúng đoạn được trích.
   Nhỏ hơn chữ thân một nấc và nhấc lên nửa dòng để nó đọc như chú thích chứ không
   cắt ngang câu. `vertical-align` chứ không phải <sup>: dùng thẻ sup là trình duyệt
   tự thu nhỏ thêm một lần nữa, xuống 9px thì bấm trên điện thoại không trúng. */
.chat-trich {
  font-size: 11px; line-height: 1;
  vertical-align: 2px;
  padding: 0 1px;
  color: var(--vp-ai); font-weight: 600;
  text-decoration: none;
}
.chat-trich:hover { text-decoration: underline; text-underline-offset: 2px; }
.chat-trich:focus-visible { outline: 2px solid var(--vp-ai); outline-offset: 2px; border-radius: 2px; }

.chat-nguon {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid rgba(0, 0, 0, 0.08);
}
.chat-nguon-header {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  width: fit-content;
  list-style: none;
  cursor: pointer;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--vp-muted, #64748b);
  user-select: none;
}
.chat-nguon-header::-webkit-details-marker { display: none; }
.chat-nguon-header::after {
  content: "›";
  margin-left: 2px;
  font: 600 17px/1 var(--font-display);
  color: var(--vp-primary-text);
  transform: rotate(0deg);
  transition: transform .16s ease;
}
.chat-nguon[open] .chat-nguon-header::after { transform: rotate(90deg); }
.chat-nguon-header:hover { color: var(--vp-ink); }
.chat-nguon-header:focus-visible { outline: 2px solid var(--vp-ai); outline-offset: 3px; border-radius: 4px; }
.chat-nguon-icon {
  color: var(--vp-primary, #2563eb);
}
.chat-nguon-ds {
  margin: 5px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: 4px 8px;
}
.chat-nguon-ds li {
  margin: 0;
  padding: 0;
}
.chat-nguon-card {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 34px;
  padding: 5px 7px;
  border-radius: 6px;
  text-decoration: none;
  box-sizing: border-box;
  text-align: left;
  transition: all 0.15s ease-in-out;
}
.chat-nguon-card-link {
  background: transparent;
  border: 1px solid transparent;
  color: #1d4ed8;
}
.chat-nguon-card-link:hover {
  background: var(--vp-ai-soft);
  border-color: var(--vp-line-soft);
  color: #1e40af;
}
.chat-nguon-card-lock {
  background: transparent;
  border: 1px solid transparent;
  color: #64748b;
}
.chat-nguon-so {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  border-radius: 50%;
  background: #ffffff;
  border: 1px solid rgba(0, 0, 0, 0.15);
  font-family: var(--font-mono, monospace);
  font-size: 9.5px;
  font-weight: 700;
  color: inherit;
}
.chat-nguon-info {
  min-width: 0;
  flex: 1 1 0%;
  display: flex;
  flex-direction: column;
}
.chat-nguon-title {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--vp-ink, #0f172a);
}
.chat-nguon-action {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  color: #2563eb;
}
.chat-nguon-action svg {
  transition: transform 0.15s ease-in-out;
}
.chat-nguon-card-link:hover .chat-nguon-action svg {
  transform: translate(1px, -1px);
}
.chat-nguon-action-lock {
  color: #64748b;
}

/* ══════════════════════════════════════════════════════════════════
   KHU DỊCH VỤ TIỆN ÍCH — lưới dịch vụ, kệ hàng, đơn hàng, kho ưu đãi
   ══════════════════════════════════════════════════════════════════

   MÀU CỦA TỪNG DỊCH VỤ đi vào qua biến `--dv-mau` đặt trên chính thẻ (xem
   `services.html`), không phải qua mười lăm lớp `.dv-o-dien`, `.dv-o-nuoc`…
   Danh mục dịch vụ nằm trong Python và còn dài ra; một bảng màu song song trong
   CSS sẽ lệch khỏi nó ngay ở dịch vụ thứ mười sáu, và lệch một cách im lặng.

   Màu đó CHỈ tô ô icon, không tô nền thẻ và không tô chữ. Mười lăm màu đặc cạnh
   nhau thành một trang loè loẹt không đọc được, và trên nền tối thì phần lớn
   trong số đó tương phản kém. `color-mix` pha xuống 12–16% giữ được dấu hiệu
   nhận biết mà không phá bảng màu chung. */

.dv-loi-di {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  margin: var(--s-5) 0 var(--s-7);
}
.dv-loi-di-o {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-4) var(--s-5);
  background: var(--vp-surface); border: 1px solid var(--vp-line-soft);
  border-radius: var(--r-lg); text-decoration: none; color: inherit;
  transition: border-color .15s, transform .15s;
}
.dv-loi-di-o:hover { border-color: var(--vp-primary); transform: translateY(-1px); }
.dv-loi-di-o > .ms { font-size: 22px; color: var(--vp-primary-text); }
.dv-loi-di-o b { display: block; font: 600 14px/20px var(--font-display); }
.dv-loi-di-o em { display: block; font-style: normal; font-size: 12.5px; color: var(--vp-muted); }
.dv-cham {
  margin-left: auto;
  min-width: 22px; padding: 0 6px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-full);
  background: var(--vp-primary); color: var(--vp-on-primary);
  font: 600 12px/1 var(--font-display); font-variant-numeric: tabular-nums;
}

.dv-nhom { margin-top: var(--s-7); }
.dv-nhom h2 { font: 600 15px/22px var(--font-display); color: var(--vp-muted); margin: 0 0 var(--s-4); }
.dv-luoi {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.dv-o {
  display: flex; align-items: flex-start; gap: var(--s-4);
  padding: var(--s-4) var(--s-5);
  background: var(--vp-surface); border: 1px solid var(--vp-line-soft);
  border-radius: var(--r-lg); text-decoration: none; color: inherit;
  transition: border-color .15s, transform .15s, box-shadow .15s;
}
.dv-o:hover {
  border-color: color-mix(in srgb, var(--dv-mau) 55%, var(--vp-line));
  transform: translateY(-2px);
  box-shadow: 0 4px 14px -8px rgba(0, 0, 0, .35);
}
.dv-o-ico {
  flex: none;
  width: 40px; height: 40px; border-radius: var(--r-md);
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--dv-mau) 14%, transparent);
  color: var(--dv-mau);
}
.dv-o-ico .ms { font-size: 22px; }
.dv-o-chu { min-width: 0; }
.dv-o-chu b { display: block; font: 600 14px/20px var(--font-display); }
.dv-o-chu em {
  display: block; font-style: normal; margin-top: 2px;
  font-size: 12.5px; line-height: 17px; color: var(--vp-muted);
}

/* ── Lưới rút gọn trong trang Tổng quan ──────────────────────────────
   Mỗi nhóm bày bốn ô, phần dư ẩn sau nút "Xem đầy đủ" bên cạnh tên nhóm.

   ẨN BẰNG `display: none`, KHÔNG BẰNG chiều cao 0 hay `visibility`. Ô dịch vụ
   là thẻ <a>: giấu bằng hai cách kia thì nó vẫn nhận được tiêu điểm khi bấm
   Tab, và người dùng bàn phím sẽ lạc vào một hàng ô vô hình.

   Nút mở nằm ở CHÍNH hàng tiêu đề nhóm chứ không ở cuối lưới: cuối lưới thì vị
   trí của nó nhảy theo số ô của từng nhóm, và mắt phải đi tìm lại sau mỗi lần
   mở. Ở hàng tiêu đề thì ba nút của ba nhóm thẳng một cột. */
.dv-khu { margin-top: var(--s-8); }
.dv-nhom-dau {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); margin-bottom: var(--s-4);
}
.dv-nhom-dau h3 {
  margin: 0; font: 600 15px/22px var(--font-display); color: var(--vp-muted);
}
.dv-mo-nut {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 10px 5px 12px;
  border: 1px solid var(--vp-line); border-radius: var(--r-full);
  background: var(--vp-surface); color: var(--vp-muted);
  font: 500 12.5px var(--font-display); cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}
.dv-mo-nut:hover { border-color: var(--vp-primary); color: var(--vp-primary-text); background: var(--vp-tint); }
.dv-mo-nut .ms { font-size: 18px; transition: transform .18s ease; }
/* Chữ trên nút không được xuống dòng: "Xem đầy đủ" gãy làm đôi thì nút cao gấp
   rưỡi và kéo lệch cả hàng tiêu đề nhóm. */
.dv-mo-chu { white-space: nowrap; }
/* Con số "+2" là lời hứa cụ thể của cái nút: nó nói mở ra sẽ được thêm mấy ô,
   nên không ai phải bấm thử để biết có đáng bấm không. */
.dv-mo-so {
  font-variant-numeric: tabular-nums;
  color: var(--vp-primary-text);
}
.dv-nhom.mo .dv-mo-nut .ms { transform: rotate(180deg); }

.dv-o-du { display: none; }
.dv-nhom.mo .dv-o-du { display: flex; }

/* ── Trang một dịch vụ ── */
.dv-dau { display: flex; align-items: center; gap: var(--s-5); margin-bottom: var(--s-5); }
.dv-dau-ico {
  flex: none; width: 52px; height: 52px; border-radius: var(--r-lg);
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--dv-mau) 16%, transparent);
  color: var(--dv-mau);
}
.dv-dau-ico .ms { font-size: 28px; }
.dv-dau h1 { margin: 0; }
.dv-dau .lede { margin: 2px 0 0; }

.dv-form { margin-top: var(--s-5); }
.dv-form-o {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  margin-bottom: var(--s-5);
}
.dv-form .field { margin: 0; }

.dv-tieu-ket { margin: var(--s-7) 0 var(--s-4); font: 600 17px/24px var(--font-display); }

/* ── Hoá đơn ── */
.dv-hoa-don.da-tra { opacity: .62; }
.dv-hd-dau { display: flex; gap: var(--s-4); justify-content: space-between; align-items: flex-start; flex-wrap: wrap; }
.dv-hd-dau b { font: 600 15px/22px var(--font-display); }
.dv-hd-tien { text-align: right; display: flex; flex-direction: column; gap: 4px; align-items: flex-end; }
.dv-hd-tien .num { font: 700 20px/26px var(--font-display); font-variant-numeric: tabular-nums; }
.dv-hd-chi-tiet { margin-top: var(--s-4); }
.dv-chon-form { margin-top: var(--s-5); }

/* Ô "không nợ gì" dùng giọng TÍCH CỰC, không phải giọng lỗi: viền xanh và icon
   tích. Dùng lại `.empty` màu xám thì tra ra "không có vi phạm nào" trông y hệt
   một cú tra hỏng. */
.dv-rong-tot { border-color: color-mix(in srgb, var(--vp-primary) 45%, var(--vp-line)); }
.dv-rong-tot > .ms { color: var(--vp-primary-text); }

/* ── Kệ hàng ── */
.dv-ke { display: flex; flex-direction: column; gap: var(--s-4); }
.dv-mon {
  display: flex; gap: var(--s-5); align-items: center; justify-content: space-between;
  flex-wrap: wrap;
}
.dv-mon-chu { min-width: 220px; flex: 1 1 320px; }
.dv-mon-chu b { font: 600 15px/22px var(--font-display); }
.dv-mon-meta { display: flex; flex-wrap: wrap; gap: var(--s-4); margin-top: 4px; align-items: center; }
.dv-mon-meta .ms { font-size: 14px; vertical-align: -3px; margin-right: 3px; }
.dv-sap-het { color: var(--vp-danger); font-weight: 600; }
.dv-mon-ghi { margin: var(--s-3) 0 0; }
.dv-mon-mua { display: flex; align-items: center; gap: var(--s-4); flex: none; }
.dv-mon-gia {
  font: 700 19px/26px var(--font-display); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.dv-mon-sl, .dv-mon-tien {
  height: 40px; padding: 0 var(--s-4);
  border: 1px solid var(--vp-line); border-radius: var(--r-sm);
  background: var(--vp-surface-2); color: var(--vp-ink); font: 500 14px var(--font-body);
}
.dv-mon-tien { width: 140px; text-align: right; font-variant-numeric: tabular-nums; }

/* ── Phần nghiệp vụ ── */
.dv-nghiep-vu { margin-top: var(--s-7); background: var(--vp-tint); }
.dv-nghiep-vu h2 { margin: 0 0 var(--s-4); font: 600 16px/23px var(--font-display); }
.dv-nghiep-vu p { margin: 0; color: var(--vp-muted); }
.dv-luu-y { list-style: none; margin: var(--s-5) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-3); }
.dv-luu-y li { display: flex; gap: var(--s-3); font-size: 13.5px; line-height: 20px; color: var(--vp-muted); }
.dv-luu-y .ms { flex: none; font-size: 16px; color: var(--vp-faint); margin-top: 2px; }

/* ── Đơn của tôi ── */
.dh-loc { display: flex; gap: var(--s-3); flex-wrap: wrap; margin: var(--s-5) 0; }
.dh-dong {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-5);
  flex-wrap: wrap; text-decoration: none; color: inherit;
  transition: border-color .15s;
}
.dh-dong:hover { border-color: var(--vp-primary); }
.dh-dong-chu { min-width: 0; flex: 1 1 300px; }
.dh-dong-chu b { display: block; font: 600 14.5px/21px var(--font-display); }
.dh-dong-chu em { display: block; font-style: normal; margin-top: 2px; }
.dh-dong-phai { display: flex; align-items: center; gap: var(--s-4); flex: none; }
.dh-dong-phai .num { font: 700 17px/24px var(--font-display); font-variant-numeric: tabular-nums; }

/* ── Chi tiết đơn ── */
.dn-ket { display: flex; gap: var(--s-4); align-items: flex-start; }
.dn-ket .ms { flex: none; font-size: 20px; margin-top: 1px; }
.dn-dau { display: flex; gap: var(--s-4); justify-content: space-between; align-items: flex-start; flex-wrap: wrap; }
.dn-dau h1 { margin: 0; font: 600 20px/28px var(--font-display); }
.dn-the .badge .ms { font-size: 14px; vertical-align: -2px; margin-right: 3px; }
.dn-tro-ly {
  display: flex; gap: var(--s-3); align-items: flex-start;
  margin: var(--s-5) 0 0; padding: var(--s-4);
  background: var(--vp-ai-soft); border: 1px solid var(--vp-ai-line);
  border-radius: var(--r-md); font-size: 13px; line-height: 19px;
}
.dn-tro-ly .ms { flex: none; color: var(--vp-ai); font-size: 17px; }
.dn-tien { margin-top: var(--s-5); }
.dn-tien .num { font-variant-numeric: tabular-nums; }
.dn-giam { color: var(--vp-primary-text); }
/* Dòng món kèm trong bảng tiền: lùi vào và nhạt hơn dòng gốc — chúng là những
   khoản CỘNG THÊM vào giá vé, không phải những khoản ngang hàng với nó. */
.dn-kem-dong { padding-left: var(--s-4); color: var(--vp-muted); font-size: 13px; }
.dn-tong-nhan { font-weight: 600; }
.dn-tong { font: 700 20px/28px var(--font-display); }

/* ── Mua thêm (món kèm) ──────────────────────────────────────────────
   MỘT form cho cả bảng, nên bố cục là một danh sách chứ không phải mấy thẻ rời:
   mắt phải quét được cả cột giá và cả cột số lượng theo chiều dọc thì mới so
   được combo nào đáng mua. */
.dn-kem { margin-top: var(--s-6); }
.dn-kem h2 { font: 600 15px/22px var(--font-display); margin: 0 0 var(--s-3); }
.dn-kem-ds { list-style: none; margin: 0 0 var(--s-4); padding: 0; }
.dn-kem-ds li {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--vp-line-soft); border-radius: var(--r-md);
  background: var(--vp-surface);
}
.dn-kem-ds li + li { margin-top: var(--s-3); }
/* Món đang có trong đơn phải NHÌN LÀ THẤY, không phải đọc con số trong ô chọn:
   sau khi bấm cập nhật, khách cần một dấu hiệu rằng thao tác đã ăn. */
.dn-kem-ds li.da-chon { border-color: var(--vp-primary); background: var(--vp-tint); }
.dn-kem-chu { flex: 1; min-width: 0; }
.dn-kem-chu b { display: block; font: 600 13.5px/19px var(--font-display); }
.dn-kem-chu em {
  display: block; font-style: normal; margin-top: 1px;
  font-size: 12px; line-height: 17px; color: var(--vp-muted);
}
.dn-kem-gia {
  flex: none; font: 600 13.5px var(--font-display);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Đơn vị nhỏ và nhạt hơn con số: "129.000 ₫/combo" phải đọc ra là một cái giá,
   không phải hai mẩu thông tin ngang hàng. */
.dn-kem-gia i { font-style: normal; font-weight: 400; font-size: 11.5px; color: var(--vp-muted); }
.dn-kem-sl {
  flex: none; width: 62px;
  height: 34px; padding: 0 var(--s-3);
  border: 1px solid var(--vp-line); border-radius: var(--r-sm);
  background: var(--vp-surface-2); color: var(--vp-ink);
  font: 500 13.5px var(--font-body); font-variant-numeric: tabular-nums;
}

@media (max-width: 520px) {
  /* Hẹp thì giá xuống dòng dưới tên, ô số lượng vẫn ở bên phải — cắt ô số
     lượng đi là bỏ mất thứ duy nhất trên dòng này bấm được. */
  .dn-kem-ds li { flex-wrap: wrap; }
  .dn-kem-chu { flex: 1 1 100%; }
  .dn-kem-gia { margin-right: auto; }
}
.dn-ma { margin-top: var(--s-6); }
.dn-ma label { display: block; font: 600 13px var(--font-display); margin-bottom: var(--s-2); }
.dn-ma-hang { display: flex; gap: var(--s-3); }
.dn-ma-hang input {
  flex: 1; height: 42px; padding: 0 var(--s-4);
  border: 1px solid var(--vp-line); border-radius: var(--r-sm);
  background: var(--vp-surface-2); color: var(--vp-ink);
  font: 500 14px var(--font-mono); text-transform: uppercase;
}
.dn-go-ma {
  border: 0; background: none; padding: 0; margin-left: var(--s-2);
  color: var(--vp-danger); font: inherit; text-decoration: underline; cursor: pointer;
}
.dn-goi-y { margin-top: var(--s-4); }
.dn-goi-y-ds { list-style: none; margin: var(--s-3) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s-3); }
.dn-goi-y-so { color: var(--vp-primary-text); font-weight: 600; font-variant-numeric: tabular-nums; }
.dn-nut { display: flex; flex-direction: column; gap: var(--s-3); margin-top: var(--s-6); }
.dn-gd, .dn-ghi-chu { margin: var(--s-5) 0 0; font-size: 13px; color: var(--vp-muted); }
.dn-ghi-chu { display: flex; gap: var(--s-3); align-items: flex-start; }
.dn-ghi-chu .ms { flex: none; font-size: 16px; }

/* ── Kho ưu đãi ── */
.uu-luoi {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  margin-top: var(--s-5);
}
.uu-the { display: flex; align-items: center; gap: var(--s-4); }
.uu-the-ico {
  flex: none; width: 40px; height: 40px; border-radius: var(--r-md);
  display: grid; place-items: center;
  background: var(--vp-tint-2); color: var(--vp-primary-text);
}
.uu-the-ico .ms { font-size: 21px; }
.uu-the-chu { flex: 1; min-width: 0; }
.uu-the-chu b { display: block; font: 600 14px/20px var(--font-display); }
.uu-the-han { margin-top: 4px; font-size: 12px; color: var(--vp-faint); }
.uu-the-han .ms { font-size: 13px; vertical-align: -2px; margin-right: 3px; }
.uu-the-ma {
  flex: none; display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px dashed var(--vp-line); border-radius: var(--r-sm);
  background: var(--vp-tint); color: var(--vp-ink);
  font: 600 13px var(--font-mono); cursor: pointer;
}
.uu-the-ma:hover { border-color: var(--vp-primary); border-style: solid; }
.uu-the-ma .ms { font-size: 15px; color: var(--vp-faint); }
.uu-the-ma.xong { border-color: var(--vp-primary); border-style: solid; }
.uu-the-ma.chep-loi { border-color: var(--vp-danger); }
.uu-luoi-mo .uu-the-tat { opacity: .55; }

@media (max-width: 700px) {
  .dv-mon { flex-direction: column; align-items: stretch; }
  .dv-mon-mua { justify-content: space-between; }
  .dv-dau-ico { width: 44px; height: 44px; }
  .dv-dau-ico .ms { font-size: 24px; }
}

/* ── Panel khu dịch vụ trên bảng làm việc ────────────────────────
   Khác bốn loại panel cũ ở một điểm: có FORM. Ba panel đầu chỉ để nhìn rồi bấm
   sang trang chi tiết; ở đây khách chọn một chuyến bay và chốt một đơn ngay trên
   bảng. Nút vì thế phải đủ to để bấm được trong ô một-phần-tư (~380px), và phần
   chữ phải cắt gọn thay vì đẩy nút rơi xuống hàng. */

.vat-dv-ds li,
.vat-uu-ds li { padding: var(--s-3) var(--s-4); }
.vat-dv-o {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
}
.vat-dv-chu { min-width: 0; flex: 1; }
.vat-dv-chu b {
  display: block; font: 600 13px/18px var(--font-display);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vat-dv-phu, .vat-dv-meta {
  display: block; font: 400 11.5px/16px var(--font-body); color: var(--vp-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vat-dv-meta { color: var(--vp-faint); }
.vat-dv-phai { display: flex; align-items: center; gap: var(--s-3); flex: none; }
.vat-dv-gia {
  font: 700 13.5px var(--font-display); font-variant-numeric: tabular-nums; white-space: nowrap;
}

.vat-nut-chon {
  display: inline-flex; align-items: center; gap: 4px;
  min-height: 30px; padding: 0 var(--s-4);
  border: 1px solid var(--vp-line); border-radius: var(--r-full);
  background: var(--vp-surface-2); color: var(--vp-ink);
  font: 600 12px var(--font-display); cursor: pointer; white-space: nowrap;
}
.vat-nut-chon:hover { border-color: var(--vp-ai); color: var(--vp-ai); }
.vat-nut-chon .ms { font-size: 15px; }
.vat-nut-chinh {
  background: var(--vp-ai); border-color: var(--vp-ai); color: var(--vp-on-ai);
  min-height: 38px; width: 100%; justify-content: center;
}
.vat-nut-chinh:hover { background: var(--vp-ai-2); border-color: var(--vp-ai-2); color: var(--vp-on-ai); }
.vat-dv-them { margin: var(--s-3) 0 0; font: 400 11.5px var(--font-body); color: var(--vp-muted); }
.vat-dv-them a { color: var(--vp-ai); }

/* Đơn chờ xác nhận — panel DUY NHẤT trên bảng có một nút hành động chính. Tô đậm
   nó có chủ ý: đây là chỗ khách chốt việc, và mọi thứ khác trên panel chỉ để họ
   đọc lại cho chắc trước khi bấm. */
.vat-don { display: flex; flex-direction: column; gap: var(--s-3); }
.vat-don-dau { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-3); }
.vat-don-dau b { font: 600 14px/20px var(--font-display); min-width: 0; }
.vat-don-phu { margin: 0; font: 400 12px var(--font-body); color: var(--vp-muted); }
.vat-don-tien { display: flex; align-items: baseline; gap: var(--s-3); }
.vat-don-goc { font: 400 12.5px var(--font-display); color: var(--vp-faint); text-decoration: line-through; }
.vat-don-tong { font: 700 20px/26px var(--font-display); font-variant-numeric: tabular-nums; }
.vat-don-nut { display: flex; flex-direction: column; gap: var(--s-2); margin-top: var(--s-2); }
.vat-don-mo { font: 400 12px var(--font-body); color: var(--vp-ai); text-align: center; }

.vat-dong-mo {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  text-decoration: none; color: inherit; min-width: 0;
}

.vat-uu-o { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.vat-uu-ma {
  flex: none; padding: 3px var(--s-3);
  border: 1px dashed var(--vp-line); border-radius: var(--r-sm);
  background: var(--vp-tint); color: var(--vp-ink);
  font: 600 12px var(--font-mono); cursor: pointer;
}
.vat-uu-ma:hover, .vat-uu-ma.xong { border-style: solid; border-color: var(--vp-ai); }
.vat-uu-ma.chep-loi { border-color: var(--vp-danger); }

/* ── Chuyển sang CSKH người thật ─────────────────────────────────
   Hai ô bấm nằm ngay dưới câu trả lời của trợ lý, và khung gọi là một
   <dialog> toàn màn.

   VÌ SAO KHUNG GỌI TO. Bản vẽ tham chiếu là một thẻ nhỏ xíu kiểu thông báo
   cuộc gọi nhỡ trong ứng dụng nhắn tin — trên một màn hình rộng thì nó vừa
   khó bấm vừa trông như một cái toast lạc chỗ. Cuộc gọi là việc khách đang
   LÀM, không phải một thông báo lướt qua, nên nó chiếm trọn sự chú ý cho tới
   khi cúp máy. */

.cskh-the {
  margin-top: var(--s-4);
  padding: var(--s-4);
  background: var(--vp-surface-2);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
}
.cskh-nhan {
  margin: 0 0 var(--s-1);
  font: 600 11px var(--font-display); letter-spacing: .07em; text-transform: uppercase;
  color: var(--vp-faint);
}
.cskh-ly { margin: 0 0 var(--s-3); font: 400 12.5px/18px var(--font-body); color: var(--vp-muted); }
.cskh-luoi { display: grid; gap: var(--s-3); grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.cskh-o {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4);
  text-align: left;
  background: var(--vp-surface); border: 1px solid var(--vp-line);
  border-radius: var(--r-md); color: inherit; cursor: pointer;
  transition: border-color .15s, transform .15s;
}
.cskh-o:hover { border-color: var(--vp-ai); transform: translateY(-1px); }
.cskh-o-ico {
  flex: none; width: 34px; height: 34px; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: var(--vp-ai-soft); color: var(--vp-ai);
}
.cskh-o-ico .ms { font-size: 18px; }
.cskh-o-chu { min-width: 0; }
.cskh-o-chu b { display: block; font: 600 13px/18px var(--font-display); }
.cskh-o-chu em {
  display: block; font-style: normal; font: 400 11.5px/16px var(--font-body);
  color: var(--vp-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Ô "Gọi" là hành động chính — tô nền xanh của nút gọi, không tô tím trợ lý:
   đầu dây bên kia là người, không phải AI. */
.cskh-o-chinh .cskh-o-ico { background: color-mix(in srgb, #1f9d55 20%, transparent); color: #1f9d55; }
.cskh-o-chinh:hover { border-color: #1f9d55; }

.cskh-bao {
  display: flex; align-items: center; gap: var(--s-3);
  margin-top: var(--s-4); padding: var(--s-3) var(--s-4);
  background: var(--vp-tint); border: 1px solid var(--vp-line-soft);
  border-radius: var(--r-md);
  font: 400 12.5px/18px var(--font-body); color: var(--vp-muted);
}
.cskh-xong .ms { font-size: 17px; color: var(--vp-faint); }

/* ── 👍/👎 cho một câu trả lời ──
   Phải MỜ. Nó nằm dưới mọi câu trả lời, tức là thứ xuất hiện nhiều nhất trong khung
   chat — tô đậm bằng ai thì cả trang thành một hàng nút xin điểm. Hiện rõ khi rê chuột
   vào bong bóng là đủ: người muốn chấm sẽ tìm thấy, người không muốn thì không bị hỏi. */
.pn {
  display: flex; gap: var(--s-1);
  margin-top: var(--s-3);
  opacity: .35;
  transition: opacity .15s;
}
.msg:hover .pn, .pn:focus-within { opacity: 1; }
.pn-nut {
  display: grid; place-items: center;
  width: 28px; height: 28px;
  background: transparent; border: 0; border-radius: var(--r-md);
  color: var(--vp-muted); cursor: pointer;
  transition: background .12s, color .12s;
}
.pn-nut svg { width: 16px; height: 16px; overflow: visible; }
.pn-nut:hover { background: var(--vp-tint); color: var(--vp-ink); }
.pn-nut:focus-visible { outline: 2px solid var(--vp-ai); outline-offset: 1px; }
.pn-xong { font: 400 11.5px/16px var(--font-body); color: var(--vp-muted); }

/* ── Chấm điểm dịch vụ ──
   Hai bề mặt, và chúng cố ý KHÁC HẠNG NHAU.

   `.dg-the` là một tấm thẻ đầy đủ — nó chỉ hiện khi trợ lý tự thấy việc đã xong,
   hoặc ngay sau một cuộc gọi. Cả hai lúc đó khách vừa nhận được một thứ gì đó, nên
   xin họ hai câu là hợp lý.

   `.dg-dai` tự nhảy ra sau một quãng im lặng, không ai mời nó. Vì vậy nó là một dải
   một dòng, không viền dày, không tiêu đề, chỉ một câu hỏi và ba nút — và một trong
   ba nút là "bỏ qua". Cho nó cùng dáng với tấm thẻ kia là biến một câu hỏi nhẹ thành
   một hộp thoại chắn đường, mà thứ chắn đường thì người ta đóng chứ không trả lời. */

.dg-the {
  margin-top: var(--s-4);
  padding: var(--s-4);
  background: var(--vp-surface-2);
  border: 1px solid var(--vp-line);
  border-radius: var(--r-lg);
}
.dg-nhan {
  margin: 0 0 var(--s-1);
  font: 600 11px var(--font-display); letter-spacing: .07em; text-transform: uppercase;
  color: var(--vp-faint);
}
.dg-hoi { margin: 0; font: 600 14px/20px var(--font-display); }
.dg-viec { margin: var(--s-1) 0 0; font: 400 12px/17px var(--font-body); color: var(--vp-muted); }

.dg-sao { display: flex; gap: var(--s-1); margin-top: var(--s-3); }
.dg-sao-nut {
  display: grid; place-items: center;
  width: 40px; height: 40px;
  background: transparent; border: 0; border-radius: var(--r-md);
  color: var(--vp-faint); cursor: pointer;
  transition: color .12s, transform .12s;
}
.dg-sao-nut .ms { font-size: 26px; }
.dg-sao-nut:hover { transform: translateY(-1px); }
.dg-sao-nut.on { color: #e0a109; }
.dg-sao-nut:focus-visible { outline: 2px solid var(--vp-ai); outline-offset: 2px; }

.dg-xong-chua { margin-top: var(--s-3); padding-top: var(--s-3); border-top: 1px solid var(--vp-line-soft); }
.dg-hoi2 { margin: 0 0 var(--s-2); font: 400 12.5px/18px var(--font-body); color: var(--vp-muted); }

.dg-xong {
  display: flex; align-items: center; gap: var(--s-2);
  margin: 0;
  font: 400 12.5px/18px var(--font-body); color: var(--vp-muted);
}
.dg-xong .ms { font-size: 17px; color: var(--tone-ok-fg, #1f9d55); }

.dg-dai {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3);
  margin-top: var(--s-4); padding: var(--s-3) var(--s-4);
  background: var(--vp-tint); border: 1px solid var(--vp-line-soft);
  border-radius: var(--r-full);
}
.dg-dai-chu { flex: 1 1 auto; font: 400 12.5px/18px var(--font-body); color: var(--vp-muted); }
.dg-dai-nut { display: flex; gap: var(--s-2); }
.dg-dai-o {
  min-height: 32px; padding: 0 var(--s-4);
  background: var(--vp-surface); border: 1px solid var(--vp-line);
  border-radius: var(--r-full);
  font: 500 12px var(--font-body); color: inherit; cursor: pointer;
  transition: border-color .15s;
}
.dg-dai-o:hover { border-color: var(--vp-ai); }
/* "Bỏ qua" phải nhìn ra là lối thoát, không phải một lựa chọn ngang hàng — nhưng vẫn
   phải thấy được. Làm nó mờ tới mức phải đi tìm là ép trả lời bằng thiết kế. */
.dg-dai-o-phu { background: transparent; border-color: transparent; color: var(--vp-faint); }
.dg-dai-o-phu:hover { border-color: var(--vp-line); }

/* ── Khung gọi ── */
.goi {
  border: 0; padding: 0; background: transparent;
  max-width: min(420px, 92vw); width: 100%;
}
.goi::backdrop { background: rgba(6, 14, 14, .72); backdrop-filter: blur(3px); }
.goi-than {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-3);
  padding: var(--s-8) var(--s-7) var(--s-7);
  background: var(--vp-surface); border: 1px solid var(--vp-line);
  border-radius: var(--r-3xl);
  box-shadow: 0 24px 60px -24px rgba(0, 0, 0, .6);
  text-align: center;
}
.goi-ava {
  position: relative;
  width: 92px; height: 92px; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: var(--vp-ai-soft); color: var(--vp-ai);
  margin-bottom: var(--s-2);
}
.goi-ava .ms { font-size: 42px; }
/* Vòng sáng lan ra trong lúc đổ chuông rồi TẮT khi đã nối máy: nó là dấu hiệu
   "đang chờ", để yên sau khi nối là nói sai trạng thái. */
.goi-ava::after {
  content: ""; position: absolute; inset: -6px;
  border-radius: var(--r-full); border: 2px solid var(--vp-ai);
  animation: goi-song 1.6s ease-out infinite;
}
.goi.da-noi .goi-ava::after { animation: none; opacity: 0; }
@keyframes goi-song {
  0% { transform: scale(1); opacity: .7; }
  100% { transform: scale(1.28); opacity: 0; }
}
.goi-ten { margin: 0; font: 600 19px/26px var(--font-display); }
.goi-so { margin: 0; font: 400 13px var(--font-mono); color: var(--vp-muted); }
.goi-tt {
  margin: var(--s-1) 0 var(--s-5);
  font: 600 15px var(--font-display); font-variant-numeric: tabular-nums;
  color: var(--vp-primary-text);
}
.goi-nut { display: flex; align-items: center; gap: var(--s-6); }
.goi-nut-nho {
  width: 48px; height: 48px; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: var(--vp-tint-2); border: 1px solid var(--vp-line);
  color: var(--vp-ink); cursor: pointer;
}
.goi-nut-nho:hover { border-color: var(--vp-ai); }
.goi-nut-nho.on { background: var(--vp-ink); color: var(--vp-surface); border-color: var(--vp-ink); }
.goi-nut-nho .ms { font-size: 21px; }
.goi-cup {
  width: 64px; height: 64px; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: #d92d20; border: 0; color: #fff; cursor: pointer;
  box-shadow: 0 8px 20px -8px rgba(217, 45, 32, .8);
}
.goi-cup:hover { background: #b42318; }
.goi-cup .ms { font-size: 28px; }
.goi-ghi { margin: var(--s-5) 0 0; font: 400 11.5px/16px var(--font-body); color: var(--vp-faint); }

@media (prefers-reduced-motion: reduce) {
  .goi-ava::after { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════
   BA VÙNG — điều hướng · hội thoại · bảng làm việc
   ══════════════════════════════════════════════════════════════════

   Khai SAU khối `.agent` gốc để đè lên nó. Giữ khối cũ nguyên vẹn thay vì sửa
   tại chỗ là có chủ ý: những chú thích ở đó ghi lại vì sao cột trợ lý từng rộng
   380–560px và vì sao trần tính bằng phần trăm — cả hai lý lẽ vẫn đúng, chỉ là
   cột ấy đã đổi bên.

   VÌ SAO BẢNG KHÔNG CHIẾM CHỖ KHI TRỐNG. Bản trước chia màn cố định: bảng bên
   trái, chat dán mép phải. Nghĩa là khung chat bị ép hẹp suốt cả cuộc trò
   chuyện để giữ chỗ cho một thứ phần lớn thời gian trống trơn. Giờ `.agent` chỉ
   có một cột cho tới khi app.js gắn `co-bang` — hội thoại nở ra giữa màn, và
   bảng đẩy vào từ phải đúng lúc trợ lý dựng bảng biểu đầu tiên. */

.agent { grid-template-columns: minmax(0, 1fr); }

/* Không có bảng → hội thoại đứng giữa với bề ngang đọc được. Để nó kéo hết 1440px
   thì mỗi dòng chữ dài gần hai trăm ký tự, mắt lạc dòng khi xuống hàng. */
.agent > .chat {
  border-left: 0;
  border-right: 1px solid var(--vp-line);
  width: 100%;
  max-width: 980px;
  margin: 0 auto;
}
.agent:not(.co-bang) > .chat { border-right: 0; }

/* Bảng trống thì BIẾN MẤT HẲN, không phải co lại. `.agent` một cột nên `.bang`
   còn trong DOM sẽ rơi xuống thành hàng thứ hai và nằm ngay dưới khung chat —
   một hộp xám to đùng nói "bảng còn trống", đúng thứ vừa bỏ đi ở bố cục cũ.
   Ô "bảng còn trống" vẫn còn dùng: nó là trạng thái thoáng qua lúc panel cuối
   cùng vừa đóng, và là thứ khách thấy trên màn hẹp khi mở tấm gập ra. */
.agent:not(.co-bang) > .bang { display: none; }

/* Có bảng → hai cột. Cột trợ lý giữ khoảng 400–620px: hẹp hơn thì tiếng Việt vỡ
   dòng liên tục (lý do đã ghi ở khối `.agent` gốc), rộng hơn thì bảng không đủ
   chỗ cho lưới 2×2. */
.agent.co-bang {
  grid-template-columns: clamp(430px, 38%, 660px) minmax(0, 1fr);
}
.agent.co-bang > .chat { max-width: none; margin: 0; }

/* Bảng đẩy vào từ mép phải. Bảng biểu đầu tiên xuất hiện giữa lúc trợ lý đang
   viết, nên chuyển động phải nhẹ — một cú nhảy đột ngột ở nửa màn kéo mắt khỏi
   câu đang đọc. */
.agent.co-bang > .bang { animation: bang-vao .28s cubic-bezier(.2, .7, .3, 1) both; }
@keyframes bang-vao {
  from { opacity: 0; transform: translateX(18px); }
  to { opacity: 1; transform: none; }
}

/* ── Ghim & so sánh ── */
.vat-nut { display: flex; align-items: center; gap: var(--s-1); }
.vat-ghim {
  display: grid; place-items: center;
  width: 30px; height: 30px; border-radius: var(--r-full);
  border: 0; background: transparent; color: var(--vp-faint); cursor: pointer;
}
.vat-ghim:hover { background: var(--vp-tint-2); color: var(--vp-ink); }
.vat-ghim .ms { font-size: 17px; }
/* Đã ghim thì phải NHÌN LÀ BIẾT, kể cả khi liếc qua: đây là thứ giải thích vì sao
   một bảng biểu không tự cập nhật nữa. */
.vat-ghim.on { background: var(--vp-ai-soft); color: var(--vp-ai); }
.vat-the:has(.vat-ghim.on) { border-color: var(--vp-ai-line); }

.bang-so-sanh { display: inline-flex; align-items: center; gap: 5px; }
.bang-so-sanh .ms { font-size: 15px; }

/* Chế độ so sánh: mọi bảng biểu bằng nhau trên MỘT hàng ngang. Hai biểu đồ vẽ
   trên hai bề ngang khác nhau thì mắt đọc ra hai độ dốc khác nhau dù số liệu y
   hệt — đó là lý do chế độ này tồn tại. */
.bang-than.so-sanh {
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  grid-template-rows: minmax(0, 1fr);
  grid-auto-flow: column;
}
.bang-than.so-sanh .vat-the { grid-column: auto; grid-row: auto; }

@media (max-width: 1100px) {
  /* Ba vùng không vừa nữa: bảng vẫn ở bên phải nhưng cột trợ lý co lại tối thiểu. */
  .agent.co-bang { grid-template-columns: minmax(340px, 38%) minmax(0, 1fr); }
}

@media (max-width: 900px) {
  /* Một cột — bảng thành tấm gập toàn màn (luật cũ ở `body.xem-bang` vẫn giữ). */
  .agent, .agent.co-bang { grid-template-columns: minmax(0, 1fr); }
  .agent > .chat, .agent.co-bang > .chat { max-width: none; margin: 0; border-right: 0; }
  .agent.co-bang > .bang { animation: none; }
  .bang-than.so-sanh { grid-auto-flow: row; grid-template-rows: none; }
}

@media (prefers-reduced-motion: reduce) {
  .agent.co-bang > .bang { animation: none; }
}
