  /* Bảng token đã chuyển sang token.css — file đó phải nạp TRƯỚC file này. */
  * { box-sizing:border-box; }
  body { margin:0; background:var(--nen-trang); color:var(--muc);
    font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif; font-size:var(--cx-than); line-height:1.5; }
  /* Đệm khung trang PHẢI trùng với `.admin-than` (admin.css) — đây là thứ
     quyết định hai trang có đọc lên như một sản phẩm hay không. Trước đây là
     `22px 20px 64px` bên này và `20px 22px 72px` bên kia: hai con số ngang-dọc
     bị ĐỔI CHỖ cho nhau, và đáy lệch 8px. Cả ba giá trị đều không nằm trên
     thang, nên sửa token không kéo theo gì. */
  .wrap { max-width:1320px; margin:0 auto;
    padding:var(--kc-5) var(--kc-6) var(--kc-10); }
  /* BA QUY TẮC `header` TRẦN ĐÃ BỎ (25/08, phát hiện #B2 của lượt soi).
     Chúng là di tích từ thời tiêu đề trang nằm trong `<header>`; nay hai trang
     nội dung dùng `.khu-tieu-de` (khai ở cuối file này) và `<header>` chỉ còn
     tồn tại ĐÚNG MỘT chỗ trong cả dự án: thanh dùng chung `_dau_trang.html`.

     Nên `header h1` và `header p` không trúng gì cả, còn `header { }` thì rót
     `justify-content:space-between` + `margin-bottom:16px` vào một cái thanh mà
     `.dau-trang` đã tự khai đầy đủ. Đo được: thanh đầu trang cao **65px** trong
     khi `--cao-dau-trang` khai 49px.

     Và 16px lạc đó nuôi luôn lỗi thứ hai ở file khác: `.admin{min-height:100vh}`
     đo từ y=65 nên cả TÁM khu quản trị có thanh cuộn dọc thừa đúng 65px. Một
     khai báo chết, hai lỗi ở hai chỗ, không chỗ nào chỉ về đây.

     `justify-content` bỏ được vì `.dau-nguoi` có `margin-left:auto` — đó mới là
     thứ đẩy khối người dùng sang phải, và nó làm việc đó một mình. */
  /* !important là CỐ Ý và cần thiết: .hidden phải thắng MỌI khai báo display
     khác. Trước đây nó nằm trước .bento{display:grid} với cùng độ đặc hiệu
     (0,1,0), nên khai sau thắng — #viewMain không ẩn được nữa và bấm vào một
     chiến dịch thì trang chi tiết hiện ĐÈ LÊN trang tổng vẫn còn nguyên.
     Đây là lớp tiện ích, đúng chỗ dùng !important. */
  .hidden { display:none !important; }

  /* Đệm `var(--kc-5)` — trùng `.bento > .card` ngay dưới và trùng bậc mà
     token.css:84 đã ghi tên `.card`. Trước là `16px 18px`: 18 không nằm trên
     thang, và ba thẻ ca-rỗng ở bao_cao.html còn gõ inline `padding:22px` đè
     lên — ba con số cho cùng một mặt phẳng. */
  .card { background:var(--nen-the); border:1px solid var(--vien); border-radius:var(--bo-goc-the); padding:var(--kc-5);
    min-width:0; box-shadow:var(--bong-1); }
  .card h3 { margin:0 0 var(--kc-1); font-size:var(--cx-tieu-de); font-weight:700; display:flex; align-items:center; gap:var(--kc-2); }
  .card .desc { margin:0 0 var(--kc-2); color:var(--muc-mo); font-size:var(--cx-nhan); }
  .info { color:var(--muc-mo); cursor:help; font-size:var(--cx-nhan); border:1px solid var(--vien-tuong-tac); border-radius:50%;
    width:16px; height:16px; display:inline-flex; align-items:center; justify-content:center; flex:none; }
  .info:hover, .info:focus-visible { color:var(--nhan-manh); border-color:var(--nhan-manh); outline:none; }

  /* KHÔNG margin-bottom: `#viewCampaign > *` nuốt nó rồi, nên con số ở đây
     chỉ là một con số không có tác dụng — ai đọc `.crumb` sẽ tưởng khe dưới
     nó là 12px trong khi thật ra là 16px do gap phát. */
  .crumb { display:flex; align-items:center; gap:var(--kc-2); flex-wrap:wrap; }

  .crumb .ctitle { font-size:var(--cx-tieu-de); font-weight:700; }
  .crumb .csub { color:var(--muc-mo); font-size:var(--cx-nhan); }

  /* `160px` chứ không `180px`: trang chiến dịch có BẢY thẻ từ 31/08, và ở khổ
     1440 (vùng nội dung 1.272px) mốc 180 chỉ xếp nổi 6 cột — thẻ thứ bảy rơi
     xuống hàng hai đứng một mình. Đo được: 6 thẻ 202×132px, thẻ mồ côi
     202×115px. Với 160 thì bảy thẻ vừa một hàng (171px mỗi thẻ).
     `grid-auto-rows:1fr` lo phần còn lại: khổ nào hẹp tới mức phải xuống hàng
     thì MỌI hàng cao bằng nhau, không còn hàng cuối lùn hơn 17px. */
  .kpis { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(160px,100%),1fr));
    grid-auto-rows:1fr; gap:var(--kc-3); }
  /* flex + margin-top:auto → số các thẻ luôn thẳng hàng dù nhãn dài xuống 2 dòng */
  .kpi { position:relative; display:flex; flex-direction:column; }
  .kpi.click { cursor:pointer; } .kpi.click:hover { border-color:var(--nhan-manh); }
  /* NHÃN THẺ — ba thứ đổi cùng lúc ngày 31/08, người dùng: *"các chữ bị nhảy
     dòng lung tung và không canh chuẩn layout, các con số và đề mục thể hiện
     mờ nhạt, không nổi bật"*.

     1. CHỖ CHO HAI DÒNG, LUÔN LUÔN (`min-height`). Đo được: trong bảy thẻ chỉ
        "Account gửi thành công" xuống hai dòng, sáu thẻ kia một dòng — nên mép
        trên của các con số không nằm trên một đường. `margin-top:auto` ở
        `.value` đã kéo số xuống đáy cho thẳng hàng, nhưng nó để lại một khoảng
        trống lệch nhau ở giữa, và đó chính là cái "nhảy dòng lung tung" nhìn
        thấy. Chừa sẵn hai dòng thì thẻ nào cũng cùng một khung.
     2. `--muc-2` thay `--muc-mo`. Cả hai đều đạt chuẩn tương phản (10,22:1 và
        6,23:1 trên nền thẻ) nên đây không phải sửa lỗi tiếp cận — đây là thứ
        bậc: nhãn là thứ người ta đọc để biết con số nghĩa là gì, không phải
        chú thích. `--muc-mo` vẫn là bậc nhạt nhất được phép và vẫn dùng cho
        dòng phụ.
     3. 12px thay 11px (`--cx-nhan`, đúng bậc token.css ghi là "nhãn KPI").
        Chữ IN HOA cỡ 11 có letter-spacing là tổ hợp khó đọc nhất trên màn nhỏ. */
  .kpi .label { color:var(--muc-2); font-size:var(--cx-nhan); font-weight:600; text-transform:uppercase;
    letter-spacing:.03em; padding-right:var(--kc-6); line-height:1.35;
    min-height:calc(var(--cx-nhan) * 1.35 * 2); }
  /* Số chính thống nhất màu mực; chỉ chỉ số cảnh báo mới tô màu (audit 2.3) */
  .kpi .value { font-size:var(--cx-kpi); font-weight:700; margin-top:auto; padding-top:var(--kc-1); letter-spacing:-.02em;
    line-height:1.1; color:var(--muc); }
  /* Vế dưới của phân số trên thẻ KPI. Nhạt hơn và nhỏ hơn để mắt đọc vế trên
     trước — hai vế cùng cỡ thì không rõ đâu là con số chính. */
  .kpi .sub { font-size:var(--cx-nhan); color:var(--muc-2); margin-top:var(--kc-1); }
  /* Dòng "đi tới đâu" của thẻ bấm được. Nói RA ĐÍCH bằng chữ, không để mỗi mũi
     tên ↗ ở nhãn: bốn thẻ mới đều bấm được nhưng dẫn tới hai chỗ khác nhau. */
  .kpi .toi { display:inline-flex; align-items:center; gap:var(--kc-1); font-size:var(--cx-micro);
    color:var(--nhan-manh); font-weight:600; margin-top:var(--kc-2); }
  .kpi.bad .value { color:var(--xau); }
  .kpi .info { position:absolute; top:14px; right:14px; }
  .kpi.click .label::after { content:'↗'; color:var(--nhan-manh); margin-left:var(--kc-1); font-size:var(--cx-micro); }

  .cbody { flex:1 1 auto; min-height:0; }
  /* `:not(.ic)` là bắt buộc, không phải cho gọn: quy tắc này nhắm BIỂU ĐỒ, mà
     `.cbody svg` có độ ưu tiên (0,1,1) còn `.ic-nho` chỉ (0,1,0) — nên mọi biểu
     tượng lọt vào một `.cbody` đều bị kéo thành 100%×100%. Khu "Xem xuyên chuỗi"
     dùng `.cbody`, và hộp nhắc trong đó từng hiện ra thành một mũi tên khổng lồ
     kèm chữ đọc dọc một cột. */
  .cbody svg:not(.ic) { width:100%; height:100%; display:block; }
  .hscroll { overflow-x:auto; overflow-y:hidden; scrollbar-gutter:stable; }
  .hscroll svg { display:block; }
  svg text { fill:var(--muc-mo); }
  svg .axis-line { stroke:var(--truc); stroke-width:1; }
  svg .grid-line { stroke:var(--luoi); stroke-width:1; }

  .clist-tools { display:flex; gap:var(--kc-2); flex-wrap:wrap; align-items:center; margin-bottom:var(--kc-2); }

  /* ── BA NÚT LỌC BĂNG của bảng Top Account (31/08) ────────────────────────
     Dải nút liền khối, cùng khuôn dải nút mà cụm chọn khoảng từng dùng.

     `--vien-tuong-tac`, KHÔNG `--vien`: luật 3 của token.css. Đây là dải nút
     trên nền trắng, mà `--vien` chỉ đạt 1,28:1 nên ranh giới control gần như
     không thấy — WCAG 1.4.11 đòi >= 3:1. Vạch chia giữa hai nút cũng vậy.

     Nút ĐANG CHỌN đổi cả NỀN chứ không chỉ màu chữ: ba nút cạnh nhau mà chỉ
     khác sắc chữ thì người nhìn lướt không biết mình đang xem băng nào — mà cả
     tính năng này sinh ra để nhìn lướt.

     Màu Hot/Warm chỉ tô khi nút ĐANG CHỌN. Tô sẵn cả ba lúc chưa bấm là ba mảng
     màu tranh nhau ngay trên đầu bảng, trong khi bảng bên dưới mới là chỗ cần
     đọc. Con số trên nút mới là thứ nói "ở đây có mấy người". */
  .nhom-bang { display:inline-flex; border:1px solid var(--vien-tuong-tac);
    border-radius:var(--bo-goc-khoi); overflow:hidden; background:var(--nen-the);
    box-shadow:var(--bong-1); }
  .nhom-bang button { min-height:34px; padding:0 var(--kc-3); border:none; background:none;
    font:inherit; font-size:var(--cx-nho); color:var(--muc-2); cursor:pointer;
    display:inline-flex; align-items:center; gap:var(--kc-1); }
  .nhom-bang button + button { border-left:1px solid var(--vien-tuong-tac); }
  .nhom-bang button:hover { background:var(--hang-hover); color:var(--nhan-manh); }
  .nhom-bang button .cnt { font-variant-numeric:tabular-nums; font-weight:700; }
  .nhom-bang button[aria-pressed="true"] { background:var(--nhan-manh); color:var(--tren-dac);
    font-weight:650; }
  .nhom-bang button.hot[aria-pressed="true"] { background:var(--xau); color:var(--tren-dac); }
  .nhom-bang button.warm[aria-pressed="true"] { background:var(--canh-bao); color:var(--tren-dac); }
  /* Câu "còn N người nữa chưa hiện" nằm NGAY DƯỚI bảng, không phải cuối thẻ:
     nó nói về cái bảng vừa đọc xong, để xa là thành một ghi chú chung chung. */
  .cat-tran { margin-top:var(--kc-2); }
  .clist-tools input { flex:1; min-width:160px; border-radius:var(--bo-goc-nut); border:1px solid var(--vien-tuong-tac);
    background:var(--nen-the); color:var(--muc); font:inherit; font-size:var(--cx-nho); }

  /* Ô TÌM NHANH của các bảng account. Kế thừa `.clist-tools input` ở trên (viền
     tương tác, bo góc, cỡ chữ) — chỉ khai thêm ba thứ:
     · `appearance:none` — `type=search` của WebKit tự vẽ nút xoá và viền riêng,
       nên cùng một ô trông khác nhau trên Safari và Chrome;
     · `flex:0 1 240px` chứ không nhận `flex:1` — thanh này còn chở nút tải và
       dòng "khớp N người", để ô nhập nở hết chỗ là đẩy chúng ra khỏi tầm mắt;
     · viền tiêu điểm, vì ô này là chỗ NGƯỜI DÙNG BÀN PHÍM vào đầu tiên. */
  .o-tim { appearance:none; -webkit-appearance:none; flex:0 1 240px; min-width:150px; }
  .o-tim:focus-visible { outline:2px solid var(--nhan-manh); outline-offset:1px; }
  .o-tim::-webkit-search-cancel-button { cursor:pointer; }
  .cwrap { overflow-x:auto; }
  .clist-scroll { max-height:300px; overflow:auto; }   /* ~5 chiến dịch, còn lại cuộn dọc trong thẻ */
  /* min-width để trên mobile bảng CUỘN NGANG thay vì bị bóp mất cột (audit 3.1).
     860px chứ không 560px nữa: bảng lên 8 cột (14/08), mà cột Hoạt động chở
     chip "● Hôm nay" cộng chip "! lệch" nên một mình nó cần ~150px. Bóp xuống
     560px thì tên chiến dịch còn ~90px và chữ xếp dọc — đúng thứ bộ đo bắt. */
  table.clist { width:100%; min-width:860px; border-collapse:collapse; font-variant-numeric:tabular-nums; }
  table.clist th { text-align:right; padding:var(--kc-2); font-size:var(--cx-micro); color:var(--muc-mo); font-weight:600;
    border-bottom:2px solid var(--vien); white-space:nowrap; text-transform:uppercase; letter-spacing:.02em;
    position:sticky; top:0; background:var(--nen-the); z-index:1; }
  table.clist th.left { text-align:left; }
  table.clist td { padding:var(--kc-3) var(--kc-2); text-align:right; border-bottom:1px solid var(--vien); white-space:nowrap; }
  table.clist td.left { text-align:left; white-space:normal; }
  table.clist tbody tr:nth-child(even) td { background:var(--van); }
  table.clist tr.crow:hover td { background:var(--hang-hover); }
  tr.crow { cursor:pointer; }
  .cn { font-weight:600; }
  .crange { display:block; font-weight:500; color:var(--muc-mo); font-size:var(--cx-micro); margin-top:var(--kc-1); }
  .cn .go { color:var(--nhan-manh); font-size:var(--cx-micro); white-space:nowrap; }

  /* Hai tầng: hàng sự kiện bung ra hiện các chuỗi bên trong. Chuỗi thụt vào
     và chữ nhạt hơn để mắt phân biệt ngay tầng nào là tầng nào. */
  tr.skrow > td { background:var(--van); }
  tr.skrow.mo > td { background:var(--nhan-manh-nhat); }
  tr.skrow { cursor:default; }          /* thao tác nằm ở hai nút, không ở cả hàng */

  /* Hai thao tác tách biệt trên một hàng: nút mũi tên (ra thẳng email) và tên
     chiến dịch (ra các chuỗi). Nút phải đủ lớn để bấm được — bản trước để mũi
     tên rộng 14px, quá nhỏ. */
  .skdong { display:flex; align-items:center; gap:var(--kc-2); }
  .btn-so { flex:none; width:32px; height:32px; padding:0; cursor:pointer;
            display:inline-flex; align-items:center; justify-content:center;
            font-size:var(--cx-nho); line-height:1; color:var(--muc-2);
            background:var(--nen-the); border:1px solid var(--vien-tuong-tac);
            border-radius:var(--bo-goc-nut); }
  .btn-so:hover { background:var(--hang-hover); border-color:var(--nhan-manh); color:var(--nhan-manh); }
  .btn-so.mo { background:var(--nhan-manh); border-color:var(--nhan-manh); color:var(--tren-dac); }
  /* opacity .5 khớp `.nut:disabled` bên admin.css — trước đây ba nút đã tắt
     có ba độ mờ (.35 / .5 / .32) cho cùng một trạng thái.
     --vien-manh chứ không --vien: token.css:45 dành đúng bậc đó cho nút
     disabled. WCAG 1.4.11 miễn ngưỡng 3:1 cho control đã tắt, nên đây là lệch
     TẦNG chứ không phải lỗi tương phản. */
  .btn-so[disabled] { opacity:.5; cursor:default; }
  .btn-so[disabled]:hover { background:var(--nen-the); border-color:var(--vien-manh); color:var(--muc-2); }
  .btn-so:focus-visible, .cn-btn:focus-visible { outline:2px solid var(--nhan-manh); outline-offset:2px; }
  .cn-btn { flex:1; min-width:0; text-align:left; cursor:pointer; padding:4px 2px;
            font:inherit; font-weight:700; color:var(--muc);
            background:none; border:0; border-radius:6px; }
  .cn-btn:hover { color:var(--nhan-manh); text-decoration:underline; }

  /* 38px là một LỀ THỤT căn hàng con vào dưới tên hàng cha, không phải một
     bậc nhịp — nên nó cố ý nằm ngoài thang --kc-*. */
  tr.chrow > td.left { padding-left:38px; }
  tr.chrow .cn { font-weight:500; }
  tr.chrow > td { border-top:1px solid var(--luoi); }

  /* Kiểu của hàng email bung phẳng (`tr.emrow`) đã bỏ 23/08 cùng với nút ▸:
     bảng chiến dịch nay chỉ bung tới cấp CHUỖI, còn danh sách email thì xem ở
     trang riêng của chiến dịch. */
  td.chuagui { font-size:var(--cx-nhan); font-style:italic; }
  .tt { display:inline-block; margin-left:var(--kc-2); padding:1px 7px; border-radius:var(--bo-goc-tron);
        font-size:var(--cx-micro); font-weight:600; letter-spacing:.02em; vertical-align:1px;
        background:var(--van); color:var(--muc-2); }
  .tt.good { background:var(--tot-nhat); color:var(--tot); }
  .tt.warn { background:var(--canh-bao-nhat); color:var(--canh-bao); }

  .rank { color:var(--muc-mo); width:26px; } .muted { color:var(--muc-mo); }
  /* Nền trung tính NGAY TRÊN `.pill`, không chỉ trên các biến thể. Trước đây
     mọi nền nằm ở biến thể, nên `<span class="pill">chỉ nhận tin</span>` hiện
     ra thành chữ đậm lạc giữa dòng — một "pill" không phải pill, đặt cạnh
     `.pill tot` trong cùng bảng thì hàng nhãn không cùng một loại phần tử.
     Đặt ở đây thay vì chỉ sửa hai chỗ gọi: nó chặn cả những chỗ dựng pill
     trần sau này. */
  .pill { display:inline-block; padding:var(--kc-1) var(--kc-2); border-radius:var(--bo-goc-tron); font-size:var(--cx-nhan); font-weight:600;
    font-variant-numeric:tabular-nums; background:var(--nen-chim); color:var(--muc-2); }
  .pill.good { background:var(--tot-nhat); color:var(--tot); }
  .pill.warn { background:var(--canh-bao-nhat); color:var(--canh-bao); }
  .pill.bad { background:var(--xau-nhat); color:var(--xau); }
  /* Bốn dòng dưới đặt tên TIẾNG VIỆT cho khớp lối đặt tên của dự án (xem đầu
     token.css). Ba dòng trên là di sản của dashboard tĩnh cũ, vẫn còn chỗ dùng
     nên giữ nguyên cả hai lối gọi.
     Vì sao phải thêm: Giai đoạn 5 viết `class="pill tot"`, `pill canh`,
     `pill cho` mà KHÔNG có dòng nào khai — bốn pill trong khu Thông báo hiện ra
     không màu, chỉ còn mỗi cái khung. Tên lớp là hợp đồng ngầm giữa JS và CSS,
     và không bên nào báo lỗi khi hợp đồng bị lệch. */
  /* Mẫu quá nhỏ để tô tốt/xấu (dưới 30 lượt gửi — xem MAU_TOI_THIEU trong
     bao_cao.js). Trung tính CÓ CHỦ Ý: một email gửi cho 1 người mà mở 100% thì
     tô xanh là mời sale chọn nhầm mẫu thư, tô đỏ 0% là loại oan. Dùng nền chìm
     + chữ --muc-mo (bậc nhạt nhất được phép cho chữ, 6,23:1 trên thẻ). */
  .pill.nho { background:var(--nen-chim); color:var(--muc-mo); }
  .pill.tot { background:var(--tot-nhat); color:var(--tot); }
  /* Kèm viền 1px theo luật 2 trong token.css: --canh-bao-nhat là nền nhạt nhất
     bộ, chỉ 1,121:1 so với thẻ trắng nên tự nó không tách khỏi nền. */
  .pill.canh { background:var(--canh-bao-nhat); color:var(--canh-bao);
    border:1px solid var(--canh-bao); }
  /* `cho` = đang chờ, chưa có gì xảy ra. Trung tính — mượn màu trạng thái ở đây
     là nói dối, "chưa bắn lần nào" không tốt cũng không xấu. */
  .pill.cho { background:var(--nen-chim); color:var(--muc-mo); }

  /* Dùng '>' để quy tắc KHÔNG rò xuống bảng account (.ptable) lồng bên trong dòng chi tiết */
  table.emails { width:100%; min-width:620px; border-collapse:collapse; font-variant-numeric:tabular-nums; }
  /* Đệm nằm ở NÚT, không ở ô: vậy thì vùng bấm trùng đúng vùng người ta nhìn thấy.
     Để đệm trên <th> thì viền ô rộng hơn nút bên trong, và dải đệm quanh nút vừa
     đổi màu khi rê chuột vừa không bấm được.
     `cursor:pointer` cũng đã chuyển vào nút — nó KHÔNG bao giờ được là thứ duy
     nhất báo hiệu "bấm được", vì bàn phím không đọc được con trỏ chuột. */
  table.emails > thead > tr > th { text-align:right; padding:0; font-size:var(--cx-micro); color:var(--muc-mo); font-weight:600;
    border-bottom:2px solid var(--vien); white-space:nowrap; user-select:none;
    text-transform:uppercase; letter-spacing:.02em; }
  table.emails > thead > tr > th > button.th-sap { all:unset; box-sizing:border-box; display:flex;
    align-items:center; justify-content:flex-end; gap:var(--kc-1); width:100%; padding:var(--kc-2);
    min-height:30px; font:inherit; color:inherit; cursor:pointer; }
  table.emails > thead > tr > th.left > button.th-sap { justify-content:flex-start; }
  table.emails > thead > tr > th > button.th-sap:hover { color:var(--nhan-manh); }
  table.emails > thead > tr > th > button.th-sap:focus-visible { outline:2px solid var(--nhan-manh); outline-offset:-2px; }
  table.emails > thead > tr > th.left { text-align:left; }
  table.emails > tbody > tr > td { padding:var(--kc-3) var(--kc-2); text-align:right; border-bottom:1px solid var(--vien); white-space:nowrap; }
  table.emails > tbody > tr > td.left { text-align:left; white-space:normal; }
  tr.email-row { cursor:pointer; } tr.email-row:hover td { background:var(--hang-hover); }
  /* Hàng người MỞ ĐƯỢC hồ sơ. Bộ chọn bám thẳng vào `data-acct` chứ không đẻ
     tên lớp mới: chính thuộc tính ấy là thứ quyết định hàng có bấm được không,
     nên gắn kiểu vào nó thì không bao giờ lệch giữa cái trông-bấm-được và cái
     bấm-được-thật. Hàng không có danh bạ (nhóm click vô danh) không có thuộc
     tính này nên tự khắc trông đúng là hàng thường. */
  .ptable tr[data-acct] { cursor:pointer; }
  .ptable tr[data-acct]:hover td { background:var(--hang-hover); }
  .ptable tr[data-acct]:focus-visible { outline:2px solid var(--nhan-manh); outline-offset:-2px; }
  tr.email-row.hl td { background:var(--nhan-manh-nhat); }
  .ename { font-weight:600; } .ename .expand { color:var(--muc-mo); font-size:var(--cx-micro); margin-right:var(--kc-2); display:inline-block; width:10px; }
  .edate { color:var(--muc-mo); font-size:var(--cx-micro); }
  /* `text-align:left` PHẢI có. `table.emails > tbody > tr > td` đặt
     `text-align:right` cho mọi ô số, và hàng bung kế thừa nó — nên tiêu đề
     nhóm "Ưu tiên 1/2/3" dán sát mép phải của một hộp rộng 1.169px, để lại
     một dải trắng gần cả bề ngang với thanh xanh đứng trơ bên trái. Bảng bên
     trong thoát được vì `.ptable` khai lại text-align; mọi khối chữ khác thì
     không. Đo được: getComputedStyle('.nhomut').textAlign = "right". */
  table.emails > tbody > tr.detail-row > td { background:var(--nen-trang); padding:0;
    white-space:normal; text-align:left; }
  /* 34px cuối cùng là LỀ THỤT căn vào dưới tên email ở hàng cha (xem
     `tr.chrow > td.left` phía trên) — ngoài thang có chủ ý. Ba giá trị còn
     lại là nhịp, đi qua token. */
  .detail { padding:var(--kc-2) var(--kc-3) var(--kc-3) 34px; }
  .tabs { display:flex; flex-wrap:wrap; gap:var(--kc-2); margin:var(--kc-2) 0 var(--kc-1); }
  .tab { font-weight:600; }
  .tab.active { background:var(--nhan-manh); color:var(--tren-dac); border-color:var(--nhan-manh); }
  .tab .cnt { opacity:.8; font-weight:500; }
  /* TRẦN CHIỀU CAO cho tab account. Không có nó thì tab "Đã gửi" đổ hết 381
     hàng ra giữa trang chiến dịch, đẩy mọi thứ bên dưới xuống hàng nghìn px và
     mất luôn chỗ đứng của người đang đọc. 70vh dùng lại đúng con số đã có ở
     #nguoiBody, không đẻ ngưỡng thứ hai. `max-height` chứ không `height` nên
     tab ít hàng (Unsub, Tệp khách) không đổi gì. */
  .panel { margin-top:var(--kc-2); max-height:70vh; overflow:auto; } .panel-tools { display:flex; gap:var(--kc-2); margin:var(--kc-1) 0 var(--kc-2); }
  /* Tiêu đề nhóm của tab "Ưu tiên gọi". Ba nhóm loại trừ nhau nên mỗi nhóm là
     một bảng riêng — dải tiêu đề này là thứ duy nhất ngăn người đọc tưởng cả ba
     nhóm là một danh sách liền. */
  .nhomut { margin:var(--kc-3) 0 var(--kc-2); padding:var(--kc-2) var(--kc-3); background:var(--van);
    border-left:3px solid var(--nhan-manh); border-radius:var(--bo-goc-nut); }
  .nhomut:first-child { margin-top:0; }
  /* `--muc` chứ không `--muc-1`: bậc đó KHÔNG TỒN TẠI. Theo CSS Variables,
     một custom property không xác định làm hỏng khai báo ở bước
     computed-value, nên `color` rơi về `inherit` — hiện đúng màu chỉ nhờ
     tình cờ thừa hưởng `--muc` từ body, và sẽ đổi màu ngày nào khối này nằm
     trong một vùng có màu chữ khác. Không lỗi, không log, không lệch pixel. */
  .nhomut b { font-size:var(--cx-nhan); color:var(--muc); }
  .nhomut .cnt { font-size:var(--cx-nhan); color:var(--muc-mo); font-weight:500; }
  .nhomut .desc { font-size:var(--cx-micro); color:var(--muc-mo); margin-top:2px; }
  /* Đổi tên từ `.btn-copy` 21/08: mọi nút mang lớp này giờ đều là nút TẢI FILE,
     không còn nút nào copy. Giữ tên cũ là để lại một cái tên nói sai cho người
     đọc sau. Hai trạng thái `.ok`/`.err` cũ đã bỏ theo: chúng là phản hồi của
     lượt copy trong trình duyệt, còn lượt tải thì trình duyệt tự báo.
     `text-decoration:none` là BẮT BUỘC, không phải cho gọn: đây là thẻ <a> nên
     mặc định có gạch chân, mà ba nút này đứng cạnh `.tab` nên gạch chân làm
     hàng nút trông như hai loại phần tử khác nhau. */
  .btn-tai { text-decoration:none; transition:background .12s,color .12s,border-color .12s; }
  /* --bo-goc-khoi, KHÔNG --bo-goc-nut: token.css:126 gọi đích danh bốn khối
     lồng (.dstat .ptable .note-box .dtl-item). Dùng bậc của nút cho khối lồng
     là làm khối và nút trông cùng một cấp. */
  .ptable { width:100%; border-collapse:collapse; background:var(--nen-the); border:1px solid var(--vien); border-radius:var(--bo-goc-khoi); overflow:hidden; }
  /* Phải dùng .ptable thead th / .ptable tbody td để thắng quy tắc của table.emails
     (nếu không sẽ bị canh phải + nowrap kế thừa → bảng account tràn ngang, mất cột) */
  .ptable thead th { text-align:left; padding:var(--kc-2); font-size:var(--cx-micro); color:var(--muc-mo); font-weight:600;
    text-transform:uppercase; letter-spacing:.02em; border-bottom:1px solid var(--vien); background:var(--nen-trang);
    cursor:default; white-space:nowrap; }
  .ptable tbody td { padding:var(--kc-2); font-size:var(--cx-nho); text-align:left; white-space:normal;
    border-bottom:1px solid var(--vien); vertical-align:top; }
  .ptable tbody tr:nth-child(even) td { background:var(--van); }
  .ptable tr:last-child td { border-bottom:none; }
  .ptable a { color:var(--nhan-manh); text-decoration:none; } .ptable a:hover { text-decoration:underline; }
  /* Link mail và số điện thoại trong bảng account: chữ cao 16px nên vùng bấm
     cũng chỉ 16px, dưới sàn 32px của dự án. Lỗi này CÓ SẴN ở cả năm tab cũ —
     nó chỉ lộ ra khi tab "Ưu tiên gọi" kéo trang chi tiết email vào bộ đo lần
     đầu, đúng bài học gốc: lỗi giao diện luôn nằm ở chỗ bộ đo không soi tới.
     Cùng cách chữa đã dùng cho ô địa chỉ thư trong bảng account (`.acct-phu a`,
     kế thừa chính quy tắc này). */
  /* `min-width` cũng bắt buộc, không chỉ `min-height`: vùng bấm là cạnh NGẮN
     nhất, mà số điện thoại trong dữ liệu thật có những giá trị rất ngắn —
     "0123" (577 người) và "123" (231 người) từ Mautic. Link "123" chỉ rộng
     24,3px dù đã cao đủ 32. Bộ đo trước đây chụp email 146 (74 hàng) nên
     không gặp; đổi sang email 136 (1.202 hàng) là lộ ra 298 chỗ.
     `justify-content:center` để chữ ngắn nằm giữa ô bấm thay vì dính mép. */
  .ptable a[href^="mailto:"], .ptable a[href^="tel:"] {
    display:inline-flex; align-items:center; justify-content:center;
    min-height:32px; min-width:32px; }
  .ptable .idx { color:var(--muc-mo); width:30px; }
  .ptable .clklinks { color:var(--muc-2); font-size:var(--cx-nhan); max-width:420px; }
  .ptable .clklinks div { margin:1px 0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:420px; }
  .empty { color:var(--muc-mo); font-style:italic; padding:12px 2px; }
  .note-box { background:var(--canh-bao-nhat); color:var(--canh-bao); border:1px solid var(--canh-bao); border-radius:var(--bo-goc-khoi); padding:var(--kc-3); font-size:var(--cx-nho); line-height:1.6; }
  .linksum { background:var(--nhan-manh-nhat); border-radius:var(--bo-goc-nut); padding:var(--kc-2) var(--kc-3); margin-bottom:var(--kc-2); font-size:var(--cx-nhan); overflow-wrap:anywhere; }
  .linksum b { color:var(--nhan-manh); }

  /* KHUNG CUỘN của mọi bảng account — nay chỉ còn cấp B (`#cvHot`)
     dùng chung từ 23/08, khi cấp B bỏ lưới hai cột: lưới hai cột không thể có
     tiêu đề cột dính, vì cột phải của nó không có tiêu đề nào ở trên.

     Trần cao chở đúng 12 người rồi cuộn tới hết trong khung — người dùng chốt
     10-15. Con số đo lại 23/08 sau khi hàng chip thành hàng bảng.
     `overflow:auto` cả HAI trục, không chỉ trục dọc: bảng khai `min-width:940px`
     nên ở khổ hẹp nó TRƯỢT NGANG trong khung thay vì bị bóp mất cột.
     Chính khối này là khung neo của `position:sticky` ở thead — đổi `overflow`
     ở đây là gỡ luôn tiêu đề dính.
     Đừng đổi trần cao thành `none` ở bất kỳ khối nào bên dưới — xem test
     `test_danh_sach_account_luon_co_tran_cao`. */
  /* `min()` với 70vh: 852px cố định thì trên laptop 13" (vùng nhìn ~720px)
     khung cuộn CAO HƠN CẢ MÀN HÌNH, và khi đó tiêu đề cột "dính" không bao giờ
     nằm trong tầm nhìn lúc người dùng cuộn TRANG — nó dính vào một khung mà
     đỉnh khung đã trôi lên trên màn. Đo được: khung 852px trên màn 720px,
     `dinhTrongTam:false`. */
  .hotlist { max-height:min(852px, 70vh); overflow:auto; }

  /* BÓNG MÉP PHẢI khi bảng còn cột chưa thấy. Ở 1024px bảng account thừa 115px
     và cột "Lần mở cuối" — cột quyết định của sale — nằm ngoài vùng nhìn mà
     KHÔNG dấu hiệu nào báo là còn nữa; người dùng đọc 8 cột rồi đi.

     Làm bằng bốn lớp `background` chứ không JS: hai lớp `--nen-the` bám mép
     (`background-attachment:local`) che hai lớp bóng bám khung
     (`background-attachment:scroll`). Cuộn hết sang phải thì lớp che trượt tới
     và bóng biến mất — tự tắt đúng lúc không còn gì bên phải, không cần ai
     nghe sự kiện cuộn. */
  .hotlist, .clist-scroll {
    background-image:
      linear-gradient(to right, var(--nen-the), var(--nen-the)),
      linear-gradient(to left,  var(--nen-the), var(--nen-the)),
      linear-gradient(to right, var(--bong-cuon), transparent),
      linear-gradient(to left,  var(--bong-cuon), transparent);
    background-position:left center, right center, left center, right center;
    background-repeat:no-repeat;
    background-size:22px 100%, 22px 100%, 12px 100%, 12px 100%;
    background-attachment:local, local, scroll, scroll; }
  /* ── KHUÔN BẢNG ACCOUNT ────────────────────────────────────────────────
     Trước 23/08 hàng account là flex chở chip xếp phải. Chip chỉ vẽ khi có số
     nên hàng thiếu một chip là cả hàng lệch, và không tiêu đề nào nói "2 link"
     là link gì. Người dùng gọi đúng chỗ đó: "cái có cái không".

     Nay là `table.ptable.acct` — mỗi số liệu MỘT CỘT, ô trống ghi "—", tiêu đề
     DÍNH lên đầu khung cuộn `.hotlist`. */
  table.ptable.acct { width:100%; min-width:940px;
    /* `overflow:visible` là BẮT BUỘC, không phải dọn dẹp. `.ptable` khai
       `overflow:hidden` để bo góc, mà `overflow:hidden` TỰ TẠO khung cuộn —
       nên `position:sticky` của thead bám vào chính cái bảng thay vì vào
       `.hotlist`, tức là không dính gì cả. Im lặng: không exception, không
       log, và lúc trang đứng yên thì không lệch một điểm ảnh nào.
       Viền và bo góc bỏ theo: khung ngoài (`.hotlist`) mới là cái hộp. */
    overflow:visible; border:none; border-radius:0; }
  table.ptable.acct > thead > tr > th { position:sticky; top:0; z-index:2;
    background:var(--nen-the); text-align:right; white-space:nowrap;
    text-transform:uppercase; letter-spacing:.02em;
    border-bottom:2px solid var(--vien-manh); }
  table.ptable.acct > tbody > tr > td { text-align:right; white-space:nowrap;
    font-variant-numeric:tabular-nums; vertical-align:top; }
  table.ptable.acct > thead > tr > th.left,
  table.ptable.acct > tbody > tr > td.left { text-align:left; white-space:normal; }
  /* Cột chở CỤM TỪ ngắn (Vì sao, Cảnh báo): không cho gãy. `white-space:normal`
     làm "click xuyên chiến dịch" gãy thành "click xuyên chiến / dịch" — đọc lên
     như hai thứ khác nhau. Bảng rộng thêm thì `.hotlist` cuộn ngang, đã có sẵn. */
  table.ptable.acct > thead > tr > th.nw,
  table.ptable.acct > tbody > tr > td.nw { white-space:nowrap; }
  /* Cột số thứ tự: mờ và hẹp. Nó để đếm "đang ở hàng thứ mấy", không phải một
     số liệu — đậm bằng các cột kia là nó tranh mắt với số thật. */
  table.ptable.acct > thead > tr > th.stt,
  table.ptable.acct > tbody > tr > td.stt { width:1%; color:var(--muc-mo);
    font-size:var(--cx-micro); }
  /* Ô "Người" chở hai dòng: tên + (công ty ·) địa chỉ thư. `min-width` để tên
     không bị bóp xuống dưới ngưỡng đọc được khi bảng trượt ngang ở khổ hẹp. */
  table.ptable.acct > tbody > tr > td.left .acct-ten { font-weight:600;
    font-size:var(--cx-nho); }
  .acct-phu { color:var(--muc-mo); font-size:var(--cx-micro); }
  .acct-phu a { color:var(--muc-2); text-decoration:none; }
  .acct-phu a:hover { text-decoration:underline; }
  .chip { font-size:var(--cx-micro); padding:2px 7px; border-radius:var(--bo-goc-tron); background:var(--nen-trang); color:var(--muc-2); border:1px solid var(--vien); white-space:nowrap; }
  .chip.c { background:var(--nong-nhat); color:var(--nong); border-color:transparent; font-weight:600; }
  /* Nghi máy quét. GIỮ viền 1px: --canh-bao-nhat là nền nhạt nhất bộ, chỉ
     1,121:1 so với thẻ nên tự nó không tách khỏi nền (luật 2 trong token.css).
     Dấu ⚠ đi kèm trong chữ, không để màu đứng một mình mang nghĩa (luật 4). */
  .chip.canh { background:var(--canh-bao-nhat); color:var(--canh-bao);
    border-color:var(--canh-bao); font-weight:600; }
  /* Chip chiến dịch ĐÃ MỞ ở khu xuyên chuỗi. Không khai `.chip` lần thứ hai —
     dòng 234 đã có; đây chỉ là một bậc của nó. Chip có số = đã mở mấy email,
     chip dấu · = có nhận nhưng chưa mở: hai trạng thái khác nhau, phân biệt
     bằng cả DẤU trong chữ lẫn màu, không chỉ màu. */
  .chip.mo { background:var(--tot-nhat); color:var(--tot); border-color:var(--tot); font-weight:600; }

  /* ---- Panel hồ sơ account (trượt từ phải) ---- */
  .drawer-bg { position:fixed; inset:0; background:var(--phu-nen); z-index:70;
    opacity:0; visibility:hidden; transition:opacity .16s, visibility .16s; }
  .drawer-bg.open { opacity:1; visibility:visible; }
  .drawer { position:fixed; top:0; right:0; height:100%; width:min(460px,94vw); z-index:71;
    background:var(--nen-noi); border-left:1px solid var(--vien); box-shadow:var(--bong-drawer);
    display:flex; flex-direction:column; transform:translateX(100%); visibility:hidden;
    transition:transform .2s ease, visibility .2s; }
  .drawer.open { transform:none; visibility:visible; }
  /* Khoá cuộn trang nền khi ngăn hồ sơ mở. Không khoá thì cuộn hết `.dbody` là
     lượt cuộn nối tiếp xuống trang phía sau, và người dùng mất chỗ đang đọc —
     đóng ngăn ra thì trang đã trôi đi đâu mất. */
  body.khoa-cuon { overflow:hidden; }
  /* `inert` là thuộc tính mới; trình duyệt hiểu nó tự làm mờ và chặn bấm. Khai
     thêm ở đây để không phụ thuộc mặc định của từng trình duyệt. */
  [inert] { pointer-events:none; }
  .dhead { padding:16px 46px 13px 18px; border-bottom:1px solid var(--vien); position:relative; flex:none; }
  .dhead .dname { font-size:var(--cx-tieu-de); font-weight:700; line-height:1.25; }
  .dhead .dco { color:var(--muc-2); font-size:var(--cx-nhan); margin-top:2px; }
  /* Nút "← <tên rổ>" khi hồ sơ được mở TỪ một danh sách nhanh của thẻ KPI.
     Nằm TRÊN tiêu đề chứ không cạnh: `.dhead` đã chừa 46px bên phải cho nút
     đóng, nên đặt cạnh tiêu đề là hai nút tranh nhau một dải hẹp. */
  .dhead .backbtn { margin-bottom:var(--kc-2); }
  .dhead .dcontact { margin-top:var(--kc-2); font-size:var(--cx-nhan); display:flex; flex-wrap:wrap; gap:var(--kc-1) var(--kc-3); }
  /* Link mailto/tel trong ngăn hồ sơ chỉ cao 18px — dưới sàn 32px của dự án.
     Cùng họ lỗi đã chữa cho `.ptable a`, chỉ khác là ngăn hồ sơ
     chưa từng được soat.mjs soi VÀO TRONG nên nó sống sót qua cả hai lần dọn.
     Nay có màn `bao-cao-ho-so-tu-email-*` mở ngăn ra rồi mới đo. */
  .dhead .dcontact a { color:var(--nhan-manh); text-decoration:none;
    display:inline-flex; align-items:center; justify-content:center;
    min-height:32px; min-width:32px; }
  .dhead .dcontact a:hover { text-decoration:underline; }
  .dclose { position:absolute; top:12px; right:12px; width:32px; height:32px; border-radius:var(--bo-goc-nut); line-height:1;
    border:1px solid var(--vien-tuong-tac); background:var(--nen-the); color:var(--muc-2); font-size:var(--cx-tieu-de); cursor:pointer; }
  .dclose:hover { border-color:var(--nhan-manh); color:var(--nhan-manh); }
  .dbody { padding:var(--kc-3) var(--kc-4) var(--kc-6); overflow-y:auto; flex:1; }
  .dstats { display:grid; grid-template-columns:1fr 1fr 1fr; gap:var(--kc-2); }
  @media (max-width:520px){ .dstats { grid-template-columns:1fr 1fr; } }
  .dstat { background:var(--nen-trang); border-radius:var(--bo-goc-khoi); padding:var(--kc-2); }
  .dstat .l { font-size:var(--cx-micro); color:var(--muc-mo); font-weight:600; text-transform:uppercase; letter-spacing:.02em; }
  .dstat .v { font-size:var(--cx-khu); font-weight:700; margin-top:2px; line-height:1.15; font-variant-numeric:tabular-nums; }
  .dstat .v small { font-size:var(--cx-micro); font-weight:500; color:var(--muc-mo); }
  .dsec { font-size:var(--cx-nhan); font-weight:700; text-transform:uppercase; letter-spacing:.03em; color:var(--muc-mo);
    margin:var(--kc-4) 0 var(--kc-2); display:flex; align-items:center; gap:var(--kc-2); }
  .dtl { display:flex; flex-direction:column; gap:var(--kc-2); }
  .dtl-item { border:1px solid var(--vien); border-left:3px solid var(--vien-tuong-tac); border-radius:var(--bo-goc-khoi); padding:var(--kc-2) var(--kc-3); }
  .dtl-item.yes { border-left-color:var(--tot); }
  .dtl-item.clicked { border-left-color:var(--nong); }
  .dtl-item.uns { border-left-color:var(--xau); }
  .dtl-name { font-weight:600; font-size:var(--cx-nho); line-height:1.35; }
  .dtl-meta { color:var(--muc-mo); font-size:var(--cx-micro); margin-top:var(--kc-1); }
  .dtl-tags { display:flex; gap:var(--kc-1); flex-wrap:wrap; margin-top:var(--kc-2); }
  .dtl-links { font-size:var(--cx-micro); margin-top:var(--kc-2); color:var(--muc-2); }
  .dtl-links a { color:var(--nhan-manh); text-decoration:none; } .dtl-links a:hover { text-decoration:underline; }
  /* Cùng cách đã dùng cho `.dlink-row`: cho hàng thành flex để `<a>` bị khối
     hoá, rồi chuyển việc cắt chữ XUỐNG chính `<a>`. Giữ ellipsis mà vẫn đủ
     32px. Đừng đổi `<a>` thành inline-flex ở đây — nó thành phần tử nguyên
     khối, `text-overflow` của div cha hết cắt được, và link drive dài sẽ bị
     xén cứng (soat.mjs đếm ngay vào mục "khối bị xén"). */
  .dtl-links div { display:flex; align-items:center; min-height:32px; margin:1px 0; }
  .dtl-links div a { overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    line-height:32px; min-width:0; }
  .dlink-row { display:flex; justify-content:space-between; gap:var(--kc-2); font-size:var(--cx-nhan); padding:var(--kc-1) 0;
    border-bottom:1px solid var(--vien); }
  .dlink-row:last-child { border-bottom:none; }
  /* Vùng bấm 32px cho link ở đây KHÔNG dùng được `inline-flex` như chỗ khác:
     `<a>` là con của một hàng flex nên nó đã là khối, và chính nhờ vậy
     `text-overflow:ellipsis` mới cắt được tên chiến dịch dài. Đổi sang
     inline-flex thì chữ thành phần tử flex con và dấu "…" biến mất — link
     drive dài 300px sẽ đẩy hàng tràn ngang thay vì cắt.
     Cách giữ được cả hai: nới CHIỀU CAO HÀNG rồi cho line-height bằng đúng
     chiều cao ấy. Con vẫn là khối, ellipsis còn nguyên, hộp đủ 32px. */
  .dlink-row { min-height:32px; }
  .dlink-row span, .dlink-row a { line-height:32px; }
  .dlink-row span { color:var(--muc-mo); white-space:nowrap; }
  .dlink-row a { color:var(--nhan-manh); text-decoration:none; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

  #tt { position:fixed; z-index:80; pointer-events:none; background:var(--nen-noi); color:var(--muc);
    border:1px solid var(--vien); border-radius:var(--bo-goc-nut); padding:var(--kc-2); font-size:var(--cx-nhan); line-height:1.5;
    box-shadow:var(--bong-tt); max-width:260px; opacity:0; transition:opacity .08s; }
  footer { margin-top:var(--kc-6); color:var(--muc-mo); font-size:var(--cx-nhan); text-align:center; }

  /* ── Trang tài khoản: đăng nhập, đổi mật khẩu ─────────────────────────────
     Dùng chung biến màu ở trên để hai trang không như hai sản phẩm khác nhau. */
  /* Canh GIỮA theo chiều dọc thay vì dán lên nửa trên. Đo được ở 1440×900:
     đáy thẻ ở 606px, còn 295px trắng bên dưới mà trang KHÔNG cuộn — thẻ trông
     như bị đẩy lệch lên chứ không như một màn đăng nhập.
     `min-height` trừ đúng chiều cao thanh đầu trang để không đẻ ra thanh cuộn.
     Đệm dọc vẫn giữ để màn thấp (hoặc bàn phím điện thoại đẩy lên) không cắt
     mất thẻ — `justify-content:center` với `min-height` thì nội dung cao hơn
     khung sẽ tràn ra ngoài cả hai đầu, đệm chặn đúng chuyện đó. */
  .khung-hep { max-width:400px; margin:0 auto; padding:var(--kc-10) var(--kc-5);
    /* Trừ CẢ đệm dọc của `.wrap` (var(--kc-5) trên + var(--kc-10) dưới), không
       chỉ thanh đầu trang: `.khung-hep` nằm TRONG `.wrap`, nên bỏ sót 60px đó
       là trang sinh thanh cuộn đúng lúc đang chữa chuyện thừa khoảng trắng.
       Đo được lần đầu: đáy thẻ 916px trên màn 900px. */
    min-height:calc(100vh - var(--cao-dau-trang) - var(--kc-5) - var(--kc-10));
    display:flex; flex-direction:column; justify-content:center; }
  .khung-hep h1 { font-size:var(--cx-khu); margin:0 0 var(--kc-1); }
  .khung-hep .desc { margin:0 0 var(--kc-4); }
  /* ── Trang đăng nhập: mấy NÚT LỚN thay cho ô tên + ô mật khẩu (22/08) ──
     Nút phải to thật, không phải nút thường: đây là thao tác duy nhất của trang
     và người dùng là đội sale mở trên điện thoại giữa lúc đi gặp khách. 64px
     chứ không 32px sàn — sàn 32px là cho nút phụ trong bảng dày chữ. */
  .khung-hep { max-width:440px; }
  .hang-vai { display:flex; flex-direction:column; gap:var(--kc-2); }
  .hang-vai .vai { margin:0; }
  .nut-vai { display:flex; flex-direction:column; align-items:flex-start; gap:2px;
             width:100%; min-height:64px; padding:var(--kc-3) var(--kc-4); cursor:pointer;
             text-align:left; font:inherit; background:var(--nen-the);
             border:1px solid var(--vien-tuong-tac); border-radius:var(--bo-goc-nut);
             transition:background .12s,border-color .12s; }
  .nut-vai:hover { border-color:var(--nhan-manh); background:var(--nhan-manh-nhat); }
  .nut-vai:focus-visible { outline:2px solid var(--nhan-manh); outline-offset:1px; }
  .vai-ten { font-size:var(--cx-nho); font-weight:700; color:var(--muc); }
  .vai-phu { font-size:var(--cx-nhan); color:var(--muc-mo); }
  /* Ô mã PIN nằm NGAY TRONG khối của nút Admin: hỏi mã ở một chỗ khác thì người
     dùng phải tự tìm xem gõ vào đâu. */
  .vai-pin { display:flex; flex-wrap:wrap; align-items:center; gap:var(--kc-2);
             padding:var(--kc-3) var(--kc-4); border-radius:var(--bo-goc-nut);
             border:1px solid var(--nhan-manh); background:var(--nhan-manh-nhat); }
  .vai-pin .vai-ten { flex:1 0 100%; }
  .vai-nhac { font-size:var(--cx-nhan); color:var(--muc-2); }
  /* --vien-tuong-tac chứ KHÔNG phải --vien: luật 3 trong token.css, và trang
     đăng nhập chỉ nạp token.css + bao_cao.css — KHÔNG nạp admin.css, nên quy
     tắc ô nhập đúng bên đó không với tới đây được. */
  .vai-pin input { flex:1; min-width:96px; min-height:42px; font:inherit;
                   letter-spacing:.32em; text-align:center; padding:var(--kc-2);
                   border-radius:var(--bo-goc-nut); border:1px solid var(--vien-tuong-tac);
                   background:var(--nen-the); color:var(--muc); }
  .vai-pin .nut-chinh { flex:0 0 auto; padding:0 var(--kc-5); }

  /* nút cao 42px: vùng bấm ≥30px như đã chốt ở nguyên tắc giao diện */
  .nut-chinh { min-height:42px; border:none; border-radius:var(--bo-goc-nut); background:var(--nhan-manh);
    color:var(--tren-dac); font:inherit; font-weight:600; cursor:pointer; }
  .nut-chinh:hover { filter:brightness(1.08); }
  /* Lỗi có cả viền trái đậm + chữ, không chỉ dựa vào màu */
  .hop-loi { border-radius:var(--bo-goc-nut); padding:var(--kc-2) var(--kc-3); font-size:var(--cx-nho);
    background:var(--xau-nhat); color:var(--xau); border-left:3px solid var(--xau); }
  .lien-ket-phu { margin-top:var(--kc-4); font-size:var(--cx-nhan); color:var(--muc-mo); text-align:center; }
  .lien-ket-phu a { color:var(--nhan-manh); }
  /* ── Bộ chọn khoảng thời gian ───────────────────────────────────────────
     Chọn xong là tải lại trang với ?khoang=... — cố ý dùng địa chỉ URL chứ
     không lọc ở trình duyệt: số liệu phải tính lại bằng SQL, mà làm vậy thì
     gửi được link "90 ngày qua" cho người khác. */

  /* Dấu chỉ cột đang sắp. Trước đây JS dựng `.mui-ten` mà KHÔNG file CSS nào
     khai, nên mũi tên ăn nguyên màu --muc-mo cỡ 11px của tiêu đề cột và không
     nổi hơn chính nhãn cột — tức nó không nói được cột nào đang sắp. */
  .mui-ten { color:var(--nhan-manh); font-weight:700; margin-left:2px; }

  /* Hộp một dòng trạng thái ("đang tải…", "không có kết quả", hộp lỗi). Có lớp
     thật thay vì `style="padding:16px"` rải trong JS: inline style thắng mọi
     lớp nên mỗi chỗ đó là một ngoại lệ không sửa được từ token. */
  .hop-trong { padding:var(--kc-4); }

  /* Khối lệnh copy-paste trong ba thẻ ca-rỗng của bao_cao.html. Trước là
     `style="...padding:12px;border-radius:8px..."` viết thẳng trong template —
     8px là bậc của NÚT, mà đây là khối lồng (token.css:126). */
  .khoi-lenh { background:var(--nen-chim); padding:var(--kc-3);
    border-radius:var(--bo-goc-khoi); overflow-x:auto; }

  /* Mũi tên tăng/giảm trên thẻ KPI. Kèm ▲▼ chứ không chỉ tô màu. */

  /* ── Bo góc + cắt tràn ────────────────────────────────────────────────
     Viết HAI dòng, đúng thứ tự này. overflow:clip chỉ có từ Chrome 90 /
     Safari 16 / Firefox 81; trình duyệt cũ bỏ nguyên dòng `clip` và rơi về
     dòng `hidden` phía trên. Gộp một dòng `clip` là mất cắt tràn trên
     Safari 15 — bản còn nhiều trên máy công ty. */
  .card { overflow:hidden; overflow:clip; }

  /* KHÔNG đặt overflow lên .clist-scroll: table.clist th đang position:sticky
     và .clist-scroll chính là khung cuộn của nó. Đặt overflow ở đây là giết
     tiêu đề cột dính, im lặng, không báo lỗi gì. */

  /* Ngăn kéo phải có VIỀN chứ không chỉ có bóng. Chế độ tương phản cao của
     Windows ép box-shadow về none — bỏ viền thì panel trắng tan vào nền
     trắng. Đo thêm: lớp phủ trên nền trang ra #9DA5B6, panel trắng trên đó
     chỉ 2,47:1, không đủ tự tách. */
  .drawer { border-left:1px solid var(--vien); }

  @media (forced-colors:active){
    .drawer, .hop, #tt, .card, .the, .kpi { border:1px solid CanvasText; }
  }

  /* ── Vùng bấm và trạng thái nói bằng HÌNH ─────────────────────────────
     Hai nguyên tắc đã chốt với người dùng, trước đây khai trong tài liệu
     nhưng code vi phạm ở nhiều chỗ:
       · vùng bấm >= 32x32
       · trạng thái nói bằng hình + màu, không chỉ màu
     Đo được: hai màu chuỗi sát nhau nhất khi mù màu đỏ-lục chỉ cách ΔE 8,7.
     Màu một mình KHÔNG đủ để phân biệt. */

  /* Dấu "i" giữ cỡ nhìn 16px nhưng nới vùng bấm ra 32px bằng lớp phủ vô hình.
     Phóng to phần nhìn thấy sẽ làm nó tranh chú ý với số liệu. */
  /* z-index cần thiết: không có nó thì phần tử vẽ SAU trong DOM che mất lớp
     phủ ở góc dưới phải, vùng bấm chỉ nới được một nửa. Đã đo bằng
     elementFromPoint mới thấy. */
  .info { position:relative; z-index:1; }
  /* inset tính từ PADDING BOX chứ không phải border box. .info có viền 1px
     nên padding box chỉ 14x14 — inset:-8px ra 30px, thiếu đúng 2px so với
     ngưỡng 32px đã chốt. Đo bằng elementFromPoint mới thấy: bấm lệch 14px
     trúng, 15px trượt. -9px mới ra đủ 32px. */
  .info::after { content:''; position:absolute; inset:-9px; }

  .cn-btn { min-height:32px; }


  /* ══════════════════════════════════════════════════════════════════════
     NÚT PHỤ — MỘT công thức, dùng ở cả bốn trang

     Trước đây có BẢY bản sao gần giống nhau, đo trên app thật:
       .nut      cao 34 · đệm 6/12 · chữ 13 · khe 6
       .tab      cao 32 · đệm 6/12 · chữ 12 · khe 4
       .btn-tai  cao 32 · đệm 5/10 · chữ 12 · khe 4
       .cong-tac cao 32 · đệm 4/10 · chữ 13 · khe 4
       .so-sanh  cao 34 · đệm 6/13 · chữ 13 · khe 7
       .sortsel  cao 33 · đệm 7/10 · chữ 12
       .backbtn  đệm 7/12 · chữ 13 · KHÔNG có sàn chiều cao — nó đạt 32px chỉ
                 nhờ line-height, tức sàn vùng bấm là chuyện tình cờ
     Đặt cạnh nhau (ba `.tab` cạnh hai `.btn-tai`; `.nut` cạnh `.cong-tac`)
     chúng lệch 2px chiều cao và 1px cỡ chữ — đủ để hàng nút trông gợn sóng.

     Khai ở bao_cao.css vì file này nạp ở CẢ BỐN template; admin.css chỉ nạp ở
     một trang. Mỗi thành phần bên dưới chỉ còn khai phần KHÁC BIỆT THẬT.
     ══════════════════════════════════════════════════════════════════════ */
  .nut, .tab, .btn-tai, .backbtn, .sortsel, .cong-tac {
    display:inline-flex; align-items:center; justify-content:center;
    /* Đệm dọc 0, chiều cao do `min-height` quyết định. Có đệm dọc thì chiều
       cao = max(min-height, chữ + đệm) và nó nhảy theo cỡ chữ — 6px cho ra
       34px, 8px cho ra 35,5px. Để 0 thì mọi nút cao ĐÚNG 34px, và con số đó
       nằm ở một chỗ duy nhất. `align-items:center` canh chữ giữa. */
    gap:var(--kc-1); min-height:34px; padding:0 var(--kc-3);
    border:1px solid var(--vien-tuong-tac); border-radius:var(--bo-goc-nut);
    background:var(--nen-the); color:var(--muc-2);
    font:inherit; font-size:var(--cx-nho); cursor:pointer; }
  .nut:hover, .tab:hover, .btn-tai:hover, .backbtn:hover,
  .cong-tac:hover {
    border-color:var(--nhan-manh); color:var(--nhan-manh); }

  /* ══════════════════════════════════════════════════════════════════════
     Ô NHẬP CHỮ — MỘT công thức, dùng ở cả bốn trang

     Trước đây trang báo cáo cao 37,5px đệm 8/12 và KHÔNG có sàn chiều cao,
     trang quản trị cao 35,5px đệm 7/9 có sàn 34px. Chữ trong hai ô không nằm
     cùng khoảng cách so với mép.

     DANH SÁCH ĐEN, không phải danh sách trắng. Lý do (giữ nguyên từ admin.css):
     liệt kê kiểu ô được style thì kiểu mới thêm về sau bỏ sót TRONG IM LẶNG —
     Giai đoạn 5 thêm ba ô `time` / `number` / `date` cho khu Thông báo và cả ba
     rơi ra ngoài, thành ô mặc định trình duyệt cao 21–24px, dưới sàn vùng bấm
     30px. Không lỗi nào báo. Chừa ra đúng những kiểu KHÔNG phải ô nhập chữ:
     hidden không hiện · submit là nút, phải mang `.nut` · checkbox/radio có
     hình riêng của hệ điều hành.

     `:where()` bọc phần `:not()`: KHÔNG phải cho gọn. Bốn `:not([type=...])`
     cộng vào độ đặc hiệu (0,4,1) — cao hơn cả `.vai-pin input` (0,1,1), nên
     nếu để trần thì ô mã PIN của trang đăng nhập mất `min-height:42px` của nó
     mà không ai báo. `:where()` đóng góp ĐỘ ĐẶC HIỆU BẰNG KHÔNG, nên quy tắc
     này thành nền thật: mọi lớp cụ thể đều đè lên được. Safari 14+.
     ══════════════════════════════════════════════════════════════════════ */
  input:where(:not([type=hidden]):not([type=submit]):not([type=checkbox]):not([type=radio])),
  select, textarea {
    font:inherit; font-size:var(--cx-nho); padding:var(--kc-2) var(--kc-3);
    border:1px solid var(--vien-tuong-tac); border-radius:var(--bo-goc-nut);
    background:var(--nen-the); color:var(--muc); min-height:34px; max-width:100%; }
  input:focus, select:focus, textarea:focus {
    outline:2px solid var(--nhan-manh); outline-offset:1px;
    border-color:var(--nhan-manh); }

  /* Viên tỉ lệ: ba mức hiệu quả có ba HÌNH khác nhau, không chỉ ba màu.
     Tam giác lên = tốt · gạch ngang = giữa · tam giác xuống = kém. */
  .pill { display:inline-flex; align-items:center; gap:var(--kc-1); }
  .pill.good::before, .pill.warn::before, .pill.bad::before {
    content:''; flex:none; width:0; height:0; }
  .pill.good::before {
    border-left:4px solid transparent; border-right:4px solid transparent;
    border-bottom:6px solid currentColor; }
  .pill.bad::before {
    border-left:4px solid transparent; border-right:4px solid transparent;
    border-top:6px solid currentColor; }
  .pill.warn::before { width:8px; height:2px; background:currentColor; }

  /* ══════════════════════════════════════════════════════════════════════
     LƯỚI BENTO — trang báo cáo

     Ô to dành cho thứ người dùng nhìn đầu tiên. Tỉ lệ ô neo (bảng chiến
     dịch) so với ô phụ (khách nóng) là 2:1 — bố cục cũ 1,3:1 nên mắt không
     đọc ra ô nào quan trọng hơn. Đây là thay đổi tác động lớn nhất tới cảm
     giác "bento".

     minmax(0,1fr) chứ KHÔNG phải 1fr: min-width mặc định của ô grid là
     min-content, thiếu nó thì bảng nhiều cột đẩy toác cả lưới. Đây là lỗi
     CSS thật, không phải sở thích.

     KHÔNG dùng grid-auto-flow:dense, KHÔNG đảo thứ tự ô. Thứ tự vùng
     dai→kpi→dong→chien→khach→nhan đúng bằng thứ tự DOM nên đi bằng
     bàn phím và trình đọc màn hình không bị ngược.

     14/08: hai ô biểu đồ cũ (hieu · thoi) đã bỏ, thay bằng MỘT ô dòng thời
     gian trải hết bề ngang. Bảng chiến dịch cũng trải hết bề ngang chứ không
     còn 7/12: nó lên 8 cột (thêm Người nhận · Người click · Hoạt động), và
     8 cột đó cần ~860px — nhồi vào ô 7/12 (692px) là đẻ ra cuộn ngang ở
     chính bảng người dùng đọc nhiều nhất. Danh sách khách xuống hàng riêng,
     đúng như bố cục `dang-bung` vẫn dùng.
     ══════════════════════════════════════════════════════════════════════ */
  .bento {
    display:grid;
    grid-template-columns:repeat(12, minmax(0,1fr));
    gap:var(--khe);
    grid-template-areas:
      "kpi   kpi   kpi   kpi   kpi   kpi   kpi   kpi   kpi   kpi   kpi   kpi"
      "dong  dong  dong  dong  dong  dong  dong  dong  dong  dong  dong  dong"
      "chien chien chien chien chien chien chien chien chien chien chien chien";
    /* Vùng "roi" ĐÃ BỎ 26/08 cùng khu "Đã bấm rồi huỷ đăng ký"; vùng "khach"
       ĐÃ BỎ 30/08 cùng thẻ "Account cần quan tâm"; vùng "dai" ĐÃ BỎ 31/08 cùng
       cụm chọn khoảng thời gian. Một vùng khai mà không ô nào bám vào là dòng
       lưới chết, và `test_moi_vung_luoi_deu_co_o_bam_vao` bắt đúng chuyện đó. */
    grid-template-rows:auto auto minmax(var(--cao-bang),auto);
  }
  /* Nuốt mọi margin dọc: thành ô grid thì margin CỘNG THÊM vào gap */
  .bento > * { margin-block:0; }

  #kpis          { grid-area:kpi; }
  #cardGantt     { grid-area:dong; }
  #cardCampaigns { grid-area:chien; }
  /* Vùng `nhan` (#cardNhan — "Xem xuyên chuỗi") đã bỏ 24/08 và `khach`
     (#cardHot — "Account cần quan tâm") đã bỏ 30/08, cùng cả thẻ. Ô không
     khai `grid-area` thì trình duyệt tự xếp chỗ cho nó NGOÀI lưới đã vẽ,
     không lỗi nào báo — đã xảy ra thật với #cardHotXuyen. */

  /* Đệm tăng theo cỡ ô: hai ô neo rộng hơn thì thở hơn */
  .bento > .card { display:flex; flex-direction:column; min-width:0;
                   padding:var(--kc-5); }
  #cardGantt, #cardCampaigns { padding:var(--kc-6); }

  /* Ô neo phải NỞ ĐƯỢC. Thay sạch cụm hack height:clamp cũ.
     CHỈ `.clist-scroll` — bảng chiến dịch là ô neo của lưới nên phải nở cho kín
     ô. `.hotlist` thì KHÔNG: nhả trần cao của nó ra là danh sách dài theo nội
     dung, mới 15 hàng đã cao 1271px và `Tất cả` thì khoảng 21.000px. Đúng chỗ
     người dùng gọi là "dài thòn" 21/08. Hai khối, hai luật. */
  .clist-scroll { flex:1 1 auto; min-height:0; overflow:auto;
                  max-height:none; }
  .hotlist { flex:0 1 auto; min-height:0; }

  /* Bung chuỗi ra thì bảng thoát khỏi khung cao cố định. Bố cục lưới KHÔNG đổi
     nữa (mọi ô đã trải hết bề ngang), nên chỉ còn một việc: nhả khung cuộn. */
  .bento.dang-bung .clist-scroll { overflow:visible; flex:none; }

  /* ── 760–1179px: 6 cột ───────────────────────────────────────────────── */
  @media (max-width:1179px){
    .bento {
      grid-template-columns:repeat(6, minmax(0,1fr));
      grid-template-areas:
        "kpi   kpi   kpi   kpi   kpi   kpi"
        "dong  dong  dong  dong  dong  dong"
        "chien chien chien chien chien chien";
      grid-template-rows:auto auto auto;
    }
    .bento > .card, #cardGantt, #cardCampaigns { padding:var(--kc-5); }
  }

  /* ── dưới 760px: một cột, đúng thứ tự DOM ────────────────────────────── */
  @media (max-width:759px){
    .bento {
      grid-template-columns:minmax(0,1fr);
      grid-template-areas:"kpi" "dong" "chien";
      grid-template-rows:auto;
      gap:var(--kc-3);
    }
    .bento > .card, #cardGantt, #cardCampaigns { padding:var(--kc-4); }

    /* Thẻ KPI xếp một cột thì không còn gì để canh hàng — bỏ dòng phụ rỗng,
       lấy lại 18px trắng mỗi thẻ, sáu thẻ là hơn 100px cuộn. */
    #viewCampaign .kpi .sub:empty { display:none; }
  }

  /* Lớp cỡ biểu tượng — TRÙNG TÊN và trùng giá trị với admin.css để một icon
     dùng ở hai trang trông y hệt nhau. */
  .ic { width:18px; height:18px; fill:none; stroke:currentColor; stroke-width:1.7;
        stroke-linecap:round; stroke-linejoin:round; flex:none; vertical-align:-3px; }
  .ic-nho { width:14px; height:14px; vertical-align:-2px; }
  .linksum, .note-box { display:flex; align-items:flex-start; gap:var(--kc-2); }
  .dclose { display:inline-flex; align-items:center; justify-content:center; }

  /* ══════════════════════════════════════════════════════════════════════
     THANH ĐẦU TRANG DÙNG CHUNG — cả bốn trang

     Trước đây trang báo cáo có <header> phẳng còn trang quản trị có menu
     dọc, đi qua lại thấy như hai sản phẩm. Nay một dải duy nhất, cùng vị
     trí, cùng thứ tự ở cả bốn trang.

     Nền ĐẶC chứ không backdrop-filter: (1) thiếu -webkit- thì Safari 15-17
     rơi vào nhánh không có hiệu ứng, tức phần lớn máy Mac trong công ty;
     (2) dải chạy suốt bề ngang buộc trình duyệt lấy mẫu và làm mờ lại nền
     mỗi frame cuộn, mà thứ cuộn bên dưới là bảng 40 dòng.
     ══════════════════════════════════════════════════════════════════════ */
  /* Dải NỀN chạy suốt bề ngang, nhưng NỘI DUNG trong dải bị giữ trong cùng một
     trần với nội dung trang — phát hiện #B1 của lượt soi 25/08.

     Trước: dải trần 1.920px trong khi thân trang bị cắt và canh giữa, nên ở màn
     1920 mép phải của hai thứ lệch **344px** và logo nằm tít ngoài lề trong khi
     chữ bắt đầu ở giữa trang. Đo được cả hai trang.

     `padding-inline: max(--kc-6, (100% - 1320px)/2)` làm cả hai việc bằng một
     dòng: màn hẹp thì vẫn là đệm --kc-6 như cũ, màn rộng thì đệm tự nở ra đúng
     bằng phần lề mà trần 1320px để lại. 1320 là trần của `.wrap` (trang báo
     cáo) và nay cũng là trần của `.admin` — ba thứ cùng một mốc, nên logo thẳng
     hàng với chữ ở CẢ HAI trang.

     Không bọc thêm một <div> bên trong: nền phải chạy hết bề ngang, mà thanh
     này còn `position:sticky` — thêm một tầng nữa là thêm một chỗ để lệch. */
  /* BA CỘT: điều hướng trái · logo GIỮA · người dùng phải (26/08, người dùng
     yêu cầu). Lưới chứ không flex, vì "ở giữa" phải là giữa THẬT — hai cột bên
     chia đều phần dư thì cột giữa mới rơi đúng tâm thanh.

     `minmax(0,1fr)` chứ KHÔNG phải `1fr` trần: `1fr` trần có min là `auto`, nên
     cột nào nội dung rộng hơn sẽ nở quá phần chia của nó và đẩy logo lệch tâm —
     mà hai bên gần như chắc chắn không bằng nhau (trái hai nút có chữ, phải là
     tên đăng nhập + chip nhóm + nút thoát).

     Đệm và chiều cao KHÔNG ĐƯỢC ĐỔI. `--cao-dau-trang` (token.css) tính từ
     `--kc-2` hai phía + `--cao-dau-hieu` + 1px viền = 49px, và menu dọc trang
     quản trị neo vào con số đó (`.admin-nav{top:...}`), còn `.admin{min-height:
     100vh}` đo từ dưới thanh. Thanh cao lên là cả TÁM khu quản trị có thanh cuộn
     dọc thừa — đúng cặp lỗi #B2/#B3 đã tốn hai lượt đo để chữa. */
  .dau-trang { position:sticky; top:0; z-index:30;
    display:grid; grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);
    align-items:center; gap:var(--kc-4);
    padding:var(--kc-2) max(var(--kc-6), calc((100% - 1320px) / 2));
    background:var(--nen-the); border-bottom:1px solid var(--vien); }

  /* `min-height` đi qua token: chính con số này là một trong ba thứ quyết
     định chiều cao thanh đầu trang, mà `--cao-dau-trang` (token.css) tính từ
     đó ra để menu dọc trang quản trị neo đúng chỗ. */
  /* GHIM vào cột 2, không để lưới tự xếp. Trang đăng nhập không dựng thanh điều
     hướng lẫn khối người dùng, nên tự xếp sẽ đặt logo vào cột ĐẦU và nó dạt hẳn
     sang trái ở đúng cái trang chỉ có mỗi nó. */
  .dau-hieu { grid-column:2; justify-self:center;
    display:inline-flex; align-items:center; gap:var(--kc-2);
    min-height:var(--cao-dau-hieu); font-weight:700; font-size:var(--cx-nho);
    color:var(--muc); text-decoration:none; white-space:nowrap; }
  .dau-hieu:hover { color:var(--nhan-manh); }

  .dau-tab { grid-column:1; justify-self:start;
    display:flex; gap:var(--kc-1); }
  .dau-tab a { display:inline-flex; align-items:center; gap:var(--kc-2);
    min-height:32px; padding:var(--kc-1) var(--kc-3);
    border-radius:var(--bo-goc-nut); font-size:var(--cx-nho);
    color:var(--muc-2); text-decoration:none; white-space:nowrap; }
  .dau-tab a:hover { background:var(--hang-hover); color:var(--muc); }
  /* Trang đang mở: nền đậm + chữ đậm + vạch dưới. Ba dấu chứ không chỉ màu. */
  .dau-tab a[aria-current="page"] { background:var(--nhan-manh-nhat);
    color:var(--nhan-manh); font-weight:650; box-shadow:inset 0 -2px 0 var(--nhan-manh); }

  /* `margin-left:auto` ĐÃ BỎ (26/08). Nó là thứ đẩy khối này sang phải hồi
     thanh còn là flex; nay lưới ba cột làm việc đó bằng `justify-self:end`, và
     giữ lại cả hai là để hai luật cùng cãi nhau về một vị trí. */
  .dau-nguoi { grid-column:3; justify-self:end;
    display:flex; align-items:center; gap:var(--kc-2); min-width:0; }
  .dau-ten { font-size:var(--cx-nhan); color:var(--muc-2); white-space:nowrap;
    overflow:hidden; text-overflow:ellipsis; }

  /* .nut-ic khai trong admin.css — trang báo cáo giờ cũng dùng, nên phải có
     ở đây. Cùng giá trị, đừng để lệch giữa hai file. */
  .nut-ic { width:32px; height:32px; padding:0; display:inline-flex;
    align-items:center; justify-content:center; border:1px solid var(--vien-tuong-tac);
    border-radius:var(--bo-goc-nut); background:var(--nen-the); color:var(--muc-2);
    cursor:pointer; flex:none; text-decoration:none; }
  .nut-ic:hover { border-color:var(--nhan-manh); color:var(--nhan-manh);
    background:var(--nhan-manh-nhat); }

  .khu-tieu-de { margin-bottom:var(--kc-4); }
  /* Khe dưới hộp báo lỗi link. Trước là `style="margin-bottom:var(--khe)"` viết
     thẳng trong template — inline style thắng MỌI lớp nên token không sửa được
     nó, và không quy tắc nuốt margin nào với tới. Nó nằm NGOÀI lưới bento (cố
     ý) trong `.wrap` vốn display:block không gap, nên margin này là thứ DUY
     NHẤT giữ nó khỏi dính vào #viewMain — đúng kiểu vá đã đẻ ra "thẻ dính
     nhau", chỉ khác là ở đây cái vá còn nguyên nên chưa dính.
     `--kc-4` = `--khe` = 16px nên KHÔNG đổi một pixel nào; chốt luôn rằng khe
     cấp TRANG đọc từ thang `--kc-*`, còn `--khe` là gap của lưới. */
  #loiLink { margin-bottom:var(--kc-4); }
  .khu-tieu-de h1 { font-size:var(--cx-khu); margin:0 0 var(--kc-1); letter-spacing:-.01em; }
  .khu-tieu-de p { margin:0; color:var(--muc-2); font-size:var(--cx-nhan); }

  @media (max-width:759px){
    .dau-trang { padding:var(--kc-2) var(--kc-3); gap:var(--kc-2); }
    /* `.dau-hieu` KHÔNG còn chữ để ẩn từ 26/08 — cụm chữ đã chuyển vào nút
       "Báo cáo Email". Logo rộng 96px nên không cần chặn dưới 32px. */
    .dau-tab a span { display:none; }    /* tab còn icon, vẫn đủ 32px và có aria-label */
    .dau-ten { display:none; }
  }

  /* ── Trang chi tiết một chiến dịch ────────────────────────────────────
     Màn này CỐ Ý không bento hoá: luồng của nó là tuần tự
     chiến dịch → email → link → khách. Ép lưới bento (12 cột +
     grid-template-areas) lên nội dung tuần tự là lỗi có tên.

     NHƯNG "không bento" KHÔNG có nghĩa "không có bố cục" — và chính chỗ hiểu
     nhầm đó đẻ ra lỗi "thẻ dính nhau" ngày 26/08. Màn này vẫn cần một khối cha
     phát khoảng cách; nó chỉ không cần LƯỚI CÓ VÙNG. Xem khối ngay dưới.

     (Đoạn cũ ở đây hướng dẫn "trả lại quy tắc .hero" để xếp hai thẻ biểu đồ
     cạnh nhau. ĐÃ XOÁ 26/08 vì nó thành một chỉ thị dựng lại đúng con bug vừa
     chữa: `.hero` mang `margin-bottom`, đặt cạnh một khối đã có `gap` là khe
     ĐÔI. Hai thẻ biểu đồ đó đã bỏ 25/08 và grep toàn repo không còn nơi nào
     sinh ra `.hero`.) */
  /* ══════════════════════════════════════════════════════════════════════
     BỐ CỤC TRANG CHIẾN DỊCH — khoảng cách do CHA quyết, không vá từng thẻ

     Người dùng báo 26/08: "mấy cái thẻ bị dính dính với nhau". Đo trên trình
     duyệt thật, khe giữa các thẻ của trang này là 12 · 16 · 0 · 16 · 0 — hai
     cặp thẻ CHẠM NHAU, viền dính liền, nhìn như một thẻ bị cắt đôi.

     Nguyên nhân không phải thiếu một con số mà là thiếu một HỆ THỐNG. Trang
     chính là `.bento` (grid + gap) nên khe của nó đều 16px và thẻ mới thêm vào
     tự có khoảng cách. Trang này là `display:block` trần, khoảng cách đến từ
     hai quy tắc vá THEO ID:

         #viewCampaign .kpis  { margin-bottom:var(--khe) }
         #viewCampaign #cvHot { margin-bottom:var(--khe) }

     Thẻ nào không được ai vá thì dính. Nên THÊM MỘT THẺ MỚI VÀO TRANG LÀ NÓ
     DÍNH, IM LẶNG — đúng chuyện vừa xảy ra khi thêm thẻ `#cvBot` cùng ngày.

     Đối chứng đáng nhớ: trang quản trị KHÔNG dính, dù cũng dùng margin. Khác
     biệt duy nhất là quy tắc của nó nhắm vào CLASS (`.the`), nên thẻ mới tự có
     khoảng cách. Vá theo ID thì mỗi thẻ mới là một lần phải nhớ.

     `minmax(0,1fr)` chứ không `1fr` trần: `1fr` có min là `auto` nên một bảng
     rộng bên trong sẽ nong cột ra và đẻ cuộn ngang. Cùng cái bẫy đã làm lệch
     tâm logo thanh đầu trang 26/08.

     `.hidden{display:none!important}` khai ở đầu file nên `display:grid` ở đây
     KHÔNG làm trang chiến dịch hết ẩn được — bẫy đó đã rào từ trước.
     ══════════════════════════════════════════════════════════════════════ */
  #viewCampaign { display:grid; grid-template-columns:minmax(0,1fr);
                  gap:var(--khe); align-content:start; }
  /* Nuốt mọi margin dọc, cùng lối `.bento > *`: thành ô grid thì margin CỘNG
     THÊM vào gap, nên `.crumb` (margin-bottom 12px) sẽ cho khe 28px trong khi
     mọi khe khác là 16px. `.crumb` chỉ dùng ở trang này nên nuốt nó là an toàn. */
  #viewCampaign > * { margin-block:0; }

  /* Màn hẹp co GIỐNG trang chính. Trước đây trang này không khai gì ở đây nên
     nó giữ nguyên khe và đệm của màn rộng: đo thật ở 390px, trang chính khe
     12px / đệm 16px, trang chiến dịch khe 16px / đệm 20px — một trang co, một
     trang không, trên cùng một điện thoại. */
  @media (max-width:759px){
    #viewCampaign { gap:var(--kc-3); }
    #viewCampaign > .card { padding:var(--kc-4); }
  }

  /* Quy tắc `#viewCampaign .hero` ĐÃ XOÁ 26/08. Hai thẻ biểu đồ mà nó xếp cạnh
     nhau đã bỏ ngày 25/08, và grep toàn repo xác nhận không JS hay template nào
     còn sinh ra `.hero` (nó chỉ còn trong `dashboard.html` cũ, file đó có CSS
     inline riêng). Giữ lại một quy tắc chết mang `margin-bottom:var(--khe)`
     ngay cạnh một khối vừa chuyển sang gap là để sẵn một khe ĐÔI cho người sau. */
  /* Hàng sáu thẻ này đọc theo chiều NGANG — mắt quét một dãy số, không đọc từng
     thẻ một. Nên số phải nằm cùng một độ cao và cùng một trục dọc; lệch vài
     pixel là cả dãy gợn sóng. Dòng phụ luôn được vẽ (rỗng thì là khoảng trắng)
     nên `margin-top:auto` của `.value` đẩy mọi thẻ xuống bằng nhau. */
  #viewCampaign .kpi { text-align:center; }
  /* BỎ chỗ chừa cho icon ⓘ — thẻ ở trang chiến dịch KHÔNG có icon đó (chỉ thẻ
     trang chính mới vẽ, xem `renderKpis`). Quy tắc chung `.kpi .label` chừa
     `padding-right:var(--kc-6)` cho nó, mà ở đây chữ lại canh GIỮA, nên 24px
     chừa thừa đẩy mọi nhãn lệch trái 12px so với tâm thẻ. Trên dãy bảy thẻ
     cạnh nhau, lệch đều một bên đọc ra thành "không canh chuẩn" — đúng chữ
     người dùng dùng ngày 31/08. */
  #viewCampaign .kpi .label { padding-right:0; }
  #viewCampaign .kpi .value { font-variant-numeric:tabular-nums; }
  /* Dòng phụ rỗng vẫn chiếm chỗ để sáu thẻ KPI thẳng hàng khi chúng đứng
     CẠNH nhau. Từ 640px xuống thì chúng xếp một cột, không còn gì để canh
     hàng, nên 18px trắng đó chỉ là 18px trắng — xem quy tắc gỡ bỏ ở khối
     màn hẹp phía dưới. */
  #viewCampaign .kpi .sub:empty::before { content:'\00a0'; }

  /* Hàng chuỗi chưa gửi email nào: KHÔNG con trỏ tay, KHÔNG đổi nền khi rê
     chuột, và nói thẳng lý do. Trước đây nó trông y hệt hàng bấm được nhưng
     bấm vào không có gì xảy ra. */
  tr.chrow.chua-mo-duoc { cursor:default; }
  tr.chrow.chua-mo-duoc:hover td { background:inherit; }
  .cn .go.chua { color:var(--muc-mo); font-style:italic; }

  /* ══════════════════════════════════════════════════════════════════════
     CHIP ĐỘ TƯƠI — bốn bậc, HÌNH + MÀU

     Luật 4 của token.css: hai màu chuỗi sát nhau nhất khi mù màu đỏ-lục chỉ
     cách ΔE 8,7, nên MỖI BẬC BẮT BUỘC CÓ HÌNH RIÊNG. Bỏ `.dau` đi là chip mất
     nghĩa với người mù màu, mà không test nào báo.
     Người dùng gọi bộ này là "xanh đỏ vàng tím" — đây là bản dịch sang hệ token.
     ══════════════════════════════════════════════════════════════════════ */
  .tuoi { display:inline-flex; align-items:center; gap:var(--kc-1); padding:2px 9px;
    border-radius:var(--bo-goc-tron); font-size:var(--cx-nhan); font-weight:600;
    border:1px solid transparent; white-space:nowrap; }
  .tuoi .dau { font-size:10px; line-height:1; }
  .tuoi.hom-nay { background:var(--tot-nhat); color:var(--tot); border-color:var(--tot); }
  /* --chuoi-2-nhat chỉ 1,121:1 so với thẻ — NHẠT NHẤT BỘ, viền 1px là bắt buộc. */
  .tuoi.tuan { background:var(--chuoi-2-nhat); color:var(--canh-bao); border-color:var(--canh-bao); }
  .tuoi.thang { background:var(--chuoi-5-nhat); color:var(--chuoi-5); border-color:var(--chuoi-5); }
  .tuoi.im { background:var(--nen-chim); color:var(--muc-mo); border-color:var(--vien-manh); }
  /* Cờ trạng thái đặt tay chọi với dữ liệu gửi. */
  .tuoi.lech { background:var(--xau-nhat); color:var(--xau); border-color:var(--xau); }
  /* Dấu tăng từ lượt đồng bộ trước. */
  .motang { font-size:var(--cx-micro); color:var(--tot); font-weight:700; margin-left:var(--kc-1); }

  /* ── CHÚ GIẢI độ tươi, dưới bảng chiến dịch ──────────────────────────
     Lớp `.chugiai` do bao_cao.js dựng ra nhưng CSS KHÔNG khai luật nào cho nó,
     từ lúc khu này ra đời tới 30/08. Không lỗi nào báo — một div không có luật
     vẫn là một div hợp lệ. Đo lúc phát hiện: khe giữa năm cặp là 0px ở CẢ BỐN
     chỗ nối, nên năm cặp dính thành một dải chữ liền, chữ lấy màu mực đậm của
     thân trang thay vì màu chú giải, và margin-top 0 làm nó dán vào đáy bảng.
     Người dùng chỉ ra bằng ảnh chụp.

     Cùng họ với `.mui-ten` — JS dựng lớp, CSS không khai — và cũng cùng cách
     chữa: khai luật, đừng vá bằng cách nhồi khoảng trắng vào chuỗi HTML.

     HAI khe, hai nghĩa, và đó là toàn bộ việc của khối này:
        trong một cặp (chip ↔ lời giải)  ->  --kc-1, dính lấy nhau
        giữa hai cặp                     ->  --kc-4, tách hẳn ra
     Cùng con số `.tl-chugiai` dùng cho chú giải dòng thời gian ở ngay trên —
     hai chú giải trên một trang mà khác nhau thì mắt đọc thành hai loại khác
     nhau. Đặt khe bằng nhau ở hai cấp là quay về đúng dải chữ liền vừa chữa. */
  .chugiai { display:flex; flex-wrap:wrap; align-items:center;
    gap:var(--kc-2) var(--kc-4); margin-top:var(--kc-3);
    font-size:var(--cx-micro); color:var(--muc-mo); }
  .chugiai > span { display:inline-flex; align-items:center; gap:var(--kc-1); }
  /* `.motang` mang sẵn margin trái để đứng sau con số trong ô bảng. Ở đây nó
     đứng ĐẦU cặp nên margin ấy thành một chỗ thụt vào không ai giải thích. */
  .chugiai .motang { margin-left:0; }

  /* ══════════════════════════════════════════════════════════════════════
     NĂM SỐ khi bung một chiến dịch trong danh sách

     Tên lớp là `.namso`, KHÔNG phải `.dstat`: `.dstat` đã có chủ ở dòng 265
     (sáu ô số trong ngăn kéo hồ sơ account) với cấu trúc khác hẳn —
     `.dstats > .dstat` là ô con, còn khối này là ô CHA. Dùng lại tên đó là để
     hai thành phần khác nhau tranh nhau một hợp đồng CSS, đúng họ lỗi số 3 của
     dự án. Khai tên riêng thì mỗi bên đổi được mà không đụng bên kia.
     ══════════════════════════════════════════════════════════════════════ */
  /* Hàng chở khối năm số trong bảng chiến dịch. Không mang class `crow` nên
     không có con trỏ tay; ô của nó cũng bớt đệm vì khối bên trong đã có đệm. */
  table.clist > tbody > tr.namso-hang > td { padding:var(--kc-2) var(--kc-3) 0; }
  .namso { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
    gap:var(--kc-3); background:var(--nen-chim); border-radius:var(--bo-goc-khoi);
    padding:var(--kc-4); margin-bottom:var(--kc-2); }
  .namso-o { min-width:0; }
  .namso-nhan { font-size:var(--cx-nhan); color:var(--muc-mo); font-weight:600;
    text-transform:uppercase; letter-spacing:.03em; line-height:var(--lh-nhan-hoa); }
  .namso-so { font-size:var(--cx-tieu-de); font-weight:700; color:var(--muc); margin:2px 0;
    font-variant-numeric:tabular-nums; }
  .namso-so .mau { color:var(--muc-mo); font-weight:400; font-size:70%; }
  .namso-phu { font-size:var(--cx-micro); color:var(--muc-mo); }

  /* ══════════════════════════════════════════════════════════════════════
     KHU XEM XUYÊN CHUỖI — mỗi hàng một NGƯỜI

     Mọi bộ chọn nhắm vào ô của bảng này PHẢI dùng tổ hợp con `>`: hàng bung ra
     chở một bảng `.mini` LỒNG bên trong, bộ chọn không có `>` là rò xuống đó —
     đã sập hai lần trong dự án, lần gần nhất ngay trong chính bộ kiểm mới viết.
     ══════════════════════════════════════════════════════════════════════ */
  /* Bảng 7 cột × 300 hàng: cuộn CẢ HAI chiều trong thẻ.
     · ngang — bóp 7 cột vào 318px màn điện thoại là chữ xếp dọc (cùng lối với
       .nhands ở trên);
     · dọc — 300 hàng để tự do là thẻ cao ~14.000px, đủ để ảnh chụp toàn trang
       vượt ngân sách 24 triệu điểm ảnh của cong-cu/chup.mjs và bị cắt.
     70vh chứ không px cứng: khung cuộn phải vừa màn hình đang xem. */
  #nguoiBody { overflow:auto; max-height:70vh;
    border:1px solid var(--vien); border-radius:var(--bo-goc-nut); }
  /* `overflow:visible` BẮT BUỘC đi kèm tiêu đề dính bên dưới. `.ptable` khai
     `overflow:hidden` để bo góc, mà `overflow:hidden` tự tạo khung cuộn nên
     sticky bám vào chính cái bảng chứ không vào `#nguoiBody`. Đo bằng Chrome
     23/08: ca `overflow:hidden` trôi hẳn −400px sau khi cuộn 400px.
     Bo góc chuyển ra `#nguoiBody`. */
  table.ptable.nguoi { min-width:820px; overflow:visible; border:none; border-radius:0; }
  /* Tiêu đề DÍNH. Bảng này có `<thead>` thật từ đầu nhưng cuộn là mất — trong
     khi `table.clist` cùng file đã dính từ trước. Người dùng chỉ ra 23/08:
     "khi cuộn nó không cố định mà nó mất theo danh sách luôn".
     `#nguoiBody` là khung cuộn (overflow:auto, max-height 70vh) nên sticky bám
     vào đó. Nền phải ĐỤC: thiếu `background` thì hàng chạy xuyên qua chữ tiêu
     đề. `z-index` để chip trong hàng không trồi lên trên tiêu đề. */
  table.ptable.nguoi > thead > tr > th { text-align:right;
    position:sticky; top:0; z-index:2; background:var(--nen-the);
    border-bottom:2px solid var(--vien-manh); }
  table.ptable.nguoi > tbody > tr > td { text-align:right; white-space:nowrap;
    font-variant-numeric:tabular-nums; }
  table.ptable.nguoi > thead > tr > th.left,
  table.ptable.nguoi > tbody > tr > td.left { text-align:left; white-space:normal; }
  /* Cột số thứ tự: mờ và hẹp. Nó để đếm "đang ở hàng thứ mấy trong 300", không
     phải một số liệu — đậm bằng các cột kia là nó tranh mắt với số thật.
     `vertical-align:top` để số bám dòng tên, chứ hàng có hai dòng (tên + email)
     thì số trôi xuống giữa và không còn thẳng hàng với tên nó đánh số. */
  table.ptable.nguoi > thead > tr > th.stt,
  table.ptable.nguoi > tbody > tr > td.stt { width:1%; text-align:right;
    color:var(--muc-mo); font-size:var(--cx-micro); vertical-align:top;
    padding-right:var(--kc-1); }
  /* Kiểu hàng bấm được KHÔNG khai lại ở đây: `.ptable tr[data-acct]` ở trên đã
     lo con trỏ, hover và viền tiêu điểm cho MỌI bảng người. Từ 23/08 bảng này
     dùng đúng lối đó (bấm là mở hồ sơ) nên nó không còn tên lớp riêng. */
  table.nguoi > tbody > tr > td > .ten { font-weight:600; color:var(--muc); }
  /* overflow-wrap:anywhere — địa chỉ mail là MỘT từ dài 34 ký tự, nên nó một
     mình đòi 215px bề rộng nhỏ nhất và đẩy cả bảng qua 1.300px. Cho nó ngắt
     được thì bảng vừa màn 1440 mà không cuộn ngang. */
  table.nguoi > tbody > tr > td > .hang-phu { font-size:var(--cx-micro); color:var(--muc-mo);
    overflow-wrap:anywhere; }
  /* Người đã unsub: gạch tên để sale không gọi nhầm. Họ vẫn hiện vì đây là bảng
     TRA CỨU, không phải phép đếm. */
  /* BẬC MÀU, không phải `opacity`. `opacity:.55` trên chữ --muc-2 cho ra
     3,96:1 — dưới sàn 4,5 — và nó còn làm mờ CẢ dấu "—" của ô rỗng bên trong
     xuống 2,33:1. Đây lại đúng chỗ sale cần đọc rõ nhất để không gọi nhầm
     người đã unsub.
     `--muc-mo` là bậc nhạt nhất luật 1 của token.css cho phép dùng cho chữ
     (6,23:1 trên thẻ), và gạch ngang vẫn giữ nguyên nên trạng thái không chỉ
     nói bằng độ đậm nhạt. */
  table.nguoi > tbody > tr > td > .ten.bo { text-decoration:line-through;
    color:var(--muc-mo); }
  table.nguoi > tbody > tr > td > .chip { margin:1px 2px 1px 0; }
  /* Khối `table.mini` (bảng con trong hàng bung) đã bỏ 23/08 cùng với hàng
     bung: bấm một hàng người nay mở NGĂN HỒ SƠ, và khu "Lịch sử email" của
     ngăn đó chở nhiều hơn bảng con cũ. Không giữ kiểu cho thứ không ai dựng
     ra nữa — kiểu chết là kiểu người sau đọc rồi tưởng còn dùng. */

  /* ══════════════════════════════════════════════════════════════════════
     DÒNG THỜI GIAN — dựng lại 25/08 theo đặc tả người dùng gửi

     Khác bản cũ ở bốn điểm cốt lõi:
       1. Trục có NGÀY cụ thể, không chỉ mốc tháng chung chung.
       2. Vạch hôm nay màu NHẤN, đứt nét — không dùng --xau. Đỏ chỉ dành cho
          trạng thái lỗi; dùng nó cho "hôm nay" là dạy người đọc bỏ qua màu đỏ.
       3. Cột tên DÍNH khi cuộn ngang, trục thời gian DÍNH khi cuộn dọc.
       4. Email có thanh có đầu-cuối, không còn là một chấm không ngữ cảnh.

     Toàn bộ toạ độ do JS tính bằng NGÀY rồi ghi vào `left/width` theo px —
     CSS ở đây không chia đều cột. Xem chú thích ở `veDongThoiGian`.
     ══════════════════════════════════════════════════════════════════════ */
  .an-doc { position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }

  .tl-dau-khu { display:flex; align-items:flex-start; justify-content:space-between;
    gap:var(--kc-4); flex-wrap:wrap; }
  .tl-dau-khu h3 { margin:0; }
  .tl-dau-khu .desc { margin:2px 0 0; }

  .tl-dieu-khien { display:flex; align-items:center; gap:var(--kc-2); flex:none; }
  .tl-nhom-nut { display:flex; }
  .tl-nut { width:32px; height:32px; display:flex; align-items:center;
    justify-content:center; font-size:16px; line-height:1; cursor:pointer;
    color:var(--muc-2); background:var(--nen-the);
    border:1px solid var(--vien-tuong-tac); }
  .tl-nhom-nut .tl-nut:first-child { border-radius:var(--bo-goc-nut) 0 0 var(--bo-goc-nut); }
  .tl-nhom-nut .tl-nut:last-child { border-radius:0 var(--bo-goc-nut) var(--bo-goc-nut) 0;
    margin-left:-1px; }
  .tl-nut:hover { background:var(--hang-hover); color:var(--muc); }
  .tl-nut:focus-visible { outline:2px solid var(--nhan-manh); outline-offset:1px; z-index:1; }
  .tl-chon select { height:32px; padding:0 var(--kc-2); cursor:pointer;
    border:1px solid var(--vien-tuong-tac); border-radius:var(--bo-goc-nut);
    background:var(--nen-the); color:var(--muc-2); font:inherit; font-size:var(--cx-nho); }
  .tl-chon select:focus-visible { outline:2px solid var(--nhan-manh); outline-offset:1px; }

  /* `overflow:clip` ở khung ngoài, `auto` ở khung trong: khung ngoài chỉ để
     bo góc, còn thanh cuộn phải nằm ở khung có trục và thân, nếu không trục
     cuộn rời khỏi thân. */
  .tl-khung { margin-top:var(--kc-3); border:1px solid var(--vien);
    border-radius:var(--bo-goc-khoi); overflow:clip; background:var(--nen-the); }
  /* CUỘN CẢ HAI CHIỀU, và trần chiều cao là bắt buộc. Đo 25/08 trên dữ liệu
     thật: 9 chiến dịch, 5 cái đang chạy được bung mặc định (đặc tả §13) ra 43
     hàng — thẻ cao 2.521px, tức trang chính biến thành một cột dài phải cuộn
     mãi mới tới bảng bên dưới. Có trần thì trục thời gian dính (`position:
     sticky`) mới có tác dụng thật, đúng ý đặc tả §21. */
  .tl-cuon { overflow:auto; max-height:min(70vh, 620px); overscroll-behavior:contain; }
  /* `+ var(--tl-du)`: CHỖ THỪA BÊN PHẢI, và nó không phải trang trí.

     Nhãn "Hôm nay" và nhãn của vạch trục đều đặt `position:absolute; left:x` rồi
     chảy sang phải, nên chúng tràn ra ngoài `.tl-noi` và tự nới vùng cuộn ra.
     Đo ở 390px lẫn 1440px: vùng cuộn rộng hơn `.tl-noi` đúng 37px (nhãn "Hôm
     nay") — và `.tl-noi` mới là khung giới hạn của `position:sticky`.

     Hậu quả đo được: cuộn tới cuối, cột tên dính bị hụt lại 37px so với mép trái
     khung nhìn. Ở khổ máy tính cột tên rộng 240px nên 37px chỉ hơi lệch; ở điện
     thoại ô tên rộng trọn 306px thì 37px đó nuốt mất chấm trạng thái và chữ cái
     đầu của tên email. Lỗi này có từ trước, chỉ là khổ hẹp mới làm nó lộ ra.

     56px = 37px đo được, làm tròn lên có biên. */
  .tl-noi { min-width:calc(var(--tl-cot) + var(--tl-rong) + var(--tl-du));
    position:relative; }

  /* Trục DÍNH khi cuộn dọc, và Ô GÓC của nó DÍNH khi cuộn ngang.
     Thiếu ô góc thì lúc cuộn ngang, vùng ngay trên cột tên hiện vạch ngày của
     những thanh đang bị chính cột tên che — người dùng đọc ra thành "ngày chạy
     lung tung" (báo 25/08). z-index cao hơn cột tên vì trục nằm trên cùng. */
  .tl-truc { position:sticky; top:0; z-index:6; display:flex;
    background:var(--nen-the); border-bottom:1px solid var(--vien); }
  .tl-goc { position:sticky; left:0; z-index:1; flex:none; width:var(--tl-cot);
    background:var(--nen-the); border-right:1px solid var(--vien); }
  .tl-truc-noi { flex:none; }
  .tl-dai, .tl-vach { position:relative; height:22px; }
  .tl-dai span { position:absolute; top:0; display:flex; align-items:center;
    padding-left:var(--kc-2); font-size:var(--cx-micro); font-weight:600;
    color:var(--muc-2); height:22px; white-space:nowrap; overflow:hidden;
    border-left:1px solid var(--vien); }
  .tl-vach { height:26px; }
  .tl-vach span { position:absolute; top:0; height:26px; display:flex;
    flex-direction:column; align-items:center; justify-content:center;
    font-size:var(--cx-micro); color:var(--muc-mo); overflow:hidden;
    border-left:1px solid var(--luoi); }
  .tl-vach span.manh { border-left-color:var(--vien-manh); }
  .tl-vach span.cuoi-tuan { background:var(--van); }
  .tl-vach b { font-weight:600; color:var(--muc-2); line-height:1.1; }
  .tl-vach i { font-style:normal; font-size:9px; line-height:1; }
  .tl-hom-nay-so { display:inline-flex; align-items:center; justify-content:center;
    min-width:18px; height:18px; border-radius:var(--bo-goc-tron);
    background:var(--nhan-manh); color:var(--tren-dac); }

  .tl-than { position:relative; }

  /* Vạch HÔM NAY chạy xuyên thân. Đứt nét + màu nhấn (đặc tả §5). */
  /* z-index THẤP HƠN cột tên (4): vạch hôm nay phải chui xuống dưới cột tên khi
     cuộn ngang, chứ không kẻ một đường xanh xuyên qua tên chiến dịch. Vẫn cao
     hơn các thanh vì thanh không khai z-index. */
  .tl-nay { position:absolute; top:0; bottom:0; width:0;
    border-left:2px dashed var(--nhan-manh); z-index:3; pointer-events:none;
    margin-left:var(--tl-cot); }
  .tl-nay span { position:absolute; top:2px; left:6px; font-size:var(--cx-micro);
    font-weight:600; color:var(--nhan-manh); background:var(--nen-the);
    padding:0 4px; border-radius:var(--bo-goc-tron); white-space:nowrap; }

  .tl-hang { display:flex; align-items:stretch; min-height:56px;
    border-top:1px solid var(--luoi); }
  .tl-hang:first-child { border-top:0; }
  .tl-hang[data-tl-bung] { cursor:pointer; }
  .tl-hang[data-tl-bung]:hover { background:var(--hang-hover); }
  .tl-hang[data-tl-bung]:hover .tl-ten { background:var(--hang-hover); }
  .tl-hang:focus-visible { outline:2px solid var(--nhan-manh); outline-offset:-2px; }
  .tl-con { min-height:52px; background:var(--nen-chim); }
  .tl-con:hover, .tl-con:hover .tl-ten { background:var(--hang-hover); }

  /* Cột tên DÍNH khi cuộn ngang. Nền đặc bắt buộc: trong suốt thì thanh của
     hàng chạy ra sau chữ và đọc thành một mớ chồng nhau. */
  .tl-ten { position:sticky; left:0; z-index:4; flex:none; width:var(--tl-cot);
    display:flex; align-items:center; gap:var(--kc-2);
    padding:var(--kc-2) var(--kc-3); background:var(--nen-the);
    border-right:1px solid var(--vien); }
  .tl-con .tl-ten { background:var(--nen-chim); padding-left:var(--kc-6); }
  .tl-chuoi-ten { min-width:0; display:flex; flex-direction:column; gap:1px; }
  .tl-chuoi-ten b { font-size:var(--cx-nho); font-weight:600; color:var(--muc);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .tl-con .tl-chuoi-ten b { font-weight:400; color:var(--muc-2); }
  .tl-phu { font-size:var(--cx-micro); color:var(--muc-mo);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

  .tl-nut-bung { flex:none; width:18px; height:18px; padding:0; border:0;
    background:none; display:flex; align-items:center; justify-content:center; }
  .tl-mui { width:0; height:0; border-left:5px solid var(--muc-mo);
    border-top:4px solid transparent; border-bottom:4px solid transparent;
    transition:transform 160ms ease; }
  .tl-mui.mo { transform:rotate(90deg) translateX(1px); }

  /* Chấm trạng thái cạnh tên: trạng thái KHÔNG được truyền đạt chỉ bằng màu
     của thanh, vì thanh có thể nằm ngoài tầm nhìn khi cuộn ngang. */
  .tl-dau { flex:none; width:9px; height:9px; border-radius:var(--bo-goc-tron); }

  .tl-lane { position:relative; flex:1; min-width:var(--tl-rong); }
  .tl-thanh { position:absolute; top:50%; transform:translateY(-50%);
    height:18px; border-radius:var(--bo-goc-nut); display:flex; align-items:center;
    justify-content:center; font-size:var(--cx-thanh); font-weight:600;
    overflow:hidden; }
  .tl-con .tl-thanh { height:14px; }
  .tl-diem { border-radius:var(--bo-goc-tron); }
  .tl-chu { padding:0 6px; white-space:nowrap; }
  .tl-trong { position:absolute; top:50%; transform:translateY(-50%); left:var(--kc-3);
    font-size:var(--cx-micro); color:var(--muc-mo); font-style:italic; }
  /* Nhãn ngày của thanh hẹp. `pointer-events:none` để nó không chắn tooltip
     của chính thanh nó đang chú thích. */
  .tl-nhan-ngoai { position:absolute; top:50%; transform:translateY(-50%);
    font-size:var(--cx-thanh); color:var(--muc-mo); white-space:nowrap;
    pointer-events:none; }

  /* Năm trạng thái. Mỗi cái có HÌNH riêng, không chỉ có màu — luật 4 của
     token.css: hai màu chuỗi sát nhau nhất khi mù màu đỏ-lục cách ΔE 8,7. */
  .tl-chay  { background:var(--tot); color:var(--tren-dac); }
  .tl-xong  { background:var(--muc-mo); color:var(--tren-dac); }
  .tl-se    { background:var(--nhan-manh-nhat); color:var(--nhan-manh);
    border:2px dashed var(--nhan-manh); }
  .tl-khong { background:repeating-linear-gradient(45deg,var(--vien-manh) 0 5px,
    var(--luoi) 5px 10px); color:var(--muc-mo); }
  .tl-lech  { background:var(--xau-nhat); color:var(--xau);
    border:2px solid var(--xau); }
  span.tl-dau.tl-chay  { background:var(--tot); }
  span.tl-dau.tl-xong  { background:var(--muc-mo); }
  span.tl-dau.tl-se    { background:var(--nen-the); border:2px dashed var(--nhan-manh); }
  span.tl-dau.tl-khong { background:var(--vien-manh); }
  span.tl-dau.tl-lech  { background:var(--xau); }

  .tl-chugiai { display:flex; flex-wrap:wrap; gap:var(--kc-4);
    font-size:var(--cx-micro); color:var(--muc-mo); margin-top:var(--kc-3);
    align-items:center; }
  .tl-chugiai i { display:inline-block; width:20px; height:10px; border-radius:3px;
    margin-right:var(--kc-1); vertical-align:-1px; }
  .tl-i-nay { border-left:2px dashed var(--nhan-manh); width:0 !important;
    height:12px !important; border-radius:0 !important; }

  /* Khổ laptop công ty. Đặc tả §23 xin cột tên khoảng 260px ở dải 1024-1439;
     240 vì tên chuỗi dài nhất đang dùng ("Recap và cảm ơn sau sự kiện") vừa đủ
     trong 240 mà chừa thêm chỗ cho vùng thời gian. */
  @media (max-width:1400px) { .tl-noi { --tl-cot:240px; } }
  @media (max-width:900px) {
    .tl-noi { --tl-cot:180px; }
    .tl-dau-khu { flex-direction:column; }
    .tl-hang { min-height:48px; }
    .tl-con { min-height:44px; }
  }

  /* ĐIỆN THOẠI: tên chuyển LÊN TRÊN thanh, không đứng cạnh.

     Đo trên Chrome ở 390px, dữ liệu thật: khung cuộn của thẻ rộng 306px mà cột
     tên chiếm 180px — 59% bề ngang màn hình dành cho tên, còn đúng 126px cho
     dòng thời gian trong khi dải thật rộng 1.504px. Tức người dùng nhìn thấy 8%
     cái thứ mà thẻ này sinh ra để hiện.

     Xếp dọc thì thanh được trọn 306px, gấp 2,4 lần. Tên KHÔNG cuộn đi mất:
     `align-self:flex-start` cho nó co về đúng bề rộng chữ, rồi `sticky left:0`
     ghim nó ở mép trái khung nhìn trong lúc thanh trượt bên dưới.

     `--tl-cot:0` là chỗ duy nhất phải sửa cho phần còn lại tự theo: ô góc của
     trục, và `margin-left` của vạch HÔM NAY, đều đọc chính biến này. Đặt số 0 ở
     đây thay vì sửa ba nơi là cách giữ cho chúng không bao giờ lệch nhau. */
  @media (max-width:600px) {
    .tl-cuon { container-type:inline-size; }
    .tl-noi { --tl-cot:0px; }
    .tl-goc { display:none; }
    .tl-hang, .tl-con { flex-direction:column; align-items:stretch;
      min-height:0; }
    /* `max-width` phải theo bề rộng NHÌN THẤY của khung cuộn, không theo 100%
       — 100% ở đây là bề rộng cả dải thời gian (1.324px), nên tên dài không bị
       cắt mà tràn thẳng ra ngoài màn hình. Đo được: ô tên hàng con rộng 598px
       trong khung nhìn 306px. `100cqw` đọc đúng bề rộng khung cuộn nhờ
       `container-type:inline-size` đặt trên `.tl-cuon` ngay dưới đây. */
    .tl-ten { align-self:flex-start; width:fit-content; max-width:100cqw;
      padding:var(--kc-2) var(--kc-3) var(--kc-1); border-right:0;
      min-height:36px; }
    .tl-con .tl-ten { padding-left:var(--kc-4); }
    /* Thanh cần chiều cao riêng vì nó không còn được cột tên kéo cao hộ. */
    .tl-lane { min-height:30px; }
    .tl-con .tl-lane { min-height:26px; }
  }

  /* Tên công ty chuyển xuống dòng dưới, đứng cùng chỗ với địa chỉ email.
     Trước đây nó ghép vào cùng dòng với tên người: "Nhung Do Thi Phuong ·
     ICD TAN CANG SONG THAN" dài 320px trong khi cột chỉ có 267px — tên
     người bị cắt trước cả khi đọc xong. Tách ra thì tên người gần như
     không bao giờ bị cắt, còn dòng dưới có cắt cũng không mất danh tính. */
  .hotco { color:var(--muc-2); }


  /* ── LOGO SMARTLOG ────────────────────────────────────────────────────
     KHÔNG dùng class `.ic`: `.ic` khai `fill:none; stroke:currentColor`, và CSS
     thắng presentation attribute — logo sẽ vô hình mà không báo lỗi gì. Đúng cái
     bẫy đã ghi ở `i-tay-keo` trong _icon.html.

     Đặt cỡ theo BỀ RỘNG, không theo chiều cao: viewBox 204×60 gồm cả đuôi chữ
     "g", nên phần chữ chỉ chiếm ~72% chiều cao. Đặt height:28px thì chữ ra 20px
     — nhỏ hơn hẳn dòng chữ 13px bên cạnh nó về mặt thị giác. */
  .logo-sl { width:96px; height:28px; flex:none; display:block;
    fill:var(--thuong-hieu); stroke:none; }
  /* Trang đăng nhập: to hơn, và có khoảng thở phía dưới. */
  .logo-to { width:150px; height:44px; margin-bottom:var(--kc-4); }
  /* Chế độ tương phản cao của Windows ép mọi màu về bảng của hệ điều hành; để
     nguyên `fill` thì logo biến thành một khối đặc một màu. `CanvasText` giữ nó
     đọc được như một hình. */
  @media (forced-colors:active){ .logo-sl { fill:CanvasText; } }

  /* ── HAI TÊN LỚP JS DỰNG RA MÀ CSS CHƯA TỪNG KHAI ──────────────────────
     Tên lớp là hợp đồng ngầm giữa JS và CSS, và KHÔNG bên nào báo lỗi khi hợp
     đồng bị lệch: phần tử vẫn hiện ra, chỉ là không có kiểu nào. Đúng họ lỗi đã
     làm bốn pill trong khu Thông báo hiện ra không màu (xem chú thích `.pill`). */

  /* Nhãn băng HOT / WARM / COLD đứng cạnh tên người trong bảng account. Trước
     đây nó là chữ trần dính liền vào tên, không phân biệt được đâu là tên và đâu
     là nhãn. KHÔNG dùng lại `.chip`: chip ở bảng này đã mang nghĩa khác (chip
     chiến dịch đã mở), và hai nghĩa một hợp đồng là cái bẫy dự án đã sập. */
  .bacnhan { display:inline-block; margin-left:var(--kc-1); padding:0 var(--kc-1);
    border-radius:var(--bo-goc-nut); background:var(--nen-chim);
    color:var(--muc-mo); font-size:var(--cx-micro); font-weight:700;
    letter-spacing:.02em; vertical-align:1px; }

  /* Chip TEAM ở thanh đầu trang dùng chung. Tên riêng chứ không `.chip xong`:
     `.chip` được khai hai lần với giá trị khác nhau ở bao_cao.css và admin.css,
     nên cùng một chip trong cùng thanh đầu trang lại hiện hai kiểu ở hai trang.
     Xem chú thích trong _dau_trang.html. */
  .chip-nhom { display:inline-flex; align-items:center; padding:2px 9px;
    border-radius:var(--bo-goc-tron); border:1px solid var(--vien);
    background:var(--van); color:var(--muc-2);
    font-size:var(--cx-micro); font-weight:650; white-space:nowrap; }

  /* `.hotrow` CỐ Ý KHÔNG có kiểu nào và ĐỪNG XOÁ nó khỏi bao_cao.js.
     Nó là ĐIỂM NEO của bộ đo: `chup.mjs` và `soat.mjs` bám `.hotrow` ở 8 chỗ để
     biết bảng account đã dựng xong. Kiểu của hàng bấm được nằm ở
     `.ptable tr[data-acct]` — neo vào thuộc tính quyết định hàng có bấm được
     không, nên không bao giờ lệch giữa cái trông-bấm-được và cái bấm-được-thật. */

  /* Cột "Mở thư ở" của bảng Ưu tiên gọi: KHÔNG cho gãy chữ. "1 chiến dịch" gãy
     thành "1 chiến / dịch" đọc lên như hai thứ, và mọi hàng đều mang đúng cụm từ
     đó nên cả cột cao gấp đôi vô ích. Cùng luật đã có ở `table.ptable.acct .nw`,
     chỉ khác là bảng này không đi qua `bangAcct` nên phải khai riêng.
     Bảng nằm trong `.cwrap` (overflow-x:auto) nên rộng thêm thì nó cuộn ngang. */
  .ptable th.nw, .ptable td.nw { white-space:nowrap; }

  /* ── CÂU CHÚ "đã loại N người đã huỷ đăng ký" ──────────────────────────
     Dưới bảng Top Account, cả cấp chiến dịch lẫn cấp toàn hệ. Bảng CỐ Ý bỏ
     người đã huỷ đăng ký; câu này để nó thôi bỏ trong im lặng.
     Tông TRUNG TÍNH, không dùng màu cảnh báo: đây không phải lỗi, mà là một
     luật đang chạy đúng. Mặc màu cảnh báo cho việc chạy đúng là dạy người đọc
     bỏ qua màu cảnh báo. */
  .chu-loai-unsub { margin-top:var(--kc-3); border-top:1px solid var(--vien); padding-top:var(--kc-2); }
  .mo-loai-unsub { display:flex; align-items:center; gap:var(--kc-2); width:100%;
    min-height:32px; padding:var(--kc-2); background:none; border:0; cursor:pointer;
    text-align:left; font:inherit; font-size:var(--cx-nho); color:var(--muc-mo);
    border-radius:var(--bo-goc-nut); }
  .mo-loai-unsub:hover { background:var(--hang-hover); color:var(--muc); }
  .mo-loai-unsub b { color:var(--muc); }
  .mo-loai-unsub .mui-ten { margin-left:auto; }
  .ds-loai-unsub { margin-top:var(--kc-2); }
  .ds-loai-unsub > .desc { margin:0 0 var(--kc-2); }

  /* ── THẺ "NGHI BOT QUÉT" dưới mỗi chiến dịch ───────────────────────────
     Nhóm này ĐÃ bị trừ khỏi mọi chỉ số, nên thẻ là phần GIẢI THÍCH cho các số
     phía trên, không phải một số để đọc song song. Tông trung tính, không màu
     cảnh báo: đây là một luật đang chạy đúng, không phải một lỗi. */
  .the-nghi-bot .ly-do { color:var(--muc-2); font-size:var(--cx-nho); line-height:1.5; }
  .the-nghi-bot .nut-quet { white-space:nowrap; }
  .the-nghi-bot .nut-quet button + button { margin-left:var(--kc-2); }
  /* Vùng bấm ≥30px — luật thao tác giao diện người dùng chốt. `soat.mjs` đo
     đúng chỗ này ở mọi màn. */
  .btn-nho { min-height:32px; padding:var(--kc-2) var(--kc-3); border:1px solid var(--vien);
    border-radius:var(--bo-goc-nut); background:var(--nen-the); color:var(--muc);
    font:inherit; font-size:var(--cx-nho); cursor:pointer; }
  .btn-nho:hover { background:var(--hang-hover); }
  .btn-nho:disabled { opacity:.5; cursor:default; }
  .btn-nho.btn-xau { border-color:var(--xau); color:var(--xau); }

  /* ══════════════════════════════════════════════════════════════════════
     ĐIỆN THOẠI — BẢNG DỮ LIỆU KHÔNG PHẢI CUỘN NGANG NỬA MÀN HÌNH
     ══════════════════════════════════════════════════════════════════════
     ĐO TRƯỚC KHI SỬA, ở khung 390px (khung cuộn thật rộng 306-308px):

         .clist-scroll   nội dung   860px  ->  phải cuộn 552px
         .hotlist        nội dung   940px  ->  phải cuộn 632px
         #cvList.cwrap   nội dung   936px  ->  phải cuộn 628px

     Không trang nào TRÀN ra ngoài màn (`documentElement` cuộn ngang = 0 ở cả
     390 lẫn 360px) — chuyện đã đúng từ trước và không đụng tới. Cái sai là
     phải kéo ngang gấp đôi bề rộng màn mới đọc hết một hàng, mà kéo xong thì
     mất luôn tên chiến dịch nên không biết đang đọc số của ai.

     THỦ PHẠM CHÍNH là mốc rộng GÕ CỨNG: `table.clist{min-width:860px}` và
     `table.emails{min-width:620px}`. Chúng sinh ra cho màn rộng, nơi bảng cần
     giãn cho thoáng; trên điện thoại chúng ép bảng phình to hơn nội dung thật.
     Đo bằng cách bỏ mốc đó ngay trên trang: 860px -> 449px.

     BỐN VIỆC, và thứ tự này có lý do:
       1. Bỏ mốc rộng gõ cứng  — 860 -> 449px, ăn đứt ba việc còn lại cộng lại.
       2. Siết đệm ô           — đệm desktop 12/8px là thoáng, ở đây là xa xỉ.
       3. Bỏ cột SỐ THỨ TỰ     — "#" chỉ đếm 1..9, thứ tự đã nhìn thấy bằng mắt.
          Đây là cột DUY NHẤT được bỏ; mọi cột chở SỐ LIỆU đều ở lại. Giấu một
          con số trên điện thoại là để người đọc kết luận sai mà không biết.
       4. GHIM cột tên         — còn phải cuộn thì ít nhất luôn thấy đang đọc ai.

     KHÔNG đổi sang bố cục thẻ xếp dọc. Ba bảng này để SO SÁNH giữa các dòng
     (chiến dịch nào mở nhiều hơn, email nào chết); xếp dọc thành thẻ là bỏ mất
     đúng khả năng đó, và đo được là trang dài gấp ~3. Bảng vẫn là bảng.
     ══════════════════════════════════════════════════════════════════════ */
  @media (max-width:759px){
    /* 1. Mốc rộng gõ cứng — chỉ có nghĩa trên màn rộng.
       BỐN chỗ, không phải hai. Lượt đầu tôi chỉ gỡ `clist` và `emails` rồi đo
       lại thì `.hotlist` không nhúc nhích một điểm ảnh — mốc của nó nằm ở
       `table.ptable.acct{min-width:940px}`, đúng bằng con số đo được. Bảng
       `.nguoi` (xuyên chiến dịch) cũng có mốc riêng 820px.
       Phải khai đủ `table.ptable.acct`, không rút gọn thành `.ptable`: bộ chọn
       gốc có độ đặc hiệu (0,2,1), viết nhẹ hơn là thua và im lặng không đổi gì. */
    table.clist  { min-width:0; }
    table.emails { min-width:0; }
    table.ptable.acct  { min-width:0; }
    table.ptable.nguoi { min-width:0; }

    /* 2. Đệm. Giữ chiều cao hàng đủ cho ngón tay: `td` vẫn 8px trên dưới nên
       hàng cao ~36px, trên ngưỡng 30px người dùng đã chốt. */
    table.clist th, table.emails > thead > tr > th,
    .ptable thead th { padding:var(--kc-1) 5px; white-space:normal; }
    table.clist td, table.emails > tbody > tr > td,
    .ptable tbody td { padding:var(--kc-2) 5px; }

    /* 3. Cột số thứ tự. `visibility` không dùng được — nó vẫn chiếm chỗ.
       Hai tên lớp vì hai nơi dựng bảng đặt tên khác nhau: `idx` ở các bảng
       viết tay, `stt` ở `bangAcct` (bảng Top Account dựng từ mảng `cot`).
       Bỏ sót `stt` là `.hotlist` không nhúc nhích — đã đo đúng lần đầu. */
    table.clist th.rank, table.clist td.rank,
    .ptable th.idx, .ptable td.idx,
    .ptable th.stt, .ptable td.stt { display:none; }

    /* 4. Ghim cột tên. Cột đầu SAU KHI bỏ "#" — dùng nth-child vì `.left` có
       mặt ở cả cột "Hoạt động" cuối bảng, bám theo lớp là ghim nhầm hai cột.
       Nền ĐẶC bắt buộc: cột ghim trượt lên trên các ô khác, để trong suốt là
       chữ chồng lên chữ. Hàng sọc và hàng hover phải khai lại nền của chính
       chúng, nếu không cột ghim giữ nền trắng và lộ ra một vệt dọc lệch màu. */
    table.clist  > * > tr > :nth-child(2),
    table.emails > tbody > tr > td:first-child,
    table.emails > thead > tr > th:first-child,
    .ptable > * > tr > :nth-child(2) {
      position:sticky; left:0; z-index:2; background:var(--nen-the); }
    table.clist tbody tr:nth-child(even) > :nth-child(2) { background:var(--van); }
    table.clist tr.crow:hover > :nth-child(2) { background:var(--hang-hover); }
    .ptable tr[data-acct]:hover > :nth-child(2) { background:var(--hang-hover); }
    /* Tiêu đề cột ghim nằm ở giao của hai trục dính, phải nổi trên cả hai. */
    table.clist > thead > tr > :nth-child(2),
    table.emails > thead > tr > th:first-child { z-index:3; }
    /* Viền phải để mắt thấy chỗ cột ghim kết thúc và phần cuộn bắt đầu. */
    table.clist  > * > tr > :nth-child(2),
    table.emails > * > tr > :first-child,
    .ptable > * > tr > :nth-child(2) { border-right:1px solid var(--vien); }

    /* Bề rộng cột tên là một ĐÁNH ĐỔI thật, đo hết ở khung 390px:

           cột tên    bảng   phải cuộn   cao một hàng   bảng cao
           (gốc)      860px      552px          78px       732px
           130px      501px      193px         103px      1125px
           150px      541px      233px          88px       910px   <- chọn
           170px      581px      273px          88px       868px
           190px      621px      313px          70px       793px

       Hẹp quá thì tên gãy ba bốn dòng và hàng phình lên — đổi cuộn ngang lấy
       cuộn dọc còn nhiều hơn, tức là không được gì. Rộng quá thì hàng đẹp
       nhưng phải kéo ngang gần bằng cũ.

       150px chọn vì nó cắt được NHIỀU NHẤT ở trục khó (ngang, −58%) trong khi
       chỉ trả 10px mỗi hàng ở trục dễ (dọc — điện thoại vốn để cuộn dọc). Cộng
       thêm cột tên đã GHIM nên quãng 233px còn lại là một cú vuốt, không phải
       một chuyến đi lạc. */
    table.clist td.left { min-width:150px; }
    table.emails > tbody > tr > td.left { min-width:150px; }
    /* Dòng phụ gọn lại còn "N chuỗi" — xem chú thích ở chỗ dựng nó trong
       bao_cao.js. Khoảng ngày nằm trên dòng thời gian ngay phía trên. */
    .crange-ngay { display:none; }
    .crange { white-space:nowrap; }

    /* ĐỊA CHỈ THƯ VÀ TÊN CÔNG TY ĐƯỢC NGẮT GIỮA TỪ.
       Một địa chỉ như `nguyenthithanhxuan@congtyabc.com.vn` là MỘT từ 38 ký tự
       — không có chỗ nào cho trình duyệt xuống dòng, nên nó một mình đặt sàn
       bề rộng cho cả bảng. Đo được: bảng account lồng trong hàng chi tiết đang
       bung rộng 890px, và chính nó kéo bảng email ngoài lên 765px, nên gỡ mốc
       `min-width` ở tầng trên không nhích được một điểm ảnh.
       `anywhere` chứ không `break-all`: nó vẫn ưu tiên ngắt ở dấu chấm và gạch
       ngang, chỉ cắt giữa từ khi không còn cách nào — tên người vì thế không bị
       cắt bừa khi vẫn còn chỗ xuống dòng tử tế.

       SÀN 150px LÀ BẮT BUỘC, đừng gỡ. Cho phép ngắt giữa từ mà không đặt sàn
       thì bảng không còn gì giữ bề rộng cột "Người": đo được nó tụt xuống 48px
       và tên vỡ thành từng chữ cái một dòng — "NGU / YEN / T / THAN / H /
       XUAN" — hàng cao tới 307px. Con số cuộn ngang lúc đó ĐẸP NHẤT trong mọi
       phương án (53px) trong khi giao diện hỏng nặng nhất: đúng bài học "phép
       đo tự nó đo SAI THỨ", và chỉ nhìn ảnh chụp mới thấy.

           sàn      bảng   cuộn   hàng cao nhất
           (không)   361px   53px      307px   <- vỡ
           130px     483px  175px      119px
           150px     523px  215px      103px   <- chọn
           190px     603px  295px      102px   (thêm 80px cuộn, không được gì) */
    .ptable td.left, .acct-phu, .ptable td a { overflow-wrap:anywhere; }
    .ptable tbody td { white-space:normal; }
    .ptable td.left, .ptable th.left { min-width:150px; }

    /* DÒNG THỜI GIAN: BỎ TRẦN CHIỀU DỌC TRÊN ĐIỆN THOẠI.

       Trần `min(70vh, 620px)` sinh ra cho màn rộng và có lý do đúng ở đó (xem
       chú thích tại `.tl-cuon`): giữ thẻ khỏi dài 2.521px và cho trục thời gian
       dính có chỗ mà dính.

       Trên điện thoại nó quay ra hại. Đo ở khung 390×844: 70vh = 591px, mà 9
       chiến dịch thu gọn đã cao 769px — HAI chiến dịch cuối nằm dưới đáy một
       khung cuộn DỌC LỒNG chiếm 70% màn hình, không một dấu hiệu nào cho biết
       còn thứ ở dưới. Một khung cuộn dọc nằm trong một trang cũng cuộn dọc là
       thứ trên cảm ứng người ta hay vuốt trượt qua mà không biết mình bỏ sót.

       Bỏ trần thì trang tự dài ra — đúng thứ điện thoại vốn để làm. Đã đo lại
       sau khi bỏ: hết cuộn dọc lồng, cuộn NGANG giữ nguyên 225px, và trục thời
       gian VẪN `position:sticky` (khối vẫn là khung cuộn nhờ `overflow:auto`
       cho trục ngang). Bung một chiến dịch: thẻ 1.250px, trang 3.069px — dài,
       nhưng là dài do người dùng tự bấm, và cuộn tiếp là thấy. */
    .tl-cuon { max-height:none; }
  }
