/* /Components/BoMau/BieuTuongChuDe.razor.rz.scp.css */
/* ═══════════ HIỆU ỨNG CHO BIỂU TƯỢNG CHỦ ĐỀ — đợt 1340 (việc o541) ═══════════
   Anh Tiến giao 26/08/2026: *"đồ họa 3D đẹp và có hiệu ứng"*.

   ⚠ TỆP .razor.css ⇒ Blazor gộp sẵn vào `TKNB.Web.styles.css` mà `App.razor` đã nạp —
     ⛔ KHÔNG phải thêm thẻ <link> nào (đúng khuôn `NutChonBoMau.razor.css` bên cạnh).

   🔑 KHỐI 3D LÀ VIỆC CỦA SVG, ⛔ KHÔNG PHẢI CỦA TỆP NÀY. Bề dày khối, dải sáng, bóng đổ
     đều nằm trong chính thẻ <svg> — nhờ vậy biểu tượng vẫn ra hình khối đầy đủ cả khi tệp
     CSS này không tới nơi (mạng cắt, gói scoped cũ). Ở đây chỉ có phần CHUYỂN ĐỘNG.
     ⇒ Mất tệp này thì biểu tượng đứng yên, ⛔ không phải biến thành mảng màu phẳng.

   ⚠ Vệt loé cố ý GIỮ `transform="rotate(20 32 32)"` ngay trên thẻ <rect> trong SVG, dù CSS
     bên dưới sẽ ghi đè (CSS `transform` thắng thuộc tính `transform` của SVG). Lý do: nếu
     một ngày gói scoped không tới nơi, vệt loé vẫn hiện ĐÚNG CHỖ và chỉ đứng yên — hỏng mà
     NHÌN THẤY ĐƯỢC. Bỏ thuộc tính đó đi thì dải sáng nằm thẳng đứng, trông như lỗi vẽ.
     🔑 Tâm quay `32 32` phải TRÙNG gốc quay của CSS (`transform-box: view-box` ⇒ 50% 50%
       của khung 64×64) — lệch tâm là bản CÓ css và bản KHÔNG css vẽ ra hai chỗ khác nhau
       (đợt 1354; trước đó ghi `rotate(20 0 0)` và vệt nằm ngoài mép). */

.btd[b-2ckiq8j913] {
    display: block;
    width: 2.4rem;
    height: 2.4rem;
    flex: none;                 /* nằm trong hàng flex: ⛔ đừng để bị bóp méo khi nhãn dài */
    overflow: visible;          /* lá nhô ra mép không bị cắt cụt */
}

/* Cỡ nhỏ — dùng cho nút mở menu trên thẻ tài khoản, nơi chỗ rất hẹp. */
.btd-nho[b-2ckiq8j913] {
    width: 1.35rem;
    height: 1.35rem;
}

/* Ô emoji của nhánh lối lui: căn cho bằng chỗ đúng bằng một biểu tượng thật, kẻo hàng
   nhảy cỡ khi có chủ đề chưa kịp vẽ. */
.btd-lui[b-2ckiq8j913] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    line-height: 1;
}

.btd-nho.btd-lui[b-2ckiq8j913] { font-size: .95rem; }

/* ───────────────── Vệt loé quét qua mặt khối ─────────────────
   ⭐ ĐỢT 1354 — anh Tiến 26/08/2026: *"hiệu ứng của logo E10 đang bị lỗi chạy bóng đến nửa
     nút thì dừng"*.

   🔑 BẢN CŨ DỪNG THẬT, và nó dừng ở chỗ NHÌN THẤY ĐƯỢC: mốc cuối `translateX(78px)` giữ
     nguyên từ 55% tới 100%, tức gần NỬA chu kỳ (1,6 giây trên 3,6) vệt đứng chết một chỗ.
     Ý đồ ban đầu là "nghỉ giữa hai lượt quét", nhưng nghỉ chỉ vô hình khi vệt đã ra HẲN
     ngoài khung — mà nó thì chưa.
   ⇒ Nay nhịp nghỉ chạy với `opacity: 0`. Đây mới là chỗ chặn gốc bệnh: dù quãng dịch có
     tính lệch vài đơn vị (hệ toạ độ SVG lồng `translate` + `scale`, gốc quay do
     `transform-box: view-box` quyết) thì vệt cũng ⛔ KHÔNG THỂ đứng lại giữa mặt nút.
     ⛔ Đừng "dọn gọn" bằng cách bỏ hai mốc opacity và chỉ tăng quãng dịch — thế là quay về
     đúng thế cũ, chỉ khác con số, và lỗi sẽ lặng lẽ trở lại khi ai đó đổi cỡ biểu tượng.

   ⚠ Hai mốc 42%/43% sát nhau là CỐ Ý (tắt gần như tức thời ngay khi vệt vừa ra khỏi mép),
     và 99%/100% cũng vậy (bật lại đúng lúc vệt đã lùi về chỗ xuất phát). Nới rộng khoảng
     giữa chúng là thấy vệt mờ dần / hiện dần giữa không trung.
   ⚠ Quãng ±72px đo cho vệt tĩnh đặt ở x=18…34 (xem thẻ <rect> trong .razor): đủ để cả bốn
     góc của dải nghiêng 20° ra khỏi khung 64×64. Đổi cỡ vệt thì tính lại quãng này. */
@keyframes btd-quet-b-2ckiq8j913 {
    0%   { transform: rotate(20deg) translateX(-72px); opacity: 1; }
    42%  { transform: rotate(20deg) translateX(72px);  opacity: 1; }
    43%  { transform: rotate(20deg) translateX(72px);  opacity: 0; }
    99%  { transform: rotate(20deg) translateX(-72px); opacity: 0; }
    100% { transform: rotate(20deg) translateX(-72px); opacity: 1; }
}

.btd-loe[b-2ckiq8j913] {
    animation: btd-quet-b-2ckiq8j913 3.6s ease-in-out infinite;
}

/* ───────────────── Tán lá "thở" ─────────────────
   ⚠ `transform-box: fill-box` là BẮT BUỘC: mặc định của SVG là `view-box`, tức gốc toạ độ
     nằm ở góc khung ⇒ `scale()` sẽ ĐẨY cả cụm lá trượt đi thay vì phình nhẹ tại chỗ. */
@keyframes btd-tho-b-2ckiq8j913 {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.025); }
}

.btd-tan[b-2ckiq8j913] {
    transform-box: fill-box;
    transform-origin: center;
    animation: btd-tho-b-2ckiq8j913 5.2s ease-in-out infinite;
}

/* Lớp lá non lệch pha với tán chính — hai lớp thở cùng nhịp thì trông như một khối
   cao su, chứ không ra cây. */
.btd-tan-cham[b-2ckiq8j913] {
    animation-duration: 6.8s;
    animation-delay: -1.7s;
}

/* ───────────────── Hoa lấp lánh ─────────────────
   Lệch pha từng bông bằng `--i` (chỉ số đặt qua nth-child): cả chùm nháy cùng lúc là
   thành đèn báo, không phải hoa. */
@keyframes btd-lap-lanh-b-2ckiq8j913 {
    0%, 100% { opacity: .35; }
    50%      { opacity: 1; }
}

.btd-hoa circle[b-2ckiq8j913] {
    animation: btd-lap-lanh-b-2ckiq8j913 2.8s ease-in-out infinite;
}

.btd-hoa circle:nth-child(3n)[b-2ckiq8j913]   { animation-delay: -.9s; }
.btd-hoa circle:nth-child(3n+1)[b-2ckiq8j913] { animation-delay: -1.8s; }
.btd-hoa circle:nth-child(4n)[b-2ckiq8j913]   { animation-duration: 3.6s; }

/* ───────────────── Lá bay ───────────────── */
@keyframes btd-bay-b-2ckiq8j913 {
    0%   { transform: translate(0, 6px) rotate(0deg);      opacity: 0; }
    18%  {                                                 opacity: .95; }
    100% { transform: translate(-9px, -26px) rotate(-38deg); opacity: 0; }
}

.btd-bay[b-2ckiq8j913] {
    transform-box: fill-box;
    transform-origin: center;
    animation: btd-bay-b-2ckiq8j913 6.5s linear infinite;
}

.btd-bay2[b-2ckiq8j913] { animation-duration: 8.2s; animation-delay: -3.4s; }

/* ───────────────── Dòng chảy dưới chân ─────────────────
   Chạy bằng `stroke-dashoffset` chứ ⛔ không phải `translateX`: đường cong dịch ngang thì
   hai đầu lộ ra khỏi khung, còn nét đứt chạy thì dòng chảy trông như đang trôi tại chỗ. */
@keyframes btd-troi-b-2ckiq8j913 {
    from { stroke-dashoffset: 0; }
    to   { stroke-dashoffset: -46; }
}

.btd-dong[b-2ckiq8j913] {
    stroke-dasharray: 14 9;
    animation: btd-troi-b-2ckiq8j913 3.4s linear infinite;
}

.btd-dong2[b-2ckiq8j913] { animation-duration: 4.6s; }

/* ───────────────── Tôn trọng người tắt hiệu ứng ─────────────────
   ⚠ ⛔ ĐỪNG đổi thành `display: none` cho `.btd-loe`: mất vệt loé thì mặt khối phẳng hẳn
     đi. Dừng animation thì nó đứng lại ở CHỖ CŨ (thuộc tính `transform` trong SVG), vẫn là
     một dải sáng nằm đúng chỗ. */
@media (prefers-reduced-motion: reduce) {
    .btd-loe[b-2ckiq8j913],
    .btd-tan[b-2ckiq8j913],
    .btd-hoa circle[b-2ckiq8j913],
    .btd-bay[b-2ckiq8j913],
    .btd-dong[b-2ckiq8j913] {
        animation: none;
    }

    .btd-bay[b-2ckiq8j913] { opacity: .9; }
}
/* /Components/BoMau/HuyHieuChuDe.razor.rz.scp.css */
/* ═══════════ HUY HIỆU SỰ KIỆN TRÊN THANH NAV — đợt 1340 (việc o541) ═══════════
   Anh Tiến 26/08/2026: *"khi bật chủ đề thì sẽ hiển thị thêm trên thanh nav TRÔI VỀ BÊN PHẢI"*.

   🔑 CƠ CHẾ HIỆN/ẨN GIỐNG HỆT DẤU ✓ CỦA NÚT CHỦ ĐỀ: `data-bo-mau` nằm sẵn trên thẻ <html> (do
     `bo-mau.js` đặt ngay lúc nạp), CSS chỉ việc so nó với `data-ma`. ⛔ Không một dòng C# nào —
     máy chủ không biết người dùng đang ở chủ đề nào (xem đầu tệp .razor).
   ⚠ Thêm chủ đề có huy hiệu riêng thì thêm MỘT dòng ở khối "hiện" bên dưới. Quên thì huy hiệu
     không bao giờ hiện — hỏng nhẹ và thấy được, ⛔ không phải hỏng câm. */

.hhc[b-z14xval4a5] {
    display: flex;
    align-items: center;
    /* ⛔ KHÔNG `flex: 1` — thanh nav có luật MỘT HÀNG (đợt 654). Huy hiệu phải chịu co lại
       chứ tuyệt đối không được đẩy cụm tài khoản xuống hàng thứ hai. */
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
}

/* Mặc định ẩn HẾT — chủ đề nào có huy hiệu thì tự bật ở khối dưới. Làm ngược lại (mặc định hiện)
   là mọi chủ đề chưa khai đều trưng nhầm huy hiệu của sự kiện khác. */
.hhc-mot[b-z14xval4a5] { display: none; }

:root[data-bo-mau="e10"] .hhc-mot[data-ma="e10"][b-z14xval4a5] {
    display: flex;
    align-items: center;
    gap: .45rem;
}

.hhc-hh[b-z14xval4a5] {
    display: block;
    height: 2.15rem;
    width: auto;
    flex: none;
    /* Bóng nhẹ để con tem tách khỏi dải nav ở CẢ nền sáng lẫn nền tối — hai huy hiệu đều có
       khung TRẮNG nên trên dải trời xanh chúng vốn đã nổi, bóng chỉ làm rõ mép. */
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .28));
}

/* ───────────────── Hiệu ứng TRƯỢT VÀO ─────────────────
   Trượt vào từ TRÁI rồi dừng ở chỗ của nó — đó là nghĩa của "trôi về bên phải" trong câu anh Tiến
   dặn ở đợt 1340.
   📍 ĐỢT 1470 (việc o672) — con tem nay đứng ở CỤM BÊN TRÁI, kế logo Petrolimex (anh Tiến
     29/08/2026: *"chuyển nút E10 sang kế icon petro bên trái"*). Hiệu ứng GIỮ NGUYÊN: nó là một
     lượt trượt-vào lúc dựng trang, ⛔ không phải phép đặt chỗ — chỗ đứng do `MainLayout.razor`
     quyết. ⛔ Đừng đảo chiều thành trượt-từ-phải cho "hợp bên trái": con tem sẽ chạy đè lên logo
     cơ quan trong 0,75 giây mỗi lượt tải trang.
   ⚠ Tên `hhc-troi-phai` giữ nguyên — nó bị gọi ở dòng `animation` phía dưới; đổi tên mà sót một
     chỗ là hoạt hình chết LẶNG LẼ (CSS ⛔ không báo lỗi tên keyframes không có thật).
   ⛔ Cố ý KHÔNG cho chạy lặp vô hạn: thanh nav có mặt ở MỌI trang, một vật thể động không
   ngừng ngay cạnh chỗ làm việc là thứ gây mỏi mắt nhất, mà lại chẳng thêm thông tin gì sau lượt
   nhìn đầu. */
@keyframes hhc-troi-phai-b-z14xval4a5 {
    from { transform: translateX(-4.5rem) scale(.86); opacity: 0; }
    60%  { opacity: 1; }
    to   { transform: translateX(0) scale(1); opacity: 1; }
}

/* Vệt sáng lướt qua mặt kính — chi tiết làm con tem "bóng" chứ không phải một mảng màu.
   ⛔ Cố ý chạy THƯA (7,5s) và chỉ trong 1,1s: thanh nav có mặt ở MỌI trang, ánh sáng quét liên
     tục ngay cạnh chỗ làm việc là thứ gây mỏi mắt nhất.

   🔴 ĐỢT 1433 — BẢN CŨ ĐỨNG CHẾT GIỮA CON TEM E10 SUỐT 86% CHU KỲ. Đây đúng lỗi anh Tiến báo ở
     đợt 1354 (*"hiệu ứng của logo E10 đang bị lỗi chạy bóng đến nửa nút thì dừng"*); lượt ấy chữa
     cho BIỂU TƯỢNG chủ đề (`btd-quet`) mà ⛔ quên con tem trên thanh nav — nơi 433 tài khoản nhìn
     thấy ở MỌI trang, MỌI lượt tải, vì E10 đang áp chung.
   📐 Vì sao: một quãng dịch cố định `210px` dùng cho HAI huy hiệu có khung KHÁC NHAU.
     · EURO 5 — viewBox rộng **64**, vệt đặt ở x=-30 ⇒ 210 đưa nó ra hẳn ngoài. ✅
     · E10   — viewBox rộng **208**, vệt đặt ở x=-40 ⇒ mép trái vệt mới tới x≈170, tức **còn nằm
       trong khung**, phủ đúng vùng chữ E10 rồi đứng im 6,45 giây mỗi vòng. Người tắt hiệu ứng
       (`prefers-reduced-motion`) thì thấy nó **vĩnh viễn** — nhánh cuối tệp đẩy về đúng chỗ hỏng.
   ⇒ Chốt gốc bệnh là **nhịp nghỉ chạy với `opacity: 0`**, y khuôn `btd-quet` (xem
     `BieuTuongChuDe.razor.css`): dù quãng dịch có tính lệch bao nhiêu, hoặc mai này ai đổi cỡ tem,
     vệt cũng ⛔ KHÔNG THỂ đứng lại giữa mặt tem. Quãng nâng 210 → **270** chỉ là vế thứ hai.
   ⛔ Đừng "dọn gọn" bằng cách bỏ hai mốc opacity và chỉ giữ con số 270 — thế là quay về đúng thế
     cũ, và lỗi sẽ lặng lẽ trở lại ở con tem tiếp theo có khung rộng hơn.
   ⚠ Hai mốc 14%/15% sát nhau là CỐ Ý (tắt gần như tức thì khi vệt vừa ra khỏi mép), 99%/100% cũng
     vậy (bật lại đúng lúc vệt đã lùi về chỗ xuất phát). Nới rộng khoảng giữa chúng là thấy vệt mờ
     dần / hiện dần giữa không trung. */
@keyframes hhc-quet-b-z14xval4a5 {
    0%      { transform: rotate(18deg) translateX(0);     opacity: 1; }
    14%     { transform: rotate(18deg) translateX(270px); opacity: 1; }
    15%     { transform: rotate(18deg) translateX(270px); opacity: 0; }
    99%     { transform: rotate(18deg) translateX(0);     opacity: 0; }
    100%    { transform: rotate(18deg) translateX(0);     opacity: 1; }
}

.hhc-loe[b-z14xval4a5] {
    animation: hhc-quet-b-z14xval4a5 7.5s ease-in-out infinite;
}

:root[data-bo-mau="e10"] .hhc-mot[data-ma="e10"] .hhc-hh[b-z14xval4a5] {
    animation: hhc-troi-phai-b-z14xval4a5 .75s cubic-bezier(.22, .9, .3, 1) both;
}

/* Con tem thứ hai vào sau một nhịp — hai cái cùng trượt thì trông như một mảng, lệch pha thì
   mắt đọc ra "hai con tem". */
:root[data-bo-mau="e10"] .hhc-mot[data-ma="e10"] .hhc-e10[b-z14xval4a5] {
    animation-delay: .12s;
}

/* ───────────────── Khổ hẹp: bỏ bớt, giữ luật MỘT HÀNG ─────────────────
   Dưới 1200px thì chỉ giữ con tem E10 (nó mang đúng tên chủ đề); dưới 900px thì ẩn hẳn cụm.
   ⛔ Đừng thu nhỏ tiếp thay vì ẩn: chữ trên tem đã ở ngưỡng đọc được rồi, nhỏ hơn nữa là một
   vệt màu vô nghĩa chiếm chỗ của dải tab. */
@media (max-width: 1200px) {
    .hhc-euro[b-z14xval4a5] { display: none; }
}

@media (max-width: 900px) {
    .hhc[b-z14xval4a5] { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    :root[data-bo-mau="e10"] .hhc-mot[data-ma="e10"] .hhc-hh[b-z14xval4a5],
    .hhc-loe[b-z14xval4a5] {
        animation: none;
    }

    /* ⛔ Đừng để `.hhc-loe` đứng nguyên tại chỗ xuất phát: chỗ đó là MÉP TRÁI con tem, dừng ở đấy
       thì có một vệt sáng dựng đứng trông như lỗi vẽ.
       🔴 ĐỢT 1433 — bản cũ chữa bằng cách ĐẨY RA NGOÀI (`translateX(210px)`), và với con tem E10
         khung 208 thì **210 chưa ra tới nơi** ⇒ người tắt hiệu ứng nhìn thấy vệt sáng đè lên chữ
         E10 VĨNH VIỄN. Ẩn hẳn bằng `opacity` là phép chắc chắn: nó ⛔ không phụ thuộc bề rộng
         khung, nên đúng cho mọi con tem thêm về sau. Vệt loé thuần trang trí, ẩn đi ⛔ không mất
         một thông tin nào. */
    .hhc-loe[b-z14xval4a5] { opacity: 0; }
}
/* /Components/BoMau/NutChonBoMau.razor.rz.scp.css */
/* ═══════════ Kiểu dáng nút chọn bộ màu (đợt 608) ═══════════
   Tệp .razor.css ⇒ Blazor gộp sẵn vào TKNB.Web.styles.css mà App.razor đã nạp
   từ trước ⇒ KHÔNG phải thêm thẻ <link> nào. Cố ý chọn đường này: bớt một dòng
   phải chèn vào App.razor là bớt một chỗ để quên.

   ⚠ Dáng ở đây cố ý TRUNG TÍNH (dùng currentColor + màu Bootstrap gốc): nút này
   nằm trên thanh trên, mà thanh trên có tới 4 dáng khác nhau (sáng/tối/cổ điển/
   cổ điển tối) NHÂN với từng bộ màu. Tô màu cứng vào đây là chắc chắn chỏi ở một
   tổ hợp nào đó. */

.cbm-boc[b-fzo0jsof56] {
    position: relative;
    display: inline-block;
}

/* Bỏ mũi tam giác mặc định của <summary> — thẻ này đang đóng vai một cái nút. */
.cbm-nut[b-fzo0jsof56] {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .2rem .55rem;
    border: 1px solid transparent;
    border-radius: .25rem;
    white-space: nowrap;
    user-select: none;
}

.cbm-nut[b-fzo0jsof56]::-webkit-details-marker { display: none; }

.cbm-nut:hover[b-fzo0jsof56],
.cbm-boc[open] .cbm-nut[b-fzo0jsof56] {
    border-color: currentColor;
}

/* Vòng lấy nét bàn phím: người dùng bàn phím không có gì khác để biết mình đang ở đâu. */
.cbm-nut:focus-visible[b-fzo0jsof56] {
    outline: 2px solid currentColor;
    outline-offset: 1px;
}

.cbm-bang[b-fzo0jsof56] {
    position: absolute;
    top: calc(100% + .25rem);
    right: 0;
    z-index: 1080;
    min-width: 15rem;
    padding: .3rem;
    background-color: var(--bm-nen-the, #ffffff);
    color: var(--bm-chu, #212529);
    border: 1px solid var(--bm-vien, #dee2e6);
    border-radius: .25rem;
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .18);
}

/* ═══ ⭐ ĐỢT 1275 (việc o462) — BẢNG NÀY TỪNG RA NỀN TRẮNG GIỮA GIAO DIỆN TỐI ═══
   Ba biến `--bm-*` ở trên chỉ có giá trị khi người dùng ĐÃ BẤM CHỌN một bộ màu. Bộ **mặc định**
   (`petro-co-dien`) CỐ Ý không khai biến nào — đó là "chốt an toàn số 1" của `bo-mau/ap-dung.css`:
   máy nào chưa bấm gì thì thẻ <html> không mang `data-bo-mau` nên không luật bộ màu nào khớp.
   ⇒ Với ĐA SỐ người dùng, cả ba `var()` rơi về **giá trị lui SÁNG** ngay giữa nền tối.

   📏 Đo 25/08/2026 (bản tĩnh `--chu-de dark`, chốt ngược `d-flex` ĐẠT): nền bảng ra `#ffffff`, trong
     khi mục chọn `.cbm-muc` là thẻ <a> nên ăn màu chữ nền tối `#dde1e6` ⇒ **1,31:1** — chữ trắng
     trên nền trắng, không đọc được. (Tiêu đề và ghi chú thì vẫn đen nên bảng "trông có chữ" —
     đúng kiểu hỏng nửa vời khó nghi nhất.)
   🔴 Nó **hỏng lặng lẽ** và mãi không ai thấy vì hai lẽ: bảng chỉ hiện sau HAI cú bấm (ảnh đại diện
     → 🎨), và `.cbm-muc { color: inherit }` ở trên đọc rất thuyết phục — nhưng luật màu liên kết của
     giao diện tối có độ đặc hiệu cao hơn nên `inherit` **không bao giờ tới nơi**.
   ⛔ Giá trị lui SÁNG ở trên giữ nguyên: giao diện sáng đang đúng (đo cùng lượt: nền trắng, chữ
     `#212529` = 15,9:1) và quy tắc #18 — ⛔ đừng đổi thứ đang chạy đúng cho 433 tài khoản.
   ⓘ Ba con số dưới đây lấy từ **nhánh tối của bộ màu mẫu** (`bo-mau/_mau-chep-va-doi.css`), ⛔ không
     bịa màu mới: người đã chọn bộ màu vẫn thắng nhờ chính `var()` — luật này chỉ đỡ ca KHÔNG có biến. */
:root[data-theme="dark"] .cbm-bang[b-fzo0jsof56] {
    background-color: var(--bm-nen-the, #262a31);
    color: var(--bm-chu, #dde1e6);
    border-color: var(--bm-vien, #3a3f47);
}

.cbm-tieu-de[b-fzo0jsof56] {
    padding: .2rem .5rem .35rem;
    font-size: .78rem;
    font-weight: 600;
    opacity: .75;
}

.cbm-muc[b-fzo0jsof56] {
    display: flex;
    align-items: center;
    gap: .45rem;
    padding: .35rem .5rem;
    border-radius: .2rem;
    color: inherit;
    text-decoration: none;
}

.cbm-muc:hover[b-fzo0jsof56],
.cbm-muc:focus-visible[b-fzo0jsof56] {
    background-color: rgba(127, 127, 127, .18);
    text-decoration: none;
}

/* ───── Dấu ✓ của bộ màu ĐANG DÙNG ─────
   Không cần một dòng mã nào: `data-bo-mau` nằm sẵn trên thẻ <html> (do bo-mau.js
   đặt), CSS chỉ việc so nó với `data-ma` của từng mục.
   ⚠ Phải liệt kê TỪNG bộ màu một — CSS không so được hai thuộc tính với nhau.
   Thêm bộ màu mới thì thêm một cặp dòng ở đây; quên thì mục đó chỉ mất dấu ✓,
   bấm vẫn đổi màu bình thường (hỏng nhẹ, không phải hỏng câm). */
.cbm-muc[b-fzo0jsof56]::after {
    content: "";
    margin-left: auto;
    font-weight: 700;
}

:root[data-bo-mau="petro-co-dien"] .cbm-muc[data-ma="petro-co-dien"][b-fzo0jsof56]::after,
:root:not([data-bo-mau]) .cbm-muc[data-ma="petro-co-dien"][b-fzo0jsof56]::after,
:root[data-bo-mau="e10"] .cbm-muc[data-ma="e10"][b-fzo0jsof56]::after,
:root[data-bo-mau="ky-niem-81-nam"] .cbm-muc[data-ma="ky-niem-81-nam"][b-fzo0jsof56]::after {
    content: "✓";
}

.cbm-ghi-chu[b-fzo0jsof56] {
    margin: .3rem .5rem 0;
    padding-top: .3rem;
    border-top: 1px solid var(--bm-vien, #dee2e6);
    font-size: .72rem;
    opacity: .7;
}

/* Vạch ngăn của dòng ghi chú cuối bảng — cùng bệnh, cùng thuốc như khối ⭐ ở trên. Bỏ sót dòng này
   thì ở nền tối còn lại đúng một vạch SÁNG nằm giữa bảng, trông như lỗi vẽ.
   ⓘ Đặt SAU luật thường là cố ý: luật thường dùng shorthand `border-top`, luật này dùng longhand
     `border-top-color`. Độ đặc hiệu (0,3,0) đã thắng (0,2,0) nên thứ tự không đổi kết quả — nhưng
     người đọc sau này thấy shorthand nằm dưới thì rất dễ tưởng nó nuốt mất longhand ở trên. */
:root[data-theme="dark"] .cbm-ghi-chu[b-fzo0jsof56] {
    border-top-color: var(--bm-vien, #3a3f47);
}

/* ═══ ⭐ ĐỢT 1340 (việc o541) — CHỖ ĐẶT BIỂU TƯỢNG 3D CỦA TỪNG CHỦ ĐỀ ═══
   Anh Tiến 26/08/2026: *"icon giống trong hình, đồ họa 3D đẹp và có hiệu ứng"*.
   Hình vẽ nằm trong `BieuTuongChuDe.razor`; tệp này chỉ lo CHỖ ĐỨNG và CỠ.

   ⚠ Mọi luật chạm vào phần tử của component CON phải đi qua `::deep` — CSS cách ly chỉ
     gắn dấu phạm vi vào phần tử do CHÍNH tệp .razor này viết ra. Thiếu `::deep` thì luật
     biên dịch thành `.btd[b-xxx]` và ⛔ không bao giờ khớp: build xanh, test xanh, biểu
     tượng vẫn hiện — chỉ mất đúng phần chỉnh cỡ, tức hỏng LẶNG LẼ. */

/* Bảng nới thêm: 15rem vừa đủ cho ô emoji, nhưng biểu tượng 2,4rem thì tên chủ đề
   ("E10 — xăng sinh học") bị ngắt xuống hai dòng. */
.cbm-bang[b-fzo0jsof56] {
    min-width: 16.5rem;
}

.cbm-muc[b-fzo0jsof56] {
    gap: .6rem;
}

/* Biểu tượng nảy nhẹ khi rê chuột / lấy nét bàn phím — dấu hiệu "mục này bấm được". */
.cbm-muc[b-fzo0jsof56]  .btd {
    transition: transform .18s ease;
}

.cbm-muc:hover[b-fzo0jsof56]  .btd,
.cbm-muc:focus-visible[b-fzo0jsof56]  .btd {
    transform: scale(1.08);
}

@media (prefers-reduced-motion: reduce) {
    .cbm-muc:hover[b-fzo0jsof56]  .btd,
    .cbm-muc:focus-visible[b-fzo0jsof56]  .btd {
        transform: none;
    }
}

/* ───── Nút mở menu trưng biểu tượng của chủ đề ĐANG DÙNG ─────
   Cùng khuôn với dấu ✓ ở trên: `data-bo-mau` nằm sẵn trên thẻ <html> (do bo-mau.js đặt),
   CSS chỉ việc so nó với `data-ma` của từng biểu tượng đã dựng sẵn. ⛔ Không một dòng mã
   C# nào — component này cố ý không giữ trạng thái (xem đầu tệp .razor).

   🔑 THỨ TỰ HAI LUẬT DƯỚI ĐÂY LÀ CÓ CHỦ Ý: mặc định ẩn hết biểu tượng và HIỆN ô 🎨, rồi mỗi
     chủ đề đã khai mới lật ngược lại. Làm ngược (mặc định ẩn 🎨) thì chủ đề mới chưa kịp
     khai sẽ để lại một cái nút TRỐNG TRƠN — hỏng câm. Kiểu này thì tệ nhất cũng chỉ về ô 🎨
     như trước đợt 1340. */
.cbm-dang-dung[b-fzo0jsof56] {
    display: inline-flex;
    align-items: center;
    line-height: 1;
}

.cbm-dang-dung > [data-ma][b-fzo0jsof56] { display: none; }

.cbm-mac-dinh[b-fzo0jsof56] {
    display: inline-block;
    font-size: 1rem;
    line-height: 1;
}

:root:not([data-bo-mau]) .cbm-dang-dung > [data-ma="petro-co-dien"][b-fzo0jsof56],
:root[data-bo-mau="petro-co-dien"] .cbm-dang-dung > [data-ma="petro-co-dien"][b-fzo0jsof56],
:root[data-bo-mau="e10"] .cbm-dang-dung > [data-ma="e10"][b-fzo0jsof56],
:root[data-bo-mau="ky-niem-81-nam"] .cbm-dang-dung > [data-ma="ky-niem-81-nam"][b-fzo0jsof56] {
    display: inline-block;
}

:root:not([data-bo-mau]) .cbm-mac-dinh[b-fzo0jsof56],
:root[data-bo-mau="petro-co-dien"] .cbm-mac-dinh[b-fzo0jsof56],
:root[data-bo-mau="e10"] .cbm-mac-dinh[b-fzo0jsof56],
:root[data-bo-mau="ky-niem-81-nam"] .cbm-mac-dinh[b-fzo0jsof56] {
    display: none;
}
/* /Components/ChuKySo/KhungKySo.razor.rz.scp.css */
/* ═══ KHỐI KÝ SỐ DÙNG CHUNG — đợt 2936, việc o2154 ═══

   Khối này nằm LỒNG trong màn hình của phân hệ khác (hồ sơ sửa chữa, đơn nghỉ phép…) nên nó phải tự
   tách mình ra khỏi nội dung xung quanh bằng viền + nền, ⛔ mượn khung của trang chủ nhà: mỗi trang một
   khung khác nhau, mượn là mỗi nơi một kiểu.

   ⚠ Màu lấy từ BIẾN CHUNG của app (--bs-*), ⛔ gõ mã màu cứng: app có nền sáng và nền tối, gõ cứng là
   khối này sáng trắng giữa trang tối. */

.kks-khoi[b-ad80gp4uph] {
    border: 1px solid var(--bs-border-color);
    border-left: 4px solid var(--bs-success);
    border-radius: .5rem;
    padding: .75rem .9rem;
    margin-top: 1rem;
    background: var(--bs-body-bg);
}

.kks-dau[b-ad80gp4uph] {
    display: flex;
    flex-wrap: wrap;          /* Khổ điện thoại: tên văn bản dài thì xuống dòng, ⛔ đẩy trang trôi ngang. */
    align-items: baseline;
    gap: .5rem;
    margin-bottom: .5rem;
}

.kks-ten[b-ad80gp4uph] {
    font-weight: 600;
}

.kks-hop-pin[b-ad80gp4uph] {
    border-top: 1px dashed var(--bs-border-color);
    padding-top: .6rem;
    margin-top: .2rem;
}
/* /Components/ChungTu/BangRonChay.razor.rz.scp.css */
/* ═══ BĂNG RÔN CHẠY — đợt 1359 (việc o558) · DỜI LÊN THANH NAV ở đợt 1367 (việc o564) ═══
   Dải chữ chạy ngang dịp kỷ niệm.

   ⭐ ĐỢT 1367 — anh Tiến 26/08/2026: *"em đưa băng rôn lên thanh nav nhưng KHÔNG ĐƯỢC CHE CÁC NÚT,
     đồ hoạ chữ cho đẹp và có hiệu ứng"*.

   🔑 CÁCH GIỮ LỜI HỨA "KHÔNG CHE NÚT" — đọc kỹ trước khi đổi bất kỳ con số nào dưới đây.
     Băng rôn là một Ô CO GIÃN CỦA CHÍNH `.top-row` (flex item), ⛔ KHÔNG phải một lớp phủ
     `position: absolute` nằm đè lên thanh. Đây là khác biệt sống còn: lớp phủ thì "che hay không
     che" phụ thuộc vào việc đoán đúng bề rộng hai cụm nút ở hai đầu — mà hai cụm đó đổi bề rộng
     theo TRANG, theo VAI TRÒ (người bị chặn hết / tài khoản trình chiếu không có cụm trái) và theo
     việc anh thêm nút mới. Đoán trượt là băng rôn nằm đè lên nút, mà ⛔ không phép đo nào của app
     bắt được. Là ô co giãn thì trình duyệt TỰ chia chỗ: băng rôn chỉ ăn phần CÒN LẠI, và
     `gap: 1rem` của `.top-row` luôn chừa một quãng thở giữa nó với hàng xóm.

   📏 ĐO THẬT 26/08/2026 (bản tĩnh, khung 1600px, chốt ngược `d-flex` ĐẠT) — khoảng trống giữa cụm
     điều hướng bên trái và cụm tài khoản bên phải:
       · `/` trang chủ ........ 1110px (không có dải tab nào)
       · `/quan-tri-mang` ......  776px (dải tab 457px)
       · `/cong-viec` ..........  738px (dải app 495px)
       · `/nuoc` ...............  582px (dải tab 651px — trang CHẬT NHẤT đo được)
     ⇒ Chỗ trống là CÓ THẬT ở mọi trang, ⛔ không phải phỏng đoán.

   ⚠ Màu CỐ ĐỊNH cho cả giao diện sáng lẫn tối, không lấy theo bộ màu đang chọn: đây là tấm băng rôn
     lễ, nó phải giữ đúng sắc đỏ - vàng ở mọi chủ đề. Đổi màu theo chủ đề là có chủ đề cho ra băng rôn
     xanh nhạt chữ xám, tức mất hẳn ý nghĩa của thứ đang treo.
   ⚠ Chiều cao KẸP CỨNG 2rem: thanh nav có luật MỘT HÀNG (đợt 654) và `min-height` của nó là 3.5rem.
     Cao hơn là băng rôn tự kéo cao cả thanh nav ở MỌI trang, MỌI ngày trong đợt treo. */

/* ───────────────── Tấm băng rôn ───────────────── */
.bang-ron[b-zxa95aeiuc] {
    position: relative;
    display: flex;
    align-items: center;
    align-self: center;
    height: 2rem;
    overflow: hidden;
    border-radius: 999px;
    /* Nền lụa đỏ: hai mép sẫm, giữa tươi — mắt đọc ra một tấm vải căng chứ không phải một ô màu phẳng. */
    background: linear-gradient(90deg, #5c0a0e 0%, #7e0f14 18%, #96131a 50%, #7e0f14 82%, #5c0a0e 100%);
    border: 1px solid #ffd54f;
    /* Ba lớp: bóng đổ tách khỏi thanh nav · viền vàng thứ hai bên trong (dáng khung lễ) · gờ sáng trên. */
    box-shadow: 0 1px 3px rgba(0, 0, 0, .35),
                inset 0 0 0 2px rgba(255, 213, 79, .22),
                inset 0 1px 0 rgba(255, 255, 255, .18);
    /* Vào một lần rồi thôi. ⛔ Cố ý KHÔNG lặp: dải chữ đã chạy không ngừng rồi, thêm một vật thể
       động thứ hai lặp mãi ngay cạnh chỗ làm việc là thứ gây mỏi mắt nhất (bài học đợt 1340). */
    animation: bang-ron-vao-b-zxa95aeiuc .7s cubic-bezier(.22, .9, .3, 1) both;
}

/* ───────────────── HAI ĐẦU: TRỤC CUỘN VẢI ─────────────────
   ⭐ ĐỢT 1426 (việc o621) — anh Tiến 27/08/2026: *"băng rôn đồ hoạ như CUỘN VẢI có hoạ tiết đẹp"*.

   🔑 HAI THẺ NÀY VỐN LÀ DẢI MỜ, VÀ VIỆC ẤY ⛔ KHÔNG ĐƯỢC MẤT. Trước đợt 1426 chúng chỉ là một dải
     `#5c0a0e → trong suốt` để chữ TRÔI VÀO và TRÔI RA thay vì bị chặt cụt ở mép bo tròn. Nay chúng
     mang thêm dáng một khúc vải quấn quanh trục, nhưng NỬA TRONG vẫn phải tan dần về trong suốt —
     bỏ vế đó là chữ lại bị cắt ngang giữa nét ở đúng hai mép. Vì thế dáng trục nằm gọn trong NỬA
     NGOÀI (0 → 34%), phần còn lại vẫn đúng là dải mờ cũ.
   ⛔ Đừng làm bằng `mask-image` trên khối đang chạy: mặt nạ sẽ TRƯỢT THEO chữ, tức mờ nhầm chỗ.
   ⓘ Vệt ánh đồng `#d2a05a` nằm ở ~15%, tức trong vùng vải ĐỤC HOÀN TOÀN ⇒ ⛔ không có chữ nào nằm
     trên nó ⇒ ⛔ không đụng con số tương phản 4,66:1 đã đo ở đợt 1367. Đây là chỗ DUY NHẤT của tấm
     vải được phép dùng ánh kim sáng — xem phép đo ở khối `--vai-hoa-tiet` trong `app.css`. */
.bang-ron[b-zxa95aeiuc]::before,
.bang-ron[b-zxa95aeiuc]::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    width: 2.6rem;
    z-index: 2;
    pointer-events: none;
}

.bang-ron[b-zxa95aeiuc]::before {
    left: 0;
    /* Lớp trên: sợi chỉ vàng viền mép trục. Lớp dưới: thân trục rồi tan dần về trong suốt. */
    background: linear-gradient(90deg, rgba(255, 213, 79, .9) 0 1.5px, rgba(255, 213, 79, 0) 1.5px),
                linear-gradient(90deg, #3a0507 0%, #7c1218 8%, #d2a05a 15%, #6b0d11 23%,
                                #5c0a0e 34%, #5c0a0e 60%, rgba(92, 10, 14, 0) 100%);
}

.bang-ron[b-zxa95aeiuc]::after {
    right: 0;
    background: linear-gradient(270deg, rgba(255, 213, 79, .9) 0 1.5px, rgba(255, 213, 79, 0) 1.5px),
                linear-gradient(270deg, #3a0507 0%, #7c1218 8%, #d2a05a 15%, #6b0d11 23%,
                                #5c0a0e 34%, #5c0a0e 60%, rgba(92, 10, 14, 0) 100%);
}

/* ───────────────── Hoạ tiết gấm chìm + đốm lấp lánh ─────────────────
   ⚠ HAI LỚP NÀY LÀ THẺ RỖNG RIÊNG chứ ⛔ KHÔNG phải một `background-image` chồng lên `.bang-ron`.
     Lý do ở quy tắc #5: `.bang-ron` dựng mặt vải bằng SHORTHAND `background`, mà thêm một
     `background-image` sau đó là GHI ĐÈ mặt vải ấy — mất sạch dải lụa đỏ, ⛔ không một câu lỗi nào.
     Tách thẻ thì biến `--vai-*` hỏng cũng chỉ làm lớp này trong suốt: mặt vải còn nguyên.
   🩸 Đốm sáng nằm DƯỚI chữ (`z-index: 0`, chữ ở `z-index: 1`) nhưng vẫn phải dè chừng: chỗ ⛔ không có
     nét chữ thì người ta nhìn thẳng vào nền, nên một đốm trắng sát cạnh nét vẫn kéo tụt tương phản
     CỤC BỘ. Thứ giữ được nó là `filter: drop-shadow(0 1px 1px rgba(70,0,0,.8))` ở `.bang-ron-truot`
     — mỗi nét chữ có sẵn một quầng sẫm bao quanh. ⛔ Gỡ dòng `filter` ấy thì phải gỡ luôn lớp này.
   ⛔ Đừng đổi hoạ tiết sang chỉ vàng cho "rực rỡ": số đo ở `app.css` đã bác bỏ (4,32:1 < 4,5:1). */
.bang-ron-hoa-tiet[b-zxa95aeiuc],
.bang-ron-lap-lanh[b-zxa95aeiuc] {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    border-radius: inherit;
}

.bang-ron-hoa-tiet[b-zxa95aeiuc] {
    background-image: var(--vai-hoa-tiet);
}

.bang-ron-lap-lanh[b-zxa95aeiuc] {
    background-image: var(--vai-lap-lanh-a);
    animation: bang-ron-lap-lanh-b-zxa95aeiuc 4.5s ease-in-out infinite;
}

/* Chùm đốm thứ hai LỆCH PHA — cả tấm cùng nháy một nhịp thì mắt đọc ra "đèn nhấp nháy", ⛔ không
   phải "lụa lấp lánh". Đặt ở pseudo để ⛔ khỏi thêm một thẻ nữa vào markup. */
.bang-ron-lap-lanh[b-zxa95aeiuc]::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--vai-lap-lanh-b);
    animation: bang-ron-lap-lanh-b-zxa95aeiuc 6.7s ease-in-out infinite;
    animation-delay: -2.4s;
}

/* ───────────────── Vệt sáng lướt qua mặt vải ─────────────────
   ⛔ Cố ý chạy THƯA (9s một vòng, chỉ sáng ~1,4s): xem lý do ở chú thích `animation` của `.bang-ron`. */
.bang-ron-loe[b-zxa95aeiuc] {
    position: absolute;
    top: -60%;
    bottom: -60%;
    left: -3rem;
    width: 4.5rem;
    z-index: 3;
    pointer-events: none;
    background: linear-gradient(90deg,
                rgba(255, 255, 255, 0) 0%,
                rgba(255, 245, 200, .55) 50%,
                rgba(255, 255, 255, 0) 100%);
    filter: blur(2px);
    animation: bang-ron-quet-b-zxa95aeiuc 9s ease-in-out infinite;
}

/* Dải trượt: rộng bằng ĐÚNG hai bản chữ, nên dịch một nửa là về đúng vạch xuất phát.
   `width: max-content` để dải không bị bó theo bề ngang màn hình.
   ⚠ `filter` đặt Ở ĐÂY chứ ⛔ không phải `text-shadow` trên từng chữ: chữ dưới đây được TÔ BẰNG
     GRADIENT (`background-clip: text` + `color: transparent`), mà bóng chữ thì KHÔNG bị cắt theo
     nét — để nguyên `text-shadow` là một vệt mờ hình chữ nhật hiện ra sau chữ trong suốt. */
.bang-ron-truot[b-zxa95aeiuc] {
    display: flex;
    width: max-content;
    position: relative;
    z-index: 1;
    filter: drop-shadow(0 1px 1px rgba(70, 0, 0, .8));
    animation-name: bang-ron-truot-b-zxa95aeiuc;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    will-change: transform;
}

/* ───────────────── Đồ hoạ chữ: nét vàng kim ─────────────────
   ⚠ Khối này phải ĐỨNG TRƯỚC khối `@supports` bên dưới và phải mang `color` ĐỌC ĐƯỢC: trình duyệt
     nào không hiểu `background-clip: text` sẽ dừng ở đây và vẫn thấy chữ vàng. Làm ngược lại (đặt
     `color: transparent` ở khối chung) là chữ BIẾN MẤT hoàn toàn trên trình duyệt cũ — hỏng lặng lẽ
     đúng chỗ tệ nhất: một tấm băng rôn trống trơn treo cho cả cơ quan xem. */
.bang-ron-chu[b-zxa95aeiuc] {
    /* ✍ ĐỢT 1583 (việc o890) — cùng lý do với `.bnh-chu` của biểu ngữ dọc: Arial trên máy này thiếu
       dải U+1EA0–1EF9 nên chữ HAI DẤU (Ừ Ộ Ợ Ễ Ậ) bị mượn từ phông khác ⇒ lệch nét ngay giữa từ.
       Băng rôn chạy là chỗ chữ TO nhất và trôi ngang qua mắt cả ngày nên phải chữa cùng nhịp.
       🔑 Đổi phông thì sửa biến `--chu-bieu-ngu` ở wwwroot/chu/chu-viet.css, ⛔ đừng gõ tên vào đây. */
    /* ⚠ Đợt 2655 (việc o2143): bỏ "Helvetica Neue"/"Helvetica" khỏi dây dự phòng — trên máy Apple
       hai họ ấy CÓ THẬT mà thiếu chữ hai dấu, nên lỡ tệp phông tự lưu không tải được thì tấm băng
       rôn rơi đúng vào lỗi vừa chữa. Windows ⛔ đổi gì: nó vốn ánh xạ Helvetica sang Arial. */
    font-family: var(--chu-bieu-ngu, "Be Vietnam Pro"), Arial,
                 "Liberation Sans", "DejaVu Sans", sans-serif,
                 "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    white-space: nowrap;
    padding-right: 120px;   /* khớp LuatBangRonChay.PxKhoangDem — đổi ở đây thì đổi cả hằng số bên đó */
    font-weight: 800;
    font-size: .93rem;
    letter-spacing: .05em;
    color: #ffe082;
}

/* Nét chữ dập nổi kiểu chữ vàng trên băng rôn lễ: sáng ở đỉnh nét, đậm dần xuống chân nét.

   🩸 CẶP MÀU PHẢI ĐO LÀ **NẤC SẪM NHẤT CỦA CHỮ trên NẤC TƯƠI NHẤT CỦA VẢI** — ⛔ đừng đo nấc sẫm
     trên nấc sẫm rồi yên tâm. Hai gradient chạy VUÔNG GÓC nhau (chữ dọc 180deg, vải ngang 90deg)
     nên ở GIỮA tấm băng rôn, chân nét chữ nằm đúng trên chỗ đỏ tươi nhất. Bản thảo đầu của đợt 1367
     dùng vải `#6d0d11 → #d21f21` và chữ chân nét `#f4a11a`: đo ra **2,52:1** — dưới xa ngưỡng 4,5:1
     của chữ thường, mà nhìn thì vẫn "trông có lý" vì hai đầu tấm vải sẫm nên đọc được.
   📏 Đo lại sau khi hạ sắc đỏ về tông cờ đậm (26/08/2026, giao diện SÁNG và TỐI cho cùng số):
       · chân nét #f7b02e trên vải tươi nhất #96131a ....... 4,66:1  ✅
       · chân nét #f7b02e trên vải sẫm nhất  #5c0a0e ....... 7,45:1  ✅
       · nấc vàng  #ffd54f trên vải tươi nhất .............. 6,19:1  ✅
       · đỉnh nét  #fffdf3 trên vải tươi nhất .............. 8,56:1  ✅
     (Bốn số trên do TRÌNH DUYỆT đo, mã màu bóc thẳng từ `getComputedStyle` chứ ⛔ không gõ tay lại —
      gõ tay là đo bản trong đầu mình, không phải bản đang ship.)
   ⛔ Đừng đẩy nấc chân nét xuống nâu/cam sẫm cho "kim loại hơn", cũng ⛔ đừng kéo sắc đỏ lên tươi
     lại cho "rực rỡ": cả hai đều ăn thẳng vào con số 4,66 mỏng nhất ở trên. Đổi màu thì ĐO LẠI. */
@supports (background-clip: text) or (-webkit-background-clip: text) {
    .bang-ron-chu[b-zxa95aeiuc] {
        background-image: linear-gradient(180deg,
                          #fffdf3 0%, #ffeaa7 32%, #ffd54f 55%, #f7b02e 84%, #ffe9b0 100%);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;

        /* ⭐ ĐỢT 1426 — ÁNH KIM LƯỚT DỌC NÉT CHỮ ("hiệu ứng lung linh", anh Tiến 27/08/2026).
           Khổ nền cao GẤP ĐÔI dòng chữ rồi trượt đúng một khổ ⇒ vệt sáng đi hết chữ này sang chữ kia,
           ⛔ không có điểm nối.
           🩸 ⛔ KHÔNG SINH RA NẤC TƯƠNG PHẢN MỚI — điều này phải tự chứng minh trước khi thêm: phép
             trượt chỉ đổi CHỖ của các nấc màu, ⛔ không thêm nấc nào. Nấc sẫm nhất vẫn đúng là
             `#f7b02e`, nên con số 4,66:1 đo ở đợt 1367 còn nguyên giá trị. Đổi một mã màu trong dải
             trên thì ĐO LẠI, ⛔ đừng đoán. */
        background-size: 100% 200%;
        animation: bang-ron-anh-kim-b-zxa95aeiuc 5.5s linear infinite;
    }
}

/* Ánh kim lướt dọc nét chữ — trượt đúng MỘT khổ nền (200% − 100%) nên vòng lặp liền mạch. */
@keyframes bang-ron-anh-kim-b-zxa95aeiuc {
    from { background-position: 0 0; }
    to   { background-position: 0 -100%; }
}

/* Đốm lấp lánh: mờ hẳn rồi sáng lên, kèm một nhịp xê dịch rất nhỏ cho ra vẻ ánh sáng đổi góc.
   ⛔ Đừng cho `opacity` chạm 1: đốm đứng nguyên ở độ sáng cực đại là một chấm trắng CHẾT, ⛔ không
   phải một tia lấp lánh. */
@keyframes bang-ron-lap-lanh-b-zxa95aeiuc {
    0%, 100% { opacity: .12; transform: translateY(0); }
    50%      { opacity: .85; transform: translateY(-1px); }
}

@keyframes bang-ron-truot-b-zxa95aeiuc {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

@keyframes bang-ron-vao-b-zxa95aeiuc {
    from { opacity: 0; transform: translateY(-6px) scaleX(.92); }
    to   { opacity: 1; transform: none; }
}

/* Đi hết một tấm băng rôn rộng nhất (72rem) rồi ĐỨNG YÊN NGOÀI KHUNG cho tới vòng sau —
   ⛔ đừng cho nó quay về mép trái trong lúc còn nhìn thấy, đó là một vệt sáng chạy ngược. */
@keyframes bang-ron-quet-b-zxa95aeiuc {
    0%        { transform: rotate(16deg) translateX(0); opacity: 0; }
    3%        { opacity: .95; }
    15%       { transform: rotate(16deg) translateX(80rem); opacity: .95; }
    17%, 100% { transform: rotate(16deg) translateX(80rem); opacity: 0; }
}

/* ═══════════════ CHỖ ĐỨNG TRÊN THANH NAV ═══════════════
   ⚠ Mọi luật dưới đây bám `.top-row` có chủ ý: ô XEM THỬ ở trang 🛠 Công cụ dev nhúng CHÍNH
     component này trong một thẻ thường, ⛔ không phải trong thanh nav. Viết trần (không `.top-row`)
     là ô xem thử cũng biến mất khi cửa sổ hẹp — người khai băng rôn mất chỗ nhìn mà ⛔ không có
     một câu nào giải thích. */

/* 🔑 `flex-grow` LỚN (20) là cách nhường quyền cho DẢI TAB mà ⛔ KHÔNG phải sửa luật của nó.
     Dải tab `.mcd-dai-tab` khai `flex: 1 1 auto` (đợt 422) nên nó cũng ăn khoảng trống; để băng rôn
     ở `flex-grow: 1` thì hai bên CHIA ĐÔI chỗ thừa — đo trên `/nuoc`: băng rôn chỉ được 291px còn
     dải tab phình ra 942px trong khi 5 cái tab của nó chỉ cần 651px, tức một khoảng trống to đùng
     nằm giữa tab cuối và băng rôn. Đặt 20 thì băng rôn ăn ~95% chỗ thừa, dải tab giữ gần đúng bề
     rộng nội dung của nó.
   🔑 `flex-basis: 0` + `min-width` là cặp giữ lời hứa "không che nút": chỗ CHẬT thì phần thiếu bị
     trừ vào DẢI TAB (nó có `min-width: 0` và tự cuộn ngang — đúng cơ chế đợt 679 dựng sẵn), ⛔ không
     bao giờ trừ vào hai cụm nút hai đầu (chúng `flex-shrink: 0`).
   ⛔ ĐỪNG hạ `min-width` xuống 0 cho "gọn": ô flex bề rộng 0 mà còn viền + đệm thì vẫn hiện ra một
     mẩu đỏ ~30px trông y như lỗi vẽ. Có sàn thì hoặc thấy một tấm băng rôn đọc được, hoặc ⛔ không
     thấy gì cả. */
/* 🔴 ĐỢT 1469 (việc o671) — `flex-grow` 20 → 0, ⛔ ĐỪNG TRẢ LẠI. Anh Tiến 28/08/2026:
     *"sắp xếp các nút và các icon sao cho băng rôn nằm giữa nhất có thể"*.
   🔑 Băng rôn CÒN ĂN chỗ thừa thì ⛔ không cách nào căn giữa nó được: chỗ thừa đi hết vào chính nó, và
     hai đầu thanh ở nguyên bề rộng nội dung của chúng — mà hai bề rộng ấy khác nhau (đo 28/08 trên màn
     1920: 245px bên trái, 615px bên phải). Nay băng rôn nhận một BỀ RỘNG CƠ SỞ cố định và **nhường chỗ
     thừa cho hai cụm hai đầu**, hai cụm ấy cùng `flex: 1 1 0` nên chia đều — xem
     `.top-row:has(.bang-ron)` trong `MainLayout.razor.css`.
   ⚠ VẪN GIỮ `flex-shrink: 1`: chỗ chật thì băng rôn co lại trước, ⛔ không bao giờ bóp vào hai cụm nút
     (chúng `flex-shrink: 0`). Lời hứa "không che nút" của đợt 1367 ⛔ không đổi một chữ.
   ⚠ Lý lẽ CŨ của số 20 (nhường quyền cho dải tab) nay ⛔ không còn chỗ dùng: từ đợt này băng rôn chỉ
     hiện ở TRANG CHỦ, mà trang chủ ⛔ không có dải tab nào. Giữ lại đoạn ấy ở khối chú thích trên để
     hiểu vì sao con số từng là 20.
   ⛔ ĐỪNG hạ `min-width` xuống 0 cho "gọn" — xem lý do ở khối chú thích trên. */
.top-row .bang-ron[b-zxa95aeiuc] {
    flex: 0 1 62rem;
    min-width: 13rem;
    max-width: 72rem;
}

/* Khổ 1201–1400px (máy xách tay): vẫn treo được nhưng khiêm tốn lại, nhường chỗ cho dải tab —
   dải tab là công cụ làm việc, băng rôn là đồ trang trí, chật thì đồ trang trí lùi trước. */
@media (max-width: 1400px) {
    .top-row .bang-ron[b-zxa95aeiuc] {
        min-width: 9rem;
    }

    .top-row .bang-ron .bang-ron-chu[b-zxa95aeiuc] {
        font-size: .85rem;
        letter-spacing: .03em;
    }

    .top-row .bang-ron[b-zxa95aeiuc]::before,
    .top-row .bang-ron[b-zxa95aeiuc]::after {
        width: 1.5rem;
    }
}

/* ⛔ Dưới 1200px thì ẨN HẲN. Đây đúng là nấc mà đợt 422 gọi là "thanh nav bị tràn": `.top-row` mở
   `flex-wrap` và hàng tab tụt xuống hàng thứ hai. Cố treo băng rôn ở đó là nó tranh chỗ với đúng
   hàng tab vừa phải xuống dòng vì thiếu chỗ. ⛔ Đừng đổi thành "thu nhỏ tiếp": chữ đã ở ngưỡng đọc
   được rồi, nhỏ hơn nữa là một vệt đỏ vô nghĩa chiếm chỗ của thanh điều hướng. */
@media (max-width: 1200px) {
    .top-row .bang-ron[b-zxa95aeiuc] {
        display: none;
    }
}

/* ───────────────── Người tắt hiệu ứng chuyển động ─────────────────
   Băng rôn ĐỨNG YÊN mà vẫn đọc được: bỏ bản chữ thứ hai đi (nó chỉ để nối vòng chạy), cho dải tự
   cuộn ngang trong lòng nó, và đẩy vệt sáng ra hẳn ngoài khung.
   ⛔ Đừng để nguyên hai bản: đứng yên mà nhân đôi là câu chúc hiện ra hai lần cạnh nhau.
   ⛔ Đừng để `.bang-ron-loe` đứng ở vạch xuất phát: chỗ đó nằm ngay mép trái tấm vải, dừng ở đấy thì
     có một vệt sáng dựng đứng trông như lỗi vẽ (bài học đợt 1340).
   ⚠ Cao thêm một chút để chừa chỗ cho thanh cuộn mảnh — vẫn dưới trần 3.5rem của thanh nav. */
@media (prefers-reduced-motion: reduce) {
    .bang-ron[b-zxa95aeiuc] {
        animation: none;
        overflow-x: auto;
        height: 2.35rem;
        scrollbar-width: thin;
        scrollbar-color: rgba(255, 213, 79, .5) transparent;
    }

    .bang-ron-truot[b-zxa95aeiuc] { animation: none; }

    .bang-ron-chu + .bang-ron-chu[b-zxa95aeiuc] { display: none; }

    .bang-ron-loe[b-zxa95aeiuc] {
        animation: none;
        opacity: 0;
    }

    /* ⭐ ĐỢT 1426 — hai hiệu ứng mới cũng phải đứng lại.
       ⚠ Hoạ tiết gấm thì GIỮ NGUYÊN: nó ⛔ không hề chuyển động, gỡ đi là phạt người tắt chuyển động
         bằng cách lấy mất một thứ TĨNH mà họ ⛔ chưa bao giờ phàn nàn.
       ⚠ Đốm lấp lánh hạ về độ sáng thấp chứ ⛔ không `display: none`: tắt hẳn thì tấm vải của họ khác
         hẳn tấm vải mọi người thấy, mà thứ họ xin chỉ là "đừng nhúc nhích". */
    .bang-ron-lap-lanh[b-zxa95aeiuc],
    .bang-ron-lap-lanh[b-zxa95aeiuc]::after {
        animation: none;
        opacity: .3;
    }

    .bang-ron-chu[b-zxa95aeiuc] {
        animation: none;
    }
}
/* /Components/ChungTu/BangRonSinhNhat.razor.rz.scp.css */
/* ═══ BĂNG RÔN CHÚC MỪNG SINH NHẬT (đợt 2266 — việc o1769) ═══
   Anh Tiến 10/09/2026: "làm thêm Băng rôn chúc mừng sinh nhật của 1 người, hiển thị trong ngày".

   🔑 Vì sao là TẤM BĂNG RÔN chứ không phải một thẻ card nữa: dải gọn `KhoiSinhNhat` ngay dưới đã lo
      việc "báo cho biết" suốt ±7 ngày. Tấm này lo việc KHÁC — nó là lời chúc, nên nó phải to, phải
      thấy ngay khi mở trang, và chỉ sống đúng một ngày.
   ⛔ Bảng màu CỐ Ý ĐỨNG RIÊNG, không ăn theo bảng ba chặng của huy hiệu sinh nhật: chặng là để phân
      biệt hôm nay - sắp tới - đã qua, mà tấm này chỉ có ĐÚNG MỘT chặng.
   ⛔ Chữ trắng trên nền gradient ĐẬM để đọc được ở CẢ nền sáng lẫn nền tối - tấm này không đổi màu
      theo chủ đề, nên nó phải tự đủ tương phản.  */

.brs-boc[b-9x19ctom9c] {
    display: flex;
    flex-direction: column;
    gap: .55rem;
    margin: 0 0 1rem;
}

/* Tấm băng rôn: hai đầu cắt vát hình đuôi én, y như một tấm băng rôn treo thật.
   ⛔ Đừng đổi sang bo tròn hết cỡ: dáng viên thuốc là dáng của DẢI GỌN ngay dưới, hai kênh mà cùng
   một dáng thì người xem đọc chúng thành một thứ.  */
/* 🔴 VÙNG KHOÉT ĐUÔI ĐO BẰNG PIXEL TỪ ĐÁY, ⛔ TUYỆT ĐỐI KHÔNG BẰNG PHẦN TRĂM.
   `clip-path` khoét phần THÂN giữa ở đáy để lấy dáng hai đuôi băng rôn; chữ nào rơi vào vùng ấy thì
   BIẾN MẤT mà ⛔ không câu lỗi nào: build xanh, test xanh, tấm vẫn đẹp.
   📏 Đo thật 10/09/2026, HAI lượt hỏng liên tiếp - cả hai đều do mốc tính bằng PHẦN TRĂM:
     · mốc 74% + đệm .85rem ⇒ tấm cao 128,2px, khoét từ 94,9px, dòng lời chúc nằm 96,3-114,6px
       ⇒ MẤT TRỌN một dòng chữ.
     · mốc 84% + đệm 1.8rem ⇒ khổ rộng thì đủ, nhưng khung HẸP làm lời chúc dài ra 3 dòng ⇒ tấm cao
       lên, mà vùng khoét cũng cao theo (84% của một số lớn hơn) ⇒ CẮT LẠI đúng dòng ấy.
   🔑 Gốc bệnh: phần trăm làm vùng khoét NỞ THEO nội dung, tức càng nhiều chữ càng khoét sâu - đúng
      chiều ngược với thứ mình cần. Đo bằng `calc(100% - 22px)` thì đuôi luôn cao đúng 22px, chữ dài
      bao nhiêu dòng cũng an toàn, và chỉ cần đệm dưới > 22px là xong.
   ⚠ Đệm dưới phải LỚN HƠN chiều cao khoét (1.95rem = 31,2px > 22px), ⛔ đừng hạ xuống bằng nhau: đo
   10/09 với đệm 1.7rem chỉ dư ĐÚNG 1px - một lượt đổi cỡ chữ là mất chữ trở lại.  */
/* 🎨 BẢNG MÀU KHAI ĐÚNG MỘT CHỖ (20/09/2026) — mọi luật phía dưới gọi qua biến, ⛔ đừng rải mã màu
   xuống từng luật. Tấm này cố ý đứng NGOÀI hai trục giao diện (sáng/tối · bộ màu), nên chỗ duy nhất
   đổi được màu của nó phải là chỗ này; rải mã màu ra mười luật là lần sau sửa sót một chỗ mà ⛔ không
   phép đo nào bắt được.
   ⛔ Mã màu vẫn Y NGUYÊN bộ cũ — lượt này chỉ ĐẶT TÊN cho chúng, ⛔ không đổi sắc.  */
.brs-tam[b-9x19ctom9c] {
    --brs-do: #b4232a;
    --brs-cam: #d94a2b;
    --brs-vang: #e8862a;
    --brs-do-tham: #c22f36;
    --brs-chu: #fff;
    --brs-sang: rgba(255, 255, 255, .28);
    --brs-sang-mo: rgba(255, 255, 255, .14);
    --brs-vien-trong: rgba(255, 255, 255, .22);
    --brs-net: rgba(255, 255, 255, .55);
    --brs-bong: rgba(0, 0, 0, .22);

    position: relative;
    overflow: hidden;
    padding: .85rem 3.2rem 1.95rem;
    text-align: center;
    color: var(--brs-chu);
    background-image: linear-gradient(100deg, var(--brs-do) 0%, var(--brs-cam) 38%, var(--brs-vang) 62%, var(--brs-do-tham) 100%);
    border-radius: .75rem;
    box-shadow: 0 .35rem .9rem var(--brs-bong), inset 0 0 0 1px var(--brs-vien-trong);
    clip-path: polygon(0 0, 100% 0, 100% 100%, 96% 100%, 92% calc(100% - 22px), 8% calc(100% - 22px), 4% 100%, 0 100%);
    animation: brs-hien-b-9x19ctom9c .5s cubic-bezier(.2, .9, .3, 1.25) both;
}

/* Tấm hiện ra bằng một cú NẢY RẤT NHẸ, ⛔ không phải một màn trình diễn: chỉ mờ-dần-rõ cộng nhích
   xuống 0,4rem. ⛔ Đừng cho nó chạy lặp — một tấm nhún nhảy suốt ngày trên trang chủ của 433 tài
   khoản là thứ ⛔ KHÔNG ai tắt được.
   🔑 Chỉ chạm `opacity` + `transform` nên ⛔ không tính lại bố cục ⇒ ⛔ không dịch một điểm ảnh nào
      của phần trang bên dưới.  */
@keyframes brs-hien-b-9x19ctom9c {
    from {
        opacity: 0;
        transform: translateY(-.4rem) scale(.985);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* Ánh sáng dịu rọi từ đỉnh xuống — cho mặt tấm có ĐỘ CONG như vải băng rôn thật, thay vì một mảng
   phẳng. Lớp phủ RIÊNG, ⛔ không trộn vào nền gradient chính: trộn vào là lần sau đổi nền phải tính
   lại cả hai thứ cùng lúc.
   ⚠ Nó ⛔ KHÔNG thêm một điểm ảnh chiều cao nào — `inset: 0` nằm gọn trong đúng hộp sẵn có.  */
.brs-tam[b-9x19ctom9c]::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: radial-gradient(120% 150% at 50% -45%, var(--brs-sang) 0%, rgba(255, 255, 255, 0) 62%);
    pointer-events: none;
}

/* Vệt sáng lướt ngang - lớp phủ RIÊNG, không phải `filter` trên chính tấm.
   🔑 Luật đợt 2239: hai danh sách `filter` lệch họ hàm thì trình duyệt NHẢY PHẮT sang đích mà không
      báo gì; ở đây chỉ chạy `transform` nên không dính họ lỗi ấy.  */
.brs-tam[b-9x19ctom9c]::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: -40%;
    width: 32%;
    background-image: linear-gradient(100deg, rgba(255, 255, 255, 0) 0%, var(--brs-sang) 50%, rgba(255, 255, 255, 0) 100%);
    transform: skewX(-18deg);
    animation: brs-luot-b-9x19ctom9c 5.5s ease-in-out infinite;
    pointer-events: none;
}

@keyframes brs-luot-b-9x19ctom9c {
    0% { transform: translateX(0) skewX(-18deg); }
    55% { transform: translateX(430%) skewX(-18deg); }
    100% { transform: translateX(430%) skewX(-18deg); }
}

.brs-ruot[b-9x19ctom9c] {
    position: relative;
    z-index: 1;
}

/* Dòng lời chúc nay đi giữa HAI NÉT KẺ NGẮN — dấu hiệu quen thuộc của một tấm thiệp chúc mừng, để
   người xem đọc ra ngay đây là lời chúc chứ ⛔ không phải một dải cảnh báo của phần mềm.
   ⚠ Hai nét là `::before`/`::after` trên chính dòng chữ nên chúng nằm CÙNG HÀNG, ⛔ không đẻ thêm
     một hàng mới (tấm ⛔ được cao lên — anh Tiến đã hai lần ép dải sinh nhật thấp xuống).  */
.brs-chuc[b-9x19ctom9c] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .22em;
    text-transform: uppercase;
    opacity: .92;
}

.brs-chuc[b-9x19ctom9c]::before,
.brs-chuc[b-9x19ctom9c]::after {
    content: "";
    flex: 0 0 auto;
    width: 1.6rem;
    height: 1px;
    background: var(--brs-net);
    opacity: .75;
}

/* ⚠ `text-wrap: balance` chia đều chữ khi TÊN DÀI phải xuống hai dòng, thay cho một dòng đầy và một
   dòng trơ hai chữ. Trình duyệt ⛔ KHÔNG hiểu thì bỏ qua đúng MỘT dòng này và tên vẫn xuống dòng như cũ —
   ⛔ không có chiều hỏng lặng lẽ nào.  */
.brs-ten[b-9x19ctom9c] {
    margin-top: .1rem;
    font-size: 1.85rem;
    font-weight: 800;
    line-height: 1.15;
    text-wrap: balance;
    text-shadow: 0 .1rem .35rem rgba(0, 0, 0, .3);
}

.brs-phu[b-9x19ctom9c] {
    margin-top: .1rem;
    font-size: .92rem;
    opacity: .95;
}

/* Ngày-tháng đi kèm dòng phụ chứ không đứng một mình: một con số trần là một câu đố (luật bảng
   07/09/2026 mục ③). Viền quanh nói rằng đây là một mốc ngày, không phải một mẩu chữ.  */
.brs-ngay[b-9x19ctom9c] {
    display: inline-block;
    margin-left: .35rem;
    padding: .05rem .45rem;
    border: 1px solid var(--brs-net);
    border-radius: 999px;
    background: var(--brs-sang-mo);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.brs-cau[b-9x19ctom9c] {
    margin-top: .35rem;
    font-size: .88rem;
    opacity: .9;
}

/* Hai hình trang trí hai bên - thuần trang trí nên mang `aria-hidden` trong markup.
   🔑 ĐU ĐƯA RẤT CHẬM (3,6 giây một nhịp, nghiêng 4 độ, nhích lên 8% thân hình): đủ để tấm có hơi thở
      ngày vui, còn xa mức hoạt hình. ⛔ Đừng tăng biên độ hay rút ngắn nhịp — đây là app nghiệp vụ,
      hai hình nhảy nhót cạnh TÊN NGƯỜI là cướp mất thứ chính của tấm.
   🩸 Keyframes PHẢI chép lại `translateY(-50%)`: hoạt hoạ `transform` GHI ĐÈ trọn thuộc tính, bỏ sót
      là hai hình tụt hẳn xuống dưới nửa thân - hỏng LẶNG LẼ, ⛔ không câu lỗi nào.
   ⚠ `filter` ở đây là ĐỔ BÓNG TĨNH, ⛔ không phải thứ đem ra hoạt hoạ - xem luật hai danh sách
      `filter` lệch họ hàm ở khối chú thích vệt sáng phía trên.  */
.brs-nhay[b-9x19ctom9c] {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    font-size: 1.9rem;
    opacity: .85;
    pointer-events: none;
    filter: drop-shadow(0 .1rem .2rem var(--brs-bong));
    animation: brs-dung-dua-b-9x19ctom9c 3.6s ease-in-out infinite;
}

@keyframes brs-dung-dua-b-9x19ctom9c {
    0%, 100% { transform: translateY(-50%) rotate(-4deg); }
    50% { transform: translateY(-58%) rotate(4deg); }
}

.brs-nhay-trai[b-9x19ctom9c] { left: .9rem; }

/* Hình bên phải đi LỆCH NHỊP nửa vòng: hai hình đu cùng chiều cùng lúc trông như một lỗi vẽ lại. */
.brs-nhay-phai[b-9x19ctom9c] {
    right: .9rem;
    animation-delay: -1.8s;
}

.brs-con-lai[b-9x19ctom9c] {
    padding: 0 .2rem;
    font-size: .86rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* ═══ KHỔ ĐIỆN THOẠI ═══
   ⛔ Một điều kiện, không viết câu media ghép: bộ biên dịch CSS cách ly của Blazor từng gắn dấu scope
      vào đuôi câu media ghép, làm trình duyệt bỏ NGUYÊN KHỐI (luật đợt 1481).
   🔑 Ở 375px, hai hình trang trí hai bên ăn mất chỗ của TÊN - thứ chính của tấm. Chúng lui về nhỏ và
      đệm hai bên co lại, đúng luật "thứ phụ nhường chỗ cho thứ chính" (07/09/2026).  */
@media (max-width: 576px) {
    .brs-tam[b-9x19ctom9c] {
        padding: .75rem 2.2rem 1.4rem;
        clip-path: polygon(0 0, 100% 0, 100% 100%, 94% 100%, 89% calc(100% - 16px), 11% calc(100% - 16px), 6% 100%, 0 100%);
    }

    .brs-chuc[b-9x19ctom9c] {
        font-size: .7rem;
        letter-spacing: .14em;
    }

    /* Hai nét kẻ co lại để nhường chỗ cho CHỮ - thứ phụ nhường thứ chính, đúng luật khổ hẹp. */
    .brs-chuc[b-9x19ctom9c]::before,
    .brs-chuc[b-9x19ctom9c]::after { width: .9rem; }

    .brs-ten[b-9x19ctom9c] { font-size: 1.35rem; }
    .brs-phu[b-9x19ctom9c] { font-size: .84rem; }
    .brs-cau[b-9x19ctom9c] { font-size: .8rem; }

    .brs-nhay[b-9x19ctom9c] { font-size: 1.25rem; }
    .brs-nhay-trai[b-9x19ctom9c] { left: .45rem; }
    .brs-nhay-phai[b-9x19ctom9c] { right: .45rem; }
}

/* Người đã tắt hiệu ứng chuyển động trong hệ điều hành thì DỪNG HẲN mọi chuyển động - tấm băng rôn
   vẫn còn NGUYÊN VẸN (nền, chữ, hai hình, vùng khoét đuôi), chỉ mất phần lấp lánh và đu đưa.
   ⚠ BA hoạt hoạ thì phải tắt ĐỦ BA: vệt sáng · lượt hiện ra của cả tấm · hai hình hai bên. Tắt sót
     một cái là câu hứa "tôn trọng lựa chọn của người dùng" chỉ đúng một phần - mà phần sót lại
     thường là phần động nhất.
   🩸 `.brs-nhay { animation: none }` trả hai hình về đúng luật gốc `translateY(-50%)`, ⛔ đừng khai
     thêm `transform` ở đây kẻo nó chọi với luật gốc ấy.  */
@media (prefers-reduced-motion: reduce) {
    .brs-tam[b-9x19ctom9c] { animation: none; }
    .brs-tam[b-9x19ctom9c]::after { animation: none; }
    .brs-nhay[b-9x19ctom9c] { animation: none; }
}
/* /Components/ChungTu/BieuNguHong.razor.rz.scp.css */
/* ═══ HAI BIỂU NGỮ DỌC HAI BÊN HÔNG TRANG — đợt 1395 (việc o590) ═══

   ⭐ Anh Tiến 27/08/2026: "2 bên hông còn không gian cho 2 biểu ngữ em nghiên cứu làm thêm".
     Anh chốt cùng ngày: chữ dọc phần mềm tự vẽ · hiện ở mọi trang.

   📐 KHOẢNG TRỐNG ẤY TÍNH ĐƯỢC, ⛔ KHÔNG PHẢI ƯỚC. Cột nội dung `.content` chặn bề rộng rồi căn giữa
     (chỉ đạo anh Tiến 30/07/2026) ⇒ mỗi bên còn `(100vw − trần cột) / 2`, trừ 24px.
     🔴 TRẦN CỘT LÀ **HAI CON SỐ**, ⛔ đừng nhớ mỗi số 1400 (đây đúng là chỗ đợt 1481 phải đi chữa):
       · trang THƯỜNG .... 1400px
       · TRANG CHỦ ....... `min(96vw, 1760px, calc(100vw - 128px))` từ khổ 1600px trở lên
                           (`wwwroot/trang-chu.css`, đợt 684d nới — đợt 1580 thêm vế chừa chỗ)
     ⇒ Phép tính dưới đây hỏi biến `--tran-cot-noi-dung` (khai ở `app.css`, trang chủ đặt lại), ⛔ đừng
       gõ thẳng con số vào đây nữa. Bảng dải trống THẬT của TRANG CHỦ (đo 31/08/2026, sau đợt 1580):
       · màn 3.840px .... dải 1.040px    · màn 2.560px .... dải 400px
       · màn 2.048px .... dải  144px     · màn 1.920px .... dải  80px  ⇒ tấm 56px
       · màn 1.700px .... dải   64px     · màn 1.600px .... dải  64px  ⇒ tấm 40px (vế `calc` giữ sàn)
       · màn 1.536px .... dải   68px  (cột chưa nới ⇒ y hệt trang thường)
       · màn 1.528px .... dải   64px  (NGƯỠNG CHUNG - vừa đúng 40 + 24)
     🔑 ⭐ ĐỢT 1580 (việc o885) — **NAY CHỈ CÒN MỘT NGƯỠNG 1528px CHO MỌI TRANG**. Trước đó trang chủ
       có ngưỡng riêng 1888px và vì thế **⛔ không treo được tấm nào ở khổ dưới 1888px** (anh Tiến
       31/08: *"ở trang chủ mất băng rôn 2 bên"*) — xem khối "NGƯỠNG THỨ HAI ĐÃ BỎ" ở cuối tệp.
     Bốn con số 1400 · 1760 · 40 · 24 phải khớp `LuatBieuNguHong.RongCotNoiDungPx` ·
     `.RongCotNoiDungTrangChuPx` · `.RongDaiToiThieuPx` · `.KheThoPx`, ngưỡng 1528px khớp
     `.RongManHinhToiThieuPx`, và 128px của vế `calc` khớp `.ChuaChoHaiTamPx` — hàng rào
     `LuatBieuNguHongTests` đối chiếu tận nơi, sửa một chỗ mà quên chỗ kia là build ĐỎ.

   ⭐ ĐỢT 1419 (việc o614) HẠ NGƯỠNG 1700 -> 1528. Anh Tiến 27/08/2026: "anh chưa thấy vận hành …
     băng rôn 2 bên trang web". Gốc bệnh KHÔNG phải cấu hình (đo trên CSDL: tám khóa đều bật, máy chủ
     dựng đủ hai thẻ `aside`) mà là chính ngưỡng này: 1700px loại luôn khổ 1536px — khổ của màn 1920
     đặt tỉ lệ 125%, tức mặc định của Windows trên máy để bàn. Số 110px "dải tối thiểu" của đợt 1395
     là ƯỚC LƯỢNG; đo thật thì dòng chữ dọc chỉ rộng 23px ⇒ 40px là đủ, còn 8,5px thở mỗi bên.

   ⛔ DƯỚI NGƯỠNG THÌ ẨN HẲN, ⛔ đừng đổi thành "thu nhỏ tiếp" — cùng lẽ với ngưỡng 1200px của băng rôn
     (đợt 1367): hẹp hơn nữa thì hoặc chữ không đọc nổi, hoặc biểu ngữ ĐÈ LÊN nội dung, mà nội dung là
     thứ người ta vào đây để làm việc.

   ⛔ `pointer-events: none` LÀ CHỐT AN TOÀN, ⛔ KHÔNG PHẢI TRANG ĐIỂM. Hai tấm này `position: fixed`
     nên chúng đứng NGOÀI phép chia chỗ của trình duyệt — khác hẳn băng rôn trên thanh nav (một ô co
     giãn, xem `BangRonChay.razor.css`). Không có phép chia chỗ thì lời hứa "không che nút" phải trả
     bằng cách khác: chuột đi xuyên qua. Gỡ dòng ấy là mở lại đúng ca đã đo và loại bỏ.

   ⚠ MÀU CỐ ĐỊNH cho cả giao diện sáng lẫn tối, ⛔ không lấy theo bộ màu đang chọn — chép đúng lý lẽ
     của băng rôn: đây là tấm biểu ngữ lễ, nó phải giữ sắc đỏ vàng ở mọi chủ đề.

   📏 TƯƠNG PHẢN: cặp màu vải và chữ lấy NGUYÊN XI của băng rôn (đợt 1367) nên số đo bên đó còn nguyên
     giá trị — nấc mỏng nhất là chân nét chữ `#f7b02e` trên vải tươi nhất `#96131a` = 4,66:1, các nấc
     khác 6,19 đến 8,56. ⛔ Đổi một trong hai mã màu thì ĐO LẠI, đừng đoán. */

/* ───────────────── Tấm biểu ngữ ───────────────── */
.bnh[b-p74zwcjlvq] {
    position: fixed;
    /* Bắt đầu ngay dưới thanh nav (min-height 3.5rem) và chừa một quãng thở. */
    top: 4.5rem;
    bottom: 1.25rem;
    z-index: 5;   /* dưới thanh nav (900) và dưới mọi hộp thoại — biểu ngữ ⛔ không bao giờ nằm trên chúng */

    /* ⭐ ĐỢT 1481 (việc o684) — DẢI TRỐNG HỎI BIẾN `--tran-cot-noi-dung`, ⛔ ĐỪNG GÕ LẠI SỐ 1400.
       Anh Tiến 29/08/2026: *"băng rôn 2 bên bị vỡ to với màn hình lớn hơn 27 inch"*.
       🔴 Gốc bệnh: TRANG CHỦ tự nới cột nội dung lên `min(96vw, 1760px)` từ khổ 1600px
         (`TongQuan.razor`, đợt 684d — cho 9 ô lối tắt về một hàng) trong khi phép tính ở đây vẫn đóng
         đinh 1400px ⇒ hai tấm ĐÈ LÊN nội dung. Đo tận tay 29/08 trên bản tĩnh (chốt ngược `d-flex` ✓):
           · khổ 1.920px .... tấm đè **164px** vào `.content`, **140px** vào khối `.trang-chu`
           · khổ 2.048px .... đè **120px** / **96px**   (khổ của màn 2560 đặt tỉ lệ 125% Windows)
           · trang THƯỜNG cùng khổ 1920 ... khe 16px hai bên — ⇒ lỗi CHỈ ở trang chủ.
         Đè ở MỌI khổ 1.528–2.288px; từ 2.288px trở lên thì hết đè nhưng tấm lại **dạt ra sát mép màn
         hình**, cách nội dung 136px (2560) · 776px (3840) — rời hẳn khỏi trang, đúng chữ "vỡ" của anh.
       ⇒ Nay bề rộng VÀ chỗ đứng đều suy từ dải trống THẬT. Biến khai ở `app.css`, trang chủ đặt lại
         bằng `:root:has(.trang-chu)` — một con số, hai nơi đọc. Trình duyệt không hiểu `:has()` thì cả
         hai vế cùng rơi về 1400px (trang chủ cũng thôi nới) ⇒ vẫn khớp nhau, ⛔ không có ca lệch.
       ⚠ 24px = 8px lùi khỏi mép màn hình + 16px khe thở tới cột nội dung (khớp `LuatBieuNguHong
         .KheThoPx`). Khe ấy còn nuốt luôn khoản `100vw` nhỉnh hơn vì thanh cuộn dọc ở vài trình duyệt. */
    --bnh-dai-trong: calc((100vw - var(--tran-cot-noi-dung, 1400px)) / 2);

    /* ⛔ TRẦN 6rem CHO TẤM CHỮ, ⛔ đừng trả về 16rem. Dòng chữ dọc đo thật chỉ **23px**, nên tấm 256px
       của đời trước để trống **91% mặt vải** — đó chính là vế "TO" trong câu anh báo. 96px cho chữ
       36px thở mỗi bên. Tấm treo bằng ẢNH thì ngược lại: ảnh là tranh vẽ sẵn, bóp còn 96px là ⛔ không
       xem được gì ⇒ nó giữ trần 16rem ở luật ngay dưới. */
    --bnh-tran-rong: 6rem;
    --bnh-rong: min(var(--bnh-tran-rong), calc(var(--bnh-dai-trong) - 24px));
    width: var(--bnh-rong);

    /* ⛔ MẶC ĐỊNH ẨN, chỉ nhánh @media dưới cùng mới bật lên. Viết ngược lại (hiện sẵn rồi ẩn ở khổ
       hẹp) là ca trình duyệt không hiểu `@media` sẽ TREO ở mọi khổ — hỏng đúng phía đắt hơn. */
    display: none;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    pointer-events: none;   /* xem chú thích đầu tệp — đây là chốt an toàn, ⛔ đừng gỡ */

    border-radius: 14px;
    /* Nền lụa đỏ chạy DỌC (180deg): hai đầu sẫm, giữa tươi — cùng bảng màu với băng rôn ngang. */
    background: linear-gradient(180deg, #5c0a0e 0%, #7e0f14 18%, #96131a 50%, #7e0f14 82%, #5c0a0e 100%);
    border: 1px solid #ffd54f;
    /* Ba lớp: bóng đổ tách khỏi nền trang · viền vàng thứ hai bên trong (dáng khung lễ) · gờ sáng. */
    box-shadow: 0 2px 6px rgba(0, 0, 0, .35),
                inset 0 0 0 2px rgba(255, 213, 79, .22),
                inset 1px 0 0 rgba(255, 255, 255, .18);

    /* Tấm vải TRÔI VÀO một lần rồi thôi — đây chỉ nói về lượt vào, ⛔ không nói về nội dung bên trong.
       🔴 ĐÍNH CHÍNH ĐỢT 1426: câu cũ ở đây ghi *"⛔ Cố ý KHÔNG có hiệu ứng lặp: … thêm hai vật thể động
         lặp mãi ở hai bên chỗ làm việc là thứ gây mỏi mắt nhất (bài học đợt 1340)"* — **HẾT HIỆU LỰC**.
         Anh Tiến 27/08/2026 chốt ngược: dải chữ phải CHẠY dọc, và tấm vải phải có *"hiệu ứng lung
         linh"*. Lo ngại mỏi mắt ⛔ không bị vứt đi mà được trả bằng SỐ — tốc độ cuộn 40px/s, bằng 57%
         nấc "vừa" của băng rôn ngang (xem `LuatBieuNguHong.PxMoiGiayCuon`) — và bằng việc mọi hiệu ứng
         mới đều có nhánh `prefers-reduced-motion` ở cuối tệp.
       ⚠ Khai bằng LONGHAND, ⛔ đừng viết gộp `animation: bnh-vao …` ở đây: tên hình động do LỚP TỪNG
         BÊN đặt (mỗi bên trôi vào từ một phía), nên viết gộp là phải bịa ra một cái tên chung ⛔ không
         hề tồn tại — trông như có mà thật ra rỗng. */
    animation-duration: .6s;
    animation-timing-function: cubic-bezier(.22, .9, .3, 1);
    animation-fill-mode: both;
}

/* ⭐ ĐỢT 1481 — TẤM TREO BẰNG ẢNH ĐƯỢC RỘNG HƠN (xem chú thích `--bnh-tran-rong` ở trên).
   🔑 Viết `.bnh:has(.bnh-anh)` chứ ⛔ không thêm một lớp vào markup: dấu hiệu "bên này treo bằng ảnh"
     đã có sẵn ngay trong cây thẻ (`BieuNguHong.razor` chỉ dựng `<img class="bnh-anh">` khi anh khai
     ảnh) — thêm lớp là dựng dấu hiệu THỨ HAI phải nhớ đồng bộ với dấu hiệu thứ nhất.
   ⓘ Trình duyệt không hiểu `:has()` thì tấm ảnh chịu trần 6rem — hẹp, nhưng ⛔ không vỡ gì. */
.bnh:has(.bnh-anh)[b-p74zwcjlvq] {
    --bnh-tran-rong: 16rem;
}

/* ⭐ ĐỢT 1481 — BÁM SÁT CỘT NỘI DUNG, ⛔ KHÔNG DÍNH MÉP MÀN HÌNH NỮA.
   Đời trước treo cứng `left/right: .5rem` nên trên màn rộng hai tấm trôi ra rìa: đo 29/08 ở khổ 3840px
   chúng cách cột nội dung **776px** — hai vệt đỏ lạc lõng ở hai mép, ⛔ không còn ra "hai bên hông
   trang". Nay mép TRONG của tấm luôn cách cột nội dung đúng 16px, dải trống thừa dồn ra phía ngoài.
   ⚠ `max(.5rem, …)` là nấc lùi cho khổ hẹp: dải trống bé thì phép trừ ra số âm, mà số âm ở đây là tấm
     thò ra ngoài màn hình. ⛔ Đừng bỏ vế `max`. */
.bnh-trai[b-p74zwcjlvq] {
    left: max(.5rem, calc(var(--bnh-dai-trong) - 16px - var(--bnh-rong)));
    animation-name: bnh-vao-trai-b-p74zwcjlvq;
}

.bnh-phai[b-p74zwcjlvq] {
    right: max(.5rem, calc(var(--bnh-dai-trong) - 16px - var(--bnh-rong)));
    animation-name: bnh-vao-phai-b-p74zwcjlvq;
}

/* ───────────────── HAI ĐẦU: TRỤC CUỘN VẢI ─────────────────
   ⭐ ĐỢT 1426 (việc o621) — anh Tiến 27/08/2026: *"băng rôn đồ hoạ như CUỘN VẢI có hoạ tiết đẹp"*.

   🔑 HAI THẺ NÀY VỐN LÀ DẢI MỜ, VÀ VIỆC ẤY ⛔ KHÔNG ĐƯỢC MẤT — nay còn quan trọng hơn trước: từ đợt
     1426 chữ CUỘN, nên lúc nào cũng có một chữ đang đi qua hai mép. Dải mờ là thứ làm chữ TRÔI VÀO
     và TRÔI RA thay vì bị chặt cụt giữa nét. Vì thế dáng trục nằm gọn trong NỬA NGOÀI (0 → 40%),
     phần còn lại vẫn đúng là dải mờ cũ. Đây là hai đầu một tấm tranh cuộn: trục ở trên và ở dưới.
   ⓘ Vệt ánh đồng `#d2a05a` nằm trong vùng vải ĐỤC HOÀN TOÀN ⇒ ⛔ không có chữ nào nằm trên nó ⇒ ⛔ không
     đụng con số tương phản 4,66:1 của đợt 1367. Xem phép đo ở khối `--vai-hoa-tiet` trong `app.css`. */
.bnh[b-p74zwcjlvq]::before,
.bnh[b-p74zwcjlvq]::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    height: 2.4rem;
    z-index: 2;
    pointer-events: none;
}

.bnh[b-p74zwcjlvq]::before {
    top: 0;
    /* Lớp trên: sợi chỉ vàng viền mép trục. Lớp dưới: thân trục rồi tan dần về trong suốt. */
    background: linear-gradient(180deg, rgba(255, 213, 79, .9) 0 1.5px, rgba(255, 213, 79, 0) 1.5px),
                linear-gradient(180deg, #3a0507 0%, #7c1218 9%, #d2a05a 17%, #6b0d11 26%,
                                #5c0a0e 40%, #5c0a0e 62%, rgba(92, 10, 14, 0) 100%);
}

.bnh[b-p74zwcjlvq]::after {
    bottom: 0;
    background: linear-gradient(0deg, rgba(255, 213, 79, .9) 0 1.5px, rgba(255, 213, 79, 0) 1.5px),
                linear-gradient(0deg, #3a0507 0%, #7c1218 9%, #d2a05a 17%, #6b0d11 26%,
                                #5c0a0e 40%, #5c0a0e 62%, rgba(92, 10, 14, 0) 100%);
}

/* ───────────────── Hoạ tiết gấm chìm + đốm lấp lánh ─────────────────
   ⚠ HAI LỚP NÀY LÀ THẺ RỖNG RIÊNG chứ ⛔ KHÔNG phải một `background-image` chồng lên `.bnh`. Lý do ở
     quy tắc #5: `.bnh` dựng mặt vải bằng SHORTHAND `background`, thêm một `background-image` sau đó
     là GHI ĐÈ mất dải lụa đỏ, ⛔ không một câu lỗi nào. Tách thẻ thì biến `--vai-*` hỏng cũng chỉ làm
     lớp này trong suốt: mặt vải còn nguyên.
   🩸 Đốm sáng nằm DƯỚI chữ nhưng vẫn phải dè chừng: chỗ ⛔ không có nét chữ thì mắt nhìn thẳng vào nền,
     nên đốm trắng sát cạnh nét vẫn kéo tụt tương phản CỤC BỘ. Thứ giữ được nó là
     `filter: drop-shadow(0 1px 1px rgba(70,0,0,.8))` ở `.bnh-cuon` — mỗi nét chữ có sẵn một quầng sẫm
     bao quanh. ⛔ Gỡ dòng `filter` ấy thì phải gỡ luôn lớp lấp lánh này.
   ⛔ Đừng đổi hoạ tiết sang chỉ vàng cho "rực rỡ": số đo ở `app.css` đã bác bỏ (4,32:1 < 4,5:1). */
.bnh-hoa-tiet[b-p74zwcjlvq],
.bnh-lap-lanh[b-p74zwcjlvq] {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    border-radius: inherit;
}

.bnh-hoa-tiet[b-p74zwcjlvq] {
    background-image: var(--vai-hoa-tiet);
}

.bnh-lap-lanh[b-p74zwcjlvq] {
    background-image: var(--vai-lap-lanh-a);
    animation: bnh-lap-lanh-b-p74zwcjlvq 5.2s ease-in-out infinite;
}

/* Chùm đốm thứ hai LỆCH PHA — cả tấm cùng nháy một nhịp thì mắt đọc ra "đèn nhấp nháy", ⛔ không phải
   "lụa lấp lánh". Đặt ở pseudo để ⛔ khỏi thêm một thẻ nữa vào markup (markup ở đây lặp HAI lần). */
.bnh-lap-lanh[b-p74zwcjlvq]::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--vai-lap-lanh-b);
    animation: bnh-lap-lanh-b-p74zwcjlvq 7.4s ease-in-out infinite;
    animation-delay: -3.1s;
}

/* ═════════════════ CHỮ ĐỨNG, CUỘN THEO CHIỀU DỌC ═════════════════
   ⭐ ĐỢT 1426 (việc o621) — anh Tiến 27/08/2026: *"dải chữ bên hông cho chạy theo chiều dọc đừng để
     NGANG khó đọc"*.

   🔴 ĐÍNH CHÍNH ĐOẠN CHÚ THÍCH ĐỜI TRƯỚC — đọc kỹ, vì nó KHUYÊN ĐÚNG THỨ ANH VỪA BÁC BỎ. Câu cũ ghi:
     *"`writing-mode: vertical-rl` xoay dòng chữ 90 độ như gáy sách … khác hẳn `text-orientation:
     upright` (mỗi chữ cái đứng thẳng) vốn chỉ hợp vài chữ"*. `vertical-rl` + `mixed` **đặt NẰM
     NGHIÊNG từng chữ cái**: cả dòng đúng là chạy dọc, nhưng muốn đọc thì phải **NGHIÊNG ĐẦU** —
     đúng cái "để ngang khó đọc" anh nói. Nay dùng `upright`: mỗi chữ ĐỨNG THẲNG, xếp chồng từ trên
     xuống như câu đối, đọc thẳng mặt.
   🔑 VÀ VÌ THẾ PHẢI CUỘN — hai việc này ⛔ KHÔNG tách rời được. Chữ đứng ăn trọn chiều cao dòng
     (**đo thật 21,7px/ký tự** trên bản tĩnh 1920×1080 ngày 27/08/2026) thay vì bề rộng ≈8px của đời
     chữ nghiêng, tức sức chứa tụt còn **chưa tới một nửa**: tấm vải cao 988px chỉ vừa ≈45 ký tự,
     trong khi câu đang treo thật dài **78 ký tự** (sau khi thay `{nam}`). Đứng yên là **cắt cụt hơn
     nửa câu**. Cuộn trả lại sức chứa ấy, đúng cách băng rôn ngang đã làm từ đợt 1359.
   📏 Số đo lấy được vì hai câu KHÁC HẲN NHAU cho ra cùng một con số: 78 ký tự ⇒ 21,70px · 33 ký tự
     ⇒ 21,77px. Ước lượng ban đầu (15,2px cỡ chữ × 1,12 dòng = 17px) lệch **28%** — nó quên
     `letter-spacing` và bề rộng tiến của chữ HOA tiếng Việt ở chế độ đứng.
   🔴 VÌ THẾ `.bnh-trai .bnh-chu { transform: rotate(180deg) }` ĐÃ BỎ, ⛔ đừng khôi phục. Nó có mặt
     để bên trái đọc TỪ DƯỚI LÊN cho hai tấm cùng hướng vào giữa — chuyện chỉ có nghĩa với chữ nằm
     nghiêng. Với chữ ĐỨNG thì cả hai bên vốn đã đọc từ trên xuống; lật 180 độ bây giờ là **dựng
     ngược từng chữ cái**.

   🔴 ĐÍNH CHÍNH THỨ HAI — LUẬT "⛔ CỐ Ý KHÔNG CÓ HIỆU ỨNG LẶP" CỦA ĐỢT 1395 NAY HẾT HIỆU LỰC.
     Khối `.bnh` ở trên từng ghi: *"⛔ Cố ý KHÔNG có hiệu ứng lặp: … thêm hai vật thể động lặp mãi ở
     hai bên chỗ làm việc là thứ gây mỏi mắt nhất"*. Anh Tiến 27/08/2026 chốt ngược lại: chữ phải
     CHẠY, và tấm vải phải có *"các hiệu ứng lung linh"*. Lo ngại mỏi mắt ⛔ không bị bỏ qua mà được
     trả bằng SỐ: tốc độ cuộn 40px/s — bằng **57%** nấc "vừa" của băng rôn ngang (70px/s), xem
     `LuatBieuNguHong.PxMoiGiayCuon`. ⛔ Đừng nâng tốc độ ấy lên cho "khớp băng rôn". */
.bnh-cuon[b-p74zwcjlvq] {
    /* ⚠ `align-self` chứ ⛔ không phải bỏ `align-items: center` của `.bnh`: ẢNH vẫn phải được canh
       giữa. Dải chữ thì bám mép TRÊN — phép dịch −50% chỉ tiling đúng khi hai bản xếp từ một mốc. */
    align-self: flex-start;
    display: block;
    position: relative;
    z-index: 1;
    /* ⚠ `filter` đặt Ở ĐÂY chứ ⛔ không phải `text-shadow` trên từng chữ: chữ được TÔ BẰNG GRADIENT
       (`background-clip: text` + `color: transparent`), mà bóng chữ thì KHÔNG bị cắt theo nét — để
       nguyên `text-shadow` là một vệt mờ hình chữ nhật hiện ra sau chữ trong suốt.
       🩸 Quầng sẫm này còn là thứ bảo vệ tương phản CỤC BỘ trước lớp đốm lấp lánh — xem chú thích
       `.bnh-lap-lanh` ở trên. ⛔ Gỡ dòng này thì phải gỡ luôn lớp ấy. */
    filter: drop-shadow(0 1px 1px rgba(70, 0, 0, .8));
    /* ⚠ Khai LONGHAND: thời lượng do LUẬT cấp và đi vào bằng `style=` trên chính thẻ này, nên viết
       gộp `animation: bnh-cuon …` ở đây là GHI ĐÈ mất con số ấy — biểu ngữ chạy sai tốc độ hoặc
       đứng im, ⛔ không một câu lỗi nào. */
    animation-name: bnh-cuon-b-p74zwcjlvq;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    will-change: transform;
}

.bnh-chu[b-p74zwcjlvq] {
    /* ✍ ĐỢT 1583 (việc o890) — PHÔNG PHẢI CÓ ĐỦ CHỮ HAI DẤU, ⛔ ĐỪNG BỎ DÒNG NÀY.
       Trước đợt này dải chữ thừa kế chuỗi phông của `body` (Helvetica/Arial). Arial trên máy này
       thiếu dải U+1EA0–1EF9 nên Ừ · Ộ · Ợ · Ễ · Ậ được trình duyệt lấy từ MỘT PHÔNG KHÁC ⇒ trong
       cùng một từ có hai bộ nét, hai chiều cao dấu — anh Tiến 31/08/2026 gọi đúng tên là "lỗi form".
       Đo trên chính dải chữ đang treo: ô chữ của C/H/O/M/N/G cao 22,43px, ô của Ừ/Ộ chỉ 18,27px.
       ⚠ Đây là chỗ lỗi lộ ra RÕ NHẤT vì chữ đứng thẳng (`text-orientation: upright`): mỗi ký tự một
       ô riêng nên chênh lệch chiều cao thành một nhịp gãy nhìn thấy được.
       🔑 Tên họ chữ nằm ở biến `--chu-bieu-ngu` (wwwroot/chu/chu-viet.css) — đổi phông thì sửa BIẾN,
       ⛔ đừng gõ tên họ chữ vào đây; băng rôn ngang cũng đọc đúng biến ấy. */
    /* ⚠ Đợt 2655 (việc o2143): bỏ "Helvetica Neue"/"Helvetica" — xem lý do ở BangRonChay.razor.css. */
    font-family: var(--chu-bieu-ngu, "Be Vietnam Pro"), Arial,
                 "Liberation Sans", "DejaVu Sans", sans-serif,
                 "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    display: block;
    writing-mode: vertical-rl;
    text-orientation: upright;
    white-space: nowrap;
    font-weight: 800;
    font-size: .95rem;
    letter-spacing: .05em;
    /* 🔑 KHOẢNG ĐỆM GIỮA HAI BẢN CHỮ — `52vh` chứ ⛔ KHÔNG phải một con số px nhỏ. Vòng cuộn vô tận
       dựng bằng hai bản rồi dịch đúng một nửa, nên hai bản cộng lại BẮT BUỘC phải cao hơn tấm vải;
       ngắn hơn là sau mỗi vòng có một mảng vải trống trôi qua rồi chữ NHẢY về. Câu ngắn là chuyện
       thường (ô bên phải đang treo chỉ 33 ký tự = 561px) nên px cố định ⛔ không đủ; `52vh` cho hai
       bản luôn phủ ≥104vh ở MỌI khổ màn hình. Đổi con số này thì đổi `LuatBieuNguHong.PxKhoangDem`
       theo — bên đó suy ra thời lượng cuộn. */
    padding-bottom: 52vh;
    /* 🎯 CĂN GIỮA CỘT CHỮ TRONG DẢI CUỘN — ⛔ ĐỪNG GỠ, VÀ ⛔ ĐỪNG VIẾT `margin-inline: auto`.
       Anh Tiến 01/09/2026: *"chữ băng rôn hai bên bị lệch không căn giữa"*.
       🔑 Gốc bệnh: `.bnh` đã `justify-content: center` nên DẢI CUỘN đứng giữa tấm vải — nhưng dải cuộn
         rộng bằng đứa con RỘNG NHẤT của nó, mà đó là khối BIỂU TƯỢNG (29,7px) chứ ⛔ không phải cột
         chữ (22,8px). Cột chữ là một khối `display: block` hẹp hơn cha, nên nó nằm sát MÉP TRÁI của
         dải cuộn trong khi biểu tượng có `text-align: center` và đứng giữa ⇒ hai thứ ⛔ không thẳng cột.
       📏 Đo sống 01/09/2026 (Chrome thật, khung 1920×1080, tài khoản `tienvm`, có chốt ngược `d-flex`
         và đã tắt `transition`): cột chữ lệch TRÁI **−3,4px** so với tâm tấm vải ở **CẢ HAI bên**
         (tấm 56px ⇒ lệch 6,1% bề ngang), trong khi khối biểu tượng và dải cuộn đều lệch **0**.
         Sau khi thêm hai dòng dưới: **0 / 0** ở cả hai bên.
       ⚠ PHẢI LÀ MARGIN VẬT LÝ `left`/`right`. Thẻ này mang `writing-mode: vertical-rl`, mà
         `margin-inline` tính theo trục dòng của CHÍNH NÓ — tức trục DỌC ⇒ viết `margin-inline: auto`
         là đặt nhầm sang margin trên/dưới: chữ vẫn lệch y như cũ và ⛔ không một câu lỗi nào.
       ⚠ Bề rộng cột chữ do nội dung quyết (dòng chữ ĐỨNG ⇒ bề rộng ≈ một ô ký tự), ⛔ không phải
         `width` khai tay — nên hai margin `auto` chia đôi chỗ thừa được. ⛔ Đừng "chữa" bằng cách cho
         `.bnh-cuon` thành cột flex: đo cho ra ĐÚNG cùng con số 0, nhưng nó đổi hẳn kiểu bố cục của
         khối đang gánh vòng cuộn `translateY(-50%)` — trả giá lan truyền mà ⛔ không được gì thêm.
       🔒 Chốt ngược đã đo: chiều cao dải cuộn GIỮ NGUYÊN (3221,8px bên trái · 2629px bên phải) trước
         và sau khi vá ⇒ quãng đường cuộn ⛔ không đổi, nên thời lượng do `LuatBieuNguHong` cấp vẫn
         đúng nhịp. Đổi hai dòng này thì ĐO LẠI chiều cao ấy. */
    margin-left: auto;
    margin-right: auto;
    /* 🩸 MÀU LUI PHẢI ĐỌC ĐƯỢC VÀ PHẢI ĐỨNG TRƯỚC khối `@supports` bên dưới. Trình duyệt nào không
       hiểu `background-clip: text` sẽ dừng ở đây và vẫn thấy chữ vàng. Làm ngược lại (đặt
       `color: transparent` ở khối chung) là chữ BIẾN MẤT hoàn toàn trên trình duyệt cũ — hỏng lặng lẽ
       đúng chỗ tệ nhất: hai tấm biểu ngữ trống trơn treo cho cả cơ quan xem. */
    color: #ffe082;
}

/* Nét chữ dập nổi kiểu chữ vàng trên biểu ngữ lễ.
   🔴 GRADIENT ĐỔI TỪ 90deg SANG 180deg — ⛔ đừng "sửa lại cho khớp chú thích cũ". Chữ nằm nghiêng thì
     chiều CAO của nét là trục ngang (90deg); chữ ĐỨNG thì chiều cao của nét là trục dọc (180deg).
     Giữ 90deg là ánh kim chạy ngang qua mặt chữ đứng — trông như một vệt loang, ⛔ không ra nét dập nổi.
   🔑 `background-size: 100% 1.15em` + lặp dọc là thứ làm nên chuyện: khổ nền bằng ĐÚNG một dòng chữ
     nên MỖI ký tự nhận trọn dải sáng→sẫm. Bỏ dòng ấy đi thì gradient trải hết cả cột cao 900px ⇒ chữ
     trên cùng trắng, chữ dưới cùng cam sẫm — mà đó ⛔ không phải nấc màu ai đã đo.
   📏 Nấc sẫm nhất vẫn đúng là `#f7b02e` ⇒ con số 4,66:1 đo ở đợt 1367 còn nguyên giá trị (phép trượt
     chỉ đổi CHỖ của các nấc, ⛔ không thêm nấc nào). Đổi một mã màu thì ĐO LẠI, ⛔ đừng đoán. */
@supports (background-clip: text) or (-webkit-background-clip: text) {
    .bnh-chu[b-p74zwcjlvq] {
        background-image: linear-gradient(180deg,
                          #fffdf3 0%, #ffeaa7 32%, #ffd54f 55%, #f7b02e 84%, #ffe9b0 100%);
        background-size: 100% 1.15em;
        background-repeat: repeat;
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
        /* ⭐ ÁNH KIM LƯỚT DỌC NÉT CHỮ ("hiệu ứng lung linh"): trượt đúng MỘT khổ nền nên liền mạch. */
        animation: bnh-anh-kim-b-p74zwcjlvq 4.8s linear infinite;
    }
}

/* ═════════════════ ⭐ ĐỢT 1468 (việc o663) — BIỂU TƯỢNG ĐI KÈM CHỮ ═════════════════
   Anh Tiến 28/08/2026: *"«Chào mừng hội thao mừng ngày thành lập tổng kho» có thêm các biểu tượng
   liên quan có hiệu ứng và «Chào mừng Đợt diễn tập PCCC 2026» và các biểu tượng liên quan"*.

   🔑 KHỐI NÀY CUỘN CÙNG CHỮ, ⛔ không phải một lớp phủ đứng yên. Tấm vải chỉ rộng 40–236px và đã kín
     chỗ; đắp thêm một lớp cố định là biểu tượng nằm ĐÈ lên chữ đang trôi qua. Nằm trong dòng cuộn thì
     nó tự có chỗ, và `LuatBieuNguHong.PxMoiBieuTuong` đã cộng chiều cao ấy vào quãng đường cuộn.
   ⚠ NHỊP LỆCH KHAI BẰNG `nth-child`, ⛔ không bằng `animation-delay` ghép trong thuộc tính kiểu ở
     Razor — app chạy `vi-VN` nên số thực nội suy ra dấu phẩy và cả khai báo bị vứt (quy tắc #5).
   ⛔ ĐỪNG cho biểu tượng to hơn: cỡ hiện tại đã gần gấp đôi chữ. To nữa là tấm biểu ngữ hoá một cột
     hình, mà thứ người ta đọc là CÂU CHỮ. */
.bnh-bieu[b-p74zwcjlvq] {
    display: block;
    position: relative;
    z-index: 1;
    text-align: center;
    line-height: 1;
    padding: .35rem 0;
}

.bnh-bieu-mot[b-p74zwcjlvq] {
    display: block;
    font-size: 1.35rem;
    padding: .18rem 0;
    /* Quầng ấm quanh hình cho nó ăn nhập với vải đỏ. Lớp thứ hai là quầng SẪM — cùng vai với
       `filter` của `.bnh-cuon`: giữ hình tách khỏi nền khi lớp đốm lấp lánh sáng lên. */
    filter: drop-shadow(0 0 6px rgba(255, 190, 90, .55)) drop-shadow(0 1px 1px rgba(70, 0, 0, .8));
    animation: bnh-bieu-nhun-b-p74zwcjlvq 3.2s ease-in-out infinite;
}

/* Sáu nhịp lệch — trần `LuatBieuNguHong.TranBieuTuong` cũng là 6, nên ⛔ không có ô nào rơi ra ngoài
   bảng này. Đổi trần ở luật thì thêm/bớt dòng ở đây cho khớp. */
.bnh-bieu-mot:nth-child(1)[b-p74zwcjlvq] { animation-delay: 0s; }
.bnh-bieu-mot:nth-child(2)[b-p74zwcjlvq] { animation-delay: -.55s; }
.bnh-bieu-mot:nth-child(3)[b-p74zwcjlvq] { animation-delay: -1.1s; }
.bnh-bieu-mot:nth-child(4)[b-p74zwcjlvq] { animation-delay: -1.65s; }
.bnh-bieu-mot:nth-child(5)[b-p74zwcjlvq] { animation-delay: -2.2s; }
.bnh-bieu-mot:nth-child(6)[b-p74zwcjlvq] { animation-delay: -2.75s; }

@keyframes bnh-bieu-nhun-b-p74zwcjlvq {
    0%, 100% { transform: scale(1)    rotate(0deg); }
    50%      { transform: scale(1.16) rotate(-4deg); }
}

/* ═════════════════ ⭐ ĐỢT 1468 (việc o663) — PHÁO HOA ═════════════════
   Anh Tiến 28/08/2026: *"2 băng rôn 2 bên bổ sung thêm hiệu ứng bắn pháo hoa"*.

   🔴 ĐỢT 1793 (việc o1065) — ANH TIẾN BÁO 02/09/2026: *"hiệu ứng pháo hoa và sao băng đang xấu cần
     cải thiện"*. Đời trước là **MỘT VÒNG TRÒN 12 chấm cách đều nhau, cùng một bán kính 16px**, phóng
     to rồi tắt. Vòng tròn đều tăm tắp ⛔ KHÔNG phải là hình một chùm pháo — mắt đọc nó ra cái
     **bánh răng** hoặc **dấu sao** đang to dần. Ba chùm lại dùng CHUNG một `box-shadow` nên là ba
     bản sao, chỉ khác chỗ đứng và độ trễ. Ba chỗ chữa, vẫn giữ nguyên cách dựng rẻ tiền cũ:
       ① **BÁN KÍNH VÀ GÓC LỆCH NHAU** — 11 tia rải trên vành 14…19px ở những góc ⛔ không đều. Chỉ
          riêng việc phá cái vành đều đã đủ để hình thôi ra "bánh răng".
       ② **HAI TẦNG + MỘT CÚ LOÉ** — `::before` là quầng sáng loé ngay lúc nổ rồi tắt trong ~11% chu
          kỳ (pháo thật bao giờ cũng loé trước khi bung tia); `::after` là chùm tia TRONG bán kính
          8…11px xoay lệch 26° với vành ngoài. Ba tầng chồng nhau mới ra được chiều sâu.
       ③ **CÓ TRỌNG LỰC** — tàn pháo rơi `var(--bnh-roi)` xuống dưới ở cuối vòng đời. Nở ra rồi đứng
          yên tại chỗ là thứ đọc ra "đồ hoạ", ⛔ không phải "tàn lửa".
     Ba chùm nay khác nhau bằng BA BIẾN chứ ⛔ không phải ba bản chép: `--bnh-goc` (xoay cả chùm) ·
     `--bnh-no` (chùm to nhỏ khác nhau) · `--bnh-roi` (rơi nhiều ít khác nhau).
   ⚠ `animation-duration`/`-delay` của `::before` và `::after` khai `inherit` — CỐ Ý. Nhịp thật nằm ở
     ba dòng `nth-child` bên dưới; chép tay lại nhịp ấy cho hai lớp giả là dựng thêm HAI chỗ phải nhớ
     sửa cùng lúc, mà quên một chỗ thì ba tầng của CÙNG một chùm nổ **lệch pha nhau** — trông thành
     ba chùm chồng lên nhau, và ⛔ không một câu lỗi nào. `inherit` giữ ĐÚNG MỘT nguồn sự thật.

   🔑 MỘT CHÙM NỔ DỰNG BẰNG `box-shadow` NHIỀU CHẤM TRÊN MỘT THẺ DUY NHẤT, rồi phóng to + mờ dần. Đây
     là cách rẻ nhất: ba thẻ cho cả tấm, và cả vòng đời chỉ chạy `transform` + `opacity` — hai thuộc
     tính trình duyệt hợp ảnh trên GPU. ⛔ Đừng dựng mỗi tia một thẻ (11 tia × 3 chùm × 2 tấm = 66 thẻ
     động ở rìa tầm mắt, chạy suốt ngày).
   🩸 NẰM DƯỚI CHỮ (`z-index: 0` so với `1` của `.bnh-cuon`): đây là tấm biểu ngữ có câu chữ người ta
     đọc, ⛔ không phải một màn pháo hoa. Chấm sáng chạy đè lên nét chữ là làm mất chữ.
   ⚠ `overflow: hidden` của `.bnh` đã cắt gọn mọi tia lọt ra mép — ⛔ đừng gỡ nó để "cho pháo bay rộng
     hơn": gỡ là dải chữ cuộn cũng hết bị cắt và tràn ra ngoài tấm vải.
   ⓘ Ba màu vàng kim · cam thương hiệu · trắng ngà — cùng bảng với chữ và viền, ⛔ đừng thêm màu lạ. */
.bnh-phao-hoa[b-p74zwcjlvq] {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    border-radius: inherit;
}

.bnh-phao-hoa i[b-p74zwcjlvq] {
    position: absolute;
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: transparent;

    /* Ba số làm nên "ba chùm khác nhau" — ba dòng `nth-child` bên dưới đặt lại từng số. */
    --bnh-goc: 0deg;   /* xoay cả chùm ⇒ cùng một hình mà ⛔ không đọc ra là chép lại */
    --bnh-no: 1;       /* chùm nở to tới đâu */
    --bnh-roi: 6px;    /* tàn rơi xuống bao nhiêu ở cuối vòng đời */

    /* ⭐ ĐỢT 1793 — VÀNH NGOÀI 11 TIA, BÁN KÍNH 14…19px, GÓC ⛔ KHÔNG ĐỀU. Cột thứ 4 (độ nở của
       chấm) đổi theo từng tia để tàn có hạt to hạt nhỏ; ⛔ đừng "dọn cho đều" — đều tăm tắp chính là
       cái xấu vừa chữa.
       ⚠ Khổ hẹp nhất của tấm vải là 40px, mà `overflow: hidden` của `.bnh` cắt gọn phần thò ra ⇒ tia
         19px vẫn an toàn, chỉ là ở khổ ấy thấy được non nửa chùm. */
    box-shadow:
          0px -19px 0  0.9px #fff6d8,    8px -13px 0  0.1px #ffd54f,   18px  -9px 0  0.5px #f7b02e,
         16px   1px 0  0.1px #ffe9b0,   15px  11px 0  0.8px #ffd54f,    5px  13px 0 -0.1px #fff6d8,
         -5px  17px 0  0.5px #f47b20,  -10px  11px 0  0.1px #ffd54f,  -19px   6px 0  0.9px #ffe9b0,
        -15px  -4px 0  0px   #f7b02e,  -12px -15px 0  0.5px #ffd54f;

    animation: bnh-phao-no-b-p74zwcjlvq 4.2s ease-out infinite;
}

/* ⭐ ĐỢT 1793 — CÚ LOÉ LÚC NỔ. Quầng sáng bung ra rồi tắt trong ~11% chu kỳ: nó chỉ có mặt đúng lúc
   chùm tia còn đang bé, nên nó ⛔ KHÔNG che chữ, mà lại là thứ nói cho mắt biết "vừa có một tiếng
   nổ" — thiếu nó thì chùm tia cứ thế lớn dần lên từ hư không.
   ⓘ Vẽ bằng `radial-gradient` chứ ⛔ không bằng `box-shadow` nhoè: cần một quầng TAN DẦN ra rìa. */
.bnh-phao-hoa i[b-p74zwcjlvq]::before {
    content: "";
    position: absolute;
    inset: -5px;
    border-radius: 50%;
    background: radial-gradient(circle,
                #fffdf5 0%, rgba(255, 233, 176, .9) 34%, rgba(247, 176, 46, .45) 60%,
                rgba(244, 123, 32, 0) 78%);
    animation-name: bnh-phao-loe-b-p74zwcjlvq;
    animation-timing-function: ease-out;
    animation-iteration-count: infinite;
    animation-duration: inherit;   /* ⛔ đừng chép tay nhịp — xem khối chú thích ⚠ ở đầu mục */
    animation-delay: inherit;
}

/* ⭐ ĐỢT 1793 — CHÙM TIA TRONG, bán kính 8…11px, xoay lệch 26° so với vành ngoài. Hai vành xoay lệch
   nhau mới ra chiều sâu; xếp thẳng hàng là hình lại phẳng ra đúng như đời trước. */
.bnh-phao-hoa i[b-p74zwcjlvq]::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: transparent;
    box-shadow:
          3px  -9px 0  0px   #fff6d8,   10px  -4px 0  0.3px #ffd54f,    7px   3px 0 -0.2px #ffe9b0,
          3px  10px 0  0.1px #f7b02e,   -5px   8px 0 -0.1px #fff6d8,  -11px  -2px 0  0.2px #ffd54f,
         -7px  -4px 0 -0.1px #f47b20;
    animation-name: bnh-phao-trong-b-p74zwcjlvq;
    animation-timing-function: ease-out;
    animation-iteration-count: infinite;
    animation-duration: inherit;
    animation-delay: inherit;
}

/* Ba chùm: khác chỗ đứng · khác nhịp · và (từ đợt 1793) khác cả góc xoay, cỡ nở, tốc rơi. */
.bnh-phao-hoa i:nth-child(1)[b-p74zwcjlvq] {
    top: 16%; left: 34%;
    animation-delay: -.2s;
    --bnh-goc: 0deg;    --bnh-no: 1;    --bnh-roi: 6px;
}
.bnh-phao-hoa i:nth-child(2)[b-p74zwcjlvq] {
    top: 48%; left: 66%;
    animation-delay: -1.7s; animation-duration: 5.1s;
    --bnh-goc: 137deg;  --bnh-no: .78;  --bnh-roi: 4px;
}
.bnh-phao-hoa i:nth-child(3)[b-p74zwcjlvq] {
    top: 78%; left: 40%;
    animation-delay: -3.1s; animation-duration: 3.6s;
    --bnh-goc: 64deg;   --bnh-no: 1.14; --bnh-roi: 8px;
}

/* ⛔ Mốc 82% tới 100% CỐ Ý giữ nguyên độ mờ 0: chùm nổ tắt hẳn rồi mới tới lượt sau, ⛔ không co về
   tâm trong lúc còn nhìn thấy — thấy được là ra một chấm sáng thu nhỏ, đọc thành "lỗi vẽ".
   ⭐ ĐỢT 1793 — dáng vòng đời đổi hẳn cho giống pháo thật: BUNG RẤT NHANH (0→26%) rồi TRÔI CHẬM và
     RƠI XUỐNG trong lúc tàn dần. Đời trước nở đều tay suốt 55% chu kỳ nên trông như một hình vẽ đang
     được phóng to, ⛔ không như một cú nổ.
   ⚠ `translateY` viết TRƯỚC `rotate`/`scale`: phép dời chỗ phải đọc theo pixel thật của màn hình.
     Đặt nó sau `scale` là quãng rơi bị nhân theo cỡ chùm — chùm to rơi xa, chùm nhỏ gần như đứng im. */
@keyframes bnh-phao-no-b-p74zwcjlvq {
    0%        { transform: translateY(0) rotate(var(--bnh-goc)) scale(calc(var(--bnh-no) * .06)); opacity: 0; }
    5%        { opacity: 1; }
    26%       { transform: translateY(0) rotate(var(--bnh-goc)) scale(calc(var(--bnh-no) * .92)); opacity: 1; }
    46%       { transform: translateY(calc(var(--bnh-roi) * .35)) rotate(var(--bnh-goc)) scale(calc(var(--bnh-no) * 1.08)); opacity: 1; }
    64%       { transform: translateY(calc(var(--bnh-roi) * .7)) rotate(var(--bnh-goc)) scale(calc(var(--bnh-no) * 1.2)); opacity: .62; }
    82%, 100% { transform: translateY(var(--bnh-roi)) rotate(var(--bnh-goc)) scale(calc(var(--bnh-no) * 1.32)); opacity: 0; }
}

/* Cú loé: có mặt đúng đoạn đầu rồi nằm im ở độ mờ 0 hết vòng — ⛔ đừng cho nó loé lại giữa chừng. */
@keyframes bnh-phao-loe-b-p74zwcjlvq {
    0%        { transform: scale(.5);  opacity: 0; }
    3%        { transform: scale(1.6); opacity: 1; }
    11%, 100% { transform: scale(2.6); opacity: 0; }
}

/* Chùm trong bung chậm hơn vành ngoài và tắt sớm hơn — đó là thứ làm chùm nổ có "ruột".
   ⚠ `rotate(26deg)` phải LẶP LẠI ở mọi mốc: `transform` trong `@keyframes` GHI ĐÈ trọn cả chuỗi ở
     luật tĩnh phía trên, ⛔ không cộng dồn vào nó. Quên một mốc là chùm trong tự xoay về 0° trong
     lúc chạy — hai vành trùng nhau, và mất sạch chiều sâu vừa dựng. */
@keyframes bnh-phao-trong-b-p74zwcjlvq {
    0%        { transform: rotate(26deg) scale(.1);   opacity: 0; }
    7%        { transform: rotate(26deg) scale(.34);  opacity: .95; }
    34%       { transform: rotate(26deg) scale(1);    opacity: .9; }
    52%       { transform: rotate(26deg) scale(1.14); opacity: .7; }
    70%, 100% { transform: rotate(26deg) scale(1.3);  opacity: 0; }
}

/* ───────────────── Ảnh (khi anh cấp tệp) ─────────────────
   `contain` chứ ⛔ không `cover`: biểu ngữ do anh vẽ sẵn, cắt mất một phần là cắt mất chữ trên đó.
   ⓘ Ảnh ⛔ KHÔNG cuộn — nó là một tấm vẽ sẵn, trôi qua trôi lại chỉ làm ⛔ không xem được gì. Vì thế
     `LuatBieuNguHong` trả `GiayCuonCss` RỖNG cho bên treo bằng ảnh, và markup ⛔ không bọc ảnh vào
     `.bnh-cuon`. Ảnh vẫn hưởng hoạ tiết + đốm lấp lánh vì hai lớp ấy nằm dưới (`z-index: 0`). */
.bnh-anh[b-p74zwcjlvq] {
    position: relative;
    z-index: 1;
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* Dải chữ trôi lên đúng MỘT bản (hai bản nối đuôi ⇒ −50%) rồi về vạch xuất phát, mắt ⛔ không thấy
   điểm nối. ⛔ Đừng đổi sang `translateY(-100%)`: đó là trôi hết cả hai bản, tức để lại một khoảng
   trống dài bằng cả tấm vải sau mỗi vòng. */
@keyframes bnh-cuon-b-p74zwcjlvq {
    from { transform: translateY(0); }
    to   { transform: translateY(-50%); }
}

/* Ánh kim lướt dọc nét chữ — trượt đúng MỘT khổ nền (`1.15em`) nên vòng lặp liền mạch. */
@keyframes bnh-anh-kim-b-p74zwcjlvq {
    from { background-position: 0 0; }
    to   { background-position: 0 1.15em; }
}

/* Đốm lấp lánh: mờ hẳn rồi sáng lên, kèm một nhịp xê dịch rất nhỏ cho ra vẻ ánh sáng đổi góc.
   ⛔ Đừng cho `opacity` chạm 1: đốm đứng nguyên ở độ sáng cực đại là một chấm trắng CHẾT, ⛔ không
   phải một tia lấp lánh. */
@keyframes bnh-lap-lanh-b-p74zwcjlvq {
    0%, 100% { opacity: .12; transform: translateY(0); }
    50%      { opacity: .85; transform: translateY(-1px); }
}

@keyframes bnh-vao-trai-b-p74zwcjlvq {
    from { opacity: 0; transform: translateX(-14px); }
    to   { opacity: 1; transform: none; }
}

@keyframes bnh-vao-phai-b-p74zwcjlvq {
    from { opacity: 0; transform: translateX(14px); }
    to   { opacity: 1; transform: none; }
}

/* ═══════════════ NGƯỠNG HIỆN ═══════════════
   1528px suy ra từ ba hằng số ở đầu tệp: cần `(V − 1400) / 2` không nhỏ hơn `40 + 24` ⇒ V từ 1528.
   ⛔ Đừng hạ tiếp cho "nhiều máy thấy hơn": dưới đó chữ chạm mép vải, rồi biểu ngữ lấn vào cột nội
   dung — mà màn 1366px thì dải KHÔNG TỒN TẠI (0px), hạ bao nhiêu cũng vô ích.
   📏 Đo tận tay 27/08/2026 ở khung nhìn 1536px: tấm 44px · khe tới `.content` 8,5px · cộng 24px đệm
     của `.content` ⇒ 32px tới chữ đầu tiên. Chốt "không đè lên nội dung" còn nguyên. */
@media (min-width: 1528px) {
    .bnh[b-p74zwcjlvq] {
        display: flex;
    }
}

/* ═══════════════ ⭐ ĐỢT 1580 (việc o885) — NGƯỠNG THỨ HAI ĐÃ BỎ, CHỈ CÒN MỘT ═══════════════
   Anh Tiến 31/08/2026: *"ở trang chủ mất băng rôn 2 bên"*.

   🔴 CHỖ NÀY TỪNG CÓ MỘT NHÁNH `@media (max-width: 1887px)` ẨN RIÊNG CHO TRANG CHỦ (đợt 1481) —
     ⛔ ĐỪNG DỰNG LẠI NÓ. Nhánh ấy chữa vế "biểu ngữ đè lên nội dung", nhưng chữa bằng cách **cắt hẳn
     hai tấm khỏi trang chủ ở mọi khổ dưới 1888px**, tức ở đúng khổ màn phổ biến nhất. Đo sống 31/08
     (Chrome thật, so trang chủ với `/cong-viec` cùng khổ): 1528 · 1536 · 1700 · 1887px thì trang
     thường treo đủ **2 tấm** còn trang chủ **0 tấm**. Riêng dải **1528–1599px** là chỗ nhánh ấy SAI
     hẳn chứ ⛔ không chỉ khắt khe: vế nới cột của trang chủ bắt đầu từ 1600px, nên dưới 1600 trang
     chủ có cột **y hệt** trang thường (1400px) và dải trống đo được **68px** — thừa chỗ mà vẫn ẩn.

   ⇒ Nay chữa ở GỐC, tại `wwwroot/trang-chu.css`: cột nội dung của trang chủ tự chừa lại
     `2 × (RongDaiToiThieuPx + KheThoPx) = 128px` bằng vế `calc(100vw - 128px)` trong phép `min()`.
     Trang chủ vì thế **luôn còn đủ dải trống từ 1528px trở lên** ⇒ dùng chung ĐÚNG MỘT ngưỡng với
     mọi trang khác, và hằng số `RongManHinhToiThieuTrangChuPx` cũng đã bỏ theo.
   🔑 Vì sao chữa ở đó mà ⛔ không chữa ở đây: `@media` ⛔ KHÔNG đọc được biến CSS, nên mọi cách chữa
     tại tệp này đều phải gõ tay một con số ngưỡng thứ hai — mà con số ấy chỉ đúng chừng nào ⛔ không
     ai đụng vào trần cột của trang chủ. Vế `calc()` bên kia thì tự đúng theo mọi khổ màn hình.
   ⓘ Trình duyệt không hiểu `:has()` thì trang chủ ⛔ không nới cột (cùng một `:has()`) nên nó về đúng
     1400px và ngưỡng 1528px ở trên lại là con số đúng — hai vế hỏng cùng chiều, ⛔ không có ca lệch.

   🩸 ⛔ TUYỆT ĐỐI ĐỪNG VIẾT `@media (min-width: …) and (max-width: …)` TRONG TỆP `.razor.css` —
     BÀI HỌC TRẢ GIÁ Ở ĐỢT 1481, GIỮ LẠI DÙ NHÁNH SINH RA NÓ ĐÃ BỎ. Bộ biên dịch CSS cách ly của
     Blazor ⛔ KHÔNG hiểu câu điều kiện có `and`: nó gắn dấu scope vào ĐUÔI CÂU MEDIA thay vì vào bộ
     chọn bên trong, cho ra `@media (min-width: 1528px) and (max-width: 1887px)[b-p74zwcjlvq] {` —
     một câu media **hỏng cú pháp**, nên trình duyệt **bỏ nguyên khối**. Hỏng đúng kiểu LẶNG LẼ nhất:
     build xanh · test xanh (bài test đọc TỆP NGUỒN, ⛔ không đọc gói đã biên dịch) · chỉ đo sống mới
     lòi ra. ⚠ Chốt ngược nếu có ngày phải viết câu media mới ở đây: mở
     `obj/Debug/net10.0/scopedcss/bundle/TKNB.Web.styles.css` xem dấu `[b-…]` nằm ở đâu, ⛔ đừng tin
     tệp nguồn. Hàng rào `LuatBieuNguHongTests` bắt ĐỎ khi tệp này có câu media mang chữ `and`. */

/* ═══════════════ Ô XEM THỬ ở khối cấu hình trang Dev ═══════════════
   ⚠ CHỈ đổi CHỖ ĐỨNG và KHỔ. Màu · chữ · viền · gradient vẫn là của chính component thật ở trên, nên
     đây ⛔ KHÔNG phải bản sao CSS thứ hai — sửa màu ở trên là ô xem thử đổi theo ngay.
   🔑 VIẾT GHÉP HAI LỚP `.bnh.bnh-xem-thu` chứ ⛔ không `.bnh-xem-thu` trần: CSS này là scoped nên
     `.bnh-xem-thu` biên dịch ra (0,2,0) — BẰNG `.bnh`, thắng thua chỉ nhờ THỨ TỰ trong tệp, mà thứ tự
     là thứ một lượt dọn dẹp về sau xê dịch được không dấu vết. Ghép hai lớp cho ra (0,3,0), thắng chắc
     chắn kể cả nhánh `@media` bật `display: flex` ở trên (đúng khuôn `.content.content-rong`, quy tắc #5).
   ⚠ Phải ép `display: flex` vì `.bnh` mặc định ẨN: không ép thì người khai trên máy dưới 1700px mở
     khối cấu hình ra thấy một ô trống rồi tưởng chưa lưu được. */
.bnh.bnh-xem-thu[b-p74zwcjlvq] {
    position: static;
    display: flex;
    width: 7rem;
    height: 13rem;
    max-width: none;
    animation: none;
}

/* ⭐ ĐỢT 1426 — ô xem thử phải thu KHOẢNG ĐỆM theo, ⛔ không thì nó rỗng gần hết.
   Khoảng đệm thật là `52vh` (≈561px) — to gấp ba lần cả cái ô xem thử cao 13rem ⇒ người khai mở khối
   cấu hình ra sẽ thấy một tấm vải TRỐNG suốt gần cả vòng cuộn rồi tưởng chữ chưa lưu được.
   ⚠ Đây vẫn đúng luật "CHỈ đổi CHỖ ĐỨNG và KHỔ" ghi ở trên: khoảng đệm là KHỔ, ⛔ không phải màu hay
     chữ. Vòng cuộn vì thế nhanh hơn ngoài đời — ô này để xem MÀU và CHỮ, ⛔ không phải để đo tốc độ. */
.bnh.bnh-xem-thu .bnh-chu[b-p74zwcjlvq] {
    padding-bottom: 6rem;
}

/* ───────────────── Người tắt hiệu ứng chuyển động ─────────────────
   🔴 KHỐI NÀY ĐỔI HẲN Ở ĐỢT 1426, ⛔ đừng đọc câu cũ. Câu cũ ghi: *"Ở đây ⛔ KHÔNG có gì khác phải dừng
     vì cả tấm vốn đã đứng yên"* — đúng cho tới 27/08/2026, ngày anh Tiến cho chữ CHẠY. Nay có ba thứ
     chuyển động phải xử lý, và mỗi thứ một lối:
   ⚠ DẢI CHỮ: dừng cuộn thì chữ dài bị khung cắt cụt ⇒ phải trả lại đường ĐỌC bằng tay — cho tấm vải
     tự cuộn dọc và bỏ bản chữ thứ hai đi (nó chỉ để nối vòng chạy; giữ lại là câu chúc hiện HAI lần
     chồng nhau). Đúng khuôn `BangRonChay.razor.css` đã dựng cho cùng ca này.
   ⚠ ĐỐM LẤP LÁNH: hạ về độ sáng thấp chứ ⛔ không `display: none` — tắt hẳn thì tấm vải của họ khác
     hẳn tấm mọi người thấy, mà thứ họ xin chỉ là "đừng nhúc nhích".
   ⚠ HOẠ TIẾT GẤM: GIỮ NGUYÊN, nó vốn ⛔ không hề chuyển động. */
@media (prefers-reduced-motion: reduce) {
    .bnh[b-p74zwcjlvq] {
        animation: none;
        overflow-y: auto;
        scrollbar-width: thin;
        scrollbar-color: rgba(255, 213, 79, .5) transparent;
    }

    .bnh-cuon[b-p74zwcjlvq] {
        animation: none;
    }

    .bnh-chu[b-p74zwcjlvq] {
        animation: none;
        padding-bottom: 0;
    }

    /* 🔴 ĐỢT 1468 — ⛔ ĐỪNG TRẢ VỀ `.bnh-chu + .bnh-chu`. Từ đợt này mỗi bản là một CẶP
       [biểu tượng][chữ], nên hai thẻ `.bnh-chu` ⛔ KHÔNG còn kề nhau và bộ nối `+` khớp 0 phần tử —
       bản thứ hai hiện lại, người tắt chuyển động đọc câu chúc HAI LẦN chồng nhau. Đếm theo THỨ TỰ
       CON thì đúng ở cả hai đời markup: hai thẻ đầu là một bản, từ thẻ thứ ba trở đi là bản lặp. */
    .bnh-cuon > :nth-child(n+3)[b-p74zwcjlvq] {
        display: none;
    }

    /* Biểu tượng GIỮ NGUYÊN chỗ đứng, chỉ thôi nhún — cùng lẽ với đốm lấp lánh ngay dưới: thứ họ xin
       là "đừng nhúc nhích", ⛔ không phải "bỏ bớt hình của tôi đi". */
    .bnh-bieu-mot[b-p74zwcjlvq] {
        animation: none;
    }

    /* Pháo hoa thì ẨN HẲN: nó là một chuyển động NỔ, đứng yên ở khung hình bất kỳ là ba chấm sáng
       nằm chết giữa tấm vải — trông như lỗi vẽ, ⛔ không phải như một hiệu ứng đã tắt. */
    .bnh-phao-hoa[b-p74zwcjlvq] {
        display: none;
    }

    .bnh-lap-lanh[b-p74zwcjlvq],
    .bnh-lap-lanh[b-p74zwcjlvq]::after {
        animation: none;
        opacity: .3;
    }
}
/* /Components/ChungTu/ChuMucTieuMotDong.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 1597 (việc o889) — MỘT DÒNG · RÊ CHUỘT CHẠY CHỮ ═══
   Anh Tiến 31/08/2026: *"chỉ hiển thị 1 dòng, phần còn lại ẩn đi, khi đưa chuột tới thì chữ chạy ra"*. */

/* Gỡ hết dáng nút của Bootstrap — người dùng phải thấy một dòng chữ, ⛔ không phải một cái nút. */
.cmt-nut[b-7ngjox6yuz] {
    display: block;
    width: 100%;
    max-width: 100%;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    line-height: inherit;
    cursor: pointer;
}

/* 🔴 KHUNG CẮT. `container-type: inline-size` là thứ làm cho phép tính trong @keyframes chạy được —
      ⛔ đừng gỡ, gỡ là chữ trượt đi mất hút (xem chú thích của `cmt-truot`). */
.cmt-mot-dong[b-7ngjox6yuz] {
    container-type: inline-size;
    overflow: hidden;
    /* ⚠ Chiều cao khoá theo MỘT dòng: thiếu dòng này thì lúc chữ đang chạy (đã bỏ ellipsis) trình
       duyệt vẫn tính chiều cao theo chữ tràn, và cả hàng nảy lên một nhịp mỗi lần rê chuột vào. */
    line-height: 1.5;
    height: 1.5em;
}

/* Lúc ĐỨNG YÊN: một dòng, tràn thì ba chấm. */
.cmt-chu[b-7ngjox6yuz] {
    display: inline-block;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: top;
    /* ⛔ Đừng đặt transition cho transform: lượt rê chuột RA phải trả chữ về chỗ cũ NGAY, không thì
       chữ bò ngược chậm rãi trong khi người dùng đã sang dòng khác. */
}

/* Lúc RÊ CHUỘT VÀO (hoặc bàn phím tab tới): bỏ ba chấm, thả cho chữ dài hết cỡ rồi trượt. */
.cmt-nut:hover .cmt-chu[b-7ngjox6yuz],
.cmt-nut:focus-visible .cmt-chu[b-7ngjox6yuz] {
    max-width: none;
    text-overflow: clip;
    animation: cmt-truot-b-7ngjox6yuz 7s ease-in-out infinite alternate;
}

/* 🔑 PHÉP TÍNH TỰ GIỚI HẠN — chỗ dễ làm hỏng nhất, ⛔ đọc trước khi sửa.
   `100cqw` = bề ngang KHUNG CẮT · `100%` trong translateX = bề ngang CHÍNH CHỮ.
   · Chữ NGẮN hơn khung ⇒ (100cqw − 100%) DƯƠNG ⇒ min(0px, dương) = 0 ⇒ ⛔ KHÔNG nhúc nhích.
     Đây là vế quan trọng: thiếu `min()` thì mọi dòng ngắn cũng trượt lung tung cho không việc gì.
   · Chữ DÀI hơn khung ⇒ hiệu ÂM, đúng bằng phần bị cắt ⇒ trượt vừa đủ để đọc hết cái đuôi,
     ⛔ không trượt quá thành màn hình trống.
   ⚠ Hai mốc dừng 15% / 85% là chỗ chữ ĐỨNG YÊN cho mắt bắt kịp ở hai đầu — bỏ đi thì chữ đảo chiều
     ngay khi vừa tới nơi, đọc không kịp chữ cuối. */
@keyframes cmt-truot-b-7ngjox6yuz {
    0%, 15% {
        transform: translateX(0);
    }

    85%, 100% {
        transform: translateX(min(0px, calc(100cqw - 100%)));
    }
}

/* ♿ Người đã tắt hiệu ứng chuyển động trong hệ điều hành thì ⛔ KHÔNG chạy chữ — nhưng vẫn phải đọc
   được hết, nên đổi sang thả cho chữ dài ra rồi cuộn ngang bằng tay. ⛔ Đừng bỏ nhánh này: bỏ là
   nhóm ấy mất luôn đường đọc đuôi trên máy tính. */
@media (prefers-reduced-motion: reduce) {
    .cmt-nut:hover .cmt-chu[b-7ngjox6yuz],
    .cmt-nut:focus-visible .cmt-chu[b-7ngjox6yuz] {
        animation: none;
    }

    .cmt-nut:hover.cmt-mot-dong[b-7ngjox6yuz],
    .cmt-nut:focus-visible.cmt-mot-dong[b-7ngjox6yuz] {
        overflow-x: auto;
    }
}

/* Dấu cho biết bấm được — chỉ hiện khi rê chuột, để lúc thường dòng chữ vẫn sạch. */
.cmt-nut:hover .cmt-chu[b-7ngjox6yuz],
.cmt-nut:focus-visible .cmt-chu[b-7ngjox6yuz] {
    text-decoration: underline dotted;
    text-underline-offset: 3px;
}

/* Mục tiêu ĐÃ ĐẠT: gạch ngang + màu xanh, do màn hình truyền vào qua tham số `Lop`. */
.cmt-nut.cmt-dat .cmt-chu[b-7ngjox6yuz] {
    text-decoration: line-through;
}

.cmt-nut.cmt-dat:hover .cmt-chu[b-7ngjox6yuz],
.cmt-nut.cmt-dat:focus-visible .cmt-chu[b-7ngjox6yuz] {
    text-decoration: line-through underline dotted;
}
/* /Components/ChungTu/DaiThoiGian.razor.rz.scp.css */
/* ═══ DẢI THỜI GIAN — ĐỢT 1323 (việc o527) · BỎ GÓC NGHIÊNG Ở ĐỢT 1334 (việc o534) ═══
   Anh Tiến 26/08/2026 sáng: "thanh timeline động, 3d có hiệu ứng, đẹp hiện đại".
   Anh Tiến 26/08/2026 chiều: "em bỏ nút nghiêng 3D trên timeline, timeline đo theo từng năm".
   ⇒ Dải LUÔN PHẲNG, nhưng vẫn ĐỘNG: thanh nở ra, vệt sáng chạy, dòng trỏ chuột nổi lên.

   MÀU lấy từ token --bs-* của Bootstrap 5.3 (app đã nạp bản này từ đợt 831) nên dải tự đúng ở CẢ hai
   giao diện sáng và tối. KHÔNG gõ mã màu cứng, KHÔNG dùng lớp bg-light: nền sáng gắn cứng thành mảng
   chói ở giao diện tối.

   Mọi hiệu ứng động đều tắt ở khối prefers-reduced-motion cuối tệp — người đặt máy ở chế độ giảm
   chuyển động thường là người bị chóng mặt vì chuyển động, dải nhảy múa là dải họ không đọc nổi. */

.dtg-khung[b-m98unuwp4g] {
    --dtg-cao-dong: 34px;
    --dtg-cot-ten: 210px;
    --dtg-vien: color-mix(in srgb, var(--bs-body-color) 16%, transparent);
    --dtg-nen-lan: color-mix(in srgb, var(--bs-body-color) 6%, transparent);
    --dtg-thanh-a: var(--bs-primary);
    --dtg-thanh-b: var(--bs-info);
    position: relative;
    padding: .25rem 0 .5rem;
}

/* Mặt phẳng chứa cả dải.
   ⭐ ĐỢT 1334 — thuộc tính perspective ĐÃ BỎ cùng góc nghiêng: không còn gì xoay thì nó chỉ là một
   dòng luật chết, mà dòng luật chết trong tệp dùng chung là chỗ lượt sửa sau đọc vào rồi hiểu nhầm
   là dải vẫn đang dựng cảnh ba chiều. */
.dtg-san[b-m98unuwp4g] {
    position: relative;
}

/* ─────────── Trục thời gian ─────────── */
.dtg-truc[b-m98unuwp4g] {
    position: relative;
    height: 22px;
    margin-left: var(--dtg-cot-ten);
    border-bottom: 1px solid var(--dtg-vien);
}

.dtg-vach[b-m98unuwp4g] {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 0;
    border-left: 1px dashed var(--dtg-vien);
}

.dtg-vach-nhan[b-m98unuwp4g] {
    position: absolute;
    top: 0;
    left: 3px;
    font-size: .7rem;
    line-height: 1;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}

/* Kim HÔM NAY chạy suốt chiều cao dải — nó là thứ trả lời "đang ở đâu trên đường đi". */
.dtg-kim[b-m98unuwp4g] {
    position: absolute;
    top: 0;
    height: calc(100% + var(--dtg-cao-cua-san, 0px));
    width: 0;
    border-left: 2px solid var(--bs-danger);
    z-index: 3;
    pointer-events: none;
}

.dtg-kim-nhan[b-m98unuwp4g] {
    position: absolute;
    top: -2px;
    left: 4px;
    font-size: .68rem;
    line-height: 1.1;
    padding: 0 .3rem;
    border-radius: .5rem;
    color: var(--bs-danger-text-emphasis, var(--bs-danger));
    background: var(--bs-danger-bg-subtle, transparent);
    white-space: nowrap;
}

/* ─────────── Một dòng ─────────── */
.dtg-dong[b-m98unuwp4g] {
    display: grid;
    grid-template-columns: var(--dtg-cot-ten) 1fr;
    align-items: center;
    column-gap: .5rem;
    min-height: var(--dtg-cao-dong);
    transition: transform .28s ease, filter .28s ease;
}

.dtg-ten[b-m98unuwp4g] {
    display: flex;
    flex-direction: column;
    justify-content: center;
    font-size: .82rem;
    line-height: 1.15;
    overflow: hidden;
}

.dtg-ten > span[b-m98unuwp4g],
.dtg-ten-lien[b-m98unuwp4g] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dtg-ten-lien[b-m98unuwp4g] {
    color: var(--bs-link-color);
    text-decoration: none;
}

.dtg-ten-lien:hover[b-m98unuwp4g] {
    text-decoration: underline;
}

.dtg-phu-chu[b-m98unuwp4g] {
    font-size: .7rem;
    color: var(--bs-secondary-color);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Làn chạy của một dòng — nền mờ để thấy được phần dải KHÔNG thuộc dòng này. */
.dtg-lan[b-m98unuwp4g] {
    position: relative;
    height: 20px;
    border-radius: 10px;
    background: var(--dtg-nen-lan);
}

/* ─────────── Thanh 3D ─────────── */
.dtg-thanh[b-m98unuwp4g] {
    position: absolute;
    top: 1px;
    bottom: 1px;
    border-radius: 9px;
    overflow: hidden;
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--dtg-thanh-a) 88%, white 12%) 0%,
        var(--dtg-thanh-a) 46%,
        color-mix(in srgb, var(--dtg-thanh-a) 74%, black 26%) 100%);
    box-shadow:
        0 1px 0 color-mix(in srgb, white 45%, transparent) inset,
        0 -2px 4px color-mix(in srgb, black 28%, transparent) inset,
        0 4px 10px color-mix(in srgb, var(--dtg-thanh-a) 45%, transparent);
    /* Nở đều hai mép trên dưới (hiệu ứng nay là scaleY — xem chú thích ở keyframes dtg-moc-ra). */
    transform-origin: center center;
    animation: dtg-moc-ra-b-m98unuwp4g .7s cubic-bezier(.22, .8, .3, 1) both;
    animation-delay: var(--dtg-tre, 0s);
}

/* Bốn dòng đầu đổi tông dần để mắt tách được các dòng khi chúng gần nhau trên trục. */
.dtg-dong:nth-child(3n+1) .dtg-thanh[b-m98unuwp4g] { --dtg-thanh-a: var(--bs-primary); }
.dtg-dong:nth-child(3n+2) .dtg-thanh[b-m98unuwp4g] { --dtg-thanh-a: var(--bs-info); }
.dtg-dong:nth-child(3n) .dtg-thanh[b-m98unuwp4g] { --dtg-thanh-a: var(--bs-success); }

/* Vệt sáng chạy dọc thanh — phần "động" mà anh Tiến yêu cầu. Nó chạy CHẬM và mờ: vệt sáng gắt trên
   một màn hình đầy số liệu là thứ người ta phải nhìn đi chỗ khác để đọc được bảng bên cạnh. */
.dtg-loe[b-m98unuwp4g] {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 38%;
    left: -40%;
    background: linear-gradient(90deg,
        transparent 0%,
        color-mix(in srgb, white 38%, transparent) 50%,
        transparent 100%);
    animation: dtg-loe-chay-b-m98unuwp4g 3.4s ease-in-out infinite;
    animation-delay: var(--dtg-tre, 0s);
}

/* ─────────── Mốc (từng văn bản / từng việc) ─────────── */
.dtg-moc[b-m98unuwp4g] {
    position: absolute;
    top: 50%;
    width: 9px;
    height: 9px;
    margin-left: -4.5px;
    border-radius: 50%;
    background: var(--bs-body-bg);
    border: 2px solid var(--bs-body-color);
    transform: translateY(-50%);
    transition: transform .18s ease, box-shadow .18s ease;
    z-index: 2;
    cursor: help;
}

.dtg-moc:hover[b-m98unuwp4g] {
    transform: translateY(-50%) scale(1.75);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--bs-primary) 28%, transparent);
    z-index: 4;
}

/* ─────────── Dòng đang trỏ chuột ───────────
   ⭐ ĐỢT 1334 — anh Tiến bỏ nút "🧊 Nghiêng 3D" (26/08/2026), dải nay LUÔN PHẲNG.
   🔑 Chính chú thích của khối cũ đã tự nói ra lý do: "nghiêng thì mắt ước lượng vị trí ngang bị
      lệch". Mà anh yêu cầu cùng lượt là đo trục theo TỪNG NĂM — tức đọc vị trí chính xác hơn. Hai
      câu ấy chỉ đi được với nhau khi dải phẳng.
   ⚠ Hiệu ứng nổi lên khi trỏ chuột thì GIỮ: nó giúp tách đúng dòng đang xem khỏi các dòng sát nó. */
.dtg-dong:hover[b-m98unuwp4g] {
    transform: translateY(-2px);
    filter: drop-shadow(0 6px 12px color-mix(in srgb, black 26%, transparent));
    z-index: 5;
}

/* 🔴 KHUNG HÌNH ĐẦU PHẢI NHÌN THẤY ĐƯỢC — ⛔ ĐỪNG ĐƯA VỀ scaleX(0) hay opacity 0.
   Bản đầu của đợt 1323 cho thanh mọc từ scaleX(0); đo sống ngày 26/08/2026 trên chính trang
   /tong-quan-nhiem-vu thì cả hai thanh ra `transform: matrix(0,0,0,1,0,0)` — tức scaleX(0), THANH VÔ
   HÌNH — vì trình duyệt lúc ấy KHÔNG dựng khung hình nên đồng hồ animation đứng ở khung đầu (đúng
   bẫy đã ghi ở quy tắc #6). Tab chạy nền, chế độ tiết kiệm pin, hay một máy yếu đều dựng lại đúng
   cảnh ấy: dải trắng trơn mà build xanh, test xanh, ⛔ không câu lỗi nào.
   ⇒ Hiệu ứng chỉ được NỞ THEO CHIỀU DÀY và SÁNG DẦN; bề rộng — thứ mang thông tin — luôn đúng ngay
   từ khung hình đầu. Hàng rào `DaiThoiGianHieuUngTests` canh câu này. */
@keyframes dtg-moc-ra-b-m98unuwp4g {
    from { transform: scaleY(.45); opacity: .55; }
    to { transform: scaleY(1); opacity: 1; }
}

@keyframes dtg-loe-chay-b-m98unuwp4g {
    0% { left: -40%; }
    55% { left: 102%; }
    100% { left: 102%; }
}

/* Màn hình hẹp: cột tên thu lại, không thì làn chạy chỉ còn vài chục điểm ảnh và dải hết đọc được. */
/* ═══ ⭐ ĐỢT 1353 (việc o552) — CHẾ ĐỘ MỘT THANH DUY NHẤT ═══
   Anh Tiến 26/08/2026: "em làm 1 thanh timeline duy nhất, trên đó có các điểm chính ghi nhận mốc thời
   gian chính, đưa chuột vào điểm nào thì có chú thích cho điểm đó, nhấn vào chi tiết thì sẽ ra bảng
   timeline chi tiết như hiện tại".

   🔑 CỘT TÊN VỀ 0 Ở CHẾ ĐỘ GỌN — đây là cả mẹo của khối này. Trục và các dòng đều lùi vào bằng đúng
      biến --dtg-cot-ten, nên đặt lại MỘT biến là trục chạy hết bề ngang mà ⛔ không phải viết lại một
      bộ luật thứ hai cho trục. Chế độ gọn ⛔ không có tên dòng để mà chừa chỗ: thanh chỉ có một. */
.dtg-khung.dtg-che-do-gon[b-m98unuwp4g] {
    --dtg-cot-ten: 0px;
}

/* Dòng của thanh gọn ⛔ không có cột tên ⇒ một cột, chiếm trọn bề ngang. */
.dtg-dong-gon[b-m98unuwp4g] {
    grid-template-columns: 1fr;
    min-height: 44px;
}

/* Làn dày hơn làn thường: nó phải gánh các điểm chính to hơn chấm mốc, và nó là thứ DUY NHẤT trên
   màn hình ở chế độ này nên ⛔ không phải tranh chỗ với dòng nào. */
.dtg-lan-gon[b-m98unuwp4g] {
    height: 28px;
    border-radius: 14px;
}

/* ─────────── Điểm chính ───────────
   Kế thừa .dtg-moc (vị trí tuyệt đối, canh giữa dọc, hiệu ứng khi trỏ chuột) rồi phóng to lên: chấm
   9px là cỡ hợp với dải 135 mốc, còn ở đây chỉ có hơn chục điểm và mỗi điểm phải MỜI được người ta rê
   chuột vào. */
.dtg-diem[b-m98unuwp4g] {
    width: 15px;
    height: 15px;
    margin-left: -7.5px;
    border-width: 3px;
    border-color: var(--bs-primary);
    box-shadow: 0 1px 4px color-mix(in srgb, black 30%, transparent);
}

/* Điểm GOM NHIỀU MỐC phải nhìn ra ngay là cụm — ⛔ không thì người xem đọc nó thành một tờ giấy duy
   nhất, tức đánh rơi các mốc còn lại đúng chỗ mình vừa cứu chúng. Con số in trong chấm nói thẳng
   ra có mấy tờ; chú thích khi rê chuột liệt kê đủ tên. */
.dtg-diem-cum[b-m98unuwp4g] {
    width: 20px;
    height: 20px;
    margin-left: -10px;
    border-color: var(--bs-warning);
    background: var(--bs-warning-bg-subtle, var(--bs-body-bg));
}

/* ⚠ ⛔ KHÔNG hạ xuống dưới .65rem (10,4px) — hàng rào CoChuONgayTests bắt ĐỎ, và nó bắt ĐÚNG: bản đầu
   của đợt 1353 gõ .6rem ở đây và bị chặn ngay lượt chạy đầu. Con số in trong chấm cụm là thứ nói ra
   "chấm này đang thay mặt mấy tờ giấy" — in nhỏ hơn ngưỡng đọc được thì thà ⛔ đừng in. Chấm cụm vì
   thế rộng 20px chứ ⛔ không phải 15px như điểm thường: phải đủ chỗ cho hai chữ số ở cỡ này. */
.dtg-diem-so[b-m98unuwp4g] {
    display: block;
    font-size: .65rem;
    line-height: 14px;
    font-weight: 600;
    text-align: center;
    color: var(--bs-body-color);
}

@media (max-width: 767.98px) {
    .dtg-khung[b-m98unuwp4g] { --dtg-cot-ten: 120px; }
    /* ⚠ Chế độ gọn giữ cột tên bằng 0 ở MỌI khổ màn hình — luật trên nằm sau luật gốc nên nó thắng,
       nhưng khối @media này lại đứng SAU nữa. ⇒ Phải nhắc lại, ⛔ không thì màn hẹp tự dựng lại một
       khoảng trống 120px ở đầu thanh mà ⛔ không có gì trong đó. */
    .dtg-khung.dtg-che-do-gon[b-m98unuwp4g] { --dtg-cot-ten: 0px; }
    /* ⚠ ⛔ KHÔNG hạ xuống dưới .65rem (10,4px): hàng rào CoChuONgayTests bắt ĐỎ, và nó bắt đúng —
       nhãn trục nhỏ hơn ngưỡng ấy là nhãn ⛔ không đọc nổi trên đúng khổ màn hình chật nhất. */
    .dtg-vach-nhan[b-m98unuwp4g] { font-size: .65rem; }
}

@media (prefers-reduced-motion: reduce) {
    .dtg-thanh[b-m98unuwp4g] { animation: none; }
    .dtg-loe[b-m98unuwp4g] { animation: none; display: none; }
    .dtg-dong[b-m98unuwp4g] { transition: none; }
    .dtg-moc[b-m98unuwp4g] { transition: none; }
    /* ⭐ ĐỢT 1353 — điểm chính dùng lại .dtg-moc nên nó đã được tắt ở dòng trên; nhắc lại tường minh
       để lượt sửa sau đổi .dtg-diem sang một hiệu ứng riêng thì ⛔ không quên chỗ này. */
    .dtg-diem[b-m98unuwp4g] { transition: none; }
}

/* ─────────── ⭐ ĐỢT 1628 (việc o918) — Ô CHỌN NĂM + DẤU THANH BỊ CẮT Ở BIÊN NĂM ───────────
   Anh Tiến 01/09/2026: trục bó về MỘT năm, chia đủ 12 tháng, có ô chọn năm để xem lại năm cũ. */
.dtg-chon-nam[b-m98unuwp4g] {
    width: auto;
    min-width: 5rem;
    font-size: .78rem;
}

/* ⚠ Ngưỡng vùng chạm đọc từ BIẾN dùng chung, ⛔ đừng gõ số 44px vào đây (luật đợt 1518/1521): ô chọn
   nằm trong tệp CSS cách ly nên nó thắng luật của app.css, tức nếu ⛔ không nhắc lại thì đúng ô này
   tụt xuống dưới ngưỡng ngón tay mà ⛔ không hàng rào nào của app.css với tới. */
@media (pointer: coarse) {
    .dtg-chon-nam[b-m98unuwp4g] { min-height: var(--cham-toi-thieu, 44px); }
}

/* Dòng chạy vượt ra ngoài năm đang xem thì thanh BỊ CẮT — mép cắt vuông góc và có vệt răng cưa, để
   mắt phân biệt được với một dòng thật sự khởi động đúng 01/01 hoặc kết thúc đúng 31/12.
   🔑 Đây là phần HÌNH của cùng một sự thật mà câu chú giải đang nói bằng chữ; bỏ một trong hai là để
      lại một tấm hình nói dối cho người ⛔ không rê chuột vào. */
.dtg-thanh-noi-truoc[b-m98unuwp4g] {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

.dtg-thanh-noi-sau[b-m98unuwp4g] {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

.dtg-thanh-noi-truoc[b-m98unuwp4g]::before,
.dtg-thanh-noi-sau[b-m98unuwp4g]::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    width: 8px;
    background: repeating-linear-gradient(135deg,
        color-mix(in srgb, white 72%, transparent) 0 2px,
        transparent 2px 4px);
    pointer-events: none;
}

.dtg-thanh-noi-truoc[b-m98unuwp4g]::before { left: 0; }
.dtg-thanh-noi-sau[b-m98unuwp4g]::after { right: 0; }

/* ─────────── ⭐ ĐỢT 1979 (việc o1261) — SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY ───────────
   📏 Đo sống 04/09/2026 trên Chrome thật, khung 390×844, chế độ cảm ứng, đăng nhập `tienvm`
      (`node cong-cu/do-song-khung-hep.mjs --trang /tong-quan-nhiem-vu`): dải thời gian đóng góp
      **13/13** chỗ chữ nhỏ của cả trang — `.dtg-vach-nhan` **10,4px × 12 lượt** (luật `.65rem`
      trong `@media (max-width: 767.98px)` ngay trên) và `.dtg-kim-nhan` **10,9px × 1 lượt**.
      Sàn của chính dự án là `--cham-chu-toi-thieu` = **12px** (app.css) — dưới mức ấy người 45
      tuổi trở lên phải đưa máy lại gần mới đọc được.

   🔑 VÌ SAO ĐẶT Ở `pointer: coarse` CHỨ ⛔ KHÔNG SỬA THẲNG HAI LUẬT TRÊN: hai luật ấy đang phục
      vụ **màn hình hẹp của MÁY TÍNH** nữa (`max-width: 767.98px` bắt cả cửa sổ trình duyệt thu
      nhỏ), mà ở đó con trỏ là chuột — chữ .65rem đọc bằng chuột thì vẫn ổn và nhãn trục càng nhỏ
      càng đỡ chồng lên nhau. Nâng chung cả hai là đổi bố cục của người ⛔ không hề cầm điện thoại.
      ⇒ Chốt ngược của đợt này là `--chuot` (1369×900, ⛔ không cảm ứng) phải ra **trùng khít**.

   ⚠ `max()` chứ ⛔ KHÔNG gõ thẳng 12px: giữ nguyên ý đồ tỉ lệ cũ, chỉ **nâng sàn**. Ngày nào đổi
      `--cham-chu-toi-thieu` thì chỗ này đi theo, ⛔ không phải đi sửa lại từng tệp.
   ⚠ ⛔ KHÔNG mâu thuẫn với lời dặn *"⛔ không hạ xuống dưới .65rem"* ở khối `@media` trên: lời dặn
      ấy chặn chiều **XUỐNG** (hàng rào `CoChuONgayTests`), còn đây là chiều **LÊN**. */
@media (pointer: coarse) {
    .dtg-vach-nhan[b-m98unuwp4g] { font-size: max(.7rem, var(--cham-chu-toi-thieu)); }
    .dtg-kim-nhan[b-m98unuwp4g] { font-size: max(.68rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/ChungTu/HopChiTietMucTieu.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 1597 (việc o889) — HỘP CHI TIẾT MỤC TIÊU ═══ */

/* Cột nhãn rộng cố định để hai cột thẳng hàng nhau — ⛔ đừng để nhãn tự co: nhãn "Kỳ" và
   "Nhiệm vụ đã link" chênh nhau quá nhiều, giá trị sẽ so le mỗi dòng một chỗ. */
.hcm-nhan[b-qgr0pt5ht2] {
    flex: 0 0 8.5rem;
    max-width: 8.5rem;
}

/* Khổ điện thoại: xếp chồng, vì 8,5rem cho nhãn là gần nửa bề ngang máy. */
@media (max-width: 420px) {
    .hcm-nhan[b-qgr0pt5ht2] {
        flex-basis: 6.5rem;
        max-width: 6.5rem;
    }
}
/* /Components/ChungTu/HopThoai.razor.rz.scp.css */
/* ═══ KHUNG HỘP THOẠI DÙNG CHUNG — VÙNG CHẠM CHO NGÓN TAY (đợt 1828, việc o1047) ═══

   Tệp này sinh ra CHỈ để nới vùng chạm của nút ✕. Nó cố ý KHÔNG đẻ lớp mới nào và KHÔNG đặt
   màu nào — mọi hình thức của hộp thoại vẫn do Bootstrap và bộ token của app quyết định, nên
   hộp tự đúng ở cả giao diện sáng lẫn giao diện tối.

   ── VÌ SAO LUẬT PHẢI NẰM Ở ĐÂY, KHÔNG NƠI NÀO KHÁC ───────────────────────────────────────
   ① KHÔNG vá được từ tệp CSS của trang gọi. CSS cách ly áp theo COMPONENT: luật viết ở
      KhoiHoSoYTe.razor.css biên dịch thành .btn-close[b-hoso] và KHÔNG BAO GIỜ khớp nút ✕
      nằm trong HopThoai.razor — nút ấy mang dấu scope của chính khung này.
   ② KHÔNG vá được từ khối chạm dùng chung ở wwwroot/app.css. Khối ấy nới .btn, mà .btn-close
      là lớp RIÊNG của Bootstrap và KHÔNG mang lớp .btn ⇒ nó đứng ngoài tầm với. Mà kể cả có
      viết .btn-close ở app.css thì bộ chọn ấy chỉ (0,1,0), thua .btn-close[b-…] (0,2,0) của
      chính tệp này — một luật KHÔNG BAO GIỜ CHẠY, mà build xanh và test xanh.
   ⇒ Đây là đúng một chỗ sửa được, và cũng vì thế nó chạm MỌI hộp thoại của app: đo 03/09/2026
     có 64 tệp .razor dựng <HopThoai>.

   ── SỐ ĐO TRƯỚC KHI VÁ (Chrome thật, hộp thoại THẬT mở bằng cú bấm thật) ──────────────────
   Trang /hanh-chinh/cap-phat-vat-tu, nút "Thêm vật tư":
       ngón tay 390×844 cảm ứng … 32 × 32 px   ⇒ DƯỚI ngưỡng --cham-toi-thieu (44px)
       chuột    1400×900        … 32 × 32 px
   Cả hai đều box-sizing: content-box, padding 8px — tức 1em nội dung (16px) cộng hai lần
   0,25em đệm. Dòng việc o1047 ghi 32×30 vì đo ở hộp thoại khác của app Y tế; con số nhỏ hơn
   ngưỡng ở cả hai lượt đo nên kết luận không đổi.

   ── HAI DÒNG DƯỚI ĐÂY ĐI LIỀN MỘT CẶP, ĐỪNG BỎ DÒNG NÀO ──────────────────────────────────
   Bootstrap khai .btn-close là box-sizing: content-box. Giữ nguyên nó rồi đặt min-width 44px
   thì 44px ấy là cỡ của phần NỘI DUNG, cộng thêm 16px đệm ⇒ nút ra 60 × 60 px: to gấp đôi thứ
   cần, và nó đội cao cả thanh tiêu đề của mọi hộp thoại. Phải đổi sang border-box thì 44px mới
   là cỡ NGOÀI của nút.
   Hình chữ ✕ KHÔNG to lên theo: Bootstrap vẽ nó bằng background cỡ 1em căn giữa, nên nút rộng
   ra thì hình vẫn 16px và vẫn nằm giữa. Đó chính là điều muốn có — nới VÙNG BẤM, giữ nguyên
   thứ mắt nhìn thấy.

   ⚠ Ngưỡng đọc từ biến --cham-toi-thieu khai ở wwwroot/app.css. ĐỪNG gõ thẳng số 44px vào đây:
     ngưỡng phải nằm ở đúng MỘT chỗ, rải số ra từng tệp là mở đường cho mỗi tệp lệch một kiểu.

   ⚠ Luật nằm trong câu media (pointer: coarse), MỘT điều kiện, KHÔNG dùng max-width. Khổ hẹp
     KHÔNG có nghĩa là ngón tay (cửa sổ thu nhỏ vẫn dùng chuột) và màn rộng vẫn có thể là cảm
     ứng ⇒ điều kiện phải là cái CHẠM VÀO. Chốt ngược: chế độ chuột phải ra đúng 32 × 32 px như
     trước, không lệch một pixel.

   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo. Cặp ký tự đó lọt vào GIỮA câu văn là chú
     thích đóng sớm, phần chữ còn lại dính vào bộ chọn của luật ngay dưới và trình duyệt bỏ
     nguyên luật đó — không một câu lỗi nào. Vì thế ở đây gọi tên cặp ký tự ấy bằng CHỮ.

   🚧 Hàng rào canh: TKNB.Tests/VungChamNgonTayTests.cs (tệp này đã khai trong sổ của nó).
   📏 Đo lại: bản đo một-ô của đợt 1828, hoặc node cong-cu/do-song-khung-hep.mjs --chuot */

@media (pointer: coarse) {

    /* Nút ✕ đóng hộp thoại — nút duy nhất của khung này, và là nút bấm nhiều nhất trong app. */
    .btn-close[b-azuomk6aav] {
        box-sizing: border-box;
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
    }
}
/* /Components/ChungTu/HopThoaiTaoNhiemVu.razor.rz.scp.css */
/* ═══ HỘP TẠO NHIỆM VỤ — ĐỢT 1635 (việc o928) ═══
   Anh Tiến 01/09/2026, kèm ảnh chụp đúng khối "Thuộc dự án / sửa chữa": *"gom 2 ô này làm 1"*.
   Trước lượt này ô lọc 🔎 và ô chọn hồ sơ nằm HAI HÀNG chồng lên nhau, ăn mất một hàng dọc của hộp
   thoại mà ⛔ không nói thêm được điều gì. Nay cả hai nằm chung một `input-group`: ô lọc GIÃN, ô chọn
   CO bên cạnh.

   🔴 Ô chọn BẮT BUỘC khai đủ `flex` + `width` + `min-width`, ⛔ ĐỪNG chỉ gắn `flex-grow-0` ở markup
     rồi thôi. Bootstrap khai cho ô chọn nằm trong dải nhập: `flex: 1 1 auto; width: 1%; min-width: 0`.
     Ba giá trị ấy đi CÙNG NHAU mới có nghĩa — `width: 1%` chỉ là một CHỖ NEO cho `flex-grow: 1` kéo
     ra. Bỏ mỗi vế kéo ra mà để nguyên hai vế kia là ô tụt còn bằng cái mũi tên, đúng lỗi anh Tiến đã
     báo một lần ở đợt 1448.
   ⚠ Có TRẦN: bề rộng tự nhiên của một thẻ select là bề rộng DÒNG DÀI NHẤT trong danh sách, mà tên hồ
     sơ dự án thì dài — thiếu trần là ô chọn nuốt sạch hàng và ô lọc còn vài ký tự.
   ⚠ Có SÀN: dòng mặc định "— Nhiệm vụ độc lập… —" ngắn hơn tên hồ sơ thật, thiếu sàn thì ô teo lại
     đúng lúc người ta cần đọc mình đang gắn nhiệm vụ vào hồ sơ nào.
   ⓘ Thẻ select ở đây là thẻ HTML do CHÍNH tệp `.razor` này viết ra nên nó mang dấu phạm vi, ⛔ không
     cần `::deep`. Bên `GiaoViec.razor.css` thì NGƯỢC LẠI — ô chọn bên ấy do component `InputSelect`
     dựng ra, xem chú thích tại chỗ.
   👆 ⛔ Đừng ép cả hàng vào một dòng cứng (`flex-nowrap`): `.input-group` tự xuống dòng khi hẹp, và đó
     là đường lui duy nhất cho khổ điện thoại (luật vùng chạm đợt 1518). */
.htn-o-ho-so[b-7xn8mzvrgu] {
    width: auto;
    flex: none;
    min-width: 12rem;
    max-width: 22rem;
}

/* Ô lọc là thứ giãn ra chiếm phần còn lại của hàng. Đặt SÀN để khổ hẹp thì cả hàng XUỐNG DÒNG nguyên
   khối thay vì ô lọc co còn vài ký tự — trong dải nhập, thẻ `form-control` mang sẵn `min-width: 0`
   của Bootstrap nên ⛔ không có sàn thì nó co tới gần bằng không TRƯỚC khi hàng chịu xuống dòng.
   ⚠ Sàn CỐ Ý thấp: đây là ô gõ VÀI CHỮ để lọc, ⛔ không phải ô gõ cả câu. */
.htn-hang-ho-so .form-control[b-7xn8mzvrgu] {
    min-width: 10rem;
}
/* /Components/ChungTu/KhoiCayNhiemVu.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 2228 (việc o1663) — KHUNG NGOÀI CỦA CÂY NHIỆM VỤ ═══

   🔑 Chỉ lo THẺ BỌC tầng đầu; mọi luật vẽ một nhánh nằm ở `NhanhCayNhiemVu.razor.css` — ⛔ đừng chép
      luật thụt lề sang đây, hai bản sẽ lệch nhau lúc nào ⛔ không ai biết.

   ⚠ CSS scoped ⛔ KHÔNG xuyên qua ranh giới component: lớp `.ncn-*` của từng nhánh ⛔ không sửa được
     từ tệp này (đúng chủ ý — tránh sửa từ xa một component dùng chung). */

.ckn-goc[b-s8wf4pgc1n] {
    list-style: none;
    margin: 0;
    padding-left: 0;
}
/* /Components/ChungTu/KhoiChiTietHopDong.razor.rz.scp.css */
/* ═══ VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (đợt 1605, việc o737) ═══
   ô tick TRẦN “nghiệm thu bàn giao” ở hàng khai đợt nghiệm thu.

   🔑 Nới VÙNG CHẠM QUANH ô tick, ⛔ KHÔNG phóng to chính ô vuông: ô tick 44px đứng cạnh dòng chữ
     15px trông như lỗi phần mềm, và nó cũng ⛔ không giải đúng bài — thứ ngón tay bấm trượt là
     khoảng trống quanh ô. Thẻ bọc `label.o-tick-cham` khai ngay trong `.razor`.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là ngón
     tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ
     quyết định vùng bấm là CÁI CHẠM VÀO, ⛔ không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, ⛔ không một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css`: bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi: node cong-cu/soi-o-tick-tran.mjs · node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ⚠ Cần CẢ `min-width`: ô tick chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt NGANG.
       ⚠ `inline-flex` chứ ⛔ không `flex`: thẻ bọc nằm trong một ô bảng (hoặc một hàng `d-flex`)
         cạnh nút và ô gõ khác — hoá `flex` là nó chiếm trọn một dòng, hàng đang gọn bỗng vỡ ra.
       ⚠ `min-height` chứ ⛔ không `height`: hàng nào cao hơn thì cứ để cao — đặt `height` là bóp
         chúng lại, sửa một lỗi rồi đẻ một lỗi. */
    .o-tick-cham[b-askczqhssy] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/ChungTu/KhoiSinhNhat.razor.rz.scp.css */
/* ═══ Ô SINH NHẬT TRÊN TRANG CHỦ (đợt 1382 — việc o578 · vẽ lại ở đợt 1600 — việc o901) ═══

   ⚠ Mọi luật dưới đây khai CẢ NỀN LẪN MÀU CHỮ thành cặp. ⛔ Đừng khai một vế: khai màu chữ mà quên
     nền (hoặc ngược lại) là chữ trắng trên nền trắng ở một trong hai giao diện — hỏng lặng lẽ, chỉ
     người dùng giao diện kia mới thấy.
   ⚠ Hai trục giao diện của app là `data-theme` (sáng/tối) và `data-bo-mau` (bộ màu). Dải này CỐ Ý
     ⛔ không đi theo bộ màu: nó là một lời chúc, màu ấm vàng–hồng đọc đúng nghĩa ở mọi bộ màu, y như
     dải ô số liệu quanh nó cũng giữ dải màu riêng. Chỉ trục SÁNG/TỐI mới đổi bảng màu. */

/* ─── ĐỢT 1622 (việc o917): DẢI MỘT HÀNG, CÓ THANH NHẤN DỌC ──────────────────────────────────
   ⭐ Anh Tiến 01/09/2026 gửi ảnh chụp chính dải này: "em xem lại cái này, đang xấu cần đồ họa lại
     kiểu khác".
   📏 Đo bản cũ (cổng 5290, khung 1500px, 22 người): 1352 × 214 px, 88 phần tử DOM.

   BA TẬT VÀ CÁCH CHỮA TỪNG CÁI:
     ① Bo tròn 999px trên khối cao BỐN HÀNG. Dáng viên thuốc chỉ đọc đúng khi cao một dòng; cao bốn
        hàng thì bốn góc bo thành một cục méo. ⇒ Dải nay CAO ĐÚNG MỘT HÀNG (C# đã kẹp số huy hiệu ở
        LuatDaiSinhNhatGon.TranChipTrenDai), và bo góc về 10px cho cùng giọng với thẻ của app.
     ② Nền chuyển sắc cam sang hồng chói, đứng ngay trên dải nút xanh và bốn ô số liệu nên lạc giọng.
        ⇒ Nền nay là một sắc ấm RẤT nhạt, dấu hiệu nhận biết dồn vào THANH NHẤN DỌC bên trái —
        cùng ngôn ngữ với các dải thông tin khác của app, ⛔ không còn là một mảng kẹo.
     ③ Tật nặng nhất: 22 huy hiệu ngang sức nặng ⇒ người sinh nhật HÔM NAY chìm mất. Chữa ở TẦNG
        LUẬT chứ ⛔ không ở CSS — xem `Domain/LuatDaiSinhNhatGon.cs`.

   🔴 THỨ GIỮ DẢI THẤP LÀ PHÉP KẸP SỐ HUY HIỆU Ở TẦNG LUẬT, ⛔ KHÔNG PHẢI LỆNH CẤM XUỐNG DÒNG.
     Bản thảo đầu của chính đợt này viết `flex-wrap: nowrap` cho chắc — và ĐO SỐNG BÁC BỎ NGAY ở khổ
     820px: bốn huy hiệu cùng co lại, mà phần co được duy nhất là TÊN NGƯỜI (chip phòng và ngày đều
     `nowrap`) ⇒ màn hình còn trơ "B…", "H…" cạnh một chip phòng và một ngày. Tức phép cắt chữ ăn
     đúng thứ tính năng này sinh ra để bày. ⇒ Cho phép xuống dòng, và ⛔ CẤM huy hiệu CO LẠI
     (`flex: 0 0 auto` ở `.sn-nguoi`). Xấu nhất là dải thành HAI hàng ở khổ hẹp — vẫn còn xa con số
     214px của bản cũ, và mọi cái tên đọc được trọn.
   ⛔ ĐỪNG đặt lại `width: max-content` của đợt 1600: dải nay có nút "+N" neo bên phải, cần trọn
     bề ngang để nút ấy đứng đúng chỗ mắt tìm nó. */
/* 🔴 BẢNG MÀU KHAI CHO CẢ HAI NƠI MỌC HUY HIỆU — ⛔ ĐỪNG GOM VỀ RIÊNG `.sn-dai`.
   Từ đợt 1622 huy hiệu mọc ở HAI chỗ: trên dải, và trong HỘP THOẠI. Hộp thoại dựng ở một nhánh DOM
   KHÁC (khung `HopThoai`) nên nó ⛔ KHÔNG nằm trong `.sn-dai` ⇒ mọi biến khai riêng ở đó là biến
   ⛔ KHÔNG phân giải được khi huy hiệu nằm trong hộp. Mà biến không phân giải được thì trình duyệt
   VỨT nguyên khai báo — huy hiệu trong hộp mất sạch nền, viền và màu chữ, ⛔ KHÔNG một câu lỗi nào:
   build xanh, test xanh, dải trên trang vẫn đẹp (bài học `--bs-border-color` đợt 831).
   ⚠ Thêm chỗ mọc huy hiệu THỨ BA thì thêm bộ chọn vào ĐÚNG HAI khối này (bảng sáng + bảng tối). */
.sn-dai[b-u4z18iur9f],
.sn-nhom[b-u4z18iur9f] {
    --sn-nen: #fffaf1;
    --sn-vach: #e0a13a;
    --sn-vien: rgba(190, 132, 30, .26);
    --sn-chu: #6b3d09;
    --sn-chu-nhat: #8a5a12;
    --sn-nhan-mau: #8a5a12;
    --sn-chip-nen: #fff;
    --sn-chip-vien: rgba(190, 132, 30, .26);
    --sn-phong-nen: rgba(190, 132, 30, .14);
    --sn-phong-chu: #6b3d09;
    --sn-mo-nen: rgba(120, 110, 95, .08);
    --sn-mo-chu: #5f5548;
    --sn-vach-day: 3px;
    --sn-nen-di-chuot: #fff2dc;

    /* Hai biến THÊM 20/09/2026 cho lượt làm ấm dải: ánh sáng dịu quét chéo mặt nền, và bóng đổ rất
       nhẹ để dải rời khỏi mặt trang. ⚠ PHẢI khai đủ ở CẢ hai bảng màu (sáng + tối) — thiếu một bảng
       là biến ⛔ không phân giải được, trình duyệt VỨT nguyên khai báo và dải mất nền ở đúng một
       giao diện, ⛔ không một câu lỗi nào (bài học `--bs-border-color` đợt 831). */
    --sn-anh-sang: rgba(255, 255, 255, .65);
    --sn-bong: rgba(0, 0, 0, .06);
    --sn-bong-day: rgba(0, 0, 0, .10);
    --sn-chip-bong-tren: rgba(255, 255, 255, .55);
}

/* ─── ĐỢT 1633 (việc o925): CẢ DẢI LÀ MỘT `<button>` ──────────────────────────
   ⭐ Anh Tiến 01/09/2026: *"chỉ cần nhấn vào ô này là mở hộp hội thoại, biến ô này thành nút"*.
   🔴 THẺ ĐỔI TỪ `<div>` SANG `<button>` THÌ PHẢI TRẢ LẠI BẰNG TAY BA THỨ MÀ NÚT ⛔ KHÔNG KẾ THỪA:
   phông chữ (`font: inherit`) · căn chữ (`text-align: start`) · màu chữ (`color` đã khai sẵn bên dưới).
   Trình duyệt cấp cho `<button>` một bộ phông RIÊNG của hệ điều hành và căn GIỮA — thiếu hai dòng
   ấy là cả dải đổi phông, đổi cỡ và dồn vào giữa, mà build vẫn xanh và test vẫn xanh (hỏng LẶNG LẼ).
   ⚠ `.sn-nhan` đang tự khai `font-size`/`font-weight` nên nó ⛔ không cứu được chỗ này: cái bị trả về
   phông mặc định là THẺ BỌC và mọi chữ bên trong chưa tự khai (chợ "+N", tên người trong huy hiệu). */
.sn-dai[b-u4z18iur9f] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem .5rem;
    width: 100%;
    min-width: 0;
    margin-bottom: .75rem;
    padding: .3rem .55rem;
    border: 1px solid var(--sn-vien);
    border-inline-start: var(--sn-vach-day) solid var(--sn-vach);
    border-radius: 10px;
    /* 🩸 KHAI `background-color` RIÊNG, ⛔ KHÔNG dùng lối viết gộp `background`: lối gộp DỌN SẠCH
       `background-image` ⇒ mỗi luật nào đó đổi màu nền (di chuột chẳng hạn) là ánh sáng quét chéo
       biến mất, mà biến mất LẶNG LẼ. Vì vậy luật `:hover` bên dưới cũng khai `background-color`. */
    background-color: var(--sn-nen);
    background-image: linear-gradient(100deg, var(--sn-anh-sang, transparent) 0%, rgba(255, 255, 255, 0) 58%);
    color: var(--sn-chu);
    box-shadow: 0 1px 2px var(--sn-bong, transparent);
    font: inherit;
    text-align: start;
    cursor: pointer;
    transition: background-color .18s ease, box-shadow .18s ease, transform .18s ease;
    animation: sn-hien-b-u4z18iur9f .36s ease-out both;
}

/* Dải hiện ra bằng một lượt MỜ DẦN RỒI RÕ, ⛔ không nhún nhảy: nó đứng ngay cạnh dải nút bàn làm
   việc, một khối nhảy chỗ ở đó là mắt người dùng bị kéo khỏi việc họ đang định bấm.
   🔑 CỐ Ý chỉ chạm `opacity` — ⛔ ĐỪNG đụng `transform`: hoạt hoạ có `transform` và chế độ `both` sẽ ĐÈ
      LÊN luật nhấc nhẹ lúc di chuột ngay bên dưới, và nó đè LẶNG LẼ. */
@keyframes sn-hien-b-u4z18iur9f {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* Di chuột ⇒ nền đậm thêm một nấc. ⛔ Đừng đổi `border-color`: viền trái của dải là THANH NHẤN
   mang nghĩa "hôm nay có người" (đợt 1622) — tô đè cả bốn cạnh là nuốt mất đúng tín hiệu ấy.
   ⚠ Màu lấy từ BIẾN, khai đủ ở CẢ BỐN bảng màu (sáng · sáng-mừng · tối · tối-mừng): thiếu một bảng
   là biến ⛔ không phân giải được ⇒ trình duyệt VỨT nguyên khai báo ⇒ dải mất nền lúc di chuột, ⛔ không
   một câu lỗi nào (bài học `--bs-border-color` đợt 831). */
.sn-dai:hover[b-u4z18iur9f] {
    background-color: var(--sn-nen-di-chuot, var(--sn-nen));
    box-shadow: 0 .25rem .55rem var(--sn-bong-day, transparent);
    transform: translateY(-1px);
}

/* Lối bàn phím. Dùng `:focus-visible` chứ ⛔ không `:focus`: bấm chuột xong cũng là focus, mà vẽ một
   vòng viền sau mỗi cú bấm chuột là nhiễu. */
.sn-dai:focus-visible[b-u4z18iur9f] {
    outline: 2px solid var(--sn-vach);
    outline-offset: 2px;
}

/* Đầu dải: biểu tượng + nhãn. ⛔ Không co lại — mất nhãn là dải hoá một hàng chip ⛔ không rõ nghĩa. */
.sn-dau[b-u4z18iur9f] {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    flex: 0 0 auto;
}

/* 🎂 Chiếc bánh nghiêng nhẹ và to lên một nấc khi con trỏ đậu trên dải — một lời chào, ⛔ không phải
   một màn hoạt hình: nó CHỈ động lúc có người thật sự trỏ vào, còn lại đứng yên tuyệt đối.
   🔑 `transform` ăn được vào đây vì `.sn-icon` là con của `.sn-dau` (`inline-flex`) nên nó đã thành
      phần tử khối; một `<span>` trôi trong dòng chữ thì `transform` ⛔ KHÔNG có tác dụng gì. */
.sn-icon[b-u4z18iur9f] {
    font-size: 1rem;
    line-height: 1;
    transition: transform .18s ease;
}

.sn-dai:hover .sn-icon[b-u4z18iur9f],
.sn-dai:focus-visible .sn-icon[b-u4z18iur9f] {
    transform: scale(1.14) rotate(-8deg);
}

/* Nhãn đầu dải — chuỗi do LuatDaiSinhNhatGon.NhanDai dựng, ⛔ không gõ chữ thẳng ở đây. */
.sn-nhan[b-u4z18iur9f] {
    font-size: .82rem;
    font-weight: 600;
    white-space: nowrap;
    color: var(--sn-nhan-mau, var(--sn-chu));
}

/* Ruột dải — nơi các huy hiệu đứng. Xuống dòng được, nhưng từng huy hiệu thì ⛔ KHÔNG co
   (xem điều cấm ở khối chú thích đầu tệp: co huy hiệu là ăn mất TÊN NGƯỜI). */
.sn-ruot[b-u4z18iur9f] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
    flex: 1 1 auto;
    min-width: 0;
}

/* Nhãn "+N" — neo bên PHẢI, ⛔ không co. Mắt tìm "còn nữa" ở cuối dải, ⛔ không phải giữa dải.
   ⭐ ĐỢT 1633 — NÓ ĐÃ THÔI LÀ MỘT `<button>` (HTML cấm nút lồng trong nút; cả dải nay mới là nút).
   ⇒ ⛔ BỎ `cursor: pointer` riêng — cả dải đã mang con trỏ bàn tay, để riêng ở đây là nói dối rằng
     "chỉ chỗ này bấm được", đúng hiểu nhầm đợt này đi dẹp.
   ⇒ Hiệu ứng di chuột bám vào `.sn-dai:hover` chứ ⛔ không vào `.sn-them:hover`: đổi nét viền chỉ khi
     chuột đậu ĐÚNG trên chữ "+N" là chia một cái nút thành hai vùng phản ứng khác nhau. */
.sn-them[b-u4z18iur9f] {
    flex: 0 0 auto;
    margin-inline-start: auto;
    padding: .08rem .5rem;
    border: 1px dashed var(--sn-chip-vien);
    border-radius: 999px;
    background: transparent;
    color: var(--sn-chu-nhat);
    font-size: .78rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.sn-dai:hover .sn-them[b-u4z18iur9f],
.sn-dai:focus-visible .sn-them[b-u4z18iur9f] {
    border-style: solid;
    background: var(--sn-phong-nen);
    color: var(--sn-chu);
}

/* ─── HỘP THOẠI BÀY TRỌN (đợt 1622) ──────────────────────────────────────────────────────────
   Ở đây thì ⛔ KHÔNG kẹp gì cả: hộp mở ra chính là để xem đủ. Ba nhóm xếp dọc, mỗi nhóm một nhãn
   riêng trên hàng của nó — bố cục mà đợt 1612 đã cố ép vào một dải và ⛔ không vừa. */
.sn-nhom + .sn-nhom[b-u4z18iur9f] {
    margin-top: .85rem;
}

.sn-nhan-hop[b-u4z18iur9f] {
    margin-bottom: .35rem;
    font-size: .85rem;
    font-weight: 600;
    color: var(--sn-nhan-mau, #6b3d09);
}

.sn-hang[b-u4z18iur9f] {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem .35rem;
}

/* Một người — huy hiệu nền sáng nổi trên dải, để mắt bắt được TÊN trước tiên. */
.sn-nguoi[b-u4z18iur9f] {
    display: inline-flex;
    align-items: baseline;
    gap: .3rem;
    /* ⛔ ĐỢT 1622 — HUY HIỆU KHÔNG ĐƯỢC CO. Phần co được duy nhất bên trong là TÊN NGƯỜI, nên cho
       co là khổ hẹp ăn mất đúng cái tên (đo sống 01/09 ở khổ 820px: màn hình còn trơ "B…", "H…").
       `max-width` vẫn giữ để MỘT cái tên dài bất thường ⛔ không đẩy ngang cả trang. */
    flex: 0 0 auto;
    max-width: 100%;
    min-width: 0;
    padding: .1rem .5rem .14rem;
    border: 1px solid var(--sn-chip-vien);
    border-radius: 999px;
    background: var(--sn-chip-nen);
    color: var(--sn-chu);
    font-size: .84rem;
    font-weight: 500;
}

.sn-ten[b-u4z18iur9f] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Ngày–tháng in nhạt hơn tên người: tên mới là thứ mắt cần bắt trước.
   ⛔ Làm nhạt bằng MÀU RIÊNG chứ ⛔ không bằng `opacity`: opacity kéo cả chữ lẫn nền huy hiệu xuống
   nên tương phản tụt theo, mà số đo thì ⛔ không ai nhìn ra bằng mắt. */
.sn-ngay[b-u4z18iur9f] {
    color: var(--sn-chu-nhat);
    font-size: .75rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ⭐ ĐỢT 1612 — TÊN PHÒNG VIẾT TẮT (anh Tiến giao 31/08/2026).
   Chữ ĐỨNG GIỮA tên người và ngày: mắt đọc "ai — ở đâu — ngày nào" theo đúng thứ tự ấy.
   ⛔ Đừng làm nhạt bằng `opacity` (kéo cả chữ lẫn nền huy hiệu xuống, tương phản tụt mà mắt không
   thấy) — đây là một chip nền riêng, đo được. Chuỗi do `LuatTenPhongNoiBo.TenNganGon` dựng nên đã
   ngắn sẵn ("P. THTDH"), ⛔ không cần cắt chữ. */
.sn-phong[b-u4z18iur9f] {
    padding: 0 .3rem;
    border-radius: 999px;
    background: var(--sn-phong-nen);
    color: var(--sn-phong-chu);
    font-size: .72rem;
    font-weight: 600;
    white-space: nowrap;
}

/* ─── BA CHẶNG CỦA CỬA SỔ ±7 NGÀY (đợt 1612) ──────────────────────────────────────────────────
   Dải nay gom cả người sắp tới và vừa qua, nên từng HUY HIỆU phải tự nói mình thuộc chặng nào —
   trước đợt này chỉ có cả dải đậm/nhạt, mà một dải 15 ngày thì ⛔ không đủ.
   ⚠ Cùng bảng màu và cùng ba tên lớp với ô sinh nhật tháng (`KhoiSinhNhatThang.razor.css`), chỉ khác
   tiền tố — đổi màu thì đổi CẢ HAI, nếu không một tính năng lại có hai bộ mặt ở hai màn hình. */
.sn-nguoi.sn-homnay[b-u4z18iur9f] {
    border-color: rgba(176, 118, 20, .85);
    background: linear-gradient(100deg, #ffdf94, #ffc9dd);
    font-weight: 700;
    /* Một vệt sáng mảnh ở MÉP TRÊN huy hiệu: đủ để người sinh nhật HÔM NAY nổi hẳn khỏi hai chặng
       còn lại mà ⛔ không phải tô đậm thêm mảng nền (mảng nền đậm là đúng cái tật "kẹo" đợt 1622).
       ⛔ Đừng đổi sang `opacity` hay nền sáng hơn — cả hai đều kéo tương phản của TÊN NGƯỜI xuống. */
    box-shadow: 0 1px 4px rgba(206, 148, 36, .32), inset 0 1px 0 var(--sn-chip-bong-tren, transparent);
}

/* ⚠ Chip phòng trên huy hiệu HÔM NAY nằm trên nền gradient vàng đậm ⇒ phải tự đậm lên, ⛔ không
   dùng lại nền nhạt của trạng thái thường. Đo sống 31/08/2026 trước khi vá: 4,07:1 — dưới ngưỡng
   đọc 4,5:1, mà build vẫn xanh và mắt vẫn thấy "có màu". */
.sn-nguoi.sn-homnay .sn-phong[b-u4z18iur9f] {
    background: rgba(140, 92, 12, .20);
    color: var(--sn-chu);
}

/* ĐÃ QUA ⇒ nhạt đi nhưng VẪN ĐỌC ĐƯỢC — màu riêng, ⛔ không phải `opacity`. */
.sn-nguoi.sn-daqua[b-u4z18iur9f] {
    border-color: rgba(120, 110, 95, .28);
    background: var(--sn-mo-nen);
    color: var(--sn-mo-chu);
    font-weight: 400;
}

.sn-nguoi.sn-daqua .sn-ngay[b-u4z18iur9f],
.sn-nguoi.sn-daqua .sn-phong[b-u4z18iur9f] {
    color: var(--sn-mo-chu);
}

.sn-nguoi.sn-daqua .sn-phong[b-u4z18iur9f] {
    background: rgba(120, 110, 95, .14);
}

/* CÓ NGƯỜI SINH NHẬT ĐÚNG HÔM NAY ⇒ cả dải đậm lên. Hôm nay và ngày mai là hai việc khác nhau,
   ⛔ đừng để chúng trông giống hệt. Lớp do C# gắn, ⛔ không dùng `:has` (xem chú thích `_coHomNay`).
   ⭐ ĐỢT 1622 — tín hiệu "hôm nay" nay dồn vào THANH NHẤN DỌC (dày gấp đôi, màu đậm hơn) thay cho
     việc tô đậm cả mảng nền. Một dải một hàng thì thanh nhấn đọc rõ hơn hẳn mảng nền, mà ⛔ không
     kéo theo cái tật "mảng kẹo" anh Tiến vừa chê. */
.sn-dai.sn-mung[b-u4z18iur9f] {
    --sn-nen: #fff4dd;
    --sn-vach: #d3860f;
    --sn-vach-day: 6px;
    --sn-vien: rgba(190, 132, 30, .42);
    --sn-nen-di-chuot: #ffeac2;
}

/* ─── GIAO DIỆN TỐI ───────────────────────────────────────────────────────────────────────────
   ⚠ Độ đặc hiệu phải CAO HƠN luật `.sn-mung` ở trên, nếu không nền tối sẽ thắng luôn cả nhánh
     "hôm nay" và hai trạng thái lại trông giống hệt nhau. Ở đây:
       `.sn-dai`            ⇒ (0,2,0) sau khi CSS cách ly gắn dấu scope
       `.sn-dai.sn-mung`    ⇒ (0,3,0)
       `:root[data-theme] .sn-dai`         ⇒ (0,4,0)  — thắng cả hai luật nền sáng
       `:root[data-theme] .sn-dai.sn-mung` ⇒ (0,5,0)  — nên phải khai riêng, ⛔ đừng bỏ. */
:root[data-theme="dark"] .sn-dai[b-u4z18iur9f],
:root[data-theme="dark"] .sn-nhom[b-u4z18iur9f] {
    --sn-nen: #2e2416;
    --sn-vach: #c08a2e;
    --sn-vien: rgba(255, 209, 122, .24);
    --sn-chu: #ffe6b8;
    --sn-chu-nhat: #e6c68c;
    --sn-nhan-mau: #ffd9a0;
    --sn-chip-nen: rgba(255, 255, 255, .09);
    --sn-chip-vien: rgba(255, 209, 122, .24);
    --sn-phong-nen: rgba(255, 209, 122, .16);
    --sn-phong-chu: #ffe6b8;
    --sn-mo-nen: rgba(255, 255, 255, .05);
    --sn-mo-chu: #c9bda5;
    --sn-nen-di-chuot: #3a2d1b;

    /* Cùng bốn biến của bảng sáng, ⛔ đừng bỏ sót — xem lời dặn ở bảng sáng. Ở nền tối thì ánh sáng
       quét chỉ còn rất mỏng (một vệt trắng .65 ở đây là một mảng chói), còn bóng đổ phải ĐẬM HƠN vì
       bóng nhạt trên nền tối là bóng ⛔ không ai thấy. */
    --sn-anh-sang: rgba(255, 255, 255, .05);
    --sn-bong: rgba(0, 0, 0, .28);
    --sn-bong-day: rgba(0, 0, 0, .38);
    --sn-chip-bong-tren: rgba(255, 255, 255, .12);
}

/* Ba chặng ở nền tối — khai RIÊNG từng lớp vì luật nền tối phải thắng luật trạng thái nền sáng
   (0,3,0), mà một bộ chọn gộp thì ⛔ không đủ cao ở mọi vế. */
:root[data-theme="dark"] .sn-nguoi.sn-homnay[b-u4z18iur9f] {
    border-color: rgba(255, 209, 122, .70);
    background: linear-gradient(100deg, #6a4d16, #62333f);
    color: #fff1cf;
    box-shadow: 0 1px 5px rgba(255, 196, 92, .18), inset 0 1px 0 var(--sn-chip-bong-tren, transparent);
}

:root[data-theme="dark"] .sn-nguoi.sn-homnay .sn-ngay[b-u4z18iur9f],
:root[data-theme="dark"] .sn-nguoi.sn-homnay .sn-phong[b-u4z18iur9f] {
    color: #fff1cf;
}

:root[data-theme="dark"] .sn-nguoi.sn-homnay .sn-phong[b-u4z18iur9f] {
    background: rgba(60, 38, 4, .40);
}

:root[data-theme="dark"] .sn-nguoi.sn-daqua[b-u4z18iur9f] {
    border-color: rgba(255, 255, 255, .12);
    background: var(--sn-mo-nen);
    color: var(--sn-mo-chu);
}

:root[data-theme="dark"] .sn-nguoi.sn-daqua .sn-ngay[b-u4z18iur9f],
:root[data-theme="dark"] .sn-nguoi.sn-daqua .sn-phong[b-u4z18iur9f] {
    color: var(--sn-mo-chu);
}

:root[data-theme="dark"] .sn-nguoi.sn-daqua .sn-phong[b-u4z18iur9f] {
    background: rgba(255, 255, 255, .08);
}

:root[data-theme="dark"] .sn-dai.sn-mung[b-u4z18iur9f] {
    --sn-nen: #3d2f16;
    --sn-vach: #ffc75c;
    --sn-vach-day: 6px;
    --sn-vien: rgba(255, 209, 122, .42);
    --sn-nen-di-chuot: #4b3a1b;
}

/* ─── THẺ CẢNH BÁO "chưa có ngày sinh nào" (đợt 1419) ─────────────────────────────────────────
   Nhánh này CỐ Ý vẫn là một thẻ `card`: nó là CẢNH BÁO một việc còn dở, không phải lời chúc — nên
   nó phải trông giống mọi dải cảnh báo khác của app chứ ⛔ không giống dải sinh nhật ở trên.

   ⚠ Viết `.card.sn-the` chứ ⛔ KHÔNG `.sn-the` trần — bản một lớp (0,1,0) thua luật thẻ của app.
   CSS cách ly biên dịch ra `.card.sn-the[b-xxxx]` = (0,3,0). Bài học đợt 1268 (`.content.content-rong`).

   ⛔ CỐ Ý KHÔNG đặt `border-color` ở đây — đo sống 27/08/2026: app đã có luật
   `:root[data-theme="light"] .card:not(.trang-chu *)` ở (0,4,0), thắng luật này, nên một khai báo
   viền ở đây là MÃ CHẾT (bài học đợt 808 — nhánh không bao giờ chạy là chỗ không phép đo nào canh
   nổi). Muốn đổi viền thì sửa luật thẻ chung, ⛔ đừng đua độ đặc hiệu ở đây. */
.card.sn-the[b-u4z18iur9f] {
    background: var(--bs-warning-bg-subtle, #fff9e6);
}

/* ─────────── ⭐ ĐỢT 1979 (việc o1261) — SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY ───────────
   📏 Đo sống 04/09/2026 (Chrome thật, 390×844, cảm ứng, `tienvm`, `do-song-khung-hep.mjs --trang /`):
      dải sinh nhật ở TRANG CHỦ đóng góp **4/4** chỗ chữ nhỏ của cả trang — huy hiệu tên phòng
      `.sn-phong` đo ra **11,5px** (luật `.72rem` ở trên), dưới sàn `--cham-chu-toi-thieu` = 12px.
      Đây là chữ nói *người sắp sinh nhật thuộc phòng nào* — thứ ⛔ không đoán được nếu đọc không ra.
   ⚠ Huy hiệu đang `white-space: nowrap` nên nó chỉ dài thêm chứ ⛔ không vỡ dòng; dải đã có khung
      cuộn riêng. Đo lại sau khi sửa: trang chủ vẫn **tràn ngang 0**.
   ⚠ `max()` chứ ⛔ không gõ thẳng 12px — nâng SÀN, giữ tỉ lệ. */
@media (pointer: coarse) {
    .sn-phong[b-u4z18iur9f] { font-size: max(.72rem, var(--cham-chu-toi-thieu)); }

    /* ⭐ CẢ DẢI LÀ MỘT CÁI NÚT (đợt 1633) mà nó ⛔ KHÔNG mang lớp `.btn`, nên luật vùng chạm 44px
       dùng chung của app ⛔ KHÔNG với tới nó. Khai ở đây, và chỉ khi màn hình cầm bằng NGÓN TAY.
       ⚠ Dùng biến `--cham-toi-thieu` của app, ⛔ đừng gõ thẳng 44px: sàn ấy đổi thì dải đổi theo.
       ⚠ Đây là chỗ DUY NHẤT lượt làm đẹp này cho dải cao thêm (≈34px ⇒ 44px, và CHỈ trên điện
         thoại) — ⛔ đừng nhân cớ này mà nới trên máy để bàn: anh Tiến đã ép dải từ 214px xuống một
         hàng, lượt làm đẹp ⛔ KHÔNG được đi ngược lượt ấy. */
    .sn-dai[b-u4z18iur9f] { min-height: var(--cham-toi-thieu); }
}

/* ─────────── TÔN TRỌNG NGƯỜI ĐÃ TẮT HIỆU ỨNG CHUYỂN ĐỘNG (20/09/2026) ───────────
   Người tắt chuyển động trong hệ điều hành thì dải vẫn ĐỦ NGUYÊN: nền, ánh sáng quét chéo, bóng đổ,
   thanh nhấn dọc, mọi huy hiệu. Thứ mất đi chỉ là ba chuyển động: lượt hiện ra, cú nhấc nhẹ lúc di
   chuột, và chiếc bánh nghiêng.
   ⚠ Tắt cả `transition` chứ ⛔ KHÔNG chỉ `animation`: nền và bóng vẫn đổi lúc di chuột (đó là phản
     hồi cần thiết, ⛔ không phải hiệu ứng trang trí), chỉ là đổi TỨC THÌ thay vì trôi dần. */
@media (prefers-reduced-motion: reduce) {
    .sn-dai[b-u4z18iur9f] {
        animation: none;
        transition: none;
    }

    .sn-dai:hover[b-u4z18iur9f] { transform: none; }

    .sn-icon[b-u4z18iur9f] { transition: none; }

    .sn-dai:hover .sn-icon[b-u4z18iur9f],
    .sn-dai:focus-visible .sn-icon[b-u4z18iur9f] { transform: none; }
}
/* /Components/ChungTu/KhoiSinhNhatThang.razor.rz.scp.css */
/* ═══ Ô NHỎ "SINH NHẬT TRONG THÁNG" Ở ĐẦU BẢNG CHẤM CÔNG (đợt 1600 — việc o901) ═══

   ⚠ Mọi luật khai CẢ NỀN LẪN MÀU CHỮ thành cặp — khai một vế là chữ trắng trên nền trắng ở một trong
     hai giao diện, hỏng lặng lẽ và chỉ người dùng giao diện kia mới thấy.
   ⚠ Cùng bảng màu ấm với dải sinh nhật trang chủ (`KhoiSinhNhat.razor.css`) — đổi màu thì đổi CẢ HAI,
     nếu không cùng một tính năng lại có hai bộ mặt ở hai màn hình. */

/* 🔴 BẢNG MÀU KHAI CHO CẢ HAI NƠI MỌC HUY HIỆU — ⛔ ĐỪNG GOM VỀ RIÊNG `.snt-o` (đợt 2213, việc f75).
   Từ đợt này huy hiệu mọc ở HAI chỗ: trên dải `.snt-o`, và trong HỘP THOẠI `.snt-hop`. Hộp thoại dựng
   ở một nhánh DOM KHÁC (khung `HopThoai`) nên nó ⛔ KHÔNG nằm trong `.snt-o` ⇒ mọi biến khai riêng ở
   đó là biến ⛔ KHÔNG phân giải được khi huy hiệu nằm trong hộp. Mà biến không phân giải được thì
   trình duyệt VỨT nguyên khai báo — huy hiệu trong hộp mất sạch nền, viền và màu chữ, ⛔ KHÔNG một câu
   lỗi nào: build xanh, test xanh, dải trên bảng công vẫn đẹp (bài học `--bs-border-color` đợt 831).
   ⚠ Thêm chỗ mọc huy hiệu THỨ BA thì thêm bộ chọn vào ĐÚNG HAI khối này (bảng sáng + bảng tối). */
.snt-o[b-jft43mscs5],
.snt-hop[b-jft43mscs5] {
    --snt-nen1: #fff4d4;
    --snt-nen2: #ffe6ef;
    --snt-vien: rgba(190, 132, 30, .40);
    --snt-vien-di-chuot: rgba(190, 132, 30, .75);
    --snt-chu: #6b3d09;
    --snt-chip-nen: rgba(255, 255, 255, .86);
    --snt-chip-vien: rgba(190, 132, 30, .26);
    --snt-mo-nen: rgba(255, 255, 255, .55);
    --snt-mo-chu: #5f5548;
    --snt-phong-nen: rgba(190, 132, 30, .14);
    --snt-phong-chu: #6b3d09;
}

/* Ô phải NHỎ đúng nghĩa: nó đứng ngay trên bảng công, mà bảng công mới là thứ người ta mở trang để
   xem. `width: max-content` ⇒ phòng có một người sinh nhật thì ô chỉ dài bằng một người.
   ⚠ `max-width: 100%` phải đi liền: mục CẢ KHO có thể ra vài chục người (đo 31/08/2026 trên dữ liệu
     thật: tháng 8 có 28, tháng 9 có 38) — thiếu vế này là ô đẩy ngang cả trang.

   ⭐ ĐỢT 2213 (việc f75) — anh Tiến 08/09/2026: *"ô sinh nhật để khung vuông bo góc, và chỉ hiển thị
     1 dòng, cần thì nhấn vào sẽ hiển thị ra hộp thoại chi tiết"*. Ba đổi thay ở đúng khối này:
     ① `border-radius` 999px ⇒ **10px**, cùng con số với `.sn-dai` của dải trang chủ — ⛔ đừng gõ một
        bán kính lạc lõng. Dáng viên thuốc chỉ đọc đúng khi khối cao MỘT dòng; ảnh anh gửi là khối cao
        hai dòng, bốn góc bo thành một cục méo.
     ② `flex-wrap: nowrap` + `overflow: hidden` ⇒ dải ⛔ không thể rơi xuống dòng thứ hai ở BẤT KỲ bề
        ngang nào. Đây là LỚP NGOÀI; lớp trong là phép kẹp số huy hiệu ở `LuatDaiSinhNhatGon.Chia`.
        ⛔ Đừng bỏ lớp trong mà chỉ giữ lớp này: cắt bằng `overflow` thì huy hiệu cuối bị cắt NGANG
        THÂN, còn "+N" thì ⛔ không có số nào để đếm.
     ③ Thẻ đổi từ `<div>` sang `<button>`.
   🔴 ĐỔI SANG `<button>` THÌ PHẢI TRẢ LẠI BẰNG TAY BA THỨ MÀ NÚT ⛔ KHÔNG KẾ THỪA: phông chữ
     (`font: inherit`) · căn chữ (`text-align: start`) · màu chữ (`color`, đã khai sẵn bên dưới).
     Trình duyệt cấp cho `<button>` một bộ phông RIÊNG của hệ điều hành và căn GIỮA — thiếu hai dòng
     ấy là cả dải đổi phông, đổi cỡ và dồn vào giữa, mà build vẫn xanh và test vẫn xanh. */
.snt-o[b-jft43mscs5] {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: .25rem .35rem;
    width: max-content;
    max-width: 100%;
    min-width: 0;
    overflow: hidden;
    margin-bottom: .6rem;
    padding: .28rem .65rem .28rem .45rem;
    border: 1px solid var(--snt-vien);
    border-radius: 10px;
    background: linear-gradient(100deg, var(--snt-nen1), var(--snt-nen2));
    box-shadow: 0 1px 3px rgba(0, 0, 0, .07), inset 0 1px 0 rgba(255, 255, 255, .5);
    color: var(--snt-chu);
    font: inherit;
    text-align: start;
    cursor: pointer;
}

/* Di chuột ⇒ nét viền đậm lên một nấc. ⛔ Đừng đổi `box-shadow` ở đây: bảng màu TỐI khai lại
   `box-shadow` cho `.snt-o` ở độ đặc hiệu cao hơn (0,4,0) nên luật di chuột (0,2,0) sẽ thua lặng lẽ
   ở đúng một trong hai giao diện.
   ⚠ Màu lấy từ BIẾN và có GIÁ TRỊ DỰ PHÒNG: thiếu biến ở một bảng màu thì trình duyệt VỨT nguyên khai
   báo ⇒ dải mất phản ứng di chuột, ⛔ không một câu lỗi nào (bài học `--bs-border-color` đợt 831). */
.snt-o:hover[b-jft43mscs5] {
    border-color: var(--snt-vien-di-chuot, var(--snt-vien));
}

/* Lối bàn phím. Dùng `:focus-visible` chứ ⛔ không `:focus`: bấm chuột xong cũng là focus, mà vẽ một
   vòng viền sau mỗi cú bấm chuột là nhiễu. */
.snt-o:focus-visible[b-jft43mscs5] {
    outline: 2px solid var(--snt-vien-di-chuot, var(--snt-vien));
    outline-offset: 2px;
}

.snt-nhan[b-jft43mscs5] {
    flex: 0 0 auto;
    font-size: .8rem;
    font-weight: 600;
    white-space: nowrap;
    padding-inline: .2rem;
    color: var(--snt-chu);
}

/* Ruột dải — nơi huy hiệu đứng, và là NƠI DUY NHẤT được phép cắt.
   🔑 `min-width: 0` là dòng sống còn: flex item mặc định `min-width: auto` = bề rộng nội dung nhỏ
     nhất ⇒ thiếu nó thì cụm này NỞ RA theo cả danh sách, đẩy nhãn và "+N" ra ngoài mép, và phép cắt
     `overflow` ⛔ chưa từng chạy một lần nào.
   🔴 HUY HIỆU BÊN TRONG ⛔ KHÔNG ĐƯỢC CO (`flex: 0 0 auto` ở `.snt-nguoi`) — đo sống 01/09/2026 ở dải
     trang chủ, khổ 820px: cho huy hiệu co thì phần co được DUY NHẤT là TÊN NGƯỜI (chip phòng và ngày
     đều `nowrap`), màn hình còn trơ "B…", "H…" cạnh một chip phòng và một ngày. Tức phép cắt chữ ăn
     đúng thứ tính năng này sinh ra để bày. ⇒ Thà cắt NGANG một huy hiệu ở mép phải còn hơn cắt cụt
     mọi cái tên. */
.snt-ruot[b-jft43mscs5] {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: .25rem .35rem;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
}

/* Nhãn "+N" — số người CHỈ có trong hộp thoại. ⛔ Không co, và ⛔ không mang `cursor` riêng: cả dải đã
   là nút, để riêng ở đây là nói dối rằng "chỉ chỗ này bấm được".
   ⛔ Nó cố ý ⛔ KHÔNG mang `title`: chữ "+N" ⛔ không hề bị ẩn (quy tắc #23). */
.snt-them[b-jft43mscs5] {
    flex: 0 0 auto;
    padding: .06rem .45rem .1rem;
    border: 1px dashed var(--snt-chip-vien);
    border-radius: 999px;
    background: transparent;
    color: var(--snt-chu);
    font-size: .78rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.snt-o:hover .snt-them[b-jft43mscs5],
.snt-o:focus-visible .snt-them[b-jft43mscs5] {
    border-style: solid;
    background: var(--snt-phong-nen);
}

/* ─── HỘP THOẠI BÀY TRỌN THÁNG (đợt 2213 — việc f75) ─────────────────────────────────────────
   Ở đây huy hiệu ĐƯỢC xuống dòng: hộp thoại sinh ra để bày ĐỦ, ⛔ không phải để giữ một dòng. */
.snt-hop[b-jft43mscs5] {
    color: var(--snt-chu);
}

/* Câu phạm vi dữ liệu — chữ nhỏ, nhạt, đứng trên dải huy hiệu. */
.snt-pham-vi[b-jft43mscs5] {
    margin: 0 0 .55rem;
    font-size: .82rem;
    color: var(--snt-mo-chu);
}

.snt-hang[b-jft43mscs5] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem .35rem;
}

/* Một người = số NGÀY đứng trước, tên đứng sau — danh sách xếp theo ngày nên cột số bên trái đọc
   được như một dòng lịch nhỏ. */
.snt-nguoi[b-jft43mscs5] {
    display: inline-flex;
    align-items: baseline;
    gap: .28rem;
    flex: 0 0 auto;
    max-width: 100%;
    padding: .06rem .45rem .1rem .3rem;
    border: 1px solid var(--snt-chip-vien);
    border-radius: 999px;
    background: var(--snt-chip-nen);
    color: var(--snt-chu);
    font-size: .8rem;
    font-weight: 500;
}

.snt-ngay[b-jft43mscs5] {
    min-width: 1.35rem;
    padding: 0 .2rem;
    border-radius: 999px;
    background: rgba(190, 132, 30, .16);
    color: var(--snt-chu);
    font-size: .74rem;
    font-weight: 700;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.snt-ten[b-jft43mscs5] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ⭐ ĐỢT 1612 — TÊN PHÒNG VIẾT TẮT (anh Tiến giao 31/08/2026), đứng SAU tên người.
   ⚠ Chữ này ⛔ không được cắt bằng `text-overflow`: chuỗi đã ngắn sẵn ("P. THTDH"), cắt nó là mất
   đúng thứ vừa thêm vào. Thứ được phép co lại vẫn là TÊN người (`.snt-ten`). */
.snt-phong[b-jft43mscs5] {
    padding: 0 .28rem;
    border-radius: 999px;
    background: var(--snt-phong-nen);
    color: var(--snt-phong-chu);
    font-size: .7rem;
    font-weight: 600;
    white-space: nowrap;
}

.snt-nguoi.snt-daqua .snt-phong[b-jft43mscs5] {
    background: rgba(120, 110, 95, .14);
    color: var(--snt-mo-chu);
}

/* ⚠ Chip phòng trên huy hiệu HÔM NAY nằm trên nền gradient vàng đậm ⇒ phải tự đậm lên. Cùng con số
   với dải trang chủ — đổi màu thì đổi CẢ HAI. */
.snt-nguoi.snt-homnay .snt-phong[b-jft43mscs5] {
    background: rgba(140, 92, 12, .20);
    color: var(--snt-chu);
}

/* ĐÚNG HÔM NAY ⇒ huy hiệu tô đặc. Đây là thông tin, ⛔ không phải trang trí: trong một danh sách cả
   tháng thì "hôm nay" mới là dòng người ta cần thấy ngay. */
.snt-nguoi.snt-homnay[b-jft43mscs5] {
    border-color: rgba(176, 118, 20, .85);
    background: linear-gradient(100deg, #ffdf94, #ffc9dd);
    font-weight: 700;
    box-shadow: 0 1px 4px rgba(206, 148, 36, .32);
}

.snt-nguoi.snt-homnay .snt-ngay[b-jft43mscs5] {
    background: rgba(140, 92, 12, .22);
}

/* ĐÃ QUA ⇒ nhạt đi nhưng VẪN ĐỌC ĐƯỢC. ⛔ Đừng làm nhạt bằng `opacity`: nó kéo cả chữ lẫn nền xuống
   nên tương phản tụt theo mà ⛔ không ai nhìn ra bằng mắt. Đây là màu riêng, đo được. */
.snt-nguoi.snt-daqua[b-jft43mscs5] {
    border-color: rgba(120, 110, 95, .28);
    background: var(--snt-mo-nen);
    color: var(--snt-mo-chu);
    font-weight: 400;
}

.snt-nguoi.snt-daqua .snt-ngay[b-jft43mscs5] {
    background: rgba(120, 110, 95, .14);
    color: var(--snt-mo-chu);
    font-weight: 600;
}

/* ─── GIAO DIỆN TỐI ───────────────────────────────────────────────────────────────────────────
   ⚠ Độ đặc hiệu phải CAO HƠN các luật trạng thái ở trên, nếu không nền tối sẽ thắng luôn cả nhánh
     "hôm nay" và ba trạng thái lại trông giống hệt nhau. Sau khi CSS cách ly gắn dấu scope:
       `.snt-nguoi.snt-homnay`               ⇒ (0,3,0)
       `:root[data-theme] .snt-nguoi.snt-homnay` ⇒ (0,5,0) — nên phải khai riêng từng trạng thái. */
:root[data-theme="dark"] .snt-o[b-jft43mscs5],
:root[data-theme="dark"] .snt-hop[b-jft43mscs5] {
    --snt-nen1: #3a2c14;
    --snt-nen2: #38202c;
    --snt-vien: rgba(255, 209, 122, .30);
    --snt-vien-di-chuot: rgba(255, 209, 122, .62);
    --snt-chu: #ffe6b8;
    --snt-chip-nen: rgba(255, 255, 255, .09);
    --snt-chip-vien: rgba(255, 209, 122, .24);
    --snt-mo-nen: rgba(255, 255, 255, .05);
    --snt-mo-chu: #c9bda5;
    --snt-phong-nen: rgba(255, 209, 122, .16);
    --snt-phong-chu: #ffe6b8;
}

:root[data-theme="dark"] .snt-o[b-jft43mscs5] {
    box-shadow: 0 1px 3px rgba(0, 0, 0, .35), inset 0 1px 0 rgba(255, 255, 255, .06);
}

:root[data-theme="dark"] .snt-nguoi.snt-homnay .snt-phong[b-jft43mscs5] {
    background: rgba(60, 38, 4, .40);
    color: #fff1cf;
}

:root[data-theme="dark"] .snt-nguoi.snt-daqua .snt-phong[b-jft43mscs5] {
    background: rgba(255, 255, 255, .08);
    color: var(--snt-mo-chu);
}

:root[data-theme="dark"] .snt-ngay[b-jft43mscs5] {
    background: rgba(255, 209, 122, .18);
}

:root[data-theme="dark"] .snt-nguoi.snt-homnay[b-jft43mscs5] {
    border-color: rgba(255, 209, 122, .70);
    background: linear-gradient(100deg, #6a4d16, #62333f);
    color: #fff1cf;
    box-shadow: 0 1px 5px rgba(255, 196, 92, .18);
}

:root[data-theme="dark"] .snt-nguoi.snt-homnay .snt-ngay[b-jft43mscs5] {
    background: rgba(60, 38, 4, .40);
    color: #fff1cf;
}

:root[data-theme="dark"] .snt-nguoi.snt-daqua[b-jft43mscs5] {
    border-color: rgba(255, 255, 255, .12);
    background: var(--snt-mo-nen);
    color: var(--snt-mo-chu);
}

:root[data-theme="dark"] .snt-nguoi.snt-daqua .snt-ngay[b-jft43mscs5] {
    background: rgba(255, 255, 255, .08);
    color: var(--snt-mo-chu);
}

/* ─────────── ⭐ SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY (việc o1397) ───────────
   Hai chỗ đo tĩnh dưới sàn `--cham-chu-toi-thieu` (12px): `.snt-ngay` .74rem = 11,84px ·
   `.snt-phong` .7rem = 11,2px.
   📏 ⛔ Không tràn: `.snt-ngay` có `min-width: 1.35rem` (21,6px) và chỉ chứa TỐI ĐA hai chữ số —
      ở 12px hai chữ số ≈ 14px, vẫn dư. `.snt-phong` là viên thuốc `white-space: nowrap` co giãn
      theo chữ nên nó tự nong.
   ⭐ ĐỢT 2213 — cả dải nay là một NÚT ⇒ phải đủ SÀN VÙNG CHẠM 44px (`--cham-toi-thieu`), nếu không
      ngón tay bấm trượt xuống bảng công ngay bên dưới.
   ⛔ CHỈ trong `@media (pointer: coarse)` — xem lý do đầy đủ ở `app.css` (đợt 1518). */
@media (pointer: coarse) {
    .snt-o[b-jft43mscs5] { min-height: var(--cham-toi-thieu); }
    .snt-ngay[b-jft43mscs5] { font-size: max(.74rem, var(--cham-chu-toi-thieu)); }
    .snt-phong[b-jft43mscs5] { font-size: max(.7rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/ChungTu/KhoiTienDoNhiemVu.razor.rz.scp.css */
/* ═══ KHỐI TIẾN ĐỘ NHIỆM VỤ — MỘT ĐƯỜNG RAY, HAI LỚP MÀU · ĐỢT 1333 (việc o533) ═══
   Anh Tiến 26/08/2026: "2 dòng này tìm cách gộp làm 1 và có hiệu ứng, em dùng màu khác nhé".

   HAI MÀU CỐ Ý KHÁC HẲN HỌ NHAU:
     · phần việc   = xanh biển (primary), chuyển lục khi xong và đỏ khi chậm — do lớp bg- của
       LuatTienDoNhiemVu.LopMauThanh đặt, lớp ấy mang !important của Bootstrap nên luôn thắng;
     · quỹ thời gian = HỔ PHÁCH (warning). Chọn hổ phách vì nó không trùng bất kỳ màu nào mà thanh
       việc có thể mang, nên hai lớp không bao giờ lẫn vào nhau ở bất kỳ trạng thái nào.

   MÀU lấy từ token --bs-* của Bootstrap 5.3 (app nạp bản này từ đợt 831) nên khối tự đúng ở CẢ hai
   giao diện sáng và tối. KHÔNG gõ mã màu cứng.

   HIỆU ỨNG — BỀ RỘNG LUÔN ĐÚNG NGAY TỪ KHUNG HÌNH ĐẦU (bài học đợt 1323, trả giá thật):
   thanh chỉ được NỞ THEO CHIỀU DÀY và SÁNG DẦN. Đưa về scaleX(0) hay width 0 là ở những lúc trình
   duyệt không dựng khung hình (tab chạy nền, chế độ tiết kiệm pin, máy yếu, lượt chụp bản tĩnh) thì
   đồng hồ animation đứng ở khung đầu và thanh VÔ HÌNH — mà build xanh, test xanh, không câu lỗi nào.

   Mọi chuyển động tắt ở khối prefers-reduced-motion cuối tệp. */

.ktn-khoi[b-fp4n318pp4] {
    --ktn-cao-ray: 22px;
    /* Bề dày cái kim mốc thời gian. ⛔ ĐỪNG gõ số này lần thứ hai ở đâu nữa: nó vừa là nét vẽ của kim
       (`border-left`) vừa là độ hẹp của LÀN kim chạy (`.ktn-lan-kim`) — hai con số ấy lệch nhau một
       pixel là kim thò ra ngoài ray rồi bị `overflow: hidden` cắt SẠCH (đợt 2256, việc o1643). */
    --ktn-day-kim: 2px;
    --ktn-mau-viec: var(--bs-primary);
    --ktn-mau-thoi-gian: var(--bs-warning);
    --ktn-nen-ray: color-mix(in srgb, var(--bs-body-color) 10%, transparent);
}

/* ─────────── Đường ray dùng chung của hai lớp ─────────── */
.ktn-ray[b-fp4n318pp4] {
    position: relative;
    height: var(--ktn-cao-ray);
    border-radius: calc(var(--ktn-cao-ray) / 2);
    background: var(--ktn-nen-ray);
    overflow: hidden;
}

/* Ca quỹ thời gian CHƯA CHẠY: ray phải trông ĐỨNG YÊN, không phải trống rỗng. Vạch chéo tĩnh nói
   "chưa bắt đầu tính", còn ray trơn thì đọc ra như "còn nguyên thời gian" — hai chuyện khác hẳn. */
.ktn-ray-tinh[b-fp4n318pp4] {
    background-image: repeating-linear-gradient(135deg,
        color-mix(in srgb, var(--bs-body-color) 7%, transparent) 0 6px,
        transparent 6px 12px);
}

/* ─────────── Lớp DƯỚI: quỹ thời gian đã tiêu ─────────── */
.ktn-thoi-gian[b-fp4n318pp4] {
    position: absolute;
    inset: 0 auto 0 0;
    border-radius: calc(var(--ktn-cao-ray) / 2);
    background-color: color-mix(in srgb, var(--ktn-mau-thoi-gian) 42%, transparent);
    background-image: repeating-linear-gradient(135deg,
        color-mix(in srgb, var(--ktn-mau-thoi-gian) 70%, transparent) 0 7px,
        transparent 7px 14px);
    background-size: 40px 100%;
    animation: ktn-soc-chay-b-fp4n318pp4 2.6s linear infinite;
}

/* ─────────── LÀN CHẠY CỦA KIM — hẹp hơn ray ĐÚNG bằng bề dày kim ───────────
   ⭐ ĐỢT 2256 (việc o1643). Kim đặt bằng `left: <phần trăm>%` nên nó mọc sang PHẢI của cái mốc: ở mốc
   100% mép trái kim rơi đúng mép phải ray, cả 2px nét vẽ nằm NGOÀI ⇒ `overflow: hidden` của ray cắt
   sạch. Đo sống 09/09/2026 trên hồ sơ #1000083 (quỹ thời gian đã đi 100%): ray kết thúc ở 550,1px mà
   kim nằm ở [550,1 → 552,1] ⇒ **0px nhìn thấy**, ở CẢ khổ 1400px lẫn 375px. Tức đúng lúc người quản lý
   cần nhìn nhất (việc vừa hết quỹ thời gian) thì dấu hiệu ấy biến mất, màn hình đọc ra như việc CHƯA
   chạy hết quỹ — hỏng LẶNG LẼ, build xanh test xanh.
   🔑 Chữa bằng cách thu hẹp CHỖ KIM ĐƯỢC CHẠY, ⛔ không phải dịch kim đi một đoạn cố định: một phép dời
   `-1px` cứu được mốc 100% thì lại đẩy mốc 0% ra ngoài mép trái, cắt mất đúng chừng ấy. Làn này là khối
   định vị tuyệt đối rộng `ray - <bề dày kim>`, nên `left: 0%` cho kim nằm ở [0 → 2] và `left: 100%` cho
   kim nằm ở [W-2 → W]: **hai đầu đều TRỌN VẸN**, sai số vị trí nhiều nhất 1px (kim ôm lấy cái mốc).
   ⛔ Đừng thay bằng `padding-inline` trên ray: khối con định vị tuyệt đối lấy HỘP ĐỆM của ray làm khung,
   mà thêm đệm ⛔ không hề làm hộp ấy hẹp đi — mép cắt cũng nằm nguyên chỗ cũ, tức một lượt sửa ⛔ không
   đổi một pixel nào. */
.ktn-lan-kim[b-fp4n318pp4] {
    position: absolute;
    inset: 0 var(--ktn-day-kim) 0 0;
    z-index: 3;
    pointer-events: none;
}

/* Kim ở mép phải mảng hổ phách — đọc được cả khi thanh việc đã phủ lên trên nó. */
.ktn-kim[b-fp4n318pp4] {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 0;
    border-left: var(--ktn-day-kim) solid color-mix(in srgb, var(--ktn-mau-thoi-gian) 88%, var(--bs-body-color));
    animation: ktn-kim-tho-b-fp4n318pp4 2.8s ease-in-out infinite;
}

/* ─────────── Lớp TRÊN: phần việc ───────────
   background-color để ở đây là CỐ Ý dùng độ đặc hiệu THẤP: lớp bg-success hay bg-danger của
   Bootstrap mang !important nên khi có mặt là chúng thắng, còn khi không có thì rơi về xanh biển. */
.ktn-viec[b-fp4n318pp4] {
    position: absolute;
    inset: 0 auto 0 0;
    border-radius: calc(var(--ktn-cao-ray) / 2);
    background-color: var(--ktn-mau-viec);
    overflow: hidden;
    z-index: 2;
    box-shadow:
        0 1px 0 color-mix(in srgb, white 40%, transparent) inset,
        0 -2px 4px color-mix(in srgb, black 24%, transparent) inset;
    transform-origin: center center;
    animation: ktn-day-len-b-fp4n318pp4 .55s cubic-bezier(.22, .8, .3, 1) both;
}

/* Vệt sáng chạy dọc thanh việc — phần "có hiệu ứng" anh Tiến đặt hàng. Chạy CHẬM và MỜ: vệt gắt trên
   một trang đầy số liệu là thứ người ta phải nhìn đi chỗ khác mới đọc nổi bảng bên cạnh. */
.ktn-loe[b-fp4n318pp4] {
    position: absolute;
    top: 0;
    bottom: 0;
    left: -42%;
    width: 38%;
    background: linear-gradient(90deg,
        transparent 0%,
        color-mix(in srgb, white 42%, transparent) 50%,
        transparent 100%);
    animation: ktn-loe-chay-b-fp4n318pp4 3.6s ease-in-out infinite;
}

/* Huy hiệu kết luận hiện ra sau thanh một nhịp — đủ để mắt bắt được là nó vừa xuất hiện.
   Khung hình đầu vẫn NHÌN THẤY ĐƯỢC (opacity .55, không phải 0) — cùng lẽ với thanh ở trên. */
.ktn-huy-hieu[b-fp4n318pp4] {
    animation: ktn-hien-len-b-fp4n318pp4 .5s ease-out both;
    animation-delay: .18s;
}

@keyframes ktn-day-len-b-fp4n318pp4 {
    from { transform: scaleY(.45); opacity: .6; }
    to { transform: scaleY(1); opacity: 1; }
}

@keyframes ktn-hien-len-b-fp4n318pp4 {
    from { opacity: .55; transform: translateY(-2px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes ktn-loe-chay-b-fp4n318pp4 {
    0% { left: -42%; }
    58% { left: 104%; }
    100% { left: 104%; }
}

@keyframes ktn-soc-chay-b-fp4n318pp4 {
    from { background-position: 0 0; }
    to { background-position: 40px 0; }
}

@keyframes ktn-kim-tho-b-fp4n318pp4 {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ktn-mau-thoi-gian) 55%, transparent); }
    50% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--ktn-mau-thoi-gian) 22%, transparent); }
}

@media (prefers-reduced-motion: reduce) {
    .ktn-viec[b-fp4n318pp4] { animation: none; }
    .ktn-loe[b-fp4n318pp4] { animation: none; display: none; }
    .ktn-thoi-gian[b-fp4n318pp4] { animation: none; }
    .ktn-kim[b-fp4n318pp4] { animation: none; }
    .ktn-huy-hieu[b-fp4n318pp4] { animation: none; }
}
/* /Components/ChungTu/KhungTraoDoiViec.razor.rz.scp.css */
/* ═══ KHUNG TRAO ĐỔI KIỂU CHAT — ĐỢT 1264 (việc o434) ═══
   Lý do từng quyết định: xem chú thích đầu KhungTraoDoiViec.razor và LuatKhungTraoDoi.

   ⚠ CHÚ THÍCH CSS: ⛔ đừng gõ dấu SAO liền trước dấu GẠCH CHÉO ở giữa câu văn — chú thích đóng ngay
     tại đó, phần chữ còn lại dính vào bộ chọn của luật nằm dưới và trình duyệt bỏ NGUYÊN luật đó.
     Hỏng lặng lẽ tuyệt đối: build xanh, test xanh (bài học đợt 852, hàng rào đợt 961).

   🎨 MÀU LẤY TỪ TOKEN `--bs-*` CỦA BOOTSTRAP 5.3, ⛔ ĐỪNG GÕ MÃ MÀU CỨNG. App bắc cầu
     `data-theme` sang `data-bs-theme` (App.razor + tai-file.js) nên mấy token này TỰ đổi theo giao
     diện sáng/tối. Gõ mã cứng là khung chat sáng trưng giữa nền tối — đúng loại lỗi mà
     NenSangSotOGiaoDienToiTests sinh ra để chặn. Đã đối chiếu: cả 6 token dùng ở đây đều có khai
     trong wwwroot/bootstrap/bootstrap.min.css, mỗi token 2 lượt (một cho nền sáng, một cho nền tối). */

/* Vùng cuộn của cuộc trao đổi.
   Vì sao có TRẦN chiều cao: một nhiệm vụ bàn nhiều lượt thì danh sách phẳng cũ đẩy ô soạn xuống tít
   dưới, người ta phải cuộn cả trang mới gõ được. Khung chat thì cuộn TRONG khung, ô soạn luôn ở đó.
   ⚠ `overflow-anchor: none` — trình duyệt tự "neo" điểm cuộn khi nội dung phía trên đổi, mà ở đây
     nội dung mới luôn thêm vào phía DƯỚI nên phép neo ấy chỉ cãi nhau với khung-trao-doi.js. */
.ktd-cuon[b-cl9iylygcm] {
    max-height: 420px;
    overflow-y: auto;
    overflow-anchor: none;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding-right: 0.4rem;
}

/* Dải ngăn ngày: một chữ nhỏ nằm giữa hai vạch. */
.ktd-dai-ngay[b-cl9iylygcm] {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0.5rem 0 0.15rem;
    color: var(--bs-secondary-color);
    font-size: 0.75rem;
}

.ktd-dai-ngay[b-cl9iylygcm]::before,
.ktd-dai-ngay[b-cl9iylygcm]::after {
    content: "";
    flex: 1 1 auto;
    border-top: 1px solid var(--bs-border-color);
}

.ktd-dong[b-cl9iylygcm] {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
}

/* Tin của CHÍNH người đang xem: đảo chiều hàng ⇒ bóng nằm bên PHẢI.
   🔑 Đảo bằng `row-reverse` chứ ⛔ không đẩy bằng `margin-left: auto`: cách kia phải nhớ đổi thứ tự
      ảnh và thân ở markup, tức hai chỗ phải khớp nhau bằng tay. */
.ktd-toi[b-cl9iylygcm] {
    flex-direction: row-reverse;
}

/* Ô giữ chỗ cho ảnh đại diện — GIỮ NGUYÊN bề rộng kể cả khi tin gộp nhóm (⛔ không có ảnh).
   Bỏ ô này đi thì tin gộp nhóm thụt hẳn sang trái, khối tin của một người ⛔ không còn thẳng hàng. */
.ktd-anh[b-cl9iylygcm] {
    flex: 0 0 32px;
    width: 32px;
}

.ktd-tron[b-cl9iylygcm] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;

    /* Chữ TRẮNG là bắt buộc, ⛔ không đổi: 8 màu nền của LuatAnhDaiDien.MauNen được chọn để chữ trắng
       đạt tối thiểu 5,16:1 — bảng số đo nằm ngay trong lớp ấy. Đổi màu chữ là vứt cả bảng đo đó. */
    color: #fff;
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1;
    user-select: none;
}

.ktd-than[b-cl9iylygcm] {
    /* `min-width: 0` cho phép bóng co lại trong flex ⇒ chữ dài mới xuống dòng thay vì trào ra ngoài. */
    min-width: 0;
    max-width: 78%;
    display: flex;
    flex-direction: column;
}

.ktd-toi .ktd-than[b-cl9iylygcm] {
    align-items: flex-end;
}

.ktd-dau[b-cl9iylygcm] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-bottom: 0.15rem;
    font-size: 0.8rem;
}

.ktd-ten[b-cl9iylygcm] {
    font-weight: 600;
}

/* Huy hiệu vai trò. Cố ý ⛔ KHÔNG dùng `text-bg-light` của Bootstrap: lớp ấy gắn nền sáng cứng nên ở
   giao diện tối nó thành một mảng sáng chói giữa khung. Tô bằng token thì tự theo giao diện. */
.ktd-vai[b-cl9iylygcm] {
    background: var(--bs-secondary-bg);
    color: var(--bs-body-color);
    border: 1px solid var(--bs-border-color);
    font-weight: 500;
}

.ktd-gio[b-cl9iylygcm] {
    color: var(--bs-secondary-color);
    font-size: 0.75rem;
}

/* Bóng chat của NGƯỜI KHÁC — nền trung tính. */
.ktd-bong[b-cl9iylygcm] {
    background: var(--bs-tertiary-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 12px;
    padding: 0.35rem 0.7rem;
    color: var(--bs-body-color);

    /* Giữ nguyên xuống dòng người ta gõ (giống khối cũ), và cắt được cả một chuỗi dài không dấu cách
       — dán một đường dẫn dài vào ô chat là chuyện thường. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Bóng chat CỦA MÌNH — nền xanh nhạt theo token, đổi theo giao diện. */
.ktd-toi .ktd-bong[b-cl9iylygcm] {
    background: var(--bs-primary-bg-subtle);
    border-color: var(--bs-primary-border-subtle);
}

/* Mép nhọn về phía người nói — dấu hiệu quen thuộc của khung chat. */
.ktd-dong .ktd-bong[b-cl9iylygcm] {
    border-top-left-radius: 4px;
}

.ktd-toi .ktd-bong[b-cl9iylygcm] {
    border-top-left-radius: 12px;
    border-top-right-radius: 4px;
}

/* Màn hình hẹp: bóng được rộng gần hết khung, không thì mỗi dòng còn vài chữ. */
@media (max-width: 575.98px) {
    .ktd-than[b-cl9iylygcm] {
        max-width: 88%;
    }
}
/* /Components/ChungTu/LuoiODien.razor.rz.scp.css */
/* ===== ĐỢT 2274 (việc o1789) — LƯỚI Ô ĐIỀN, CỘT LÀ CỘT =====
   Xem lý do ở chú thích đầu LuoiODien.razor. Ở đây chỉ còn phép đo. */

.lde-luoi[b-f7v94cd95m] {
    display: grid;
    grid-template-columns: repeat(var(--lde-cot, 3), minmax(0, 1fr));
    gap: .7rem 1rem;
    align-items: stretch;
}

/* Khổ máy tính bảng: ba cột thành hai. Ô khai `Rong` vẫn trọn hàng vì nó dùng `1 / -1`. */
@media (max-width: 767.98px) {
    .lde-luoi[b-f7v94cd95m] {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Khổ điện thoại: một cột. Mốc 575.98 ăn khớp mốc chung 576px của app. */
@media (max-width: 575.98px) {
    .lde-luoi[b-f7v94cd95m] {
        grid-template-columns: 1fr;
        gap: .55rem;
    }
}

/* ⭐ SUBGRID — thứ làm cho «thẳng lối» đúng nghĩa.
   Chỉ đẩy ô nhập xuống đáy ô (`margin-top: auto` bên ODien) thì đủ khi mọi ô có cùng bộ phần:
   nhãn + ô nhập. Nhưng ô nào có thêm DÒNG CHÚ THÍCH NHỎ ở dưới thì ô nhập của nó bị đội lên —
   đo sống 10/09 trên phiếu khai máy: ô «Sê-ri» có chú thích ⇒ đáy 382 trong khi «Hãng» và
   «Tình trạng» cùng hàng ở 412, lệch 30px.
   Subgrid cho cả ba phần (nhãn · ô nhập · chú thêm) mượn ĐÚNG ba vạch ngang của hàng, nên mọi ô
   trong hàng thẳng theo cả ba.
   ⛔ Trình duyệt chưa hiểu subgrid thì bỏ trọn khối này và rơi về phép `margin-top: auto` — vẫn
   thẳng ở mọi hàng ⛔ không có chú thích. Chiều fail ĐÚNG. */
@supports (grid-template-rows: subgrid) {
    .lde-luoi[b-f7v94cd95m] {
        row-gap: 0;   /* khe giữa hàng chuyển sang lề dưới của ô, ⛔ không rơi vào giữa ba vạch */
    }

    .lde-luoi[b-f7v94cd95m] >  .lde-o {
        grid-row: span 3;
        display: grid;
        grid-template-rows: subgrid;
        margin-bottom: .7rem;
    }
}
/* /Components/ChungTu/NenHopThoai.razor.rz.scp.css */
/* ═══ NỀN PHỦ "NHẤN RA NGOÀI LÀ ĐÓNG" (đợt 853) ═══
   Lý do từng chốt + hai kiểu dùng: xem chú thích đầu NenHopThoai.razor.

   ⚠ CHÚ THÍCH CSS: ⛔ đừng gõ dấu SAO liền trước dấu GẠCH CHÉO ở giữa câu văn — chú thích đóng ngay
     tại đó, phần chữ còn lại dính vào bộ chọn của luật nằm dưới và trình duyệt bỏ NGUYÊN luật đó.
     Hỏng lặng lẽ tuyệt đối: build xanh, test xanh (bài học đợt 852). */

/* ⛔ CỐ Ý KHÔNG CÓ LUẬT NÀO CHO `.nhn-nen` — đừng thêm vào.
   `.nhn-nen` chỉ là cái MÓC để nhận ra thẻ nền (hàng rào test đọc nó), không phải chỗ trang điểm. Ở
   kiểu NỀN BỌC, phần định vị do lớp bên ngoài truyền vào lo (vd `modal d-block` của Bootstrap); mà
   CSS phạm vi (scoped) của component mang thêm thuộc tính `[b-xxxx]` nên độ đặc hiệu của nó CAO HƠN
   `.modal` ⇒ khai bố cục ở đây là lặng lẽ đè lên khuôn Bootstrap mà bên gọi không hề hay biết. */

/* Kiểu NỀN RỜI: thẻ này chính là tấm phủ kín màn hình. */
.nhn-roi[b-cpfcf1eaxi] {
    position: fixed;
    inset: 0;

    /* ⛔ ĐỪNG DỌN DÒNG NÀY VÌ THẤY `inset: 0` ĐÃ ĐỦ.
       Thanh nav `.top-row` khai `backdrop-filter` (kính mờ). Theo đặc tả CSS, phần tử có
       `filter` / `backdrop-filter` / `transform` khác `none` trở thành KHỐI CHỨA của mọi hậu duệ
       `position: fixed` ⇒ `inset: 0` hết nghĩa "phủ kín khung nhìn", chỉ còn phủ đúng dải nav cao
       ~56px, bấm xuống thân trang là bấm trượt. Đúng lỗi anh Tiến báo 14/08/2026, đã có hàng rào
       riêng cho hai nền phủ đời trước (TKNB.Tests/NenPhuBamRaNgoaiTests.cs).
       ⛔ Cũng ĐỪNG "chữa" bằng `right: -100vw` / `width: 300vw`: phần trào sang phải cộng vào vùng
         cuộn của trang và đẻ ra thanh cuộn ngang mỗi lần mở hộp. */
    height: 100vh;
    height: 100dvh;   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */

    /* Dưới thân hộp (các hộp trong app đang dùng 1060) và trên mọi thứ khác của trang.
       Màn hình nào cần số khác thì truyền qua `KieuNen`, ⛔ đừng sửa số ở đây. */
    z-index: 1055;

    margin: 0;
    cursor: default;

    /* Cố ý KHÔNG tô màu: nền phủ mặc định là TRONG SUỐT.
       Có hộp muốn làm tối màn hình phía sau, có hộp là thẻ nhỏ ở góc mà làm tối cả màn hình thì quá
       tay. Ai cần tối thì truyền `KieuNen="background: rgba(0,0,0,.45);"`. */
    background: transparent;
}

/* Thẻ chặn sự kiện của phần thân hộp (kiểu NỀN BỌC).
   🔑 `display: contents` là cả cái mẹo: thẻ vẫn nằm trong cây DOM nên vẫn chặn được `pointerdown` /
      `click` trên đường đi lên nền, nhưng KHÔNG sinh ra hộp bố cục nào ⇒ với con mắt của CSS thì
      phần thân bên trong vẫn là con trực tiếp của nền.
   ⛔ Bỏ dòng này là gãy mọi luật dựa vào quan hệ cha–con, rõ nhất là `.modal-dialog-centered` của
      Bootstrap (căn giữa bằng `min-height` tính theo cha) — hộp sẽ dính lên mép trên màn hình. */
.nhn-khung[b-cpfcf1eaxi] {
    display: contents;
}
/* /Components/ChungTu/NhanhCayNhiemVu.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 2228 (việc o1663) — MỘT NHÁNH CỦA CÂY NHIỆM VỤ ═══

   🔑 CSS SCOPED (`.razor.css`) — chỉ ăn vào markup của chính component này. Component TỰ GỌI CHÍNH NÓ
      nên mọi tầng dùng chung một bộ luật, ⛔ đừng viết luật riêng theo nấc.

   ⛔ ĐỪNG đặt bề ngang cố định cho dòng: cây bày trên trang chi tiết, mà trang ấy phải đọc được ở khổ
      375px. Dòng dùng flex-wrap ⇒ hết chỗ thì xuống hàng, ⛔ không đẩy trang trượt ngang.

   ⚠ Cỡ chữ ⛔ KHÔNG được xuống dưới 12px trên điện thoại (luật giao diện của dự án) — ở đây dùng
     lớp `small` của Bootstrap (0,875rem ≈ 14px), ⛔ đừng thêm font-size nhỏ hơn. */

.ncn-nut[b-ue4ifp33xa] {
    list-style: none;
    padding: 0;
    margin: 0;
}

/* Một DÒNG hồ sơ: mọi thứ trên cùng hàng, hết chỗ thì xuống hàng chứ ⛔ không trượt ngang. */
.ncn-dong[b-ue4ifp33xa] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem;
    padding: .25rem 0;
    min-height: 2rem;               /* vùng chạm đủ cao cho ngón tay */
    border-bottom: 1px solid var(--bs-border-color-translucent);
}

/* Nút bung/thu — vuông, đủ rộng để bấm bằng ngón tay, ⛔ không phải một ký tự trần. */
.ncn-bung[b-ue4ifp33xa] {
    flex: 0 0 auto;
    width: 1.75rem;
    height: 1.75rem;
    line-height: 1;
    padding: 0;
    border: 0;
    border-radius: .25rem;
    background: transparent;
    color: var(--bs-secondary-color);
}

.ncn-bung:hover[b-ue4ifp33xa] {
    background: var(--bs-secondary-bg);
}

/* Nhánh cụt: giữ ĐÚNG chỗ của nút bung để mọi dòng cùng tầng thẳng cột — bỏ đi là cây răng cưa. */
.ncn-bung-trong[b-ue4ifp33xa] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: default;
}

/* Tên là thứ người ta tìm ⇒ cho nó co giãn, các ô còn lại giữ nguyên cỡ chữ của mình. */
.ncn-ten[b-ue4ifp33xa] {
    flex: 1 1 12rem;
    min-width: 0;
    font-weight: 600;
}

.ncn-ma[b-ue4ifp33xa],
.ncn-nguoi[b-ue4ifp33xa],
.ncn-han[b-ue4ifp33xa] {
    flex: 0 0 auto;
    white-space: nowrap;
}

.ncn-nut-lenh[b-ue4ifp33xa] {
    display: inline-flex;
    gap: .25rem;
    margin-left: auto;              /* dồn về mép phải khi còn chỗ */
}

/* Câu giải thích (bị chặn chia nhỏ · hạn vượt cha) — thụt bằng đúng bề ngang nút bung. */
.ncn-chan[b-ue4ifp33xa] {
    padding: .1rem 0 .25rem 1.75rem;
}

/* TẦNG DƯỚI — thụt vào một nấc, kèm vạch dọc để mắt lần được nhánh nào thuộc nhánh nào.
   ⚠ Thụt bằng `rem` chứ ⛔ không `%`: cây sâu 5 nấc mà thụt theo phần trăm thì tầng cuối chỉ còn
     vài ký tự bề ngang trên điện thoại. */
.ncn-tang[b-ue4ifp33xa] {
    list-style: none;
    margin: 0;
    padding-left: .9rem;
    border-left: 2px solid var(--bs-border-color);
}

@media (max-width: 575.98px) {
    /* Điện thoại: thụt nông hơn — 5 nấc × 0,9rem đã ăn mất một phần tư bề ngang màn hình. */
    .ncn-tang[b-ue4ifp33xa] {
        padding-left: .5rem;
    }

    .ncn-chan[b-ue4ifp33xa] {
        padding-left: 1.75rem;
    }
}
/* /Components/ChungTu/ODien.razor.rz.scp.css */
/* ===== ĐỢT 2274 (việc o1789) — MỘT Ô ĐIỀN =====
   Lý do từng luật nằm ở chú thích đầu ODien.razor; ở đây chỉ còn phép đo. */

.lde-o[b-7ga48s6hdd] {
    display: flex;
    flex-direction: column;
    min-width: 0;   /* thiếu dòng này là ô dài NỞ RA theo nội dung, phá cả lưới (bài học đợt 2198) */
}

/* Ô trọn hàng và ô hai cột: dùng vạch của lưới cha, ⛔ không gõ bề ngang bằng tay. */
.lde-o-rong[b-7ga48s6hdd] {
    grid-column: 1 / -1;
}

.lde-o-doi[b-7ga48s6hdd] {
    grid-column: span 2;
}

@media (max-width: 575.98px) {
    .lde-o-doi[b-7ga48s6hdd] {
        grid-column: 1 / -1;
    }
}

.lde-nhan[b-7ga48s6hdd] {
    display: block;
    margin-bottom: .25rem;
    font-size: .8125rem;
    line-height: 1.15rem;
    min-height: 1.15rem;   /* ô không có nhãn vẫn giữ đúng một dòng ⇒ đáy các ô còn thẳng */
    font-weight: 500;
    color: var(--bs-secondary-color, #6c757d);
}

.lde-sao[b-7ga48s6hdd] {
    margin-left: .15rem;
    color: var(--bs-danger, #dc3545);
}

/* 🔑 ĐÂY LÀ CHỖ LÀM CHO ĐÁY CÁC Ô THẲNG HÀNG: nhãn hai dòng thì phần thừa rơi vào khoảng trống
   GIỮA nhãn và ô, ⛔ không đẩy ô tụt xuống. */
.lde-than[b-7ga48s6hdd] {
    margin-top: auto;
}

/* Điều khiển do NGƯỜI GỌI đặt vào nên mang dấu cách ly của tệp khác ⇒ bắt buộc `::deep`. */
.lde-than[b-7ga48s6hdd]  .form-control,
.lde-than[b-7ga48s6hdd]  .form-select,
.lde-than[b-7ga48s6hdd]  textarea {
    width: 100%;
}

/* Ô ngày là một `span` inline-block rộng cố định 150px (app.css) — trong lưới thì nó ngắn hơn hẳn
   ô bên cạnh, mép phải so le. Ép về đúng bề ngang của cột. */
.lde-than[b-7ga48s6hdd]  .o-ngay-boc {
    display: block;
    width: 100%;
}

.lde-them[b-7ga48s6hdd] {
    margin-top: .2rem;
    font-size: .75rem;
    line-height: 1.1;
    color: var(--bs-secondary-color, #6c757d);
}
/* /Components/ChungTu/OGoTim.razor.rz.scp.css */
/* ⭐ ĐỢT 1968 (việc o1216) — bảng gợi ý của ô gõ-để-chọn.
   ⚠ Màu lấy từ token Bootstrap 5.3 (app nạp 5.3.8 từ đợt 831) nên tự đúng ở CẢ nền sáng lẫn nền tối
     — cầu nối data-bs-theme đặt ở App.razor. ⛔ Đừng gõ mã màu cứng vào đây. */

/* THẺ NEO chỉ ôm ô gõ: bảng đặt bằng `top: 100%` tính trên chính thẻ này. */
.ogt-neo[b-f7fdwjglqa] {
    position: relative;
}

/* 🔴 ⭐ ĐỢT 1970m (việc o1270) — BA SỐ Z LÀ MỘT BỘ, ⛔ ĐỪNG XÊ DỊCH MỘT MÌNH.
     nền phủ **4**  ·  ô gõ + hai nút **5**  ·  bảng gợi ý **6**
   🔑 Nền phủ là thứ làm nên vế ② của anh Tiến (*"nhấn ra ngoài danh sách sẽ đóng lại"*). Nó phủ kín
     khung nhìn, nên MỌI thứ người ta còn phải bấm được lúc bảng đang mở đều phải nằm TRÊN nó:
     · Hạ `.ogt-bang` xuống dưới 4 ⇒ bấm một dòng gợi ý là trúng nền ⇒ **bảng đóng mà ⛔ không chọn
       được ai** — đúng thứ hỏng mà đoạn 🔑 ở đầu `OGoTim.razor` dặn về bản `focusout`.
     · Quên nâng `.input-group` ⇒ ô gõ nằm DƯỚI nền ⇒ bấm lại vào ô là đóng bảng chứ ⛔ không mở lại,
       tức mất luôn vế ③ *"nhấn lại thì hiện ra"*. Cả hai hỏng LẶNG LẼ: build xanh, test xanh.
   ⚠ Ba số này CỐ Ý nhỏ — chúng chỉ cần thắng nhau và thắng nội dung liền kề, ⛔ không được trèo lên
     tầng hộp thoại (1055/1060 của `NenHopThoai`): ô này sống được cả trong `<HopThoai>`. */
.ogt-bang[b-f7fdwjglqa] {
    position: absolute;
    top: 100%;
    z-index: 6;
    left: 0;
    right: 0;
    background: var(--bs-body-bg, #fff);
    border-color: var(--bs-border-color, #dee2e6);
}

/* Ô gõ + nút ✕ + nút ▼ đứng TRÊN nền phủ khi bảng đang mở — xem khối 🔴 ngay trên.
   ⚠ Phải có `position: relative`: `z-index` ⛔ không có tác dụng nào trên một thẻ `static`, và thiếu
     nó thì luật này im lặng ⛔ không làm gì cả. */
.ogt-neo.ogt-mo .input-group[b-f7fdwjglqa] {
    position: relative;
    z-index: 5;
}

/* Dải thẻ đã chọn cũng phải bấm được lúc bảng đang mở (bỏ một thứ vừa chọn nhầm). */
.ogt-the-hang[b-f7fdwjglqa] {
    position: relative;
    z-index: 5;
}

/* 🩸 THẺ ĐÃ CHỌN PHẢI XUỐNG DÒNG TRONG LÒNG Ô — ⛔ đừng bỏ luật này.
   📏 ĐO SỐNG 04/09/2026 (đợt 1970k, khi dải thẻ còn nằm ở `KhoiMucTieuTrangChu`): khổ **1024px** ⇒
     thân hộp tràn ngang **18px**, thủ phạm là hai thẻ thiết bị rộng **272,2** và **283,6px** trong
     một ô chỉ **250,1px** (*"Hệ thống báo tràn C111-BT (Cần C111)"* — tên chở cả mã lẫn tên điểm).
   🔑 Gốc bệnh: `.badge` của Bootstrap là `d-inline-flex` một hàng, chữ ⛔ không tự xuống dòng ⇒ thẻ
     cứ dài ra mãi và đẩy tràn CẢ HỘP. `max-width: 100%` một mình ⛔ không đủ — thiếu
     `white-space: normal` thì chữ vẫn là một dòng liền và tràn ra khỏi khung thẻ.
   ⚠ `word-break` cho ca xấu nhất: một mã thiết bị dài hơn cả bề ngang ô (⛔ không có dấu cách nào để
     ngắt) — thiếu nó là tràn lại y như cũ ở khổ điện thoại.
   ⚠ `text-align: left` vì `.badge` căn giữa: thẻ hai dòng mà căn giữa thì dòng hai lệch hẳn.
   🪦 Luật này DỜI VỀ ĐÂY ở đợt 1970m (việc o1270) từ lớp `.kmt-the-chon` của
     `KhoiMucTieuTrangChu.razor.css`: dải thẻ nay do CHÍNH component này dựng, nên nơi gọi ⛔ không
     còn thẻ nào để trang điểm. ⛔ Đừng dựng lại một lớp thứ hai ở nơi gọi. */
.ogt-the[b-f7fdwjglqa] {
    max-width: 100%;
    white-space: normal;
    word-break: break-word;
    text-align: left;
}

/* 🩸 SỔ NGƯỢC LÊN LÀ LỰA CHỌN CỦA NƠI GỌI (`SoLen`), ⛔ KHÔNG phải luật chung của hộp thoại.
   🧨 ĐO SỐNG 04/09/2026 (đợt 1968) BÁC BỎ BẢN ĐẦU của chính đợt này: bản ấy chép nguyên luật
     `.modal-dialog-scrollable .modal-body .ogt-bang { bottom: 100% }` từ `ONhieuNguoi` (đợt 1670) và
     áp cho MỌI ô. Kết quả trên app thật: ô "mục tiêu cho ai" nằm ở HÀNG ĐẦU của hộp ⇒ bảng sổ ngược
     lên rơi **lên trên mép thân hộp** ⇒ bấm ▼ thấy nút đổi thành ▲ (tức bảng ĐANG MỞ) mà màn hình
     ⛔ KHÔNG hiện gì. Hỏng lặng lẽ đúng kiểu tệ nhất: build xanh, test xanh, nút có phản ứng.
   🔑 Luật đợt 1670 ĐÚNG cho ô của nó — hai ô ấy ở HÀNG CUỐI biểu mẫu, dưới chúng ⛔ không còn nội dung
     nào để cuộn tới. Điều kiện thật ⛔ không phải "có ở trong hộp thoại không" mà là "DƯỚI ô còn chỗ
     không" — thứ CSS ⛔ không đo được. ⇒ Để nơi gọi khai, mặc định SỔ XUỐNG. */
.ogt-bang.ogt-len[b-f7fdwjglqa] {
    top: auto;
    bottom: 100%;
}

.ogt-dau[b-f7fdwjglqa] {
    background: var(--bs-tertiary-bg, #f8f9fa);
}

/* Trần chiều cao chính là chữ CUỘN: hơn 400 tài khoản mà ⛔ không có trần thì bảng dài quá màn hình
   và nút bấm bên dưới bị đẩy khuất. */
.ogt-cuon[b-f7fdwjglqa] {
    max-height: 220px;
    overflow-y: auto;
}

.ogt-nhom[b-f7fdwjglqa] {
    background: var(--bs-tertiary-bg, #f8f9fa);
    font-weight: 500;
}

.ogt-dong[b-f7fdwjglqa] {
    display: block;
    cursor: pointer;
    line-height: 1.35;
}

.ogt-dong:hover[b-f7fdwjglqa],
.ogt-dong:focus[b-f7fdwjglqa] {
    background: var(--bs-secondary-bg, #e9ecef);
}

/* Dòng ĐANG CHỌN phải nhận ra được khi mở lại bảng — ⛔ không chỉ bằng màu: thêm dấu ✓ thì lại đổi bề
   ngang dòng, nên ở đây dùng màu nền ĐẬM hơn cộng chữ đậm (hai kênh, ⛔ không riêng màu). */
.ogt-dong.ogt-dang-chon[b-f7fdwjglqa] {
    background: var(--bs-secondary-bg, #e9ecef);
    font-weight: 600;
}

.ogt-ten[b-f7fdwjglqa] {
    font-weight: 500;
}

/* 🧨 ĐO SỐNG 04/09/2026 BẮT: thiếu khoảng cách thì hai mảnh DÍNH LIỀN thành một chuỗi vô nghĩa —
   *"Bùi Minh CảnhPhòng Bảo vệ - Phòng cháy chữa cháy"*. Component anh em `ONhieuNguoi` ⛔ không dính
   vì phép dựng chữ phụ của nó tự chèn *" — "* vào đầu chuỗi; ô này nhận chữ phụ từ NƠI GỌI nên khoảng
   cách phải do CSS lo, ⛔ không thể trông chờ mọi nơi gọi nhớ gõ dấu ngăn. */
.ogt-phu[b-f7fdwjglqa] {
    white-space: normal;
    margin-left: .45rem;
}

/* ═══ 📱 ĐỢT 2181 (việc o1624) — BẢN NGẮN CỦA CHỮ CHÌM, CHỈ Ở KHỔ ĐIỆN THOẠI ═══

   📏 Đo sống 375px (đợt 2176, việc o1620): lọt lòng của ô gõ còn **283px** ⇒ mọi câu chữ chìm của
     khung tạo nhiệm vụ bị cắt giữa chừng — người dùng đọc được *"Người liên quan — gõ tên hoặc ph…"*.
     Chữ chìm ở ô này ĐANG THAY CHO NHÃN (anh Tiến 03/09/2026) nên phần bị cắt ⛔ không phải trang trí.

   🔑 PHÉP CHỌN NẰM HẲN Ở ĐÂY, ⛔ KHÔNG Ở C# VÀ ⛔ KHÔNG Ở JS: máy chủ ⛔ không biết bề ngang trình
     duyệt, còn một lượt JS ghi đè thuộc tính `placeholder` thì Blazor vá lại DOM ở lượt dựng kế tiếp
     và xoá mất — hỏng lặng lẽ, ⛔ không câu báo nào. Cả hai câu cùng đi ra DOM, CSS quyết ai hiện.

   🔴 HAI LUẬT DƯỚI ĐÂY LÀ MỘT CẶP, ⛔ ĐỪNG TÁCH: một luật giấu câu dài, một luật bày câu ngắn. Giữ
     luật giấu mà bỏ luật bày là ô **trắng trơn** trên điện thoại. Vì thế cả hai cùng đi qua đúng một
     cửa `:placeholder-shown` và đúng một lớp `.ogt-co-ngan` — trình duyệt đời cũ ⛔ không hiểu
     `:placeholder-shown` thì **bỏ CẢ HAI** ⇒ câu dài hiện y như trước đợt này (chiều fail ĐÚNG).

   🔴 LỚP `.ogt-co-ngan` DO C# ĐÓNG VÀO, và nó chở một chốt ⛔ không thể viết bằng CSS: lúc bảng MỞ mà
     ô một giá trị đã có lựa chọn thì `placeholder` là chính TÊN ĐANG CHỌN — phủ câu ngắn lên lúc ấy
     là xoá mất câu trả lời *"tôi đang chọn cái gì"*. Xem `OGoTim.razor.cs`, `ChuChimNganDangHien`.

   ⚠ `z-index: 6` là CỐ Ý và nằm trong bộ ba ở khối 🔴 đầu tệp: Bootstrap nâng ô gõ lên **5** khi nó
     giữ con trỏ, nên thẻ phủ để mặc là **chìm xuống dưới nền ô** đúng lúc người ta vừa bấm vào —
     ô hoá trắng trơn. Số 6 ở đây ⛔ không trèo lên bảng gợi ý: lúc bảng mở, `.input-group` mang
     `z-index: 5` nên nó dựng một ngữ cảnh xếp chồng và nuốt trọn con số này.

   ⚠ `pointer-events: none` BẮT BUỘC — thiếu nó là thẻ phủ nuốt cú bấm vào ô, tức ô ⛔ không mở được
     bảng gợi ý, mà `title` và mọi thứ khác vẫn trông bình thường.

   📐 Mốc 576px chính là `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx` — mọi khung của trang chủ
     đổi dáng ở CÙNG một bề ngang. Đổi mốc thì đổi ở luật ấy rồi rà lại các tệp `.razor.css` của
     trang chủ, ⛔ đừng sửa lẻ một tệp. */

/* Luật NỀN: máy tính ⛔ không bao giờ thấy thẻ phủ. ⛔ Đừng đổi thành `visibility` — thẻ vẫn chiếm chỗ
   trong `input-group` và đẩy hai nút ✕ ▼ ra khỏi khung ở khổ hẹp. */
.ogt-chu-ngan[b-f7fdwjglqa] {
    display: none;
}

@media (max-width: 576px) {

    .ogt-o.ogt-co-ngan:placeholder-shown[b-f7fdwjglqa]::placeholder {
        color: transparent;
    }

    .ogt-o.ogt-co-ngan:placeholder-shown ~ .ogt-chu-ngan[b-f7fdwjglqa] {
        display: block;
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        left: 0;
        /* Chừa chỗ cho hai nút ✕ ▼ ở cuối ô — thẻ phủ ⛔ không được chạy dưới chúng, kẻo chữ ngắn
           đâm vào biểu tượng và cả hai cùng khó đọc. */
        max-width: calc(100% - 4.5rem);
        /* 1px là bề dày viền ô: thiếu nó thì câu ngắn lệch trái so với chữ người ta sắp gõ vào. */
        padding-left: calc(.5rem + 1px);
        z-index: 6;
        pointer-events: none;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: .875rem;
        color: var(--bs-secondary-color, #6c757d);
    }
}
/* /Components/ChungTu/ONhieuNguoi.razor.rz.scp.css */
/* ⭐ ĐỢT 1417 (việc o611) — bảng gợi ý CUỘN của ô chọn nhiều người.
   ⚠ Màu lấy từ token Bootstrap 5.3 (app đã nạp 5.3.8 từ đợt 831) nên tự đúng ở CẢ nền sáng lẫn nền
     tối — cầu nối data-bs-theme đặt ở App.razor. ⛔ Đừng gõ mã màu cứng vào đây. */

/* ⭐ ĐỢT 1670 (việc o941) — THẺ NEO của bảng gợi ý.
   Trước đợt này thẻ neo là chính `.onn`, tức khối ôm cả DẢI THẺ 👤 nằm dưới ô gõ. Nay neo hẹp lại
   còn NHÃN + Ô GÕ, vì bảng phải đặt được theo CẢ HAI chiều và mỗi chiều đo từ một mép của thẻ neo:
     · sổ XUỐNG  — `top: 100%`    ⇒ mép DƯỚI thẻ neo, tức ngay dưới ô gõ.
     · sổ LÊN    — `bottom: 100%` ⇒ mép TRÊN thẻ neo, tức ngay trên NHÃN.
   ⛔ Đừng nới thẻ neo ra ôm luôn dải thẻ 👤: lối sổ xuống sẽ rơi xuống DƯỚI dải ấy, cách hẳn ô gõ.
   ⛔ Đừng thu thẻ neo lại còn mỗi ô gõ: lối sổ lên sẽ PHỦ MẤT NHÃN, mà nhãn là câu duy nhất nói cho
     người dùng biết mình đang chọn người cho ô nào (hai ô đứng cạnh nhau, giống hệt nhau). */
.onn-neo[b-qe0dryx9dg] {
    position: relative;
}

/* Bảng gợi ý NỔI LÊN TRÊN, ⛔ không đẩy các ô bên dưới xuống: hộp thoại có sáu ô xếp thành cột, bảng
   chiếm chỗ thật là mỗi lần gõ tên cả biểu mẫu lại nhảy một nhịp. */
/* 🔴 ⭐ ĐỢT 2008b (việc o1292) — BA SỐ Z LÀ MỘT BỘ, ⛔ ĐỪNG XÊ DỊCH MỘT MÌNH.
     nền phủ 4  ·  ô gõ + hai nút + dải thẻ 👤 là 5  ·  bảng gợi ý 6
   🔑 Anh Tiến 05/09/2026: *"danh sách sổ xuống không tự đóng khi nhấn ra ngoài"*. Đường đóng mới là
     một NỀN PHỦ trong suốt phủ kín khung nhìn (thẻ NenHopThoai dựng trong nhánh _dangMo). Nền phủ
     nuốt mọi cú bấm nằm dưới nó, nên MỌI thứ người ta còn phải bấm được lúc bảng đang mở đều phải
     nằm TRÊN nó:
     · Hạ bảng gợi ý xuống dưới 4 ⇒ bấm một dòng là trúng nền ⇒ bảng ĐÓNG mà ⛔ không chọn được ai —
       đúng thứ hỏng mà điều cấm focusout của đợt 1417 nói tới, chỉ khác cách sinh ra.
     · Quên nâng ô gõ và hai nút ⇒ bấm lại vào ô là ĐÓNG chứ ⛔ không mở lại, tức mất hẳn vế
       *"nhấn lại thì hiện ra"* (thao tác VaoO của luật thuần thành ra ⛔ không bao giờ chạy).
     · Quên nâng dải thẻ 👤 ⇒ bỏ một người vừa thêm nhầm phải đóng bảng trước, thừa một thao tác.
     Cả ba đều hỏng LẶNG LẼ: build xanh, test xanh, màn hình trông vẫn có lý.
   ⚠ Con số của bảng gợi ý ĐỔI 5 → 6 ở đợt này, cố ý: 5 nay là bậc của ô gõ, để hai thứ ngang nhau
     rồi trông chờ THỨ TỰ DOM phân xử là một chốt ⛔ không ai đọc ra khi sửa sau này.
   ⚠ Ba số CỐ Ý nhỏ — chúng chỉ cần thắng nhau và thắng nội dung liền kề, ⛔ không được trèo lên tầng
     hộp thoại (1055/1060 của NenHopThoai): ô này sống được cả trong HopThoai.
   ⓘ Trong hộp thoại, cú bấm ra ngoài đóng BẢNG trước và hộp thoại VẪN MỞ (phải bấm thêm một lượt
     nữa mới đóng hộp). Đó là hành vi đúng — ⛔ đừng đi "chữa" nó bằng cách hạ nền xuống. */
.onn-bang[b-qe0dryx9dg] {
    position: absolute;
    top: 100%;
    z-index: 6;
    left: 0;
    right: 0;
    background: var(--bs-body-bg, #fff);
    border-color: var(--bs-border-color, #dee2e6);
}

/* Ô gõ + nút 👤 + nút mũi tên, và dải thẻ 👤 đã chọn, đứng TRÊN nền phủ khi bảng đang mở.
   ⚠ Phải có position relative: z-index ⛔ không có tác dụng nào trên một thẻ tĩnh, thiếu nó là luật
     này im lặng ⛔ không làm gì cả (Bootstrap đã cho input-group relative, nhưng dải thẻ thì KHÔNG —
     và ⛔ đừng dựa vào một dòng của thư viện bên ngoài để giữ tính năng của mình).
   🔴 Lớp mốc onn-mo là CÓ ĐIỀU KIỆN, ⛔ đừng nâng thường trực. Phiếu lập dự án dựng BỐN ô này: nâng
     hết thì lúc ô A đang mở, cú bấm vào ô B rơi thẳng xuống ô B mà nền của A ⛔ không hề nhận được
     cú bấm nào ⇒ HAI bảng cùng bung — đúng lỗi đợt 1471 đã chữa cho ba bảng thả của thanh nav.
     Để nguyên như đây thì cú bấm ấy đóng A; bấm lượt nữa mới mở B. */
.onn.onn-mo .input-group[b-qe0dryx9dg],
.onn.onn-mo .onn-the-hang[b-qe0dryx9dg] {
    position: relative;
    z-index: 5;
}

/* 🩸 ĐỢT 1670 (việc o941) — TRONG HỘP THOẠI CÓ THÂN CUỘN THÌ BẢNG SỔ NGƯỢC LÊN.
   Đo sống 01/09/2026 (đợt 1636, việc o929) bằng cong-cu/do-song-o-goi-y-nhieu-nguoi.mjs, tài khoản
   tienvm, trang /cong-viec: lối bấm-dòng-gợi-ý CHẾT HẲN — `document.elementFromPoint` tại điểm giữa
   dòng đầu tiên trả về `div.modal-footer`, tức cú bấm của người dùng ⛔ KHÔNG tới nút dòng. Tái hiện
   ở CẢ BA khung 1400x950, 1400x1300, 1400x700.

   🔑 GỐC BỆNH ⛔ KHÔNG PHẢI Z-INDEX, và đây là chỗ dễ đi chữa nhầm nhất. `HopThoai` mặc định bật
     `CuonTrongThan` ⇒ khung mang lớp `modal-dialog-scrollable` ⇒ Bootstrap đặt
     `.modal-body { overflow-y: auto }`. Một khối `position: absolute` bị CẮT bởi mọi tổ tiên có
     overflow nằm giữa nó và thẻ neo của nó — mà thẻ neo ở đây nằm TRONG thân hộp. Đẩy z-index lên
     bao nhiêu cũng vô ích: thứ vẽ đè lên bảng là mép cắt, ⛔ không phải một lớp nào cao hơn.

   🔴 VÌ SAO SỔ LÊN CHỨ ⛔ KHÔNG PHẢI CHO BẢNG VÀO DÒNG CHẢY (position: static) — hai lối đều hết bị
     cắt, nhưng chúng KHÁC HẲN nhau ở chỗ có phải cuộn thêm hay không:
       · Sổ XUỐNG (bản cũ): bảng chiếm khoảng nội dung nằm NGOÀI chiều cao cuộn được của thân hộp
         ⇒ cuộn hết cỡ vẫn ⛔ không tới — đo được `scrollHeight - clientHeight` = 76px trong khi bảng
         cao ~250px. Đây là ca ⛔ KHÔNG CÓ ĐƯỜNG NÀO chạm tới, tức lỗi thật.
       · Vào DÒNG CHẢY: hết cắt, nhưng hai ô này nằm ở HÀNG CUỐI biểu mẫu nên bảng vẫn rơi xuống dưới
         mép nhìn thấy — người dùng phải tự cuộn xuống mới bấm được, và cả biểu mẫu nhảy một nhịp mỗi
         lượt mở (đúng thứ đợt 1417 cố ý tránh).
       · Sổ LÊN: bảng nằm gọn trong khoảng nội dung ĐÃ CÓ ở phía trên (ba hàng biểu mẫu, luôn dày hơn
         chiều cao bảng) ⇒ thấy ngay, ⛔ không phải cuộn, ⛔ không nhảy hàng.
   ⚠ Điều kiện `.modal-dialog-scrollable` CỐ Ý HẸP: ngoài hộp thoại thì ⛔ không có mép cắt nào, mà sổ
     lên vô cớ là bắt mắt người dùng đi ngược chiều đọc. Dùng component này ở chỗ mới thì đo lại,
     ⛔ đừng chép luật này sang.
   ⓘ CSS cách ly gắn dấu phạm vi vào bộ chọn CUỐI ⇒ hai lớp tổ tiên là lớp THẬT của Bootstrap và của
     `HopThoai`, ⛔ không cần `::deep`. Độ đặc hiệu (0,4,0) thắng luật nền (0,2,0) bất kể thứ tự. */
.modal-dialog-scrollable .modal-body .onn-bang[b-qe0dryx9dg] {
    top: auto;
    bottom: 100%;
}

.onn-dau[b-qe0dryx9dg] {
    background: var(--bs-tertiary-bg, #f8f9fa);
}

/* Trần chiều cao là thứ làm nên chữ CUỘN anh Tiến xin — hơn 400 tài khoản mà không có trần thì bảng
   dài quá màn hình và nút Tạo nhiệm vụ bị đẩy khuất. */
.onn-cuon[b-qe0dryx9dg] {
    max-height: 220px;
    overflow-y: auto;
}

.onn-dong[b-qe0dryx9dg] {
    display: block;
    cursor: pointer;
    line-height: 1.35;
}

.onn-dong:hover[b-qe0dryx9dg],
.onn-dong:focus[b-qe0dryx9dg] {
    background: var(--bs-secondary-bg, #e9ecef);
}

.onn-ten[b-qe0dryx9dg] {
    font-weight: 500;
}

.onn-phu[b-qe0dryx9dg] {
    white-space: normal;
}
/* /Components/ChungTu/SoDienBienNhiemVu.razor.rz.scp.css */
/* ═══ SỔ DIỄN BIẾN — DÒNG THỜI GIAN DỌC · ĐỢT 1336 (việc o536) ═══
   Anh Tiến 26/08/2026: "trình bày và hiệu ứng dạng khác em nhé" (gửi kèm ảnh huy hiệu số thứ tự bị
   kéo giãn thành hình bầu dục cao).

   GỐC của cái bầu dục: huy hiệu là phần tử flex trong một dòng d-flex, mà mặc định flex là CĂNG cho
   bằng chiều cao dòng. Dòng nào có ghi chú dài thì huy hiệu kéo dài theo. Chữa đúng gốc là cho chấm
   mốc một cỡ CỐ ĐỊNH và đặt tuyệt đối, chứ không phải nới thêm min-width.

   MÀU lấy từ token --bs-* của Bootstrap 5.3 nên sổ tự đúng ở CẢ hai giao diện sáng và tối.
   KHÔNG gõ mã màu cứng.

   HIỆU ỨNG — KHUNG HÌNH ĐẦU PHẢI CÒN NHÌN THẤY ĐƯỢC (bài học đợt 1323, đã trả giá thật): các dòng
   chỉ TRƯỢT NHẸ và SÁNG DẦN từ opacity .55, KHÔNG bao giờ từ 0. Ở những lúc trình duyệt không dựng
   khung hình (tab chạy nền, máy yếu, lượt chụp bản tĩnh) thì đồng hồ animation đứng ở khung đầu —
   bắt đầu từ 0 là cả sổ trắng trơn mà build xanh, test xanh, không câu lỗi nào. */

.sdb-doc[b-8bq87v48ws] {
    --sdb-co-cham: 24px;
    --sdb-le-ray: calc(var(--sdb-co-cham) / 2);
    position: relative;
    padding-left: calc(var(--sdb-co-cham) + 12px);
}

/* Đường ray dọc nối các mốc — thứ nói "đây là một dòng chảy", chứ không phải bốn dòng rời nhau. */
.sdb-doc[b-8bq87v48ws]::before {
    content: "";
    position: absolute;
    left: var(--sdb-le-ray);
    top: 6px;
    bottom: 6px;
    width: 2px;
    transform: translateX(-1px);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--bs-primary) 45%, transparent) 0%,
        color-mix(in srgb, var(--bs-body-color) 14%, transparent) 100%);
}

.sdb-muc[b-8bq87v48ws] {
    position: relative;
    margin-bottom: .6rem;
    animation: sdb-truot-vao-b-8bq87v48ws .45s ease-out both;
    animation-delay: var(--sdb-tre, 0s);
}

.sdb-muc:last-child[b-8bq87v48ws] {
    margin-bottom: 0;
}

/* ─────────── Chấm mốc ─────────── */
.sdb-cham[b-8bq87v48ws] {
    position: absolute;
    left: 0;
    top: .35rem;
    width: var(--sdb-co-cham);
    height: var(--sdb-co-cham);
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: .72rem;
    font-weight: 600;
    line-height: 1;
    box-shadow: 0 0 0 3px var(--bs-body-bg);
    cursor: help;
    z-index: 2;
}

/* 👆 ĐỢT 2192 (việc o1634) — SÀN CỠ CHỮ CHO NGÓN TAY.
   `.badge` của `app.css` đã có sàn `max(.75em, var(--cham-chu-toi-thieu))` trong khối
   `@media (pointer: coarse)`, nhưng luật ấy chỉ mang độ đặc hiệu (0,1,0) còn `.sdb-cham` ở tệp cách
   ly này biên dịch ra `.sdb-cham[b-…]` (0,2,0) ⇒ nó THẮNG và chấm mốc giữ nguyên 0,72rem = 11,52px.
   📏 Đo sống 375×812 trên hồ sơ 1000058 (3 mốc): 3 chấm đều ra 11,52px, dưới sàn
     `--cham-chu-toi-thieu: 12px`. Chấm rộng 24px nên 12px vẫn nằm gọn, ⛔ không phải nới chấm.
   ⛔ Đừng gõ thẳng `12px`: ngưỡng ở ĐÚNG MỘT CHỖ là biến `--cham-chu-toi-thieu` (luật đợt 1518/1521).
   ⛔ Đừng bỏ vế `max()`: máy tính giữ nguyên 0,72rem, và chốt ngược ở chế độ chuột đo lại phải ra
     ĐÚNG 11,52px như trước. */
@media (pointer: coarse) {
    .sdb-cham[b-8bq87v48ws] {
        font-size: max(.72rem, var(--cham-chu-toi-thieu));
    }
}

/* Mốc MỚI NHẤT đeo một vòng sáng đứng yên — nó trả lời "hồ sơ đang dừng ở đâu".
   Vòng này CỐ Ý không nhấp nháy: sổ diễn biến là chỗ đọc chậm, một chấm nhảy múa ở đây chỉ kéo mắt
   khỏi phần chữ. */
.sdb-muc-moi .sdb-cham[b-8bq87v48ws] {
    box-shadow:
        0 0 0 3px var(--bs-body-bg),
        0 0 0 6px color-mix(in srgb, var(--bs-primary) 22%, transparent);
}

/* ─────────── Thẻ nội dung ─────────── */
.sdb-the[b-8bq87v48ws] {
    padding: .4rem .6rem;
    border-radius: .5rem;
    background: color-mix(in srgb, var(--bs-body-color) 4%, transparent);
    border-left: 3px solid color-mix(in srgb, var(--bs-body-color) 12%, transparent);
    transition: background-color .18s ease, border-color .18s ease, transform .18s ease;
}

.sdb-muc:hover .sdb-the[b-8bq87v48ws] {
    background: color-mix(in srgb, var(--bs-primary) 8%, transparent);
    border-left-color: color-mix(in srgb, var(--bs-primary) 55%, transparent);
    transform: translateX(2px);
}

.sdb-ghi-chu[b-8bq87v48ws] {
    white-space: pre-wrap;
}

@keyframes sdb-truot-vao-b-8bq87v48ws {
    from { opacity: .55; transform: translateX(-6px); }
    to { opacity: 1; transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
    .sdb-muc[b-8bq87v48ws] { animation: none; }
    .sdb-the[b-8bq87v48ws] { transition: none; }
    .sdb-muc:hover .sdb-the[b-8bq87v48ws] { transform: none; }
}

/* ─────────── ⭐ SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY (việc o1519) ───────────
   `.sdb-cham` đo tĩnh .72rem = 11,52px, dưới sàn `--cham-chu-toi-thieu` (12px, khai ở `app.css`).
   📏 ⛔ Không tràn: chấm mốc là hình tròn `--sdb-co-cham` = 24px và chỉ chứa ĐÚNG MỘT ký tự (số chặng,
      «✏» hoặc «↩») — một ký tự 12px rộng chừng 8px nên vẫn nằm gọn giữa vòng tròn; chấm lại
      `position: absolute` nên nâng cỡ ⛔ không đẩy được thẻ nội dung bên phải.
   🔑 Đọc BIẾN, ⛔ đừng gõ thẳng 12px. ⛔ CHỈ trong `@media (pointer: coarse)` — xem `app.css` (đợt 1518). */
@media (pointer: coarse) {
    .sdb-cham[b-8bq87v48ws] { font-size: max(.72rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/ChungTu/ThanhPhanTrang.razor.rz.scp.css */
/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   DẢI PHÂN TRANG DÙNG CHUNG — VÙNG CHẠM CHO NGÓN TAY (đợt o1276, 05/09/2026)

   📏 ĐO SỐNG TRƯỚC KHI SỬA — Chrome thật, khung 390×844, chế độ cảm ứng, đăng nhập `tienvm`:
      `node cong-cu/do-song-khung-hep.mjs --trang /du-an` ⇒ **8 vùng bấm nhỏ**, cả 8 là
      `button.page-link` đo **25,8×31px** (‹ Trước · 1 … 6 · Sau ›). Trang `/quan-tri/nhat-ky` mang
      đúng 8 chỗ y hệt — vì cả hai chỉ dùng CHUNG một thẻ này. Ngưỡng ngón tay là 44px (biến
      `--cham-toi-thieu` ở `app.css`, khối đợt 1518).

   🔑 VÌ SAO LÀ TỆP CSS CÁCH LY CỦA CHÍNH THẺ, ⛔ KHÔNG PHẢI MỘT LUẬT CHUNG Ở app.css:
      lớp `page-link` chỉ xuất hiện ở ĐÚNG MỘT tệp `.razor` của cả app — tệp này. Đặt luật ở
      `app.css` là hứa với mọi dải phân trang tương lai, mà hôm nay ⛔ chưa có cái nào để hứa cùng.
      Ngày có tệp thứ hai dùng `page-link` thì DỜI khối này lên `app.css` (khuôn `.btn` ở đó),
      ⛔ đừng chép thành bản thứ hai.

   ⚠ NỚI NÚT LÊN 44px THÌ DẢI PHẢI ĐƯỢC XUỐNG DÒNG: `.pagination` của Bootstrap là `display: flex`
      ⛔ không wrap, mà 9 nút × 44px = 396px đã hơn cả khung 390 — thiếu dòng `flex-wrap` là
      trang TRÀN NGANG, một lỗi nặng hơn lỗi đang chữa. Đo sau khi sửa phải ra tràn ngang **0**.
   ⚠ `min-height`/`min-width` chứ ⛔ không `height`/`width`: nút «‹ Trước» rộng hơn 44px vốn dĩ,
      ép `width` là cắt chữ.
   ⚠ `inline-flex` + canh giữa: nút của Bootstrap là `display: block` với đệm nhỏ; nới `min-height`
      mà ⛔ không canh giữa thì con số dính mép trên, dưới là khoảng trống — trông như nút hỏng.
   🔒 Toàn bộ nằm trong `@media (pointer: coarse)`: máy tính (chuột) ⛔ không đổi một pixel —
      chốt ngược bằng `do-song-khung-hep.mjs --chuot` trước và sau phải TRÙNG KHÍT.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — ⛔ đừng viết cặp ấy vào giữa câu văn
      (bài học đợt 852, hàng rào ChuThichCssDongSomTests).
   🚧 Hàng rào: TKNB.Tests/VungChamPhanTrangVaThongKeTests.cs
   📏 Đo lại: node cong-cu/do-song-khung-hep.mjs --trang /du-an   (chốt ngược: thêm --chuot) */

@media (pointer: coarse) {
    .pagination[b-xjsjlp4t49] {
        flex-wrap: wrap;
        row-gap: .25rem;
    }

    .page-link[b-xjsjlp4t49] {
        min-height: var(--cham-toi-thieu);
        min-width: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/CoDien/IconApp.razor.rz.scp.css */
/* ═════════ CỠ ICON NÉT MẢNH CỦA LƯỚI APP (đợt 598) ═════════

   ⚠ VÌ SAO CÓ TỆP RIÊNG, KHÔNG GỘP VÀO MenuApp.razor.css: CSS cách ly của Blazor gắn thuộc tính phạm vi
     THEO TỪNG COMPONENT. Thẻ <svg> do IconApp.razor vẽ ra mang phạm vi của IconApp, nên luật viết trong
     MenuApp.razor.css KHÔNG với tới nó (phải dùng ::deep mới với được — mà ::deep thì lại xuyên qua ranh
     giới component, đúng thứ nên tránh). Component nào tự lo màu/cỡ của component đó.

   ⭐ ĐỢT 648 — MÀU RIÊNG TỪNG APP nay khai ở CUỐI TỆP NÀY (anh Tiến 14/08/2026: *"app menu sử dụng icon
     có màu nhé"*). Câu cũ ở đây ghi *"MÀU thì CỐ Ý không khai ở đây"* — nay đã lạc hậu, nhưng LÝ DO của
     nó thì vẫn được giữ trọn: màu đặt bằng thuộc tính `color` của thẻ <svg>, còn nét vẫn
     `stroke="currentColor"` ⇒ vẫn có nhánh nền tối, vẫn tự mờ khi ô ở nhóm "Coming soon" (bỏ lớp màu). */

.ma-net[b-xa18d6dyeu] {
    /* Cỡ 26px: ô lưới rộng 112px, icon lớn hơn nữa là chữ tên app bị đẩy xuống rớt dòng. */
    width: 26px;
    height: 26px;
    display: block;
    /* Nét mảnh không được co giãn méo khi ô hẹp lại. */
    flex: none;
}

/* ĐƯỜNG LUI khi app chưa có nét vẽ riêng — giữ đúng cỡ chỗ của icon để lưới không nhảy dòng. */
.ma-o-bieu[b-xa18d6dyeu] {
    font-size: 1.55rem;
    line-height: 26px;
    height: 26px;
    display: block;
}

/* Lớp bọc CHỈ ĐỂ MANG MÀU cho ô Camera (component IconCameraPtz nằm ngoài phạm vi CSS của tệp này —
   `color` là thuộc tính di truyền nên xuyên qua được, `width`/`height` thì không).
   `line-height: 0` để chiếc <span> không cao hơn chính cái icon nó bọc, kẻo lưới lệch một dòng. */
.ma-boc-mau[b-xa18d6dyeu] {
    display: block;
    line-height: 0;
    flex: none;
}

/* ═════════════ ĐỢT 648 — MỖI APP MỘT MÀU RIÊNG ═════════════
   Anh Tiến 14/08/2026: *"app menu sử dụng icon có màu nhé"*.

   🎨 CÁCH ĐẶT MÀU: chỉ đặt thuộc tính `color` của thẻ <svg>; nét vẫn `stroke="currentColor"` nên nó lấy
      đúng màu này. Ba cái lợi của `currentColor` vì thế KHÔNG mất cái nào:
        · nền tối có bộ màu riêng (khối thứ hai bên dưới) — cùng một chỗ khai;
        · ô "Coming soon" chỉ cần BỎ lớp màu (tham số `Tat` của IconApp) là icon tự mờ theo màu chữ;
        · không thẻ <path> nào bị ghim mã màu ⇒ sửa bảng màu chỉ sửa ở đây.

   📏 TƯƠNG PHẢN — TÍNH BẰNG SỐ, KHÔNG ƯỚM MẮT (đo 14/08/2026 bằng công thức WCAG 2.x, lấy giá trị THẤP
      NHẤT trong các nền mà icon có thể nằm lên):
        nền SÁNG: #ffffff (bảng lưới bản cổ điển) và #eef2f9 (đáy dốc của ô ở 3 giao diện hiện đại)
        nền TỐI : #292a2d (bảng lưới) và #2d323a (đỉnh dốc của ô)
      Ngưỡng dự án cho ĐỒ HỌA là 3:1. Thấp nhất đo được: **4,01 nền sáng · 5,40 nền tối** ⇒ đạt, và còn
      vượt cả ngưỡng 4,5:1 của chữ thường. Số của từng app ghi ngay cuối dòng.
   ⚠ Đổi một mã màu thì ĐO LẠI rồi sửa luôn con số ở dòng đó — hàng rào `MenuAppTests` (mục 7) tự đọc
      chính tệp này, tự tính lại tương phản và báo ĐỎ nếu tụt dưới 3:1, nên số ghi sai sẽ bị bắt.
   ⚠ Thêm app mới mà quên khai màu cũng ĐỎ (icon sẽ lạc lõng vì ăn màu chữ giữa một lưới icon có màu). */

.ma-mau-tong-quan[b-xa18d6dyeu]   { color: #1a73e8; } /* xanh dương — 4,01 */
.ma-mau-cong-viec[b-xa18d6dyeu]   { color: #c65200; } /* cam đất    — 4,06 */
/* Chàm (sắc độ ~257°) cho 🏗 Dự án và ô liu sẫm (~57°) cho 🔧 Sửa chữa / Mua sắm — đợt 2234, việc
   o1709. CỐ Ý chọn theo khoảng trống của ĐÚNG NHÓM «Phòng nghiệp vụ» mà hai ô đứng: nhóm ấy đang có
   217° (xanh dương, Tổng quan) · 25° (cam đất, Nhiệm vụ) · 200° (xám thép, Quản lý phòng). 257° nằm
   giữa quãng rỗng 217→287 (tím lan), còn 57° nằm giữa quãng 39 (vàng dầu) → 76 (xanh rêu).
   ⛔ KHÔNG cho hai ô mới màu cam/nâu "cho hợp với hồ sơ": chúng vừa tách khỏi ô 🗃 cam đất và đứng
   NGAY CẠNH nó trên lưới — ba ô cùng dải cam ở cỡ 26px là mất sạch tác dụng phân biệt của màu. */
.ma-mau-du-an[b-xa18d6dyeu]       { color: #4527a0; } /* chàm       — 9,12 */ /* đợt 2234 */
.ma-mau-sua-chua[b-xa18d6dyeu]    { color: #827717; } /* ô liu sẫm  — 4,07 */ /* đợt 2234 */
.ma-mau-quan-ly-phong[b-xa18d6dyeu] { color: #37474f; } /* xám thép  — 8,59 */ /* đợt 834 */
.ma-mau-cham-cong[b-xa18d6dyeu]   { color: #188038; } /* xanh lá    — 4,47 */
.ma-mau-an-ninh[b-xa18d6dyeu]     { color: #d93025; } /* đỏ         — 4,25 */
.ma-mau-mang[b-xa18d6dyeu]        { color: #00796b; } /* lam ngọc   — 4,74 */
.ma-mau-nuoc[b-xa18d6dyeu]        { color: #0277bd; } /* xanh nước  — 4,27 */
.ma-mau-cong-nghe[b-xa18d6dyeu]   { color: #8a5a00; } /* vàng dầu   — 5,28 */ /* đợt 752 */
.ma-mau-cong-nghe-pccc[b-xa18d6dyeu] { color: #c62828; } /* đỏ chữa cháy — 5,63 */ /* đợt 913 */
.ma-mau-nhan-su[b-xa18d6dyeu]     { color: #7b1fa2; } /* tím lan    — 7,31 */ /* đợt 777 */
/* Xanh lá mạ (sắc độ ~100°) — CỐ Ý chọn khoảng trống lớn nhất còn lại của bảng màu: hai màu lục đang
   dùng nằm ở 76° (xanh rêu, Công cụ) và 143° (xanh lá, Chấm công), nên ô này đứng đúng giữa và không
   lẫn với bên nào ở cỡ icon 26px. */
.ma-mau-hanh-chinh[b-xa18d6dyeu]  { color: #3f7d20; } /* xanh lá mạ — 4,49 */ /* đợt 1443 */
/* Tím đỗ quyên (sắc độ ~311°) — CỐ Ý chọn KHOẢNG TRỐNG LỚN NHẤT còn lại của bảng màu: đo sắc độ của
   22 màu đang dùng thì quãng rỗng dài nhất là 287° (tím lan, Nhân sự) → 336° (hồng sen, Trình chiếu),
   và 311° đứng đúng giữa. ⛔ KHÔNG chọn màu lam ngọc "kiểu bệnh viện" như phản xạ đầu tiên: dải
   170–200° đã có BA app (Quản trị mạng · CN nhập xuất · Mực in), thêm cái thứ tư là bốn ô xanh lơ
   cạnh nhau — ở cỡ icon 26px thì màu hết tác dụng phân biệt, mà đó là toàn bộ lý do icon có màu. */
/* Chàm tím (sắc độ ~242°) cho 📆 Quản lý lịch — CỐ Ý chọn theo khoảng trống của ĐÚNG NHÓM nó
   đứng, y như 🚐 Tổ xe ngay dưới. Nhóm 🗄 Hành chính đang có sắc độ 30 · 100 · 138 · 202 · 282 ·
   311 · 339, tức quãng rỗng dài nhất trong nhóm là 202°→282° (rộng 80°) và 242° đứng đúng giữa,
   cách ô gần nhất TRONG NHÓM tới 40°.
   ⚠ Cả bảng có hai màu lân cận (232° Thử nghiệm · 247° Tài khoản) nhưng cả hai thuộc NHÓM KHÁC và
     đều là tông TRUNG; màu này tương phản 6,73 so với 7,74 và 5,63 của chúng, nên vẫn tách được khi
     lưới bày nhiều nhóm cùng lúc — cùng lý lẽ đã ghi cho 🚐 Tổ xe.
   ⛔ KHÔNG lấy khoảng trống 138°→202° (mid ~170°): dải 170–200° đã có BỐN ô (Quản trị mạng · Mực in ·
     Quản lý phòng · Quản trị nước) — ở cỡ icon 26px thì thêm một ô xanh lơ nữa là màu hết tác dụng
     phân biệt, mà đó là toàn bộ lý do icon có màu. */
.ma-mau-quan-ly-lich[b-xa18d6dyeu]{ color: #4d49ca; } /* chàm tím   — 6,73 */ /* đợt 2277 */
.ma-mau-y-te[b-xa18d6dyeu]        { color: #a11287; } /* tím đỗ quyên — 6,33 */ /* đợt 1696 */
/* Cam cháy sẫm (sắc độ ~30°) cho 🚐 Tổ xe — CỐ Ý chọn theo khoảng trống của ĐÚNG NHÓM nó đứng, chứ
   không theo khoảng trống của cả bảng. Nhóm 🗄 Hành chính đang có sáu ô ở sắc độ 96 · 143 · 202 · 287
   · 311 · 340, tức cả một cung 340°→96° (rộng 116°) bỏ trống; 30° nằm gần giữa cung ấy nên cách ô gần
   nhất TRONG NHÓM tới 58°. Khoảng trống lớn nhất của cả bảng lại là 96°→143° (mid ~120° = xanh lá
   nguyên) — ⛔ KHÔNG lấy: nhóm này đã có HAI màu xanh lá, mà ở cỡ icon 26px thì hai ô xanh cạnh nhau
   là màu hết tác dụng phân biệt, đúng toàn bộ lý do icon có màu.
   ⚠ Ba màu ấm của các nhóm KHÁC nằm ở 25 · 33 · 45 nhưng đều là tông TRUNG; màu này cố ý SẪM hơn hẳn
   (tương phản 6,85 so với 4,06 của Công việc) nên vẫn tách được khi lưới bày cả hai nhóm cùng lúc. */
.ma-mau-to-xe[b-xa18d6dyeu]       { color: #8f4700; } /* cam cháy sẫm — 6,85 */
.ma-mau-chu-ky-so[b-xa18d6dyeu]   { color: #0f6e5c; } /* xanh lục thẫm — đợt 2659 */
.ma-mau-chat-bot[b-xa18d6dyeu]    { color: #5b3fa8; } /* tím chàm    — đợt 2718c, việc o2270 */
.ma-mau-trinh-chieu[b-xa18d6dyeu] { color: #c2185b; } /* hồng sen   — 5,23 */
.ma-mau-quan-tri[b-xa18d6dyeu]    { color: #5f6368; } /* xám chì    — 5,39 */
.ma-mau-cong-cu[b-xa18d6dyeu]     { color: #5b7c0a; } /* xanh rêu   — 4,32 */ /* đợt 662 */
.ma-mau-nghi-phep[b-xa18d6dyeu]   { color: #a61e4d; } /* đỏ mận     — 6,42 */ /* đợt 676 */
.ma-mau-tai-khoan[b-xa18d6dyeu]   { color: #5b4bcc; } /* tím chàm   — 5,63 */
.ma-mau-thu-nghiem[b-xa18d6dyeu]  { color: #3949ab; } /* chàm đậm   — 7,74 */ /* đợt 892 */
.ma-mau-khac[b-xa18d6dyeu]        { color: #795548; } /* nâu        — 5,83 */
.ma-mau-dien[b-xa18d6dyeu]        { color: #b45309; } /* hổ phách   — 4,47 */
.ma-mau-tu-dong-hoa[b-xa18d6dyeu] { color: #00838f; } /* xanh cổ vịt— 4,03 */
.ma-mau-ai[b-xa18d6dyeu]          { color: #9334e6; } /* tím        — 4,83 */
.ma-mau-muc-in[b-xa18d6dyeu]      { color: #1a5f7a; } /* xanh mực   — 6,32 */ /* đợt 1467 */
/* Tím violet (sắc độ ~261°) cho 📠 Máy photocopy — đợt 2557 (việc o2063), chọn theo khoảng trống của
   ĐÚNG dải nó đứng (💻 CNTT: mạng 174° · mực in 197° · trình chiếu 340°): quãng 197°→340° rộng 143°.
   ⚠ Ô này đứng SÁT ô 🖨 (tách ra từ nó) nên CỐ Ý tránh mọi sắc xanh: hai ô cạnh nhau cùng tông là
     màu hết tác dụng phân biệt. Gần nhất cả bảng là 🗂 Dự án #4527a0 (chàm SẪM, nhóm khác) — ô này
     sáng hơn hẳn (6,51 so với 9,12) nên vẫn tách được. */
.ma-mau-may-photo[b-xa18d6dyeu]   { color: #6f42c1; } /* tím violet — 6,51 */ /* đợt 2557 */

/* NỀN TỐI — bộ màu SÁNG HƠN của cùng dải màu (đúng khuôn Material dark: nâng độ sáng, hạ độ bão hòa).
   ⚠ Gộp cả `dark` lẫn `co-dien-toi`: hai giao diện nền tối phải cùng một bộ màu, tách ra là hai nơi lệch. */
:root[data-theme="dark"] .ma-mau-tong-quan[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-tong-quan[b-xa18d6dyeu]   { color: #8ab4f8; } /* 6,12 */
:root[data-theme="dark"] .ma-mau-cong-viec[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-cong-viec[b-xa18d6dyeu]   { color: #fcad70; } /* 6,97 */
:root[data-theme="dark"] .ma-mau-du-an[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-du-an[b-xa18d6dyeu]       { color: #b39ddb; } /* 5,38 — đợt 2234 */
:root[data-theme="dark"] .ma-mau-sua-chua[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-sua-chua[b-xa18d6dyeu]    { color: #d4e157; } /* 9,03 — đợt 2234 */
:root[data-theme="dark"] .ma-mau-quan-ly-phong[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-quan-ly-phong[b-xa18d6dyeu] { color: #b0bec5; } /* 6,77 — đợt 834 */
:root[data-theme="dark"] .ma-mau-cham-cong[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-cham-cong[b-xa18d6dyeu]   { color: #81c995; } /* 6,58 */
:root[data-theme="dark"] .ma-mau-an-ninh[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-an-ninh[b-xa18d6dyeu]     { color: #f28b82; } /* 5,40 */
:root[data-theme="dark"] .ma-mau-mang[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-mang[b-xa18d6dyeu]        { color: #7bd4c8; } /* 7,42 */
:root[data-theme="dark"] .ma-mau-nuoc[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-nuoc[b-xa18d6dyeu]        { color: #7fcfff; } /* 7,53 */
:root[data-theme="dark"] .ma-mau-cong-nghe[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-cong-nghe[b-xa18d6dyeu]   { color: #e8c26a; } /* 7,59 — đợt 752 */
:root[data-theme="dark"] .ma-mau-cong-nghe-pccc[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-cong-nghe-pccc[b-xa18d6dyeu] { color: #ff9e9e; } /* 7,81 — đợt 913 */
:root[data-theme="dark"] .ma-mau-nhan-su[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-nhan-su[b-xa18d6dyeu]     { color: #e0b3f0; } /* 7,29 — đợt 777 */
:root[data-theme="dark"] .ma-mau-hanh-chinh[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-hanh-chinh[b-xa18d6dyeu]  { color: #8ed46a; } /* 7,23 — đợt 1443 */
:root[data-theme="dark"] .ma-mau-quan-ly-lich[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-quan-ly-lich[b-xa18d6dyeu]{ color: #b0adf5; } /* 6,19 — đợt 2277 */
:root[data-theme="dark"] .ma-mau-y-te[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-y-te[b-xa18d6dyeu]        { color: #ec93dc; } /* 6,35 — đợt 1696 */
:root[data-theme="dark"] .ma-mau-to-xe[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-to-xe[b-xa18d6dyeu]       { color: #f2a76a; } /* 7,92 */
:root[data-theme="dark"] .ma-mau-chu-ky-so[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-chu-ky-so[b-xa18d6dyeu]   { color: #5fd3bb; } /* đợt 2659 */
:root[data-theme="dark"] .ma-mau-chat-bot[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-chat-bot[b-xa18d6dyeu]    { color: #b9a4f5; } /* đợt 2718c */
:root[data-theme="dark"] .ma-mau-trinh-chieu[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-trinh-chieu[b-xa18d6dyeu] { color: #ff8bab; } /* 5,85 */
:root[data-theme="dark"] .ma-mau-quan-tri[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-quan-tri[b-xa18d6dyeu]    { color: #bdc1c6; } /* 7,13 */
:root[data-theme="dark"] .ma-mau-cong-cu[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-cong-cu[b-xa18d6dyeu]     { color: #b8d178; } /* 7,63 — đợt 662 */
:root[data-theme="dark"] .ma-mau-nghi-phep[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-nghi-phep[b-xa18d6dyeu]   { color: #faa2c1; } /* 6,75 — đợt 676 */
:root[data-theme="dark"] .ma-mau-tai-khoan[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-tai-khoan[b-xa18d6dyeu]   { color: #aeb4f9; } /* 6,57 */
:root[data-theme="dark"] .ma-mau-thu-nghiem[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-thu-nghiem[b-xa18d6dyeu]  { color: #9fa8da; } /* 7,02 — đợt 892 */
:root[data-theme="dark"] .ma-mau-khac[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-khac[b-xa18d6dyeu]        { color: #d7bdb5; } /* 7,27 */
:root[data-theme="dark"] .ma-mau-dien[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-dien[b-xa18d6dyeu]        { color: #fdd663; } /* 9,19 */
:root[data-theme="dark"] .ma-mau-tu-dong-hoa[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-tu-dong-hoa[b-xa18d6dyeu] { color: #6fd2e0; } /* 7,35 */
:root[data-theme="dark"] .ma-mau-ai[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-ai[b-xa18d6dyeu]          { color: #d7aefb; } /* 6,96 */
:root[data-theme="dark"] .ma-mau-muc-in[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-muc-in[b-xa18d6dyeu]      { color: #7fc8e8; } /* 6,96 — đợt 1467 */
:root[data-theme="dark"] .ma-mau-may-photo[b-xa18d6dyeu],
:root[data-theme="co-dien-toi"] .ma-mau-may-photo[b-xa18d6dyeu]   { color: #b69cf0; } /* 5,53 — đợt 2557 */
/* /Components/CoDien/MenuApp.razor.rz.scp.css */
/* ═════════ LƯỚI APP KIỂU GOOGLE — CSS RIÊNG CỦA COMPONENT (đợt 598) ═════════

   ⛔ CỐ Ý KHÔNG viết một dòng nào vào wwwroot/app.css hay co-dien.css: hai tệp đó là CHỖ DÙNG CHUNG rủi
      ro cao nhất (quy tắc cứng #18) và đang có hai luồng khác cùng sửa (đợt 602 · 603). CSS cách ly của
      Blazor tự thêm thuộc tính phạm vi vào bộ chọn nên không luật nào rò ra ngoài component này.

   ⚠ CÁCH BLAZOR CÁCH LY: thuộc tính phạm vi chỉ được thêm vào phần tử CUỐI của mỗi bộ chọn. Nhờ vậy
     `:root[data-theme="dark"] .ma-bang` vẫn chạy đúng — `:root` nằm ngoài component, `.ma-bang` nằm
     trong. Đây là lý do đổi giao diện vẫn MƯỢT: chỉ đổi màu bằng CSS, không gọi máy chủ, không vẽ lại.

   🎨 TƯƠNG PHẢN — TÍNH BẰNG SỐ, không ướm mắt (dự án vừa mất một đợt riêng đi vá 43 tệp lóa trắng).
      Ngưỡng tự đặt của dự án: chữ thường ≥ 4,5:1 · chữ to/đậm ≥ 3:1.
        NỀN SÁNG  — chữ chính #202124 trên #ffffff = 16,1:1 ✔ · chữ phụ #5f6368 trên #ffffff = 6,05:1 ✔
        NỀN TỐI   — chữ chính #e8eaed trên #292a2d = 11,9:1 ✔ · chữ phụ #9aa0a6 trên #292a2d = 5,44:1 ✔
      ⚠ Đổi bất kỳ mã màu nào ở dưới thì TÍNH LẠI rồi sửa luôn mấy con số này — để số cũ còn tệ hơn không
        ghi số, vì người sau sẽ tin vào nó. */

/* ───────── Nút 9 chấm ───────── */

.ma-boc[b-45aw0s8ahf] {
    position: relative;
    display: inline-flex;
    align-items: center;
}

/* ═════ ĐỢT 645 — ĐÓNG/MỞ BẰNG Ô ĐÁNH DẤU ẨN (thay `<details>`), để BẤM RA NGOÀI LÀ TỰ THU GỌN ═════
   Khuôn lấy nguyên của `TheTaiKhoan`: ô đánh dấu ẩn giữ trạng thái · một `<label>` phủ kín màn hình làm
   "vùng bấm ra ngoài" · bảng chỉ hiện khi ô được tick. Thuần CSS ⇒ chạy cả khi trang dựng TĨNH.
   ⚠ Nền phủ phải nằm DƯỚI bảng về thứ tự z (z-index thấp hơn) nhưng TRÊN mọi thứ khác, nếu không bấm
     vào chính bảng cũng trúng nền và bảng đóng ngay khi vừa mở. */
.ma-cong-tac[b-45aw0s8ahf] {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    pointer-events: none;
}

.ma-nen[b-45aw0s8ahf] { display: none; }

/* ⛔⛔ ĐỢT 649 — "BẤM RA NGOÀI KHÔNG THU MENU LẠI" (anh Tiến báo 14/08/2026). ĐỌC TRƯỚC KHI SỬA CHỖ NÀY.

   THỦ PHẠM: `.top-row` có `backdrop-filter: blur(8px)` (app.css mục 7, hai giao diện SÁNG và TỐI).
   Theo đặc tả CSS, phần tử có `filter`/`backdrop-filter`/`transform` khác `none` trở thành **KHỐI CHỨA
   của mọi hậu duệ `position: fixed`** — tức `inset: 0` của nền phủ này KHÔNG còn nghĩa "phủ kín khung
   nhìn" mà thành "phủ kín đúng dải thanh nav" (cao ~56px). Bấm xuống thân trang là bấm trượt cái nền
   ⇒ ô đánh dấu không đổi ⇒ lưới đứng nguyên.
   ⓘ Vì vậy lỗi này CHỈ có ở hai giao diện hiện đại; hai bản cổ điển không khai kính mờ nên vẫn đóng
     bình thường — đúng dáng "lúc được lúc không" rất khó đoán nếu chỉ đọc mã của riêng component này.
   ⚠ Đây KHÔNG phải chuyện "ngữ cảnh xếp chồng" mà đợt 467 đã đo và loại trừ (xem app.css dòng ~762):
     cùng một thuộc tính `backdrop-filter` nhưng đây là hệ quả KHÁC — khối chứa, không phải thứ tự vẽ.

   CÁCH CHỮA: giữ `inset: 0` (đúng sẵn cho ca khối chứa là khung nhìn) rồi **ghi đè CHIỀU CAO** bằng
   `100vh` — luật "quá ràng buộc" của CSS sẽ bỏ `bottom` và lấy chiều cao này ⇒ nền phủ kéo từ mép trên
   thanh nav xuống hết một màn hình, phủ trọn thân trang ở CẢ HAI ca khối chứa.
   ⛔ ĐỪNG "chữa" bằng cách nới sang phải (`right: -100vw`, `width: 300vw`): nền phủ lúc này không còn
     neo theo khung nhìn nên phần trào sang PHẢI/ĐÁY sẽ cộng vào vùng cuộn của trang ⇒ đẻ ra thanh cuộn
     ngang mỗi lần mở menu. Trào sang TRÁI/LÊN TRÊN thì vô hại, nhưng cũng không cần.
   ⚠ Đánh đổi đã biết, cố ý chấp nhận: hai dải mép trái/phải rộng đúng bằng `padding` của thanh nav
     (1rem và 1,5rem) nằm ngoài nền phủ ⇒ bấm trúng đúng vệt vài chục điểm ảnh sát mép màn hình thì
     menu chưa đóng. Đổi lấy chuyện KHÔNG đẻ thanh cuộn ngang cho mọi trang.
   📌 `TheTaiKhoan` (thẻ tài khoản góc phải) dựng theo cùng khuôn nên dính CÙNG lỗi — đã vá cùng lượt.
   🧱 Hàng rào: `TKNB.Tests/NenPhuBamRaNgoaiTests.cs` (còn kính mờ ở thanh nav thì còn bắt buộc dòng này). */
.ma-cong-tac:checked ~ .ma-nen[b-45aw0s8ahf] {
    display: block;
    position: fixed;
    inset: 0;
    height: 100vh;
    height: 100dvh;   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */
    z-index: 1070;
    cursor: default;
}

/* Bảng ẩn mặc định, chỉ bung khi ô được tick. */
.ma-goc > .ma-bang[b-45aw0s8ahf] { display: none; }
.ma-cong-tac:checked ~ .ma-goc > .ma-bang[b-45aw0s8ahf] { display: block; }

/* Vòng focus của bàn phím: ô đánh dấu ẩn nên phải vẽ vòng lên chính cái nút. */
.ma-cong-tac:focus-visible ~ .ma-goc > .ma-nut[b-45aw0s8ahf] {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

.ma-nut[b-45aw0s8ahf] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Vùng bấm 40×40: đây là nút NGOÀI CÙNG BÊN TRÁI, thứ đầu tiên người dùng nhìn thấy (anh Tiến chốt
       13/08) — để nhỏ hơn là khó trúng trên máy bảng. */
    width: 40px;
    height: 40px;
    border-radius: 50%;
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.ma-nut:hover[b-45aw0s8ahf],
.ma-nut:focus-visible[b-45aw0s8ahf] { background: rgba(127, 127, 127, 0.22); }

/* ⭐ ĐỢT 904 — MŨI TÊN QUAY LẠI đứng đúng chỗ lưới 9 chấm ở các trang phái sinh.
   Dùng lại `.ma-nut` để hai hình có CÙNG vùng bấm 40×40 và cùng chỗ đứng — người dùng không phải đưa
   mắt đi tìm nút khác khi đổi trang.
   🔁 ĐỢT 922 (việc o144) — nay là thẻ <a> (trước là <button>): vẫn phải gỡ dáng mặc định của trình
      duyệt, và bỏ thêm gạch chân của liên kết. */
.ma-quay-lai[b-45aw0s8ahf] {
    border: 0;
    padding: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-decoration: none;
    position: relative;
    /* Hiệu ứng chỉ chạy khi rê chuột / focus — thanh nav hiện ở MỌI trang, một hiệu ứng chạy liên tục
       là thứ người dùng phải nhìn cả ngày (anh Tiến dặn "hiệu ứng đẹp", không phải "hiệu ứng động mãi"). */
    transition: transform 160ms ease, background-color 160ms ease;
}

/* ⭐ ĐỢT 922 (việc ㉑) — anh Tiến: *"nút quay trở lại hiện tại đang khá xấu và cổ điển, em tìm nút đẹp
   hơn, trend hơn và đồ họa, thêm hiệu ứng đẹp cho nó nhé"*.

   Ngôn ngữ đồ họa lấy của CHIP LOGO 3D (đợt 887/893) mà anh đã ưng — bóng sâu, nâng lên khi rê chuột —
   để thanh nav chỉ có MỘT phong cách, ⛔ không đẻ phong cách thứ hai.

   ⚠ MÀU: chỉ dùng `currentColor` và `rgba` xám trung tính ⇒ tự đúng ở CẢ nền sáng lẫn nền tối, ⛔ không
     gõ mã màu cứng (đó là cách sinh ra đợt đi vá 43 tệp lóa màu). */
.ma-quay-lai[b-45aw0s8ahf]::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: rgba(127, 127, 127, 0.16);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35),
                0 1px 2px rgba(0, 0, 0, 0.18);
    transform: scale(0.82);
    opacity: 0;
    transition: transform 180ms ease, opacity 180ms ease;
}

.ma-quay-lai:hover[b-45aw0s8ahf]::before,
.ma-quay-lai:focus-visible[b-45aw0s8ahf]::before {
    transform: scale(1);
    opacity: 1;
}

.ma-quay-lai:hover[b-45aw0s8ahf] { transform: translateX(-2px); }
.ma-quay-lai:active[b-45aw0s8ahf] { transform: translateX(-1px) scale(0.94); }

/* Vùng bấm 40×40 của `.ma-nut` đã có nền xám khi hover; ở nút này vòng nền ::before lo phần đó rồi
   nên tắt đi, kẻo hai lớp nền chồng nhau thành một mảng đậm. */
.ma-quay-lai:hover[b-45aw0s8ahf],
.ma-quay-lai:focus-visible[b-45aw0s8ahf] { background: none; }

/* Chín chấm vẽ bằng TÔ (fill), mũi tên vẽ bằng NÉT (stroke) — luật `.ma-svg svg { fill: currentColor }`
   ở trên sẽ bôi đặc mũi tên thành một mảng đen nếu không đảo lại đúng hai thuộc tính này. */
.ma-quay-lai .ma-svg[b-45aw0s8ahf] {
    position: relative;   /* nổi trên vòng nền ::before */
}

.ma-quay-lai .ma-svg svg[b-45aw0s8ahf] {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Cái đuôi ngang của mũi tên: bình thường thu về gốc, rê chuột thì kéo dài ra — gợi đúng động tác
   "đi ngược về". `stroke-dasharray` bằng chiều dài nét nên không cần đo bằng JS. */
.ma-quay-lai .ma-mui-ngan[b-45aw0s8ahf] {
    stroke-dasharray: 12;
    stroke-dashoffset: 12;
    transition: stroke-dashoffset 200ms ease;
}

.ma-quay-lai:hover .ma-mui-ngan[b-45aw0s8ahf],
.ma-quay-lai:focus-visible .ma-mui-ngan[b-45aw0s8ahf] { stroke-dashoffset: 0; }

/* ⚠ Người đã tắt hiệu ứng chuyển động ở hệ điều hành thì KHÔNG ép họ xem: nút vẫn đủ rõ, chỉ là đứng
   yên. Đây là nút điều hướng chính của mọi trang phái sinh, không phải chỗ trình diễn. */
@media (prefers-reduced-motion: reduce) {
    .ma-quay-lai[b-45aw0s8ahf],
    .ma-quay-lai[b-45aw0s8ahf]::before,
    .ma-quay-lai .ma-mui-ngan[b-45aw0s8ahf] { transition: none; }

    .ma-quay-lai:hover[b-45aw0s8ahf] { transform: none; }
    .ma-quay-lai .ma-mui-ngan[b-45aw0s8ahf] { stroke-dashoffset: 0; }
}

/* Chín chấm tô theo MÀU CHỮ ĐANG THỪA KẾ ⇒ tự đúng ở cả 4 giao diện, không phải khai màu 4 lần. */
.ma-svg svg[b-45aw0s8ahf] { display: block; fill: currentColor; }

/* Nhãn chữ chỉ dành cho trình đọc màn hình — nút là biểu tượng, hướng dẫn nằm ở title. */
.ma-an-chu[b-45aw0s8ahf] {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ───────── Bảng lưới bung ra ───────── */

.ma-bang[b-45aw0s8ahf] {
    position: absolute;
    top: calc(100% + 6px);
    /* NEO MÉP TRÁI của nút — đúng dáng Google, và nút này nằm ngoài cùng bên trái thanh nav. */
    left: 0;
    z-index: 1080;
    padding: 12px;
    border-radius: 10px;
    box-shadow: 0 4px 18px rgba(0, 0, 0, 0.28);
    /* MÀN HÌNH HẸP: bảng không được tràn ra ngoài khung nhìn. `max-width` tính theo bề rộng khung nhìn
       trừ hai lề 12px; quá cao thì cuộn TRONG bảng chứ không đẩy dài cả trang. */
    max-width: calc(100vw - 24px);
    max-height: calc(100vh - 90px);
    max-height: calc(100dvh - 90px);   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */
    overflow-y: auto;
    overscroll-behavior: contain;
    background: #ffffff;
    color: #202124;
    border: 1px solid #dadce0;
}

.ma-luoi[b-45aw0s8ahf] {
    display: grid;
    /* Tự co số cột theo bề rộng: máy để bàn ra 3–4 cột, điện thoại ra 2 cột — không cần media query. */
    grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
    gap: 4px;
    /* Đủ rộng cho 3 cột trên máy để bàn, nhưng vẫn nhường `max-width` ở trên khi màn hình hẹp. */
    width: 360px;
    max-width: 100%;
}

.ma-trong[b-45aw0s8ahf] {
    margin: 4px 8px 8px;
    font-size: 0.85rem;
    color: #5f6368;
}

/* ───────── Ô app ───────── */

.ma-o[b-45aw0s8ahf] { border-radius: 8px; }
.ma-o > summary[b-45aw0s8ahf]::-webkit-details-marker { display: none; }
.ma-o > summary[b-45aw0s8ahf]::marker { content: ""; }

/* ═════ ĐỢT 645 — MÀU GIỐNG DẢI Ô LỐI TẮT Ở TRANG CHỦ (anh Tiến 13/08/2026) ═════
   Mượn ĐÚNG ngôn ngữ hình của `.o-loi-tat` (TongQuan.razor, đợt 450): khối nổi nhẹ, nền dốc, gờ sáng ở
   mép trên, bóng đổ dưới, và nhún lên khi rê chuột — để hai chỗ dẫn đường của app trông cùng một họ.
   ⚠ CHỈ ba giao diện HIỆN ĐẠI mới có khối nổi. Hai giao diện cổ điển cố ý PHẲNG (module cổ điển luật 1:
     trông như phần mềm đời cũ) ⇒ bọc bằng `:root:not([data-theme^="co-dien"])`, đúng khuôn trang chủ.
   ⚠ Giữ nguyên phần khung xương ở trên (lưới, cỡ chữ, bo góc): đây chỉ là lớp TRANG TRÍ chồng lên. */
:root:not([data-theme^="co-dien"]) .ma-o[b-45aw0s8ahf] {
    border: 1px solid rgba(10, 61, 145, .16);
    border-radius: .7rem;
    background: linear-gradient(180deg, #ffffff, #eef2f9);
    box-shadow: 0 1px 0 rgba(255, 255, 255, .95) inset,
                0 4px 10px rgba(0, 23, 68, .10), 0 1px 2px rgba(0, 23, 68, .14);
    transition: transform .18s ease-out, box-shadow .18s ease-out;
}

:root:not([data-theme^="co-dien"]) .ma-o:hover[b-45aw0s8ahf] {
    transform: translateY(-2px);
    box-shadow: 0 1px 0 rgba(255, 255, 255, .95) inset,
                0 10px 20px rgba(0, 23, 68, .16), 0 2px 4px rgba(0, 23, 68, .18);
}

/* Ô app ĐANG MỞ: bỏ nhún (nó đang trải rộng cả hàng, nhún là cả khối trang nhảy). */
:root:not([data-theme^="co-dien"]) .ma-o[open]:hover[b-45aw0s8ahf] { transform: none; }

/* Nền tối: cùng cặp màu thẻ/khối của giao diện tối (#262a31 · #2d323a) chứ không phải trắng dốc. */
:root[data-theme="dark"] .ma-o[b-45aw0s8ahf] {
    border-color: rgba(120, 180, 255, .18);
    background: linear-gradient(180deg, #2d323a, #262a31);
    box-shadow: 0 4px 10px rgba(0, 0, 0, .35);
}

/* Máy khai "hạn chế chuyển động" thì bỏ phần nhún — giữ nguyên nền và bóng. */
@media (prefers-reduced-motion: reduce) {
    :root:not([data-theme^="co-dien"]) .ma-o[b-45aw0s8ahf] { transition: none; }
    :root:not([data-theme^="co-dien"]) .ma-o:hover[b-45aw0s8ahf] { transform: none; }
}

.ma-o-dau[b-45aw0s8ahf] {
    display: flex;
    flex-direction: column;
    /* ⭐ ĐỢT 654 — chốt thứ nhất chống tràn chữ: cho phép hộp co nhỏ hơn nội dung. Xem `.ma-o-ten`. */
    min-width: 0;
    align-items: center;
    gap: 4px;
    padding: 12px 6px;
    text-align: center;
    cursor: pointer;
    list-style: none;
    border-radius: 8px;
}

.ma-o-dau:hover[b-45aw0s8ahf],
.ma-o-dau:focus-visible[b-45aw0s8ahf] { background: #f1f3f4; }

/* ⓘ Cỡ của ICON nằm ở IconApp.razor.css — CSS cách ly gắn phạm vi theo từng component, luật viết ở đây
   không với tới thẻ <svg> do IconApp vẽ ra. */

/* ⭐ ĐỢT 654 — CHỮ PHẢI NẰM GỌN TRONG KHUNG Ô (anh Tiến 14/08/2026: *"chữ phải được sắp xếp gọn trong
   khung"*, kèm ảnh tên app tràn hẳn ra ngoài mép ô).
   VÌ SAO TRÀN: `.ma-o-dau` là hộp flex, mà **phần tử con của flex mặc định KHÔNG co nhỏ hơn nội dung**
   (`min-width: auto`) ⇒ một tên dài hơn ô 112px thì đẩy rộng cả ô rồi trào ra ngoài, chứ không xuống dòng.
   ⇒ Ba dòng dưới đây là ba chốt khác nhau, thiếu dòng nào cũng vẫn tràn:
     · `min-width: 0` ở hộp bọc — cho phép co (gỡ đúng cái mặc định nói trên);
     · `max-width: 100%` — chặn chữ đẩy rộng ô;
     · `overflow-wrap: anywhere` — cho xuống dòng **kể cả khi một TỪ dài hơn ô** (tên phòng, tên viết
       liền không có dấu cách). Dùng `anywhere` chứ không `break-word`: `break-word` chỉ cắt khi đã hết
       cách, còn `anywhere` cắt sớm nên ô không bao giờ phình. */
.ma-o-ten[b-45aw0s8ahf] {
    font-size: 0.78rem;
    line-height: 1.2;
    max-width: 100%;
    overflow-wrap: anywhere;
}

/* ⭐ ĐỢT 647 — Ô APP LÀ MỘT LIÊN KẾT (anh Tiến: bấm icon phải VÀO TRANG TỔNG QUAN của app, không sổ
   list). Ba lớp cũ của danh sách trang con (`.ma-o-trang` · `.ma-nhom-ten` · `.ma-trang`) và luật
   `.ma-o[open]` đã XÓA cùng lượt — không còn markup nào dùng tới. */
.ma-o-lien-ket[b-45aw0s8ahf] {
    display: block;
    text-decoration: none;
    color: inherit;
}

.ma-o-lien-ket:hover[b-45aw0s8ahf],
.ma-o-lien-ket:focus-visible[b-45aw0s8ahf] { text-decoration: none; color: inherit; }

/* ───────── Dải NHÓM CHỦ ĐỀ (đợt 1792, việc o1066) ─────────

   Anh Tiến 02/09/2026: *"tách cách trình bày các app thành các nhóm"*. Trước đợt này chỉ có ĐÚNG MỘT
   dải có đầu đề — nhóm "Coming soon" (hai lớp `.ma-sap-co` · `.ma-sap-co-dau`, đợt 598); nay MỌI dải
   đều có đầu đề nên hai lớp ấy đổi tên thành `.ma-nhom` · `.ma-nhom-dau` cho đúng nghĩa.
   ⛔ ĐỪNG để lại hai lớp cũ "cho chắc": markup không còn chỗ nào dùng ⇒ chúng thành CSS chết, thứ làm
     người sau đi tìm một khối markup không tồn tại (đúng lý do đợt 647 đã dọn ba lớp `.ma-o-trang`…).

   ⚠ Vạch ngăn + khoảng thở chỉ đặt cho dải THỨ HAI trở đi (`.ma-nhom + .ma-nhom`): dải đầu tiên nằm sát
     mép trong của bảng, kẻ một vạch trên nó là vẽ hai đường song song với chính viền bảng.
   ⓘ Cỡ chữ và màu giữ NGUYÊN của `.ma-sap-co-dau` cũ — màu #5f6368 trên nền #ffffff đã đo 6,05:1
     (nền tối: #9aa0a6 trên #292a2d = 5,44:1), xem khối 🎨 ở đầu tệp. Đổi màu thì TÍNH LẠI hai số đó. */

.ma-nhom + .ma-nhom[b-45aw0s8ahf] {
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px solid #dadce0;
}

.ma-nhom-dau[b-45aw0s8ahf] {
    margin: 0 0 6px 2px;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #5f6368;
}

/* KHÔNG dùng `opacity` để làm mờ: nó kéo tương phản của CHỮ xuống dưới ngưỡng mà không ai tính được nữa.
   Dùng đúng màu chữ phụ đã đo (6,05:1 nền sáng · 5,44:1 nền tối) — vẫn đọc rõ, vẫn thấy là chưa dùng được. */
.ma-o-tat[b-45aw0s8ahf] { cursor: not-allowed; }
/* ⚠ Đặt màu mờ lên CẢ Ô (.ma-o-dau) chứ không riêng chữ: nét icon vẽ bằng `currentColor` nên nó THỪA KẾ
   màu từ đây — icon tự mờ theo chữ. Nếu chỉ tô mờ .ma-o-ten thì chữ mờ mà icon vẫn đậm, nhìn như ô đang
   dùng được. Màu thừa kế xuyên qua ranh giới component nên vẫn tới được thẻ <svg> của IconApp. */
.ma-o-tat .ma-o-dau[b-45aw0s8ahf] { cursor: not-allowed; color: #5f6368; }
.ma-o-tat:hover .ma-o-dau[b-45aw0s8ahf] { background: transparent; }

/* ═════════ NỀN TỐI — cả giao diện hiện đại (dark) lẫn cổ điển nền tối (co-dien-toi) ═════════ */

:root[data-theme="dark"] .ma-bang[b-45aw0s8ahf],
:root[data-theme="co-dien-toi"] .ma-bang[b-45aw0s8ahf] {
    background: #292a2d;
    color: #e8eaed;
    border-color: #3c4043;
}

/* ⚠ ĐỢT 647 — đã gỡ `.ma-trang` và `.ma-nhom-ten` khỏi hai chùm dưới: markup của chúng không còn
   (danh sách trang con nay nằm ở trang `/app/{mã}`). Để lại là CSS chết — thứ làm người sau đi tìm
   một khối markup không tồn tại. */
:root[data-theme="dark"] .ma-o-dau:hover[b-45aw0s8ahf],
:root[data-theme="dark"] .ma-o-dau:focus-visible[b-45aw0s8ahf],
:root[data-theme="co-dien-toi"] .ma-o-dau:hover[b-45aw0s8ahf],
:root[data-theme="co-dien-toi"] .ma-o-dau:focus-visible[b-45aw0s8ahf] { background: #3c4043; }

:root[data-theme="dark"] .ma-trong[b-45aw0s8ahf],
:root[data-theme="dark"] .ma-nhom-dau[b-45aw0s8ahf],
:root[data-theme="dark"] .ma-o-tat .ma-o-dau[b-45aw0s8ahf],
:root[data-theme="co-dien-toi"] .ma-trong[b-45aw0s8ahf],
:root[data-theme="co-dien-toi"] .ma-nhom-dau[b-45aw0s8ahf],
:root[data-theme="co-dien-toi"] .ma-o-tat .ma-o-dau[b-45aw0s8ahf] { color: #9aa0a6; }

:root[data-theme="dark"] .ma-nhom + .ma-nhom[b-45aw0s8ahf],
:root[data-theme="co-dien-toi"] .ma-nhom + .ma-nhom[b-45aw0s8ahf] { border-top-color: #3c4043; }

/* ═══ ⭐ ĐỢT 1521 (việc o723 + o724) — VÙNG CHẠM 44px CHO HAI NÚT TRÒN TRÊN THIẾT BỊ CẢM ỨNG ═══
   Anh Tiến chốt 29/08/2026 khi được hỏi ba thứ mà đợt 1518 CỐ Ý chừa: *"nới cả hai lên 44px"*.
   Lượt đo đầu tiên đo được của màn hình `/dien-thoai` (đợt 1521 — trước đó nó khoá vai trò Dev nên
   chưa ai đo được lần nào) cho thấy trang SẠCH mọi mặt, chỉ còn ĐÚNG hai vùng bấm dưới ngưỡng, và
   cả hai là nút DÙNG CHUNG của thanh nav: `.ma-nut` 40×40 và `.mcd-logo` 42px.

   🔴 VÌ SAO LUẬT NÀY PHẢI Ở ĐÂY chứ ⛔ KHÔNG ở `app.css`: CSS cách ly biên dịch ra `.ma-nut[b-…]`
      **(0,2,0)**, còn bộ chọn `.ma-nut` viết trong `app.css` chỉ **(0,1,0)** ⇒ đặt bên ấy là viết
      một luật **⛔ KHÔNG BAO GIỜ CHẠY**, mà build vẫn xanh và test vẫn xanh. Đây đúng lý do khối
      `@media (pointer: coarse)` của đợt 1518 phải ghi `.ma-nut` vào danh sách *cố ý đứng ngoài*.

   🔑 Vẫn đọc BIẾN `--cham-toi-thieu` của `app.css` (khai trên `:root`, biến CSS kế thừa xuyên qua
      ranh giới cách ly) ⇒ ngưỡng vẫn nằm ở ĐÚNG MỘT CHỖ. ⛔ Đừng gõ số 44px vào đây.

   ⛔ CHỈ trong `@media (pointer: coarse)`: con số **40×40 là anh Tiến chốt 13/08/2026** cho màn hình
      MÁY TÍNH và vẫn nguyên hiệu lực ở đó — 433 tài khoản phần lớn ngồi chuột. Nới cho cả hai loại
      thiết bị là đổi bố cục toàn app để giải bài toán của điện thoại (rủi ro lan truyền, quy tắc #18).
   ⚠ ⛔ Đừng đổi sang `@media (max-width: …)`: khổ hẹp ⛔ không có nghĩa là ngón tay (cửa sổ thu nhỏ
      vẫn dùng chuột), và màn rộng vẫn có thể là cảm ứng. Thứ quyết định là CÁI CHẠM VÀO. */
@media (pointer: coarse) {
    .ma-nut[b-45aw0s8ahf] {
        width: var(--cham-toi-thieu, 44px);
        height: var(--cham-toi-thieu, 44px);
    }

    /* ⭐ VIỆC o1397 — SÀN CỠ CHỮ 12px. `.ma-nhom-dau` là dòng tên nhóm in hoa trong bảng menu, đo
       tĩnh 0.72rem = 11,52px, dưới sàn `--cham-chu-toi-thieu` khai ở `app.css`.
       ⚠ Chữ này đã `text-transform: uppercase` + `letter-spacing: .04em` nên nó là chỗ khó đọc
         NHẤT của bảng menu ở cỡ nhỏ, ⛔ không phải chỗ trang trí bỏ qua được.
       🔑 Vẫn `max()` + đọc BIẾN, ⛔ đừng gõ 12px — cùng lẽ với `--cham-toi-thieu` ngay trên. */
    .ma-nhom-dau[b-45aw0s8ahf] { font-size: max(0.72rem, var(--cham-chu-toi-thieu)); }
}

/* ═════ ⭐ ĐỢT 2146 (việc o1554) — Ô APP THÀNH NÚT 3D CÓ MÀU CỦA CHÍNH APP ĐÓ ═════
   Anh Tiến 06/09/2026 (kèm ảnh chụp ô 📊 Tổng quan): *"anh cần đồ hoạ lại nút này cho đẹp hơn, 3D có màu"*.
   Trước đợt này lớp trang trí của đợt 645 CHỈ có màu ở NÉT ICON — thân nút trắng dốc xám nên 20 app
   trông y hệt nhau. Nay thân nút mượn đúng màu của app: nền phớt màu · viền màu · GỜ DÀY DƯỚI ĐÁY
   (thứ tạo ra khối 3D thật, ⛔ không phải bóng mờ) · bóng đổ ăn màu · chip màu sau icon · lún khi bấm.

   🔑 VÌ SAO PHẢI KHAI LẠI BẢNG MÀU Ở ĐÂY thay vì dùng lại của `IconApp.razor.css`: CSS cách ly gắn dấu
      phạm vi RIÊNG cho từng component ⇒ luật của IconApp ⛔ không với sang thẻ <a> do MenuApp dựng, mà
      biến CSS thì chỉ chảy XUỐNG con chứ ⛔ không ngược lên cha. Hai danh sách phải TỰ khớp nhau, và
      cái canh chuyện đó là hàng rào `MauOAppKhopIconTests` — lệch một mã là nút một màu icon một màu
      mà build vẫn sạch (đúng họ "hỏng lặng lẽ").

   🩸 HAI BẪY ĐÃ TRẢ GIÁ NGAY TRONG LƯỢT VIẾT NÀY, ⛔ đừng lặp lại:
      (1) Bản nháp gom 23 dòng biến vào MỘT khối CSS lồng `:root:not(...) { .ma-o-lien-ket... }`. Bộ dịch CSS
        cách ly ⛔ KHÔNG hiểu cú pháp lồng — nó gắn dấu phạm vi vào phần tử CUỐI của bộ chọn, tức
        `:root:not(...)[b-xxx]`, mà thẻ <html> ⛔ không mang dấu ấy ⇒ CẢ 23 dòng câm. ⇒ Viết PHẮNG.
      (2) Chip màu ban đầu nhắm `.ma-o-dau > :first-child`. Phần tử đó là <svg> do **IconApp** vẽ ra nên
        nó mang dấu phạm vi của IconApp, ⛔ không phải của MenuApp ⇒ luật cũng câm. ⇒ Phải đi qua
        `::deep` (đúng khuôn `NutChonBoMau.razor.css` đã dùng). Cả hai bẫy đều "đẹp trên trình duyệt
        khi tiêm tay, câm sau khi build" — thứ ⛔ không bộ đo tĩnh nào thấy.

   ⚠ `color-mix()` là cách CỐ Ý: trình duyệt cũ ⛔ không hiểu thì cả khai báo bị bỏ, nút rơi về nguyên
     lớp đợt 645 ở trên — vẫn đẹp, ⛔ không vỡ. Vì thế khối này đặt SAU khối cũ và ⛔ đừng xoá khối cũ.
   ⚠ Bọc `:root:not([data-theme^="co-dien"])` — hai giao diện cổ điển CỐ Ý PHẮNG (quy tắc #20, đóng băng).
   ⚠ Ô TẮT (`.ma-o-tat`) ⛔ KHÔNG lấy màu: luật đợt 648 — tô màu ô chưa mở là mời người ta bấm vào
     thứ chưa bấm được. Vì thế mọi bộ chọn dưới đây đều đi qua `.ma-o-lien-ket`.
   🎨 Tương phản: chữ `.ma-o-ten` vẫn là #202124 và nền đậm nhất của nút là màu app pha 15% vào trắng
     (sáng hơn #e8eaed) ⇒ tỉ số ⛔ không tụt xuống dưới con số 16,1:1 ghi ở đầu tệp. */

:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-tong-quan[b-45aw0s8ahf]        { --ma-mau: #1a73e8; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-cong-viec[b-45aw0s8ahf]        { --ma-mau: #c65200; }
/* ⭐ ĐỢT 2234 (việc o1709) — hai ô app mới tách khỏi 🗃; mã màu chép ĐÚNG bảng nền sáng của
   IconApp.razor.css, `MauOAppKhopIconTests` khoá hai bảng phải khớp TỪNG MÃ và TỪNG GIÁ TRỊ. */
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-du-an[b-45aw0s8ahf]            { --ma-mau: #4527a0; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-sua-chua[b-45aw0s8ahf]         { --ma-mau: #827717; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-quan-ly-phong[b-45aw0s8ahf]    { --ma-mau: #37474f; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-cham-cong[b-45aw0s8ahf]        { --ma-mau: #188038; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-an-ninh[b-45aw0s8ahf]          { --ma-mau: #d93025; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-mang[b-45aw0s8ahf]             { --ma-mau: #00796b; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-nuoc[b-45aw0s8ahf]             { --ma-mau: #0277bd; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-cong-nghe[b-45aw0s8ahf]        { --ma-mau: #8a5a00; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-cong-nghe-pccc[b-45aw0s8ahf]   { --ma-mau: #c62828; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-nhan-su[b-45aw0s8ahf]          { --ma-mau: #7b1fa2; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-hanh-chinh[b-45aw0s8ahf]       { --ma-mau: #3f7d20; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-y-te[b-45aw0s8ahf]             { --ma-mau: #a11287; }
/* 📆 ĐỢT 2277 (việc o1795) — app «Quản lý lịch». Mã màu phải TRÙNG KHÍT bản ở IconApp.razor.css
   (#4d49ca): MauOAppKhopIconTests so từng ký tự, và quên bảng này là ô rơi về trắng trơn — đúng
   bẫy đã trả giá với 🚐 Tổ xe ở khối ngay dưới. */
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-quan-ly-lich[b-45aw0s8ahf]     { --ma-mau: #4d49ca; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-trinh-chieu[b-45aw0s8ahf]      { --ma-mau: #c2185b; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-quan-tri[b-45aw0s8ahf]         { --ma-mau: #5f6368; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-cong-cu[b-45aw0s8ahf]          { --ma-mau: #5b7c0a; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-nghi-phep[b-45aw0s8ahf]        { --ma-mau: #a61e4d; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-tai-khoan[b-45aw0s8ahf]        { --ma-mau: #5b4bcc; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-thu-nghiem[b-45aw0s8ahf]       { --ma-mau: #3949ab; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-khac[b-45aw0s8ahf]             { --ma-mau: #795548; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-dien[b-45aw0s8ahf]             { --ma-mau: #b45309; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-tu-dong-hoa[b-45aw0s8ahf]      { --ma-mau: #00838f; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-ai[b-45aw0s8ahf]               { --ma-mau: #9334e6; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-muc-in[b-45aw0s8ahf]           { --ma-mau: #1a5f7a; }
/* 📠 ĐỢT 2557 (việc o2063) — app «Máy photocopy» tách khỏi 🖨. Mã màu TRÙNG KHÍT IconApp.razor.css
   (#6f42c1): quên bảng này là ô rơi về trắng trơn — bẫy đã trả giá với 🚐 Tổ xe ngay dưới. */
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-may-photo[b-45aw0s8ahf]        { --ma-mau: #6f42c1; }
/* ✍🤖 ĐỢT 2856 (việc o2357) — anh Tiến 20/09/2026: *"nút chatbot và nút chữ ký số không đồ họa giống các nút khác"*.
   📏 Đo trên nhánh nền (commit 4ce184c5): `chu-ky-so` (đợt 2659) và `chat-bot` (đợt 2718c) đã khai
   màu ICON ở `IconApp.razor.css` (dòng 106–107 nền sáng, 160–163 nền tối) nhưng **quên bảng thứ hai** ở đây
   ⇒ `--ma-mau` ⛔ không ai đặt, mà thân nút dựng bằng `color-mix(… var(--ma-mau) …)` ⇒ **cả khai báo bị vứt**
   và hai ô rơi về trắng trơn, ⛔ không thẻ ⛔ không viền — đúng bẫy đã trả giá với 🚐 Tổ xe đợt 2202b. Đây là
   lần thứ BA của cùng một bệnh ⇒ hàng rào `AppMoiPhaiKhaiDuTests` (đợt 2856) nay so thẳng với `LuatMenuApp.DanhSach`.
   ⚠ Mã màu phải **TRÙNG KHÍT** bảng nền sáng của icon — `MauOAppKhopIconTests` so từng ký tự. */
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-chu-ky-so[b-45aw0s8ahf]       { --ma-mau: #0f6e5c; }
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-chat-bot[b-45aw0s8ahf]        { --ma-mau: #5b3fa8; }
/* 🚐 ĐỢT 2202b (việc o1661) — anh Tiến 08/09/2026: *"icon tổ xe ở menu app chưa đồ họa thành nút"*.
   📏 Đo trên bản chạy: ô 🚐 Tổ xe đứng **trần** giữa lưới — ⛔ không khung, ⛔ không nền, ⛔ không viền,
   trong khi 22 ô còn lại đều có thân nút màu. Gốc bệnh: app `to-xe` (đợt 2078) khai màu ICON ở
   `IconApp.razor.css` nhưng **quên bảng thứ hai** ở đây ⇒ `--ma-mau` ⛔ không ai đặt, mà thân nút dựng
   bằng `color-mix(... var(--ma-mau) ...)` ⇒ **cả khai báo bị vứt** và ô rơi về trắng trơn — đúng bẫy
   *"var không phân giải được thì cả luật chết lặng"* (đợt 831), build xanh và test cũng xanh vì
   `MauOAppKhopIconTests` chỉ chạy được khi cây test lành.
   ⚠ Mã màu phải **TRÙNG KHÍT** icon (`#8f4700`) — hàng rào ấy so từng ký tự, và lệch màu là nút một
   màu icon một màu. */
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket.ma-mau-to-xe[b-45aw0s8ahf]            { --ma-mau: #8f4700; }

:root:not([data-theme^="co-dien"]) .ma-o-lien-ket[b-45aw0s8ahf] {
    border: 1px solid color-mix(in srgb, var(--ma-mau) 34%, transparent);
    border-radius: .85rem;
    background:
        radial-gradient(120% 78% at 50% 0%, color-mix(in srgb, var(--ma-mau) 13%, #fff) 0%, #fff 60%),
        linear-gradient(180deg, #fff, color-mix(in srgb, var(--ma-mau) 15%, #fff));
    /* Gờ ĐẶC dưới đáy = bề DÀY của phím; bóng mờ bên dưới = nó đang nổi trên mặt bàn. Thiếu gờ đặc thì
       chỉ còn bóng mờ, mắt đọc thành "ảnh có đổ bóng" chứ ⛔ không thành khối. */
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .95),
        inset 0 -1px 0 color-mix(in srgb, var(--ma-mau) 20%, transparent),
        0 3px 0 color-mix(in srgb, var(--ma-mau) 42%, #ffffff),
        0 7px 15px color-mix(in srgb, var(--ma-mau) 24%, transparent),
        0 1px 2px rgba(0, 23, 68, .14);
}

:root:not([data-theme^="co-dien"]) .ma-o-lien-ket:hover[b-45aw0s8ahf] {
    transform: translateY(-3px);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .95),
        inset 0 -1px 0 color-mix(in srgb, var(--ma-mau) 24%, transparent),
        0 6px 0 color-mix(in srgb, var(--ma-mau) 46%, #ffffff),
        0 14px 24px color-mix(in srgb, var(--ma-mau) 30%, transparent),
        0 2px 4px rgba(0, 23, 68, .16);
}

/* BẤM XUỐNG: gờ mỏng lại và nút lún — nửa còn lại của cảm giác 3D; thiếu nó nút chỉ là bức ảnh nổi.
   `:active` nên máy cảm ứng cũng có (ngón tay chạm là trình duyệt bật `:active`). */
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket:active[b-45aw0s8ahf] {
    transform: translateY(2px);
    box-shadow:
        inset 0 2px 4px color-mix(in srgb, var(--ma-mau) 22%, transparent),
        0 1px 0 color-mix(in srgb, var(--ma-mau) 40%, #ffffff),
        0 2px 6px color-mix(in srgb, var(--ma-mau) 22%, transparent);
}

/* Nền xám `#f1f3f4` của `.ma-o-dau:hover` (khai ở trên) phủ kín lớp màu vừa dựng ⇒ tắt nó ở ba giao
   diện hiện đại. ⛔ Đừng xoá luật gốc: hai giao diện cổ điển vẫn đang dùng đúng nền xám ấy. */
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket .ma-o-dau:hover[b-45aw0s8ahf],
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket .ma-o-dau:focus-visible[b-45aw0s8ahf] { background: transparent; }

/* Chip màu sau icon. `::deep` vì <svg> do IconApp vẽ (bẫy (2) ở đầu khối). `content-box` để phần đệm
   ⛔ không bóp nhỏ nét vẽ — nét mảnh bị thu lại là icon mất luôn dáng. */
:root:not([data-theme^="co-dien"]) .ma-o-lien-ket .ma-o-dau[b-45aw0s8ahf]  svg {
    box-sizing: content-box;
    padding: 5px;
    border-radius: 10px;
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--ma-mau) 10%, #fff),
        color-mix(in srgb, var(--ma-mau) 22%, #fff));
    box-shadow: inset 0 1px 0 #fff, 0 1px 2px color-mix(in srgb, var(--ma-mau) 38%, transparent);
}

/* NềN TỐI: cùng một biến màu, nhưng pha vào mặt tối #262a31 chứ ⛔ không pha vào trắng — pha trắng là
   nút sáng loá trên nền tối, đúng lỗi mà dự án đã mất một đợt riêng đi vá 43 tệp. */
:root[data-theme="dark"] .ma-o-lien-ket[b-45aw0s8ahf] {
    border-color: color-mix(in srgb, var(--ma-mau) 45%, #262a31);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--ma-mau) 20%, #2d323a),
        color-mix(in srgb, var(--ma-mau) 12%, #262a31));
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .06),
        0 3px 0 color-mix(in srgb, var(--ma-mau) 34%, #14161a),
        0 7px 15px rgba(0, 0, 0, .40);
}
:root[data-theme="dark"] .ma-o-lien-ket .ma-o-dau[b-45aw0s8ahf]  svg {
    background: color-mix(in srgb, var(--ma-mau) 24%, #262a31);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08);
}

/* Máy khai "hạn chế chuyển động": bỏ NHÙN và bỏ LÚN, giữ nguyên màu và gờ (gờ là hình KHỐI, ⛔ không
   phải chuyển động). Nối tiếp khối @media đã có ở trên, ⛔ không thay nó. */
@media (prefers-reduced-motion: reduce) {
    :root:not([data-theme^="co-dien"]) .ma-o-lien-ket:hover[b-45aw0s8ahf],
    :root:not([data-theme^="co-dien"]) .ma-o-lien-ket:active[b-45aw0s8ahf] { transform: none; }
}
/* /Components/CoDien/TabTrangCuaApp.razor.rz.scp.css */
/* ═════════ DẢI TAB CÁC TRANG CỦA APP ĐANG MỞ — CSS RIÊNG CỦA COMPONENT (đợt 650) ═════════

   ⛔ CỐ Ý KHÔNG viết dòng nào vào `wwwroot/app.css`: đó là tệp dùng chung rủi ro cao nhất (quy tắc #18).
      CSS cách ly của Blazor tự gắn dấu phạm vi nên không luật nào ở đây rò ra ngoài component.

   🎨 MÀU LẤY THEO MÀU CHỮ ĐANG THỪA KẾ, không ghim mã màu nào — nền tô bằng XÁM TRUNG TÍNH có độ trong
      (`rgba(127,127,127,…)`). Nhờ vậy một bộ luật chạy đúng ở CẢ 4 GIAO DIỆN (sáng · tối · cổ điển ·
      cổ điển tối) mà không phải khai màu bốn lần, và tự hợp với mọi bộ màu chủ đề.
      ⓘ Chữ của tab kế thừa màu chữ của thanh nav — thứ đã được đo tương phản sẵn ở app.css mục 7.
      ⛔ Đừng "làm rõ hơn" bằng cách gõ mã màu cứng: đó đúng là cách sinh ra đợt đi vá 43 tệp lóa trắng.

   ⚠ DẢI NÀY ĐỨNG TRONG `.top-row` cùng với dải tab CỦA TRANG (`.mcd-dai-tab`, đợt 422). Hai dải khác
     việc nhau: dải kia là tab BÊN TRONG một trang, dải này là các TRANG của cùng app.
     ⇒ `flex: 0 1 auto` + `min-width: 0`: dải này NHƯỜNG chỗ trước (co lại và tự cuộn ngang), để dải tab
       của trang — thứ người dùng đang thao tác — giữ được chỗ. Chật nữa thì cả hai cùng cuộn. */

.tta-dai[b-9pr0xx9a22] {
    display: flex;
    align-items: center;
    gap: .15rem;
    /* Nhường chỗ: co được, không tự nở. min-width 0 để `overflow` thật sự cắt được trong hộp flex. */
    flex: 0 1 auto;
    min-width: 0;
    overflow-x: auto;
    /* ⭐ ĐỢT 679 — THANH CUỘN MẢNH THAY VÌ ẨN HẲN. Anh Tiến 15/08/2026: *"khi anh muốn vào tab công cụ
       dev thì phải THU NHỎ LẠI 150%"*.
       📏 Gốc lỗi: dải NÀY VỐN ĐÃ cuộn ngang được từ đợt 650 — nhưng `scrollbar-width: none` giấu sạch
         thanh cuộn, nên không có một dấu hiệu nào cho biết còn tab ở bên phải, cũng không có gì để kéo.
         Người dùng chỉ còn một cách: thu nhỏ cả trang cho lọt hết vào màn hình.
       ⇒ Hiện thanh cuộn ở dạng MẢNH (~5px) + mặt nạ mờ dần hai mép: nhìn là biết còn nữa, chuột kéo
         được, mà vẫn không ăn mất chiều cao của thanh nav.
       ⛔ Đừng quay lại `scrollbar-width: none` cho "gọn" — đó chính là thứ vừa sửa. */
    scrollbar-width: thin;
    scrollbar-color: rgba(127, 127, 127, .45) transparent;
    /* Mặt nạ: mờ dần 12px ở hai mép ⇒ chữ bị cắt trông "còn tiếp" chứ không như bị hỏng. */
    mask-image: linear-gradient(to right, transparent 0, black 12px,
                                black calc(100% - 12px), transparent 100%);
    /* Cuộn mượt khi bấm tab hoặc lăn chuột ngang. */
    scroll-behavior: smooth;
    /* Cuộn trong dải không được kéo cả trang trôi theo khi đã chạm mép. */
    overscroll-behavior-x: contain;
    /* Vạch ngăn với cụm logo bên trái — mảnh, theo màu chữ nên hợp cả 4 giao diện. */
    padding-left: .5rem;
    border-left: 1px solid rgba(127, 127, 127, .35);
}

/* Chrome/Edge: thanh cuộn mảnh, chỉ hiện con trượt — cùng chủ ý với `scrollbar-width: thin` ở trên. */
.tta-dai[b-9pr0xx9a22]::-webkit-scrollbar { height: 5px; }
.tta-dai[b-9pr0xx9a22]::-webkit-scrollbar-track { background: transparent; }
.tta-dai[b-9pr0xx9a22]::-webkit-scrollbar-thumb { background: rgba(127, 127, 127, .45); border-radius: 3px; }
.tta-dai[b-9pr0xx9a22]::-webkit-scrollbar-thumb:hover { background: rgba(127, 127, 127, .65); }

.tta-tab[b-9pr0xx9a22] {
    display: inline-flex;
    align-items: center;
    /* ⭐ ĐỢT 656 — TRẢ LẠI TÊN ĐẦY ĐỦ (anh Tiến: *"nếu có đủ chỗ thì hiện đủ tên"*). Dải này nay chỉ
       xuất hiện ở trang KHÔNG có tab riêng, tức lúc nó là lối đi duy nhất sang trang anh em — mà một lối
       đi thì phải đọc được tên. Chật thì dải tự cuộn ngang (khai ở `.tta-dai`), không cắt chữ. */
    gap: .3rem;
    /* ⭐ ĐỢT 976 — CỠ CHỮ LẤY TỪ KHUÔN NÚT THANH NAV, ⛔ đừng gõ lại số ở đây. Anh Tiến 22/08/2026:
       *"làm đồng nhất tất cả các nút trên thanh nav, mỗi app lại có một kiểu nút khác nhau"*.
       📏 Trước đợt này dải NÀY chữ .84rem / đệm .25rem .55rem ⇒ cao 26,8px, trong khi dải tab của
         TRANG (`.mcd-dai-tab .nav-link`) cao 32,6px — mà hai dải KHÔNG bao giờ hiện cùng lúc (luật ẩn
         `:has()` ở cuối tệp này), nên người dùng đi từ app có tab riêng sang app không có là thấy nút
         đổi cỡ. Nay cả hai lấy chung bộ biến `--nav-nut-*` khai ở `app.css`.
       ⚠ Biến kế thừa theo cây DOM nên dùng được thẳng trong CSS cách ly; app.css còn ép lần nữa bằng
         `!important` để lớp cỡ tiện dụng của Bootstrap không lách vào được. */
    font-size: var(--nav-nut-chu, .875rem);
    /* Tab KHÔNG được xuống dòng: thanh nav cao cố định, một tab gãy đôi là vỡ cả dải. */
    white-space: nowrap;
    flex: none;
    /* ⭐ ĐỢT 976 — đệm và bo góc cũng lấy từ khuôn chung (xem chú thích cỡ chữ ở trên). Giá trị lui
       giữ đúng số của khuôn, ⛔ không phải số cũ của riêng dải này. */
    padding: var(--nav-nut-dem-doc, .3rem) var(--nav-nut-dem-ngang, .65rem);
    min-height: var(--nav-nut-cao, 2.05rem);
    border-radius: var(--nav-nut-bo, .45rem);
    line-height: 1.25;
    color: inherit;
    text-decoration: none;
}

/* ⚠ `.top-row ::deep a:hover { text-decoration: underline }` của MainLayout với tới được thẻ này (bộ
   chọn hậu duệ, `::deep`) — ép bỏ gạch chân để tab không nhảy chữ khi rê chuột; nền đổi là đủ tín hiệu. */
.tta-tab:hover[b-9pr0xx9a22],
.tta-tab:focus-visible[b-9pr0xx9a22] {
    background: rgba(127, 127, 127, .20);
    text-decoration: none;
    color: inherit;
}

/* TAB ĐANG MỞ: đậm hơn + gạch chân dày dưới đáy. CỐ Ý dùng HAI kênh (nền + gạch), không chỉ màu —
   người phân biệt màu kém vẫn thấy được mình đang ở đâu. */
.tta-dang-mo[b-9pr0xx9a22] {
    background: rgba(127, 127, 127, .28);
    font-weight: 600;
    box-shadow: inset 0 -2px 0 0 currentColor;
}

.tta-dang-mo:hover[b-9pr0xx9a22] { background: rgba(127, 127, 127, .32); }

/* ⭐ ĐỢT 760 (việc f28) — ĐỒNG NHẤT với dạng nút tab của trang (anh Tiến 17/08, ảnh /cai-dat-cham-cong:
   *"các loại nút khác nhau không đồng nhất"*). Thẻ <a> nay mang thêm lớp dùng chung `nut-noi` (khuôn 3D
   đợt 447/680 trong app.css) ⇒ viền + bo góc + nổi lấy từ MỘT nguồn, không chép công thức vào đây.
   Khối dưới chỉ lo trạng thái ĐANG MỞ: navy gradient chữ trắng — đúng cặp màu đã đo ≥ 8:1 của tab thanh
   nav (app.css mục 4). CHỈ hai giao diện hiện đại; cổ điển (đã đóng băng, quy tắc #20) giữ chip xám cũ.
   ⚠ box-shadow là MỘT thuộc tính nên phải NHẮC LẠI vạch gạch dưới `currentColor` (kênh thứ hai cho người
   kém phân biệt màu) ghép cùng bóng nổi — tách rời là luật `nut-noi` (đặc hiệu cao hơn) nuốt mất vạch. */
:root:not([data-theme^="co-dien"]) .tta-dang-mo[b-9pr0xx9a22] {
    background-color: #0a58ca;
    background-image: linear-gradient(120deg, #0a3d91, #002169);
    color: #fff;
    box-shadow: inset 0 -2px 0 0 currentColor,
                inset 0 1px 0 rgba(255, 255, 255, .38),
                0 2px 3px rgba(0, 0, 0, .2);
}

:root:not([data-theme^="co-dien"]) .tta-dang-mo:hover[b-9pr0xx9a22] {
    background-color: #0a58ca;
    background-image: linear-gradient(120deg, #0a3d91, #002169);
    color: #fff;
}

/* ⛔⛔ ĐỢT 656 — TRANG ĐÃ CÓ DẢI TAB RIÊNG THÌ **BỎ HẲN** DẢI NÀY. ĐỌC TRƯỚC KHI SỬA.

   Anh Tiến 14/08/2026, sau khi xem bản đợt 654: *"vẫn có 2 nút trùng lặp, nếu có đủ chỗ thì hiện đủ tên"*.

   📌 ĐỢT 654 CHỮA SAI CHỖ. Lúc đó tôi cắt chữ của dải này cho đỡ chật — nhưng cái sai không phải là DÀI,
      mà là **TRÙNG**: ở trang nước, sáu biểu tượng của dải này với sáu tab có đồ họa của trang là **đúng
      một danh sách**. Cắt chữ chỉ làm bản sao gọn hơn chứ vẫn là bản sao, và còn tệ thêm một bậc — người
      dùng phải đoán sáu hình không tên đó là gì.
   ⇒ Chữa đúng: **trang nào tự bày tab của nó rồi thì dải này biến mất hẳn**; trang nào KHÔNG có (đa số
      trang) thì dải này hiện **ĐỦ TÊN**, vì lúc đó nó là thứ duy nhất dẫn sang trang anh em.

   CƠ CHẾ: `.mcd-dai-tab` là ô mà mỗi trang tự đẩy tab của mình vào (`SectionOutlet`); trang không có tab
   thì ô đó RỖNG. `:has()` cho phép hỏi ngược lên thanh nav: *"trong thanh có ô tab nào KHÔNG rỗng không?"*.
   ⚠ Ô rỗng vẫn chứa chú thích `<!--!-->` do Blazor chèn — chú thích KHÔNG tính là con nên `:empty` vẫn đúng.
   ⚠ Trình duyệt quá cũ không hiểu `:has()` thì luật bị bỏ qua ⇒ quay về hiện cả hai dải — **xấu chứ không
     hỏng**, và không mất lối đi nào. Đây là kiểu thoái lui đúng cho một luật trang trí. */
:root .top-row:has(.mcd-dai-tab:not(:empty)) .tta-dai[b-9pr0xx9a22] { display: none; }

/* Khổ điện thoại: dải tab của TRANG quan trọng hơn — dải này ẩn hẳn, lối đi vẫn còn ở lưới 9 chấm.
   ⓘ Cố ý ẩn chứ không nén tiếp: dưới 820px thanh nav đã phải xuống dòng, thêm một dải cuộn ngang nữa là
     hai dải cùng cuộn cạnh nhau — không ai bấm trúng cái nào. */
@media (max-width: 820px) {
    .tta-dai[b-9pr0xx9a22] { display: none; }
}
/* /Components/Layout/ChuongThongBao.razor.rz.scp.css */
/* ═══ 🔔 BẢNG THẢ CỦA CHUÔNG THÔNG BÁO — góc phải thanh nav ═══
   Anh Tiến 18/08/2026: *"Khi nhấn vào chuông báo xem thông báo thì sẽ sổ xuống danh sách thông báo
   cho phép cuộn."*

   ⚠ ĐẶT Ở CSS PHẠM VI COMPONENT (.razor.css), KHÔNG đổ vào wwwroot/app.css: app.css là tệp DÙNG CHUNG
     rủi ro lan lỗi cao nhất (quy tắc #18). Ở đây Blazor gắn thêm dấu phạm vi [b-xxxxx] cho mọi luật nên
     KHÔNG thể chạm tới phần tử nào ngoài component này.

   ⚠ HAI GIAO DIỆN: sáng và tối (quy tắc #20 — từ đợt 808 chỉ còn `light` và `dark`; hai bản cổ điển đã
     ngưng phát triển và người dùng được tự chuyển sang hiện đại). Vẫn bắt kèm `co-dien-toi` y như
     `TheTaiKhoan.razor.css` bên cạnh: máy nào chưa kịp qua lượt chuyển thì vẫn ra nền tối đúng, chi phí
     bằng không.

   📏 TƯƠNG PHẢN CHỮ (WCAG 2.1 — chữ thường cần ≥ 4,5:1). Bảng màu lấy NGUYÊN của `TheTaiKhoan.razor.css`
     để hai bảng thả cạnh nhau trên cùng thanh nav không lệch tông, và để khỏi phải tính lại một bộ số mới:
     · Nền sáng: chữ #212529 trên #ffffff ........ 15,43:1
                 chữ phụ #5c636a trên #ffffff .....  6,09:1
                 liên kết #0a58ca trên #ffffff ....  6,44:1
     · Nền tối:  chữ #dde1e6 trên #262a31 ........ 10,97:1
                 chữ phụ #adb5bd trên #262a31 .....  6,94:1
                 liên kết #6bb3ff trên #262a31 ....  6,53:1
   ⓘ Nền của mục CHƯA ĐỌC cố ý dùng alpha rất thấp (.06 / .10) nên tỷ lệ trên gần như không đổi — nó là
     dấu hiệu phụ, còn dấu hiệu chính là CHẤM tròn + chữ đậm (⛔ không bao giờ chỉ dựa vào màu).
   ⛔ Đổi bất kỳ mã màu nào ở dưới thì phải TÍNH LẠI và sửa luôn bảng số này. */

.tb-boc[b-wve4mie4dx] {
    position: relative;
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    z-index: 1050;

    --tb-nen-bang: #ffffff;
    --tb-vien: #dee2e6;
    --tb-chu: #212529;
    --tb-chu-phu: #5c636a;
    --tb-lienket: #0a58ca;
    --tb-ro-chuot: rgba(0, 0, 0, .05);
    --tb-nen-chua-doc: rgba(13, 110, 253, .06);
    --tb-bong: 0 .5rem 1.5rem rgba(0, 0, 0, .18);
}

:root[data-theme="dark"] .tb-boc[b-wve4mie4dx],
:root[data-theme="co-dien-toi"] .tb-boc[b-wve4mie4dx] {
    --tb-nen-bang: #262a31;
    --tb-vien: #3a3f47;
    --tb-chu: #dde1e6;
    --tb-chu-phu: #adb5bd;
    --tb-lienket: #6bb3ff;
    --tb-ro-chuot: rgba(255, 255, 255, .08);
    --tb-nen-chua-doc: rgba(107, 179, 255, .10);
    --tb-bong: 0 .5rem 1.5rem rgba(0, 0, 0, .55);
}

/* ───── Ô ĐÁNH DẤU ẨN = chính cái nút bấm ─────
   Trong suốt nhưng PHỦ KÍN mặt chuông, nên bấm vào đâu trong nút cũng trúng nó.
   ⛔ KHÔNG giấu bằng display:none / visibility:hidden — hai cái đó làm phần tử MẤT KHẢ NĂNG NHẬN TIÊU
      ĐIỂM ⇒ không Tab tới được, không bấm Space mở được. */
.tb-cong-tac[b-wve4mie4dx] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    opacity: 0;
    cursor: pointer;
    z-index: 3;
}

/* Vòng tiêu điểm vẽ lên NÚT (ô đánh dấu vô hình nên tự nó không hiện được vòng nào).
   :focus-visible chứ không :focus — bấm chuột thì không ai muốn thấy vòng viền. */
.tb-cong-tac:focus-visible ~ .tb-nut[b-wve4mie4dx] {
    outline: 3px solid var(--tb-lienket);
    outline-offset: 2px;
}

/* `.the-bang-ten` (app.css) cấp dáng nút nổi 3D, nhưng luật padding của nó chỉ nhắm thẻ `a`
   (`.top-row a.the-bang-ten`) — ở đây là `label` nên phải tự cấp cho bằng nút bên cạnh. */
.tb-nut[b-wve4mie4dx] {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    /* ⭐ ĐỢT 976 — ĐỆM VÀ CHIỀU CAO LẤY TỪ KHUÔN NÚT THANH NAV (`--nav-nut-*` khai ở app.css).
       📏 Đo trước đợt này: chuông cao 30,4px trong khi dải tab bên cạnh cao 32,6px — hai nút đứng
         cùng một hàng mà lệch nhau 2,2px, thấy rõ ở mép trên/dưới. ⛔ Đừng gõ lại số ở đây. */
    padding: var(--nav-nut-dem-doc, .3rem) var(--nav-nut-dem-ngang, .65rem);
    min-height: var(--nav-nut-cao, 2.05rem);
    margin: 0;
    cursor: pointer;
    user-select: none;
    line-height: 1.2;
}

/* ═══ ⭐ ĐỢT 1141 — CHUÔNG NỔI KHỐI 3D + LẮC ĐÁNH ĐỘNG KHI CÓ TIN ═══
   Anh Tiến 23/08/2026: *"nút chuông thông báo, em đồ họa chuông lắc lắc có hiệu ứng 3D để đánh động
   khi có thông báo nhé"*.

   Ba lớp, mỗi lớp một việc — bỏ lớp nào là mất một nửa ý:
   ① KHỐI 3D cho mặt chuông: `drop-shadow` xếp chồng (gờ sáng mỏng ở trên + bóng đổ xuống dưới). ⚠ Phải
     là `drop-shadow` chứ KHÔNG `box-shadow`/`text-shadow` — 🔔 là emoji ẢNH MÀU (glyph có màu sẵn):
     `text-shadow` vẽ theo ô chữ vuông nên ra một vệt xám hình hộp phía sau, còn `drop-shadow` bám đúng
     đường viền cái chuông. Ở cỡ 16px thì hai cái này khác nhau rõ bằng mắt.
   ② LẮC: xoay quanh QUAI CHUÔNG (`transform-origin: 50% 12%`) — chuông thật treo ở quai, xoay quanh
     tâm thì thành cái nút bị vặn, không ra chuông rung. Bốn nhịp thuận-nghịch nhỏ dần trong ~1,1s rồi
     ĐỨNG IM 2,9s: đủ để lọt vào tầm mắt ngoại vi mà không biến thanh nav thành chỗ nhấp nháy cả ngày
     (cùng lẽ nhịp thưa với vệt quét chip logo đợt 893 và khối cầu avatar đợt 1141).
   ③ HUY HIỆU SỐ ĐỎ đập nhịp + vòng bóng đỏ lan ra — thứ "đánh động" được ở khoảng cách xa, vì cái lắc
     14 độ của một icon 16px thì chỉ thấy khi đang nhìn thẳng vào nó. ⚠ Lớp này **ĐÃ CÓ SẴN** ở app.css
     mục 6 (`plx-nhip`) — ⛔ đừng chồng thêm nhịp thứ hai ở đây, xem chú thích ngay dưới khối lắc.

   ⛔ Phần động CHỈ chạy khi có tin (`.tb-co-tin`, do Razor gắn theo `SoChuaDoc > 0`), ngưng khi bảng
     đang mở, và tắt sạch ở máy khai "giảm chuyển động" — xem khối `prefers-reduced-motion` cuối tệp. */
.tb-icon[b-wve4mie4dx] {
    font-size: 1rem;
    line-height: 1;
    /* Chuyển động cần khối hình riêng: `<span>` inline thì `transform` không có tác dụng. */
    display: inline-block;
    transform-origin: 50% 12%;
    filter:
        drop-shadow(0 -1px 0 rgba(255, 255, 255, .85))
        drop-shadow(0 1px 1px rgba(0, 23, 68, .30))
        drop-shadow(0 2px 3px rgba(0, 23, 68, .32));
}

:root[data-theme="dark"] .tb-icon[b-wve4mie4dx],
:root[data-theme="co-dien-toi"] .tb-icon[b-wve4mie4dx] {
    filter:
        drop-shadow(0 -1px 0 rgba(255, 255, 255, .22))
        drop-shadow(0 1px 1px rgba(0, 0, 0, .55))
        drop-shadow(0 2px 4px rgba(0, 0, 0, .60));
}

.tb-co-tin .tb-icon[b-wve4mie4dx] {
    animation: tb-lac-chuong-b-wve4mie4dx 4s ease-in-out infinite;
}

/* Biên độ nhỏ dần (14° → 10° → 6° → 3°) = quả lắc tắt dần. Nhảy thẳng về 0 ở nhịp cuối là ra cái giật
   cơ học, đúng thứ mắt đọc là "đồ họa", không phải "vật rung". */
@keyframes tb-lac-chuong-b-wve4mie4dx {
    0%, 72%, 100% { transform: rotate(0deg); }
    74%  { transform: rotate(14deg); }
    77%  { transform: rotate(-10deg); }
    80%  { transform: rotate(7deg); }
    83%  { transform: rotate(-5deg); }
    86%  { transform: rotate(3deg); }
    89%  { transform: rotate(-2deg); }
    92%  { transform: rotate(0deg); }
}

/* ⛔ HUY HIỆU SỐ ĐỎ: KHÔNG ĐỤNG VÀO — app.css mục 6 ĐÃ CÓ SẴN nhịp đập `plx-nhip` (2,6s: phóng 1,1×
   kèm một vòng bóng đỏ lan ra rồi tắt). Bản thảo đầu của đợt 1141 có thêm một nhịp đập + một vòng
   sóng `::after` nữa ở đây; ĐO SỐNG trên bản tĩnh cho thấy phải gỡ, vì hai lẽ:
   ① Luật kia là `:root[data-theme="light"] .top-row .badge.bg-danger` — độ đặc hiệu (0,5,0) THẮNG
     `.tb-nut .badge` (0,3,0) của tệp này ⇒ `animation` và `box-shadow` viết ở đây **chết lặng lẽ**
     (đo được: `animationName` trả về `plx-nhip`, không phải tên keyframes vừa viết).
   ② Riêng vòng sóng `::after` thì KHÔNG chết (pseudo là phần tử khác, không ai tranh) ⇒ kết quả là
     HAI vòng sóng chạy lệch pha nhau (2,6s và 4s) trên cùng một huy hiệu — rối mắt, đúng thứ cần
     tránh. ⇒ Muốn đổi nhịp huy hiệu thì sửa `plx-nhip` ở app.css, ⛔ đừng chồng thêm nhịp thứ hai. */

/* RÊ CHUỘT / BẤM trên nút chuông.
   🔴 Bám vào `.tb-cong-tac` chứ KHÔNG phải `.the-bang-ten:hover`: ô đánh dấu phủ KÍN nút với
     `z-index: 3` và nó là ANH EM của `.tb-nut` chứ không phải tổ tiên ⇒ con trỏ luôn đứng trên ô đánh
     dấu, `.tb-nut` không nhận `:hover` ⇒ toàn bộ phần `:hover`/`:active` của khuôn `.the-bang-ten`
     (app.css đợt 447) chưa từng chạy được ở nút này. Cùng một bệnh với nút ảnh đại diện bên cạnh. */
.tb-cong-tac:hover ~ .tb-nut[b-wve4mie4dx] {
    transform: translateY(-1px);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .95),
        inset 0 -1px 0 rgba(0, 23, 68, .10),
        0 2px 2px rgba(0, 23, 68, .12),
        0 6px 12px rgba(0, 23, 68, .24);
}

.tb-cong-tac:active ~ .tb-nut[b-wve4mie4dx] {
    transform: translateY(1px);
    box-shadow:
        inset 0 1px 2px rgba(0, 23, 68, .22),
        0 1px 1px rgba(0, 23, 68, .10);
}

:root[data-theme="dark"] .tb-cong-tac:hover ~ .tb-nut[b-wve4mie4dx],
:root[data-theme="co-dien-toi"] .tb-cong-tac:hover ~ .tb-nut[b-wve4mie4dx] {
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .14),
        inset 0 -1px 0 rgba(0, 0, 0, .35),
        0 2px 3px rgba(0, 0, 0, .38),
        0 7px 14px rgba(0, 0, 0, .5);
}

:root[data-theme="dark"] .tb-cong-tac:active ~ .tb-nut[b-wve4mie4dx],
:root[data-theme="co-dien-toi"] .tb-cong-tac:active ~ .tb-nut[b-wve4mie4dx] {
    box-shadow:
        inset 0 1px 3px rgba(0, 0, 0, .55),
        0 1px 1px rgba(0, 0, 0, .35);
}

/* ĐANG MỞ BẢNG: nút giữ trạng thái ấn xuống, và chuông NGƯNG LẮC — người ta đang xem đúng cái danh
   sách mà nó gọi, tiếp tục vẫy tay là thừa. */
.tb-cong-tac:checked ~ .tb-nut[b-wve4mie4dx] {
    transform: translateY(1px);
    box-shadow:
        inset 0 1px 2px rgba(0, 23, 68, .22),
        0 1px 1px rgba(0, 23, 68, .10);
}

.tb-cong-tac:checked ~ .tb-nut .tb-icon[b-wve4mie4dx] {
    animation: none;
}

/* Máy khai "hạn chế chuyển động": bỏ cái lắc và phép nhô/lún, GIỮ NGUYÊN phần khối (bóng đổ, gờ sáng)
   — họ xin bớt chuyển động chứ không xin bỏ đồ họa.
   ⓘ Nhịp đập của huy hiệu số đỏ do app.css lo, và khối `prefers-reduced-motion` toàn cục ở đó (mục 10,
     đợt 381, `* !important`) đã tắt hộ. */
@media (prefers-reduced-motion: reduce) {
    .tb-co-tin .tb-icon[b-wve4mie4dx] {
        animation: none;
        transform: none;
    }

    .tb-cong-tac:hover ~ .tb-nut[b-wve4mie4dx],
    .tb-cong-tac:active ~ .tb-nut[b-wve4mie4dx],
    .tb-cong-tac:checked ~ .tb-nut[b-wve4mie4dx] {
        transform: none;
    }
}

/* ───── Nền phủ: bấm RA NGOÀI là đóng (thuần CSS) ─────
   ⛔ `height: 100vh` KHÔNG PHẢI THỪA, ĐỪNG DỌN ĐI (bài học đợt 649, có hàng rào canh). Thẻ này nằm
   trong `.top-row`, mà thanh đó khai `backdrop-filter: blur(8px)` (app.css mục 7) ⇒ theo đặc tả CSS,
   thanh nav trở thành KHỐI CHỨA của mọi hậu duệ `position: fixed` ⇒ `inset: 0` chỉ còn phủ đúng dải nav
   cao ~56px, bấm xuống thân trang là bấm TRƯỢT và bảng KHÔNG đóng. Ghi đè chiều cao thì luật "quá ràng
   buộc" của CSS bỏ `bottom`, nền phủ kéo hết một màn hình.
   ⚠ Lỗi chỉ có ở hai giao diện HIỆN ĐẠI — đừng thử ở bản cổ điển rồi kết luận là không có lỗi.
   ⛔ ĐỪNG "chữa" bằng cách nới sang phải (`right: -100vw`): phần trào ra cộng vào vùng cuộn của trang và
      đẻ ra THANH CUỘN NGANG mỗi lần mở bảng.
   🧱 Hàng rào: `TKNB.Tests/NenPhuChuongThongBaoTests.cs`. */
.tb-nen[b-wve4mie4dx] {
    display: none;
    position: fixed;
    inset: 0;
    height: 100vh;
    height: 100dvh;   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */
    z-index: 1040;
    margin: 0;
    cursor: default;
    /* Cố ý KHÔNG tô màu: đây là bảng nhỏ ở góc, làm tối cả màn hình sau lưng nó là quá tay. */
    background: transparent;
}

/* ───── Bảng thả ───── */
.tb-bang[b-wve4mie4dx] {
    display: none;
    position: absolute;
    top: calc(100% + .55rem);
    right: 0;
    z-index: 1050;
    width: 24rem;
    /* Không bao giờ tràn khỏi màn hình hẹp: mép phải đã ghim, nên chỉ cần chặn bề rộng.
       Thân trang KHÔNG được cuộn ngang — đó là lý do có dòng này. */
    max-width: calc(100vw - 1rem);
    background-color: var(--tb-nen-bang);
    color: var(--tb-chu);
    border: 1px solid var(--tb-vien);
    border-radius: .6rem;
    box-shadow: var(--tb-bong);
    text-align: left;
    overflow: hidden;   /* cho hai góc dưới của vùng cuộn bám theo bo góc của bảng */
}

.tb-cong-tac:checked ~ .tb-nen[b-wve4mie4dx],
.tb-cong-tac:checked ~ .tb-bang[b-wve4mie4dx] {
    display: block;
}

/* ───── Đầu bảng ───── */
.tb-dau[b-wve4mie4dx] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .35rem .5rem;
    padding: .6rem .75rem;
    border-bottom: 1px solid var(--tb-vien);
}

.tb-tieu-de[b-wve4mie4dx] {
    font-weight: 600;
    font-size: .95rem;
    color: var(--tb-chu);
}

.tb-dem[b-wve4mie4dx] {
    font-size: .8rem;
    margin-right: auto;   /* đẩy nút "đọc hết" về mép phải */
}

.tb-dem-so[b-wve4mie4dx] { color: var(--tb-lienket); font-weight: 600; }
.tb-dem-het[b-wve4mie4dx] { color: var(--tb-chu-phu); }

.tb-nut-doc-het[b-wve4mie4dx] {
    background: none;
    border: 1px solid var(--tb-vien);
    border-radius: .35rem;
    padding: .2rem .45rem;
    font: inherit;
    font-size: .78rem;
    color: var(--tb-lienket);
    cursor: pointer;
    white-space: nowrap;
}

.tb-nut-doc-het:hover:not(:disabled)[b-wve4mie4dx] { background-color: var(--tb-ro-chuot); }

.tb-nut-doc-het:disabled[b-wve4mie4dx] {
    color: var(--tb-chu-phu);
    cursor: default;
    opacity: .65;
}

/* ───── VÙNG CUỘN — đúng thứ anh Tiến đặt hàng ("cho phép cuộn") ─────
   `min()` để bảng không bao giờ cao quá 60% khung nhìn (màn hình thấp vẫn thấy được mép dưới + còn chỗ
   bấm ra ngoài) mà cũng không dài lê thê trên màn hình cao — 26rem ≈ 420px là khoảng 5–6 mục.
   `overscroll-behavior: contain`: cuộn hết danh sách thì DỪNG, không đẩy tiếp sang cuộn cả trang phía
   sau — trang bên dưới trôi mất chỗ đang đọc là cảm giác hỏng rõ nhất của một bảng thả. */
.tb-ds[b-wve4mie4dx] {
    max-height: min(60vh, 26rem);
    overflow-y: auto;
    overscroll-behavior: contain;
}

.tb-trong[b-wve4mie4dx] {
    padding: 1.1rem .75rem;
    text-align: center;
    color: var(--tb-chu-phu);
    font-size: .85rem;
}

/* ───── Một mục ─────
   Là `<button>` nên phải gỡ sạch dáng mặc định của trình duyệt trước khi dựng lại. */
.tb-muc[b-wve4mie4dx] {
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    width: 100%;
    padding: .55rem .75rem;
    background: none;
    border: 0;
    border-bottom: 1px solid var(--tb-vien);
    border-left: 3px solid transparent;   /* giữ sẵn chỗ để mục chưa đọc không bị xê ngang */
    font: inherit;
    color: var(--tb-chu);
    text-align: left;
    cursor: pointer;
}

.tb-muc:last-child[b-wve4mie4dx] { border-bottom: 0; }

.tb-muc:hover[b-wve4mie4dx] { background-color: var(--tb-ro-chuot); }

.tb-muc:focus-visible[b-wve4mie4dx] {
    outline: 2px solid var(--tb-lienket);
    outline-offset: -2px;
}

/* Mục CHƯA ĐỌC — ba dấu hiệu chồng nhau (nền nhạt + vạch trái + chữ đậm + chấm tròn bên phải).
   ⛔ Không bao giờ chỉ dùng MÀU làm dấu hiệu: người phân biệt màu kém sẽ không thấy gì. */
.tb-chua-doc[b-wve4mie4dx] {
    background-color: var(--tb-nen-chua-doc);
    border-left-color: var(--tb-lienket);
}

.tb-chua-doc .tb-muc-tieu-de[b-wve4mie4dx] { font-weight: 700; }

.tb-muc-icon[b-wve4mie4dx] {
    flex-shrink: 0;
    line-height: 1.35;
}

.tb-muc-than[b-wve4mie4dx] {
    display: block;
    flex: 1 1 auto;
    min-width: 0;   /* cho phép cắt chữ dài thay vì đẩy bảng phình ra */
}

.tb-muc-dau[b-wve4mie4dx] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
}

.tb-muc-tieu-de[b-wve4mie4dx] {
    font-size: .875rem;
    font-weight: 500;
    overflow-wrap: anywhere;
}

.tb-muc-gio[b-wve4mie4dx] {
    flex-shrink: 0;
    font-size: .74rem;
    color: var(--tb-chu-phu);
    white-space: nowrap;
}

/* Nội dung dài thì cắt còn 2 dòng — bảng thả là chỗ LIẾC, đọc đủ thì đã có trang /thong-bao. */
.tb-muc-noi-dung[b-wve4mie4dx] {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    margin-top: .12rem;
    font-size: .8rem;
    color: var(--tb-chu-phu);
    overflow-wrap: anywhere;
}

.tb-cham[b-wve4mie4dx] {
    flex-shrink: 0;
    width: .5rem;
    height: .5rem;
    margin-top: .38rem;
    border-radius: 50%;
    background-color: var(--tb-lienket);
}

/* ───── Chân bảng ───── */
.tb-chan[b-wve4mie4dx] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .3rem .5rem;
    padding: .5rem .75rem;
    border-top: 1px solid var(--tb-vien);
}

.tb-chan-ghi-chu[b-wve4mie4dx] {
    font-size: .74rem;
    color: var(--tb-chu-phu);
}

/* ⭐ ĐỢT 2004 — anh Tiến 05/09/2026: "nút xem tất cả vẫn còn dạng link và bỏ dấu mũi tên".
   Trước đợt này nó vẽ ra một dòng chữ xanh gạch chân khi rê chuột, tức TRÔNG như chữ chứ không như
   một thứ bấm được — mà nó là lối đi duy nhất từ bảng chuông sang trang Thông báo đầy đủ.
   🔴 VẪN GIỮ THẺ `a href`, KHÔNG đổi sang `button`: thanh nav dựng sẵn rồi mới nối mạch Blazor, nên
      trong ~1 giây đầu mỗi lượt tải trang chỉ thẻ neo mới bấm được. Lý do đầy đủ ở đầu tệp .razor
      ("KHÔNG có ngõ cụt"). Đợt này chỉ đổi CÁCH VẼ, không đổi cơ chế.
   🎨 Màu lấy từ chính bộ biến của component nên tự đúng ở cả hai nền: nền sáng ra chữ trắng trên
      #0a58ca (6,44:1), nền tối ra chữ #262a31 trên #6bb3ff (6,54:1) — cả hai đạt WCAG 2.1 AA. */
.tb-xem-tat-ca[b-wve4mie4dx] {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: .3rem .75rem;
    border: 1px solid var(--tb-lienket);
    border-radius: .375rem;
    font-size: .82rem;
    font-weight: 600;
    color: var(--tb-nen-bang);
    background-color: var(--tb-lienket);
    text-decoration: none;
    white-space: nowrap;
}

/* ⛔ Giữ `text-decoration: none` + `color` ở cả hai trạng thái: luật cũ gạch chân khi rê chuột, mà
   gạch chân trên một cái nút là dấu hiệu của CHỮ — đúng thứ anh vừa bảo bỏ. */
.tb-xem-tat-ca:hover[b-wve4mie4dx],
.tb-xem-tat-ca:focus-visible[b-wve4mie4dx] {
    filter: brightness(1.08);
    color: var(--tb-nen-bang);
    text-decoration: none;
}

/* 👆 Vùng chạm ngón tay — ngưỡng đọc từ biến dùng chung `--cham-toi-thieu` của app.css (đợt 1518),
   ⛔ đừng gõ số 44px vào đây. Chỉ áp cho cảm ứng: máy tính giữ nguyên cỡ nút gọn của chân bảng. */
@media (pointer: coarse) {
    .tb-xem-tat-ca[b-wve4mie4dx] { min-height: var(--cham-toi-thieu); }
}

/* ⛔ KHÔNG thêm luật nới bảng sang phải ở màn hình hẹp (kiểu `right: -.25rem`): mép phải đã ghim `right: 0`
   và bề ngang đã chặn bằng `max-width: calc(100vw - 1rem)`, nên bảng vốn không tràn. Nới thêm là đẻ ra
   THANH CUỘN NGANG cho cả trang — đúng cái bẫy hàng rào `NenPhuBamRaNgoaiTests` đã ghi. */
/* /Components/Layout/DaiChaoNav.razor.rz.scp.css */
/* ═══════════ DẢI NGÀY TRÊN THANH NAV (đợt 667 → 674 → ĐỒ HỌA LẠI ở đợt 1155) ═══════════

   ⭐ ĐỢT 1155 — anh Tiến 24/08/2026: *"thứ ngày tháng tại trang chủ em đồ hoạ lại chữ cho đẹp, cách tân
   và hiện đại"*. Nội dung giữ nguyên đúng một dòng thông tin (luật đợt 674), chỉ đổi cách bày:
   ô LỊCH có gáy cam + số ngày, cạnh đó là hai dòng chữ (THỨ viết hoa giãn chữ · tháng-năm nhỏ mờ).

   ⚠ LUẬT SỐNG CÒN VẪN GIỮ: thanh nav CHỈ MỘT HÀNG (đợt 654). Dải này là thứ nhường chỗ trước tiên —
     `flex: 0 1 auto` (co được, KHÔNG nở) + `min-width: 0`. Thiếu `min-width: 0` thì chuỗi chữ dài không
     cho hộp flex co lại và thanh nav vỡ hàng, đúng thứ đợt 654 vừa sửa xong.
   📏 Cao 30px cho ô lịch — thanh nav cao 3.5rem = 56px (biến `--cao-thanh-tren` ở app.css) nên còn dư
     mỗi bên 13px. ⛔ Đừng nới ô lịch quá 34px: qua đó là dải bắt đầu ép chiều cao thanh.

   🎨 MÀU CHỮ ăn theo `currentColor` của thanh nav ⇒ tự đúng ở CẢ HAI giao diện mà không khai màu hai
     lần. Riêng Ô LỊCH có nền riêng nên phải khai bảng màu — làm bằng BIẾN, đổi một chỗ là đổi cả hai.
   📏 Tương phản chữ TRONG ô lịch (WCAG 2.1): trắng #ffffff trên navy #0a3d91 = 9,17:1 (nền sáng) ·
     trắng trên #12459c = 8,17:1 (nền tối). Cả hai vượt xa ngưỡng 4,5:1 của chữ thường.
     ⛔ ĐỪNG đổi nền ô lịch sang CAM #F47B20: trắng trên cam chỉ 2,9:1 — dưới ngưỡng, mà số ngày 16px
       chưa đủ to để được tính là "chữ lớn" (ngưỡng 3:1 đòi ≥18,66px đậm). Cam vẫn có mặt, nhưng ở
       GÁY LỊCH — vạch đặc không mang chữ nên không có ràng buộc tương phản chữ. */

.dai-chao-nav[b-lb0ivvqctw] {
    display: flex;
    align-items: center;
    gap: .45rem;
    /* Co được, không nở — dải tab mới là thứ được nở ra lấp chỗ. */
    flex: 0 1 auto;
    min-width: 0;
    line-height: 1.15;
    /* Nằm trong cụm bên phải (đợt 670): mép phải thẳng với 🔔 và ô tài khoản, ngăn cách bằng một đường
       mảnh thay vì khoảng trống chết.
       ⚠ Viền lấy `currentColor` + độ mờ, ⛔ không gõ rgba(255,255,255,…): thanh nav ở giao diện SÁNG có
         nền trắng nên viền trắng ở đó là viền VÔ HÌNH (đúng lỗi bản trước đợt 1155). */
    padding-right: .75rem;
    border-right: 1px solid currentColor;
    border-right-color: color-mix(in srgb, currentColor 22%, transparent);

    --dcn-lich-nen: linear-gradient(160deg, #124fb0, #0a3d91 62%, #072f70);
    --dcn-lich-vien: rgba(255, 255, 255, .22);
    --dcn-lich-bong: 0 1px 1px rgba(0, 23, 68, .18), 0 3px 7px rgba(0, 23, 68, .26);
    --dcn-lich-tren: rgba(255, 255, 255, .38);   /* gờ sáng mép trên — vật LỒI hứng sáng ở trên */
    --dcn-gay: linear-gradient(90deg, #F47B20, #ffb26b 55%, #F47B20);
}

/* Nền TỐI: nền ô sáng lên một bậc (navy nguyên bản chìm vào #2d323a của thanh), bóng đổ đậm hơn —
   cùng lẽ với khối cầu của `TheTaiKhoan.razor.css` đợt 1141. */
:root[data-theme="dark"] .dai-chao-nav[b-lb0ivvqctw],
:root[data-theme="co-dien-toi"] .dai-chao-nav[b-lb0ivvqctw] {
    --dcn-lich-nen: linear-gradient(160deg, #1a5cc4, #12459c 62%, #0b3172);
    --dcn-lich-vien: rgba(255, 255, 255, .28);
    --dcn-lich-bong: 0 1px 1px rgba(0, 0, 0, .45), 0 3px 8px rgba(0, 0, 0, .5);
    --dcn-lich-tren: rgba(255, 255, 255, .28);
}

/* ───── Ô LỊCH ─────
   Gáy cam nằm trên đỉnh bằng `::before` (vạch đặc chạy hết bề ngang) — thứ làm khối này đọc ra là "tờ
   lịch" chứ không phải một cái huy hiệu số bất kỳ. */
.dcn-lich[b-lb0ivvqctw] {
    position: relative;
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding-top: 4px;               /* chừa chỗ cho gáy, giữ số ngày cân giữa phần giấy */
    border-radius: .5rem;
    overflow: hidden;
    background: var(--dcn-lich-nen);
    border: 1px solid var(--dcn-lich-vien);
    box-shadow: var(--dcn-lich-bong), inset 0 1px 0 var(--dcn-lich-tren);
}

.dcn-lich[b-lb0ivvqctw]::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: var(--dcn-gay);
}

/* Vệt sáng lướt qua mặt lịch — cùng nhịp thưa với vệt quét của khối cầu ảnh đại diện (đợt 1141): nằm
   im ~80% thời gian rồi lướt qua trong ~1,4s. Nhịp thưa vì đây là thứ người ta nhìn cả ngày; chớp liên
   tục là gây xao nhãng chứ không phải "sống động". */
.dcn-lich[b-lb0ivvqctw]::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(105deg, transparent 38%, rgba(255, 255, 255, .5) 50%, transparent 62%);
    background-size: 260% 100%;
    background-repeat: no-repeat;
    animation: dcn-quet-b-lb0ivvqctw 9s ease-in-out infinite;
}

@keyframes dcn-quet-b-lb0ivvqctw {
    0%, 84% { background-position: -160% 0; opacity: 0; }
    87%     { opacity: 1; }
    100%    { background-position: 160% 0; opacity: 0; }
}

.dcn-so[b-lb0ivvqctw] {
    position: relative;
    z-index: 1;
    color: #ffffff;
    font-size: 1rem;
    font-weight: 700;
    line-height: 1;
    /* Chữ số cùng bề ngang: ngày 11 và ngày 30 không làm ô nhảy qua nhảy lại mỗi khi sang ngày. */
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
}

/* ───── CỘT CHỮ ───── */
.dcn-chu[b-lb0ivvqctw] {
    display: flex;
    flex-direction: column;
    min-width: 0;                   /* cho phép co — xem luật một-hàng ở đầu tệp */
}

.dcn-thu[b-lb0ivvqctw] {
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dcn-thang[b-lb0ivvqctw] {
    font-size: .68rem;
    opacity: .72;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Người dùng khai "giảm chuyển động" ở hệ điều hành thì tắt vệt quét — cùng khuôn đã dùng cho hero
   trang chủ. Ô lịch không mất gì: nó vốn đọc được khi đứng im. */
@media (prefers-reduced-motion: reduce) {
    .dcn-lich[b-lb0ivvqctw]::after { animation: none; opacity: 0; }
}

/* ── THU GỌN THEO BẬC ── lối đi không bao giờ bị đẩy xuống dòng.
   Dưới 1200px thì bỏ cột chữ, giữ Ô LỊCH: ngày là thứ người ta liếc nhiều nhất, mà nó chỉ tốn 30px.
   Dưới 992px thì thanh nav phải nhường trọn chỗ cho dải tab + cụm tài khoản ⇒ ẩn hẳn. Không mất thông
   tin: ngày tháng có sẵn ở đồng hồ máy, và đây vốn là dòng trang trí của trang chủ. */
@media (max-width: 1200px) {
    .dcn-chu[b-lb0ivvqctw] { display: none; }
    .dai-chao-nav[b-lb0ivvqctw] { padding-right: .5rem; }
}

@media (max-width: 992px) {
    .dai-chao-nav[b-lb0ivvqctw] { display: none; }
}
/* /Components/Layout/MainLayout.razor.rz.scp.css */
.page[b-5gorr053s4] {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */

    /* ⭐ ĐỢT 1563 (việc o857) — LỀ AN TOÀN CHO MÀN HÌNH KHUYẾT (tai thỏ, cạnh bo, thanh vuốt).
       📏 ĐO 30/08/2026: `app.css` có ĐÚNG 0 lượt `safe-area-inset`, trong khi cả BA trang tĩnh
          (`cong-vao.html` · `nap-lieu/index.html` · `ngoai-mang.html`) đều đã khai. Tức trong cùng
          một app, trang tĩnh biết chừa lề còn toàn bộ phần Blazor thì không — mở ở chế độ toàn màn
          hình trên máy có khuyết là chữ mép trái/phải chui xuống dưới phần bo, và dải dưới cùng nằm
          đè lên thanh vuốt của hệ điều hành.
       🔑 `env()` chỉ có giá trị khi thẻ viewport khai `viewport-fit=cover` — đã thêm ở `App.razor`
          CÙNG ĐỢT. Thiếu một trong hai vế là bản vá trông đúng mà ⛔ KHÔNG có tác dụng gì.
       🔒 CHỐT NGƯỢC: trên máy để bàn và mọi màn hình KHÔNG khuyết, `env(safe-area-inset-*)` trả
          **0px** ⇒ ⛔ không đổi một pixel nào cho 433 tài khoản đang ngồi chuột. Giá trị lui `0px`
          viết tường minh để trình duyệt đời cũ ⛔ không hiểu `env()` cũng ra đúng số ấy. */
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
    padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* ═══ ⭐ ĐỢT 1438 (việc o634) — NỀN ẢNH CHO CÁC TRANG BÊN TRONG ═══
   Anh Tiến 28/08/2026: *"nạp thêm các tài nguyên này và sử dụng cho trang đăng nhập và hình nền các
   trang bên trong"*. Ảnh + độ mờ do `MainLayout.razor` đóng vào hai biến `--nen-trong-*`; nguồn là
   `LuatAnhNenTheoLuot` (khoá `NenTrang:Bat` · `NenTrang:Anh` · `NenTrang:DoMo`).

   🔴 `z-index: -1` LÀ BẮT BUỘC, ⛔ đừng bỏ: `main`/`article` là phần tử KHÔNG định vị nên chúng vẽ ở
      lớp dưới lớp của các phần tử ĐÃ định vị. `::before` này định vị (fixed) ⇒ ⛔ không có z-index âm
      thì nó ĐÈ LÊN toàn bộ nội dung — cả bảng biểu lẫn nút bấm, và `pointer-events: none` chỉ cứu được
      phần bấm chứ ⛔ không cứu phần nhìn.
   ⚠ `.page` cố ý ⛔ KHÔNG đặt `z-index`: đặt vào là nó thành một tầng xếp riêng và `::before` (z-index
      -1) chui xuống dưới nền của chính `.page`, tức biến mất sạch. Nền của body nằm ở lớp canvas gốc
      nên ⛔ không che được lớp này.
   ⚠ `background-attachment: fixed` + `position: fixed` ⇒ nền ĐỨNG YÊN khi cuộn. Bảng dài của app cuộn
      hàng nghìn dòng; nền trôi theo là thứ gây mỏi mắt nhất.
   ⚠ Ảnh chỉ vẽ khi biến `--nen-trong-anh` có giá trị. Cờ tắt ⇒ MainLayout ⛔ không đóng biến nào ⇒ luật
      này thành vô hại, app y như trước đợt 1438. */
.page[b-5gorr053s4]::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background-image: var(--nen-trong-anh, none);
    background-size: cover;
    background-position: center;
    background-attachment: fixed;
    background-repeat: no-repeat;
    opacity: var(--nen-trong-do-mo, 0);
    pointer-events: none;
}

/* Máy in: ⛔ TUYỆT ĐỐI không in ảnh nền — tốn mực cho một thứ cố ý mờ, và làm bẩn tờ giấy nộp lên trên. */
@media print {
    .page[b-5gorr053s4]::before { display: none; }
}

main[b-5gorr053s4] {
    flex: 1;
    min-width: 0;
}

/* ===== Menu dạng NGĂN KÉO (off-canvas) — ẨN mặc định, mở bằng nút ☰ góc trái =====
   Điều khiển thuần CSS bằng 1 checkbox ẩn (chạy được cả khi render tĩnh). */
.menu-mo-toggle[b-5gorr053s4] {
    display: none;
}

.sidebar[b-5gorr053s4] {
    background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
    position: fixed;
    top: 0;
    left: 0;
    height: 100vh;
    height: 100dvh;   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */
    width: 250px;
    max-width: 85vw;
    z-index: 1050;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
}

.menu-mo-toggle:checked ~ .sidebar[b-5gorr053s4] {
    transform: translateX(0);
    box-shadow: 2px 0 14px rgba(0, 0, 0, 0.35);
}

/* Nền mờ phủ trang khi mở menu — bấm để đóng */
.menu-backdrop[b-5gorr053s4] {
    display: none;
}

.menu-mo-toggle:checked ~ .menu-backdrop[b-5gorr053s4] {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 1040;
}

/* Cụm NÚT NHANH (Trang chủ / Chấm công / QL Nước) đứng cạnh nút ☰ Menu;
   margin-right auto đẩy các mục còn lại của top-row sang phải */
.nut-nhanh[b-5gorr053s4] {
    margin-right: auto;
}

.top-row[b-5gorr053s4] {
    background-color: #f7f7f7;   /* ⭐ ĐỢT 887 hoàn nguyên 882 — anh Tiến 19/08 tối: "lấy lại thanh nav cũ" */
    border-bottom: 1px solid #d6d5d5;
    min-height: 3.5rem;   /* min thay vì cố định: khi hẹp các mục XUỐNG DÒNG thì cao thêm, không bị cắt */
    display: flex;
    /* ⭐ ĐỢT 654 — MỘT HÀNG DUY NHẤT trên màn hình máy tính (anh Tiến 14/08/2026: *"thanh nav tràn như
       vậy xấu… Lưu ý chỉ dùng 1 hàng nav header"*).
       ⚠ VÌ SAO PHẢI SỬA Ở ĐÂY chứ không phải ở `app.css`: app.css mục "BỐ CỤC THANH NAV" đã khai
       `.top-row { flex-wrap: nowrap }` từ đợt 467, nhưng luật ĐÓ CHỈ (0,1,0) còn luật này là CSS cách ly
       nên bộ chọn thật là `.top-row[b-xxxxx]` = (0,2,0) ⇒ **luật scoped luôn thắng**, và thanh nav vẫn
       xuống dòng. Trước đợt 650 không ai thấy vì thanh còn thừa chỗ; thêm dải tab app vào là tràn ngay.
       📌 Bài học: hai tệp cùng khai một thuộc tính cho cùng một lớp — luôn hỏi "bên nào là CSS cách ly?"
          trước khi tin luật nào đang chạy.
       ⛔ Giữ nguyên nhánh xuống dòng ở khổ hẹp (khối @media ngay dưới): đó là nấc "thanh nav bị tràn"
          anh Tiến chốt ở đợt 422 — dải tab của trang tụt xuống hàng hai, chiếm trọn bề ngang. */
    flex-wrap: nowrap;
    align-items: center;
    row-gap: .4rem;       /* khoảng cách giữa các dòng khi bị bọc */
    /* ⭐ ĐỢT 1563 (việc o857) — chừa thêm phần khuyết PHÍA TRÊN cho thanh nav.
       ⚠ Phải đặt ở ĐÂY chứ ⛔ không thể dựa vào `padding-top` của `.page`: thanh này `position: sticky`
          với `top: 0`, mà sticky neo theo KHUNG NHÌN chứ ⛔ không theo hộp đệm của phần tử cha ⇒ vừa
          cuộn một dòng là nó chui thẳng lên dưới phần khuyết. Máy ⛔ không khuyết trả 0px ⇒ giữ đúng
          .35rem như cũ, ⛔ không lệch một pixel. */
    padding-top: calc(.35rem + env(safe-area-inset-top, 0px));
    padding-bottom: .35rem;
    position: sticky;
    top: 0;
    z-index: 900;
    padding-left: 1rem !important;
    padding-right: 1.5rem !important;
}

/* ⛔ ĐỢT 684b — ẢNH NỀN THANH NAV ĐÃ BỎ (anh Tiến 15/08/2026, xem bản thật rồi chốt: *"hình trên thanh
   nav xấu quá nên bỏ đi em nhé"*). Khối `.top-row::after` của đợt 684 (3 ảnh kho + lớp mờ dần) đã xóa
   sạch cùng biến `--anh-kho-*` bên MainLayout.razor — không để lại luật CSS mồ côi, thứ làm người đọc sau
   tưởng màn hình còn phần đó.
   ⓘ Ảnh kho vẫn là nền động của MÀN HÌNH ĐĂNG NHẬP (LayoutTrong.razor). Cần dựng lại: git đợt 684. */

/* ═══ ⭐ ĐỢT 893 (việc o110) — NAV CÙNG CHẤT LIỆU MÀU VỚI CHIP LOGO + VỆT NHẬN DIỆN THEO MÀU LOGO ═══
   Anh Tiến 20/08/2026: *"thanh nav ở trang chủ vẫn bị lệch màu với logo. có cách nào cho màu trùng
   không?"* — chip logo là gradient trắng→#f2f5f9→#e8edf4 (khối "BẢNG TÊN ĐEO ÁO" app.css) còn nav là
   xám phẳng #f7f7f7 KHÁC HỌ màu ⇒ nhìn lệch. Nay nav dùng ĐÚNG BỘ BA MÀU đó (đậm dần về đáy; chip
   sáng hơn một nấc nên vẫn nổi 3D — ⛔ không lặp lại chip-trong-suốt của đợt 880 mà anh đã chê).
   · KHÁC đợt 684 (ảnh kho — anh chê "hình xấu quá"): đây là ĐỒ HỌA CÁCH ĐIỆU đúng đề bài o110 anh
     giao 19/08 — một dải màu mỏng theo tông logo, không phải ảnh chụp.
   · Màu vệt ĐO TỪ ĐIỂM ẢNH logo thật (mô tả o110 dặn ⛔ đừng đoán mã màu): navy #03045e (1.924 px) ·
     cam #ea5929 (156 px) — python đọc PNG base64 trong logo-petrolimex.svg, 20/08/2026.
   · ⚠ Khai bằng background-IMAGE trong luật bọc :not(), ⛔ không đổi background-color của .top-row:
     nền TỐI đè nav bằng `background-color !important` (app.css) — thứ KHÔNG đè được image, nên image
     phải tự tránh nền tối ngay từ bộ chọn. Cổ điển thì đóng băng (quy tắc #20) — cũng loại từ bộ chọn.
   · Vệt ::after đặt được vì .top-row đã position:sticky (positioned) — cho CẢ sáng lẫn tối hiện đại:
     trên nền tối #2d323a dải navy→cam vẫn là điểm nhận diện thương hiệu. */
:root:not([data-theme^="co-dien"]):not([data-theme="dark"]) .top-row[b-5gorr053s4] {
    background-image: linear-gradient(180deg, #ffffff 0%, #f2f5f9 55%, #e8edf4 100%);
    border-bottom-color: rgba(3, 4, 94, .16);
}

:root:not([data-theme^="co-dien"]) .top-row[b-5gorr053s4]::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 3px;
    background: linear-gradient(90deg, #03045e 0%, #0a3d91 52%, #ea5929 86%, #f5a623 100%);
    pointer-events: none;
}

/* ⭐ ĐỢT 654 — nhánh XUỐNG DÒNG của khổ hẹp phải khai LẠI Ở ĐÂY.
   `app.css` đã có `@media (max-width: 1200px) { .top-row { flex-wrap: wrap } }` (đợt 422) nhưng nó
   (0,1,0) THUA luật scoped `.top-row[b-xxxxx]` (0,2,0) ở trên — y hệt lý do đã ghi tại đó. Không khai
   lại thì khổ điện thoại mất luôn nấc "dải tab tụt xuống hàng hai" mà anh Tiến chốt ở đợt 422. */
@media (max-width: 1200px) {
    .top-row[b-5gorr053s4] { flex-wrap: wrap; }
}

    .top-row[b-5gorr053s4]  a, .top-row[b-5gorr053s4]  .btn-link {
        white-space: nowrap;
        text-decoration: none;
    }

    .top-row[b-5gorr053s4]  a:hover, .top-row[b-5gorr053s4]  .btn-link:hover {
        text-decoration: underline;
    }

/* NỘI DUNG TRÔI RA GIỮA MÀN HÌNH (anh yêu cầu 30/07): các trang trước đây dồn hết về mép trái
   trên màn hình rộng. Giới hạn bề rộng cột nội dung rồi căn giữa — bảng quá rộng (bảng công
   theo ngày…) vẫn tự cuộn ngang trong khung riêng của nó, không tràn.

   ⭐ ĐỢT 1481 (việc o684) — TRẦN NAY LÀ BIẾN `--tran-cot-noi-dung` (khai ở `app.css`), ⛔ đừng gõ lại
     con số vào đây. Hai tấm biểu ngữ dọc hai bên hông đọc CÙNG biến ấy để biết dải trống còn bao
     nhiêu; đóng đinh hai con số ở hai tệp là chúng lệch nhau — đã lệch thật, xem chú thích ở
     `app.css` và ở `ChungTu/BieuNguHong.razor.css`.
   ⚠ Giá trị lui `1400px` là BẮT BUỘC: biến ⛔ không phân giải được mà không có nấc lui thì cột nội
     dung mất trần, trải hết bề ngang màn hình ở mọi trang. */
.content[b-5gorr053s4] {
    max-width: var(--tran-cot-noi-dung, 1400px);
    width: 100%;
    margin-inline: auto;
}

/* ⭐ ĐỢT 1268 (việc o454) — NGOẠI LỆ CHO TRANG CÓ BẢN VẼ (anh Tiến 25/08/2026: "cần tận dụng hiệu quả
   chiều ngang màn hình, nếu thiếu chỗ trình bày cho phép cuộn xuống để xem").
   Ai gắn lớp này: MainLayout.razor hỏi LuatTrangRongHet.Can(DuongDanHienTai) — danh sách nhánh nằm
   trong luật thuần đó, ⛔ đừng khai thêm đường dẫn ở đây (hai sổ nói hai chuyện).

   🔑 VÌ SAO VIẾT `.content.content-rong` CHỨ KHÔNG PHẢI `.content-rong` TRẦN: CSS này là scoped, biên
   dịch ra `.content[b-xxxx]` (0,2,0). Một bộ chọn `.content-rong[b-xxxx]` cũng đúng (0,2,0) ⇒ thắng
   thua chỉ nhờ THỨ TỰ trong tệp, mà thứ tự là thứ một lượt dọn dẹp về sau xê dịch được không dấu vết.
   Ghép hai lớp cho ra (0,3,0) — thắng chắc chắn, không phụ thuộc chỗ đứng.

   ⚠ Chỉ gỡ TRẦN, ⛔ đừng gỡ `margin-inline: auto`: nó vô hại khi không còn trần (còn đúng 0 lề), mà
   giữ lại thì trang vẫn tự căn giữa ở ca có ai đó đặt trần hẹp hơn từ nơi khác. */
.content.content-rong[b-5gorr053s4] {
    max-width: none;
}

/* ⭐ ĐỢT 1275 (việc o462) — DẢI BÁO LỖI TỪNG KHÔNG ĐỌC ĐƯỢC Ở GIAO DIỆN TỐI.
   Nền `lightyellow` là màu CỐ ĐỊNH (khuôn Blazor để sẵn), nhưng khối này chưa bao giờ đặt `color`
   ⇒ chữ kế thừa màu chữ của giao diện. Ở nền tối màu ấy là `#dde1e6` (sáng) nên chữ sáng nằm trên
   nền vàng nhạt.
   📏 Đo 25/08/2026 (bản tĩnh `--chu-de dark`, hai trục theme khớp app thật, chốt ngược `d-flex` ĐẠT):
     câu báo lỗi **1,29:1** · nút "Tải lại trang" và dấu 🗙 **2,17:1** — ngưỡng đọc được là 4,5:1.
   🔴 Đây là dải nói cho người dùng biết **app vừa mất kết nối**: hỏng đúng lúc mọi thứ khác đã hỏng,
     nên không lượt dùng bình thường nào lộ ra nó. Và vì `display: none` nên mọi lượt rà giao diện
     cũng bỏ qua — phải BẬT tay mới đo được.
   🔑 Nền cố định thì chữ cũng phải cố định: ⛔ đừng chữa bằng cách cho nền chạy theo giao diện
     (`var(--bs-warning-bg-subtle)`…). Vàng cảnh báo trên nền tối vẫn là vàng cảnh báo — thứ sai là
     chữ, không phải nền, và đổi nền là đổi tín hiệu người dùng đã quen ở giao diện sáng.
   ⓘ Giao diện SÁNG **không đổi một pixel**: `#212529` đúng bằng màu chữ nó vốn kế thừa ở đó. */
#blazor-error-ui[b-5gorr053s4] {
    background: lightyellow;
    color: #212529;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    /* Hai chỗ bấm được trong dải — CHỈ SIẾT Ở NỀN TỐI, ⛔ không đụng nền sáng.
       📏 Đo cùng lượt: ở giao diện SÁNG chúng đang là `#006bb7` (màu liên kết của app) = **5,21:1**
         trên nền vàng — ĐÃ ĐẠT ngưỡng 4,5. Ép màu ở cả hai giao diện là đổi một thứ đang chạy đúng
         cho 433 tài khoản, mà lại làm link trong dải lệch khỏi màu link chung của app (quy tắc #18).
         Ở nền TỐI màu ấy hoá `#6bb3ff` ⇒ **2,17:1** — đó mới là ca phải chữa; navy `#0a3d91` cho
         **9,88:1** mà vẫn khác màu chữ thường nên vẫn đọc ra là "bấm được".
       ⚠ `#blazor-error-ui` là ID (1,0,0) nên chuỗi này thắng mọi luật màu liên kết theo giao diện —
         đó chính là thứ phải thắng, vì màu link kia được chọn cho nền TRANG chứ không cho nền vàng. */
    :root[data-theme="dark"] #blazor-error-ui .reload[b-5gorr053s4],
    :root[data-theme="dark"] #blazor-error-ui .dismiss[b-5gorr053s4] {
        color: #0a3d91;
    }

    #blazor-error-ui .dismiss[b-5gorr053s4] {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

/* ═══════════ ⭐ ĐỢT 1469 (việc o671) — CÂN THANH NAV ĐỂ BĂNG RÔN NẰM GIỮA ═══════════
   Anh Tiến 28/08/2026: *"sắp xếp các nút và các icon sao cho băng rôn nằm giữa nhất có thể"*.

   📏 ĐO TRƯỚC KHI SỬA (màn 1920, trang chủ): mép trái băng rôn cách mép màn 245px, mép phải cách
     615px — lệch 370px. Gốc: cụm phải (huy hiệu chủ đề · lịch · chuông · thẻ tài khoản) rộng hơn hẳn
     hai nút đầu thanh, mà `ms-auto` thì đẩy hết chỗ thừa về MỘT phía.

   🔑 CÁCH LÀM: cho HAI cụm hai đầu cùng `flex: 1 1 0` ⇒ chúng chia ĐỀU chỗ thừa nên luôn bằng nhau,
     và băng rôn nằm đúng giữa. `min-width: max-content` là cái phanh: chỗ chật thì cụm giữ nguyên bề
     rộng nội dung của nó chứ ⛔ không bóp méo nút — lúc ấy băng rôn lệch trở lại, đúng nghĩa "giữa
     NHẤT CÓ THỂ" chứ ⛔ không phải "luôn luôn giữa".

   🔴 BỌC TRONG `:has(.bang-ron)` LÀ CHỐT AN TOÀN, ⛔ ĐỪNG GỠ. Ba điều nó bảo đảm:
     ① Trang ⛔ KHÔNG có băng rôn (từ đợt 1469 là mọi trang trừ trang chủ) giữ NGUYÊN bố cục cũ —
        `ms-auto` còn nguyên trong markup và vẫn ăn. Lượt sửa này vì thế ⛔ không đụng tới dải tab của
        bất kỳ app nào, đúng quy tắc #18 (kiểm soát rủi ro lan truyền).
     ② Trình duyệt ⛔ không hiểu `:has()` thì bỏ qua cả khối ⇒ về đúng bố cục hôm nay, ⛔ không vỡ.
     ③ `margin-left: 0` phải ghi ĐÈ `ms-auto` của Bootstrap — mà lớp tiện dụng ấy mang `!important`,
        nên ⛔ không có cách nào khác ngoài `!important`. Đây là ca chính đáng hiếm hoi (cùng lẽ với
        khuôn `--nav-nut-*` ở đợt 976): auto-margin NUỐT TRỌN chỗ thừa TRƯỚC khi `flex-grow` được
        chia, nên còn `ms-auto` thì hai cụm ⛔ không bao giờ bằng nhau.

   ⛔ ĐỪNG "chữa" bằng `position: absolute` cho băng rôn: nó sẽ đứng NGOÀI phép chia chỗ của trình
     duyệt và lời hứa "băng rôn không che nút" của đợt 1367 mất hiệu lực ngay. */
.top-row:has(.bang-ron) > .cum-ben-trai[b-5gorr053s4],
.top-row:has(.bang-ron) > .cum-ben-phai[b-5gorr053s4] {
    flex: 1 1 0;
    min-width: max-content;
}

.top-row:has(.bang-ron) > .cum-ben-phai[b-5gorr053s4] {
    margin-left: 0 !important;   /* xem chốt ③ ở trên — ⛔ không phải trang điểm */
    justify-content: flex-end;
}
/* /Components/Layout/NavMenu.razor.rz.scp.css */
.top-row[b-ns6j8exyxo] {
    height: 3.5rem;
    background-color: rgba(0,0,0,0.4);
}

.navbar-brand[b-ns6j8exyxo] {
    font-size: 1.1rem;
}

.bi[b-ns6j8exyxo] {
    display: inline-block;
    position: relative;
    width: 1.25rem;
    height: 1.25rem;
    margin-right: 0.75rem;
    top: -1px;
    background-size: cover;
}

.bi-house-door-fill-nav-menu[b-ns6j8exyxo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}

.bi-plus-square-fill-nav-menu[b-ns6j8exyxo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}

.bi-list-nested-nav-menu[b-ns6j8exyxo] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}

.nav-item[b-ns6j8exyxo] {
    font-size: 0.9rem;
    padding-bottom: 0.5rem;
}

    .nav-item:first-of-type[b-ns6j8exyxo] {
        padding-top: 1rem;
    }

    .nav-item:last-of-type[b-ns6j8exyxo] {
        padding-bottom: 1rem;
    }

    .nav-item[b-ns6j8exyxo]  .nav-link {
        color: #d7d7d7;
        background: none;
        border: none;
        border-radius: 4px;
        min-height: 2.5rem; /* KHÔNG cố định height — chữ dài xuống dòng sẽ chồng lên nhau */
        display: flex;
        align-items: center;
        line-height: 1.3;
        padding: 0.5rem 0.75rem;
        width: 100%;
    }

/* ĐỢT 386 — nền của mục ĐANG CHỌN hạ từ alpha .37 xuống .25: chữ trắng trên lớp phủ .37 chỉ đạt
   3,8:1 (đo thật trên nền navy của thanh bên), .25 lên 5,3:1. Hạ nền thì dấu "đang đứng ở đây" mờ
   đi, nên bù lại bằng chữ ĐẬM + vạch cam thương hiệu bên trái — dấu hiệu còn rõ hơn trước mà không
   phải đánh đổi độ đọc. */
.nav-item[b-ns6j8exyxo]  a.active {
    background-color: rgba(255,255,255,0.25);
    color: white;
    font-weight: 600;
    box-shadow: inset 3px 0 0 #F47B20;
}

.nav-item[b-ns6j8exyxo]  .nav-link:hover {
    background-color: rgba(255,255,255,0.1);
    color: white;
}

/* ===== Nhóm phân hệ dạng sổ ra/thu vào (details/summary) ===== */
.nhom-nav[b-ns6j8exyxo] {
    padding-bottom: 0.25rem;
}

.nhom-nav summary[b-ns6j8exyxo] {
    color: #d7d7d7;
    cursor: pointer;
    border-radius: 4px;
    min-height: 2.5rem; /* height auto — tránh chữ 2 dòng chồng nhau */
    display: flex;
    align-items: center;
    line-height: 1.3;
    padding: 0.5rem 0.75rem;
    font-size: 0.9rem;
    list-style: none; /* bỏ mũi tên mặc định — tự vẽ bên dưới */
}

.nhom-nav summary[b-ns6j8exyxo]::-webkit-details-marker {
    display: none;
}

.nhom-nav summary[b-ns6j8exyxo]::after {
    content: "▸";
    margin-left: auto;
    margin-right: 0.75rem;
    transition: transform 0.15s;
}

.nhom-nav[open] summary[b-ns6j8exyxo]::after {
    transform: rotate(90deg);
}

.nhom-nav summary:hover[b-ns6j8exyxo] {
    background-color: rgba(255,255,255,0.1);
    color: white;
}

.nhom-nav[open] summary[b-ns6j8exyxo] {
    color: white;
    background-color: rgba(255,255,255,0.06);
}

.nhom-nav .nav-item[b-ns6j8exyxo] {
    padding-left: 0.75rem; /* thụt link con vào so với tên nhóm */
    padding-bottom: 0.15rem;
}

.nav-item[b-ns6j8exyxo] {
    padding-bottom: 0.15rem;
}

/* Ngăn kéo luôn hiển thị nội dung (đóng/mở do MainLayout điều khiển bằng transform .sidebar) */
.nav-scrollable[b-ns6j8exyxo] {
    display: block;
    height: calc(100vh - 3.5rem);
    height: calc(100dvh - 3.5rem);   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */
    overflow-y: auto;
}
/* /Components/Layout/TheTaiKhoan.razor.rz.scp.css */
/* ═══ ẢNH ĐẠI DIỆN + THẺ TÀI KHOẢN — góc phải thanh nav (đợt 603) ═══

   ⚠ ĐẶT Ở CSS PHẠM VI COMPONENT (.razor.css), KHÔNG đổ vào wwwroot/app.css: app.css là tệp DÙNG CHUNG
     rủi ro cao nhất về mặt lan lỗi (quy tắc #18) và đang có phiên khác sửa cùng lúc. Ở đây mọi luật đều
     bị Blazor gắn thêm dấu phạm vi [b-xxxxx] nên KHÔNG THỂ chạm tới một phần tử nào ngoài component này.

   ⚠ BỐN GIAO DIỆN, KHÔNG PHẢI HAI: sáng · tối · cổ điển · cổ điển tối. Nền TỐI phải bắt CẢ HAI giá trị
     data-theme="dark" và data-theme="co-dien-toi" — bắt thiếu một cái là đúng kiểu "lóa trắng" mà dự án
     vừa mất nguyên một đợt đi vá 43 tệp.

   📏 TƯƠNG PHẢN CHỮ (WCAG 2.1 — chữ thường cần ≥ 4,5:1), tính bằng số, không ước lượng:
     · Nền sáng: chữ #212529 trên #ffffff ........ 15,43:1
                 chữ phụ #5c636a trên #ffffff .....  6,09:1
                 liên kết #0a58ca trên #ffffff ....  6,44:1
     · Nền tối:  chữ #dde1e6 trên #262a31 ........ 10,97:1
                 chữ phụ #adb5bd trên #262a31 .....  6,94:1
                 liên kết #6bb3ff trên #262a31 ....  6,53:1
     · Nút Đăng xuất: chữ #ffffff trên #b02a37 ....  6,50:1  (dùng CHUNG cho cả hai nền)
     · Ô chữ cái đầu: chữ trắng trên 8 màu nền — thấp nhất 5,16:1 (bảng số đầy đủ ở LuatAnhDaiDien.MauNen)
   ⛔ Đổi bất kỳ mã màu nào ở dưới thì phải TÍNH LẠI và sửa luôn bảng số này. */

.tk-boc[b-m0rmvqquqn] {
    position: relative;
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    z-index: 1050;

    /* ⭐ ĐỢT 1643 (việc o930) — TRẢ NỐT PHẦN MÃ CỦA ĐỢT 1595 (việc o835). Đợt 1595 đã commit BÀI TEST
       `MauLuiTheoGiaoDienTests` đòi năm biến này đọc biến chủ đề, nhưng ⛔ lượt sửa CSS thì chưa bao giờ
       vào master (đo 01/09/2026: `git log --all -S "--tk-nen-the: var(--bm-nen-the"` ra RỖNG) ⇒ HEAD đỏ
       hai bài suốt từ đó. Thẻ tài khoản là VỎ BỌC của bảng chọn chủ đề, nên nó phải đổi màu cùng bảng
       chọn — không thì tái hiện đúng lỗi o835.
       🔒 Giá trị lui BẰNG ĐÚNG mã màu đang chạy trước lượt sửa này (bộ mặc định cố ý ⛔ không khai
       `--bm-*` nào) ⇒ màn hình của anh Tiến ⛔ không đổi một điểm ảnh — quy tắc #17. */
    --tk-nen-the: var(--bm-nen-the, #ffffff);
    --tk-vien: var(--bm-vien, #dee2e6);
    --tk-chu: var(--bm-chu, #212529);
    --tk-chu-phu: var(--bm-chu-mo, #5c636a);
    --tk-lienket: var(--bm-lien-ket, #0a58ca);
    --tk-ro-chuot: rgba(0, 0, 0, .05);
    --tk-vien-anh: rgba(0, 0, 0, .18);
    --tk-bong: 0 .5rem 1.5rem rgba(0, 0, 0, .18);

    /* ⭐ ĐỢT 1141 — bảng màu của KHỐI CẦU 3D (xem khối `.tk-cau` phía dưới) */
    --tk-cau-chop: rgba(255, 255, 255, .62);   /* chóp sáng: nguồn sáng từ trên-trái */
    --tk-cau-tren: rgba(255, 255, 255, .85);   /* gờ sáng mép trên  — vật LỒI hứng sáng ở trên */
    --tk-cau-duoi: rgba(0, 23, 68, .30);       /* mép dưới ăn bóng — vế còn lại của cảm giác lồi */
    --tk-cau-quet: rgba(255, 255, 255, .75);   /* vệt sáng lướt qua mặt cầu */
    --tk-cau-bong: 0 1px 1px rgba(0, 23, 68, .16), 0 3px 6px rgba(0, 23, 68, .22);
    --tk-cau-bong-ro: 0 2px 3px rgba(0, 23, 68, .18), 0 7px 14px rgba(0, 23, 68, .32);
    --tk-cau-bong-lun: 0 1px 1px rgba(0, 23, 68, .14);
}

:root[data-theme="dark"] .tk-boc[b-m0rmvqquqn],
:root[data-theme="co-dien-toi"] .tk-boc[b-m0rmvqquqn] {
    /* ⭐ ĐỢT 1643 — vế nền TỐI của khối trên. ⛔ ĐỪNG chép bộ số nhánh sáng xuống đây: thẻ trắng giữa
       nền tối là đúng ca `NutChonBoMau` đã trả giá ở đợt 1275 (đo được 1,31:1). */
    --tk-nen-the: var(--bm-nen-the, #262a31);
    --tk-vien: var(--bm-vien, #3a3f47);
    --tk-chu: var(--bm-chu, #dde1e6);
    --tk-chu-phu: var(--bm-chu-mo, #adb5bd);
    --tk-lienket: var(--bm-lien-ket, #6bb3ff);
    --tk-ro-chuot: rgba(255, 255, 255, .08);
    --tk-vien-anh: rgba(255, 255, 255, .28);
    --tk-bong: 0 .5rem 1.5rem rgba(0, 0, 0, .55);

    /* Nền TỐI lật công thức: chóp sáng mờ hơn (không thì ô tròn hóa đèn pin), bóng đổ ĐẬM hơn hẳn —
       cùng lẽ với khối `.the-bang-ten` nền tối ở app.css. */
    --tk-cau-chop: rgba(255, 255, 255, .30);
    --tk-cau-tren: rgba(255, 255, 255, .22);
    --tk-cau-duoi: rgba(0, 0, 0, .55);
    --tk-cau-quet: rgba(255, 255, 255, .38);
    --tk-cau-bong: 0 1px 1px rgba(0, 0, 0, .42), 0 3px 8px rgba(0, 0, 0, .52);
    --tk-cau-bong-ro: 0 2px 3px rgba(0, 0, 0, .45), 0 8px 16px rgba(0, 0, 0, .62);
    --tk-cau-bong-lun: 0 1px 1px rgba(0, 0, 0, .40);
}

/* ───── Ô ĐÁNH DẤU ẨN = chính cái nút bấm ─────
   Trong suốt nhưng PHỦ KÍN ảnh đại diện, nên chuột bấm vào đâu trong ô tròn cũng trúng nó.
   ⛔ KHÔNG dùng display:none / visibility:hidden để giấu: hai cái đó làm phần tử MẤT KHẢ NĂNG NHẬN
      TIÊU ĐIỂM ⇒ không Tab tới được, không bấm Space mở được — hỏng đúng yêu cầu "mở được bằng bàn phím". */
.tk-cong-tac[b-m0rmvqquqn] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    opacity: 0;
    cursor: pointer;
    z-index: 3;
}

/* Vòng tiêu điểm vẽ lên ẢNH (ô đánh dấu vô hình nên tự nó không hiện được vòng nào).
   :focus-visible chứ không :focus — bấm chuột thì không ai muốn thấy vòng viền. */
.tk-cong-tac:focus-visible ~ .tk-nut .tk-anh[b-m0rmvqquqn] {
    outline: 3px solid var(--tk-lienket);
    outline-offset: 2px;
}

.tk-nut[b-m0rmvqquqn] {
    display: inline-flex;
    align-items: center;
    margin: 0;
    cursor: pointer;
}

/* ═══ ⭐ ĐỢT 1141 — ICON AVATAR THÀNH KHỐI CẦU 3D, BÓNG CÓ HIỆU ỨNG ═══
   Anh Tiến 23/08/2026: *"icon hình avatar của người dùng em đồ họa 3D và bóng có hiệu ứng lên nhé"*.

   Dùng ĐÚNG công thức nút nổi 3D của `.the-bang-ten` (app.css, đợt 447) — mắt người đọc khối nổi bằng
   HƯỚNG ÁNH SÁNG: gờ sáng mép TRÊN (vật lồi hứng sáng) + mép dưới ăn bóng + bóng đổ xuống dưới. Ô
   tròn thì thêm một lớp nữa của hình CẦU: chóp sáng lệch trên-trái, thứ làm nó trông như viên bi chứ
   không phải cái đĩa dán phẳng lên thanh nav.

   ⚠ VÌ SAO KHỐI 3D NẰM Ở LỚP BỌC CHỨ KHÔNG ĐẶT `box-shadow: inset` LÊN CHÍNH `<img>`: bóng inset vẽ
     DƯỚI nội dung, mà nội dung của `<img>` là bức ảnh phủ kín ô ⇒ đặt inset lên ảnh là bóng bị chính
     bức ảnh che sạch. Hỏng LẶNG LẼ: build xanh, test xanh, màn hình y hệt như chưa sửa gì.

   🔴 VÌ SAO MỌI TRẠNG THÁI RÊ/BẤM BÁM VÀO `.tk-cong-tac` CHỨ KHÔNG PHẢI `.tk-nut:hover`: ô đánh dấu
     phủ KÍN nút với `z-index: 3`, mà nó là ANH EM của `.tk-nut` chứ không phải tổ tiên ⇒ con trỏ luôn
     đứng trên ô đánh dấu và `.tk-nut` không nhận `:hover`. Luật cũ `.tk-nut:hover .tk-anh` (sáng lên
     8%) vì thế gần như chưa từng chạy. ⛔ Giữ CẢ HAI bộ chọn ở dưới: mất một cái là mất hiệu ứng ở
     đúng ca mình không lường trước, mà loại hỏng này thì không có câu lỗi nào. */
/* ⭐ ĐỢT 1155 — BÁN KÍNH BO LẤY TỪ BIẾN `--anh-dd-bo` (khai ở app.css, đổi theo `data-khung-anh` trên
   thẻ <html>): TRÒN 50% mặc định · VUÔNG 26% khi người dùng chọn. Anh Tiến 24/08/2026: *"icon avatar
   của tài khoản cho phép chọn khung vuông hoặc khung tròn"*.
   ⚠ Cả BỐN chỗ bo phải đi cùng nhau — lớp bọc `.tk-cau`, hai lớp trang trí `::before` (chóp sáng) và
     `::after` (vệt quét), rồi chính `<img>`. Sót một chỗ là lớp trang trí tròn nằm đè lên ảnh vuông,
     lòi ra bốn góc — hỏng LẶNG LẼ, chỉ mắt người mới thấy.
   📏 Giá trị lui `50%` trong `var(…, 50%)` là để component còn đúng khi app.css chưa nạp kịp (và khi
     ai đó mở riêng tệp này để thử) — ⛔ đừng gỡ, nó không tốn gì. */
.tk-cau[b-m0rmvqquqn] {
    position: relative;
    border-radius: var(--anh-dd-bo, 50%);
    line-height: 0;                 /* khít mép ảnh — thừa 1px là bóng tròn hóa bầu dục */
    box-shadow: var(--tk-cau-bong);
    transition: transform .14s ease, box-shadow .14s ease;
}

/* Bản 64px nằm trong thẻ đeo: `.tk-the-dau` là flex ⇒ phải chặn co lại, kẻo tên người dài đẩy ô tròn
   thành bầu dục (cùng lý do `.tk-anh` khai `flex-shrink: 0`). */
.tk-cau-to[b-m0rmvqquqn] {
    display: inline-flex;
    flex-shrink: 0;
}

/* Lớp KHỐI: chóp sáng trên-trái + gờ sáng mép trên + mép dưới ăn bóng.
   ⛔ `pointer-events: none` là bắt buộc — lớp này nằm ĐÈ lên ảnh, bắt chuột là nuốt luôn cú bấm. */
.tk-cau[b-m0rmvqquqn]::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    border-radius: var(--anh-dd-bo, 50%);
    background:
        radial-gradient(78% 62% at 30% 16%, var(--tk-cau-chop) 0%, rgba(255, 255, 255, .12) 42%, transparent 62%),
        linear-gradient(180deg, transparent 52%, var(--tk-cau-duoi) 100%);
    box-shadow:
        inset 0 1px 0 var(--tk-cau-tren),
        inset 0 -2px 3px var(--tk-cau-duoi);
    pointer-events: none;
}

/* Vệt sáng lướt qua mặt cầu — "hiệu ứng" anh dặn, cùng nhịp thưa với vệt quét của chip logo (đợt 893):
   nằm im ngoài mép trái 80% thời gian rồi lướt qua trong ~1,4s. Nhịp thưa vì đây là thứ người ta nhìn
   cả ngày trên mọi trang; chớp liên tục là gây xao nhãng, không phải "sống động". */
.tk-cau[b-m0rmvqquqn]::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    border-radius: var(--anh-dd-bo, 50%);
    overflow: hidden;
    pointer-events: none;
    background: linear-gradient(105deg, transparent 38%, var(--tk-cau-quet) 50%, transparent 62%);
    background-size: 260% 100%;
    background-repeat: no-repeat;
    animation: tk-quet-cau-b-m0rmvqquqn 7s ease-in-out infinite;
}

@keyframes tk-quet-cau-b-m0rmvqquqn {
    0%, 80% { background-position: -160% 0; opacity: 0; }
    83%     { opacity: 1; }
    100%    { background-position: 160% 0; opacity: 0; }
}

/* RÊ CHUỘT: nhô lên + phóng nhẹ, bóng dày và lan xa hơn (nguồn sáng đứng yên, vật lại gần mắt hơn) */
.tk-cong-tac:hover ~ .tk-nut .tk-cau[b-m0rmvqquqn],
.tk-nut:hover .tk-cau[b-m0rmvqquqn] {
    transform: translateY(-1px) scale(1.06);
    box-shadow: var(--tk-cau-bong-ro);
}

/* BẤM: lún xuống, bóng xẹp — đúng ngôn ngữ vật lý của `.the-bang-ten:active` */
.tk-cong-tac:active ~ .tk-nut .tk-cau[b-m0rmvqquqn] {
    transform: translateY(1px) scale(.97);
    box-shadow: var(--tk-cau-bong-lun);
}

/* ĐANG MỞ THẺ: giữ nguyên trạng thái nhô — nút phải "trông như đang được ấn giữ" suốt lúc thẻ bung,
   nếu không thì bảng thả lơ lửng mà chẳng có gì trên thanh nav nói rằng nó từ đâu ra. */
.tk-cong-tac:checked ~ .tk-nut .tk-cau[b-m0rmvqquqn] {
    transform: translateY(-1px) scale(1.04);
    box-shadow: var(--tk-cau-bong-ro);
}

.tk-anh[b-m0rmvqquqn],
.tk-anh-to[b-m0rmvqquqn] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--anh-dd-bo, 50%);
    object-fit: cover;
    /* Ảnh người dùng đưa lên đã được cắt VUÔNG ở máy chủ, nhưng vẫn ép tỷ lệ ở đây để một dòng dữ liệu
       cũ/lạ cũng không làm méo ô tròn trên thanh nav của mọi trang. */
    aspect-ratio: 1 / 1;
    border: 1px solid var(--tk-vien-anh);
    color: #ffffff;
    font-weight: 600;
    line-height: 1;
    user-select: none;
    flex-shrink: 0;
}

.tk-anh[b-m0rmvqquqn] {
    width: 34px;
    height: 34px;
    font-size: .82rem;
    letter-spacing: .02em;
}

.tk-anh-to[b-m0rmvqquqn] {
    width: 64px;
    height: 64px;
    font-size: 1.5rem;
}

/* ───── ẢNH MẶC ĐỊNH = LOGO PETROLIMEX (đợt 1001 — anh Tiến 22/08/2026) ─────
   Ba điều CỐ Ý, đừng dọn:
   ① `background: #ffffff` — mác logo là navy #03045e + cam #ea5929 trên nền TRONG SUỐT (đo bằng cách
      giải nén PNG nhúng trong wwwroot/logo-mark.svg). Để trần thì ở giao diện TỐI nửa navy chìm hẳn
      vào nền #262a31. Nền trắng là bộ đôi DUY NHẤT đọc được ở CẢ hai giao diện mà không phải khai hai
      bảng màu — cùng lẽ với bảng màu đậm + chữ trắng của ô chữ cái đời trước.
   ⛔ KHÔNG dùng lớp `.bg-white` của Bootstrap ở đây: app CỐ Ý đè nó thành nền tối ở giao diện tối
      (xem NenSangSotOGiaoDienToiTests) ⇒ mượn lớp đó là tự dựng lại đúng chỗ vừa tránh.
   ② `object-fit: contain` — ghi đè `cover` của .tk-anh: `cover` cắt cụt hai bên của mác vuông.
   ③ `padding` — chừa vành trắng để logo không chạm mép ô TRÒN (ô tròn cắt vào bốn góc của ảnh vuông).
      Đơn vị px theo từng cỡ chứ không phải %: padding tính theo bề rộng KHỐI CHỨA, mà hai ô này nằm
      trong hai khối chứa khác nhau (.tk-nut và .tk-the-dau). */
.tk-logo[b-m0rmvqquqn] {
    background: #ffffff;
    object-fit: contain;
}

.tk-anh.tk-logo[b-m0rmvqquqn] {
    padding: 3px;
}

.tk-anh-to.tk-logo[b-m0rmvqquqn] {
    padding: 6px;
}

.tk-cong-tac:hover ~ .tk-nut .tk-anh[b-m0rmvqquqn],
.tk-nut:hover .tk-anh[b-m0rmvqquqn] {
    filter: brightness(1.08);
}

/* ───── Nền phủ: bấm RA NGOÀI là đóng (thuần CSS) ─────
   ⛔ ĐỢT 649 — `height: 100vh` KHÔNG PHẢI THỪA, ĐỪNG DỌN ĐI. Thẻ này nằm trong `.top-row`, mà thanh đó
   có `backdrop-filter: blur(8px)` (app.css mục 7) ⇒ theo đặc tả CSS, thanh nav trở thành KHỐI CHỨA của
   mọi hậu duệ `position: fixed` ⇒ `inset: 0` chỉ còn phủ đúng dải nav cao ~56px, bấm xuống thân trang là
   bấm trượt và thẻ KHÔNG đóng (anh Tiến báo hiện tượng này ở lưới app 14/08/2026 — cùng một gốc).
   Ghi đè chiều cao thì luật "quá ràng buộc" của CSS bỏ `bottom`, nền phủ kéo hết một màn hình.
   ⚠ Lỗi chỉ có ở hai giao diện HIỆN ĐẠI (cổ điển không khai kính mờ) — đừng thử ở bản cổ điển rồi kết
   luận là không có lỗi. Giải thích đầy đủ + lý do không nới sang phải: `CoDien/MenuApp.razor.css`.
   🧱 Hàng rào: `TKNB.Tests/NenPhuBamRaNgoaiTests.cs`. */
.tk-nen[b-m0rmvqquqn] {
    display: none;
    position: fixed;
    inset: 0;
    height: 100vh;
    height: 100dvh;   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */
    z-index: 1040;
    margin: 0;
    cursor: default;
    /* Cố ý KHÔNG tô màu: đây là thẻ nhỏ ở góc, làm tối cả màn hình sau lưng nó là quá tay. */
    background: transparent;
}

/* ───── Thẻ tóm tắt ───── */
.tk-the[b-m0rmvqquqn] {
    display: none;
    position: absolute;
    top: calc(100% + .55rem);
    right: 0;
    z-index: 1050;
    width: 19rem;
    /* Không bao giờ tràn khỏi màn hình hẹp: mép phải đã ghim, nên chỉ cần chặn bề rộng.
       Thân trang KHÔNG được cuộn ngang — đó là lý do có dòng này. */
    max-width: calc(100vw - 1rem);
    padding: .85rem;
    background-color: var(--tk-nen-the);
    color: var(--tk-chu);
    border: 1px solid var(--tk-vien);
    border-radius: .6rem;
    box-shadow: var(--tk-bong);
    text-align: left;
}

.tk-cong-tac:checked ~ .tk-nen[b-m0rmvqquqn],
.tk-cong-tac:checked ~ .tk-the[b-m0rmvqquqn] {
    display: block;
}

.tk-the-dau[b-m0rmvqquqn] {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding-bottom: .7rem;
    border-bottom: 1px solid var(--tk-vien);
}

.tk-danh[b-m0rmvqquqn] {
    min-width: 0;   /* cho phép cắt bớt tên dài thay vì đẩy thẻ phình ra */
}

.tk-ten[b-m0rmvqquqn] {
    font-weight: 600;
    font-size: 1rem;
    color: var(--tk-chu);
    overflow-wrap: anywhere;
}

.tk-phu[b-m0rmvqquqn] {
    font-size: .82rem;
    color: var(--tk-chu-phu);
    overflow-wrap: anywhere;
}

/* ───── Bảng hai cột: nhãn / giá trị ───── */
.tk-bang[b-m0rmvqquqn] {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: .28rem .7rem;
    margin: .7rem 0;
    font-size: .86rem;
}

.tk-bang dt[b-m0rmvqquqn] {
    color: var(--tk-chu-phu);
    font-weight: 500;
    white-space: nowrap;
}

.tk-bang dd[b-m0rmvqquqn] {
    margin: 0;
    color: var(--tk-chu);
    overflow-wrap: anywhere;
}

/* ───── Hàng liên kết ───── */
.tk-nut-hang[b-m0rmvqquqn] {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin-bottom: .7rem;
}

.tk-lienket[b-m0rmvqquqn] {
    flex: 1 1 auto;
    text-align: center;
    padding: .35rem .6rem;
    font-size: .85rem;
    text-decoration: none;
    color: var(--tk-lienket);
    border: 1px solid var(--tk-vien);
    border-radius: .4rem;
    white-space: nowrap;
}

.tk-lienket:hover[b-m0rmvqquqn],
.tk-lienket:focus-visible[b-m0rmvqquqn] {
    background-color: var(--tk-ro-chuot);
    text-decoration: underline;
}

/* ═════ ĐỢT 1687 (việc o978) — MỤC 👤 THÔNG TIN CÁ NHÂN ═════
   Chiếm TRỌN một dòng chứ ⛔ không chia ba với hai mục cạnh nó: nhãn của nó dài gấp đôi, mà
   `.tk-lienket` khai `white-space: nowrap` nên ép chung một hàng là ba mục cùng bị bóp rồi tràn ra
   ngoài mép thẻ (thẻ tài khoản có bề ngang cố định).
   ⚠ Nhấn mạnh bằng ĐẬM + nền mờ, ⛔ không bằng một màu mới: tấm thẻ này đi theo bộ màu chủ đề (đợt
     1609b), đóng đinh một mã màu ở đây là mục này lạc khỏi chủ đề ở đúng những bộ màu ⛔ không ai thử. */
.tk-lienket-chinh[b-m0rmvqquqn] {
    flex: 1 1 100%;
    font-weight: 600;
    background-color: var(--tk-ro-chuot);
}

/* ═════ ĐỢT 645 — KHỐI ⚙ CÀI ĐẶT HIỂN THỊ (giao diện + chủ đề, dời từ bảng ☰ Menu) ═════
   Dùng ĐÚNG biến màu sẵn có của thẻ này (--tk-vien · --tk-ro-chuot · --tk-chu-phu) để tự đi theo cả 4
   giao diện — ⛔ không gõ mã màu mới, mỗi mã màu cứng là một chỗ phải nhớ khai lại cho nền tối. */
.tk-cai-dat[b-m0rmvqquqn] {
    margin: .6rem 0;
    padding-top: .6rem;
    border-top: 1px solid var(--tk-vien);
}

.tk-cai-dat-dau[b-m0rmvqquqn] {
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .02em;
    color: var(--tk-chu-phu);
    margin-bottom: .35rem;
}

.tk-nhom-nho[b-m0rmvqquqn] {
    font-size: .76rem;
    color: var(--tk-chu-phu);
    margin: .35rem 0 .2rem;
}

/* Bốn lựa chọn giao diện xếp thành LƯỚI 2 cột: bốn dòng dọc đẩy nút Đăng xuất xuống quá xa,
   mà thẻ tài khoản phải giữ được trong một màn hình điện thoại. */
.tk-hang-chon[b-m0rmvqquqn] {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .25rem;
}

.tk-chon[b-m0rmvqquqn] {
    display: block;
    padding: .3rem .4rem;
    font-size: .82rem;
    border: 1px solid var(--tk-vien);
    border-radius: .35rem;
    cursor: pointer;
    text-decoration: none;
    color: inherit;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tk-chon:hover[b-m0rmvqquqn],
.tk-chon:focus-visible[b-m0rmvqquqn] {
    background-color: var(--tk-ro-chuot);
}

/* Dấu ✓ cho giao diện ĐANG DÙNG — đọc thẳng `data-theme` trên thẻ <html>, không cần một dòng mã nào.
   ⚠ Bộ chọn phải neo theo `:root[data-theme=…]` chứ không theo trạng thái do JS gắn thêm: sau khi
   `datTheme()` chạy, thuộc tính trên <html> là thứ DUY NHẤT chắc chắn đã đổi. */
:root[data-theme="co-dien"] .tk-chon[onclick*="'co-dien'"][b-m0rmvqquqn],
:root[data-theme="co-dien-toi"] .tk-chon[onclick*="co-dien-toi"][b-m0rmvqquqn],
:root[data-theme="light"] .tk-chon[onclick*="'light'"][b-m0rmvqquqn],
:root[data-theme="dark"] .tk-chon[onclick*="'dark'"][b-m0rmvqquqn] {
    border-color: currentColor;
    font-weight: 600;
}

/* ───── Đăng xuất ───── */
.tk-form-ra[b-m0rmvqquqn] {
    margin: 0;
}

/* Màu đỏ đậm dùng CHUNG cho cả nền sáng lẫn nền tối: chữ trắng trên #b02a37 đạt 6,49:1 ở cả hai bên,
   nên không phải khai hai bảng màu — và cũng không có nguy cơ quên khai một bên. */
.tk-ra[b-m0rmvqquqn] {
    display: block;
    width: 100%;
    padding: .45rem .6rem;
    font-size: .9rem;
    font-weight: 500;
    color: #ffffff;
    background-color: #b02a37;
    border: 1px solid #b02a37;
    border-radius: .4rem;
    cursor: pointer;
}

.tk-ra:hover[b-m0rmvqquqn],
.tk-ra:focus-visible[b-m0rmvqquqn] {
    background-color: #8f222d;
    border-color: #8f222d;
}

/* ───── Màn hình hẹp ───── */
@media (max-width: 820px) {
    .tk-anh[b-m0rmvqquqn] {
        width: 30px;
        height: 30px;
        font-size: .75rem;
    }

    .tk-the[b-m0rmvqquqn] {
        width: 17rem;
        padding: .7rem;
    }
}

/* Máy nào người dùng đã khai "hạn chế chuyển động" thì bỏ hiệu ứng sáng lên khi rê chuột.
   ⭐ ĐỢT 1141 — bỏ luôn vệt sáng quét và phép phóng/nhô của khối cầu. KHỐI 3D THÌ GIỮ NGUYÊN: bóng đổ
   và chóp sáng là hình khối TĨNH, không hề chuyển động — bỏ nốt chúng là hiểu sai lời khai của người
   dùng (họ xin bớt chuyển động, không xin bỏ đồ họa). */
@media (prefers-reduced-motion: reduce) {
    .tk-cong-tac:hover ~ .tk-nut .tk-anh[b-m0rmvqquqn],
    .tk-nut:hover .tk-anh[b-m0rmvqquqn] {
        filter: none;
    }

    .tk-cau[b-m0rmvqquqn]::after {
        animation: none;
        opacity: 0;
    }

    .tk-cong-tac:hover ~ .tk-nut .tk-cau[b-m0rmvqquqn],
    .tk-nut:hover .tk-cau[b-m0rmvqquqn],
    .tk-cong-tac:active ~ .tk-nut .tk-cau[b-m0rmvqquqn],
    .tk-cong-tac:checked ~ .tk-nut .tk-cau[b-m0rmvqquqn] {
        transform: none;
    }
}

/* ═══ ⭐ ĐỢT 1340 (việc o541) — ẢNH ĐẠI DIỆN MẶC ĐỊNH ĐỔI THEO CHỦ ĐỀ ═══
   Anh Tiến 26/08/2026: *"chủ đề E10 thay luôn cả icon avatar mặc định"*.

   🔑 Cùng khuôn với dấu ✓ của nút chủ đề và với huy hiệu trên thanh nav: dựng sẵn cả hai ảnh, để
     `data-bo-mau` trên thẻ <html> quyết. ⛔ Không một dòng C# nào — chủ đề đổi bằng JS thuần ngay
     trên trình duyệt nên máy chủ KHÔNG biết người này đang ở chủ đề nào.

   ⚠ Mặc định ẩn ảnh của chủ đề sự kiện và GIỮ mác gốc. Làm ngược lại (mặc định ẩn mác) thì máy nào
     chưa bấm chọn chủ đề bao giờ — tức ĐA SỐ người dùng — sẽ có một ô avatar TRỐNG TRƠN. */
.tk-logo-chu-de[b-m0rmvqquqn] { display: none; }

/* 🔴 ĐỢT 1433 — MỖI CHỦ ĐỀ SỰ KIỆN CẦN ĐÚNG HAI DÒNG DƯỚI ĐÂY, ⛔ đừng quên khi thêm chủ đề mới.
   Thẻ `<img>` thì đã duyệt sổ khai (`TheTaiKhoan.razor`) nên tự sinh đủ; nhưng CSS ⛔ KHÔNG so được
   giá trị `data-bo-mau` của thẻ <html> với `data-ma` của tấm ảnh, nên vẫn phải viết tay từng cặp.
   ⚠ Thiếu cặp dòng này thì hỏng LẶNG LẼ: ảnh của chủ đề mới dựng ra rồi nằm im dưới
     `.tk-logo-chu-de { display: none }`, người dùng thấy mác gốc và ⛔ không ai biết là thiếu.
   🚧 Hàng rào `BieuTuongChuDeTests` nay đối chiếu `LuatBoMauGiaoDien.DanhSach` với chính tệp này ⇒
     thêm chủ đề mà quên hai dòng là build ĐỎ, ⛔ không phải nhớ bằng đầu. */
:root[data-bo-mau="e10"] .tk-logo-chu-de[data-ma="e10"][b-m0rmvqquqn] { display: block; }
:root[data-bo-mau="ky-niem-81-nam"] .tk-logo-chu-de[data-ma="ky-niem-81-nam"][b-m0rmvqquqn] { display: block; }

/* Ảnh mác gốc lui đi đúng khi chủ đề sự kiện lên thay — không thì hai ảnh chồng nhau. */
:root[data-bo-mau="e10"] .tk-logo:not(.tk-logo-chu-de)[b-m0rmvqquqn] { display: none; }
:root[data-bo-mau="ky-niem-81-nam"] .tk-logo:not(.tk-logo-chu-de)[b-m0rmvqquqn] { display: none; }

/* ═══ ⭐ ĐỢT 1354 (việc o541) — BIỂU TƯỢNG CHỦ ĐỀ TRÀN HẾT Ô, KHÔNG CÒN VÀNH TRẮNG ═══
   Anh Tiến 26/08/2026: *"logo vẫn còn viền trắng không đẹp, em cho tràn viền nhé"*.

   🔑 VÀNH TRẮNG ẤY KHÔNG PHẢI LỖI CỦA HÌNH — nó do `.tk-logo` CỐ Ý đặt `background: #ffffff`
     + `padding` (xem khối "ẢNH MẶC ĐỊNH = LOGO PETROLIMEX" ở trên). Mác Petrolimex vẽ trên nền
     TRONG SUỐT nên không có nền trắng thì nửa navy chìm hẳn ở giao diện tối.
     Biểu tượng chủ đề thì NGƯỢC LẠI: nó tự mang sẵn nền riêng (khối bo góc kín màu), nên đúng
     hai thứ cứu được mác gốc lại hoá thành cái vành thừa bao quanh nó.
   ⛔ VÌ THẾ CHỈ GỠ CHO BIỂU TƯỢNG CHỦ ĐỀ, tuyệt đối đừng gỡ thẳng ở `.tk-logo`: làm thế là dựng
     lại đúng lỗi mà đợt 1001 đã đo và tránh — mác Petrolimex chìm vào nền tối.

   ⚠ ĐỘ ĐẶC HIỆU PHẢI ≥ (0,2,0) để thắng `.tk-anh.tk-logo { padding: 3px }`. Viết một lớp trần
     `.tk-logo-chu-de` là (0,1,0) ⇒ THUA, vành trắng vẫn còn nguyên mà build xanh, test xanh —
     hỏng LẶNG LẼ, đúng họ bẫy độ đặc hiệu đã ghi ở quy tắc #5.
   ⚠ `border-color: transparent` chứ ⛔ không `border: 0`: giữ nguyên bề dày hộp, khỏi phụ thuộc
     vào việc `box-sizing` ở đó đang là gì. */
.tk-anh.tk-logo-chu-de[b-m0rmvqquqn],
.tk-anh-to.tk-logo-chu-de[b-m0rmvqquqn] {
    background: none;
    padding: 0;
    object-fit: cover;
    border-color: transparent;
}

/* ═══ 👆 ĐỢT 2194 (việc o1632) — VÙNG CHẠM CỦA NÚT TÀI KHOẢN ĐANG LÀ 30×30px TRÊN MỌI TRANG ═══
   Anh Tiến 07/09/2026: *"rà soát sắp xếp bố cục các màn hình tại thiết bị điện thoại"*.
   📏 Đo sống 375×812, quét 32 trang đích: `.tk-boc` đo **30 × 30px** ở CẢ 32 trang — đây là vùng bấm
     nhỏ có sức lan rộng nhất trong app, vì thẻ tài khoản nằm trên thanh đầu của mọi màn hình. Hàng
     xóm của nó, `.tb-boc` (chuông thông báo), đã đủ 44px ⇒ hai nút cạnh nhau lệch hẳn một bậc.
   🔑 NỚI Ở `.tk-boc` (cái bọc) chứ ⛔ KHÔNG nới `.tk-cong-tac` bằng `inset` âm: ô đánh dấu nằm trong
     `.tk-boc` có `z-index: 1050`, cho nó tràn ra ngoài là nó phủ lên chuông bên cạnh (mép phải chuông
     306px, mép trái thẻ 322px — chỉ cách 16px, nới mỗi bên 7px là đè). Nới cái bọc thì cụm cha
     `.cum-ben-phai` là flex nên **tự giãn** (123 → 137px, vẫn còn 24px trống tới mép 376px).
   🔑 Ảnh đại diện GIỮ NGUYÊN 30px — chỉ vùng bấm to ra, ⛔ không phải cái nhìn thấy. Phóng to ảnh là
     đổi dáng thanh đầu của cả app, thứ ⛔ không ai yêu cầu.
   🔒 Trong `@media (pointer: coarse)`: chuyện của NGÓN TAY, ⛔ không phải của bề ngang màn hình —
     máy tính (kể cả cửa sổ hẹp) ⛔ không đổi một pixel. Ngưỡng đọc từ biến, ⛔ đừng gõ số 44 vào đây. */
@media (pointer: coarse) {
    .tk-boc[b-m0rmvqquqn] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        justify-content: center;
    }

    /* ═══ 📱 ĐỢT 2285 (việc o1811) — ẢNH ĐẠI DIỆN NAY BẰNG CỠ BA CỤM CÒN LẠI TRÊN THANH ═══
       Anh Tiến 10/09/2026, ảnh chụp điện thoại: *"thanh nav các icon cũng cái to cái nhỏ sắp xếp
       lộn xộn không đều và đẹp"*.

       🔴 ĐÂY LÀ LƯỢT ĐẢO MỘT CÂU CỦA ĐỢT 2194 NGAY TRÊN, ⛔ đừng đọc hai khối thành mâu thuẫn.
       Câu ấy viết: *"Ảnh đại diện GIỮ NGUYÊN 30px — chỉ vùng bấm to ra… Phóng to ảnh là đổi dáng
       thanh đầu của cả app, thứ ⛔ không ai yêu cầu"*. Đúng ở thời điểm ấy; nay **chính anh yêu cầu**.
       📏 Đo sống 390×844 trước lượt này, bốn cụm trên thanh: lưới 9 chấm `.ma-nut` **44×44** · chip
         logo `.mcd-logo` **44×111** · chuông `.tb-nut` **44×78** · ảnh đại diện **30×30** ⇒ đúng một
         cụm thấp hơn ba cụm kia **14px (−31,8%)**, và nó đứng ngay cạnh chuông nên chỗ lệch lộ nhất.
       ⇒ Nay cả bốn cùng **44px** — cụm bọc `.tk-boc` vốn đã 44px từ đợt 2194 nên ⛔ không một phép
         đo vùng chạm nào đổi; thứ đổi là **cái nhìn thấy**.

       ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` (app.css, đợt 1518) — ⛔ đừng gõ số 44 vào đây.
       ⚠ Luật này phải đứng SAU khối `@media (max-width: 820px)` (ảnh 30px) trong CÙNG tệp: hai luật
         cùng độ đặc hiệu (0,1,0) nên thắng thua **theo THỨ TỰ**. Dời khối này lên trên là bản vá
         trông có mà ⛔ không ăn — build xanh, test xanh.
       🔒 Chốt ngược: máy tính (`pointer: fine`) giữ nguyên **34px**, cửa sổ hẹp giữ nguyên **30px**.
       ⓘ Cỡ chữ nới theo cho chữ cái đầu của người CHƯA có ảnh — ô 44px mà chữ .75rem thì trông rỗng. */
    .tk-anh[b-m0rmvqquqn] {
        width: var(--cham-toi-thieu);
        height: var(--cham-toi-thieu);
        font-size: 1rem;
    }
}

/* ═══ 📱 ĐỢT 2304 (việc o1845) — BẢNG «🎨 CHỦ ĐỀ» BUNG NGAY TRONG THẺ Ở KHỔ ĐIỆN THOẠI ═══
   Anh Tiến 10/09/2026, ảnh chụp điện thoại (thẻ tài khoản đang mở, bảng chủ đề bung lệch): "app điện
   thoại ko chọn được".
   🔑 Gốc bệnh nằm ở KHỚP NỐI hai component, ⛔ không riêng bên nào: bảng .cbm-bang của NutChonBoMau neo
      position absolute + right 0 + min-width 16.5rem vào nút «Chủ đề» — dáng ấy đúng khi nút đứng
      trên thanh nav (bảng lan sang TRÁI, còn cả màn hình). Vào thẻ tài khoản, nút nằm NỬA TRÁI một thẻ
      17rem ⇒ bảng lan quá mép trái màn hình; lại bung XUỐNG dưới đáy thẻ, mà thẻ ghim vào thanh nav nên
      phần ấy ⛔ cuộn tới được ⇒ đa số mục chọn nằm ngoài khung nhìn.
   ⇒ Khổ ≤576px: bảng thôi nổi, trải trọn bề ngang thẻ ngay dưới nút; thẻ tự cuộn dọc khi cao hơn khung
      nhìn. Hai vế đi cùng nhau: bung tĩnh mà thẻ ⛔ cuộn thì mục cuối vẫn rơi ra ngoài đáy.
   ⚠ Luật cho .cbm-boc / .cbm-bang đi qua ::deep và ĐẶT Ở ĐÂY, ⛔ không ở NutChonBoMau.razor.css: ở chỗ
      khác (thanh nav) dáng nổi vẫn là dáng đúng.
   🔒 Máy tính và mọi cửa sổ rộng hơn 576px ⛔ đổi một pixel. */
@media (max-width: 576px) {
    .tk-the[b-m0rmvqquqn] {
        max-height: calc(100vh - 4.75rem);
        max-height: calc(100dvh - 4.75rem);   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    .tk-the[b-m0rmvqquqn]  .cbm-boc {
        display: block;
    }

    .tk-the[b-m0rmvqquqn]  .cbm-bang {
        position: static;
        min-width: 0;
        margin-top: .35rem;
        box-shadow: none;
    }
}
/* /Components/Pages/BaoCao/BaoCaoNangSuat.razor.rz.scp.css */
/* 📱 ĐỢT 2194 (việc o1632) → 🩸 ĐỢT 2197 (việc o1639) — LUẬT ĐÃ DỜI LÊN `wwwroot/app.css`.

   Đợt 2194 vá dải nút chọn nhanh kỳ của trang này (đo 375×812: dải 380px trong khung 375, sáu nút
   bị ép còn 57–73px, chữ gãy hai dòng, nút cao 62px) bằng một khối `@media (max-width: 576px)`
   viết ngay trong tệp cách ly này.

   🩸 ĐO SỐNG 08/09/2026 BÁC BỎ **MỘT NỬA** BẢN VÁ ẤY: `flex-wrap: wrap` chạy thật (6 nút, 2 hàng,
   dải 351px, ⛔ không nút nào bị cắt) nhưng **5/6 nút vẫn lề âm và vẫn thiếu bo góc** — hai dòng
   `margin-left: 0` + `border-radius` ⛔ CHƯA BAO GIỜ CHẠY. Gốc: Bootstrap 5.3 đặt chúng ở
   **(0,4,0)**, còn CSS cách ly biên dịch ra `.btn-group > .btn[b-…]` chỉ **(0,3,0)** ⇒ thua.
   Hỏng LẶNG LẼ: build xanh, test xanh, dải vẫn xuống dòng nên trông như đã vá xong.

   ⇒ Luật nay nằm ở **một chỗ** — cuối `wwwroot/app.css`, ghi đè đúng bộ chọn của Bootstrap nên đủ
   độ đặc hiệu, và áp cho **cả 80 dải `.btn-group` của app** chứ ⛔ không riêng trang này.
   ⛔ Đừng chép khối media ấy về đây: chép về là dựng lại đúng bản nửa-chạy vừa gỡ. */
/* /Components/Pages/BaoCao/BaoCaoTheoKy.razor.rz.scp.css */
/* ⭐ ĐỢT 2151 (việc o1572) — màn hình 🗓 Báo cáo theo kỳ.
   ⚠ CSS phạm vi (scoped) của riêng trang này. ⛔ Đừng bê sang app.css: mấy lớp `bcky-*` chỉ có nghĩa
     trong dải ô số của trang này, đưa ra chỗ dùng chung là mời màn hình khác vô tình ăn phải.
   🎨 Màu lấy từ BIẾN của bộ giao diện (--bs-*), ⛔ không gõ mã màu tay — theme tối đổi biến là trang
     này đi theo, còn mã màu tay thì đứng nguyên và thành chữ tối trên nền tối. */

/* Nhãn kỳ nằm giữa hai nút ◀ ▶: giữ bề ngang ổn định để dải nút không nhảy khi đổi từ
   "Tuần 31/08 – 06/09/2026" sang "Quý III/2026". */
.bcky-nhan[b-ywzgt6s0od] {
    min-width: 11rem;
    opacity: 1;
    cursor: default;
}

.bcky-o[b-ywzgt6s0od] {
    border-color: var(--bs-border-color);
}

.bcky-lech[b-ywzgt6s0od] {
    display: flex;
    align-items: baseline;
    gap: .3rem;
    white-space: nowrap;
}

/* 🔴 MÀU nói TỐT/XẤU (theo nghiệp vụ), MŨI TÊN nói LÊN/XUỐNG (theo phép trừ) — hai nghĩa, hai chỗ.
   ⛔ Đừng gộp: ô "Đang quá hạn" tăng thì mũi tên phải đi LÊN mà màu phải là XẤU. */
.bcky-tot[b-ywzgt6s0od] {
    color: var(--bs-success);
}

.bcky-xau[b-ywzgt6s0od] {
    color: var(--bs-danger);
}

.bcky-trung[b-ywzgt6s0od] {
    color: var(--bs-secondary-color);
}

.bcky-muiten[b-ywzgt6s0od] {
    font-size: .85em;
}

/* ═══ 📱 ĐỢT 2194 (việc o1632) — Ở KHỔ ĐIỆN THOẠI, `nowrap` LÀM CÂU SO SÁNH BỊ CẮT CỤT ═══
   Anh Tiến 07/09/2026: *"rà soát sắp xếp bố cục các màn hình tại thiết bị điện thoại"*.
   📏 Đo sống 375×812 + ảnh chụp: bốn ô số của trang này in ra «▼ -10 (-66,7%) so với tuần **tr**»,
     «-9 (-90%) so với tuần **tr**», «-6 (-85,7%) so với tuần **t**» — chữ cụt ngay giữa từ.
   🔴 GỐC BỆNH là `white-space: nowrap` ở `.bcky-lech` phía trên: nó có lý do THẬT (giữ mũi tên dính
     với con số, ⛔ đừng gỡ ở khổ máy tính) nhưng ở 375px cả câu dài hơn bề ngang ô ⇒ tràn và bị cắt.
   🔑 CHỮA THEO LUẬT ① của skill (mảnh đứng trước chỗ gãy dòng là mảnh mồ côi): ⛔ không thả cho câu
     tự gãy tuỳ ý — cụm «mũi tên + số» ở lại NGUYÊN dòng đầu, còn vế «so với … trước» nhận
     `flex: 1 1 100%` để xuống TRỌN dòng dưới. Ngắt đúng chỗ nghĩa, ⛔ không phải ngắt giữa từ. */
@media (max-width: 576px) {
    .bcky-lech[b-ywzgt6s0od] {
        flex-wrap: wrap;
        white-space: normal;
    }

    .bcky-lech > .text-body-secondary[b-ywzgt6s0od] {
        flex: 1 1 100%;
    }
}
/* /Components/Pages/BaoCao/DanhMucBaoCao.razor.rz.scp.css */
/* Thẻ trên mục lục báo cáo (đợt 451).
   CỐ Ý là CSS RIÊNG CỦA TRANG (scoped) chứ không thêm luật vào app.css: đây là dáng của đúng một màn
   hình, nhét vào tệp dùng chung là mỗi trang mới lại phình thêm một mục mà không ai dám xóa. */

.the-bao-cao[b-10xf54k3ze] {
    transition: box-shadow .15s ease, transform .15s ease;
}

.the-bao-cao:hover[b-10xf54k3ze],
.the-bao-cao:focus-visible[b-10xf54k3ze] {
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
    transform: translateY(-2px);
}

/* Tôn trọng người dùng tắt hiệu ứng chuyển động ở hệ điều hành */
@media (prefers-reduced-motion: reduce) {
    .the-bao-cao[b-10xf54k3ze] {
        transition: none;
    }

    .the-bao-cao:hover[b-10xf54k3ze],
    .the-bao-cao:focus-visible[b-10xf54k3ze] {
        transform: none;
    }
}
/* /Components/Pages/Camera/KhoiSoDoNguyenLyCamera.razor.rz.scp.css */
/* ═══ ĐỢT 1296 — SƠ ĐỒ NGUYÊN LÝ CAMERA ═══
   CSS ISOLATION: mọi luật dưới đây CHỈ ăn trong khối này. Tên lớp ngắn (`.sdnl-*`) nên đưa sang
   `app.css` là cầm chắc đụng chỗ khác — đúng diện rủi ro lan truyền của quy tắc #18.

   ⚠ MÀU LẤY TỪ TOKEN BOOTSTRAP 5.3, ⛔ KHÔNG gõ mã hex: app có hai giao diện (sáng · tối) nối vào
     Bootstrap qua `data-bs-theme` nên token tự lật. Gõ hex là lặp lại lỗi đợt 537 (chữ trùng màu nền,
     tương phản 2,32 — nhìn như mất chữ). Nét vẽ SVG cũng vậy: dùng `stroke: var(--bs-…)`.

   ⓘ Bộ chọn nhắm thẳng thẻ SVG viết trong CHÍNH tệp `.razor` đi kèm nên ⛔ không cần `::deep`. */

.sdnl-khung[b-lv2ko6x2pt] {
    overflow-x: auto;
    border: 1px solid var(--bs-border-color);
    border-radius: .6rem;
    background: var(--bs-body-bg);
    padding: .5rem;
}

/* Khung vẽ có bề ngang tối thiểu: hẹp hơn thì ba cột chồng chữ lên nhau, nên cho CUỘN NGANG trong
   khung riêng — thân trang ⛔ không được cuộn ngang theo. */
.sdnl-hinh[b-lv2ko6x2pt] {
    min-width: 730px;
    display: block;
}

/* ───── CHỮ ───── */
.sdnl-cot-ten[b-lv2ko6x2pt] {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    fill: var(--bs-secondary-color);
}

.sdnl-hop-chu[b-lv2ko6x2pt] {
    font-size: 12px;
    fill: var(--bs-emphasis-color);
}

/* ───── HỘP ───── */
.sdnl-nen[b-lv2ko6x2pt] {
    fill: var(--bs-tertiary-bg);
    stroke: var(--bs-border-color);
    stroke-width: 1;
}

.sdnl-hop-cam .sdnl-nen[b-lv2ko6x2pt] { stroke: var(--bs-primary); }
.sdnl-hop-tu .sdnl-nen[b-lv2ko6x2pt] { stroke: var(--bs-warning); }

/* Đầu ghi đổi VIỀN theo trạng thái dò. Câu chữ vẫn nằm trong <title> của hộp — màu chỉ là lớp thứ hai,
   ⛔ không phải kênh thông tin duy nhất. */
.sdnl-nen-song[b-lv2ko6x2pt] { stroke: var(--bs-success); stroke-width: 2; }
.sdnl-nen-mat-ket-noi[b-lv2ko6x2pt] { stroke: var(--bs-danger); stroke-width: 2; }
.sdnl-nen-chua-do[b-lv2ko6x2pt] { stroke: var(--bs-warning); stroke-width: 2; }
.sdnl-nen-khong-do-duoc[b-lv2ko6x2pt] { stroke: var(--bs-secondary-color); }

/* ───── CẠNH ───── */
.sdnl-canh[b-lv2ko6x2pt] {
    fill: none;
    stroke: var(--bs-primary);
    stroke-width: 1.5;
    opacity: .75;
}

/* Quan hệ SUY RA từ danh mục (chưa khai cáp) — nét đứt mờ, phân biệt được bằng mắt với cáp đã khai.
   ⛔ Đừng vẽ giống cáp thật: giống nhau là màn hình nói dối rằng hạ tầng đã khai đủ. */
.sdnl-canh-suy[b-lv2ko6x2pt] {
    stroke: var(--bs-secondary-color);
    stroke-dasharray: 4 4;
    stroke-width: 1;
    opacity: .5;
}

/* Cáp dự phòng / đã cắt. */
.sdnl-canh-nghi[b-lv2ko6x2pt] {
    stroke: var(--bs-secondary-color);
    stroke-dasharray: 2 3;
    opacity: .45;
}

/* ───── CHÚ GIẢI ───── */
.sdnl-chu-giai[b-lv2ko6x2pt] {
    display: flex;
    flex-wrap: wrap;
    gap: .9rem;
    align-items: center;
    font-size: .8rem;
    color: var(--bs-secondary-color);
    margin-bottom: .5rem;
}

.sdnl-mau[b-lv2ko6x2pt] {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 3px;
    background: var(--bs-tertiary-bg);
    border: 2px solid var(--bs-border-color);
    vertical-align: -1px;
}

.sdnl-mau-cam[b-lv2ko6x2pt] { border-color: var(--bs-primary); }
.sdnl-mau-tu[b-lv2ko6x2pt] { border-color: var(--bs-warning); }
.sdnl-mau-dg[b-lv2ko6x2pt] { border-color: var(--bs-success); }

.sdnl-chu-giai-net[b-lv2ko6x2pt] {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
}

.sdnl-net-lien[b-lv2ko6x2pt],
.sdnl-net-dut[b-lv2ko6x2pt] {
    display: inline-block;
    width: 22px;
    height: 0;
    border-top-width: 2px;
    border-top-color: var(--bs-primary);
    border-top-style: solid;
}

.sdnl-net-dut[b-lv2ko6x2pt] {
    border-top-color: var(--bs-secondary-color);
    border-top-style: dashed;
}

/* ───── MÀN HÌNH TRỐNG ───── */
.sdnl-trong[b-lv2ko6x2pt] {
    text-align: center;
    padding: 2.5rem 1rem;
    border: 1px dashed var(--bs-border-color);
    border-radius: .6rem;
    background: var(--bs-tertiary-bg);
}
/* /Components/Pages/Camera/KhoiTuongCamera.razor.rz.scp.css */
/* ═══ TƯỜNG CAMERA (đợt 1900, việc o1157) ═══
   Số cột KHÔNG nằm ở đây: nó là style nội tuyến dựng bằng LuatTuongCamera.CssCotLuoi, vì cỡ lưới do
   người dùng chọn. Ở đây chỉ có dáng của một ô. */

.tc-luoi[b-3l7j4abm36] {
    display: grid;
    gap: .5rem;
}

/* Cả ô là một nút bấm để mở hộp xem một camera. Bỏ hết dáng nút mặc định của trình duyệt, giữ lại
   khung và bo góc cho khớp phần còn lại của app. */
.tc-nut[b-3l7j4abm36] {
    display: block;
    width: 100%;
    padding: 0;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: .375rem;
    overflow: hidden;
    background: #0b1620;
    text-align: left;
    cursor: pointer;
}

.tc-nut:hover[b-3l7j4abm36] {
    border-color: var(--bs-primary, #0d6efd);
}

/* Khung ảnh giữ tỉ lệ 16:9 kể cả lúc chưa có ảnh — thiếu nó thì lúc bấm Dừng cả lưới xẹp lại thành
   một hàng chữ, rồi bấm Xem là trang nhảy dựng đứng. */
.tc-khung[b-3l7j4abm36] {
    display: block;
    position: relative;
    aspect-ratio: 16 / 9;
    background: #0b1620;
}

.tc-anh[b-3l7j4abm36] {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.tc-nghi[b-3l7j4abm36] {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #7f8c99;
    font-size: .8rem;
}

.tc-nhan[b-3l7j4abm36] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    padding: .25rem .5rem;
    background: rgba(0, 0, 0, .55);
    color: #e9edf1;
    font-size: .8rem;
}

/* Tên camera dài thì cắt bằng dấu ba chấm, ⛔ đừng cho xuống dòng: một ô cao thêm một dòng là cả hàng
   trong lưới cao theo, mà lưới thì đang cố giữ mọi ô bằng nhau. */
.tc-ten[b-3l7j4abm36] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tc-kenh[b-3l7j4abm36] {
    flex: 0 0 auto;
    opacity: .75;
}

/* Hai nút lật trang chỉ có một ký hiệu nên `btn-sm` cho ra 37x31px — đo sống 03/09. Với NGÓN TAY thì
   phải đủ 44px (đợt 1518). Điều kiện là CÁI CHẠM VÀO, ⛔ không phải bề ngang màn hình: cửa sổ thu nhỏ
   vẫn dùng chuột, còn màn rộng vẫn có thể là cảm ứng. Ngưỡng đọc BIẾN dùng chung, ⛔ đừng gõ số 44px. */
@media (pointer: coarse) {
    .tc-lat[b-3l7j4abm36] {
        min-width: var(--cham-toi-thieu, 44px);
        min-height: var(--cham-toi-thieu, 44px);
    }
}
/* /Components/Pages/Camera/KhoiXemTrucTiepCamera.razor.rz.scp.css */
/* ═══ KHUNG XEM MỘT CAMERA (đợt 1906, việc o1172) ═══
   Anh Tiến 03/09/2026: "khi xem camera thì bị giật giật và reset khung nhìn liên tục".

   Tỉ lệ khung KHÔNG nằm ở đây: nó là style nội tuyến dựng bằng LuatCamera.TiLeKhungAnh, vì luồng phụ
   là khuôn 4:3 còn luồng chính là 16:9 — người dùng đổi luồng thì tỉ lệ đổi theo. Ở đây chỉ có dáng. */

/* Khung giữ chỗ sẵn kể cả lúc chưa có ảnh — thiếu nó thì mỗi nhịp làm mới là một cú nhảy bố cục
   (đo sống 03/09: 220px lúc trống ↔ 893px lúc có ảnh = 673px), và ô này nằm trong hộp thoại nên cả
   hộp xô theo. Giữ min-height làm sàn cho khung cửa sổ rất hẹp. */
.xtt-khung[b-wfk34bwa4n] {
    display: block;
    position: relative;
    min-height: 220px;
    overflow: hidden;
    background: #0b1620;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: .375rem;
}

/* Ảnh phủ kín khung và dùng object-fit CONTAIN, KHÔNG phải cover: đây là ô để NHÌN KỸ một chỗ, cắt
   mất mép hình là cắt mất đúng thứ người trực đang tìm. Tường thì ngược lại — nó dùng cover vì ở đó
   mọi ô phải bằng nhau để liếc cả một khu. */
.xtt-anh[b-wfk34bwa4n] {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.xtt-nghi[b-wfk34bwa4n] {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #7f8c99;
    font-size: .875rem;
}
/* /Components/Pages/Camera/TabTongQuanCamera.razor.rz.scp.css */
/* ═══ ĐỢT 1296 — TAB 📊 TỔNG QUAN CAMERA: "PHONG CÁCH CAMERA VÀ HIỆN ĐẠI" ═══
   Anh Tiến 25/08/2026: *"tạo thêm tab tổng quan camera như bên nước nhưng mang phong cách camera và
   hiện đại"*.

   ── NGÔN NGỮ HÌNH MƯỢN TỪ PHÒNG ĐIỀU KHIỂN ──
   · Ô số có VẠCH SÁNG dọc bên trái, như đèn trạng thái trên tủ rack.
   · Trạng thái đầu ghi là CHẤM TÍN HIỆU có quầng sáng, ⛔ không phải huy hiệu chữ.
   · Thanh tiến độ sa bàn giống thanh lấp đầy dung lượng kênh ghi.
   · Con số chính dùng chữ số ĐỀU LY (tabular) nên cột số không nhảy khi giá trị đổi.

   ⛔ VÌ SAO ĐỂ Ở ĐÂY MÀ KHÔNG PHẢI `app.css`: đây là CSS ISOLATION — Blazor gắn dấu phạm vi nên mọi
      luật dưới đây CHỈ ăn trong tab này. Đưa sang chỗ dùng chung là đúng diện rủi ro lan truyền
      (quy tắc #18): mấy cái tên lớp ngắn như `.tqc-o` rất dễ đụng chỗ khác.

   ⚠ MÀU LẤY TỪ TOKEN BOOTSTRAP 5.3, ⛔ KHÔNG gõ mã hex. App có hai giao diện (sáng · tối) nối vào
     Bootstrap qua `data-bs-theme`, nên token tự lật màu; gõ hex là lặp lại lỗi đợt 537 (chữ trùng màu
     nền, tương phản 2,32 — nhìn như mất chữ). Token dùng ở đây đều CÓ trong 5.3 nên ⛔ không cần giá
     trị lui; token của 5.4 trở lên thì bắt buộc viết dạng `var(--bs-x, #dee2e6)`.

   ⓘ Component con (IconCameraPtz) nằm ngoài dấu phạm vi của tệp này — màu của nó đi bằng thuộc tính
     `color` DI TRUYỀN chứ ⛔ không bằng bộ chọn, nên ở đây không cần `::deep`. */

/* ───── KHUNG NGOÀI ───── */
.tqc-gon[b-k1n0yfa2d0] {
    display: flex;
    flex-direction: column;
    gap: .75rem;
}

/* ───── 1. BỐN Ô SỐ CHÍNH ─────
   Lưới tự xuống dòng: màn hẹp thì 2 ô một hàng, ⛔ không ép 4 cột cứng (ép cứng là màn 1366 vỡ chữ). */
.tqc-luoi-so[b-k1n0yfa2d0] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: .75rem;
}

.tqc-o[b-k1n0yfa2d0] {
    position: relative;
    padding: .6rem .8rem .55rem 1rem;
    border: 1px solid var(--bs-border-color);
    border-radius: .6rem;
    background: var(--bs-body-bg);
    overflow: hidden;
}

/* VẠCH SÁNG bên trái — đèn trạng thái tủ rack. Màu do lớp phụ của từng ô quyết định. */
.tqc-o[b-k1n0yfa2d0]::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 4px;
    background: var(--tqc-vach, var(--bs-primary));
}

.tqc-o-cam[b-k1n0yfa2d0] { --tqc-vach: var(--bs-primary); }
.tqc-o-tot[b-k1n0yfa2d0] { --tqc-vach: var(--bs-success); }
.tqc-o-canh[b-k1n0yfa2d0] { --tqc-vach: var(--bs-danger); }
.tqc-o-saban[b-k1n0yfa2d0] { --tqc-vach: var(--bs-info); }
.tqc-o-nhom[b-k1n0yfa2d0] { --tqc-vach: var(--bs-warning); }

.tqc-o-dau[b-k1n0yfa2d0] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .4rem;
}

.tqc-o-ten[b-k1n0yfa2d0] {
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
    color: var(--bs-secondary-color);
}

.tqc-o-hinh[b-k1n0yfa2d0] {
    color: var(--tqc-vach, var(--bs-primary));
    opacity: .85;
    line-height: 1;
}

/* Con số chính. `font-variant-numeric: tabular-nums` giữ bề rộng chữ số cố định — cột số không
   nhấp nháy sang trái phải mỗi lượt dò lại đầu ghi. */
.tqc-con-so[b-k1n0yfa2d0] {
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    color: var(--bs-emphasis-color);
}

/* Phần MẪU SỐ và ký hiệu % — nhỏ và nhạt hơn để con số chính vẫn là thứ đập vào mắt trước. */
.tqc-o-mau[b-k1n0yfa2d0] {
    font-size: 1rem;
    font-weight: 600;
    color: var(--bs-secondary-color);
}

.tqc-o-phu[b-k1n0yfa2d0] {
    font-size: .8rem;
    color: var(--bs-secondary-color);
}

/* Hai màu theo CHIỀU LỢI/BẤT LỢI (luật ngôn từ 30/07): việc còn tồn = vàng nhắc, số bất lợi = đỏ.
   ⛔ Đừng tô đỏ cho "còn chờ đặt vị trí" — đó là việc đang làm dở, ⛔ không phải sự cố. */
.tqc-cho[b-k1n0yfa2d0] { color: var(--bs-warning-text-emphasis); font-weight: 600; }
.tqc-xau[b-k1n0yfa2d0] { color: var(--bs-danger-text-emphasis); font-weight: 600; }

/* ───── 2. HÀNG HAI: hai thẻ cạnh nhau ───── */
.tqc-hang2[b-k1n0yfa2d0] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: .75rem;
}

.tqc-the[b-k1n0yfa2d0] {
    border: 1px solid var(--bs-border-color);
    border-radius: .6rem;
    background: var(--bs-body-bg);
    padding: .6rem .8rem .7rem;
}

.tqc-the-dau[b-k1n0yfa2d0] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .5rem;
}

.tqc-the-ten[b-k1n0yfa2d0] {
    font-size: .95rem;
    font-weight: 600;
    margin: 0;
}

.tqc-the-chu[b-k1n0yfa2d0] {
    font-size: .82rem;
    margin: .4rem 0 .3rem;
}

/* ───── THANH LẤP ĐẦY kiểu dung lượng kênh ghi ───── */
.tqc-thanh[b-k1n0yfa2d0] {
    height: 10px;
    border-radius: 999px;
    background: var(--bs-tertiary-bg);
    overflow: hidden;
}

.tqc-thanh-day[b-k1n0yfa2d0] {
    height: 100%;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--bs-info), var(--bs-primary));
    transition: width .35s ease;
}

.tqc-hatang[b-k1n0yfa2d0] {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    font-size: .82rem;
    color: var(--bs-secondary-color);
}

/* ───── DẢI TRẠNG THÁI ĐẦU GHI ───── */
.tqc-ds-dg[b-k1n0yfa2d0] {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .3rem;
    max-height: 190px;
    overflow-y: auto;
}

.tqc-dg[b-k1n0yfa2d0] {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: .84rem;
    padding: .18rem .3rem;
    border-radius: .35rem;
    background: var(--bs-tertiary-bg);
}

.tqc-dg-ten[b-k1n0yfa2d0] {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
}

.tqc-dg-tt[b-k1n0yfa2d0] {
    flex: none;
    font-size: .76rem;
    color: var(--bs-secondary-color);
}

/* CHẤM TÍN HIỆU. Quầng sáng bằng `box-shadow` nên ⛔ không tốn thêm phần tử nào.
   ⚠ Trạng thái ⛔ KHÔNG chỉ phân biệt bằng MÀU: mỗi dòng luôn có chữ ở `.tqc-dg-tt` bên cạnh, để người
     phân biệt màu kém vẫn đọc được. Màu chỉ là lớp thứ hai. */
.tqc-cham[b-k1n0yfa2d0] {
    flex: none;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--bs-secondary-color);
}

.tqc-cham-song[b-k1n0yfa2d0] {
    background: var(--bs-success);
    box-shadow: 0 0 0 3px rgba(var(--bs-success-rgb), .22);
}

.tqc-cham-mat-ket-noi[b-k1n0yfa2d0] {
    background: var(--bs-danger);
    box-shadow: 0 0 0 3px rgba(var(--bs-danger-rgb), .22);
}

.tqc-cham-chua-do[b-k1n0yfa2d0] { background: var(--bs-warning); }

.tqc-cham-khong-do-duoc[b-k1n0yfa2d0] { background: var(--bs-secondary-color); opacity: .55; }

/* ───── 3. BẢNG CÁC ĐỢT LẮP ĐẶT ─────
   Bảng cuộn NGANG trong khung riêng: thân trang ⛔ không được cuộn ngang theo. */
.tqc-bang-cuon[b-k1n0yfa2d0] {
    overflow-x: auto;
    max-height: 220px;
    overflow-y: auto;
}

.tqc-bang[b-k1n0yfa2d0] {
    font-size: .84rem;
    --bs-table-cell-padding-y: .2rem;
}

.tqc-nhom-ten[b-k1n0yfa2d0] {
    font-weight: 600;
    display: block;
}

.tqc-nhom-tp[b-k1n0yfa2d0] {
    font-size: .74rem;
    display: block;
}

/* ───── 4. MÀN HÌNH TRỐNG ───── */
.tqc-trong[b-k1n0yfa2d0] {
    text-align: center;
    padding: 2.5rem 1rem;
    border: 1px dashed var(--bs-border-color);
    border-radius: .6rem;
    background: var(--bs-tertiary-bg);
}

.tqc-trong-hinh[b-k1n0yfa2d0] {
    color: var(--bs-secondary-color);
    opacity: .7;
    margin-bottom: .5rem;
}

/* ─────────── ⭐ ĐỢT 1979 (việc o1261) — SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY ───────────
   📏 Đo sống 04/09/2026 (Chrome thật, 390×844, cảm ứng, `tienvm`,
      `do-song-khung-hep.mjs --trang /camera`): trang này chỉ còn **1** chỗ chữ nhỏ và chính là
      `.tqc-nhom-tp` — **11,8px** (luật `.74rem` ở trên), dưới sàn `--cham-chu-toi-thieu` = 12px.
      Sát ngưỡng nhưng vẫn là dưới; vá nốt để trang /camera về **0**.
   ⚠ `max()` chứ ⛔ không gõ thẳng 12px — nâng SÀN, giữ tỉ lệ. */
@media (pointer: coarse) {
    .tqc-nhom-tp[b-k1n0yfa2d0] { font-size: max(.74rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/Pages/ChamCong/ChamCongTheoThang.razor.rz.scp.css */
/* ============================================================================================
   ĐỒ HỌA RIÊNG CỦA BẢNG CÔNG THÁNG — đợt 387 (anh Tiến 09/08/2026), dọn lại ở đợt 486
   ============================================================================================
   ⛔ ĐÂY LÀ CSS SCOPED (Blazor tự gắn thuộc tính b-xxxxx vào từng thẻ của ChamCongTheoThang.razor)
      nên MỌI luật dưới đây CHỈ ăn trong màn hình này. Cố ý KHÔNG đụng app.css / co-dien.css —
      hai file dùng chung do phiên đồ họa giữ, sửa vào đó là đụng mọi trang của mọi phiên.

   ⭐ ĐỢT 486 — ĐÃ XÓA HAI KHỐI TRÙNG, giờ file này chỉ còn thứ RIÊNG của màn hình này.
      Đợt 387 dựng hai bộ lớp `.o-cot-ngay*` (đầu cột ngày) và `.o-so*` (thẻ số) ngay tại đây.
      Đợt 483 bê khuôn đó sang mục 👤 Theo người và gói thành hai COMPONENT — nhưng lúc ấy đợt 478
      đang giữ file này nên KHÔNG đụng vào được, để lại hai bản chép của cùng một hình:
        • .o-cot-ngay* → nay ở OCotNgayLich.razor.css (component OCotNgayLich)
        • .o-so*       → nay ở OSoThongKe.razor.css   (component OSoThongKe)
      Đối chiếu TỪNG luật trước khi xóa: hai bản khai y hệt nhau, chỉ khác ba chỗ và cả ba đều là
      bản component RỘNG HƠN (không mất luật nào):
        ① biến màu chuyển từ thẻ bọc `.dai-so-lieu` sang chính `.o-so` — vì luật viết trong file
          scoped của COMPONENT không với tới thẻ bọc nằm ở màn hình gọi (khác scope);
        ② luật chữ sẫm cho huy hiệu tròn ở hai nền TỐI: bản cũ liệt kê đủ 6 loại thẻ, bản component
          áp cho MỌI `.o-so-hieu` ⇒ cùng kết quả với 6 loại màn hình này dùng;
        ③ component có thêm `.o-so--trung-tinh` (màn hình này không dùng — vô hại).
      ⇒ Sửa dáng thẻ số / đầu cột ngày thì sửa ở HAI file component đó, KHÔNG thêm lại vào đây.

   ⚠ BÀI HỌC SPECIFICITY (đợt 381 mục 3 — luật rời "chết câm" vì thua chuỗi dài) vẫn còn hiệu lực,
      chỉ là nó chuyển sang OCotNgayLich.razor.css: co-dien.css NẠP SAU file scoped
      (App.razor: app.css → TKNB.Web.styles.css → co-dien.css) và tô đầu bảng bằng
      `:root[data-theme^="co-dien"] .table > thead > tr > th` = (0,3,3) ⇒ luật tô cuối tuần phải viết
      ĐỦ ĐƯỜNG DẪN `.table > thead > tr > th.o-cot-ngay.ngay-xx` mới thắng. Bọc trong component còn
      ĂN CHẮC HƠN: Blazor gắn `[b-xxxxx]` vào `th` nên chuỗi chọn lên (0,5,3).

   ⚠ Số trong file .css là số TĨNH nên không dính bẫy dấu phẩy thập phân của vi-VN (quy tắc #5) —
      đó cũng là lý do mọi kích thước nằm ở đây chứ không nội suy từ C# vào thuộc tính style.
   ============================================================================================ */

/* ===== NHÓM NÚT CÁCH XEM (🔢 Công (8/4) · 🕐 Giờ đến — về) — dáng "viên thuốc" liền khối ===== */
/* KHÔNG khai ở giao diện cổ điển: co-dien.css nạp sau và đã ép .btn về góc vuông — để nguyên cho
   nó thắng, đúng luật "cổ điển phẳng nguyên trạng". */
/* ⭐ ĐỢT 2213 (việc f73) — HAI NỬA PHẢI CÂN NHAU.
   Anh Tiến 08/09/2026, nguyên văn: *"đồ họa lại nút này cho cân bằng 2 bên"* (kèm ảnh cận cảnh cụm
   «🔢 Công (8/4)» | «🕐 Giờ đến — về»: nửa trái nền đen bo tròn trông to hơn hẳn nửa phải).

   🔴 GỐC LỆCH — khoảng đệm chỉ đặt MỘT PHÍA cho mỗi nửa. Hai luật cũ sinh ra để chừa chỗ cho phần
     BO TRÒN ở hai đầu, nên nửa đầu chỉ được `padding-left` còn nửa cuối chỉ được `padding-right`.
     Cộng thêm chữ hai nửa dài ngắn khác nhau («Công (8/4)» ngắn hơn «Giờ đến — về»), kết quả là hai
     nửa vừa lệch bề rộng vừa lệch trọng tâm chữ — mắt đọc ra "nút bên trái to hơn".

   ⇒ Nay: đệm ĐỐI XỨNG `.8rem` cả hai bên cho MỌI nửa, cộng `flex: 1 1 0` để hai nửa co về CÙNG một
     bề rộng. `.btn-group` của Bootstrap vốn là `inline-flex` và đã khai `flex: 1 1 auto`; kéo
     `flex-basis` về 0 làm bề rộng ⛔ không còn phụ thuộc độ dài chữ nữa — cả hai cùng nhận bề rộng
     của nửa DÀI NHẤT. Nửa đang chọn từ đây chỉ khác MÀU NỀN chứ ⛔ không khác kích thước.

   ⛔ ĐỪNG ghim `width` / `min-width` bằng số: nhãn nút còn đổi theo chỉ đạo, số cứng là chỗ lệch của
     lần sau. ⛔ Cũng đừng hạ `flex-shrink` xuống 0 — hàng chứa cụm này còn phải co lại trên màn hình hẹp.
   ⚠ GIỮ NGUYÊN điều kiện `light` / `dark` như hai khối bo tròn bên dưới: giao diện `co-dien` cố ý
     đứng ngoài (luật đóng băng theme cổ điển), nới sang nó là đụng thứ đang đóng băng.
   ⚠ Sàn chạm 44px ⛔ KHÔNG bị đụng: nó nằm ở luật `.btn` của `app.css` trong `@media (pointer:
     coarse)`, còn luật đây chỉ NONG bề ngang chứ ⛔ không hạ chiều cao. */
:root[data-theme="light"] .nhom-kieu-xem > .btn[b-0hoohmgeru],
:root[data-theme="dark"] .nhom-kieu-xem > .btn[b-0hoohmgeru] {
    flex: 1 1 0;
    padding-left: .8rem;
    padding-right: .8rem;
    text-align: center;
}

:root[data-theme="light"] .nhom-kieu-xem > .btn:first-child[b-0hoohmgeru],
:root[data-theme="dark"] .nhom-kieu-xem > .btn:first-child[b-0hoohmgeru] {
    border-top-left-radius: 999px;
    border-bottom-left-radius: 999px;
}

:root[data-theme="light"] .nhom-kieu-xem > .btn:last-child[b-0hoohmgeru],
:root[data-theme="dark"] .nhom-kieu-xem > .btn:last-child[b-0hoohmgeru] {
    border-top-right-radius: 999px;
    border-bottom-right-radius: 999px;
}

/* ═════════ ĐỢT 670 — VÙNG CUỘN CỦA BẢNG CÔNG + ĐẦU BẢNG DÍNH ═════════

   Anh Tiến 14/08/2026: *"ở bảng chấm công nếu quá dài thì cho cuộn, đặc biệt là ô lọc tất cả"*.

   📏 Ca nặng nhất là lọc **Tất cả**: cả Tổng kho — hàng trăm dòng × 31 cột ngày. Trước đợt này khối chỉ
      cuộn NGANG (`overflow-x: auto` viết thẳng trong markup), nên bảng dài đẩy cả trang xuống và cuộn tới
      giữa bảng là **mất hàng tiêu đề ngày** — không còn biết ô đang nhìn là ngày mấy.

   ⚠ `max-height` theo **vh** chứ không phải điểm ảnh cứng: máy anh dùng và máy chiếu phòng họp cao khác
      nhau; ghim cứng thì màn hình lớn thừa chỗ mà vẫn phải cuộn. 68vh chừa đủ cho thanh nav + hàng lọc +
      phần chú giải bên dưới bảng.
   ⚠ `position: sticky` trên `th` (KHÔNG phải trên `thead`): Chrome/Edge không ghim được cả `<thead>` khi
      bảng dùng `border-collapse`, chỉ ô mới ghim được. Đây là lý do luật viết ở cấp ô.
   ⚠ Ô ghim phải có **nền đục**: `sticky` chỉ nhấc ô lên trên, nền trong suốt thì chữ của dòng đang cuộn
      chạy xuyên qua chữ tiêu đề. Dùng `var(--bs-table-bg, …)` để cả 4 giao diện tự đúng màu.
   ⓘ Cuộn NGANG giữ nguyên như cũ — nay hai chiều cùng cuộn trong một khối. */

.bang-cuon[b-0hoohmgeru] {
    overflow: auto;
    max-height: 68vh;
}

/* ⭐ ĐỢT 683 — VÁ "ĐÓNG BĂNG Ô NGÀY" (anh Tiến 15/08/2026: *"khi chấm công kéo xuống thì cũng phải đóng
   băng các ô ngày"*).

   📏 GỐC LỖI — CSS SCOPED CHỈ ĂN TRÊN THẺ DO CHÍNH MÀN HÌNH NÀY SINH RA. Luật cũ viết
      `.bang-cuon > table > thead > tr > th`, Blazor dịch thành `… > tr > th[b-CỦA-MÀN-HÌNH-NÀY]`. Nhưng
      31 ô ĐẦU CỘT NGÀY do COMPONENT `OCotNgayLich` sinh ra nên chúng mang mã scope CỦA COMPONENT ⇒ luật
      trượt sạch: chỉ hai ô "Họ tên"/"Chức vụ" (và 9 cột tổng cuối bảng) dính lại khi cuộn, còn cả dải ngày
      thì trôi đi. Anh chụp đúng cảnh đó: chỗ đáng lẽ là số ngày lại thấy giờ của các dòng bên dưới.
      ⚠ Đợt 670 dựng luật này TRƯỚC khi hiểu điều đó, nên tự nhận là "đầu bảng đã dính" — build sạch, mắt
        nhìn thoáng qua cũng thấy có dính, mà thật ra hỏng đúng phần quan trọng nhất.

   ⇒ Dùng `::deep`: Blazor gắn mã scope vào phần tử NGAY TRƯỚC nó (`tr`) và thả tự do phần sau, thành
     `… > tr[b-CỦA-MÀN-HÌNH-NÀY] th` ⇒ ăn cả ô của màn hình lẫn ô của component con.
   ⛔ Đừng "sửa cho gọn" bằng cách chép luật sticky sang OCotNgayLich.razor.css: component đó còn dùng ở
     mục 👤 Theo người (không có khối `.bang-cuon`), và tách đôi là hai nơi phải nhớ sửa cùng lúc. */
.bang-cuon > table > thead > tr[b-0hoohmgeru]  th {
    position: sticky;
    top: 0;
    z-index: 2;
    /* Nền đục để dòng cuộn qua không lộ chữ dưới gầm. Lùi về màu nền thẻ khi biến chưa có.
       (Hai giao diện cổ điển tự tô đầu bảng bằng background-image gradient ĐỤC nên vẫn kín.) */
    background-color: var(--bs-table-bg, var(--bs-body-bg, #fff));
    /* Viền trên/dưới vẽ lại bằng shadow: viền của `border-collapse` không cuộn theo ô sticky. */
    box-shadow: inset 0 1px 0 var(--bs-border-color, rgba(0, 0, 0, .175)),
                inset 0 -1px 0 var(--bs-border-color, rgba(0, 0, 0, .175));
}

/* ⭐ ĐỢT 900 — CỘT HỌ TÊN: BỎ DÁNG LINK GẠCH CHÂN.
   Anh Tiến 20/08/2026 (kèm ảnh chụp bảng công): "đồ họa lại chữ nhé em, chữ theo kiểu gạch chân link
   rất xấu" · "hoặc có nút xem chi tiết kế bên tên".

   Đợt 899 dùng lớp Bootstrap `btn-link`, và lớp đó đặt sẵn màu link + text-decoration: underline ⇒ cả
   cột tên hóa xanh và gạch chân, đọc như một danh sách liên kết chứ không như một bảng công.
   ⇒ Nay tên dùng lớp riêng: kế thừa MÀU CHỮ và FONT của ô bảng, chỉ hiện gạch chân khi rê chuột.

   🔑 CỐ Ý KHÔNG dùng `.btn` của Bootstrap nữa (kể cả biến thể trung tính): mọi biến thể đều mang đệm,
     viền và chiều cao dòng riêng, làm dòng bảng cao lên. Ở đây cần đúng một chữ nằm trong ô bảng.
   ⚠ Đây là CSS scoped của màn hình, hai thẻ nút do CHÍNH màn hình này sinh ra nên KHÔNG cần `::deep`
     (khác luật đóng băng đầu bảng ở trên — ô đầu cột ngày do component con sinh ra). */
.ten-nguoi[b-0hoohmgeru] {
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.ten-nguoi:hover[b-0hoohmgeru],
.ten-nguoi:focus-visible[b-0hoohmgeru] {
    color: var(--bs-link-hover-color, #0a3d91);
    text-decoration: underline;
}

/* Nút 📊 ĐỨNG TRƯỚC tên — chỗ NÓI RA rằng dòng này mở được ra chi tiết.
   ⭐ ĐỢT 901 — anh Tiến 20/08/2026: *"icon chi tiết chấm công mỗi người em đổi icon khác và đưa ra
     trước tên cho đều nhé"*. Đứng SAU tên thì mỗi dòng nút một chỗ (tên dài ngắn khác nhau) ⇒ cột
     nhìn so le; đứng trước thì cả cột thẳng một hàng.
   ⇒ Vì thế lề đổi từ `margin-left` sang `margin-right` (khoảng cách nay nằm giữa nút và tên), và
     bớt mờ một nấc: nút nay là ký tự ĐẦU DÒNG, mờ quá thì cả cột trông như lỗi hiển thị. */
.nut-xem-nguoi[b-0hoohmgeru] {
    padding: 0 .25rem;
    margin-right: .3rem;
    border: 0;
    border-radius: .25rem;
    background: none;
    line-height: 1;
    opacity: .6;
    cursor: pointer;
}

.nut-xem-nguoi:hover[b-0hoohmgeru],
.nut-xem-nguoi:focus-visible[b-0hoohmgeru] {
    opacity: 1;
    background-color: var(--bs-secondary-bg, rgba(0, 0, 0, .08));
}

/* ═══════════ ĐỢT 934 — CHẤM THAN ĐỎ 3D Ở GÓC Ô: ĐƠN PHÉP ĐÃ NỘP MÀ CHƯA DUYỆT ═══════════
   Anh Tiến 21/08/2026: *"khi một đơn xin phép đã nộp mà chưa duyệt thì cứ điền P là đã có phép
   nhưng góc ô có đồ hoạ thêm nút chấm than đỏ 3D có hiệu ứng để người chấm công biết"*.

   🔑 Vì sao phải NỔI KHỐI và ĐỘNG chứ không chỉ tô đỏ chữ: ô ngày chỉ rộng khoảng 22px và bảng có
      tới 31 cột — một chữ đỏ nhỏ chìm nghỉm giữa dải ô đã đủ màu (xanh lá đủ công, đỏ vắng, xanh
      lơ nghỉ phép). Khối nổi + nhịp thở là thứ mắt bắt được khi lướt cả bảng.

   ⚠ MÀU ĐÓNG ĐINH, KHÔNG qua token `--bs-*`: dấu này phải đỏ ở CẢ nền sáng lẫn nền tối (nó là
     cảnh báo, không phải màu trang trí theo chủ đề). Viền trắng mảnh giữ cho nó tách khỏi nền ô
     xanh lá / đỏ nhạt của chính ô ngày.
   ⛔ KHÔNG dùng `border-color` longhand ở đây (bài học đợt 838/840) — dấu này không phải ô nhập,
     nó tự khai trọn `border`, không giẫm lên luật nào của Bootstrap. */
/* 📐 ĐỢT 2273 (việc o1787) — ĐỆM NGANG CỦA Ô NGÀY: 4px MỖI BÊN → 1px, ĐỂ ĐỦ 31 NGÀY VÀO MỘT MÀN HÌNH.
   Anh Tiến 10/09/2026: *"màn hình hiển thị các bảng chấm công phải hiện đủ 31 ngày (hiện đủ cột không
   cần phải kéo)"*. Vế đầu cột nằm ở `OCotNgayLich.razor.css` (đo và lý do đầy đủ ghi ở đó).
   📏 Vì sao đánh vào ĐỆM chứ ⛔ không vào cỡ chữ: `.table-sm` cho mỗi ô .25rem = 4px mỗi bên, nhân 31
     cột thành **248px** — nhiều hơn cả 185px đang tràn ở khổ 1400×900, mà ⛔ không con chữ nào phải nhỏ
     đi. Viền của `table-bordered` vẫn tách hai ô cạnh nhau nên bỏ đệm ⛔ không làm chúng dính vào nhau.
   ⚠ Giữ `position: relative` — hai dấu góc ô (`.dau-phep-cho-duyet` · `.nut-cho-tinh-phep`) neo vào nó.
     Chúng nằm `absolute` nên ⛔ không chiếm bề ngang; vùng chạm 44px của máy cảm ứng cũng ⛔ không đổi. */
.o-ngay-cong[b-0hoohmgeru] {
    position: relative;
    padding-left: 1px;
    padding-right: 1px;
}

/* 📐 ĐỢT 2273 (việc o1787) — HAI CỘT ĐẦU (Họ tên · Chức vụ) CŨNG BỚT ĐỆM NGANG: 4px → 2px.
   Chúng ⛔ không phải cột ngày, nhưng ở lọc 🏢 CẢ KHO chúng ăn **236px + 156px** (tên dài nhất của
   133 người + chức vụ dài nhất), tức gần một phần ba bề ngang bảng. Bớt đệm lấy lại 8px —
   ⛔ không cắt một chữ nào, ⛔ không đụng cỡ chữ, và cột vẫn nở theo tên dài nhất như cũ.
   ⛔ ĐỪNG cắt chữ bằng `text-overflow: ellipsis` để lấy thêm chỗ: chữ bị cắt thì phải có `title` mới
     đọc lại được, mà luật ghi chú của anh Tiến (02/09/2026, quy tắc #23) chỉ cho `title` ở nút có chữ
     bị ẩn. Mất chữ để đổi lấy 30px là lỗ vốn.
   ⚠ Bám `.bang-cuon` chứ ⛔ không viết `thead th:first-child` trần: màn hình này còn nhiều bảng khác
     (nhật ký · rà soát), luật trần sẽ bóp cả chúng. */
.bang-cuon > table > thead > tr > th:first-child[b-0hoohmgeru],
.bang-cuon > table > thead > tr > th:nth-child(2)[b-0hoohmgeru],
.bang-cuon > table > thead > tr > th:nth-child(3)[b-0hoohmgeru],
.bang-cuon > table > tbody > tr > td:first-child[b-0hoohmgeru],
.bang-cuon > table > tbody > tr > td:nth-child(2)[b-0hoohmgeru],
.bang-cuon > table > tbody > tr > td:nth-child(3)[b-0hoohmgeru] {
    padding-left: 2px;
    padding-right: 2px;
}

/* 🔢 ĐỢT 2273 (việc o1804) — CỘT SỐ THỨ TỰ: hẹp nhất có thể mà vẫn đọc được ba chữ số.
   Anh Tiến 10/09/2026: *"cần có thêm cột stt"*. Bảng cả kho có hơn 2.000 người nên ô phải chứa
   được số ba chữ số của một trang (200 dòng/trang) mà ⛔ không cướp chỗ của 31 cột ngày.
   🔑 Bề rộng khai một chỗ bằng BIẾN vì luật ghim cột ở khổ điện thoại phải lấy đúng con số ấy làm
     `left` cho cột «Họ tên» — hai chỗ gõ hai số là cột tên đè lên cột STT khi cuộn ngang, mà
     ⛔ không câu báo nào. */
.bang-cuon[b-0hoohmgeru] {
    --rong-cot-stt: 30px;
}

.bang-cuon > table > thead > tr > th.o-stt[b-0hoohmgeru],
.bang-cuon > table > tbody > tr > td.o-stt[b-0hoohmgeru] {
    width: var(--rong-cot-stt);
    min-width: var(--rong-cot-stt);
    font-variant-numeric: tabular-nums;
}

.dau-phep-cho-duyet[b-0hoohmgeru] {
    position: absolute;
    top: 0;
    right: 0;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 8px;
    font-weight: 800;
    line-height: 1;
    color: #fff;
    /* Khối 3D: sáng ở góc trên-trái (nguồn sáng), sẫm dần xuống dưới-phải. */
    background: radial-gradient(circle at 32% 28%, #ff8a80 0%, #f4433f 45%, #b3160f 100%);
    border: 1px solid rgba(255, 255, 255, .85);
    /* Ba lớp bóng: viền tối ôm khối · bóng đổ xuống nền · vệt sáng nội tuyến ở mép trên. */
    box-shadow: 0 0 0 1px rgba(140, 0, 0, .35),
                0 1px 2px rgba(0, 0, 0, .45),
                inset 0 1px 1px rgba(255, 255, 255, .55);
    text-shadow: 0 1px 1px rgba(0, 0, 0, .5);
    /* Dấu chỉ để NHÌN — mọi cú bấm phải rơi xuống ô ngày bên dưới (ô mở hộp thoại chấm công).
       ⛔ Bỏ dòng này là đúng chỗ cần bấm nhất lại hóa thành chỗ bấm không ăn. */
    pointer-events: none;
    z-index: 2;
    animation: nhip-phep-cho-duyet-b-0hoohmgeru 1.9s ease-in-out infinite;
}

@keyframes nhip-phep-cho-duyet-b-0hoohmgeru {
    0%, 100% {
        transform: scale(1);
        box-shadow: 0 0 0 1px rgba(140, 0, 0, .35),
                    0 1px 2px rgba(0, 0, 0, .45),
                    inset 0 1px 1px rgba(255, 255, 255, .55);
    }
    50% {
        transform: scale(1.18);
        box-shadow: 0 0 0 1px rgba(140, 0, 0, .35),
                    0 1px 3px rgba(0, 0, 0, .5),
                    0 0 6px 2px rgba(244, 67, 63, .55),
                    inset 0 1px 1px rgba(255, 255, 255, .6);
    }
}

/* ⚠ BẮT BUỘC: người đặt máy giảm chuyển động thì DỪNG nhịp — vẫn giữ nguyên khối đỏ nổi, tức
   không mất một thông tin nào, chỉ mất phần động (cùng nếp với nút ← của đợt 922). */
@media (prefers-reduced-motion: reduce) {
    .dau-phep-cho-duyet[b-0hoohmgeru] {
        animation: none;
    }
}

/* ═══════════ ⭐ ĐỢT 1796 (việc o1051) — HÀNG CHỌN KỲ CỦA BẢNG CÔNG TRÊN TRANG CHỦ ═══════════

   Anh Tiến 02/09/2026: *"ở bảng chấm công tại trang chủ, cho phép chọn tháng/năm"*.

   👆 VÙNG CHẠM — anh dùng phần mềm ĐA PHẦN TRÊN ĐIỆN THOẠI, mà hai ô này là thứ duy nhất phải bấm
   trong hộp bảng công. Ngưỡng đọc từ BIẾN CHUNG `--cham-toi-thieu` khai ở `app.css` (44px, Apple HIG
   và WCAG 2.5.5) — sửa cỡ thì sửa BIẾN, đừng gõ số vào đây.

   🔴 VÌ SAO ĐẶT NGOÀI `@media (pointer: coarse)` — chỗ dễ đọc ngược nhất so với luật đợt 1518.
   Luật ấy bọc media vì nó sửa `.form-select` DÙNG CHUNG của cả app: nới cho máy tính là đổi bố cục
   của 433 tài khoản. Ở đây phạm vi là ĐÚNG HAI ô, chỉ dựng ở bản gọn của trang chủ, nằm trong một
   hộp thoại ⇒ nới luôn cho mọi thiết bị thì không màn hình nào khác nhúc nhích, mà bù lại đo ở khổ
   375x812 trên trình duyệt máy tính (nơi `pointer` vẫn là `fine`) cũng ra đúng 44px.
   ⛔ Đừng hạ con số này xuống dưới ngưỡng biến: ô bên cạnh là ô ĐỔI KỲ khác, bấm trượt là nạp lại cả
   bảng của một tháng người ta không định xem.

   ⚠ `.form-select` của Bootstrap khai `width: 100%`. Luật cách ly biên dịch ra `.ktc-o[b-...]` = (0,2,0)
   nên nó thắng (0,1,0) của Bootstrap; bỏ dòng `width` là mỗi ô chiếm trọn một dòng. */
.ky-trang-chu[b-0hoohmgeru] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}

.ktc-nhan[b-0hoohmgeru] {
    font-weight: 600;
    /* Sàn cỡ chữ của thiết bị chạm — cùng biến với vùng bấm, cùng một chỗ sửa. */
    font-size: max(.95rem, var(--cham-chu-toi-thieu, 12px));
}

/* 📏 ĐỢT 2202 (việc o1646) — HAI Ô NÀY ĐỨNG BẰNG NÚT «Công (8/4)» Ở MÀN HÌNH CHUỘT.

   Anh Tiến 08/09/2026, ảnh chụp chính dải này: *"nút tháng và năm quá to chỉ cần cỡ nút Công (8/4)
   là đủ"*. 📏 Đo trước (1400×900): ô chọn **112×44** và **121×44** đứng cạnh nút `.btn-sm` **31px**
   ⇒ cao hơn **13px**, dải «Kỳ xem» phình lên 61px.

   🔴 SÀN CHẠM 44px ⛔ KHÔNG BỎ — NÓ DỜI XUỐNG `@media (pointer: coarse)` Ở CUỐI TỆP. Đây là chỗ
   ĐẢO một nửa câu chú thích của đợt 1796 ngay trên: câu ấy nói *"nới cho mọi thiết bị thì ⛔ không
   màn hình nào nhúc nhích"* — đúng ở thời điểm ấy, nhưng đợt 1984 đã đặt hai nút `.btn-sm` vào
   CÙNG MỘT HÀNG với hai ô này, nên từ đó sàn 44px không còn vô hại: nó làm hàng cao thấp so le.
   ⛔ Đừng gỡ nốt vế cảm ứng — ô bên cạnh là ô ĐỔI KỲ khác, bấm trượt là nạp lại cả bảng của một
   tháng người ta ⛔ không định xem (lý do gốc của đợt 1796 vẫn nguyên giá trị).

   ⚠ Cỡ chữ lấy từ lớp `form-select-sm` gắn ở markup, ⛔ đừng gõ padding tay: nút bên cạnh cũng là
   `.btn-sm` nên hai bên cùng đọc một bộ số của Bootstrap, nâng khung là cùng đổi.
   ⚠ `.form-select` của Bootstrap khai `width: 100%`. Luật cách ly biên dịch ra `.ktc-o[b-...]` = (0,2,0)
   nên nó thắng (0,1,0) của Bootstrap; bỏ dòng `width` là mỗi ô chiếm trọn một dòng. */
.ktc-o[b-0hoohmgeru] {
    width: auto;
    min-width: 7rem;
}

/* 🏢 ĐỢT 2273 (việc o1804) — Ô CHỌN PHÒNG của hộp trang chủ (chỉ hiện với người xem được cả kho).
   Tên phòng dài hơn hẳn «Tháng 8» nên ô phải rộng hơn hai ô kỳ; trần 17rem giữ cho nó ⛔ không đẩy
   hai nút cách xem xuống dòng thứ hai ở khổ 1320px của hộp. Chữ dài hơn trần thì trình duyệt tự cắt
   trong ô chọn — ⛔ không cần `title`, đúng luật ghi chú anh ban 02/09/2026. */
.ktc-o-phong[b-0hoohmgeru] {
    max-width: 17rem;
}

/* 🔢 ĐỢT 1984 (việc o1121 mục 3) — HAI NÚT CÁCH XEM ĐỨNG CHUNG HÀNG VỚI «Kỳ xem», DẠT PHẢI.

   Anh Tiến 03/09/2026: *"đồ hoạ lại khúc này đang chừa không gian quá lớn quá lãng phí và không
   phù hợp"*. 📏 Đo trước: đầu hộp thoại cao 141px cho 2 ô chọn + 2 nút.

   🔑 Đẩy bằng MỘT thẻ đệm `flex-grow`, ⛔ không phải `justify-content: space-between`: dải này
     bọc dòng (`flex-wrap`) — khi hẹp, `space-between` xé nhãn «Kỳ xem» rời hẳn khỏi hai ô của nó
     ra hai mép màn hình, còn thẻ đệm thì tự xẹp về 0 và cả cụm ở lại liền nhau.
   ⛔ Đừng cho thẻ đệm `min-width`: có sàn là nó ép xuống dòng sớm hơn chính thứ nó phục vụ. */
.ktc-day[b-0hoohmgeru] {
    flex: 1 1 0;
    min-width: 0;
}

/* ═══════ 👆 ĐỢT 2066 (việc o1282) — HAI CHỖ BẤM CỦA CỘT HỌ TÊN, NỚI CHO NGÓN TAY ═══════

   📏 ĐO SỐNG 05/09/2026 (Chrome THẬT, khung 390×844, chế độ CẢM ỨNG, đăng nhập `tienvm`,
      `node cong-cu/do-song-khung-hep.mjs --trang cham-cong`): trang này còn **26 chỗ bấm nhỏ hơn
      ngón tay** — và sau lượt vá 34 chỗ của đợt 1995 (việc o1276) thì đây là **26 chỗ CUỐI CÙNG
      của toàn app**. Cả 26 nằm ở cột «Họ tên», lặp theo 13 dòng người:
        • `button.nut-xem-nguoi` (nút 📊 mở thống kê cả năm) — **24,5 × 12px**, hụt CẢ HAI chiều;
        • `button.ten-nguoi` (tên người bấm được) — **92,7…116,5 × 18px**, hụt chiều CAO.

   🔴 ⛔ KHÔNG đem khuôn «bảng hoá thẻ» của đợt 1989 vào đây. Khuôn ấy giải bài Ô NHẬP trong bảng
      hẹp; hai thứ này là **nút chữ trong ô bảng**, mà bảng công thì có **31 cột ngày** — hoá thẻ
      là mất luôn cái bảng, tức phá đúng thứ màn hình này sinh ra để làm.

   🔴 VÌ SAO BỌC TRONG `@media (pointer: coarse)` — trong khi khối đợt 1796 ngay bên trên cố ý đứng
      NGOÀI. Hai ca khác hẳn nhau, ⛔ đừng đọc chéo:
        · đợt 1796 nới ĐÚNG HAI ô chọn kỳ, chỉ dựng ở bản gọn của trang chủ, nằm trong một hộp
          thoại ⇒ nới cho mọi thiết bị thì ⛔ không màn hình nào nhúc nhích;
        · ở đây là hai nút của **MỌI DÒNG NGƯỜI trong bảng công thật**. Nới cho máy tính là mỗi
          dòng cao 44px thay vì ~24px ⇒ bảng dài gần gấp đôi, và cái nhìn tổng thể cả tháng — thứ
          duy nhất bảng này có mà màn hình khác ⛔ không có — mất sạch trên màn hình 433 người dùng.
      ⇒ Chốt ngược đã đo: `--chuot` (1369×900, ⛔ không cảm ứng) TRÙNG KHÍT trước/sau bản vá.

   ⚠ `min-width` chỉ đặt cho nút 📊: nó là nút một ký tự nên bị bóp cả bề ngang (24,5px).
      `.ten-nguoi` đã rộng 92…116px, ép sàn ngang cho nó là thừa và chỉ tổ nong cột.
   ⚠ `inline-flex` + `align-items: center` đi LIỀN với `min-height`, ⛔ đừng bỏ: `min-height` một
      mình chỉ nong cái HỘP, con chữ vẫn dính mép trên (đúng bài học `.btn` ở khối chạm của app.css,
      và bài `tknv-so-tong` của đợt 1995). `inline-flex` chứ ⛔ không `flex` — hai nút và thẻ
      `<small>(mã NV)</small>` phải ở LẠI trên cùng một dòng của ô `text-nowrap`.
   ⛔ ⛔ ĐỪNG gõ số `44px` vào đây: ngưỡng khai đúng MỘT chỗ là `--cham-toi-thieu` ở `app.css`.
   ⓘ Tràn ngang: cột họ tên rộng thêm ~20px, nhưng bảng nằm trong `.bang-cuon` (`overflow: auto`)
      nên phần dôi chỉ đẩy thanh cuộn CỦA KHỐI — đo lại sau khi vá: trang vẫn tràn **0px**.
   🚧 Hàng rào canh: `TKNB.Tests/VungChamPhanTrangVaThongKeTests` · đo đột biến bốn ô mới của
      `cong-cu/do-dot-bien-vung-cham-o1276.mjs`. */
@media (pointer: coarse) {

    .nut-xem-nguoi[b-0hoohmgeru] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    .ten-nguoi[b-0hoohmgeru] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }

    /* 📏 ĐỢT 2202 (việc o1646) — SÀN CHẠM CỦA HAI Ô CHỌN KỲ, DỜI TỪ LUẬT NỀN XUỐNG ĐÂY.
       Ở màn hình CHUỘT hai ô mang cỡ `.form-select-sm` cho bằng nút «Công (8/4)» đứng cùng hàng;
       ngón tay thì vẫn phải có 44px — bấm trượt là nạp lại cả bảng của một tháng ⛔ không định xem.
       ⛔ Đừng gõ số 44px vào đây: ngưỡng khai đúng MỘT chỗ là `--cham-toi-thieu` ở `app.css`.
       ⚠ Cỡ chữ nâng lại theo `--cham-chu-toi-thieu` vì `.form-select-sm` hạ chữ xuống .875rem. */
    .ktc-o[b-0hoohmgeru] {
        min-height: var(--cham-toi-thieu);
        font-size: max(.95rem, var(--cham-chu-toi-thieu, 12px));
    }
}

/* ═══ ĐỢT 2178 (việc o1621) — DẤU TÌNH TRẠNG DUYỆT trên DÒNG NGĂN CÁCH của từng phòng (bảng CẢ KHO) ═══
   Bảng gộp lẫn phòng ĐÃ duyệt với phòng CHƯA duyệt, mà tới trước đợt này dòng ngăn cách chỉ ghi
   "N người, tổng X công" nên người đọc không có cách nào biết con số nào đã khóa.

   🎨 CỐ Ý KHÔNG TÔ MÀU CHỮ, dùng viền theo `currentColor`. Dòng này là `tr.table-primary`, tức nền của
      nó do Bootstrap đặt và ĐỔI theo giao diện sáng/tối; đóng đinh một màu chữ xanh/nâu vào đây là tự
      nhận một phép cược về tương phản ở nền mình không kiểm soát. `currentColor` thì luôn đọc được
      vì chính Bootstrap đã chọn nó cho nền ấy (`--bs-table-color`).

   ⚠ CHỮ mới là kênh phân biệt ("ĐÃ DUYỆT" / "CHƯA DUYỆT"), độ đậm chỉ phụ họa — người mù màu và
      máy in đen trắng vẫn đọc đủ nghĩa. */
.dau-duyet-phong[b-0hoohmgeru] {
    display: inline-block;
    margin-left: .5rem;
    padding: 0 .4rem;
    border: 1px solid currentColor;
    border-radius: .25rem;
    font-size: .78rem;
    font-weight: normal;
    white-space: nowrap;
}

/* Kỳ chưa duyệt: nét đậm hơn một nấc để mắt dừng lại — đây là thứ người đọc cần thấy trước. */
.dau-duyet-phong.chua-chot[b-0hoohmgeru] {
    font-weight: 600;
}

/* ═══ ĐỢT 2178b (việc o1627) — ĐÓNG BĂNG CỘT «HỌ TÊN» Ở KHỔ HẸP (điện thoại) ═══
   Anh Tiến 07/09/2026, gửi kèm ảnh chụp màn hình điện thoại: "giao diện điện thoại phải đóng băng
   cột tên".

   📏 ĐO ĐƯỢC TRÊN CHÍNH ẢNH ANH GỬI: bảng cuộn ngang qua 31+ cột ngày thì cột đầu trôi hẳn khỏi màn
      hình, cột trái nhất còn nhìn thấy là "Chức vụ" — mà chức danh thì TRÙNG NHAU HÀNG LOẠT (ảnh có
      bốn dòng liền nhau cùng ghi "Công nhân cơ điện") ⇒ đọc xong một hàng giờ vào/ra mà KHÔNG biết
      của ai. Đây là hỏng LẶNG LẼ đúng nghĩa: không câu lỗi nào, số nào cũng đúng, chỉ mất mất tên
      chủ của dòng số.

   🔑 ĐỢT 670/683 mới ghim theo CHIỀU DỌC (thead), chiều NGANG thì chưa ai làm. Hai chiều dùng chung
      một khối cuộn `.bang-cuon` nên phải ăn khớp ở Ô GÓC — xem chú thích z-index bên dưới.

   📐 VÌ SAO `max-width` CHỨ KHÔNG PHẢI `pointer: coarse`: bệnh ở đây là BỀ NGANG (bảng rộng hơn màn
      hình), ⛔ không phải ngón tay. Cửa sổ máy tính thu hẹp cũng đúng bệnh ấy và cũng đáng được ghim;
      ngược lại một màn cảm ứng rộng thì ⛔ không cần. Ngưỡng 820px lấy đúng ngưỡng "khổ hẹp" mà khuôn
      `bang-the-kho-hep` đang dùng — ⛔ đừng đẻ thêm một ngưỡng thứ hai cho cùng một khái niệm.
      (Luật `pointer: coarse` ở khối trên là chuyện KHÁC: nó nói về vùng chạm của ngón tay.) */
@media (max-width: 820px) {

    /* 🔓 ĐỢT 2289 (việc o1826) — BẢNG CÔNG XIN RA KHỎI LUẬT «MỌI BẢNG TỰ THÀNH KHUNG CUỘN».
       Luật ấy ở `app.css` (đợt 400, `@media screen and (max-width: 820px)`) biến CHÍNH thẻ `<table>`
       thành khối cuộn ngang (`display: block; overflow-x: auto`). Với bảng này thì ra HAI khối cuộn
       lồng nhau: `.bang-cuon` (cuộn dọc, trần 68vh) bọc ngoài một `<table>` (cuộn ngang).

       🩸 ĐO SỐNG 10/09/2026 (Chrome, 375×812 cảm ứng, có chốt `d-flex`): cuộn dọc `.bang-cuon` xuống
          172px thì hàng tiêu đề ngày trôi đi đúng 172px — tức luật «đầu bảng dính» của đợt 670/683
          **CHẾT Ở KHỔ ĐIỆN THOẠI** mà ⛔ không câu báo nào. Gốc: `position: sticky` bám khối cuộn GẦN
          NHẤT, mà khối ấy nay là `<table>` — một khối cao bằng nội dung, ⛔ không bao giờ cuộn dọc.
          Lượt đo cột tên của đợt 2178b đậu vì nó chỉ đo chiều NGANG (bám `<table>` thì chiều ngang vẫn chạy).

       ⇒ Trả `<table>` về dáng bảng thường: `.bang-cuon` (vốn đã `overflow: auto`) cuộn CẢ HAI chiều,
         mọi ô ghim (đầu bảng · STT · Họ tên · tên phòng) cùng bám MỘT khối — đúng thiết kế đợt 670.
       🔑 Luật đợt 400 đã chừa sẵn lối `.bang-dinh-dau` cho ĐÚNG ca này, nhưng ⛔ KHÔNG dùng lối ấy ở đây:
         `:root[data-theme="dark"] .bang-dinh-dau thead th` ở (0,3,2) sẽ đè nền đầu bảng của màn hình
         này ở giao diện tối. Viết tại chỗ, (0,4,1) có dấu cách ly, thắng (0,3,1) của luật chung —
         ⛔ đừng rút về `.bang-cuon > table` (0,3,1): hòa điểm thì thắng thua theo THỨ TỰ nạp tệp.
       ⚠ `max-width: none` đi kèm: luật chung đặt `max-width: 100%`, để lại là trình duyệt được quyền
         bóp bảng về bề ngang khung.
       🩸 `width: max-content` LÀ RUỘT, ⛔ KHÔNG PHẢI DÒNG THỪA — lượt đo sống ĐẦU của chính đợt này
         ⛔ có nó: bảng giữ `width: 100%` của Bootstrap (= 349px) mà nội dung rộng hơn nhiều ⇒ thuật toán
         bảng hạ MỌI cột về bề rộng TỐI THIỂU. Hồi cả ô còn `nowrap` thì tối thiểu = một dòng nên ⛔ ai
         thấy; nay tên được gãy dòng (khối ③ ở dưới) nên cột tên co còn **46px**, tên rơi từng chữ một
         dòng, dòng người cao **53…78px**. `max-content` cho mỗi cột bề rộng TỰ NHIÊN của nó (tên thì
         trần 7rem) — bảng 28–31 ngày luôn rộng hơn khung 820px nên ⛔ bao giờ hụt mép phải. */
    .bang-cuon.card-body > table.table[b-0hoohmgeru] {
        display: table;
        width: max-content;
        max-width: none;
        overflow: visible;
    }

    /* Ô THÂN của cột tên.

       🩸 NỀN PHẢI ĐỤC, VÀ ĐÂY LÀ CHỖ ĐỢT NÀY TRẢ GIÁ — ⛔ ĐỪNG CHÉP `var(--bs-table-bg)` TỪ LUẬT
          ĐẦU BẢNG XUỐNG ĐÂY. Luật thead của đợt 683 tô bằng `--bs-table-bg` và nó ĐỤC thật, nhưng chỉ
          vì hàng ấy mang lớp `.table-light`. Ô THÂN thì khác hẳn: đo sống trên bản dựng thật (khung
          375×812, đọc `getComputedStyle`) ra `rgba(0, 0, 0, 0.027)` — tức GẦN NHƯ TRONG SUỐT ⇒ các ô
          ngày cuộn qua sẽ hiện lên NGAY DƯỚI GẦM tên, đúng cái mà câu "ô ghim phải có nền đục" của
          đợt 670 dặn phải tránh. Bản đầu của chính đợt 2178b đã viết như thế và phép đo bắt được.

       ⇒ ĐỔ NỀN BẰNG `background-image` (gradient một-màu), ⛔ KHÔNG chỉ bằng `background-color`.
         🔑 Vì sao — đo sống chỉ ra: `background-color` của ô thân **THUA một luật khác** và vẫn ra
           `rgba(0, 0, 0, 0.027)` dù luật này khai đè; còn lớp `background-image` thì ⛔ không ai
           tranh, và nó **vẽ ĐÈ LÊN** `background-color` ⇒ đây là kênh DUY NHẤT chắc chắn đục.
         🎨 Cả hai lớp cùng đọc `--bs-table-bg` (đo được `#fff` ở nền sáng — token này của Bootstrap
           5.3 là màu ĐỤC, ⛔ không phải `transparent`) nên hàng mang sắc riêng vẫn giữ đúng sắc, và
           giao diện tối tự ra màu tối. `--bs-body-bg` chỉ là nấc lui.
         ⛔ Bỏ dòng `background-image` là quay về đúng bản trong suốt vừa đo được ở trên — mà lỗi ấy
           ⛔ không có câu báo nào, chỉ hiện ra khi có người cuộn ngang thật.

       ⛔ LOẠI TRỪ `[colspan]` — chỗ dễ làm hỏng thứ hai: dòng NGĂN CÁCH PHÒNG của bảng CẢ KHO là MỘT
         ô phủ hết bề ngang bảng. Ghim một ô như thế vào mép trái là ghim NGUYÊN dải phòng lại rồi để
         phần còn lại của bảng trượt bên dưới nó — vỡ hẳn bố cục, mà build vẫn xanh.
         (Đo sống xác nhận ô ấy giữ `position: static`.) */
    /* 🔢 ĐỢT 2273 (việc o1804) — NAY GHIM HAI Ô, ⛔ KHÔNG PHẢI MỘT: cột đầu bảng đã thành «STT», nên
       luật `:first-child` cũ (viết cho ô HỌ TÊN) ⛔ không còn trỏ vào ô nó định giữ. Ghim mỗi STT thì
       cuộn ngang ở điện thoại chỉ còn thấy một dãy số ⛔ không biết của ai — hỏng đúng thứ đợt 2178b
       sinh ra để chữa, mà build vẫn xanh.
       ⚠ Ô «Họ tên» phải neo `left` bằng ĐÚNG bề ngang cột STT (biến `--rong-cot-stt` khai một chỗ ở
         trên): gõ số thứ hai vào đây là hai cột chồng nhau ở giữa chừng lượt cuộn. */
    .bang-cuon > table > tbody > tr > td:first-child:not([colspan])[b-0hoohmgeru],
    .bang-cuon > table > tbody > tr > td:nth-child(2):not([colspan])[b-0hoohmgeru] {
        position: sticky;
        /* Dưới hàng tiêu đề (z-index 2 ở luật đợt 683), trên các ô ngày đang cuộn qua. */
        z-index: 1;
        background-color: var(--bs-table-bg, var(--bs-body-bg, #fff));
        background-image: linear-gradient(var(--bs-table-bg, var(--bs-body-bg, #fff)),
                                          var(--bs-table-bg, var(--bs-body-bg, #fff)));
        /* Viền phải vẽ lại bằng shadow: viền của `border-collapse` KHÔNG cuộn theo ô sticky — thiếu
           nó thì cột ghim và phần đang trượt dính liền nhau, mắt không thấy ranh giới. */
        box-shadow: inset -1px 0 0 var(--bs-border-color, rgba(0, 0, 0, .175));
    }

    .bang-cuon > table > tbody > tr > td:first-child:not([colspan])[b-0hoohmgeru] {
        left: 0;
    }

    .bang-cuon > table > tbody > tr > td:nth-child(2):not([colspan])[b-0hoohmgeru] {
        left: var(--rong-cot-stt);
    }

    /* Ô GÓC (STT + Họ tên ở thead) — ghim CẢ HAI chiều nên phải nổi trên CẢ hàng tiêu đề LẪN cột ghim.
       ⚠ z-index phải LỚN HƠN 2 của luật đợt 683: bằng 2 thì thắng thua quyết theo thứ tự DOM và ô góc
         thua các ô ngày đứng sau nó ⇒ cuộn ngang là chữ "Họ tên" bị số ngày chồng lên.
       ⚠ Đi qua `::deep` cho khớp luật đợt 683 (Blazor gắn mã scope vào phần tử NGAY TRƯỚC `::deep`) —
         viết `> tr > th:first-child` là hai luật neo hai kiểu vào cùng một ô, sớm muộn lệch nhau. */
    .bang-cuon > table > thead > tr[b-0hoohmgeru]  th:first-child {
        left: 0;
        z-index: 3;
    }

    /* 🔢 ĐỢT 2273 (việc o1804) — ô góc THỨ HAI («Họ tên»), sinh ra cùng lượt thêm cột STT. */
    .bang-cuon > table > thead > tr[b-0hoohmgeru]  th:nth-child(2) {
        left: var(--rong-cot-stt);
        z-index: 3;
    }

    /* DÒNG NGĂN CÁCH PHÒNG của bảng CẢ KHO — ⭐ ĐỢT 2183 (việc o1629), NỬA CÒN LẠI CỦA ĐỢT 2178b.

       🩸 GHIM NỘI DUNG, ⛔ KHÔNG GHIM Ô — và đây là chỗ đợt này phải nói cho rõ, vì cái sai ở đây
          KHÔNG kêu lên tiếng nào:
          · Ghim chính cái `td[colspan]` thì VỠ BỐ CỤC — luật ô tên ngay bên trên đã cố ý loại trừ nó.
          · Mà ghim cái `td` ấy còn VÔ TÁC DỤNG nữa: ô phủ HẾT bề ngang bảng nên nó rộng hơn khung
            nhìn, tức nó chưa bao giờ bị cuộn khỏi khung để mà "dính" — `position: sticky` chỉ nhấc
            một phần tử khi phần tử ấy sắp trôi khỏi khung cuộn.
          ⇒ Kênh DUY NHẤT còn lại là ghim CÁI THẺ BỌC bên trong ô (`.ten-phong-ghim` — thẻ này do
            chính đợt 2183 thêm vào `.razor`; ⛔ gỡ thẻ là luật này chết câm).

       🔑 `display: inline-block` là BẮT BUỘC, ⛔ không phải cho đẹp: thẻ bọc là `<span>`, mà hộp
          INLINE thì `position: sticky` ⛔ không nhấc được (nó cần một hộp có bề ngang/chiều cao thật).

       🎨 ⛔ KHÔNG cần nền đục ở đây — khác hẳn ô tên ngay bên trên. Dòng này chỉ có ĐÚNG MỘT ô phủ
          hết bề ngang, nên thứ trượt dưới gầm thẻ bọc là NỀN CỦA CHÍNH Ô ẤY (một sắc `table-primary`
          đều tăm tắp), ⛔ không phải chữ của ô khác. Đổ thêm một lớp nền vào đây chỉ tổ đè mất sắc
          của hàng khi giao diện đổi màu.

       🩸 `width: 0` LÀ RUỘT CỦA LUẬT NÀY, ⛔ KHÔNG PHẢI DÒNG THỪA — và nó chỉ lòi ra khi ĐO SỐNG:
          bản đầu của đợt 2183 ⛔ không có nó, đo ra ghim ĐÚNG ở giữa đường (`scrollLeft` 400 · 900 →
          thẻ nằm đúng mép trái) nhưng **HỎNG Ở ĐÚNG CHỖ CẦN NHẤT**: cuộn hết sang phải (`scrollLeft`
          1371/1371) thì thẻ tụt về **x = −160** trong khi mép khung cuộn ở **x = 13** ⇒ chữ
          «🏢 Ban giám đốc» nằm NGOÀI màn hình, chỉ còn đuôi «— 5 người · tổng…» — tức vẫn đúng cái
          bệnh anh Tiến báo, chỉ dời sang mấy ngày CUỐI THÁNG.
          🔑 Gốc bệnh: `sticky` ⛔ không được phép trượt ra khỏi Ô CHỨA nó. Cụm chữ rộng **517px** mà
            khoảng còn lại của ô ở cuối đường cuộn chỉ còn ~349px ⇒ trình duyệt kéo ngược thẻ lại cho
            vừa ô. ⇒ Cho thẻ bề ngang **0**: ràng buộc "phải nằm trong ô" ⛔ không bao giờ cắn nữa,
            còn chữ thì TRÀN sang phải và vẫn vẽ đủ (`overflow` mặc định của `inline-block` là
            `visible`; khung cuộn cắt phần thừa ở mép phải — đúng thứ ta muốn).
          ⚠ `white-space: nowrap` đi KÈM, ⛔ đừng bỏ: bề ngang 0 mà cho xuống dòng thì mỗi chữ rơi
            một dòng, dải phòng cao vống lên.
          📏 Đo lại sau khi thêm: cả 4 mốc `scrollLeft` (0 · 400 · 1200 · 1371) đều cho thẻ đứng
            **x = 13** = đúng mép trái khung cuộn, chiều cao dòng vẫn **30px** như cũ.

       📐 Nằm TRONG khối `max-width: 820px` là có chủ ý: khổ rộng thì bảng ⛔ không tràn ngang, ghim
          vào là thừa. Đo sống ở khổ rộng phải thấy thẻ này giữ `position: static`. */
    .bang-cuon > table > tbody > tr > td[colspan] > .ten-phong-ghim[b-0hoohmgeru] {
        position: sticky;
        left: 0;
        display: inline-block;
        width: 0;
        white-space: nowrap;
    }
}

/* ═══════════ 📐 ĐỢT 2202 (việc o1646) — MỌI DÒNG CỦA BẢNG GỌN CAO BẰNG NHAU ═══════════

   Anh Tiến 08/09/2026: *"độ cao dòng làm đều không được có dòng cao thấp không đẹp"*.

   📏 ĐO TRƯỚC (1400×900, hộp «Bảng chấm công của tôi» ở trang chủ): 13 dòng dữ liệu chia HAI bậc —
   **12 dòng 41,6px** và **1 dòng 27,5px**. Bậc cao là dòng có huy hiệu/dấu trong ô ngày, bậc thấp
   là dòng ⛔ không có gì ngoài chữ ⇒ mắt đọc thành một hàng bị "hụt" giữa bảng.

   🔑 SÀN ĐẶT TRÊN `td`, ⛔ KHÔNG PHẢI TRÊN `tr`: trình duyệt tính chiều cao hàng theo Ô CAO NHẤT,
   nên `height` gắn lên `tr` là một gợi ý mà ô nào cũng có quyền phá; gắn lên chính các ô thì mọi ô
   cùng có một sàn ⇒ hàng ⛔ không thể thấp hơn sàn ấy. `vertical-align: middle` đi KÈM — có sàn mà
   ⛔ không canh giữa thì chữ dính mép trên, hàng trông càng lệch hơn lúc chưa vá.

   ⛔ ĐỪNG đổi `height` thành `min-height`: trong bảng HTML, `min-height` trên ô ⛔ KHÔNG có tác
   dụng (ô là `display: table-cell`), còn `height` thì trình duyệt hiểu đúng là SÀN — dòng có nội
   dung cao hơn vẫn nở ra bình thường.
   🔴 ĐỢT 2235 (việc o1708) — ANH TIẾN ĐẢO CÂU «CHỈ ÁP CHO BẢN GỌN» CỦA ĐỢT 2202. Nguyên văn
   09/09/2026: *"chiều cao các dòng người chấm công của các phòng khác chưa được sửa tự động đồng
   nhất"*. ⇒ Sàn nay áp cho MỌI bảng công của component (cả `/cham-cong` lẫn hộp trang chủ), nên
   bộ chọn bám thẻ bọc `.bang-cuon` chứ ⛔ không bám lớp mốc `bang-cong-gon` nữa — lớp ấy đã gỡ
   khỏi markup vì ⛔ không còn luật nào đọc nó.
   📏 Đo TRƯỚC ở đúng phòng anh chỉ (Khai thác dịch vụ, 14 người, 1400×900): BA bậc chiều cao
   **27 · 27,5 · 41,6px**, lệch **14,6px**. Dòng thấp là dòng ⛔ không có ô hai dòng giờ nào (toàn
   «V»/«N»), nên sàn phải bằng đúng bậc CAO NHẤT đang có (2,6rem = 41,6px) thì hàng mới đều.
   ⚠ ĐÁNH ĐỔI ĐÃ BIẾT, ⛔ đừng ngạc nhiên: bảng CẢ KHO ~2.000 dòng sẽ DÀI THÊM đúng phần các dòng
   thấp vừa được nâng. Đó là cái giá anh đã chọn khi ra câu trên — ⛔ đừng tự ý hạ sàn xuống cho
   bảng ngắn lại, vì hạ là dòng cao thấp trở lại.
   ⛔ Dòng tiêu đề phòng (`td[colspan]`) đứng NGOÀI: nó cố ý mỏng hơn để làm vạch ngăn, kéo bằng
   dòng người là mất luôn chức năng ngăn cách. */
.bang-cuon > table > tbody > tr > td:not([colspan])[b-0hoohmgeru] {
    height: 2.6rem;
    vertical-align: middle;
}

/* ═══════ 📜 ĐỢT 2202b (việc o1660) — HỘP TRANG CHỦ CHỈ CÒN **MỘT** THANH CUỘN DỌC ═══════

   Anh Tiến 08/09/2026, ảnh chụp hộp 📆 Bảng chấm công: *"bỏ thanh cuộn bên ngoài vì dư"*.

   📏 Trước: thân hộp (`.modal-body`) cuộn một thanh, khối `.bang-cuon` (trần 68vh) cuộn thêm một
   thanh nữa ⇒ hai thanh nằm sát nhau ở mép phải. Thanh NGOÀI gần như vô dụng: thứ duy nhất nó trượt
   qua là dải «Kỳ xem» cao 48px.

   🔑 CHUỖI FLEX PHẢI LIỀN MẠCH TỪ THÂN HỘP XUỐNG BẢNG, đứt một mắt là luật này ⛔ không chạy mà cũng
   ⛔ không báo: `.modal-body` (khai `d-flex flex-column` qua `LopThanHop` ở nơi gọi) → `.the-cong-gon`
   → `.bang-cuon`. Mỗi mắt cần `min-height: 0`, vì mặc định của flex item là `min-height: auto` =
   *"⛔ không được co nhỏ hơn nội dung"* ⇒ thẻ nở theo cả bảng và thân hộp lại phải cuộn — đúng thứ vừa
   đi bỏ.
   ⛔ `max-height: none` chỉ mở ở khổ **≥ 992px**: dưới ngưỡng ấy `hop-cao-co-dinh` trả hộp về chiều
   cao AUTO (luật `app.css`), lúc đó bỏ trần là bảng dài vô tận trong một thân hộp cũng vô tận —
   người dùng điện thoại mất luôn cái nhìn tổng thể. Mốc 992px lấy đúng của `hop-cao-co-dinh`, ⛔ đừng
   khai một mốc thứ hai. */
@media (min-width: 992px) {
    .the-cong-gon[b-0hoohmgeru] {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
        margin-bottom: 0;
    }

    .the-cong-gon > .bang-cuon[b-0hoohmgeru] {
        flex: 1 1 auto;
        min-height: 0;
        max-height: none;
    }
}

/* ═══ ĐỢT 2219 (việc o1675) — NÚT CẢNH BÁO NHỎ «Tính phép» TRÊN Ô NGÀY ═══
   Anh Tiến 08/09/2026: *"đơn xin nghỉ phép sẽ không tự động tính vào bảng chấm công mà chỉ có nút
   cảnh báo nhỏ, trưởng phòng nhấn vào nút tính phép thì mới tính phép"*.

   📍 GÓC DƯỚI-PHẢI, ⛔ không phải góc trên-phải: góc trên-phải đã có chấm đỏ `.dau-phep-cho-duyet`
     (đơn CHƯA duyệt). Hai dấu chồng lên nhau là ô ngày nói hai chuyện qua một chỗ.
   🔑 CHẤM MÀU VẼ BẰNG `::before`, ⛔ KHÔNG vẽ bằng chính hộp của nút. Đây là mấu chốt để hai luật cùng
     sống: anh Tiến bảo *"nút NHỎ"*, còn luật vùng chạm của dự án đòi 44px trên cảm ứng. Tách ra thì
     hộp của nút phình được tới sàn 44px mà **⛔ không một pixel nào hiện thêm** — chấm vẫn 15px, vẫn
     nằm đúng góc dưới-phải (nó neo theo `bottom/right` của chính hộp nút).
   ⚠ `.dau-phep-cho-duyet` bên trên dùng `position: absolute` neo vào `.o-ngay-cong`; nút này neo vào
     ĐÚNG mốc ấy, ⛔ đừng bỏ `position: relative` của `.o-ngay-cong`. */
.nut-cho-tinh-phep[b-0hoohmgeru] {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 15px;
    height: 15px;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    z-index: 2;
}

/* Chấm màu — thứ DUY NHẤT nhìn thấy được của nút.
   ⛔ Là một `<span>` THẬT trong markup chứ ⛔ không phải `content:` của `::before`: ký hiệu phải đến
   TỪ HẰNG SỐ `LuatPhepHuyChamCong.KyHieuNutChoTinhPhep` (bài học đợt 525 — gõ ký tự tay vào CSS thì
   đổi hằng số một đằng, màn hình giữ ký tự cũ một nẻo, mà ⛔ không ai kêu). */
.cham-tinh-phep[b-0hoohmgeru] {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 15px;
    height: 15px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 9px;
    line-height: 1;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, .85);
    color: #4a2c00;
    background: radial-gradient(circle at 32% 28%, #ffe08a 0%, #ffc107 45%, #d39e00 100%);
    box-shadow: 0 0 0 1px rgba(120, 80, 0, .35),
                0 1px 2px rgba(0, 0, 0, .35),
                inset 0 1px 1px rgba(255, 255, 255, .6);
}

.nut-cho-tinh-phep:hover:not(:disabled) .cham-tinh-phep[b-0hoohmgeru],
.nut-cho-tinh-phep:focus-visible .cham-tinh-phep[b-0hoohmgeru] {
    background: radial-gradient(circle at 32% 28%, #fff0bf 0%, #ffcd39 45%, #b98900 100%);
    outline: 2px solid #b98900;
    outline-offset: 1px;
}

.nut-cho-tinh-phep:disabled[b-0hoohmgeru] {
    opacity: .55;
    cursor: default;
}

/* 👆 SÀN CHẠM 44px — chỉ trên thiết bị CẢM ỨNG, đúng khuôn đợt 2042.
   ⛔ Đừng nới cho máy tính: hộp nút nở ra 44px sẽ trùm lên các ô ngày bên cạnh, mà chuột thì đã bấm
     trúng chấm 15px không khó. Trên cảm ứng thì ngược lại — 15px là hụt hẳn ngưỡng.
   🔑 Chỉ phình HỘP; chấm `.cham-tinh-phep` vẫn 15px và vẫn neo góc dưới-phải nên ⛔ không một pixel
     nào đổi. */
@media (pointer: coarse) {
    .nut-cho-tinh-phep[b-0hoohmgeru] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
    }
}

/* 🕐 CỠ CHỮ GIỜ ĐẾN / GIỜ VỀ TRONG Ô NGÀY — luật NỀN .68rem, SÀN 12px khi màn hình dùng NGÓN TAY
   (đợt 2226, việc o1642; nối tiếp phép đo của đợt 2198 việc o1640).
   🔑 Trước đợt này hai dòng giờ ấy mang `style` gõ thẳng trong thẻ, tức độ đặc hiệu (1,0,0) —
     ⛔ KHÔNG luật CSS nào của tệp này với tới, nên chữ đứng nguyên 10,9px trên điện thoại (ngưỡng
     của luật đợt 1518 là 12px). Chuyển sang một LỚP thì cả hai bề mặt cùng đọc một chỗ.
   🔴 SÀN ⛔ KHÔNG được đưa ra luật nền: luật cỡ chữ tối thiểu CHỈ áp cho `pointer: coarse` (đợt
     1518) — nới cho cả máy tính là đổi bố cục bảng công của 429 tài khoản đang ngồi chuột, mà
     chốt ngược khổ 1400x900 đòi TRÙNG KHÍT từng con số.
   📏 Đo sẵn trên bản chạy thật trước khi sửa: nâng chữ hai dòng này lên 12px thì ô ngày rộng
     36,8 → 39,7px, bảng vẫn 323px, thân hộp ⛔ KHÔNG cuộn ngang, trang vẫn 375px. */
/* 📐 ĐỢT 2273 (việc o1787) — SIẾT KHOẢNG CÁCH CHỮ, ⛔ KHÔNG HẠ CỠ CHỮ.
   Hai dòng giờ này chính là thứ QUYẾT ĐỊNH bề ngang cột ngày: đo min-content 03 dạng ô thì «13:18»
   ăn 28px, còn đầu cột (số ngày + thứ) chỉ cần 19px. ⇒ Muốn 31 ngày vào vừa màn hình thì phải chạm
   vào đây, mà cỡ chữ thì ⛔ ĐỪNG động — nó đã là sàn của luật đợt 2226 và của ngưỡng đọc.
   📏 Đo sống 10/09/2026 (khổ 1400×900, tháng 8 = 31 ngày, 🏢 CẢ KHO = 133 dòng): riêng nét siết này
     kéo bảng **1.349 → 1.298px**, ô ngày 31 → 30px. Chữ vẫn .68rem, ⛔ không một con số nào nhỏ đi. */
.ccm-gio[b-0hoohmgeru] {
    font-size: .68rem;
    letter-spacing: -.03em;
}

@media (pointer: coarse) {
    .ccm-gio[b-0hoohmgeru] {
        font-size: max(.68rem, var(--cham-chu-toi-thieu, 12px));
    }
}


/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   📱 ĐỢT 2285 (việc o1811) — THANH CÔNG CỤ VÀ DẢI THẺ SỐ Ở KHỔ ĐIỆN THOẠI: MỖI DÒNG LẤP ĐẦY,
   ⛔ KHÔNG CÒN MẢNH MỒ CÔI Ở MÉP TRÁI

   Anh Tiến 10/09/2026, ảnh chụp chính màn hình này trên điện thoại: *"nút thì cái to cái nhỏ…
   sắp xếp lộn xộn không đều và đẹp, nội dung cũng thế"*.

   📏 ĐO SỐNG TRƯỚC KHI SỬA (Chrome thật, 390×844, chế độ cảm ứng, có chốt `d-flex` ⇒ CSS đã nạp
     thật; lòng thẻ rộng 332px):
     · dòng ①  ô chọn phòng **320px** ⇒ hụt 12px so với mép phải của mọi dòng khác;
     · dòng ②  Tháng **110** + Năm **100** ⇒ bỏ trống **114px** bên phải;
     · dòng ③  «✔ Duyệt chấm công tháng» **189** + «⬇ Xuất Excel» **108**;
     · dòng ④  «🖨 In báo cáo» **107px ĐỨNG MỘT MÌNH SÁT MÉP TRÁI** — đúng mảnh mồ côi mà luật ①
               đợt 2176 gọi tên, và là thứ làm cả cụm đọc ra *"cái to cái nhỏ"*;
     · dải thẻ số: bề ngang **184 · 140 · 111 · 142 · 129px**, hàng thì hai thẻ hàng thì một, mép
               phải mỗi hàng dừng một chỗ khác nhau.

   🔑 CHỮA BẰNG `flex: 1 1 auto`, ⛔ KHÔNG PHẢI `flex: 1 1 0` — chỗ dễ làm vỡ nhất. `flex-basis: 0`
     làm cỡ khởi điểm của mọi mảnh về 0 ⇒ **cả sáu điều khiển dồn lên MỘT dòng** rồi bé tí; còn
     `auto` giữ cỡ nội dung để phép xuống dòng vẫn chia hàng như cũ, chỉ **nong nốt phần thừa của
     hàng**. Đây là khuôn luật ③ đợt 2176.
   ⚠ `flex-basis: auto` đọc chính thuộc tính `width` ⇒ hai ô Tháng/Năm vẫn lấy 110/100px gõ trong
     thẻ làm cỡ khởi điểm rồi mới nở — ⛔ đừng gỡ hai số ấy, gỡ là `.form-select { width: 100% }`
     của Bootstrap nhảy vào và mỗi ô chiếm trọn một dòng.
   🔒 CHỈ trong khổ ≤576px (mốc điện thoại chung của dự án — cùng mốc
     `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx` mà luật dải nút nhóm ở `app.css` đang dùng).
     Máy tính còn thừa chỗ; nong nút ở đó là bóp hẹp ô gõ ⇒ chốt ngược 1400×900 phải TRÙNG KHÍT.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */

/* Luật NỀN — trả lại đúng thứ hai lớp tiện dụng Bootstrap vừa gỡ khỏi markup, để màn hình máy tính
   ⛔ không đổi một pixel (khuôn luật ⑤ đợt 2181). */
.ccth-o-phong[b-0hoohmgeru] {
    max-width: 320px;
}

.ccth-dai-thong-ke[b-0hoohmgeru] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
}

@media (max-width: 576px) {

    /* ① THANH CÔNG CỤ: mọi ô chọn và mọi nút cùng nong nốt phần thừa của hàng mình đang đứng. */
    .card-header > .btn[b-0hoohmgeru],
    .card-header > .form-select[b-0hoohmgeru] {
        flex: 1 1 auto;
    }

    /* Ô chọn phòng nay được dùng hết bề ngang — trần 320px là con số của màn hình rộng. */
    .ccth-o-phong[b-0hoohmgeru] {
        max-width: none;
    }

    /* ② DẢI THẺ SỐ THÀNH **LƯỚI HAI CỘT**, ⛔ KHÔNG PHẢI HÀNG FLEX TỰ XUỐNG DÒNG.

       🔑 Vì sao lưới mới đủ: `flex-wrap` + `flex: 1 1 50%` chỉ cho các thẻ **CÙNG MỘT HÀNG** bằng
         nhau; hàng nào có chữ dài phải xuống dòng thì hàng ấy cao lên, hàng khác vẫn thấp ⇒ vẫn còn
         *"cái to cái nhỏ"*, chỉ là theo chiều dọc. `grid-auto-rows: 1fr` kéo **MỌI hàng** bằng hàng
         cao nhất ⇒ sáu ô đều tăm tắp, đúng thứ anh xin.
       ⚠ `1fr` chỉ làm được việc ấy khi thẻ bọc **cao tự do** (⛔ không khai `height`) — đặt chiều cao
         cho dải là các hàng quay về chia đều phần cao ấy chứ ⛔ không còn ăn theo nội dung. */
    .ccth-dai-thong-ke[b-0hoohmgeru] {
        display: grid;
        grid-template-columns: 1fr 1fr;
        grid-auto-rows: 1fr;
        align-items: stretch;
    }

    /* SỐ Ô LẺ ⇒ Ô CUỐI TRẢI TRỌN HÀNG, ⛔ đừng để nó đứng một mình nửa hàng bên trái. Số thẻ đổi theo
       dữ liệu từng ngày (thẻ «Ngày nghỉ không lương», «Dữ liệu mới đến», «vắng chưa lý do» chỉ hiện
       khi có) — đo 10/09 lượt đầu ra 6 ô, lượt sau cùng buổi ra **5 ô** và ô «CHƯA DUYỆT» rơi xuống
       góc trái một mình: đúng mảnh mồ côi luật ① đợt 2176 gọi tên, lần này là thẻ chứ ⛔ không phải nút.
       🔑 `::deep` là BẮT BUỘC: thẻ số là thẻ gốc của component `OSoThongKe` nên mang dấu cách ly của
         component ấy, ⛔ không mang dấu của tệp này — viết trần là bộ chọn ⛔ bao giờ khớp, build xanh.
       ⓘ Bộ chọn hậu duệ nên còn trúng cả phần tử con nằm cuối ở tầng sâu hơn; `grid-column` đặt lên
         thứ ⛔ phải ô lưới thì trình duyệt bỏ qua ⇒ vô hại. */
    .ccth-dai-thong-ke[b-0hoohmgeru]  :last-child:nth-child(odd) {
        grid-column: 1 / -1;
    }

    /* 🔴 TRẢ LẠI DÁNG "VIÊN THUỐC LIỀN KHỐI" cho cụm nút cách xem (🔢 · 🕐). Luật chung
       `.btn-group { gap: .35rem }` của đợt 2197 (cuối `app.css`) sinh ra để cứu những dải nút TRÀN
       MÉP — nhưng cụm này chỉ có hai nửa và chưa bao giờ tràn, nên ở khổ hẹp nó bị tách đôi: đo được
       khe **5px** giữa hai nút, cộng phép bo lại bốn góc ⇒ nửa đen `btn-dark` đứng rời ra trông như
       một vệt hỏng. ⛔ Đừng đi sửa luật chung ấy — nó đang giữ đúng việc của nó cho 80 dải nút khác.
       ⛔ Và ⛔ ĐỪNG ghim thêm `flex` cho cụm này: nó còn được dựng ở hàng «Kỳ xem» của hộp bảng công
         TRANG CHỦ (đợt 1984 khai một lần dựng hai chỗ) — hàng ấy vẫn là flex, ghim nửa hàng cho nó ở
         đó là bẻ một bố cục ⛔ không ai nhờ. Trong lưới, ô tự lấp đầy cột của mình. */
    .nhom-kieu-xem[b-0hoohmgeru] {
        gap: 0;
    }

    /* ③ 📱 ĐỢT 2289 (việc o1826) — CỘT «HỌ TÊN» GỌN LẠI: TÊN Ở TRÊN, MÃ NV Ở DƯỚI, BỎ NÚT 📊.
       Anh Tiến 10/09/2026: *"chấm công cột tên chiếm hết màn hình"*.
       📏 Đo trước (375×812 cảm ứng): khối ghim STT 30 + Họ tên **218px** = **248/349px (71%)** khung
         ⇒ chỉ còn ~101px, lọt **3 ô ngày**. Cột tên = nút 📊 **44px** (sàn chạm) + 4,8 + tên 93 + mã 41.

       🔑 Ba nhịp, mỗi nhịp một phần bề ngang:
         · Nút 📊 ẨN — nó gọi CÙNG `ChonNguoi` với nút tên (đợt 900), mà nút tên đã cao 44px nên chính
           nó là chỗ chạm. Ở máy tính nút 📊 vẫn đứng trước tên như anh chốt ở đợt 901.
         · Mã NV xuống dòng dưới tên (nó nằm TRONG nút tên — xem chú thích ở `.razor`) ⇒ dòng bảng
           ⛔ không cao thêm: hai dòng chữ lọt trong 44px của nút.
         · Tên dài quá 7rem thì gãy ở chỗ trống giữa hai chữ — ⛔ KHÔNG cắt ba chấm (chữ bị cắt đòi
           `title`, mà luật ghi chú 02/09/2026 ⛔ không cho). `line-height` 1,15 để tên hai dòng + mã
           vẫn ≤ 44px ⇒ mọi dòng người cao BẰNG NHAU (luật đợt 2202/2235 anh đòi).
       ⚠ `white-space: normal` khai trên CHÍNH khối: ô bọc mang `text-nowrap` của Bootstrap (có
         `!important`) nhưng thuộc tính ấy chỉ THỪA KẾ xuống con, khai ở con là thắng — ⛔ không cần
         `!important`, ⛔ không cần sửa markup ô.
       ⚠ Đôn độ đặc hiệu bằng `.bang-cuon` phía trước: luật `.nut-xem-nguoi` / `.ten-nguoi` trong khối
         `pointer: coarse` ở trên cùng (0,2,0) sau khi cách ly — hòa điểm là phó mặc cho THỨ TỰ trong tệp. */
    .bang-cuon .nut-xem-nguoi[b-0hoohmgeru] {
        display: none;
    }

    .bang-cuon .khoi-ten[b-0hoohmgeru] {
        display: inline-flex;
        flex-direction: column;
        align-items: flex-start;
        justify-content: center;
        max-width: 7rem;
        white-space: normal;
        line-height: 1.15;
        text-align: left;
        vertical-align: middle;
    }

    /* Cột «Chức vụ» ⛔ không ghim nhưng là cột ĐẦU TIÊN người ta thấy khi mở bảng — «Phó trưởng phòng»
       một dòng ăn 110px, tức hơn ba ô ngày. Cho gãy hai dòng như cột tên (cùng lý do, cùng trần dòng). */
    .bang-cuon > table > tbody > tr > td:nth-child(3):not([colspan]) > small[b-0hoohmgeru] {
        display: inline-block;
        max-width: 5.5rem;
        white-space: normal;
        line-height: 1.15;
    }
}

/* Luật NỀN của `.khoi-ten` — giữ đúng dáng máy tính «Tên (mã)» một dòng như trước đợt 2289:
   · `inline-block` cho mã: nét gạch chân lúc rê chuột lên tên (đợt 900) ⛔ lan xuống hộp inline-block,
     nên mã vẫn ⛔ gạch chân như hồi nó còn đứng ngoài nút.
   · `column-gap`: ở thiết bị cảm ứng nút tên là `inline-flex` (khối chạm đợt 2066), khoảng trắng cuối
     phần chữ tên bị trình duyệt bỏ ⇒ tên dính liền mã. `column-gap` chỉ chạy khi hộp là flex ngang,
     chỗ khác ⛔ tác dụng — ⛔ đổi thành `margin`, `margin` cộng thêm vào dấu cách sẵn có ở máy tính. */
.khoi-ten[b-0hoohmgeru] {
    column-gap: .25em;
}

.khoi-ten > small[b-0hoohmgeru] {
    display: inline-block;
}

/* 🔴 HAI LUẬT DƯỚI ĐÂY CỐ Ý ĐỨNG NGOÀI MỌI `@media`, và đây là chỗ dễ đọc ngược nhất của cả khối.
   Chúng chép ĐÚNG thứ Bootstrap vẫn làm cho một `.btn-group` bình thường (góc TRONG vuông, hai nút
   chồng viền bằng lề âm) ⇒ ở màn hình máy tính chúng ⛔ không đổi một pixel nào. Việc thật của chúng
   là **thắng lại** luật `border-radius` / `margin-left` mà đợt 2197 khai ở (0,4,0) trong khối
   `max-width: 576px` của `app.css`.
   🔑 Vì sao phải đôn độ đặc hiệu chứ ⛔ không viết `.nhom-kieu-xem > .btn` trần: CSS cách ly chỉ cộng
     **MỘT lớp** vào phần tử cuối ⇒ `.nhom-kieu-xem > .btn[b-…]` ra (0,3,0), **THUA** (0,4,0) — đúng
     bản vá nửa-chạy mà luật ⑪ đợt 2197 đã đo và bác bỏ. Mở đầu bằng `:root[data-theme=…]` (giống hai
     khối bo tròn ở đầu tệp, và cũng để giao diện cổ điển đứng ngoài — quy tắc #20) đưa nó lên (0,5,0).
   ⛔ ĐỪNG dùng `!important`: Bootstrap ⛔ không cầm `!important` ở chỗ này, mà `app.css` cũng không —
     đôn độ đặc hiệu là đủ (luật ⑪ đợt 2197). */
:root[data-theme="light"] .nhom-kieu-xem > .btn:first-child[b-0hoohmgeru],
:root[data-theme="dark"] .nhom-kieu-xem > .btn:first-child[b-0hoohmgeru] {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

:root[data-theme="light"] .nhom-kieu-xem > .btn + .btn[b-0hoohmgeru],
:root[data-theme="dark"] .nhom-kieu-xem > .btn + .btn[b-0hoohmgeru] {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    /* Lề âm là cách Bootstrap cho hai nửa chồng viền lên nhau — bỏ nó là giữa cụm hiện một vạch dày
       gấp đôi. Đọc từ chính biến của Bootstrap, ⛔ đừng gõ -1px. */
    margin-left: calc(var(--bs-border-width, 1px) * -1);
}
/* /Components/Pages/ChamCong/DonTreSomLichSuQuet.razor.rz.scp.css */
/* Cột «Lịch sử đi trễ / về sớm» của /cham-cong/don-tre-som (đợt 2436, việc o1998). */

/* Dải ô lọc: bốn ô trên MỘT hàng, không còn nhãn trên đầu ô (việc o2007). Hai ô ngày giữ chữ ngắn Từ: / Đến:
   đứng cạnh; ô phòng và ô họ tên dùng chữ chìm. Khổ điện thoại xuống dòng gọn, xem khối cuối tệp. */
.lsq-loc[b-upy2c17kok] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}

.lsq-ngay[b-upy2c17kok] {
    display: flex;
    align-items: center;
    gap: .25rem;
    flex: 0 0 auto;
}

.lsq-nhan[b-upy2c17kok] {
    font-size: .875rem;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}

.lsq-phong[b-upy2c17kok] {
    flex: 0 1 8rem;
    min-width: 5.5rem;
}

.lsq-ten[b-upy2c17kok] {
    flex: 1 1 8rem;
    width: auto;
    min-width: 7rem;
}

/* Cột Họ và tên là cột to nhất; các cột phụ co theo nội dung. */
.lsq-bang .lsq-phu[b-upy2c17kok] {
    width: 1%;
    white-space: nowrap;
}

/* Điện thoại: hàng một là hai ô ngày chia đôi, hàng hai là ô phòng và ô họ tên. Khung ô ngày đặt bề ngang bằng
   width nội tuyến, nên nới qua flex-basis (flex-basis khác auto thắng width); khung ấy thuộc component khác nên
   phải đi qua ::deep. */
@media (max-width: 576px) {
    .lsq-ngay[b-upy2c17kok] {
        flex: 1 1 calc(50% - .3rem);
        min-width: 0;
    }

    .lsq-ngay[b-upy2c17kok]  .o-ngay-boc {
        flex: 1 1 0;
        min-width: 0;
    }
}
/* /Components/Pages/ChamCong/HopThoaiNgay.razor.rz.scp.css */
/* ============================================================================================
   MÀU THEO CHỦ ĐỀ CHO HỘP THOẠI MỘT NGÀY CÔNG — đợt 554 (anh Tiến báo lỗi 12/08/2026 kèm ảnh)
   ============================================================================================
   HIỆN TƯỢNG anh chụp được ở giao diện TỐI (data-theme="dark"), trang bảng chấm công:
     · dải tiêu đề "07/08/2026 (T6)" tách hẳn khỏi nền tối bằng một vệt sáng;
     · khối "☑ ✔ Chấm công ngày này (tính 1 công)" nền TRẮNG SÁNG, chữ xám nhạt → gần như
       không đọc được;
     · ô "Lý do chấm công…" và ô "— Chọn nhanh lý do chuẩn —" viền mờ, chữ mờ;
     · riêng bảng "Chi tiết vào ra trong ngày" thì bình thường (app.css đã lo `.table` ở nền tối).

   GỐC LỖI — ba chỗ, ba nguyên nhân khác nhau:
     ① `bg-light` của Bootstrap là `background-color: #f8f9fa !important` cho MỌI chủ đề. Giao diện
        "cổ điển tối" có luật đè trong co-dien.css (mục `.bg-light:not(.badge)`), còn giao diện
        "tối hiện đại" thì CHƯA BAO GIỜ đè ⇒ khối trắng nằm giữa hộp thoại tối, chữ vẫn thừa kế
        màu sáng #dde1e6 của `.modal-content` ⇒ sáng trên sáng.
     ② khối "🗓 Ngày này cần người chấm công quyết" tô nền bằng `style="background: #fffdf3"` —
        màu dán cứng trong thuộc tính style thì không chủ đề nào đổi được.
     ③ `.modal-header` / `.modal-footer` của Bootstrap 5.1 giữ nguyên đường viền `#dee2e6` (màu của
        bản sáng) và `.btn-close` là hình chữ X ĐEN ⇒ trên nền tối vừa chói vệt viền vừa mất nút X.

   ⛔ VÌ SAO KHÔNG SỬA app.css / co-dien.css (quy tắc #18 — kiểm soát rủi ro lan truyền):
      hai file đó dùng chung cho MỌI màn hình. Đè `.bg-light` ở tầng đó thì phải kèm `!important`
      (vì lớp tiện ích Bootstrap có sẵn !important) và lập tức đổi diện mạo mọi khối `bg-light`
      của cả app — gồm cả những chỗ CỐ Ý để trắng (co-dien.css mục 15 đã ghi lại một ca thật:
      huy hiệu `badge bg-light` chỉ còn 1,43:1 vì đúng kiểu đè này). Ở đây hộp thoại tự mang nền
      riêng, khai đủ 4 chủ đề, không đụng ai.

   ⚠ ĐÂY LÀ CSS SCOPED: Blazor gắn thuộc tính `b-xxxxx` vào từng thẻ của HopThoaiNgay.razor, nên
      mọi luật dưới đây CHỈ ăn trong hộp thoại này — kể cả những luật mượn tên lớp Bootstrap
      (`.modal-header`, `.form-control`…). Bảng chấm công theo tháng và mục 👤 Theo người cùng gọi
      component này nên cùng được sửa; không màn hình nào khác bị đụng.

   ⚠ BÀI HỌC SPECIFICITY (đợt 381, ghi lại ở ChamCongTheoThang.razor.css): thứ tự nạp là
      app.css → TKNB.Web.styles.css (file scoped này) → co-dien.css, nên co-dien.css NẠP SAU và
      thắng khi bằng điểm. Vì vậy mọi luật ở đây viết đủ đường `:root[data-theme=…] .htn-ngay .X`
      (0,4,0) rồi Blazor cộng thêm `[b-xxxxx]` thành (0,5,0) — vượt mọi luật đang có ở hai file
      dùng chung, mà vẫn không với ra ngoài hộp thoại.

   ⚠ Số trong file .css là số TĨNH nên không dính bẫy dấu phẩy thập phân của vi-VN (quy tắc #5).
   ============================================================================================ */

/* ═══════════ 1. KHỐI CHẤM CÔNG (thay cho `bg-light`) ═══════════ */
/* Bản SÁNG (light + cổ điển) giữ ĐÚNG màu cũ #f8f9fa của `.bg-light` — không được đổi, đây là
   diện mạo anh Tiến đang dùng hằng ngày. Viết không kèm chủ đề để chủ đề nào chưa khai cũng ra
   nền sáng như trước (fail-safe: sai thì sai về phía "y như cũ"). */
.htn-ngay .htn-khoi-cham[b-dvv8u2qes8] {
    background-color: #f8f9fa;
}

/* Nền TỐI hiện đại — hộp thoại là #262a31 (app.css), khối này LÕM XUỐNG một bậc để vẫn thấy được
   ranh giới mà không chói: #1d2127 với chữ #dde1e6 ≈ 12,6:1. */
:root[data-theme="dark"] .htn-ngay .htn-khoi-cham[b-dvv8u2qes8] {
    background-color: #1d2127;
    color: #dde1e6;
}

/* CỔ ĐIỂN TỐI — lấy đúng cặp màu co-dien.css đang dành cho `.bg-light` ở chủ đề này (#24292e /
   #e6e9ed), để bỏ lớp `bg-light` đi mà mắt thường không nhận ra khác biệt. */
:root[data-theme="co-dien-toi"] .htn-ngay .htn-khoi-cham[b-dvv8u2qes8] {
    background-color: #24292e;
    color: #e6e9ed;
}

/* ═══════════ 2. KHỐI QUYẾT ĐỊNH NGÀY PHÉP (thay cho style="background: #fffdf3") ═══════════ */
.htn-ngay .htn-khoi-quyet[b-dvv8u2qes8] {
    background-color: #fffdf3;
}

/* Hai nền tối: mượn đúng bảng màu "cảnh báo" mà mỗi chủ đề đã khai cho `.alert-warning`, nhờ vậy
   khối này vẫn đọc ra là KHỐI VÀNG (nghĩa: cần người quyết) chứ không thành một hộp xám vô nghĩa. */
:root[data-theme="dark"] .htn-ngay .htn-khoi-quyet[b-dvv8u2qes8] {
    background-color: #332701;
    color: #ffe69c;
}

:root[data-theme="co-dien-toi"] .htn-ngay .htn-khoi-quyet[b-dvv8u2qes8] {
    background-color: #3a2f08;
    color: #ffe9a8;
}

/* ═══════════ 3. DẢI TIÊU ĐỀ + ĐÁY HỘP THOẠI ═══════════ */
/* Bootstrap 5.1 để `.modal-header`/`.modal-footer` trong suốt nhưng viền thì dán cứng #dee2e6 —
   màu của bản sáng. Ở nền tối vệt viền đó sáng gấp nhiều lần thân hộp thoại nên nhìn như một dải
   trắng cắt ngang. Tô nền dải tiêu đề bằng đúng tông `.card-header` của nền tối (#2d323a) để nó
   nằm cùng ngôn ngữ với các thẻ khác trong app, và kéo hai đường viền về màu tối.
   ⛔ KHÔNG khai cho hai chủ đề SÁNG: bản sáng đang đúng, đụng vào là đổi thứ anh đang dùng. */
:root[data-theme="dark"] .htn-ngay .modal-header[b-dvv8u2qes8] {
    background-color: #2d323a;
    color: #dde1e6;
    border-bottom-color: #3a3f47;
}

:root[data-theme="dark"] .htn-ngay .modal-footer[b-dvv8u2qes8] {
    border-top-color: #3a3f47;
}

/* CỔ ĐIỂN TỐI đã có dải tiêu đề xanh navy riêng (co-dien.css mục 5) — chỉ chỉnh nốt đường viền đáy
   mà mục đó không với tới. */
:root[data-theme="co-dien-toi"] .htn-ngay .modal-footer[b-dvv8u2qes8] {
    border-top-color: #4c5560;
}

/* Nút ✕ đóng: Bootstrap vẽ bằng ảnh SVG nét ĐEN, đặt trên dải tiêu đề tối thì gần như biến mất.
   `invert(1)` lật thành nét trắng — đúng cách Bootstrap 5.3 làm cho `.btn-close-white`, chỉ khác
   là ở đây bó gọn trong hộp thoại này. */
:root[data-theme="dark"] .htn-ngay .btn-close[b-dvv8u2qes8],
:root[data-theme="co-dien-toi"] .htn-ngay .btn-close[b-dvv8u2qes8] {
    filter: invert(1) grayscale(100%) brightness(200%);
    opacity: .8;
}

/* ═══════════ 4. HAI Ô NHẬP TRONG KHỐI CHẤM CÔNG ═══════════ */
/* app.css đã cho `.form-control`/`.form-select` nền tối #2d323a, nhưng khối chấm công nay lõm
   xuống #1d2127 nên hai ô cần SÁNG HƠN nền khối mới nổi lên thành "chỗ để gõ"; viền cũng phải
   đậm hơn mức chung (#4a5058 → #5c6673) vì anh Tiến báo "viền mờ".

   🖊 ĐỢT 846 (việc o99) — ĐẶT BIẾN `--bs-border-color`, ⛔ ĐỪNG QUAY LẠI `border-color`.
   Bootstrap 5.3 dựng viền ô nhập bằng SHORTHAND `border: … var(--bs-border-color)` ở `.form-control`
   (0,1,0), còn trạng thái lỗi `.form-control.is-invalid` đặt LONGHAND `border-color` ở chỉ (0,2,0).
   Bộ chọn ở đây (0,4,0 + scoped) cao hơn hẳn ⇒ viết longhand là NUỐT LUÔN viền đỏ báo lỗi: ô sai
   vẫn viền xám. Hỏng LẶNG LẼ — build xanh, test xanh, màu viền thường vẫn đúng #5c6673, chỉ dấu
   hiệu "ô này sai" biến mất. Đặt BIẾN thì shorthand của Bootstrap resolve theo nó nên viền thường
   không đổi một pixel, mà longhand của `.is-invalid` vẫn thắng như thiết kế.
   ⛔ Đừng "chữa" bằng `:not(.is-invalid)` — thêm một lớp là đội độ đặc hiệu rồi vỡ kiểu khác.
   Cùng khuôn đợt 838 (nền sáng) + 840 (nền tối). Hàng rào canh: `VienONhapTuongPhanTests`. */
:root[data-theme="dark"] .htn-ngay .htn-khoi-cham .form-control[b-dvv8u2qes8],
:root[data-theme="dark"] .htn-ngay .htn-khoi-cham .form-select[b-dvv8u2qes8] {
    background-color: #2f353e;
    --bs-border-color: #5c6673;
    color: #e8ecf1;
}

/* Câu gợi ý trong ô lý do là chữ MỜ theo thiết kế, nhưng mức chung #8a9099 trên nền #2f353e chỉ
   được ~3,7:1 — anh đọc không ra. #b0b8c2 lên ~6,0:1, vẫn mờ hơn chữ thật nên không lẫn với
   nội dung đã gõ. */
:root[data-theme="dark"] .htn-ngay .htn-khoi-cham .form-control[b-dvv8u2qes8]::placeholder {
    color: #b0b8c2;
}

/* ✅ ĐỢT 652 — HAI LUẬT VẼ LẠI MŨI TÊN XỔ Ở ĐÂY ĐÃ GỠ, KHÔNG PHẢI QUÊN.
   Hộp thoại này từng tự vá riêng cho mình (nét #dde1e6 cho `dark`, #e6e9ed cho `co-dien-toi`) vì lúc đó
   chưa có luật chung. Đợt 652 đưa đúng hai mã màu ấy ra `wwwroot/app.css` mục 13c (4b) và
   `wwwroot/co-dien.css` mục 8 ⇒ mọi ô chọn của cả app đều có mũi tên sáng, hộp thoại này hưởng luôn.
   ⛔ Giữ lại bản riêng là HAI NGUỒN cho cùng một hình — sửa một chỗ quên chỗ kia, đúng kiểu hỏng dự án
     này dính nhiều lần nhất. Đã đo: nét #dde1e6 trên nền ô của hộp thoại (#2f353e) đạt **9,41:1**, thừa
     ngưỡng 3:1 của chỉ báo phi văn bản.
   🧱 Hàng rào canh luật chung: `TKNB.Tests/MuiTenOChonNenToiTests.cs`. */

/* Nhóm mục trong ô chọn (`optgroup`) do trình duyệt tự vẽ theo `color-scheme`; app.css đã đặt
   `color-scheme: dark` ở `:root[data-theme="dark"]` nên phần xổ ra đã tối sẵn — không khai thêm,
   khai tay vào đây là mỗi trình duyệt một kiểu. */

/* ═══════════ 5. KHỐI LÝ DO ĐI TRỄ / VỀ SỚM — đợt 555 ═══════════ */
/* Khối này NẰM TRONG `.htn-khoi-cham` (nó chỉ hiện ở ngày có trễ/sớm và chỉ với người được sửa),
   nên hai ô nhập bên trong đã hưởng sẵn toàn bộ mục 4 ở trên — chỉ còn phải cho nó một nền
   riêng để đọc ra là "một khối khác", không thì hai ô chọn nằm sát nhau trên cùng một nền và
   người chấm công dễ chọn nhầm ô lý do chấm công thành ô lý do trễ/sớm.
   ⚠ Sắc độ đi NGƯỢC chiều khối cha ở mỗi bảng màu (bản sáng: cha #f8f9fa → con trắng; bản tối:
   cha #1d2127 → con NHÔ LÊN) — cùng một cách phân tầng, chỉ khác chiều ánh sáng. */
.htn-ngay .htn-khoi-tresom[b-dvv8u2qes8] {
    background-color: #ffffff;
}

:root[data-theme="dark"] .htn-ngay .htn-khoi-tresom[b-dvv8u2qes8] {
    background-color: #2a2f37;
    color: #dde1e6;
}

:root[data-theme="co-dien-toi"] .htn-ngay .htn-khoi-tresom[b-dvv8u2qes8] {
    background-color: #2f353c;
    color: #e6e9ed;
}

/* ═══════════ 6. Ô LÝ DO NHÁY DẤU ❗ 3D — đợt 948 ═══════════ */
/* Anh Tiến 21/08/2026: "khi bỏ tick hoặc tick chấm công em cứ hiện nút lưu lên nhưng chưa cho lưu,
   mỗi lần nhấn thì ô lý do nháy dấu chấm than 3D động".
   🔑 Vì sao thay câu chữ đỏ ở chân hộp bằng dấu này: câu đỏ nằm cách ô lý do gần một màn hình cuộn,
     nên nó nói ĐÚNG mà vẫn để người dùng tự đi tìm chỗ điền. Dấu mọc ngay cạnh ô là chỉ thẳng vào chỗ gõ.
   ⚠ HAI lớp `htn-nhay-a` / `htn-nhay-b` giống hệt nhau, chỉ khác TÊN hoạt hoạ — trình duyệt chỉ chạy
     lại `animation` khi tên luật đổi. Một tên duy nhất thì lượt bấm thứ hai trở đi câm lặng, đúng thứ
     người dùng gặp ngay lần thử lại đầu tiên. ⛔ Đừng gộp hai lớp làm một cho "gọn".
   ⚠ Số trong `translate`/`rotate` KHÔNG đi qua C# nên không dính bẫy `CultureInfo` (quy tắc #5) —
     đây là CSS tĩnh viết tay, không phải chuỗi dựng trong mã. */

.htn-ngay .htn-o-lydo[b-dvv8u2qes8] {
    position: relative;
}

/* Dấu ❗ nổi khối: đổ bóng nhiều tầng + xoay quanh trục Y để có chiều sâu thật, không phải chữ phẳng
   phóng to. `perspective` đặt ngay trên chính phần tử vì nó là thứ DUY NHẤT xoay trong khối này. */
.htn-ngay .htn-cham-than[b-dvv8u2qes8] {
    position: absolute;
    top: 50%;
    right: 0.4rem;
    transform-origin: 50% 50%;
    font-size: 1.15rem;
    line-height: 1;
    pointer-events: auto;
    cursor: help;
    text-shadow:
        0 1px 0 #b02a2a,
        0 2px 0 #8f2020,
        0 3px 4px rgba(0, 0, 0, 0.45);
}

/* ⚠ `perspective()` gọi NGAY TRONG `transform`, ⛔ không khai thuộc tính `perspective` rời:
   thuộc tính rời chỉ tạo chiều sâu cho phần tử CON, không ăn vào phép xoay của chính phần tử này
   ⇒ dấu ❗ sẽ dẹt như chữ phóng to, mất hẳn chiều sâu mà vẫn chạy trơn tru (hỏng LẶNG LẼ). */
/* ⚠ Hoạt hoạ của dấu ❗ cũng phải LUÂN PHIÊN theo lớp cha `-nhay-a`/`-nhay-b`, y hệt ô nhập.
   🧨 Bản đầu của đợt 948 gắn cứng MỘT tên hoạt hoạ ngay trên chính dấu ❗ — đo sống thấy lượt bấm
     THỨ HAI chỉ còn ô rung, dấu ❗ ĐỨNG IM (phần tử đã có sẵn, lớp không đổi ⇒ trình duyệt không
     chạy lại). Anh Tiến đặt hàng "MỖI lần nhấn thì nháy", nên đứng im từ lượt thứ hai là hỏng đúng
     chỗ người dùng gặp nhiều nhất. */
.htn-ngay .htn-nhay-a .htn-cham-than[b-dvv8u2qes8] {
    animation: htn-cham-than-3d-a-b-dvv8u2qes8 0.9s ease-out both;
}

.htn-ngay .htn-nhay-b .htn-cham-than[b-dvv8u2qes8] {
    animation: htn-cham-than-3d-b-b-dvv8u2qes8 0.9s ease-out both;
}

@keyframes htn-cham-than-3d-a-b-dvv8u2qes8 {
    0%   { transform: perspective(220px) translateY(-50%) rotateY(-180deg) scale(0.3); opacity: 0; }
    45%  { transform: perspective(220px) translateY(-50%) rotateY(20deg)   scale(1.45); opacity: 1; }
    70%  { transform: perspective(220px) translateY(-50%) rotateY(-10deg)  scale(1.05); opacity: 1; }
    100% { transform: perspective(220px) translateY(-50%) rotateY(0deg)    scale(1.15); opacity: 1; }
}

@keyframes htn-cham-than-3d-b-b-dvv8u2qes8 {
    0%   { transform: perspective(220px) translateY(-50%) rotateY(-180deg) scale(0.3); opacity: 0; }
    45%  { transform: perspective(220px) translateY(-50%) rotateY(20deg)   scale(1.45); opacity: 1; }
    70%  { transform: perspective(220px) translateY(-50%) rotateY(-10deg)  scale(1.05); opacity: 1; }
    100% { transform: perspective(220px) translateY(-50%) rotateY(0deg)    scale(1.15); opacity: 1; }
}

/* Ô nhập rung nhẹ + viền đỏ trong đúng nhịp dấu ❗ mọc ra.
   ⛔ Đặt `--bs-border-color` chứ KHÔNG đặt `border-color`: Bootstrap 5.3 dựng viền `.form-control` bằng
     SHORTHAND đọc biến này, còn trạng thái lỗi `.form-control.is-invalid` mới dùng longhand. Viết
     `border-color` thẳng ở đây là nuốt luôn viền đỏ báo lỗi của mọi ô nhập (quy tắc #5, bài học đợt
     838/840). Hàng rào canh: `VienONhapTuongPhanTests`. */
.htn-ngay .htn-nhay-a .form-control[b-dvv8u2qes8],
.htn-ngay .htn-nhay-b .form-control[b-dvv8u2qes8] {
    --bs-border-color: #dc3545;
    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
}

.htn-ngay .htn-nhay-a .form-control[b-dvv8u2qes8] {
    animation: htn-rung-a-b-dvv8u2qes8 0.5s ease-in-out;
}

.htn-ngay .htn-nhay-b .form-control[b-dvv8u2qes8] {
    animation: htn-rung-b-b-dvv8u2qes8 0.5s ease-in-out;
}

@keyframes htn-rung-a-b-dvv8u2qes8 {
    0%, 100% { transform: translateX(0); }
    15%      { transform: translateX(-6px); }
    30%      { transform: translateX(6px); }
    45%      { transform: translateX(-4px); }
    60%      { transform: translateX(4px); }
    80%      { transform: translateX(-2px); }
}

@keyframes htn-rung-b-b-dvv8u2qes8 {
    0%, 100% { transform: translateX(0); }
    15%      { transform: translateX(-6px); }
    30%      { transform: translateX(6px); }
    45%      { transform: translateX(-4px); }
    60%      { transform: translateX(4px); }
    80%      { transform: translateX(-2px); }
}

/* ♿ Người đã bật "giảm chuyển động" của hệ điều hành thì BỎ rung và BỎ xoay, nhưng ⛔ KHÔNG bỏ dấu ❗
   và ⛔ không bỏ viền đỏ — đó là NỘI DUNG (chỉ chỗ còn thiếu), chỉ phần chuyển động mới là trang trí. */
@media (prefers-reduced-motion: reduce) {
    .htn-ngay .htn-nhay-a .htn-cham-than[b-dvv8u2qes8],
    .htn-ngay .htn-nhay-b .htn-cham-than[b-dvv8u2qes8] {
        animation: none;
        transform: translateY(-50%);
    }

    .htn-ngay .htn-nhay-a .form-control[b-dvv8u2qes8],
    .htn-ngay .htn-nhay-b .form-control[b-dvv8u2qes8] {
        animation: none;
    }
}
/* /Components/Pages/ChamCong/KhaiNhieuNgayTreSom.razor.rz.scp.css */
/* Danh sách ngày để khai trong hộp «Khai đi sớm về trễ» của /cham-cong/don-tre-som (việc o2006). */

.kn-khung[b-dm0qnxk2ts] {
    margin-top: .75rem;
    padding-top: .5rem;
    border-top: 1px solid var(--bs-border-color);
    font-size: .875rem;
}

.kn-dau[b-dm0qnxk2ts] {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: .25rem .75rem;
    margin-bottom: .25rem;
}

.kn-trong[b-dm0qnxk2ts] {
    padding: .5rem 0;
}

/* Mỗi dòng là một lưới bốn cột: ô tick, ngày, giờ vào/ra, chênh lệch. Cột giờ ăn phần còn lại, cột số dồn phải. */
.kn-dong[b-dm0qnxk2ts] {
    display: grid;
    grid-template-columns: 2rem 4.5rem minmax(0, 1fr) auto;
    align-items: center;
    column-gap: .5rem;
    margin: 0;
    padding: .3rem .5rem;
    border-bottom: 1px solid var(--bs-border-color);
    font-variant-numeric: tabular-nums;
}

.kn-tieu-de[b-dm0qnxk2ts] {
    font-weight: 600;
    color: var(--bs-secondary-color);
}

.kn-so[b-dm0qnxk2ts] {
    text-align: right;
    white-space: nowrap;
}

.kn-o-tick[b-dm0qnxk2ts] {
    display: flex;
    align-items: center;
    margin: 0;
}

.kn-o-tick .form-check-input[b-dm0qnxk2ts] {
    margin: 0;
}

/* Cả dòng bấm được (thẻ label bọc ô tick): con trỏ tay, nền nhạt khi rê chuột và khi đang chọn. */
.kn-chon-duoc[b-dm0qnxk2ts] {
    cursor: pointer;
}

.kn-chon-duoc:hover[b-dm0qnxk2ts] {
    background-color: var(--bs-tertiary-bg);
}

.kn-dang-chon[b-dm0qnxk2ts],
.kn-dang-chon:hover[b-dm0qnxk2ts] {
    background-color: var(--bs-primary-bg-subtle);
}

/* Ngày đã có đơn chờ duyệt hoặc đã duyệt: chữ mờ, không có ô tick. */
.kn-da-khai[b-dm0qnxk2ts] {
    color: var(--bs-secondary-color);
}

.kn-dau-tt[b-dm0qnxk2ts] {
    margin-left: .25rem;
    font-weight: 600;
}

.kn-o-tick .kn-dau-tt[b-dm0qnxk2ts] {
    margin-left: 0;
}

/* Ngón tay: mỗi dòng cao đủ vùng chạm; cả dòng là chỗ bấm nên ô tick nhỏ vẫn trúng. Cột đầu nới cho ô chọn tất cả. */
@media (pointer: coarse) {
    .kn-dong[b-dm0qnxk2ts] {
        grid-template-columns: var(--cham-toi-thieu) 4.5rem minmax(0, 1fr) auto;
        min-height: var(--cham-toi-thieu);
    }

    .kn-tieu-de .kn-o-tick[b-dm0qnxk2ts] {
        min-height: var(--cham-toi-thieu);
        min-width: var(--cham-toi-thieu);
    }
}
/* /Components/Pages/ChamCong/OCotNgayLich.razor.rz.scp.css */
/* ============================================================================================
   Ô ĐẦU CỘT NGÀY của lịch chấm công — đợt 483 (khuôn đồ họa gốc: đợt 387)
   ============================================================================================
   ⛔ CSS SCOPED của riêng component này. Cố ý KHÔNG đụng app.css / co-dien.css dùng chung.

   ⚠ BÀI HỌC SPECIFICITY (đợt 381 mục 3 — luật rời "chết câm" vì thua chuỗi dài):
      co-dien.css NẠP SAU file scoped này (App.razor: app.css → TKNB.Web.styles.css → co-dien.css)
      và tô đầu bảng bằng `:root[data-theme^="co-dien"] .table > thead > tr > th` = (0,3,3).
      ⇒ luật tô cuối tuần phải viết ĐỦ ĐƯỜNG DẪN `.table > thead > tr > th.o-cot-ngay.ngay-xx`
      (0,4,3) mới thắng. Rút ngắn cho "gọn" là màu cuối tuần biến mất Ở ĐÚNG GIAO DIỆN MẶC ĐỊNH
      (cổ điển) mà build vẫn sạch.
   ⚠ Blazor chỉ gắn thuộc tính scope vào PHẦN TỬ CUỐI của chuỗi chọn (`… > th[b-xxx]`) nên `.table`,
      `thead`, `tr` nằm ở màn hình gọi vẫn khớp bình thường.
   ============================================================================================ */

/* 📐 ĐỢT 2273 (việc o1787) — Ô ĐẦU CỘT NGÀY HẸP LẠI ĐỂ CẢ 31 NGÀY VÀO VỪA MỘT MÀN HÌNH.
   Anh Tiến 10/09/2026: *"màn hình hiển thị các bảng chấm công phải hiện đủ 31 ngày (hiện đủ cột
   không cần phải kéo)"*.
   📏 Đo sống trước khi sửa (app dev, khổ 1400×900, tháng 8/2026 = 31 ngày, lọc 🏢 CẢ KHO = 133 dòng):
     bảng rộng **1.535px** trong khối cuộn **1.350px** ⇒ **tràn 185px**, phải kéo ngang mới thấy từ
     khoảng ngày 20 trở đi. Mỗi ô ngày chiếm **37px**, trong đó **8px là đệm ngang** của `.table-sm`
     (.25rem mỗi bên) — tức riêng đệm ăn 8 × 31 = **248px**, thừa hơn cả phần tràn.
   🔑 SÀN THẬT CỦA CỘT NGÀY ⛔ KHÔNG PHẢI `min-width` NÀY, mà là **nội dung ô giờ trong thân bảng**
     (hai dòng «13:18 / 16:03» của lớp `.ccm-gio`, đo min-content = 28px). Đầu cột chỉ cần **19px**
     (số ngày «31» 14px · chữ thứ «CN» 13px, hai dòng chồng nhau). ⇒ Hạ `min-width` xuống 22px là
     **thôi cản** chứ ⛔ không bóp chữ: cột vẫn nở theo ô giờ rộng nhất.
   ⛔ ĐỪNG hạ đệm dọc / cỡ chữ theo — chiều cao hàng tiêu đề và độ tương phản là hai thứ đã trả giá
     ở đợt 1864 và ở luật `opacity: .85` ngay dưới. */
.o-cot-ngay[b-eildssn29y] {
    min-width: 22px;
    padding-left: 1px;
    padding-right: 1px;
    padding-top: .12rem;
    padding-bottom: .12rem;
    line-height: 1.05;
}

.o-cot-ngay .so-ngay[b-eildssn29y] {
    display: block;
    font-weight: 700;
    font-size: .78rem;
    /* tabular-nums: mọi chữ số rộng bằng nhau ⇒ 31 cột thẳng hàng tăm tắp */
    font-variant-numeric: tabular-nums;
}

/* ⚠ ĐỢT 537 — ĐỘ MỜ LÀ THỦ PHẠM QUEN MẶT CỦA TƯƠNG PHẢN THẤP (đợt 463 đã vá 4 chỗ y hệt).
   `opacity: .75` kéo chữ "T7"/"CN" lại gần màu nền cùng tông của chính ô đó ⇒ đo sống ra
   CN 3,93 · T7 4,06 (sáng) và CN 3,92 · T7 4,13 (tối) — đều DƯỚI ngưỡng chữ thường 4,5:1.
   Nâng lên .85 vẫn giữ đúng ý đồ "thứ trong tuần nhạt hơn số ngày" (số ngày là .78rem đậm 700,
   chữ thứ chỉ .58rem) mà qua ngưỡng ở CẢ 4 GIAO DIỆN: CN 4,72 · T7 4,90 (sáng).
   ⛔ Đừng hạ lại xuống .75 cho "nhạt cho đẹp" — đó đúng là con số vừa bị bắt. */
.o-cot-ngay .thu-ngay[b-eildssn29y] {
    display: block;
    font-size: .58rem;
    font-weight: 600;
    letter-spacing: .02em;
    opacity: .85;
}

/* Dùng background-image (một màu) vì đầu bảng cổ điển đang là background-image gradient */
.table > thead > tr > th.o-cot-ngay.ngay-t7[b-eildssn29y] {
    background-image: linear-gradient(#e9f0fa, #e9f0fa);
    color: #2f4d78;
}

.table > thead > tr > th.o-cot-ngay.ngay-cn[b-eildssn29y] {
    background-image: linear-gradient(#fbe9e9, #fbe9e9);
    color: #9c2b2b;
}

.table > thead > tr > th.o-cot-ngay.ngay-le[b-eildssn29y] {
    background-image: linear-gradient(#f1e9fb, #f1e9fb);
    color: #5f3a9c;
}

/* ⭐ ĐỢT 896 — NGÀY ĐI LÀM BÙ: hổ phách, cùng tông huy hiệu 💼 ở tab khai ngày.
   Cố ý KHÁC HẲN ba sắc kia chứ không phải "bỏ màu": cột này là ngày làm việc NGOẠI LỆ, người đọc phải
   nhận ra ngay là hôm ấy có chuyện, đừng để nó lẫn vào các ngày thường không tô gì. */
.table > thead > tr > th.o-cot-ngay.ngay-bu[b-eildssn29y] {
    background-image: linear-gradient(#fdf0dc, #fdf0dc);
    color: #8a5a12;
}

/* Hai nền TỐI: chữ sáng trên vệt màu mờ (nền thẻ mỗi giao diện một khác nên dùng rgba, không màu đặc) */
:root[data-theme="dark"] .table > thead > tr > th.o-cot-ngay.ngay-t7[b-eildssn29y],
:root[data-theme="co-dien-toi"] .table > thead > tr > th.o-cot-ngay.ngay-t7[b-eildssn29y] {
    background-image: linear-gradient(rgba(90, 150, 255, .16), rgba(90, 150, 255, .16));
    color: #a8c8ff;
}

:root[data-theme="dark"] .table > thead > tr > th.o-cot-ngay.ngay-cn[b-eildssn29y],
:root[data-theme="co-dien-toi"] .table > thead > tr > th.o-cot-ngay.ngay-cn[b-eildssn29y] {
    background-image: linear-gradient(rgba(255, 90, 90, .18), rgba(255, 90, 90, .18));
    color: #ffa8a8;
}

:root[data-theme="dark"] .table > thead > tr > th.o-cot-ngay.ngay-le[b-eildssn29y],
:root[data-theme="co-dien-toi"] .table > thead > tr > th.o-cot-ngay.ngay-le[b-eildssn29y] {
    background-image: linear-gradient(rgba(170, 120, 255, .18), rgba(170, 120, 255, .18));
    color: #cdb0ff;
}

:root[data-theme="dark"] .table > thead > tr > th.o-cot-ngay.ngay-bu[b-eildssn29y],
:root[data-theme="co-dien-toi"] .table > thead > tr > th.o-cot-ngay.ngay-bu[b-eildssn29y] {
    background-image: linear-gradient(rgba(255, 176, 60, .20), rgba(255, 176, 60, .20));
    color: #ffd08a;
}

/* ─────────── ⭐ ĐỢT 1979 (việc o1261) — SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY ───────────
   📏 Đo sống 04/09/2026 (Chrome thật, 390×844, cảm ứng, `tienvm`,
      `do-song-khung-hep.mjs --trang /cham-cong`): `.thu-ngay` là **CỤM ĐÔNG NHẤT** của cả lượt rà
      chữ nhỏ toàn app — **30 lượt** trên một trang, đo ra **9,3px** (`.58rem`), tức chỉ bằng **77%**
      sàn `--cham-chu-toi-thieu` = 12px. Đây là chữ *thứ trong tuần* trên đầu mỗi cột ngày; đọc
      không ra nó thì bảng công mất luôn cái mốc để dò "hôm ấy là thứ mấy".

   ⚠ CHỖ NÀY RỦI RO NHẤT CỦA CẢ ĐỢT nên phải đo, ⛔ đừng suy: bảng công có **31 cột ngày**, nong
      chữ đầu cột ra là ứng viên số một cho một lượt tràn ngang. Đo lại sau khi sửa ở đúng khung
      390×844: **tràn ngang 0 → 0**, ⛔ không đổi. Lý do nó ⛔ không nong: bề ngang cột đang do
      **số ngày** quyết định (`.78rem`, đậm 700) chứ ⛔ không phải hai chữ `T3`/`CN`.
      ⇒ Ngày nào đổi cỡ số ngày thì **đo lại chỗ này**, ⛔ đừng tin dòng trên là vĩnh viễn.
   ⚠ ⛔ KHÔNG đụng `opacity: .85` ở trên — đó là con số của đợt 537, đã đo tương phản ở cả 4 giao
      diện. Nâng cỡ chữ ⛔ không hạ tương phản, hai chuyện rời nhau.
   ⚠ `max()` chứ ⛔ không gõ thẳng 12px — nâng SÀN, giữ tỉ lệ. */
@media (pointer: coarse) {
    .o-cot-ngay .thu-ngay[b-eildssn29y] { font-size: max(.58rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/Pages/ChamCong/OSoThongKe.razor.rz.scp.css */
/* ============================================================================================
   THẺ SỐ dùng chung của phân hệ chấm công — đợt 483 (khuôn đồ họa gốc: đợt 387)
   ============================================================================================
   ⛔ ĐÂY LÀ CSS SCOPED (Blazor tự gắn thuộc tính b-xxxxx vào từng thẻ của OSoThongKe.razor) nên MỌI
      luật dưới đây CHỈ ăn trong component này. Cố ý KHÔNG đụng app.css / co-dien.css — hai file dùng
      chung do phiên đồ họa giữ, sửa vào đó là đụng mọi trang của mọi phiên.

   ⚠ Biến màu khai NGAY TRÊN .o-so (không nhờ một thẻ bọc ngoài như bản đợt 387): thẻ bọc nằm ở màn
     hình gọi, mà màn hình gọi có scope KHÁC ⇒ luật viết ở đây không với tới nó được.

   ⚠ Số trong file .css là số TĨNH nên không dính bẫy dấu phẩy thập phân của vi-VN (quy tắc #5).
   ============================================================================================ */

.o-so[b-324nra35c3] {
    /* Bảng màu nền/viền/chữ phụ — mỗi loại thẻ bên dưới chỉ khai --mau và --nen của riêng nó */
    --nen-the: #ffffff;
    --vien-the: #dfe3e8;
    --chu-phu: #5c6570;

    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .22rem .6rem .22rem .45rem;
    border: 1px solid var(--vien, var(--vien-the));
    border-left: 3px solid var(--mau, var(--vien-the));
    border-radius: 10px;
    background-color: var(--nen, var(--nen-the));
    line-height: 1.1;
}

:root[data-theme="dark"] .o-so[b-324nra35c3],
:root[data-theme="co-dien-toi"] .o-so[b-324nra35c3] {
    --nen-the: rgba(255, 255, 255, .04);
    --vien-the: #454b55;
    --chu-phu: #a7aeb8;
}

/* Cổ điển: vẫn BO TRÒN (anh Tiến yêu cầu thẳng ở đợt 387, mà cổ điển đang là giao diện MẶC ĐỊNH nên
   để vuông ở đây là coi như không làm) — chỉ bo VỪA PHẢI 6px thay vì 10px, và tuyệt đối KHÔNG thêm
   bóng/gradient/chuyển động, giữ đúng luật "cổ điển phẳng" của đợt 381. */
:root[data-theme^="co-dien"] .o-so[b-324nra35c3] {
    border-radius: 6px;
}

.o-so-hieu[b-324nra35c3] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.35rem;
    height: 1.35rem;
    flex: 0 0 auto;
    border-radius: 50%;
    background-color: var(--mau, #6c757d);
    color: #fff;
    font-size: .72rem;
    font-weight: 700;
}

:root[data-theme^="co-dien"] .o-so-hieu[b-324nra35c3] {
    border-radius: 2px;
}

.o-so-than[b-324nra35c3] {
    display: flex;
    flex-direction: column;
}

.o-so-con[b-324nra35c3] {
    font-size: 1.02rem;
    font-weight: 700;
    /* tabular-nums: mọi chữ số rộng bằng nhau ⇒ dải thẻ đứng thẳng hàng */
    font-variant-numeric: tabular-nums;
    letter-spacing: .01em;
    color: var(--mau, inherit);
}

.o-so-nhan[b-324nra35c3] {
    font-size: .66rem;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--chu-phu);
    white-space: nowrap;
}

/* --- MÀU THEO CHIỀU LỢI / BẤT LỢI (luật CLAUDE.md) ---
   ĐỎ  = số bất lợi, phải giải trình (ngày vắng · ngày đi muộn · ngày về sớm)
   XANH LÁ = tin tốt / đã xong (vắng = 0, tháng đã chốt)
   XANH DƯƠNG = thông tin trung tính (nghỉ phép — đã có đơn duyệt, không phải lỗi của ai)
   VÀNG-CAM = NHẮC, không quy kết (thiếu lượt quẹt ra vẫn tính ĐỦ công; mốc dữ liệu)
   XÁM = không mang chiều lợi/bất lợi nào.
   ⚠ Số 0 KHÔNG tô đỏ: "vắng 0" là tin tốt, tô đỏ là báo động giả — nơi gọi tự đổi Loai theo giá trị.
   Mọi cặp màu dưới đây đều là chữ đậm trên nền rất nhạt cùng tông ⇒ tương phản ≥ 4,5:1. */
.o-so--vang[b-324nra35c3]        { --mau: #c62828; --nen: #fdeeee; --vien: #f2c9c9; }
.o-so--yen[b-324nra35c3]         { --mau: #1b6b3a; --nen: #ecf7f0; --vien: #c6e4d2; }
.o-so--phep[b-324nra35c3]        { --mau: #0b5ed7; --nen: #eaf1ff; --vien: #c9daf8; }
.o-so--nhac[b-324nra35c3]        { --mau: #8a5a00; --nen: #fff6e5; --vien: #f0dcb4; }
.o-so--moc[b-324nra35c3]         { --mau: #8a5a00; --nen: #fff6e5; --vien: #f0dcb4; }
.o-so--chot[b-324nra35c3]        { --mau: #1b6b3a; --nen: #ecf7f0; --vien: #c6e4d2; }
.o-so--trung-tinh[b-324nra35c3]  { --mau: #5c6570; --nen: #f6f7f9; --vien: #dfe3e8; }

:root[data-theme="dark"] .o-so--vang[b-324nra35c3],
:root[data-theme="co-dien-toi"] .o-so--vang[b-324nra35c3] {
    --mau: #ff8a80;
    --nen: rgba(255, 82, 82, .12);
    --vien: rgba(255, 138, 128, .35);
}

:root[data-theme="dark"] .o-so--yen[b-324nra35c3],
:root[data-theme="dark"] .o-so--chot[b-324nra35c3],
:root[data-theme="co-dien-toi"] .o-so--yen[b-324nra35c3],
:root[data-theme="co-dien-toi"] .o-so--chot[b-324nra35c3] {
    --mau: #7fd8a0;
    --nen: rgba(80, 200, 130, .12);
    --vien: rgba(127, 216, 160, .35);
}

:root[data-theme="dark"] .o-so--phep[b-324nra35c3],
:root[data-theme="co-dien-toi"] .o-so--phep[b-324nra35c3] {
    --mau: #8ec5ff;
    --nen: rgba(90, 150, 255, .12);
    --vien: rgba(142, 197, 255, .35);
}

:root[data-theme="dark"] .o-so--nhac[b-324nra35c3],
:root[data-theme="dark"] .o-so--moc[b-324nra35c3],
:root[data-theme="co-dien-toi"] .o-so--nhac[b-324nra35c3],
:root[data-theme="co-dien-toi"] .o-so--moc[b-324nra35c3] {
    --mau: #ffcc80;
    --nen: rgba(255, 193, 7, .12);
    --vien: rgba(255, 204, 128, .35);
}

:root[data-theme="dark"] .o-so--trung-tinh[b-324nra35c3],
:root[data-theme="co-dien-toi"] .o-so--trung-tinh[b-324nra35c3] {
    --mau: #b9c0ca;
    --nen: rgba(255, 255, 255, .05);
    --vien: rgba(185, 192, 202, .3);
}

/* Chữ trắng trên nền vàng-cam / xanh nhạt của huy hiệu tròn thì mờ ⇒ ở hai nền tối đổi sang chữ sẫm */
:root[data-theme="dark"] .o-so-hieu[b-324nra35c3],
:root[data-theme="co-dien-toi"] .o-so-hieu[b-324nra35c3] {
    color: #1c1f24;
}

/* ─────────── ⭐ ĐỢT 1979 (việc o1261) — SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY ───────────
   📏 Đo sống 04/09/2026 (Chrome thật, 390×844, cảm ứng, `tienvm`,
      `do-song-khung-hep.mjs --trang /cham-cong`): ô số thống kê góp **8** chỗ chữ nhỏ —
      `.o-so-hieu` **11,5px** (`.72rem`) và `.o-so-nhan` **10,6px** (`.66rem`), dưới sàn
      `--cham-chu-toi-thieu` = 12px. `.o-so-nhan` chính là dòng chữ giải nghĩa ký hiệu
      (*"Ngày thiếu lượt quẹt ra"* · *"Ngày nghỉ phép"*) — bỏ đọc được nó thì ký hiệu vô nghĩa.
   ⚠ `.o-so-hieu` là vòng tròn ký hiệu bề ngang **1,35rem cố định**: nâng cỡ chữ ⛔ không nong nó
      ra, chữ chỉ đầy vòng hơn. Đã đo lại sau khi sửa: /cham-cong vẫn **tràn ngang 0**.
   ⚠ `max()` chứ ⛔ không gõ thẳng 12px — nâng SÀN, giữ tỉ lệ. */
@media (pointer: coarse) {
    .o-so-hieu[b-324nra35c3] { font-size: max(.72rem, var(--cham-chu-toi-thieu)); }
    .o-so-nhan[b-324nra35c3] { font-size: max(.66rem, var(--cham-chu-toi-thieu)); }
}

/* ─────────── 📱 ĐỢT 2285 (việc o1811) — Ở KHỔ ĐIỆN THOẠI: ĐÚNG HAI THẺ MỖI HÀNG ───────────

   Anh Tiến 10/09/2026, ảnh chụp dải này trên điện thoại: *"nội dung cũng thế"* (nối tiếp câu
   *"cái to cái nhỏ sắp xếp lộn xộn không đều"*).
   📏 Đo sống 390×844 trước lượt này, dải thống kê của `/cham-cong`: bề ngang năm thẻ ra
     **184 · 140 · 111 · 142 · 129px** — mỗi thẻ một cỡ, hàng thì hai thẻ hàng thì một, mép phải
     mỗi hàng dừng một chỗ khác nhau (hàng «P 0» + «V 6» bỏ trống **102px**).

   🔑 `calc(50% - .25rem)` = một nửa trừ **NỬA** khe. Mọi thẻ bọc dải này đều là `d-flex gap-2`
     (.5rem) ⇒ hai thẻ + một khe = tròn 100%, ⛔ không thẻ nào rơi xuống dòng vì thừa một pixel.
     Trừ trọn `.5rem` là mỗi hàng hụt nửa khe và mép phải lại lệch — đúng thứ đang đi chữa.
   ⚠ `min-width: 0` là vế BẮT BUỘC: phần tử flex mặc định `min-width: auto` = bề rộng nội dung
     nhỏ nhất, mà nhãn dài («Ngày thiếu lượt quẹt ra») thì rộng hơn nửa hàng ⇒ thiếu dòng này là
     thẻ **nở quá nửa** và phép chia đôi ⛔ không bao giờ chạy, mà build vẫn xanh.
   🔒 ĐIỀU KIỆN LÀ **BỀ NGANG**, ⛔ không phải `pointer: coarse` như khối ngay trên — ⛔ đừng đọc
     hai khối thành mâu thuẫn: khối trên hỏi *"cái gì chạm vào"* (cỡ chữ cho ngón tay), khối này
     hỏi *"còn bao nhiêu chỗ"* (bố cục). Cửa sổ máy tính thu hẹp cũng đáng chia đôi y hệt.
   ⓘ Luật áp cho CẢ ba nơi dựng thẻ này (`/cham-cong` · mục 👤 Theo người · hộp trang chủ) — cố ý:
     một kiểu thẻ thì một cách xếp, ⛔ đừng để hai màn hình cùng app xếp hai kiểu. */
@media (max-width: 576px) {
    .o-so[b-324nra35c3] {
        flex: 1 1 calc(50% - .25rem);
        min-width: 0;
    }

    /* 🔴 `min-width: 0` PHẢI CÓ Ở **MỌI CẤP** FLEX ITEM TRÊN ĐƯỜNG XUỐNG CHỮ — luật ⑪ đợt 2198,
       và lượt này dính lại y nguyên nên chép luôn số đo vào đây làm bằng chứng.
       📏 Đo sống 390×844 sau khi chia đôi hàng: thẻ rộng đúng **162px**, mà `.o-so-than` bên trong
         vẫn ra **135,3px** với `min-width: auto` (= bề rộng nội dung nhỏ nhất) và nhãn thì
         `white-space: nowrap` ⇒ chữ «Ngày thiếu lượt quẹt **ra**» **TRÀN QUA MÉP PHẢI 11,5px**,
         đè lên khoảng cách giữa hai thẻ. Thẻ ⛔ không kêu gì: `overflow` của nó là `visible`.
       ⇒ Hai vế đi cùng nhau: cho cụm thân ĐƯỢC PHÉP hẹp hơn nội dung, và cho nhãn ĐƯỢC PHÉP
         xuống dòng. Thiếu vế nào cũng còn tràn — bỏ `min-width` thì cụm vẫn nở, bỏ `white-space`
         thì chữ vẫn ⛔ không có chỗ nào để ngắt.
       🔒 CHỈ ở khổ hẹp: màn hình rộng thẻ co giãn theo nội dung nên `nowrap` là đúng — nó giữ nhãn
         nằm gọn một dòng dưới con số, đúng dáng «thẻ số» của đợt 387. */
    .o-so-than[b-324nra35c3] {
        min-width: 0;
    }

    .o-so-nhan[b-324nra35c3] {
        white-space: normal;
    }
}
/* /Components/Pages/ChamCong/TrangDonTreSom.razor.rz.scp.css */
/* Trang /cham-cong/don-tre-som làm lại theo ảnh anh Tiến 13/09/2026 (đợt 2436, việc o1998). */

/* Dòng tiêu đề: tên trang bên trái, các nút dồn sang phải; khổ hẹp thì nút xuống dòng dưới. */
.dts-dau[b-rkcdvosvsy] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1rem;
}

.dts-dau h1[b-rkcdvosvsy] {
    margin-right: auto;
}

.dts-dau-nut[b-rkcdvosvsy] {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

/* Bảng đơn của tôi: cột phụ co theo nội dung, cột Lý do ăn phần còn lại (anh: "cho nhiều không gian"). */
.dts-bang .dts-phu[b-rkcdvosvsy] {
    width: 1%;
    white-space: nowrap;
}

.dts-bang .dts-lydo[b-rkcdvosvsy] {
    min-width: 12rem;
}

/* Hai hàng của hộp khai: mỗi hàng hai ô đứng cạnh nhau, ô chính giãn hết phần còn lại. */
.dts-hang[b-rkcdvosvsy] {
    display: flex;
    align-items: flex-end;
    gap: .5rem;
}

.dts-o-chinh[b-rkcdvosvsy] {
    flex: 1 1 auto;
    min-width: 0;
}

/* Ô chọn nhanh lý do chuẩn: hẹp, chữ dài bị cắt — bảng xổ xuống vẫn hiện đủ câu. */
.dts-chon-nhanh[b-rkcdvosvsy] {
    flex: 0 0 9.5rem;
    width: 9.5rem;
    min-width: 0;
    /* ⚠ ĐỢT 2693 (việc o2170) — ba dòng này phải ĐI LIỀN NHAU: thiếu `overflow` và
       `white-space` thì `text-overflow` ⛔ có tác dụng gì, mà build vẫn xanh ⇒ chính câu chú thích
       ngay trên tuyên bố đã cắt chữ lại nói sai. Có hàng rào `LuatCatChuEllipsisTests` canh. */
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.dts-truong-hop.dts-co[b-rkcdvosvsy] {
    font-weight: 600;
}

.dts-truong-hop.dts-khong[b-rkcdvosvsy] {
    color: var(--bs-secondary-color);
}

/* Hàng Ngày + Trường hợp tạm nghỉ khi đã tick ngày trong danh sách (việc o2006): nút Gửi lúc ấy gửi theo danh sách. */
.dts-tam-nghi[b-rkcdvosvsy] {
    opacity: .5;
}
/* /Components/Pages/ChuKySo/TrangChuKySoCuaToi.razor.rz.scp.css */
/* Dấu vân tay SHA-256 là chuỗi 64 ký tự — cho xuống dòng để ⛔ đẩy tràn ngang ở khổ điện thoại. */
.ckso-van-tay[b-rfawal9pud] {
    font-size: 0.72rem;
    overflow-wrap: anywhere;
}
/* /Components/Pages/ChuKySo/TrangGioiThieuChuKySo.razor.rz.scp.css */
/* Dải năm bước của vòng đời chữ ký số — đợt 2667, việc o2156.
   ⚠ Dùng LƯỚI tự co chứ ⛔ ghim 5 cột: ở khổ điện thoại năm thẻ cạnh nhau thì mỗi thẻ còn ~70px,
   chữ vỡ thành từng chữ cái một dòng. `auto-fit` + `minmax` cho máy tự hạ xuống 3 → 2 → 1 cột. */
.vong-doi[b-bfm0q6ho42] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.75rem;
}

/* Mỗi bước phải cao bằng nhau để dải nhìn thành MỘT hàng, ⛔ phải năm mẩu so le. */
.buoc-ky[b-bfm0q6ho42] {
    height: 100%;
}

/* Số thứ tự bước — tròn, đặc, đứng trước biểu tượng.
   🔑 Vì sao vẫn cần số dù thẻ đã xếp theo thứ tự: ở khổ hẹp dải rơi xuống một cột, lúc ấy thứ tự
   chỉ còn đọc được bằng con số. */
.so-buoc[b-bfm0q6ho42] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.35rem;
    height: 1.35rem;
    margin-right: 0.35rem;
    border-radius: 50%;
    background: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1;
}

/* Cột tiêu chí của bảng so sánh: giữ cho tên tiêu chí ⛔ bị bóp thành cột chữ dựng đứng khi hai cột
   nội dung dài ra. ⛔ Ghim `width` cứng — `min-width` để bảng vẫn co được ở khổ điện thoại. */
.bang-so-sanh th[scope="row"][b-bfm0q6ho42] {
    min-width: 9rem;
}

/* Khổ điện thoại: ba cột bóp trong 375px thì mỗi cột chỉ còn ~100px — đo tận tay ở 375px: dòng cao tới
   345px, tức mỗi dòng gần một màn hình, bảng dài 2.500px. ⇒ Đặt bề ngang tối thiểu cho từng Ô để bảng
   rộng ra rồi tự cuộn ngang trong khung của nó; bảng SO SÁNH mà ⛔ nhìn được hai vế cạnh nhau thì
   ⛔ còn là bảng so sánh nữa.
   🩸 BẪY ĐÃ TRẢ GIÁ (đo 16/09/2026, đợt 2667): đặt `min-width` lên chính thẻ `<table>` thì ⛔ ăn thua —
   `app.css` có luật chung ở khổ ≤820px biến mọi `.content table.table` thành khối cuộn và dập
   `min-width: 0 !important`. Luật ấy CỐ Ý (nó chặn 7 bảng khai `min-width` thẳng trong markup kéo trôi
   cả trang) ⇒ ⛔ đi phá nó, mà đặt trần lên Ô — luật chung ⛔ đụng tới ô. */
.bang-so-sanh td[b-bfm0q6ho42] {
    min-width: 12rem;
}
/* /Components/Pages/ChuKySo/TrangMaTranKySo.razor.rz.scp.css */
/* Câu giải thích dưới đầu cột và dưới tên quy trình — nhỏ hơn chữ thường một bậc, xuống dòng thoải mái.
   ⚠ ⛔ nhét câu này vào title: quy tắc cứng #23 chỉ cho title ghi TÊN của nút đang ẩn chữ. */
.ckso-giai-thich[b-f78cgbhfri] {
    font-size: 0.72rem;
    line-height: 1.15;
    white-space: normal;
}

/* Chữ nổi báo "đã lưu" — position: fixed nên ⛔ chiếm chỗ trong luồng bố cục.
   🔑 Vì sao ⛔ dùng dải xanh nằm trong luồng (bài học đợt 2281): dải ấy chen vào và ĐẨY CẢ BẢNG
   xuống một dòng sau mỗi lượt tick ⇒ ô người ta vừa bấm trượt khỏi con trỏ, lượt tick kế rơi nhầm ô. */
.ckso-chu-noi[b-f78cgbhfri] {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    padding: 0.4rem 0.9rem;
    border-radius: 0.5rem;
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
    border: 1px solid var(--bs-success-border-subtle);
    font-size: 0.85rem;
    animation: ckso-mo-dan-b-f78cgbhfri 2.6s ease-in forwards;
}

@keyframes ckso-mo-dan-b-f78cgbhfri {
    0% { opacity: 0; }
    12% { opacity: 1; }
    75% { opacity: 1; }
    100% { opacity: 0; }
}

/* 📌 GHIM CỘT TÊN QUY TRÌNH — đo sống 16/09/2026 trên app dev ở khổ 800px: bảng 8 cột buộc phải cuộn
   ngang, và khi cuộn thì tên biên bản trôi hẳn khỏi tầm nhìn ⇒ người tick ⛔ biết mình đang tick DÒNG
   NÀO. Đây đúng loại lỗi im lặng: màn hình ⛔ báo gì, chỉ là người ta tick nhầm dòng.
   ⚠ Nền phải ĐẶC (⛔ trong suốt): ô dính nổi lên trên ô đang trôi qua dưới nó, để trong suốt là hai
   lớp chữ chồng nhau. */
.table > :not(caption) > tr > :first-child[b-f78cgbhfri] {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--bs-body-bg);
}

/* Dòng tên app dùng nền xám của .table-light nên ô dính của nó phải lấy đúng nền ấy, ⛔ nền trắng. */
.table > tbody > tr.table-light > :first-child[b-f78cgbhfri] {
    background: var(--bs-secondary-bg);
}

/* Đầu bảng vừa dính TRÊN vừa dính TRÁI ⇒ tầng z phải cao hơn ô dính thường, ⛔ nó chui xuống dưới. */
.table > thead > tr > :first-child[b-f78cgbhfri] {
    z-index: 3;
}
/* /Components/Pages/ChuKySo/TrangQuanTriChungThu.razor.rz.scp.css */
/* Dấu vân tay SHA-256 dài 64 ký tự — cho xuống dòng để ⛔ đẩy tràn ngang ở khổ điện thoại. */
.ckso-van-tay[b-6w0erwiuj6] {
    font-size: 0.72rem;
    overflow-wrap: anywhere;
}
/* /Components/Pages/CongCuDev/KhoiBieuNguHong.razor.rz.scp.css */
/* ═══ DẢI TỰ CHẨN ĐOÁN CỦA KHỐI 🎏 BIỂU NGỮ DỌC — đợt 1419 (việc o614) ═══

   ⭐ Anh Tiến 27/08/2026: "anh chưa thấy vận hành … băng rôn 2 bên trang web". Cấu hình khi ấy BẬT đủ
     tám khóa và máy chủ dựng đủ hai thẻ, nhưng nhánh `@media` của `BieuNguHong.razor.css` ẩn chúng ở
     màn hẹp ⇒ người khai nhìn màn hình mình, không thấy gì, và ⛔ KHÔNG CÓ CÁCH NÀO BIẾT VÌ SAO:
     khối cấu hình vẫn in "đang treo" (nó nói về cài đặt, không nói về màn hình đang xem).

   🔑 VÌ SAO LÀM BẰNG CSS THUẦN CHỨ ⛔ KHÔNG HỎI JAVASCRIPT bề ngang màn hình: câu cần trả lời đúng là
     "nhánh @media kia có ăn trên máy này không" — hỏi chính trình duyệt bằng cùng một ngưỡng là câu
     trả lời CHẮC CHẮN, còn đo `window.innerWidth` rồi tự so là dựng một phép so THỨ HAI, tức chỗ hai
     bên lệch nhau mà ⛔ không ai kêu (đúng bài học ô xem thử: đừng đẻ bản sao thứ hai).
     Kèm theo: ⛔ không cần interop, ⛔ không có lượt vẽ lại nào, và nó đúng ngay cả khi người ta kéo
     rộng/thu hẹp cửa sổ trong lúc đang mở khối.

   ⚠ NGƯỠNG 1528px Ở ĐÂY PHẢI KHỚP `LuatBieuNguHong.RongManHinhToiThieuPx` — hàng rào
     `LuatBieuNguHongTests.Nguong_trong_dai_tu_chan_doan_khop_hang_so` đối chiếu tận nơi. Lệch một con
     số là dải này nói ngược với thứ đang xảy ra trên chính màn hình ấy, tức tệ hơn cả không có nó. */

/* ⛔ MẶC ĐỊNH LÀ CÂU "MÀN HẸP", nhánh @media mới lật lại — cùng lẽ với `.bnh { display: none }`:
   trình duyệt không hiểu `@media` sẽ dừng ở nấc "hẹp", tức nói rằng biểu ngữ đang ẩn. Viết ngược lại
   là nấc lùi hoá thành lời khẳng định "đủ rộng" trong khi ⛔ không đo được gì cả. */
.bnh-du-rong[b-z28apclryn] {
    display: none;
}

/* 🩸 ⭐ ĐỢT 1580 (việc o885) — NẤC GIỮA `.bnh-vua` ĐÃ BỎ, ⛔ ĐỪNG DỰNG LẠI.
   Đợt 1481 thêm nấc ấy vì trang chủ khi đó mang NGƯỠNG ẨN RIÊNG 1888px (cột nội dung của nó nới rộng
   hơn mọi trang khác). Đợt 1580 chữa ở gốc: cột nội dung của trang chủ tự chừa lại
   `2 × (40 + 24) = 128px` cho hai tấm (`wwwroot/trang-chu.css`) ⇒ trang chủ dùng chung ĐÚNG MỘT
   ngưỡng 1528px với mọi trang. Giữ nấc giữa lại là dải chẩn đoán nói một câu ⛔ KHÔNG còn đúng với
   thứ đang xảy ra trên chính màn hình ấy — tệ hơn cả không có nó, đúng bệnh đợt 1419 đi chữa. */
.bnh-hep[b-z28apclryn] {
    display: inline;
}

@media (min-width: 1528px) {
    .bnh-du-rong[b-z28apclryn] {
        display: inline;
    }

    .bnh-hep[b-z28apclryn] {
        display: none;
    }
}
/* /Components/Pages/CongDoan/QuanLyCongDoan.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — Ô LỌC «CHỈ NGƯỜI ĐANG GIỮ CHỨC» (đợt 1672, việc o962) ═══

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài — nâng chính ô vuông lên 44px thì nó to bằng cả dòng chữ bên cạnh. Thứ phải nới là VÙNG
   CHẠM QUANH ô, mỗi màn hình một khác.

   📏 ĐO SỐNG TRƯỚC KHI VÁ — 01/09/2026, Chrome thật, khung 390×844 cảm ứng:
     node cong-cu/do-song-o-tick-cham.mjs --trang /quan-ly-phong/cong-doan
     Cấu trúc ③ hai anh em · ô vuông 16×16 · nhãn cao 24 · hộp `.form-check` cao 24 (display
     block) · VÙNG BẤM THẬT tại cột ô tick 17px · tại cột chữ 25px · lệch tâm dọc 0px.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width` — khổ hẹp KHÔNG có nghĩa là ngón tay.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; ⛔ đừng gõ số 44px vào tệp này.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng ghép bằng chữ `and` (bài học đợt 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs` */

@media (pointer: coarse) {

    /* ① HỘP BỌC `div.form-check` — nâng sàn chiều cao, đổi sang hộp flex để ô vuông thôi dính mép
       trên (trong hộp flex thuộc tính `float` bị BỎ QUA).
       🔑 BỀ NGANG KHÔNG ĐỔI: hộp mở đầu ở 1,5em · ô tick mang lề trái −1,5em nên nằm ở 0…1em ·
         bề rộng ngoài −0,5em, cộng `gap` 0,5em ⇒ nhãn vẫn bắt đầu đúng ở 1,5em.
       ⚠ `inline-flex` chứ ⛔ không `flex`: hộp này nằm trong `card-body d-flex flex-wrap gap-2
         align-items-end` xếp ngang cùng ô chọn phòng ban và nút thêm mới. */
    .form-check[b-ihmzj87chg] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    /* ② NHÃN CHỮ — vùng bấm thật (nối với ô tick bằng `for`).
       Cặp `margin-left` + `padding-left` 1,5em kéo nhãn PHỦ TRỌN cột ô tick: khoảng ngay trên và
       ngay dưới ô vuông vốn thuộc `div.form-check`, một thẻ `div` ⛔ KHÔNG bấm được.
       `align-self: stretch` giữ nhãn cao hết hộp — thiếu nó là vùng bấm hụt lại còn cỡ con chữ.
       ⚠ Hai con số 1,5em là hằng số Bootstrap của `.form-check`, ⛔ đừng đoán. */
    .form-check-label[b-ihmzj87chg] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -1.5em;
        padding-left: 1.5em;
    }
}
/* /Components/Pages/CongNghe/DaiBonKhoCongNghe.razor.rz.scp.css */
/* ═══ DẢI BỒN THEO KHO — ĐỢT 1161 (việc o342) ═══
   Bảng màu CÔNG NGHIỆP: thép nguội cho thân bồn, vàng hổ phách cho ống góp. Cố ý KHÔNG dùng bốn màu
   ngữ nghĩa của Bootstrap (primary / info / success / warning) — chính bộ bốn màu đó là thứ làm
   dashboard công nghệ trùng khít dashboard nước.

   ⛔ VÌ SAO Ở ĐÂY MÀ KHÔNG PHẢI app.css: đây là CSS ISOLATION, Blazor tự gắn dấu phạm vi nên mọi luật
      dưới đây CHỈ ăn trong component này. Đưa vào app.css là đụng chỗ dùng chung, đúng diện rủi ro
      lan truyền của quy tắc #18.

   ⚠ Bộ biến khai TRÊN chính khung của component chứ không phải trên :root — khai ở :root trần thì
     độ đặc hiệu bằng [data-theme=dark] của Bootstrap mà app.css nạp SAU, và nó GHIM token ở giá trị
     nền sáng (bài học đợt 831).
   ⚠ Không viết cặp ký tự đóng chú thích vào giữa câu văn — chú thích đóng ngay tại đó và đoạn còn
     lại dính vào bộ chọn ngay dưới, trình duyệt bỏ NGUYÊN luật đó mà không một câu lỗi (đợt 852). */

.dbk-khung[b-zkynmpcaht] {
    /* ⛔ Năm biến MÀU THÉP của bản vẽ tay (--dbk-than-tren · --dbk-than-duoi · --dbk-nap · --dbk-dai
       · --dbk-be) ĐÃ GỠ ở đợt 1186: hình bồn nay lấy từ LuatHinhCongNghe và mỗi lớp tự mang màu. */
    --dbk-ong: #e0a800;            /* hổ phách — màu xăng dầu, ⛔ không phải xanh nước */
    --dbk-ong-vien: rgba(0, 0, 0, .28);
}

/* Nền tối: thép sẫm lại, ống góp sáng lên cho giữ tương phản. Phủ CẢ HAI trục giao diện của app
   (data-theme) — dấu phạm vi của Blazor gắn vào phần tử CUỐI nên bộ chọn này vẫn chỉ ăn trong đây. */
:root[data-theme="dark"] .dbk-khung[b-zkynmpcaht] {
    --dbk-ong: #f0b429;
    --dbk-ong-vien: rgba(0, 0, 0, .55);
}

/* ───── Đầu thẻ: con số tổng đứng ngay cạnh tiêu đề, ⛔ không tách thành một thẻ số riêng ───── */
.dbk-tong[b-zkynmpcaht] {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 1.05rem;
}

.dbk-dv[b-zkynmpcaht],
.dbk-cham[b-zkynmpcaht] {
    font-weight: 400;
    font-size: .8rem;
    color: var(--bs-secondary-color);
}

/* ───── Ống góp dưới chân bồn ─────
   Vẽ HAI nét chồng nhau: nét viền dày hơn nằm dưới tạo bóng, nét hổ phách nằm trên. Đó là cách nét
   ống trên sơ đồ nguyên lý đang vẽ, giữ cho hai màn hình cùng một giọng. */
.dbk-ong-vien[b-zkynmpcaht] {
    stroke: var(--dbk-ong-vien);
    stroke-width: 11;
    stroke-linecap: round;
}

.dbk-ong[b-zkynmpcaht] {
    stroke: var(--dbk-ong);
    stroke-width: 7;
    stroke-linecap: round;
}

/* ───── Bồn ─────
   🔄 ĐỢT 1186 (việc o373) — anh Tiến: "dùng hình bồn chuẩn đẹp đã thiết kế". Bốn lớp tô của bản vẽ
   tay (.dbk-than · .dbk-nap · .dbk-dai · .dbk-be) ĐÃ GỠ: hình nay lấy thẳng từ `LuatHinhCongNghe`
   và mỗi lớp của nó TỰ MANG màu (fill/stroke đặt trên chính thẻ path).
   ⛔ Vì thế ĐỪNG đặt `fill`/`stroke` cho `.dbk-bon path` ở đây — luật CSS thắng thuộc tính hiển thị
   trên thẻ, tức sẽ tô đè phẳng lì lên cả tám lớp và xóa sạch chỗ đẹp của hình. Bốn biến màu cũ vẫn
   giữ ở khối trên vì `--dbk-ong` (nét ống góp) còn dùng.

   Rê chuột: KHÔNG đổi màu, KHÔNG phóng to — phóng to là chiều cao thôi so được với nhau, tức phá
   đúng thứ hình này sinh ra để làm. Chỉ để con trỏ nói rằng "có chú giải đọc được ở đây". */
.dbk-bon[b-zkynmpcaht] { cursor: help; }

/* ───── Chữ dưới chân bồn ───── */
.dbk-chu text[b-zkynmpcaht] { fill: currentColor; }

.dbk-ten-kho[b-zkynmpcaht] {
    font-size: 12px;
    font-weight: 700;
}

.dbk-m3[b-zkynmpcaht] {
    font-size: 15px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* ⛔ CỠ CHỮ Ở ĐÂY CỐ Ý ĐỨNG NGOÀI SÀN 12px CỦA NGÓN TAY — ⛔ ĐỪNG "vá cho đủ" (việc o1519).
   Đây là thẻ `<text>` NẰM TRONG SVG: `font-size` đo bằng đơn vị của `viewBox`, ⛔ không phải pixel
   màn hình — bản vẽ co giãn theo khung nên 9,5 đơn vị có thể ra to hơn hoặc nhỏ hơn 9,5px. Nâng nó
   lên 12 đơn vị là phóng chữ SO VỚI hình, làm chữ tràn khỏi khối bồn mà ⛔ không giải được gì.
   📏 Chính bộ đo của dự án cũng BỎ QUA chữ trong SVG — `cong-cu/phep-do-khung-hep.mjs` (dòng 185–190,
      chú thích ở đó: bản đầu KÊU OAN 27 nhãn trục biểu đồ ở `/nuoc`). ⇒ Lượt đo TĨNH nào kể chỗ này
      vào danh sách "chữ nhỏ" là một lượt kêu oan, ⛔ không phải một việc còn nợ. */
.dbk-phu[b-zkynmpcaht] {
    font-size: 9.5px;
    opacity: .72;
}
/* /Components/Pages/CongNghe/HopThoaiTuyenCongNghe.razor.rz.scp.css */
/* ═══ HỘP THOẠI CHI TIẾT MỘT TUYẾN ỐNG — luật thu gọn riêng của hộp (đợt 1062, việc o240) ═══

   CSS isolation ⇒ mọi luật ở đây chỉ ăn trong component này, không rơi ra màn hình khác.
   ⛔ KHÔNG cần ::deep: mọi bộ chọn dưới đây trỏ vào phần tử KHAI TRONG CHÍNH tệp .razor này (dù
      chúng được HopThoai dựng ra hộ). ::deep chỉ cần khi muốn với vào ruột của component CON.

   ⚠ Chú thích CSS đóng bằng dấu sao + gạch chéo — ⛔ đừng để cặp ký tự đó lọt vào giữa một câu văn
     (bẫy đợt 852: chú thích đóng sớm, phần văn còn lại dính vào bộ chọn ngay dưới ⇒ trình duyệt bỏ
     NGUYÊN luật đó, không một câu lỗi nào). Bộ dò canh việc này:
     node cong-cu/soi-chu-thich-css.mjs */

/* ── THÂN HỘP: CHIỀU CAO CỐ ĐỊNH cho mọi tab (đợt 1261) ──
   Anh Tiến 25/08/2026: "khi chọn các tab nhỏ trong hội thoại thì kích thước hội thoại giữ nguyên
   tránh thay đổi kích thước quá nhiều gây lóa mắt".

   Số đo: tab Chi tiết ở dữ liệu thật cao ~430px (dải chặng 118 + hai bảng), tab Lịch khi tuyến chưa
   có chuyến nào chỉ ~40px. Chốt ở 58vh, kẹp trong 300–620px:
     · kẹp DƯỚI  — màn hình laptop thấp thì 58vh còn ~330px, dưới nữa là dải chặng phải cuộn dọc;
     · kẹp TRÊN  — màn hình 1440p thì 58vh ~835px, tab Lịch rỗng sẽ là một khoảng trắng mênh mông.

   ⛔ Đừng đổi thành min-height: nó chỉ chặn phía CO LẠI, tab dài vẫn đẩy hộp cao lên. Muốn hộp đứng
     yên thì chiều cao phải KHÔNG phụ thuộc nội dung.
   ⚠ overflow-y: auto đặt ở ĐÂY chứ ⛔ không để nền hộp thoại cuộn: dải tab và tiêu đề phải đứng yên
     tại chỗ khi người dùng cuộn nội dung tab, không thì bấm sang tab khác lại phải cuộn ngược lên. */
.cn-tt-than[b-hplu5unx4l] {
    height: clamp(300px, 58vh, 620px);
    overflow-y: auto;
    overflow-x: hidden;
}

/* Khung dải chặng — CUỘN NGANG trong lòng nó.
   ⚠ Phải là overflow-x ở ĐÂY chứ không phải để trang cuộn: thứ tự chặng LÀ chiều dòng chảy nên dải
     không được xuống dòng, mà một tuyến 12 van thì dài hơn màn hình. */
.cn-tt-dai[b-hplu5unx4l] {
    overflow-x: auto;
    overflow-y: hidden;
}

/* Một chặng — bề ngang CỐ ĐỊNH để mắt đọc ra một nhịp đều, và để hai nhãn dài ngắn khác nhau không
   làm hình nhảy chỗ. 92px = cỡ hình 72 + chỗ thở hai bên. */
.cn-tt-chang[b-hplu5unx4l] {
    flex: 0 0 auto;
    width: 92px;
}

/* Ô "chưa khai loại" — vuông đúng bằng cỡ hình để nhịp dải không vỡ.
   Nét ĐỨT nói "chỗ này có thiết bị nhưng danh mục chưa khai nó là gì", khác hẳn ô trống (= không có
   gì). ⛔ Đừng thay bằng một hình mặc định: vẽ nhầm van là đội giao nhận đọc sai thiết bị ngoài
   hiện trường. */
.cn-tt-trong[b-hplu5unx4l] {
    width: 72px;
    height: 72px;
    margin: 0 auto;
    border: 1px dashed var(--bs-border-color);
}

/* Đoạn ống nối hai chặng liền kề. Màu do nơi vẽ đặt bằng style nội tuyến (màu mặt hàng là DỮ LIỆU,
   không phải một tập lớp cố định). margin-top đẩy nó xuống giữa chiều cao hình. */
.cn-tt-noi[b-hplu5unx4l] {
    flex: 0 0 auto;
    align-self: flex-start;
    width: 26px;
    height: 5px;
    margin-top: 34px;
    border-radius: 3px;
    opacity: .75;
}

/* Nhãn chính (mã hiệu / tên điểm) và dòng phụ. Cả hai cắt đuôi bằng text-truncate ở markup —
   câu đầy đủ nằm trong title của cả ô, đúng luật "hướng dẫn nằm trong title" (đợt 222). */
.cn-tt-nhan[b-hplu5unx4l] {
    font-size: .8125rem;
    line-height: 1.2;
    margin-top: .25rem;
}

.cn-tt-phude[b-hplu5unx4l] {
    font-size: .6875rem;
    line-height: 1.15;
}

/* ── DẢI TAB CỦA HỘP THOẠI (đợt 1080 — việc o263) ──
   Chữ nhỏ hơn một nấc so với dải tab của TRANG để mắt đọc ra ngay đây là tầng thứ hai, nằm bên
   trong một hộp thoại. ⛔ KHÔNG mang lớp mốc `dai-tab-nav`: lớp đó dành riêng cho dải tab đẩy lên
   thanh nav (đợt 976) và hàng rào NutThanhNavDongNhatTests ép cỡ theo bộ biến --nav-nut-*, tức là
   ép đúng cái cỡ mà dải này cố ý không lấy. */
.cn-tt-dai-tab .nav-link[b-hplu5unx4l] {
    font-size: .8125rem;
    line-height: 1.6;
}

/* ─────────── ⭐ SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY (việc o1519) ───────────
   Hai chỗ đo tĩnh dưới sàn `--cham-chu-toi-thieu` (12px, khai ở `app.css`):
   `.cn-tt-phude` .6875rem = 11px · `.cn-tt-trong-chu` .7rem = 11,2px.
   🔑 `.cn-tt-trong-chu` trước đây là `style="font-size:.7rem"` gõ thẳng trong markup — style nội
      tuyến ở (1,0,0) thì ⛔ KHÔNG luật CSS nào của tệp này với tới, nên phải gỡ ra lớp mới vá được.
      Luật NỀN giữ đúng .7rem ⇒ máy tính ⛔ không đổi một pixel.
   📏 ⛔ Không tràn: `.cn-tt-phude` là dòng phụ đề mang `text-truncate` nên nó CẮT chứ ⛔ không nong ô;
      `.cn-tt-trong-chu` nằm trong ô 72×72px, câu dài nhất «chưa khai» ở 12px ≈ 54px, còn dư.
   ⛔ CHỈ trong `@media (pointer: coarse)` — xem `app.css` (đợt 1518). */
.cn-tt-trong-chu[b-hplu5unx4l] {
    font-size: .7rem;
}

@media (pointer: coarse) {
    .cn-tt-phude[b-hplu5unx4l] { font-size: max(.6875rem, var(--cham-chu-toi-thieu)); }
    .cn-tt-trong-chu[b-hplu5unx4l] { font-size: max(.7rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/Pages/CongNghe/KhoiKhaiChuoiVanCongNghe.razor.rz.scp.css */
/* ═══ ĐỢT 2196 (việc o1631) — VÙNG CHẠM CHO Ô TICK CỦA BẢNG KHAI BỔ SUNG ═══
   Ô tick vuông 16px cạnh dòng chữ .8rem là đúng dáng trên máy tính, nhưng ngón tay thì trượt.
   Luật vùng chạm (đợt 1518) bọc trong @media (pointer: coarse) — khổ hẹp KHÔNG có nghĩa là ngón tay
   (cửa sổ thu nhỏ vẫn dùng chuột), và màn rộng vẫn có thể là màn cảm ứng.
   Nới VÙNG BẤM QUANH ô tick chứ không phóng to chính ô: một ô vuông 44px cạnh chữ .8rem trông như lỗi. */

.kcv-o-tick[b-p4sex7baao] {
    text-align: center;
    vertical-align: middle;
}

@media (pointer: coarse) {
    .kcv-o-tick[b-p4sex7baao] {
        /* Ô bảng tự nó thành vùng bấm: chiều cao tối thiểu 44px và ô tick nằm giữa. */
        min-height: var(--cham-toi-thieu, 44px);
        min-width: var(--cham-toi-thieu, 44px);
        padding: 0;
    }

    /* Nhãn phủ trọn ô để cú chạm nào rơi vào ô cũng trúng ô tick — khuôn của TrangBangGiaMucXdcb
       (đợt 1619): hộp 44px CHƯA đủ, phần phủ mới là thứ đo được bằng elementFromPoint. */
    .kcv-o-tick .form-check-input[b-p4sex7baao] {
        margin: calc((var(--cham-toi-thieu, 44px) - 1em) / 2);
    }
}
/* /Components/Pages/CongNghe/KhoiThuVienHinhCongNghe.razor.rz.scp.css */
/* ══════════════════════════════════════════════════════════════════════════════
   KHỐI THƯ VIỆN HÌNH CÔNG NGHỆ — CSS cách ly
   Tệp này mở ở việc `o1519` cho ĐÚNG MỘT việc: gỡ `style="font-size:.7rem"` gõ thẳng trong markup
   ra một lớp, để còn nâng được sàn cỡ chữ khi cầm bằng ngón tay. Style nội tuyến có độ đặc hiệu
   (1,0,0) nên ⛔ KHÔNG luật CSS nào với tới — đó là lý do phải gỡ, ⛔ không phải để làm đẹp thêm.
   ══════════════════════════════════════════════════════════════════════════════ */

/* Nhãn dưới mỗi bản thu nhỏ: «@co · N lớp». */
.ktvh-nhan-co[b-v6wndua6s8] {
    font-size: .7rem;
}

/* ─────────── ⭐ SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY (việc o1519) ───────────
   `.ktvh-nhan-co` đo tĩnh .7rem = 11,2px, dưới sàn `--cham-chu-toi-thieu` (12px, khai ở `app.css`).
   📏 ⛔ Không tràn: nhãn nằm dưới một hình vuông trong dải `flex-wrap`, câu dài nhất «64 · 4 lớp»
      ở 12px ≈ 62px — hình nhỏ nhất trong dải đã rộng 48px và dải được phép xuống dòng.
   🔑 Đọc BIẾN, ⛔ đừng gõ thẳng 12px. ⛔ CHỈ trong `@media (pointer: coarse)` — xem `app.css` (đợt 1518). */
@media (pointer: coarse) {
    .ktvh-nhan-co[b-v6wndua6s8] { font-size: max(.7rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/Pages/CongNghe/MaTranNhomThietBi.razor.rz.scp.css */
/* ═══ MA TRẬN QUYỀN NHÓM THIẾT BỊ — việc o1895 ═══
   Chép khuôn MaTranChucNang.razor.css (CSS cách ly: luật của tệp ấy ⛔ với tới component này). Bảng cố ý cuộn
   ngang nên neo HAI thứ: cột HỌ TÊN / PHÒNG bên trái, hàng TIÊU ĐỀ bên trên.
   ⚠ Khung cuộn phải có TRẦN CHIỀU CAO, không thì `position: sticky` của hàng tiêu đề ⛔ có gì để bám.
   ⚠ Hai bảng mang lớp `bang-dinh-dau`: luật đợt 400 của app.css biến mọi `.content table.table` thành khung cuộn
     THỨ HAI ở khổ hẹp, CHỪA đúng lớp ấy — thiếu nó là tiêu đề dính bám nhầm khung (bài học đợt 2305 ①). */

.mtnt-khung[b-wfnuir2c8k] {
    max-height: 68vh;
    max-height: 68dvh; /* dòng lui cho điện thoại — luật đợt 1500 */
    overflow: auto;
}

.mtnt-bang[b-wfnuir2c8k] {
    /* Cột nhóm co theo nội dung, phần dôi ra dồn cho cột tên (luật ① đợt 2154). */
    width: auto;
    min-width: 100%;
}

.mtnt-bang thead th[b-wfnuir2c8k] {
    position: sticky;
    top: 0;
    z-index: 3;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
    vertical-align: bottom;
}

/* Cột HỌ TÊN / PHÒNG: cột to nhất, neo trái. */
.mtnt-cot-ten[b-wfnuir2c8k] {
    position: sticky;
    left: 0;
    z-index: 2;
    min-width: 15rem;
    background-color: var(--bs-body-bg, #fff);
}

/* Góc trên bên trái vừa neo trái vừa neo trên ⇒ phải nổi trên cả hai. */
.mtnt-bang thead th.mtnt-cot-ten[b-wfnuir2c8k] {
    z-index: 4;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

/* Cột nhóm: đầu cột in TÊN ĐẦY ĐỦ (luật ghi chú 02/09 bỏ `title` bù ở đầu cột) nên cho XUỐNG DÒNG trong một
   trần bề ngang — tám tên nhóm nằm một dòng thì mỗi cột rộng cả tấc và cột Họ tên mất chỗ. */
.mtnt-cot-nhom[b-wfnuir2c8k] {
    width: 1%;
    min-width: 5.5rem;
    max-width: 8.5rem;
    white-space: normal;
}

.mtnt-nhan[b-wfnuir2c8k] {
    display: block;
    font-size: .8rem;
    line-height: 1.15;
}

/* Hai nút ✔ / ✕ tick cả cột — ép nhỏ để ⛔ nới bề ngang cột nhóm. */
.mtnt-nut-cot[b-wfnuir2c8k] {
    display: flex;
    gap: .15rem;
    justify-content: center;
    margin-top: .15rem;
}

.mtnt-nut-cot .btn[b-wfnuir2c8k] {
    font-size: .7rem;
    line-height: 1.1;
}

/* ─── Sọc dòng TỰ TÔ (khuôn MaTranChucNang, gốc BangHoSoNhanSu đợt 2305) ───
   Hàng mang lớp mtnt-table-hang nên luật sọc chung của app.css CHỪA bảng này: luật ấy tô nền BÁN TRONG SUỐT lên
   cả ô dính, cuộn ngang là ô tick các cột bên dưới LỘ XUYÊN qua tên. Ô dính giữ nền ĐẶC, sọc phủ bằng lớp ảnh. */
.mtnt-bang > tbody > tr:nth-child(even) > td[b-wfnuir2c8k] {
    background-color: rgba(0, 0, 0, .028);
}

:root[data-theme="dark"] .mtnt-bang > tbody > tr:nth-child(even) > td[b-wfnuir2c8k] {
    background-color: rgba(255, 255, 255, .045);
}

.mtnt-bang > tbody > tr:nth-child(even) > td.mtnt-cot-ten[b-wfnuir2c8k] {
    background-color: var(--bs-body-bg, #fff);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtnt-bang > tbody > tr:nth-child(even) > td.mtnt-cot-ten[b-wfnuir2c8k] {
    background-color: var(--bs-body-bg, #212529);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}

/* ─── DÒNG ĐẦU CỦA PHÒNG (anh Tiến chốt: «mỗi phòng một dòng đầu để tick/bỏ cả phòng») ───
   Nền ĐẶC riêng + vạch trên đậm để mắt đọc được ranh giới nhóm. ⚠ Bốn luật dưới phải đứng SAU bốn luật sọc:
   cùng độ đặc hiệu, thắng thua theo THỨ TỰ — đứng trước là dòng đầu chẵn lĩnh nền sọc (hỏng lặng lẽ). */
.mtnt-bang > tbody > tr.mtnt-hang-phong > td[b-wfnuir2c8k] {
    background-color: var(--bs-secondary-bg, #e9ecef);
    border-top: 2px solid var(--bs-border-color, #dee2e6);
}

:root[data-theme="dark"] .mtnt-bang > tbody > tr.mtnt-hang-phong > td[b-wfnuir2c8k] {
    background-color: var(--bs-secondary-bg, #343a40);
}

.mtnt-bang > tbody > tr.mtnt-hang-phong > td.mtnt-cot-ten[b-wfnuir2c8k] {
    background-color: var(--bs-secondary-bg, #e9ecef);
    background-image: none;
}

:root[data-theme="dark"] .mtnt-bang > tbody > tr.mtnt-hang-phong > td.mtnt-cot-ten[b-wfnuir2c8k] {
    background-color: var(--bs-secondary-bg, #343a40);
    background-image: none;
}

/* Chữ nhỏ dưới ô ✔ MỜ — nói quyền đến từ đâu («cả phòng» · «mọi nhóm»). Kênh thứ hai bên cạnh độ mờ: màu ⛔
   được là kênh duy nhất (luật ② đợt 2154). `display: block` để nằm DƯỚI ô vuông, ⛔ nới bề ngang cột. */
.mtnt-nguon[b-wfnuir2c8k] {
    display: block;
    font-size: .65rem;
    line-height: 1;
    color: var(--bs-secondary-color, #6c757d);
    white-space: nowrap;
}

/* Chữ nổi tự ẩn (khuôn đợt 2281): `position: fixed` nên ⛔ chiếm chỗ trong bố cục; `pointer-events: none` để
   ⛔ nuốt cú bấm rơi trúng nó. */
.mtnt-chu-noi[b-wfnuir2c8k] {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    max-width: 22rem;
    padding: .5rem .9rem;
    border-radius: .5rem;
    background: var(--bs-success-bg-subtle, #d1e7dd);
    color: var(--bs-success-text-emphasis, #0a3622);
    border: 1px solid var(--bs-success-border-subtle, #a3cfbb);
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
    pointer-events: none;
    animation: mtnt-hien-b-wfnuir2c8k .18s ease-out;
}

@keyframes mtnt-hien-b-wfnuir2c8k {
    from { opacity: 0; transform: translateY(.5rem); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .mtnt-chu-noi[b-wfnuir2c8k] { animation: none; }
}

/* ─── VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (khuôn o737 · o962) ───
   LUẬT NỀN tắt hộp của thẻ bọc ở chế độ CHUỘT — để nó sinh hộp dòng là mở một vùng bấm vô hình quanh ô vuông.
   ⚠ Luật nền phải đứng TRƯỚC khối chạm: hai luật cùng độ đặc hiệu, thắng thua theo THỨ TỰ.
   ⚠ Điều kiện là CÁI CHẠM VÀO (pointer coarse), ⛔ bề ngang; ngưỡng đọc từ biến dùng chung ở app.css. */
.o-tick-cham[b-wfnuir2c8k] {
    display: contents;
}

@media (pointer: coarse) {
    .mtnt-nut-cot .btn[b-wfnuir2c8k] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
    }

    .o-tick-cham[b-wfnuir2c8k] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/CongNghe/TabTongQuanCongNghe.razor.rz.scp.css */
/* ═══ TAB 📊 TỔNG QUAN CÔNG NGHỆ — THU GỌN + BẢNG ĐIỀU KHIỂN ═══

   🔴 ĐỢT 1161 (việc o342) — anh Tiến 24/08/2026: dashboard công nghệ quá giống dashboard nước, đổi
      phong cách đồ họa cho hợp phân hệ. Tệp này trước đó tự khai là chép luôn bộ luật thu gọn của
      dashboard nước; ⛔ đừng viết lại câu như thế, nó dẫn lượt sửa sau đi chép tiếp.

   ⛔ VÌ SAO ĐỂ Ở ĐÂY MÀ KHÔNG PHẢI `app.css`: đây là CSS ISOLATION — Blazor tự gắn dấu phạm vi nên
      mọi luật dưới đây CHỈ ăn trong tab Tổng quan của phân hệ công nghệ. Đưa chúng vào `app.css` là
      đụng chỗ dùng chung, đúng diện rủi ro lan truyền của quy tắc #18.

   ⓘ `::deep` là bắt buộc với mọi thứ nằm trong component CON: dấu phạm vi chỉ được gắn cho phần tử
      viết trong chính `TabTongQuanCongNghe.razor`.

   ⚠ BẪY GIỮ NGUYÊN GIÁ TRỊ: mọi lớp TIỆN ÍCH của Bootstrap (`py-2`, `fs-3`…) đều mang `!important`.
     Luật ở đây tuy trọng số cao hơn nhưng KHÔNG có `!important` thì vẫn thua — lượt đo đầu của đợt
     694 thấy hình nhỏ đi mà lề và cỡ chữ y nguyên, không một lỗi nào báo.
   ⚠ ⛔ Không viết cặp ký tự đóng chú thích vào giữa câu văn — chú thích đóng ngay tại đó, phần còn
     lại dính vào bộ chọn ngay dưới và trình duyệt bỏ NGUYÊN luật đó, build vẫn xanh (đợt 852). */

/* ───── 1. ĐẦU THẺ + THÂN THẺ: bớt lề, giữ nguyên cỡ chữ tiêu đề ───── */
.gon-mot-khung-nhin .card-header[b-an5lvbw183],
.gon-mot-khung-nhin[b-an5lvbw183]  .card-header {
    padding-top: .1rem !important;
    padding-bottom: .1rem !important;
}

.gon-mot-khung-nhin .card-body[b-an5lvbw183],
.gon-mot-khung-nhin[b-an5lvbw183]  .card-body {
    padding-top: .3rem !important;
    padding-bottom: .3rem !important;
}

/* ───── 2. BIỂU ĐỒ CỘT "chuyến bơm 7 ngày": chặn trần chiều cao ─────
   Hình vẽ theo tỷ lệ khung (viewBox 320×90, `height:auto`) nên cột càng rộng hình càng cao. Chặn
   trần + `height:auto` giữ nguyên tỷ lệ, chỉ hẹp lại. */
.gon-mot-khung-nhin .o-bieu-do-cot svg[b-an5lvbw183] {
    max-height: 92px;
}

/* ───── 3. CHỮ DANH SÁCH CẢNH BÁO: nhỏ một nấc ─────
   ⚠ CHỈ nhắm danh sách cảnh báo — KHÔNG đụng con số nào. */
.gon-mot-khung-nhin .list-unstyled[b-an5lvbw183] {
    font-size: .82rem;
    line-height: 1.25;
}

/* ───── 4. CỘT PHẢI: hàng trong hụt đúng một khe dọc ─────
   `.row` của Bootstrap mang `margin-top: calc(-1 * var(--bs-gutter-y))`, trong khi `h-100` chỉ đặt
   chiều cao BẰNG cột cha ⇒ hàng bắt đầu cao hơn thì cũng kết thúc sớm bấy nhiêu, đáy hai cột lởm chởm.
   Cộng trả lại phần margin âm đó là hai đáy trùng nhau (cách đã đo ở đợt 747).
   ⚠ Phải có `!important`: `.h-100` của Bootstrap là `height: 100% !important`. */
.gon-mot-khung-nhin .row.g-2.h-100[b-an5lvbw183] {
    height: calc(100% + var(--bs-gutter-y)) !important;
}

/* ═══════════ 5. BẢNG ĐIỀU KHIỂN TRẠNG THÁI VẬN HÀNH ═══════════
   🔄 ĐỢT 1186 (việc o373) — ANH TIẾN ĐỔI Ý, nguyên văn 24/08/2026 kèm ảnh chụp màn hình:
      "có ô nền đen lạc tông".

   🔴 Nền đen là quyết định CỐ Ý của đợt 1161 ("khối TỐI trên dashboard nền sáng… phải bắt mắt ngay,
      và đây là dấu hiệu hình tách dashboard này khỏi dashboard nước"). Lý lẽ ấy nghe xuôi lúc viết,
      nhưng người dùng nhìn cả trang một lượt thì nó đọc ra là MỘT MẢNG LẠC chứ không phải một điểm
      nhấn — mà cái phải phục vụ là mắt anh, ⛔ không phải lý lẽ trong chú thích.

   🔑 GIỮ Ý ĐỒ "bắt mắt ngay" BẰNG THỨ KHÁC, ⛔ đừng bỏ luôn: điểm nhấn nay là ĐÈN BÁO (chấm tròn xanh
      thở nhẹ khi đang bơm) và ba ĐỒNG HỒ số bề ngang đều. Hai thứ đó vốn đã là chỗ mắt dừng lại; cái
      nền đen chỉ đang át chúng.

   🎨 MÀU LẤY BẰNG TOKEN BOOTSTRAP 5.3, ⛔ không đóng đinh mã màu: khối phải đi theo cả nền sáng lẫn
      nền tối, mà token thì tự đổi theo `data-bs-theme` (cầu nối đặt ở App.razor — quy tắc #5). Nhờ vậy
      khối này KHÔNG còn cần luật riêng cho nền tối, và cũng không còn phải đua độ đặc hiệu với luật
      `:root[data-theme="dark"] .card` của `app.css` — thứ đã cắn ở đợt 1161. Ít luật hơn, ít chỗ hỏng hơn.

   ⚠ Khai biến TRÊN CHÍNH KHUNG chứ ⛔ không phải trên `:root` trần — khai `:root` trần có độ đặc hiệu
     BẰNG `[data-bs-theme=dark]` của Bootstrap mà `app.css` nạp SAU (bài học đợt 831). */
.bdk-bang[b-an5lvbw183] {
    --bdk-nen-dh: var(--bs-tertiary-bg);
    --bdk-phu: var(--bs-secondary-color);
    --bdk-vien: var(--bs-border-color);
    --bdk-chay: #2f9e44;
    --bdk-cho: #e0a800;
    --bdk-tat: #adb5bd;
}

/* Đầu thẻ giữ nguyên dáng thẻ của tab — cùng một khuôn với ba khối còn lại. */
.bdk-bang .bdk-dau[b-an5lvbw183] {
    border-bottom-color: var(--bdk-vien);
}

/* ───── Đèn báo ───── */
.bdk-hang-den[b-an5lvbw183] {
    padding: .3rem .1rem .45rem;
}

.bdk-den[b-an5lvbw183] {
    width: 1.05rem;
    height: 1.05rem;
    border-radius: 50%;
    flex-shrink: 0;
    background: var(--bdk-tat);
    box-shadow: inset 0 0 0 2px rgba(0, 0, 0, .35);
}

/* Đèn ĐANG BƠM thở nhẹ — dấu hiệu "số này đang sống", ⛔ không phải trang trí. */
.bdk-den-chay[b-an5lvbw183] {
    background: var(--bdk-chay);
    box-shadow: inset 0 0 0 2px rgba(0, 0, 0, .3), 0 0 10px 2px var(--bdk-chay);
    animation: bdk-tho-b-an5lvbw183 1.9s ease-in-out infinite;
}

.bdk-den-cho[b-an5lvbw183] {
    background: var(--bdk-cho);
    box-shadow: inset 0 0 0 2px rgba(0, 0, 0, .3), 0 0 7px 1px var(--bdk-cho);
}

@keyframes bdk-tho-b-an5lvbw183 {
    0%, 100% { opacity: 1; }
    50% { opacity: .55; }
}

/* ⚠ Bắt buộc: người đặt máy giảm chuyển động thì đèn phải ĐỨNG YÊN mà VẪN sáng — tắt hẳn hiệu ứng
   bằng cách bỏ màu là mất luôn thông tin. */
@media (prefers-reduced-motion: reduce) {
    .bdk-den-chay[b-an5lvbw183] { animation: none; }
}

.bdk-chu-den[b-an5lvbw183] {
    font-weight: 700;
    font-size: .92rem;
    letter-spacing: .02em;
}

.bdk-phu[b-an5lvbw183] {
    font-size: .76rem;
    color: var(--bdk-phu);
}

.bdk-so-den[b-an5lvbw183] {
    font-size: 1.7rem;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

/* ───── Ba đồng hồ: tuyến · van · bơm ───── */
.bdk-hang-dh[b-an5lvbw183] {
    margin-top: .1rem;
}

.bdk-dh[b-an5lvbw183] {
    background: var(--bdk-nen-dh);
    border: 1px solid var(--bdk-vien);
    border-radius: .3rem;
    padding: .2rem .1rem;
    cursor: help;
}

.bdk-dh-so[b-an5lvbw183] {
    font-size: 1.12rem;
    font-weight: 700;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

.bdk-dh-ten[b-an5lvbw183] {
    font-size: .68rem;
    color: var(--bdk-phu);
}

.bdk-lien-kho[b-an5lvbw183] {
    margin-top: .25rem;
}

/* ⭐ ĐỢT 1339 (việc o540) — dòng "🚒 không kể hệ chữa cháy: N tuyến · N van ↗".
   Nó là CHÚ THÍCH cho ba đồng hồ ngay trên, ⛔ không phải một con số nữa ⇒ cùng cỡ chữ với dòng
   "liên kho" và đứng sát nó, đừng để nó trông như một dòng số liệu thứ hai.
   ⚠ Liên kết bên trong CỐ Ý thừa hưởng màu chữ phụ chứ ⛔ không lấy màu liên kết mặc định: xanh
     lam ở đây kéo mắt khỏi ba đồng hồ, mà nó chỉ là lối đi phụ sang app PCCC. */
.bdk-pccc[b-an5lvbw183] {
    margin-top: .1rem;
}

    .bdk-pccc a[b-an5lvbw183] {
        color: var(--bdk-phu);
        text-decoration: none;
    }

        .bdk-pccc a:hover[b-an5lvbw183],
        .bdk-pccc a:focus-visible[b-an5lvbw183] {
            text-decoration: underline;
        }

/* 🔄 ĐỢT 1186 — ĐÃ GỠ luật ghi đè màu nút "Chi tiết →" của đợt 1161. Nó dựng cho NỀN TỐI (chữ
   #cbd5e1, viền trắng mờ); khối nay về nền thường nên để nguyên `btn-outline-primary` là ĐÚNG — giữ
   lại thì nút hóa xám nhạt trên nền trắng, tức rớt tương phản đúng cái điều luật cũ đi tránh.
   ⚠ Ba nút "Chi tiết →" của tab phải NHÌN GIỐNG NHAU: khối này từng là ngoại lệ duy nhất. */

/* ─────────── ⭐ o1276 (05/09/2026) — VÙNG CHẠM 44px CHO LIÊN KẾT «🚒 không kể hệ chữa cháy» ───────────
   📏 Đo sống 05/09/2026 (Chrome thật, 390×844, cảm ứng, `tienvm`, `do-song-khung-hep.mjs --toan-app`):
      `/cong-nghe` còn đúng MỘT vùng bấm dưới ngưỡng — thẻ `a` trong `.bdk-pccc` đo **332 × 18,2px**
      (chữ phụ cỡ nhỏ, cao đúng bằng con chữ). Đây là lối đi sang app 🚒 PCCC.
   🔑 `inline-flex` + `min-height` đọc biến ngưỡng: hộp bấm cao lên, chữ canh giữa, màu và gạch chân
      của luật gốc ở trên giữ nguyên. ⛔ Không đổi cỡ chữ — dòng này CỐ Ý nhỏ (xem lời dẫn `.bdk-pccc`).
   🔒 Trong `@media (pointer: coarse)` — máy tính ⛔ không đổi một pixel (chốt ngược `--chuot`).
   🚧 Hàng rào: TKNB.Tests/VungChamPhanTrangVaThongKeTests.cs */
@media (pointer: coarse) {
    .bdk-pccc a[b-an5lvbw183] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }
}

/* ─────────── ⭐ SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY (việc o1519) ───────────
   `.bdk-dh-ten` đo tĩnh .68rem = 10,88px, dưới sàn `--cham-chu-toi-thieu` (12px, khai ở `app.css`).
   📏 ⛔ Không tràn: ba nhãn ấy («🔗 tuyến» · «⬡ van» · «⬢ bơm») nằm mỗi cái một `col-4`, dài nhất là
      7 ký tự ⇒ ở 12px ≈ 50px, còn `col-4` ở khổ 375px đã rộng ~116px.
   ⛔ Đây là ô KHÁC với `.bdk-pccc` ngay trên — dòng ấy CỐ Ý nhỏ và luật của nó ⛔ không đụng cỡ chữ.
   🔑 Đọc BIẾN, ⛔ đừng gõ thẳng 12px. ⛔ CHỈ trong `@media (pointer: coarse)` — xem `app.css` (đợt 1518). */
@media (pointer: coarse) {
    .bdk-dh-ten[b-an5lvbw183] { font-size: max(.68rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/Pages/CongNghe/TabTongQuanPccc.razor.rz.scp.css */
/* ═══ TAB 📊 TỔNG QUAN CỦA APP 🚒 CÔNG NGHỆ PCCC (đợt 1556, việc o796) ═══

   ⛔ VÌ SAO ĐỂ Ở ĐÂY MÀ KHÔNG PHẢI `app.css`: đây là CSS ISOLATION — Blazor tự gắn dấu phạm vi nên mọi
      luật dưới đây CHỈ ăn trong tab Tổng quan của app PCCC. Đưa chúng vào `app.css` là đụng chỗ dùng
      chung, đúng diện rủi ro lan truyền của quy tắc #18.

   🎨 MÀU LẤY BẰNG TOKEN BOOTSTRAP 5.3, ⛔ không đóng đinh mã màu: khối phải đi theo cả nền sáng lẫn nền
      tối, mà token thì tự đổi theo `data-bs-theme` (cầu nối đặt ở App.razor — quy tắc #5). Nhờ vậy khối
      này KHÔNG cần một luật riêng nào cho nền tối.

   ⚠ KHAI BIẾN TRÊN CHÍNH KHUNG `.tqp-gon`, ⛔ không phải trên `:root` trần — khai `:root` trần có độ
     đặc hiệu BẰNG `[data-bs-theme=dark]` của Bootstrap mà `app.css` nạp SAU, tức dòng trần thắng cả ở
     nền tối và ghim màu ở bảng nền sáng (bài học đợt 831).

   ⚠ BẪY GIỮ NGUYÊN GIÁ TRỊ: mọi lớp TIỆN ÍCH của Bootstrap (`py-2`, `small`…) đều mang `!important`.
     Luật ở đây tuy trọng số cao hơn nhưng KHÔNG có `!important` thì vẫn thua.

   ⚠ ⛔ Không gõ cặp ký tự đóng chú thích vào giữa câu văn — chú thích đóng ngay tại đó, phần còn lại
     dính vào bộ chọn ngay dưới và trình duyệt bỏ NGUYÊN luật đó, build vẫn xanh (đợt 852 · 1481).
     Soi lại bằng `node cong-cu/soi-chu-thich-css.mjs` sau mỗi lượt sửa tệp này.

   ⚠ ⛔ Đừng viết `@media (min-width: …) and (max-width: …)` trong tệp `.razor.css` (đợt 1481) — tệp này
     cố ý KHÔNG có câu media nào, bố cục co giãn hoàn toàn bằng lưới Bootstrap. */

.tqp-gon[b-6mz2k56v65] {
    --tqp-phu: var(--bs-secondary-color);
    --tqp-vien: var(--bs-border-color);
    --tqp-nen-thanh: var(--bs-tertiary-bg);
    --tqp-chay: #2f9e44;
    --tqp-tat: #adb5bd;
    --tqp-thieu: #b8860b;
}

/* ───── 1. THU GỌN THẺ — cả tab đọc trong một khung nhìn ───── */
.tqp-gon .card-header[b-6mz2k56v65] {
    padding-top: .15rem;
    padding-bottom: .15rem;
}

/* ═══════════ 2. BỐN Ô ĐO ═══════════
   Ô mang một vạch màu bên trái để phân biệt bốn câu hỏi bằng mắt, ⛔ không dùng nền màu:
   nền màu trên dashboard nền sáng đọc ra là MỘT MẢNG LẠC chứ không phải điểm nhấn (bài học đợt 1186). */
.tqp-gon .tqp-o[b-6mz2k56v65] {
    border-left-width: 4px;
    border-left-style: solid;
}

.tqp-gon .tqp-o-tram[b-6mz2k56v65] { border-left-color: #c92a2a; }
.tqp-gon .tqp-o-bom[b-6mz2k56v65] { border-left-color: #e8590c; }
.tqp-gon .tqp-o-tuyen[b-6mz2k56v65] { border-left-color: #1c7ed6; }
.tqp-gon .tqp-o-van[b-6mz2k56v65] { border-left-color: #5f3dc4; }

.tqp-gon .tqp-o-ten[b-6mz2k56v65] {
    font-size: .8rem;
    font-weight: 600;
    color: var(--tqp-phu);
    line-height: 1.2;
}

/* Chữ số đều bề ngang để bốn ô đọc như một hàng đồng hồ, ⛔ không nhảy khi số đổi. */
.tqp-gon .tqp-con-so[b-6mz2k56v65] {
    font-size: 1.9rem;
    font-weight: 700;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

.tqp-gon .tqp-o-phu[b-6mz2k56v65] {
    font-size: .78rem;
    color: var(--tqp-phu);
    line-height: 1.25;
}

/* Sắc vàng đất: đây là CẢNH BÁO SỐ LIỆU (còn thiếu), ⛔ không phải màu lỗi đỏ — phần mềm chỉ nêu con
   số và đề nghị bổ sung, ⛔ không quy kết ai. */
.tqp-gon .tqp-thieu[b-6mz2k56v65] {
    color: var(--tqp-thieu);
    font-weight: 600;
}

/* Lối sang trang khác: thẻ <a> và thẻ <button> phải trông y hệt nhau — người dùng ⛔ không cần biết
   cái nào đổi trang, cái nào đổi tab. */
.tqp-gon .tqp-lien[b-6mz2k56v65] {
    display: inline-block;
    margin-top: .2rem;
    padding: 0;
    border: 0;
    background: none;
    font-size: .76rem;
    color: var(--bs-link-color);
    text-decoration: none;
}

.tqp-gon .tqp-lien:hover[b-6mz2k56v65] {
    text-decoration: underline;
}

/* ───── 3. ĐÈN BÁO TUYẾN ĐANG VẬN HÀNH ───── */
.tqp-gon .tqp-den[b-6mz2k56v65] {
    display: inline-block;
    width: .55rem;
    height: .55rem;
    border-radius: 50%;
    margin-right: .25rem;
    vertical-align: baseline;
}

.tqp-gon .tqp-den-chay[b-6mz2k56v65] { background: var(--tqp-chay); }
.tqp-gon .tqp-den-tat[b-6mz2k56v65] { background: var(--tqp-tat); }

/* ═══════════ 4. DẢI "CÒN KHUYẾT" ═══════════
   Một dải huy hiệu MỘT DÒNG, ⛔ không phải danh sách gạch đầu dòng — lý do đầy đủ ở đầu tệp .razor. */
.tqp-gon .tqp-khuyet-dau[b-6mz2k56v65] {
    font-size: .82rem;
    font-weight: 700;
    color: var(--tqp-thieu);
}

.tqp-gon .tqp-the[b-6mz2k56v65] {
    font-size: .76rem;
    font-weight: 500;
    color: var(--bs-body-color);
    background: var(--tqp-nen-thanh);
    border: 1px solid var(--tqp-vien);
    cursor: help;
}

.tqp-gon .tqp-du[b-6mz2k56v65] {
    font-size: .82rem;
    color: var(--tqp-phu);
}

/* ⚠ ⛔ Không hạ dưới .76rem (≈12,2px): app khai sàn chữ đọc được là `--cham-toi-thieu-chu: 12px`
   (đợt 1518). Sàn ấy chỉ ĐƯỢC ÉP dưới `@media (pointer: coarse)`, nhưng hạ dưới nó ở đây là tự đặt
   một quả mìn cho ngày trang này lọt vào lượt đo màn hình điện thoại. */
.tqp-gon .tqp-the-chay[b-6mz2k56v65] {
    font-size: .76rem;
    color: #fff;
    background: var(--tqp-chay);
}

/* ═══════════ 5. HAI BẢNG ═══════════ */
.tqp-gon .tqp-bang[b-6mz2k56v65] {
    font-size: .84rem;
}

.tqp-gon .tqp-bang th[b-6mz2k56v65] {
    font-size: .76rem;
    font-weight: 600;
    color: var(--tqp-phu);
    white-space: nowrap;
}

.tqp-gon .tqp-bang td[b-6mz2k56v65],
.tqp-gon .tqp-bang th[b-6mz2k56v65] {
    padding-top: .2rem;
    padding-bottom: .2rem;
}

/* Số trạm đã chấm đứng ngay cạnh tổng số, cỡ nhỏ hơn một nấc — đọc ra là "trong số đó". */
.tqp-gon .tqp-cham[b-6mz2k56v65] {
    font-size: .76rem;
    color: var(--tqp-phu);
    margin-left: .2rem;
}

/* Thanh so sánh: mắt so ĐỘ DÀI chính xác hơn so góc quạt, và dải bo tròn hai đầu trông ra nét ống —
   cùng ngôn ngữ hình với sơ đồ nguyên lý của phân hệ. */
.tqp-gon .tqp-thanh[b-6mz2k56v65] {
    flex: 1 1 auto;
    min-width: 3rem;
    height: .5rem;
    border-radius: .25rem;
    background: var(--tqp-nen-thanh);
    overflow: hidden;
}

.tqp-gon .tqp-thanh-hep[b-6mz2k56v65] {
    max-width: 6rem;
}

.tqp-gon .tqp-thanh-day[b-6mz2k56v65] {
    display: block;
    height: 100%;
    border-radius: .25rem;
    background: #1c7ed6;
}

.tqp-gon .tqp-thanh-so[b-6mz2k56v65] {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    min-width: 2rem;
    text-align: right;
}

.tqp-gon .tqp-loai li[b-6mz2k56v65] {
    padding: .15rem 0;
}

.tqp-gon .tqp-chu-them[b-6mz2k56v65] {
    font-size: .76rem;
    color: var(--tqp-phu);
    padding-top: .25rem;
}
/* /Components/Pages/CongNghe/ThanhOngMatHangCongNghe.razor.rz.scp.css */
/* ═══ THANH ỐNG XẾP CHỒNG THEO MẶT HÀNG — ĐỢT 1161 (việc o342) ═══
   ⛔ CSS isolation: mọi luật dưới đây chỉ ăn trong component này (quy tắc #18 — ⛔ đừng dời sang
      app.css cho "gọn", đó là đụng chỗ dùng chung).
   ⚠ ⛔ Không viết cặp ký tự đóng chú thích vào giữa câu văn — chú thích đóng ngay tại đó, đoạn còn lại
     dính vào bộ chọn ngay dưới và trình duyệt bỏ NGUYÊN luật đó, không một câu lỗi (bài học đợt 852). */

.tomh-khung[b-ocgpxbgbk1] {
    --tomh-vien: rgba(0, 0, 0, .35);
    --tomh-vach: rgba(255, 255, 255, .55);
}

:root[data-theme="dark"] .tomh-khung[b-ocgpxbgbk1] {
    --tomh-vien: rgba(255, 255, 255, .28);
    --tomh-vach: rgba(0, 0, 0, .45);
}

.tomh-tong[b-ocgpxbgbk1] {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 1.02rem;
}

.tomh-dv[b-ocgpxbgbk1] {
    font-weight: 400;
    font-size: .8rem;
    color: var(--bs-secondary-color);
}

/* ───── Thanh ống ───── */
.tomh-vien[b-ocgpxbgbk1] {
    fill: none;
    stroke: var(--tomh-vien);
    stroke-width: 1.2;
}

.tomh-vach[b-ocgpxbgbk1] {
    stroke: var(--tomh-vach);
    stroke-width: 1;
}

.tomh-manh[b-ocgpxbgbk1] { cursor: help; }

/* Chữ trên mảnh: trắng + viền đục. ⚠ `paint-order: stroke` là BẮT BUỘC — thiếu nó thì nét viền vẽ ĐÈ
   lên ruột chữ và chữ hoá ra nhoè, đúng lúc cần đọc nhất. */
/* ⛔ CỠ CHỮ Ở ĐÂY CỐ Ý ĐỨNG NGOÀI SÀN 12px CỦA NGÓN TAY — ⛔ ĐỪNG "vá cho đủ" (việc o1519).
   Cùng lẽ với `.dbk-phu` ở `DaiBonKhoCongNghe.razor.css`: đây là `<text>` trong SVG nên `font-size`
   đo bằng đơn vị `viewBox`, ⛔ không phải pixel màn hình; chữ lại nằm ĐÈ trên mảnh ống nên phóng lên
   là tràn ra ngoài mảnh. Bộ đo `cong-cu/phep-do-khung-hep.mjs` (dòng 185–190) cũng bỏ qua chữ SVG. */
.tomh-chu[b-ocgpxbgbk1] {
    fill: #fff;
    stroke: rgba(0, 0, 0, .45);
    stroke-width: 2.4;
    paint-order: stroke;
    font-size: 10.5px;
    font-weight: 700;
}

/* ───── ĐỢT 1214 — DÃY CHÚ GIẢI DƯỚI THANH ĐÃ GỠ ─────
   Anh Tien 24/08/2026: "o trang tong quan, bo chu thich ... va chu thich tuong tu o bieu do o duoi".
   Năm luật của dãy đó (.tomh-chu-giai · .tomh-muc · .tomh-cham · .tomh-ten · .tomh-so) gỡ theo, ⛔ không
   để lại luật chết. Tên và trị số của mảnh nay đọc bằng cách rê chuột vào mảnh — đúng luật đợt 222
   (hướng dẫn nằm trong title, ⛔ không in thường trực ra màn hình). */
/* /Components/Pages/CongNghe/TrangCongNghe.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — 7 Ô TICK CỦA TRANG CÔNG NGHỆ NHẬP XUẤT (đợt 1763, việc o962) ═══
   Màn hình: /cong-nghe — ngăn 🧱 Danh mục của tab 🧰 Thiết bị (`?tab=danh-muc`).

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài — nâng chính ô vuông lên 44px thì nó to bằng cả dòng chữ bên cạnh, trông như lỗi giao
   diện. Thứ phải nới là VÙNG CHẠM QUANH ô, mà vùng ấy mỗi màn hình một khác nên phải nới tại
   chỗ, bằng CSS cách ly.

   📏 ĐO SỐNG TRƯỚC KHI VÁ — 02/09/2026, Chrome thật, khung 390×844 cảm ứng:
     node cong-cu/do-song-o-tick-cham.mjs --trang "/cong-nghe?tab=danh-muc"
     Cả 3 ô đo được cùng một bộ số — «Chỉ đang dùng» · «Mái phao» · «Đang dùng»:
     cấu trúc ① NHÃN BỌC · ô vuông 16×16 (float: left) · nhãn cao 24 · hộp `.form-check` cao 24
     (display: block) · VÙNG BẤM THẬT tại cột ô tick 25px · tại cột chữ 25px · lệch tâm dọc 0px.
   ⚠ Lượt đo chỉ với tới 3 trong 7 ô: bốn ô còn lại nằm ở các thẻ danh mục 🔧 Tuyến · Van · Bầu
     lược · Máy bơm, mỗi thẻ phải BẤM mới hiện biểu mẫu của nó. Bốn ô ấy có markup GIỐNG HỆT ba ô
     đã đo (`label.form-check` bọc `input.form-check-input` + `span.form-check-label`) nên cùng
     ăn đúng một luật dưới đây — ⛔ nhưng đừng đọc con số "3 ô" thành "màn hình chỉ có 3 ô"
     (bẫy SÀN ĐẾM, đợt 1014 · 1690).

   🔴 ĐÂY LÀ KIỂU ①, ⛔ KHÔNG PHẢI KIỂU ③ — thẻ bọc là một `label`, tức BẤM ĐƯỢC: bấm chỗ nào
     trong nó cũng đổi tick. Bộ đo sống đọc từ cây DOM (`closest('label')`) khai đúng như thế.
     ⇒ Ở đây CHỈ cần nâng sàn cho `.form-check`. ⛔ ĐỪNG chép thêm cặp `margin-left`/`padding-left`
       của khuôn kiểu ③ sang: cặp ấy sinh ra để kéo NHÃN trùm lên cột ô tick khi thẻ bọc là `div`
       ⛔ không bấm được — ở đây thẻ bọc đã bấm được cả, thêm vào chỉ là một luật ⛔ không cứu ai.
       Hàng rào `VungChamNgonTayTests.Kieu_mot_khong_deo_them_cap_le_dem_cua_kieu_ba` canh chiều ấy.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là ngón
     tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, và `VungChamNgonTayTests.Luat_cham_khong_ro_ra_ngoai_cau_media` đo điều đó bằng máy.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; ⛔ đừng gõ số 44px vào tệp này.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng ghép bằng chữ `and` (bài học đợt 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* THẺ BỌC `label.form-check` — chính nó là vùng bấm thật của cả 7 ô.
       🔑 BỀ NGANG ⛔ KHÔNG ĐỔI: hộp mở đầu ở 1,5em (đệm trái của `.form-check`) · ô tick mang lề
         trái −1,5em nên khung của nó nằm ở 0…1em (Y HỆT chỗ cũ) · bề rộng ngoài vì thế là −0,5em,
         cộng `gap` 0,5em ⇒ dòng chữ vẫn bắt đầu đúng ở 1,5em. Bỏ `gap` là chữ dịch sang trái nửa em.
       ⚠ Đổi `display` là BẮT BUỘC: `min-height` ⛔ KHÔNG có tác dụng gì trên một hộp mức inline —
         thẻ `label` mặc định là inline, nên dòng ấy đi kèm chứ ⛔ không phải để trang trí.
       ⚠ `inline-flex` chứ ⛔ không `flex`: cả 7 ô đều nằm trong một hàng ngang — ô «Chỉ đang dùng»
         ở thanh công cụ `d-flex gap-3 flex-wrap align-items-center` cùng ô tìm, sáu ô còn lại ở
         các cột `d-flex align-items-end` của biểu mẫu soạn (cột `col-md-3` có ĐÚNG HAI ô đứng
         cạnh nhau). Hoá `flex` là mỗi hộp chiếm trọn một dòng, thanh công cụ đang gọn bỗng vỡ ra.
       ⚠ Trong hộp flex thuộc tính `float` bị BỎ QUA, nên ô vuông nhận phép canh giữa dọc như phần
         chữ bên cạnh — đó là thứ giữ cho ô thôi dính mép trên khi hộp cao lên 44px.
       ⚠ `min-height` chứ ⛔ không `height`: nhãn dài phải xuống hai dòng thì cứ để nó cao hơn. */
    .form-check[b-fynb51vdi1] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }
}

/* ═══ ĐỢT 2195 (việc o1636) — NĂM BẢNG DANH MỤC THIẾT BỊ HOÁ THẺ Ở KHỔ ĐIỆN THOẠI ═══
   Màn hình: /cong-nghe — ngăn 🧱 Danh mục của tab 🧰 Thiết bị (điểm · tuyến · van · bầu lược tổng ·
   máy bơm). Khuôn dựng ở đợt 1989 (việc o1260), sổ khai `BangTheKhoHepSoKhaiTests.SoKhai`.

   📏 ĐO SỐNG TRƯỚC KHI VÁ — 07/09/2026, Chrome thật, khung 375×812, app cổng 5270, đăng nhập tienvm.
      Hộp `.table-responsive` rộng 351px, còn bảng rộng:
        📍 Điểm (11 cột) 556px — 4 ô cuối NGOÀI màn: Súc rửa · Số cũ · Dùng · Thao tác
        🔗 Tuyến (8 cột) 481px — 2 ô ngoài        🔩 Van (8 cột) 409px — 1 ô ngoài
        ⚙️ Máy bơm (12 cột) 737px — 7 ô ngoài     🧺 Bầu lược tổng (7 cột) 351px — 0 ô ngoài
      ⇒ BỐN trên năm bảng có cột «Thao tác» (nút ✏️ / 🗑) nằm ngoài màn hình: muốn sửa một thiết bị
        bằng điện thoại thì phải cuộn ngang BÊN TRONG hộp mới thấy nút.
   ⚠ `cong-cu/do-song-khung-hep.mjs` báo «tràn ngang 0» cho trang này và vẫn ĐÚNG — hộp mang
     `overflow-x` nên THÂN TRANG ⛔ không tràn. Loại hỏng này chỉ đo được bằng bề ngang BẢNG so với
     bề ngang HỘP, ⛔ đừng đọc con số của bộ đo ấy thành "màn hình này ổn".

   🔑 MỌI BỘ CHỌN NEO VÀO LỚP `.bang-the-kho-hep`: tệp `.razor` này có BẢY thẻ bảng mà CSS cách ly áp
     theo CẢ COMPONENT — một bộ chọn trần (kiểu `thead { display: none }`) giấu đầu của MỌI bảng
     trong tệp, kể cả bảng lịch nhập xuất và bảng thiết bị trên tuyến, mà build vẫn xanh.
     Hàng rào `Tep_nhieu_bang_thi_moi_bo_chon_phai_neo_vao_lop_rieng` canh đúng chiều này.
   ⚠ NGƯỠNG 820px phải TRÙNG khối đợt 400 của `wwwroot/app.css` (luật "mọi bảng tự thành khung cuộn
     ở khổ điện thoại"). Lệch là chừa ra một dải khổ mà bảng VỪA là thẻ VỪA là khung cuộn ngang —
     hàng rào đọc ngưỡng từ chính `app.css` rồi so, ⛔ đừng gõ một con số khác vào đây.
   ⚠ PHẢI CÓ CHỮ `screen`: bản IN đứng ngoài (khối đợt 400 cũng gate `screen`).
   ⚠ NHÃN CỘT LẤY TỪ `data-nhan` khai ở chính thẻ `td` trong `.razor`, ⛔ không chép tay vào đây kiểu
     `td:nth-child(n)::before` — chép tay là ngày ai chèn một cột vào giữa thì mọi nhãn lệch một ô,
     hỏng LẶNG LẼ và màn hình dán tên cột này lên giá trị cột kia.
   ⛔ KHÔNG ẩn ô nào cho "vừa màn hình": ẩn cột là MẤT DỮ LIỆU trong im lặng, hàng rào
     `Khoi_kho_hep_khong_duoc_an_mot_o_nao` bắt đỏ.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — cặp ký tự ấy lọt vào giữa câu văn là chú
     thích đóng sớm và trình duyệt bỏ NGUYÊN luật ngay dưới, ⛔ không một câu lỗi nào. Vì thế ở đây
     gọi tên nó bằng CHỮ. Soi: node cong-cu/soi-chu-thich-css.mjs
   📏 Đo lại: node cong-cu/do-song-khung-hep.mjs --trang "cong-nghe?tab=thiet-bi"  (chốt ngược: --chuot) */

@media screen and (max-width: 820px) {

    /* ① ĐẦU BẢNG BIẾN MẤT — tên cột nay đi theo từng ô (xem ④). */
    .bang-the-kho-hep thead[b-fynb51vdi1] { display: none; }

    .bang-the-kho-hep tbody[b-fynb51vdi1] { display: block; }

    /* ② MỖI DÒNG LÀ MỘT THẺ. Viền + bo góc để mắt tách được thiết bị này với thiết bị kia — mất hàng
       tiêu đề rồi thì đường kẻ ngang của bảng ⛔ không đủ nói "hết một thiết bị".
       ⚠ `background` đọc BIẾN `--bs-table-bg` chứ ⛔ không tô màu cứng: dòng của thiết bị NGƯNG DÙNG
         mang lớp `text-body-secondary` và các dòng cảnh báo dùng lớp nền của Bootstrap. */
    .bang-the-kho-hep tbody tr[b-fynb51vdi1] {
        display: block;
        margin-bottom: .75rem;
        padding: .5rem .75rem;
        border: 1px solid var(--bs-border-color);
        border-radius: .5rem;
        background: var(--bs-table-bg, transparent);
    }

    /* ③ MỖI Ô LÀ MỘT DÒNG «nhãn — giá trị».
       ⚠ `align-items: baseline` chứ ⛔ không `center`: ô «Thao tác» chứa một dải nút cao hơn hẳn con
         chữ của nhãn; canh giữa là nhãn trôi xuống lưng chừng dải nút.
       ⚠ Bộ chọn ⛔ KHÔNG với tới ô `colspan` (hàng «chưa có dòng nào khớp» và hàng cảnh báo xóa bung
         ra dưới mỗi tuyến): chúng ⛔ không thuộc cột nào nên ⛔ không có nhãn, để nguyên dáng khối. */
    .bang-the-kho-hep tbody td[b-fynb51vdi1] {
        display: flex;
        gap: .5rem;
        align-items: baseline;
        flex-wrap: wrap;
        width: auto;
        padding: .25rem 0;
        border: 0;
    }

    /* ④ NHÃN CỘT — lấy nguyên văn từ `data-nhan` của chính ô ấy.
       ⚠ `flex: 0 0 8.5em` cho mọi nhãn cùng một bề ngang ⇒ cột giá trị thẳng hàng nhau. */
    .bang-the-kho-hep tbody td[data-nhan][b-fynb51vdi1]::before {
        content: attr(data-nhan);
        flex: 0 0 8.5em;
        color: var(--bs-secondary-color);
        font-size: .85em;
        text-align: start;
    }

    /* ⑤ 🩸 CHỖ NÀY CỐ Ý ⛔ KHÔNG CÓ LUẬT `.bang-the-kho-hep tbody td.text-end { text-align: start }`
       — ⛔ ĐỪNG CHÉP NÓ TỪ BA TỆP KHUÔN ĐỜI TRƯỚC SANG. Bốn ô số của năm bảng này («V (m³)» · «Lưu
       tốc» · «kW» · «m³/h») đeo lớp tiện dụng `.text-end` của Bootstrap, mà lớp ấy là
       `text-align: right !important` ⇒ một luật viết ở tầng `td` là luật **⛔ KHÔNG BAO GIỜ CHẠY**,
       trong khi build xanh · test xanh · và người đọc mã tin rằng nhãn đã căn trái. Đúng họ bẫy
       `d-block` (đợt 1989) và `mt-1` (đợt 2181): thấy một luật "đúng mà ⛔ không ăn" thì hỏi ngay
       *"chỗ này có lớp tiện dụng Bootstrap nào đang cầm `!important` không?"*.
       ⇒ Phép căn trái đã dời lên chính `::before` ở khối ④ — đó là phần tử KHÁC nên ⛔ không ai tranh,
         và ⛔ không phải nống thêm một `!important` thứ hai.
       ⓘ Ô GIÁ TRỊ vẫn nhận `right` từ lớp ấy, nhưng hộp của nó co theo nội dung nên ⛔ không lệch một
         pixel nào — đã đo. Ba tệp khuôn đời trước (`TrangDanhGiaQuy` · `TrangDanhGiaThang` ·
         `TrangKeHoachDauTu`) đang mang đúng luật chết ấy: ghi thành việc, ⛔ không sửa chen ở đợt này. */

    /* ⑥ TRẦN CHIỀU CAO 62vh gõ thẳng vào thẻ bọc `.table-responsive` là hợp lý cho BẢNG (đầu bảng còn
       đó, cuộn trong khung), nhưng ở dáng THẺ nó biến danh mục thành một ô cuộn lồng trong trang đang
       cuộn — ngón tay ⛔ không biết mình đang cuộn cái nào. Ở khổ này để trang tự cuộn.
       ⚠ Bộ chọn phải đi từ thẻ bọc CÓ CHỨA bảng theo khuôn, ⛔ đừng viết `.table-responsive` trần:
         tệp này còn bảng khác cũng nằm trong thẻ bọc ấy.
       ⚠ `!important` là BẮT BUỘC ở đây — trần 62vh gõ thẳng vào thuộc tính `style` của thẻ, mà kiểu
         nội tuyến thắng mọi luật ngoài TRỪ khi luật ấy mang `!important`. Đây là ca chính đáng, ⛔ không
         phải thói quen: chữa tận gốc là gỡ `style` khỏi markup, mà markup ấy đang dùng chung với khổ
         máy tính. ⛔ Đừng chép `!important` sang các luật khác của khối.
       ⚠ Trình duyệt ⛔ không hiểu `:has` thì bỏ nguyên luật này ⇒ danh mục giữ khung cuộn 62vh như
         trước đợt — chiều fail ĐÚNG, ⛔ không mất dữ liệu và ⛔ không vỡ bố cục. */
    .table-responsive:has(.bang-the-kho-hep)[b-fynb51vdi1] {
        max-height: none !important;
        overflow-y: visible;
    }
}
/* /Components/Pages/CongNghe/TrangCongToCongNghe.razor.rz.scp.css */
/* ═══ TRANG CÔNG TƠ — CÔNG NGHỆ NHẬP XUẤT ═══

   CSS cách ly của riêng trang. Cố ý CHỈ ba lớp `.ct-*`, mỗi lớp một việc rõ ràng — mọi thứ còn
   lại (bảng, thẻ, huy hiệu, dải tab) dùng thẳng lớp Bootstrap để trang này không lệch khuôn với
   các trang anh em của phân hệ.

   ⚠ Từ đợt việc o859 tệp có THÊM một khối vùng chạm ngón tay ở CUỐI tệp. Khối ấy KHÔNG đẻ lớp
   mới — nó chỉ nới sàn chiều cao của `.form-check-label` và chỉ chạy trong câu media `pointer:
   coarse`, nên câu "cố ý chỉ ba lớp" ở trên vẫn đúng với chế độ chuột.

   ⚠ Ba lớp dưới đây KHÔNG đặt màu nào cả. Màu chữ và màu nền lấy từ bộ token của app nên trang
   tự đúng ở cả giao diện sáng lẫn giao diện tối; gõ mã màu vào đây là ghim một màu cho cả hai
   giao diện, và chỗ hỏng chỉ mắt người mới thấy.

   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo. Cặp ký tự đó lọt vào GIỮA câu văn là chú
   thích đóng sớm, phần chữ còn lại dính vào bộ chọn của luật ngay dưới và trình duyệt bỏ nguyên
   luật đó — không một câu lỗi nào. Vì thế ở đây gọi tên cặp ký tự ấy bằng CHỮ, không gõ ra. */

/* Khung cuộn của các bảng: giữ tiêu đề trang và dải tab luôn nhìn thấy được, người dùng chỉ cuộn
   trong lòng bảng. Cùng chiều cao với các bảng danh mục của trang công nghệ nhập xuất. */
.ct-bang[b-lzogb9jf01] {
    max-height: 62vh;
}

/* Số seri và mã hiệu: chữ đều nét để đọc và đối chiếu từng ký tự với hồ sơ giấy — số seri hay có
   cặp dễ nhầm như số 0 với chữ O, số 1 với chữ l. */
.ct-seri[b-lzogb9jf01] {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Courier New", monospace, "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    font-size: 0.86em;
}

/* Dòng phụ trong ô bảng và câu giải thích ngắn dưới biểu mẫu. */
.ct-nho[b-lzogb9jf01] {
    font-size: 0.86em;
}

/* ═══ VÙNG CHẠM CHO NGÓN TAY — HAI Ô TICK CỦA TRANG CÔNG TƠ (việc o859) ═══

   Trang này nằm trên đường đi của app điện thoại: ô 🧾 "Nhật ký công tơ" ở màn hình `/dien-thoai`
   dẫn thẳng vào đây, và người dùng bấm nó TẠI HIỆN TRƯỜNG bằng ngón tay chứ không phải bằng chuột.

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài, và chính nó ghi rõ lý do: nâng chính ô vuông lên 44px thì ô tick to đúng bằng cả dòng chữ
   bên cạnh, trông như lỗi. Thứ phải nới là VÙNG CHẠM QUANH ô tick, mà vùng ấy mỗi màn hình một
   khác nên phải làm tại chỗ. Đây là một trong những chỗ ấy — đo 30/08/2026: đúng HAI ô tick trong
   tệp này (ô lọc "Chỉ đang dùng" ở thanh công cụ, và ô "Dùng" trong biểu mẫu thêm/sửa công tơ).
   Cả hai đều KHÔNG nằm trong vòng lặp nào, nên một dòng mã là đúng một ô trên màn hình.

   Khuôn chép nguyên từ `Components/Pages/Nuoc/TabDanhMucNuoc.razor.css` mục ① (ô tick CÓ CHỮ đi
   kèm). Đừng nghĩ cách mới ở đây — mục ② của tệp ấy (thẻ bọc `o-tick-cham`) dành cho ô tick TRẦN,
   không có chữ nào bên cạnh để mà bấm; hai ô của trang này đều có chữ nên đi mục ①.

   🔴 ĐỢT 1619 (việc o905) — LỜI KHAI KIỂU ① Ở TRÊN LÀ ĐÚNG, NHƯNG BẢN VÁ CÒN THIẾU MỘT NỬA.
     Đo sống 01/09/2026 (Chrome thật, khung 390×844 cảm ứng,
     `node cong-cu/do-song-o-tick-cham.mjs --trang cong-nghe/cong-to`) trên bản CHƯA có khối
     `.form-check` bên dưới — hai ô "Chỉ đang dùng" và "Dùng" cho cùng một bộ số:
       · vùng bấm thật tại cột Ô TICK: 45px ✅ — thẻ `label` bọc CẢ ô tick nên phần này vốn đã đạt,
         tức trang này KHÁC hẳn `TrangBangGiaMucXdcb` (bên ấy đo được 17px, việc o905 tưởng cả hai
         cùng bệnh — đo mới tách được hai ca);
       · nhưng LỆCH TÂM DỌC giữa ô vuông và dòng chữ: −10px, ô tick dính MÉP TRÊN của nhãn 44px.
     ⇒ Bệnh còn lại KHÔNG phải vùng bấm mà là CANH CHỈNH: Bootstrap cho `.form-check .form-check-input`
       `float: left`, mà nhãn thì vừa được nâng lên 44px và canh giữa dòng chữ ⇒ ô vuông đứng lệch
       hẳn 10px so với chính dòng chữ của nó. Trên máy chuột (nhãn cao 24px) thì không thấy.
     ⇒ Cách chữa: đổi `label.form-check` thành hộp flex — trong hộp flex `float` bị BỎ QUA nên ô
       vuông nhận phép canh giữa dọc. ⛔ KHÔNG đụng `.form-check-input`: phóng to chính ô vuông là
       điều `app.css` và `VungChamNgonTayTests` đều cấm.

   🔑 VÌ SAO ĐẶT Ở TỆP CÁCH LY, KHÔNG PHẢI Ở `app.css`:
     · `app.css` dùng chung cho MỌI màn hình của 433 tài khoản, phần lớn ngồi chuột — nới
       `.form-check-label` từ bên đó là đổi bố cục cả app để giải bài toán của một trang, đúng loại
       rủi ro lan truyền quy tắc #18 dặn phải tránh.
     · CSS cách ly biên dịch ra `.form-check-label[b-xxx]` (0,2,0) nên nó thắng bộ chọn cùng tên ở
       `app.css` (0,1,0), và chỉ chạm đúng phần tử viết trong `TrangCongToCongNghe.razor`.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width` — khổ hẹp KHÔNG có nghĩa là ngón tay
     (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ quyết
     định vùng bấm là CÁI CHẠM VÀO, không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT khối này không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media. Ba lớp `.ct-*` phía trên thì ngược lại, chúng vốn áp cho cả chuột lẫn ngón tay.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — đừng viết câu media ghép bằng chữ `and` trong tệp `.razor.css`:
     bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học đợt 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* Ô TICK CÓ CHỮ ĐI KÈM — hai ô nói ở đầu khối. Ở cả hai chỗ, thẻ `label` bọc CẢ ô tick lẫn
       dòng chữ, nên chính nó là vùng bấm thật: bấm vào chữ cũng đổi tick. Nâng sàn chiều cao của
       phần chữ là ngón tay bấm trúng, mà ô vuông giữ nguyên cỡ — đúng điều `app.css` dặn.

       ⚠ Phải đổi `display`: `min-height` KHÔNG có tác dụng gì trên một hộp inline, đó là lý do
         dòng này đi kèm chứ không phải để trang trí.
       ⚠ `inline-flex` chứ không `flex`: ô "Chỉ đang dùng" nằm trong một hàng `d-flex gap-3
         flex-wrap` xếp ngang cùng ô tìm; hoá `flex` là nó chiếm trọn một dòng, thanh công cụ đang
         gọn bỗng vỡ ra nhiều dòng.
       ⚠ `min-height` chứ không `height`: nhãn nào chữ dài phải xuống hai dòng thì cứ để nó cao
         hơn; đặt `height` là bóp lại, sửa một lỗi lại đẻ một lỗi. */
    .form-check-label[b-lzogb9jf01] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }

    /* ② HỘP BỌC (chính thẻ `label.form-check`) — thêm ở đợt 1619 để ô tick thôi dính mép trên.
       Trong hộp flex thuộc tính `float` bị BỎ QUA, nên ô vuông nhận phép canh giữa dọc như phần
       chữ bên cạnh.

       🔑 BỀ NGANG KHÔNG ĐỔI — lề âm Bootstrap gán cho ô tick ăn khớp đúng với phần đệm trái 1,5em
         của hộp: hộp mở đầu ở 1,5em · ô tick mang lề trái −1,5em nên khung của nó nằm ở 0…1em
         (Y HỆT chỗ cũ) · bề rộng ngoài vì thế là −0,5em, cộng `gap` 0,5em ⇒ chữ bắt đầu đúng ở
         1,5em, cũng Y HỆT chỗ cũ. Lý lẽ đầy đủ ở `Nuoc/TabHoaDonNuoc.razor.css`.
       ⚠ KHÔNG đụng `padding-left` của Bootstrap — chính phần đệm ấy giữ bề ngang.
       ⚠ `inline-flex` chứ không `flex`: ô "Chỉ đang dùng" nằm trong một hàng `d-flex gap-3
         flex-wrap` xếp ngang cùng ô tìm, còn ô "Dùng" nằm trong một cột `d-flex align-items-end`
         của biểu mẫu; hoá `flex` là mỗi hộp chiếm trọn một dòng. */
    .form-check[b-lzogb9jf01] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }
}

/* 🔒 SỐ ĐO SAU KHI VÁ — đợt 1619, cùng lệnh và cùng khung với số "trước" ở đầu tệp; cả HAI ô cho
   cùng một bộ số (app dựng riêng ở cổng 5281 từ một `git worktree` để ⛔ không giành cổng 5270):
     · vùng bấm thật tại cột Ô TICK: 45px → 45px (vốn đã đạt, bản vá này ⛔ không đụng tới);
     · lệch tâm dọc: −10px → +2px, ô tick thôi dính mép trên (2px là phần chênh của khoảng dòng
       chữ, mắt ⛔ không thấy).
   🔒 CHỐT NGƯỢC Ở CHẾ ĐỘ CHUỘT (`--chuot`, 1369×900): trước và sau khi vá đều là ô vuông 16×16 ·
     nhãn cao 24 · hộp cao 24 (display block) · vùng bấm 25px · lệch tâm 0px — TRÙNG KHÍT từng con
     số, tức máy tính KHÔNG đổi một pixel nào. */
/* /Components/Pages/CongNghe/TrangSoDoCongNghe.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — Ô CÔNG TẮC «XEM THỬ CHUYỂN ĐỘNG MỌI TUYẾN» (đợt 1672, việc o962) ═══
   Màn hình: sơ đồ nguyên lý tuyến bơm xăng dầu (/cong-nghe/so-do).

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài — nâng chính ô lên 44px thì nó to bằng cả dòng chữ bên cạnh. Thứ phải nới là VÙNG CHẠM
   QUANH ô, mà vùng ấy mỗi màn hình một khác nên phải nới tại chỗ, bằng CSS cách ly.

   📏 ĐO SỐNG TRƯỚC KHI VÁ — 01/09/2026, Chrome thật, khung 390×844 cảm ứng:
     node cong-cu/do-song-o-tick-cham.mjs --trang /cong-nghe/so-do
     Cấu trúc ① NHÃN BỌC · ô vuông 32×16 (công tắc) · nhãn cao 24 · hộp `.form-check` cao 24
     (display block) · VÙNG BẤM THẬT tại cột công tắc 25px · tại cột chữ 25px · lệch tâm dọc 0px.

   🔴 ĐÂY LÀ KIỂU ①, ⛔ KHÔNG PHẢI KIỂU ③ — VÀ BỘ DÒ TĨNH TỪNG NÓI NGƯỢC.
     `node cong-cu/soi-o-tick-tran.mjs` xếp ô này vào «③ anh em» vì nó chỉ đọc TÊN LỚP: thẻ bọc
     mang lớp `form-check` chứ ⛔ không mang `form-check-label`. Nhưng thẻ ấy là một `label`, tức
     BẤM ĐƯỢC — bấm chỗ nào trong nó cũng gạt công tắc. Bộ đo sống đọc từ cây DOM (`closest('label')`)
     nói đúng ngay từ đầu: ① nhãn bọc. Đợt 1672 đã vá lại bộ dò tĩnh cho biết phân biệt thẻ bọc là
     `label` hay `div`.
     ⇒ Vì cả thẻ bọc đã bấm được nên ở đây CHỈ cần nâng sàn cho `.form-check`. ⛔ ĐỪNG chép thêm
       cặp `margin-left`/`padding-left` của khuôn kiểu ③ sang: nó sinh ra để kéo NHÃN trùm lên cột
       ô tick khi thẻ bọc là `div` ⛔ không bấm được — ở đây thẻ bọc đã bấm được, thêm vào chỉ là
       một luật ⛔ không cứu ai cả.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width` — khổ hẹp KHÔNG có nghĩa là ngón tay.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; ⛔ đừng gõ số 44px vào tệp này.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng ghép bằng chữ `and` (bài học đợt 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs` */

@media (pointer: coarse) {

    /* THẺ BỌC `label.form-check.form-switch` — chính nó là vùng bấm thật.
       🔑 BỀ NGANG KHÔNG ĐỔI: thẻ mở đầu ở 2,5em (đệm trái của `.form-switch`) · công tắc mang lề
         trái −2,5em nên khung của nó nằm ở 0…2em (Y HỆT chỗ cũ) · bề rộng ngoài vì thế là −0,5em,
         cộng `gap` 0,5em ⇒ dòng chữ vẫn bắt đầu đúng ở 2,5em. Bỏ `gap` là chữ dịch sang trái nửa em.
       ⚠ Đổi `display` là bắt buộc: `min-height` ⛔ KHÔNG có tác dụng gì trên một hộp mức inline —
         thẻ `label` mặc định là inline, nên dòng ấy đi kèm chứ ⛔ không phải để trang trí.
       ⚠ `inline-flex` chứ ⛔ không `flex`: thẻ này nằm trong hàng `d-flex align-items-center gap-3
         flex-wrap` xếp ngang cùng nút và câu đếm; hoá `flex` là nó chiếm trọn một dòng.
       ⚠ Trong hộp flex thuộc tính `float` bị BỎ QUA, nên công tắc nhận phép canh giữa dọc như
         phần chữ bên cạnh — đó là thứ giữ cho ô thôi dính mép trên khi hộp cao lên 44px.
       ⚠ `min-height` chứ ⛔ không `height`: nhãn dài phải xuống hai dòng thì cứ để nó cao hơn. */
    .form-check[b-mggy6ljkkb] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }
}

/* ═══ 📱 KHỔ ĐIỆN THOẠI — CÔNG TẮC VÀ NÚT LỊCH ĐỨNG CHUNG MỘT HÀNG (đợt 2307, việc o1847) ═══
   Anh Tiến 10/09/2026 gửi ảnh trang này trên điện thoại: «giao diện nhập xuất đang không đều».
   Ở khổ ≤576px nhãn công tắc «Xem thử chuyển động mọi tuyến» dài hơn phần còn lại của hàng nên nút
   «🗓 Lịch nhập xuất» rớt xuống dòng dưới và đứng một mình sát mép trái.
   ⇒ Công tắc lấy PHẦN CÒN LẠI của hàng và cho chữ xuống hai dòng, nút giữ đúng bề ngang của nó.
   🔑 `flex: 1 1 0` và `min-width: 0` là MỘT CẶP: thiếu vế sau thì hộp flex giữ bề rộng nội dung nhỏ
     nhất của dòng chữ (luật ⑪ đợt 2198) và nút vẫn bị đẩy xuống dòng.
   ⚠ Điều kiện là BỀ NGANG (bài toán bố cục), ⛔ không phải cái chạm: cửa sổ máy tính thu nhỏ cũng rớt
     dòng y hệt. Khổ rộng ⛔ không đổi một pixel nào.
   🚧 Hàng rào: TKNB.Tests/SoDoCongNgheDienThoaiTests.cs */
@media (max-width: 576px) {
    .cn-sodo-congcu > .form-check[b-mggy6ljkkb] {
        flex: 1 1 0;
        min-width: 0;
    }

    .cn-sodo-congcu > .btn[b-mggy6ljkkb] {
        flex: none;
    }
}
/* /Components/Pages/CongNghe/TrangSoDoPccc.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — Ô CÔNG TẮC «XEM THỬ CHUYỂN ĐỘNG MỌI TUYẾN» (đợt 1672, việc o962) ═══
   Màn hình: sơ đồ nguyên lý tuyến chữa cháy (/cong-nghe-pccc/so-do).

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài — nâng chính ô lên 44px thì nó to bằng cả dòng chữ bên cạnh. Thứ phải nới là VÙNG CHẠM
   QUANH ô, mà vùng ấy mỗi màn hình một khác nên phải nới tại chỗ, bằng CSS cách ly.

   📏 ĐO SỐNG TRƯỚC KHI VÁ — 01/09/2026, Chrome thật, khung 390×844 cảm ứng:
     node cong-cu/do-song-o-tick-cham.mjs --trang /cong-nghe-pccc/so-do
     Cấu trúc ① NHÃN BỌC · ô vuông 32×16 (công tắc) · nhãn cao 24 · hộp `.form-check` cao 24
     (display block) · VÙNG BẤM THẬT tại cột công tắc 25px · tại cột chữ 25px · lệch tâm dọc 0px.

   🔴 ĐÂY LÀ KIỂU ①, ⛔ KHÔNG PHẢI KIỂU ③ — VÀ BỘ DÒ TĨNH TỪNG NÓI NGƯỢC.
     `node cong-cu/soi-o-tick-tran.mjs` xếp ô này vào «③ anh em» vì nó chỉ đọc TÊN LỚP: thẻ bọc
     mang lớp `form-check` chứ ⛔ không mang `form-check-label`. Nhưng thẻ ấy là một `label`, tức
     BẤM ĐƯỢC — bấm chỗ nào trong nó cũng gạt công tắc. Bộ đo sống đọc từ cây DOM (`closest('label')`)
     nói đúng ngay từ đầu: ① nhãn bọc. Đợt 1672 đã vá lại bộ dò tĩnh cho biết phân biệt thẻ bọc là
     `label` hay `div`.
     ⇒ Vì cả thẻ bọc đã bấm được nên ở đây CHỈ cần nâng sàn cho `.form-check`. ⛔ ĐỪNG chép thêm
       cặp `margin-left`/`padding-left` của khuôn kiểu ③ sang: nó sinh ra để kéo NHÃN trùm lên cột
       ô tick khi thẻ bọc là `div` ⛔ không bấm được — ở đây thẻ bọc đã bấm được, thêm vào chỉ là
       một luật ⛔ không cứu ai cả.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width` — khổ hẹp KHÔNG có nghĩa là ngón tay.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; ⛔ đừng gõ số 44px vào tệp này.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng ghép bằng chữ `and` (bài học đợt 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs` */

@media (pointer: coarse) {

    /* THẺ BỌC `label.form-check.form-switch` — chính nó là vùng bấm thật.
       🔑 BỀ NGANG KHÔNG ĐỔI: thẻ mở đầu ở 2,5em (đệm trái của `.form-switch`) · công tắc mang lề
         trái −2,5em nên khung của nó nằm ở 0…2em (Y HỆT chỗ cũ) · bề rộng ngoài vì thế là −0,5em,
         cộng `gap` 0,5em ⇒ dòng chữ vẫn bắt đầu đúng ở 2,5em. Bỏ `gap` là chữ dịch sang trái nửa em.
       ⚠ Đổi `display` là bắt buộc: `min-height` ⛔ KHÔNG có tác dụng gì trên một hộp mức inline —
         thẻ `label` mặc định là inline, nên dòng ấy đi kèm chứ ⛔ không phải để trang trí.
       ⚠ `inline-flex` chứ ⛔ không `flex`: thẻ này nằm trong hàng `d-flex align-items-center gap-3
         flex-wrap` xếp ngang cùng nút và câu đếm; hoá `flex` là nó chiếm trọn một dòng.
       ⚠ Trong hộp flex thuộc tính `float` bị BỎ QUA, nên công tắc nhận phép canh giữa dọc như
         phần chữ bên cạnh — đó là thứ giữ cho ô thôi dính mép trên khi hộp cao lên 44px.
       ⚠ `min-height` chứ ⛔ không `height`: nhãn dài phải xuống hai dòng thì cứ để nó cao hơn. */
    .form-check[b-r5dewf79ad] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }
}
/* /Components/Pages/CongViec/ChiTietCongViec.razor.rz.scp.css */
/* ═══ TRANG CHI TIẾT NHIỆM VỤ — ĐỢT 1335 (việc o535) ═══
   Anh Tiến 26/08/2026: "nút thêm và chọn người liên quan lỗi, nếu chưa có ai thì cứ để chung 1 dòng".

   Ô chọn người nằm trong cột hẹp (dd col-7 của một thẻ col-lg-5), cạnh nút "Thêm". Bề rộng TỐI THIỂU
   mặc định của một thẻ select là bề rộng của DÒNG DÀI NHẤT trong danh sách — ở đây là họ tên kèm tên
   phòng, dài hơn cả cột. Nó ⛔ không chịu co, nên nút bên cạnh lãnh hết phần bị bóp và chữ "Thêm" vỡ
   làm hai dòng (đúng ảnh anh gửi).

   Chữa ĐÚNG GỐC: cho ô chọn co lại được. Nút thì đã có flex-shrink-0 và text-nowrap ở markup.
   ⛔ ĐỪNG chữa bằng cách rút ngắn chữ trên nút hay bỏ tên phòng khỏi danh sách: cái vỡ là BỐ CỤC,
   còn tên phòng là thứ phân biệt hai người trùng họ tên. */

.cv-lq-chon[b-wp8f9qfif0] {
    flex: 1 1 auto;
    /* Chỗ chữa THẬT. min-width mặc định của một phần tử flex là "auto" = cỡ nội dung nhỏ nhất, mà với
       thẻ select thì cỡ ấy tính theo dòng dài nhất. */
    min-width: 0;
}

/* ═══ HỘP GẮN HỒ SƠ — ĐỢT 1635 (việc o928) ═══
   Anh Tiến 01/09/2026, kèm ảnh chụp khối "Thuộc dự án / sửa chữa": *"gom 2 ô này làm 1"*.
   Ô lọc 🔎 và ô chọn hồ sơ nay chung một `input-group`: ô lọc GIÃN, ô chọn CO bên cạnh. Cùng một dáng
   với hộp tạo nhiệm vụ (`HopThoaiTaoNhiemVu.razor.css`) và form giao việc đầy đủ
   (`GiaoViec.razor.css`) — ba màn hình làm cùng một việc mà lệch dáng là thứ người dùng ⛔ không đoán ra.

   🔴 Ô chọn BẮT BUỘC khai đủ `flex` + `width` + `min-width`, ⛔ ĐỪNG chỉ gắn `flex-grow-0` ở markup
     rồi thôi. Bootstrap khai cho ô chọn nằm trong dải nhập: `flex: 1 1 auto; width: 1%; min-width: 0`.
     Ba giá trị ấy đi CÙNG NHAU mới có nghĩa — `width: 1%` chỉ là CHỖ NEO cho `flex-grow: 1` kéo ra.
     Bỏ mỗi vế kéo ra mà để nguyên hai vế kia là ô tụt còn bằng cái mũi tên (bài học đợt 1448).
   ⚠ Có TRẦN vì bề rộng tự nhiên của một thẻ select là bề rộng DÒNG DÀI NHẤT trong danh sách — đúng
     nguyên nhân đã ghi ở khối trên của tệp này, và tên hồ sơ dự án còn dài hơn họ tên người.
   ⚠ Có SÀN vì dòng mặc định "— Nhiệm vụ độc lập… —" ngắn hơn hẳn tên hồ sơ thật.
   ⓘ Thẻ select ở đây là thẻ HTML do CHÍNH tệp `.razor` này viết ra nên nó mang dấu phạm vi, ⛔ không
     cần `::deep`. Bên `GiaoViec.razor.css` thì NGƯỢC LẠI — ô chọn bên ấy do `InputSelect` dựng ra.
   👆 ⛔ Đừng ép cả hàng vào một dòng cứng (`flex-nowrap`): `.input-group` tự xuống dòng khi hẹp, và đó
     là đường lui duy nhất cho khổ điện thoại (luật vùng chạm đợt 1518).
   📏 ĐỢT 2192 (việc o1634) — TRẦN 22rem PHẢI CHỊU THUA BỀ NGANG THẬT CỦA HỘP THOẠI. Đo sống hộp
     "🗂 Gắn hồ sơ" ở khung 375×812: thân hộp `.modal-body` rộng 357px, đệm 16px mỗi bên ⇒ chỗ dùng
     được chỉ 325px, mà 22rem = 352px ⇒ hàng chọn hồ sơ tràn +26px ra khỏi thân hộp (đo:
     `.cv-hang-ho-so` clientWidth 325 · scrollWidth 351).
     `min(22rem, 100%)` giữ NGUYÊN trần cũ ở máy tính (nơi thân hộp rộng hơn 352px) và chỉ hạ trần
     đúng lúc thân hộp hẹp hơn. ⛔ Đừng chữa bằng cách hạ hẳn số 22rem: ở máy tính ô chọn sẽ ngắn
     đi và tên hồ sơ dự án lại bị cắt — đúng thứ trần ấy sinh ra để tránh. */
.cv-o-ho-so[b-wp8f9qfif0] {
    width: auto;
    flex: none;
    min-width: 12rem;
    max-width: min(22rem, 100%);
}

/* Ô lọc là thứ giãn ra chiếm phần còn lại của hàng. Đặt SÀN để khổ hẹp thì cả hàng XUỐNG DÒNG nguyên
   khối thay vì ô lọc co còn vài ký tự — trong dải nhập, thẻ `form-control` mang sẵn `min-width: 0`
   của Bootstrap. */
.cv-hang-ho-so .form-control[b-wp8f9qfif0] {
    min-width: 10rem;
}

/* ═══ Ô NGÀY TRÊN ĐỈNH HỒ SƠ + TRONG KHỐI NGHIỆM THU — ĐỢT 2192 (việc o1634) ═══
   Trước đợt này bề ngang khai bằng KIỂU NỘI TUYẾN `Kieu="width: 118px;"` ngay trên thẻ `<ONgay>`.
   Kiểu nội tuyến ⛔ không hỏi được `@media`, nên nó không biết chuyện `app.css` nới `padding-right`
   của ô gõ lên `calc(var(--cham-toi-thieu) + .2rem)` = 47,2px khi màn hình là NGÓN TAY (luật vùng
   chạm đợt 1518, để chữ không chui xuống dưới nút 📅).

   📏 Đo sống bản CŨ ở khung 375×812, chế độ ngón tay, hồ sơ 1000083:
     khung 118px · đệm trái 8px · đệm phải 47,2px · viền 2px ⇒ còn 60,8px cho chữ,
     mà "07/09/2026" ở cỡ 14px rộng 70,1px ⇒ THIẾU 9,3px, màn hình in ra "07/09/202".
     (`.o-ngay-go` scrollWidth 125 · clientWidth 116.)

   🔒 CHIỀU MÁY TÍNH GIỮ NGUYÊN 118px — đó là con số đã cân với dải nút bên cạnh ở khổ rộng, và
     ở chế độ chuột đệm phải chỉ 1,9rem = 30,4px nên 118px vẫn dư chỗ (đo: scrollWidth = clientWidth
     = 116). ⛔ Đừng gộp hai chiều lại thành một số: nới cho cả máy tính là đổi bố cục một dải nút
     mà điện thoại mới là chỗ hỏng (đúng rủi ro lan truyền quy tắc #18).

   ⓘ Phải có `::deep`: `.o-ngay-boc` là thẻ do component `<ONgay>` dựng ra nên nó ⛔ KHÔNG mang dấu
     phạm vi của trang này. Và phải là con TRỰC TIẾP (`>`) — viết `::deep` trần thì luật với tới cả
     ô ngày nằm trong các hộp thoại con dựng trên trang này (`HopThoaiSuaNhiemVu` · `KhungTaoNhiemVu`),
     nơi ô ngày đang rộng 150px và ⛔ không hề hỏng. */
.cv-dinh-hs[b-wp8f9qfif0] >  .o-ngay-boc,
.cv-hang-ngay-xong[b-wp8f9qfif0] >  .o-ngay-boc {
    width: 118px;
}

@media (pointer: coarse) {
    .cv-dinh-hs[b-wp8f9qfif0] >  .o-ngay-boc,
    .cv-hang-ngay-xong[b-wp8f9qfif0] >  .o-ngay-boc {
        /* 8 + 70,1 + 47,2 + 2 = 127,3px là mức vừa ĐỦ; lấy 9,5rem = 152px cho bằng ô ngày trong hộp
           thoại sửa hồ sơ (đo được 150px và không cụt chữ), để hai chỗ cùng một dáng. */
        width: 9.5rem;
    }
}

/* ═══ KHỔ ĐIỆN THOẠI: BẢNG "THÔNG TIN" XẾP DỌC — ĐỢT 2192 (việc o1634) ═══
   📏 Bệnh đo được ở khung 375×812 trên hồ sơ CHƯA có người liên quan (ca 1000058):
     `dd.col-7` chỉ rộng 199px, mà hàng thêm người liên quan cần
       nhãn "— chưa có ai —" 101px (`text-nowrap`, ⛔ không co được)
       + ô chọn (bị bóp còn 46px — hẹp tới mức ⛔ không đọc được tên ai)
       + nút "Thêm" 53,8px (`flex-shrink-0`, sàn chạm 44px)
     ⇒ mép phải của nút nằm ở 380,1px, tức NGOÀI khung nhìn 375px ⇒ `documentElement`
     scrollWidth 380 > clientWidth 375: CẢ TRANG cuộn ngang.
   ⇒ Chữa ĐÚNG GỐC là trả lại bề ngang cho cột giá trị: dưới 576px thì nhãn và giá trị mỗi thứ một
     dòng trọn (cột giá trị 199px → 343px). Cùng khuôn "bảng hoá thẻ ở khổ hẹp" của đợt 1989/2181.
   🔒 Mốc 576px là mốc `sm` của Bootstrap, cũng là `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx`.
     ⛔ Đừng đổi con số ấy ở đây — hàng rào khác đang neo vào nó.
   ⓘ Vì sao chọn `max-width` chứ ⛔ không `pointer: coarse` như ô ngày trên: cái hỏng ở đây là BỀ
     NGANG (199px ⛔ không đủ cho ba thứ), ⛔ không phải cái chạm vào. Một cửa sổ máy tính thu hẹp
     còn 375px hỏng y hệt.
   ⚠ Vẫn giữ `flex-wrap` cho hàng liên quan: ở màn 320px thì 343px vẫn hụt, và xuống dòng là đường
     lui duy nhất còn lại. */
@media (max-width: 576px) {
    .cv-thong-tin > dt[b-wp8f9qfif0],
    .cv-thong-tin > dd[b-wp8f9qfif0] {
        flex: 0 0 100%;
        width: 100%;
        max-width: 100%;
    }

    .cv-thong-tin > dt[b-wp8f9qfif0] {
        margin-bottom: .1rem;
    }

    .cv-thong-tin > dd[b-wp8f9qfif0] {
        margin-bottom: .6rem;
    }

    .cv-hang-lien-quan[b-wp8f9qfif0] {
        flex-wrap: wrap;
    }

    /* SÀN cho ô chọn: `min-width: 0` ở khối trên cho nó co HẾT CỠ, mà co hết cỡ thì ô chỉ còn cái
       mũi tên. 8rem = 128px là mức còn đọc được một họ tên rút gọn; hụt chỗ thì hàng xuống dòng. */
    .cv-hang-lien-quan .cv-lq-chon[b-wp8f9qfif0] {
        min-width: 8rem;
    }
}
/* /Components/Pages/CongViec/DanhSachCongViec.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 2193 (việc o1635) — KHỔ ĐIỆN THOẠI: MỖI DÒNG BẢNG THÀNH MỘT THẺ ═══

   Anh Tiến giao 07/09/2026: "tiếp tục làm phân hệ nhiệm vụ, chú trọng đến giao diện điện thoại".
   Đây là tệp CSS cách ly ĐẦU TIÊN của trang này — trước đợt này trang ⛔ không có tệp nào.

   📏 ĐO SỐNG 375×812 (bản dựng từ HEAD, cổng riêng) TRƯỚC khi vá:
     · trang ⛔ KHÔNG trôi ngang (375 = 375) — luật chung `.content table.table` ở app.css đã biến
       bảng thành khung cuộn từ mốc 820px, nên bệnh ⛔ không phải "tràn ra khỏi màn hình";
     · nhưng bảng 9 cột phải cuộn NGANG trong khung của nó: bề ngang thật 556px / khung 349px
       ⇒ 207px (37%) nằm ngoài tầm mắt, cột Trạng thái · Tiến độ · Hạn chót phải kéo mới thấy;
     · và cột TÊN chỉ còn 66px ⇒ tên "Giao mực và kiểm tra máy in cho anh nhơn kho C" xuống dòng
       thành 10 DÒNG, đẩy chiều cao một dòng bảng lên 321px. Mỗi màn hình chỉ còn đọc được 2 việc.

   🔑 VÌ SAO: bảng có 9–10 cột, mà thuật toán bảng chia chỗ theo bề ngang TỐI THIỂU của từng cột.
     Huy hiệu, ngày giờ và thanh tiến độ (min-width 90px) đều ⛔ không co được, còn cột tên thì co
     được tới một con chữ ⇒ tên bị bóp trước tiên. Nắn từng cột cho khít 375px là vỡ ở 360px.

   🔑 CÁCH SỬA — đúng khuôn đợt 2169 (việc o1613) đã chữa hộp «Nhiệm vụ của tôi» ở trang chủ, và
     đúng khuôn `BangTaiKhoan.razor.css`: xếp mỗi DÒNG thành một THẺ. Tên trọn dòng; các ô còn lại
     thành dòng «nhãn — giá trị» lấy nhãn từ `data-nhan` của chính ô ấy; hai ô huy hiệu (ưu tiên ·
     trạng thái) tự nói lên mình nên nối ngang một dòng, ⛔ không cần nhãn.

   ⚠ Mốc 576px = `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx` — hàng rào
     `BangNhiemVuHoaTheTests` đối chiếu con số này với CẢ BA tệp CSS của phân hệ nhiệm vụ.
     ⛔ Đừng sửa một mình ở đây, và ⛔ đừng chế mốc riêng cho từng trang.
   ⚠ MỘT điều kiện thôi, ⛔ đừng ghép `and` (bài học đợt 1481 với tệp `.razor.css`).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — cặp ký tự ấy lọt vào giữa câu văn là chú
     thích đóng sớm và trình duyệt nuốt nguyên luật ngay dưới, ⛔ không một câu lỗi nào. */

/* 📱 ĐỢT 2289 (việc o1826) — LUẬT NỀN của dải lọc + ô tìm: trả lại đúng thứ markup vừa gỡ (lớp
   `d-flex` · `style="min-width: 250px"`) để máy tính ⛔ không đổi một pixel (khuôn luật ⑤ đợt 2181).
   ⚠ PHẢI đứng TRƯỚC khối `@media` bên dưới: cùng độ đặc hiệu thì luật đứng sau thắng — đặt luật nền
     ở cuối tệp là nó đè ngược lên luật điện thoại, lưới hai cột ⛔ không bao giờ hiện. */
.ds-loc[b-18hf1jo9dg] {
    display: flex;
}

.ds-o-tim[b-18hf1jo9dg] {
    min-width: 250px;
}

@media screen and (max-width: 576px) {

    /* ① ĐẦU BẢNG BIẾN MẤT — tên cột nay đi theo từng ô (xem ④). */
    thead[b-18hf1jo9dg] {
        display: none;
    }

    /* ② BẢNG THÔI LÀ BẢNG. `table` cũng phải khai lại: luật chung của app.css đã cho nó
       `overflow-x: auto`, mà khung cuộn ⛔ không còn việc gì để làm khi ⛔ không còn cột nào. */
    table[b-18hf1jo9dg],
    tbody[b-18hf1jo9dg],
    tr[b-18hf1jo9dg],
    td[b-18hf1jo9dg] {
        display: block;
        width: 100%;
    }

    /* ③ MỖI DÒNG LÀ MỘT THẺ. Viền + bo góc để mắt tách được việc này với việc kia — mất hàng tiêu
       đề rồi thì đường kẻ ngang của bảng ⛔ không đủ nói "hết một việc". */
    tbody tr[b-18hf1jo9dg] {
        margin-bottom: .75rem;
        padding: .5rem .75rem;
        border: 1px solid var(--bs-border-color);
        border-radius: .5rem;
    }

    tbody td[b-18hf1jo9dg] {
        display: flex;
        gap: .5rem;
        align-items: baseline;
        flex-wrap: wrap;
        width: auto;
        padding: .2rem 0;
        border: 0;
        /* Tô hover của `table-hover` vẽ theo từng ô — ô đã thành khối rời thì thành mảng loang lổ;
           điện thoại ⛔ không có hover nên bỏ hẳn. */
        box-shadow: none;
        background-color: transparent;
    }

    /* ④ NHÃN CỘT — lấy nguyên văn từ `data-nhan` của chính ô ấy.
       ⚠ `flex: 0 0 6.5em` cho mọi nhãn cùng một bề ngang ⇒ cột giá trị thẳng hàng nhau.
       ⚠ Chặn cả ô khai nhãn RỖNG: `attr()` trả chuỗi rỗng nhưng hộp `::before` vẫn chiếm 6,5em
         ⇒ một dòng thụt lề mà ⛔ không chữ nào. */
    tbody td[data-nhan]:not([data-nhan=""])[b-18hf1jo9dg]::before {
        content: attr(data-nhan);
        flex: 0 0 6.5em;
        color: var(--bs-secondary-color);
        font-size: .85em;
    }

    /* ⑤ Ô TÊN VIỆC — tiêu đề của thẻ: trọn dòng, đậm hơn, ⛔ không nhãn (chính nó là tên).
       `anywhere` để mã hồ sơ dài ⛔ không có dấu cách vẫn ngắt được trong khổ 375px. */
    td.cv-o-ten[b-18hf1jo9dg] {
        display: block;
        padding-bottom: .35rem;
        font-weight: 600;
        overflow-wrap: anywhere;
    }

    /* ⑥ Ô DỰ ÁN chỉ có chữ khi việc thuộc một dự án. Việc không thuộc dự án nào thì ô rỗng, mà một
       dòng trống trong thẻ đọc ra như "thiếu dữ liệu" ⇒ giấu hẳn. Dấu hiệu nhận biết là ⛔ KHÔNG
       có `data-nhan` (tệp `.razor` chỉ gắn nhãn khi có dự án). */
    td.cv-o-du-an:not([data-nhan])[b-18hf1jo9dg] {
        display: none;
    }

    /* ⑦ HAI Ô HUY HIỆU (ưu tiên · trạng thái) nối ngang một dòng — huy hiệu tự nói lên mình nên
       ⛔ không cần nhãn, và đứng cạnh nhau thì thẻ vẫn đọc lướt được như một dòng bảng. */
    td.cv-o-nhan[b-18hf1jo9dg] {
        display: inline-flex;
        width: auto;
        margin-right: .5rem;
    }

    /* ⑧ Thanh tiến độ: khai `min-width: 90px` thẳng trong markup là để bảng chừa chỗ; ở thẻ thì
       nó phải nở hết phần còn lại của dòng, sau nhãn. */
    td.cv-o-tien-do > .progress[b-18hf1jo9dg] {
        flex: 1 1 8rem;
    }

    /* ⑨ 📱 ĐỢT 2289 (việc o1826) — DẢI LỌC GIAI ĐOẠN THÀNH LƯỚI HAI CỘT ĐỀU NHAU.
       Anh Tiến 10/09/2026: *"tối ưu giao diện điện thoại cho ngay hàng thẳng lối, đặc biệt là chấm
       công, nhiệm vụ"*.
       📏 Đo trước (375×812 cảm ứng): năm nút giai đoạn xuống dòng theo độ dài chữ (3 nút · 2 nút, mỗi
         nút một bề ngang), nút «⛔ Đã dừng» đứng MỘT MÌNH sát mép trái (hụt mép phải 255px), nút
         «Xuất Excel» gãy hai dòng, và cụm ô tìm thụt vào 12px vì `ms-auto` đẩy nó sang phải.
       🔑 `display: contents` cho `.btn-group`: năm nút của nó thành ô lưới NGANG HÀNG với nút «Đã
         dừng» (đứng ngoài nhóm — nó ⛔ không phải chặng thứ năm) ⇒ sáu nút chia đều 2 × 3. Luật chung
         đợt 2197 ở `app.css` vẫn trả bo góc + gỡ lề âm cho từng nút (nó bám DOM, ⛔ không bám hộp).
       ⚠ Ô tìm trải trọn hàng: `width: 100%` làm `ms-auto` (có `!important`) hết chỗ trống để đẩy. */
    .ds-loc[b-18hf1jo9dg] {
        display: grid;
        grid-template-columns: 1fr 1fr;
    }

    .ds-loc > .btn-group[b-18hf1jo9dg] {
        display: contents;
    }

    .ds-tim[b-18hf1jo9dg] {
        grid-column: 1 / -1;
        width: 100%;
    }

    .ds-o-tim[b-18hf1jo9dg] {
        flex: 1 1 auto;
        min-width: 0;
    }

    .ds-tim > .btn[b-18hf1jo9dg] {
        white-space: nowrap;
    }
}
/* /Components/Pages/CongViec/GiaoViec.razor.rz.scp.css */
/* ═══ FORM GIAO VIỆC ĐẦY ĐỦ — ĐỢT 1635 (việc o928) ═══
   Anh Tiến 01/09/2026, kèm ảnh chụp khối "Thuộc dự án / sửa chữa": *"gom 2 ô này làm 1"*.
   Ô lọc 🔎 và ô chọn hồ sơ nay chung một `input-group`: ô lọc GIÃN, ô chọn CO bên cạnh. Cùng một dáng
   với hộp tạo nhiệm vụ (`HopThoaiTaoNhiemVu.razor.css`) và hộp gắn hồ sơ ở trang chi tiết
   (`ChiTietCongViec.razor.css`) — ba màn hình làm cùng một việc mà lệch dáng là thứ người dùng
   ⛔ không đoán ra.

   🔴 BẪY RIÊNG CỦA TỆP NÀY — `::deep` LÀ BẮT BUỘC, ⛔ ĐỪNG BỎ VÌ TƯỞNG THỪA.
     Ô chọn ở đây ⛔ không phải thẻ `<select>` viết tay mà là component `InputSelect` (nó cần
     `EditContext` của `EditForm` để báo lỗi nhập liệu). CSS cách ly chỉ gắn dấu phạm vi vào phần tử
     do CHÍNH tệp `.razor` này viết ra, nên thẻ select do component CON dựng ra ⛔ KHÔNG mang dấu ấy
     ⇒ viết `.gv-o-ho-so` trần là một luật ⛔ KHÔNG BAO GIỜ CHẠY, mà build xanh và test xanh, và ô chọn
     thì tụt còn bằng cái mũi tên (bài học đợt 1448). `::deep` đẩy dấu phạm vi lên thẻ bọc đứng ngay
     trước nó — thẻ bọc thì đúng là do tệp này viết ra.
   🔴 Ô chọn BẮT BUỘC khai đủ `flex` + `width` + `min-width`, ⛔ đừng chỉ gắn `flex-grow-0` ở markup:
     Bootstrap khai cho ô chọn nằm trong dải nhập `flex: 1 1 auto; width: 1%; min-width: 0`, ba giá
     trị đi cùng nhau mới có nghĩa — `width: 1%` chỉ là CHỖ NEO cho `flex-grow: 1` kéo ra.
   ⚠ Có TRẦN vì bề rộng tự nhiên của select là bề rộng DÒNG DÀI NHẤT (tên hồ sơ dự án thì dài), và có
     SÀN vì dòng mặc định "— Nhiệm vụ độc lập —" ngắn hơn hẳn tên hồ sơ thật.
   👆 ⛔ Đừng ép cả hàng vào một dòng cứng (`flex-nowrap`): `.input-group` tự xuống dòng khi hẹp, và đó
     là đường lui duy nhất cho khổ điện thoại (luật vùng chạm đợt 1518). */
.gv-hang-ho-so[b-cd7i42d993]  .gv-o-ho-so {
    width: auto;
    flex: none;
    min-width: 12rem;
    max-width: 22rem;
}

/* Ô lọc là thứ giãn ra chiếm phần còn lại của hàng. Đặt SÀN để khổ hẹp thì cả hàng XUỐNG DÒNG nguyên
   khối thay vì ô lọc co còn vài ký tự — trong dải nhập, thẻ `form-control` mang sẵn `min-width: 0`
   của Bootstrap.
   ⓘ Ô lọc là thẻ `<input>` viết thẳng trong tệp `.razor` nên nó CÓ dấu phạm vi, ⛔ không cần `::deep`
     ở dòng này — chỉ ô chọn mới cần, vì chỉ ô chọn là do component con dựng ra. */
.gv-hang-ho-so .form-control[b-cd7i42d993] {
    min-width: 10rem;
}
/* /Components/Pages/CongViec/ThongKeNhiemVu.razor.rz.scp.css */
/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   TRANG 📈 THỐNG KÊ NHIỆM VỤ — đợt 1436 (việc o631)

   ⚠ CHÚ THÍCH CSS: đây là chỗ dự án đã trả giá thật (đợt 852). Cặp ký tự đóng chú thích rất dễ lọt
     vào giữa một câu văn khi viết tắt một bộ thuộc tính — chú thích đóng ngay tại đó, phần văn xuôi
     còn lại dính vào bộ chọn của luật nằm ngay dưới, và trình duyệt bỏ NGUYÊN luật đó. ⛔ Không một
     câu lỗi nào: build xanh, test xanh. Hàng rào canh: ChuThichCssDongSomTests.
   ⚠ MÀU lấy từ token Bootstrap 5.3 (--bs-*), ⛔ không gõ mã màu: nền tối và nền sáng dùng chung tệp
     này, mã màu đóng đinh là một trong hai nền đọc không ra chữ.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */

/* ═══ Ô KỲ TRÊN ĐẦU TRANG — thẻ button đội lốt huy hiệu ═══
   Chép dáng của .tqnv-nut-ky bên trang Tổng quan là CỐ Ý: hai tab đứng cạnh nhau trên cùng dải nav,
   ô kỳ trông khác nhau thì người dùng tưởng là hai thứ khác nhau. */
.tknv-nut-ky[b-20b5tdtmd9] {
    cursor: pointer;
    /* Thẻ button không thừa hưởng phông chữ của trang; thiếu dòng này nó rơi về phông mặc định của hệ
       điều hành và ô kỳ trông khác hẳn mọi huy hiệu bên cạnh. */
    font-family: inherit;
    transition: filter .14s ease-out;
}

.tknv-nut-ky:hover[b-20b5tdtmd9] {
    filter: brightness(1.12);
}

/* Vòng tiêu điểm cho người đi bằng bàn phím. Thẻ button mượn lớp badge nên mất vòng mặc định của
   Bootstrap, mà đây lại là cửa duy nhất để chọn kỳ ở xa. */
.tknv-nut-ky:focus-visible[b-20b5tdtmd9] {
    outline: 2px solid var(--bs-focus-ring-color, rgba(13, 110, 253, .5));
    outline-offset: 2px;
}

/* ═══ BIỂU ĐỒ XU HƯỚNG ═══
   Sáu cột, mỗi cột hai thanh (lập mới · hoàn thành).

   ⚠ CHIỀU CAO KHUNG PHẢI ĐẶT TƯỜNG MINH. Thanh bên trong cao theo phần trăm (height: N%), mà phần
     trăm chiều cao chỉ có nghĩa khi cha có chiều cao XÁC ĐỊNH — cha cao "tự động" thì trình duyệt
     coi mọi thanh là cao 0 và biểu đồ biến mất. ⛔ Không câu lỗi nào, khung vẫn vẽ ra. */
.tknv-xu-huong[b-20b5tdtmd9] {
    display: flex;
    align-items: flex-end;
    gap: .5rem;
    height: 190px;
    /* Cột nhiều hơn sức chứa thì CUỘN TRONG khung của mình, ⛔ không đẩy cả trang cuộn ngang. */
    overflow-x: auto;
    padding-top: .25rem;
}

.tknv-cot[b-20b5tdtmd9] {
    flex: 1 1 0;
    /* Sàn bề ngang: nhãn kỳ ngắn nhất là 5 ký tự (24/08), hẹp hơn nữa là chữ xuống dòng giữa cột. */
    min-width: 52px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    height: 100%;
}

/* Cặp hai thanh của một kỳ. Đây là phần tử mang chiều cao thật để hai thanh con lấy phần trăm. */
.tknv-cap[b-20b5tdtmd9] {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 3px;
    /* Chừa chỗ cho hai dòng chữ dưới chân cột (số việc + nhãn kỳ). */
    height: calc(100% - 2.6rem);
}

.tknv-thanh[b-20b5tdtmd9] {
    display: block;
    width: 14px;
    border-radius: 3px 3px 0 0;
    /* Thanh có số việc bằng 0 thì height: 0% — nó biến mất hẳn, và đó là ĐÚNG: sàn 4% của
       CaoCotXuHuong chỉ áp cho số việc > 0. Xem lời dẫn của hàm đó. */
    min-height: 0;
    transition: filter .14s ease-out;
}

.tknv-thanh-giao[b-20b5tdtmd9] {
    background-color: var(--bs-primary, #0d6efd);
}

.tknv-thanh-xong[b-20b5tdtmd9] {
    background-color: var(--bs-success, #198754);
}

.tknv-cot:hover .tknv-thanh[b-20b5tdtmd9] {
    filter: brightness(1.15);
}

.tknv-so[b-20b5tdtmd9] {
    text-align: center;
    font-size: .72rem;
    line-height: 1.1;
    margin-top: .2rem;
    color: var(--bs-secondary-color, #6c757d);
    /* Số dài (128 / 96) không được kéo cột rộng ra làm lệch hàng — cắt gọn và giữ nguyên bề ngang. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tknv-nhan[b-20b5tdtmd9] {
    text-align: center;
    font-size: .72rem;
    line-height: 1.1;
    color: var(--bs-secondary-color, #6c757d);
    white-space: nowrap;
}

/* Kỳ ĐANG XEM: dày hơn hẳn về mặt chữ, cộng một nền mờ — hai kênh chứ không chỉ một, để người không
   phân biệt được màu vẫn nhận ra cột nào đang được chọn. */
.tknv-cot-dang-xem .tknv-nhan[b-20b5tdtmd9],
.tknv-cot-dang-xem .tknv-so[b-20b5tdtmd9] {
    font-weight: 600;
    color: var(--bs-body-color, #212529);
}

.tknv-cot-dang-xem[b-20b5tdtmd9] {
    background-color: var(--bs-tertiary-bg, rgba(0, 0, 0, .04));
    border-radius: .35rem;
}

/* ⭐ ĐỢT 1453 (việc o649) — DÒNG PHÂN BỐ BẤM ĐƯỢC.
   Nền sáng lên khi rê chuột hoặc khi nhận tiêu điểm bàn phím, để người ta nhìn ra đây là một lối đi
   chứ không phải một dòng số chết.
   ⛔ Cố ý KHÔNG đổi màu chữ sang màu liên kết: bốn dòng này là SỐ LIỆU, tô chúng thành màu liên kết là
     nói rằng chúng khác loại với mọi con số còn lại trên trang.
   ⚠ Đệm âm bằng đúng đệm dương để dòng KHÔNG xê dịch một pixel nào khi mọc nền — nhảy chữ lúc rê chuột
     đọc ra thành trang đang giật. */
.tknv-dong-bam[b-20b5tdtmd9] {
    /* ⭐ o1276 — `display: block` đặt Ở ĐÂY thay cho lớp `d-block` trong markup: `d-block` mang
       `!important` nên khối vùng chạm bên dưới ⛔ không đổi được nó thành flex để canh giữa. */
    display: block;
    border-radius: .35rem;
    padding: .25rem .4rem;
    margin: -.25rem -.4rem;
}

/* ⭐ o1276 — con số TỔNG bấm được (ô thẻ số bên trái), cùng lẽ với `.tknv-dong-bam` ở trên. */
.tknv-so-tong[b-20b5tdtmd9] {
    display: block;
}

.tknv-dong-bam:hover[b-20b5tdtmd9],
.tknv-dong-bam:focus-visible[b-20b5tdtmd9] {
    background-color: var(--bs-tertiary-bg, rgba(0, 0, 0, .04));
}

/* ─────────── ⭐ ĐỢT 1979 (việc o1261) — SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY ───────────
   📏 Đo sống 04/09/2026 (Chrome thật, 390×844, cảm ứng, `tienvm`):
      `node cong-cu/do-song-khung-hep.mjs --trang /thong-ke-nhiem-vu` ⇒ **12/12** chỗ chữ nhỏ của
      trang này là `.tknv-so` và `.tknv-nhan`, cả hai đo ra **11,5px** (luật `.72rem` ở trên) trong
      khi sàn của dự án `--cham-chu-toi-thieu` là **12px**. Đây là hai dòng chú số dưới mỗi cột của
      biểu đồ tuần — thứ người ta phải đọc để biết cột ấy là tuần nào và bao nhiêu việc.

   ⚠ Cột biểu đồ có bề ngang cố định, nên nâng cỡ chữ có thể làm nhãn tuần (`31/08`) chạm nhau.
      `.tknv-nhan` đang `white-space: nowrap` ⇒ ⛔ không xuống dòng, nó chỉ sát lại. Đã đo lại sau
      khi sửa: trang vẫn **tràn ngang 0**. Đổi bố cục cột thì đo lại chỗ này.
   ⚠ `max()` chứ ⛔ không gõ thẳng 12px — nâng SÀN, giữ tỉ lệ; đổi biến thì chỗ này đi theo. */
@media (pointer: coarse) {
    .tknv-so[b-20b5tdtmd9],
    .tknv-nhan[b-20b5tdtmd9] {
        font-size: max(.72rem, var(--cham-chu-toi-thieu));
    }
}

/* ─────────── ⭐ o1276 (05/09/2026) — VÙNG CHẠM 44px CHO BẢY CHỖ BẤM CỦA TRANG ───────────
   📏 Đo sống 05/09/2026 (Chrome thật, 390×844, cảm ứng, `tienvm`):
      `node cong-cu/do-song-khung-hep.mjs --trang /thong-ke-nhiem-vu` ⇒ **7 vùng bấm nhỏ**, ⛔ không
      cái nào là ô nhập trong bảng (khuôn «bảng hoá thẻ» của đợt 1989 ⛔ không áp được ở đây):
        · `button.tknv-nut-ky` (ô kỳ đội lốt huy hiệu)     163,8 × **20,4px**
        · `a.tknv-so-tong` (con số TỔNG mở danh sách)       326 × **23,7px**
        · `a.tknv-lien-ket-dung` («N nhiệm vụ đã dừng»)     326 × **42px** — liên kết INLINE trải hai dòng
        · `a.tknv-dong-bam` × 4 (bốn dòng phân bố giai đoạn) 344,8 × **37px**

   🔑 Ba chỗ là thẻ `a` mang `d-block` trong markup — lớp ấy của Bootstrap có `!important`, nên đặt
      `display: flex` ở đây là viết một luật ⛔ không bao giờ chạy. Vì thế markup đổi `d-block` thành
      lớp riêng, còn `display: block` cho máy tính đặt ở luật nền phía trên — máy tính ⛔ không đổi.
   ⚠ `min-height` chứ ⛔ không `height`: dòng phân bố mang hai hàng (nhãn + vạch tiến độ), ngày có
      ba hàng thì `height` cắt mất vạch.
   ⚠ `justify-content: center` cho dòng phân bố: thẻ cao lên 7px (37 → 44) thì nội dung phải nằm
      giữa, ⛔ không dính mép trên để lại một dải trống dưới vạch tiến độ.
   ⚠ Liên kết «đã dừng» nằm GIỮA một câu văn ⇒ `inline-flex` chứ ⛔ không `flex`: `flex` đẩy nó ra
      thành dòng riêng, câu «Trong đó … — đứng ngoài bốn giai đoạn» gãy làm ba mảnh.
   🔒 Toàn bộ trong `@media (pointer: coarse)`; chốt ngược `--chuot` 1369×900 phải TRÙNG KHÍT.
   🚧 Hàng rào: TKNB.Tests/VungChamPhanTrangVaThongKeTests.cs */
@media (pointer: coarse) {
    .tknv-nut-ky[b-20b5tdtmd9] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }

    .tknv-so-tong[b-20b5tdtmd9] {
        min-height: var(--cham-toi-thieu);
        display: flex;
        align-items: center;
    }

    .tknv-lien-ket-dung[b-20b5tdtmd9] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }

    .tknv-dong-bam[b-20b5tdtmd9] {
        min-height: var(--cham-toi-thieu);
        display: flex;
        flex-direction: column;
        justify-content: center;
    }
}

/* ═══ ⭐ ĐỢT 2193 (việc o1635) — KHỔ ĐIỆN THOẠI: MỖI DÒNG BẢNG ⑤ THÀNH MỘT THẺ ═══

   Anh Tiến giao 07/09/2026: "tiếp tục làm phân hệ nhiệm vụ, chú trọng đến giao diện điện thoại".

   📏 ĐO SỐNG 375×812 (bản dựng từ HEAD, cổng riêng) TRƯỚC khi vá — bảng ⑤ «Theo người thực hiện»:
     · bảng này ⛔ KHÔNG tràn (317 = 317) — khác hai trang kia, ⇒ bệnh ở đây ⛔ không phải cuộn ngang;
     · nhưng SÁU cột chen trong 317px: cột tên còn 65px nên "Vũ Mạnh Tiến" xuống 3 DÒNG, và hàng
       tiêu đề cao 81px vì cả sáu tên cột đều gãy làm 3–4 dòng ("Người thực hiện" · "Xong trong
       tuần"). Một bảng mà tiêu đề chiếm 81px trước khi đọc được dòng đầu tiên.

   🔑 CÁCH SỬA — cùng một khuôn với hai trang kia (đợt 2169, việc o1613): xếp mỗi DÒNG thành một THẺ.
     🔴 Ở bảng này nhãn `data-nhan` là thứ BẮT BUỘC chứ ⛔ không phải trang trí: năm ô còn lại đều là
     SỐ TRẦN, bỏ hàng tiêu đề mà ⛔ không gắn nhãn là để lại "12 3 1 8 75%" ⛔ không ai đọc nổi.
     Năm ô số đứng NỐI NGANG (⛔ không mỗi số một dòng): chúng ngắn, và xếp dọc thì một người chiếm
     sáu dòng ⇒ màn hình 812px chỉ còn thấy hai người, thua cả bảng cũ.

   ⚠ Mốc 576px = `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx` — hàng rào `BangNhiemVuHoaTheTests`
     đối chiếu con số này với CẢ BA tệp CSS của phân hệ nhiệm vụ. ⛔ Đừng sửa một mình ở đây.
   ⛔ Khối này CỐ Ý ⛔ KHÔNG đụng tới CỠ CHỮ: hai luật `font-size` nhỏ hơn 12px ở tệp này đang thuộc
     việc `o1519` của phiên khác — sửa chồng là hai phiên giẫm nhau trên cùng một dòng.
   ⚠ MỘT điều kiện thôi, ⛔ đừng ghép `and` (bài học đợt 1481 với tệp `.razor.css`).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — cặp ký tự ấy lọt vào giữa câu văn là chú
     thích đóng sớm và trình duyệt nuốt nguyên luật ngay dưới, ⛔ không một câu lỗi nào. */
@media screen and (max-width: 576px) {

    thead[b-20b5tdtmd9] {
        display: none;
    }

    /* `table` cũng phải khai lại: luật chung của app.css đã cho nó `overflow-x: auto`, mà khung
       cuộn ⛔ không còn việc gì để làm khi ⛔ không còn cột nào. */
    table[b-20b5tdtmd9],
    tbody[b-20b5tdtmd9],
    tr[b-20b5tdtmd9],
    td[b-20b5tdtmd9] {
        display: block;
        width: 100%;
    }

    tbody tr[b-20b5tdtmd9] {
        margin-bottom: .75rem;
        padding: .5rem .75rem;
        border: 1px solid var(--bs-border-color);
        border-radius: .5rem;
    }

    tbody td[b-20b5tdtmd9] {
        width: auto;
        padding: .15rem 0;
        border: 0;
        /* Tô hover của `table-hover` vẽ theo từng ô — ô đã thành khối rời thì thành mảng loang lổ;
           điện thoại ⛔ không có hover nên bỏ hẳn. */
        box-shadow: none;
        background-color: transparent;
        /* Lớp `text-end` căn phải, hợp với bảng nhưng ⛔ không hợp với thẻ.
           🔴 `!important` ở đây là BẮT BUỘC, ⛔ không phải thói quen xấu: lớp tiện ích của Bootstrap
             (`.text-end`) tự nó đã khai `!important`, nên luật thường vẫn THUA trong im lặng. */
        text-align: start !important;
    }

    /* Ô TÊN NGƯỜI — tiêu đề của thẻ: trọn dòng, đậm hơn, ⛔ không nhãn (chính nó là tên). */
    td.tknv-o-ten[b-20b5tdtmd9] {
        padding-bottom: .3rem;
        font-weight: 600;
        overflow-wrap: anywhere;
    }

    /* NĂM Ô SỐ nối ngang thành một dải "nhãn giá trị · nhãn giá trị …". */
    td.tknv-o-so[b-20b5tdtmd9] {
        display: inline-block;
        margin-right: .9rem;
    }

    td.tknv-o-so[data-nhan]:not([data-nhan=""])[b-20b5tdtmd9]::before {
        content: attr(data-nhan) ": ";
        color: var(--bs-secondary-color);
    }
}
/* /Components/Pages/CongViec/TongQuanNhiemVu.razor.rz.scp.css */
/* ═══ TRANG TỔNG QUAN NHIỆM VỤ — cân cột bảng "Nhiệm vụ đang thực hiện" (đợt 1291, việc o481) ═══

   Anh Tiến 25/08/2026: "bảng nhiệm vụ chưa xong đổi tên thành nhiệm vụ đang thực hiện, em cân chỉnh
   lại các cột cho đẹp".

   ⚠ BẪY CỦA TỆP CSS (bài học đợt 852): chú thích CSS đóng bằng dấu sao rồi gạch chéo, mà cặp ký tự ấy
     rất dễ lọt vào giữa câu văn khi viết tắt một bộ thuộc tính. Chú thích đóng ngay tại đó, phần chữ
     còn lại dính vào bộ chọn nằm dưới và trình duyệt bỏ NGUYÊN luật đó — build xanh, test xanh, chỉ
     mắt người mới thấy. Nên trong tệp này mọi tên thuộc tính đều viết đủ chữ.
   ⚠ Đây là CSS cách ly (scoped) nên biên dịch ra dạng có hậu tố b-xxxx, độ đặc hiệu chỉ (0,2,0).
     ⛔ Đừng đặt ở đây thứ phải thắng luật của app.css hay của Bootstrap — xem GOTCHA ở
     KhoiMucTieuTrangChu.razor. Bốn luật dưới đây chỉ đụng lớp riêng của trang nên không tranh với ai. */

/* Bề rộng cột lấy theo colgroup, KHÔNG theo nội dung của lượt tải này — không có dòng này thì một cái
   tên nhiệm vụ dài bóp cột Tiến độ lại, và bảng đổi bề rộng mỗi lần anh bấm sang kỳ khác. */
.tqnv-bang-viec[b-qc5318lijf] {
    table-layout: fixed;
}

/* Tên người: một dòng, dài thì cắt bằng dấu ba chấm. Tên đầy đủ vẫn đọc được ở thuộc tính title của
   chính ô đó, nên không mất thông tin nào. */
.tqnv-o-ten[b-qc5318lijf] {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Con số phần trăm đứng NGOÀI thanh (xem chú thích trong .razor): chốt bề rộng tối thiểu để mọi hàng
   thẳng một mép, và dùng chữ số cùng bề ngang để cột số không nhảy giữa 0%, 30%, 100%. */
.tqnv-o-phan-tram[b-qc5318lijf] {
    min-width: 2.4rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Hạn chót: không ngắt dòng giữa ngày và giờ, và cũng dùng chữ số cùng bề ngang cho thẳng cột. */
.tqnv-o-ngay[b-qc5318lijf] {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* ═══ Ô KỲ TRÊN ĐẦU TRANG — thẻ button đội lốt huy hiệu (đợt 1295, việc o485) ═══

   Anh Tiến 25/08/2026: "bỏ nút tuần trước và tuần sau, muốn chọn tuần thì nhấn vào ô tuần để chọn
   tuần cần xem".

   Ô này phải là thẻ button THẬT (bàn phím đi tới được, Enter bấm được, bộ đọc màn hình đọc ra là nút),
   nhưng dáng thì giữ nguyên huy hiệu cũ để anh không phải học lại chỗ nhìn. Ba luật dưới đây chỉ bù
   đúng những gì thẻ button đánh mất khi mượn lớp badge của Bootstrap.

   Lớp badge của Bootstrap có độ đặc hiệu (0,1,0), luật scoped ở đây biên dịch ra (0,2,0) nên thắng —
   đủ, và không tranh chấp với ai khác. */
.tqnv-nut-ky[b-qc5318lijf] {
    cursor: pointer;
    /* Thẻ button không thừa hưởng phông chữ của trang; thiếu dòng này nó rơi về phông mặc định của hệ
       điều hành và ô kỳ trông khác hẳn mọi huy hiệu bên cạnh. */
    font-family: inherit;
    transition: filter .14s ease-out, box-shadow .14s ease-out;
}

.tqnv-nut-ky:hover[b-qc5318lijf] {
    filter: brightness(1.12);
}

/* Vòng tiêu điểm cho người đi bằng bàn phím. Thẻ button mượn lớp badge nên mất vòng mặc định của
   Bootstrap, mà đây lại là cửa duy nhất để đổi kỳ sau khi hai nút mũi tên đã bỏ. */
.tqnv-nut-ky:focus-visible[b-qc5318lijf] {
    outline: 2px solid var(--bs-focus-ring-color, rgba(13, 110, 253, .5));
    outline-offset: 2px;
}

/* ═══ SÁU Ô THÔNG SỐ — thẻ button đội lốt thẻ card (đợt 1295, việc o496) ═══

   Anh Tiến 25/08/2026: "khi nhấn vào 1 ô ví dụ giao việc sẽ ra hộp hội thoại hiển thị nội dung".

   Cả ô là MỘT nút, vì cả ô chỉ có đúng một việc để làm. Ba luật dưới đây bù những gì thẻ button đánh
   mất khi mượn lớp card của Bootstrap; phần đồ họa (kính mờ, vệt sáng, nhấc lên khi rê chuột) do bộ
   thẻ số dùng chung trong app.css lo, đừng khai lại ở đây. */
.tqnv-o-thong-so[b-qc5318lijf] {
    cursor: pointer;
    font-family: inherit;
}

/* Vòng tiêu điểm cho người đi bằng bàn phím — nút mượn lớp card nên mất vòng mặc định của Bootstrap. */
.tqnv-o-thong-so:focus-visible[b-qc5318lijf] {
    outline: 2px solid var(--bs-focus-ring-color, rgba(13, 110, 253, .5));
    outline-offset: 2px;
}

/* Bấm xuống thì lún nhẹ — kênh phản hồi thứ hai ngoài quầng sáng khi rê chuột, và là thứ duy nhất
   thấy được trên màn hình cảm ứng (nơi không có trạng thái rê chuột). */
.tqnv-o-thong-so:active[b-qc5318lijf] {
    transform: translateY(1px);
}

/* ═══ ⭐ VIỆC o962 — VÙNG CHẠM NGÓN TAY QUANH Ô TICK CỦA TỪNG MỤC TIÊU ═══

   📏 ĐO SỐNG 03/09/2026 (Chrome thật, khung 390×844 cảm ứng, app dựng riêng từ một `git worktree`
     ở cổng 5304, phép đo hỏi thẳng `document.elementFromPoint` — ⛔ không đọc CSS, ⛔ không đọc hộp
     cha): ô tick của thẻ 🎯 Mục tiêu ra **ô vuông 16×16 · vùng bấm thật 17px**, ngưỡng là 44px.
   ✅ ĐO LẠI 06/09/2026 TRÊN BẢN ĐÃ VÁ (cùng khung, cùng phép đo): **45px**, 0 ô dưới ngưỡng. Lượt
     ấy cũng dựng lại bản CHƯA VÁ trong cùng một worktree và đo ra đúng **17px** — tức con số 17px
     ở dòng trên tái hiện được, ⛔ không phải một lời khai cũ chép lại.
   ⚠ Số ô trên màn hình bằng số mục tiêu của kỳ đang xem, tức nó ĐỔI THEO DỮ LIỆU. Lượt đo 03/09
     bắt được đúng MỘT ô vì tài khoản đo chỉ đăng ký một mục tiêu tuần — ⛔ đừng đọc con số ấy
     thành *"màn hình này chỉ có một ô tick"*.

   🔴 Ô TICK TRẦN ⛔ KHÔNG CHỮA ĐƯỢC BẰNG CSS CÁCH LY MỘT MÌNH. Thứ đứng cạnh ô là một thẻ `div`
     đựng nội dung mục tiêu, ⛔ không phải một nhãn nối bằng `for` ⇒ bấm vào chữ ⛔ không đổi tick,
     và ⛔ không có thẻ nào bấm được để mà nới. Vì thế tệp `.razor` đi kèm bọc ô vào một thẻ `label`
     mang lớp dưới đây; luật ở đây chỉ cấp cho thẻ ấy cái cỡ.

   ⚠ `align-items: flex-start` chứ ⛔ KHÔNG `center` — đây là chỗ tệp này KHÁC khuôn kiểu ② của đợt
     1690 (ở đó ô tick đứng một mình trong một ô bảng). Hàng ở đây là một thẻ `li` mang
     `d-flex align-items-start`: cho `center` thì ô vuông rơi xuống GIỮA hộp cao 44px, tức tụt khỏi
     dòng chữ đầu tiên chừng một dòng — một lượt vá VÙNG CHẠM mà lại xê dịch thứ người ta nhìn thấy.
   ⚠ Cần CẢ bề ngang: ô tick trần ⛔ không có chữ bên cạnh để mượn (chữ là thẻ `div`, bấm ⛔ không
     ăn), nới mỗi chiều cao là ngón tay vẫn trượt NGANG ra khỏi vùng bấm.
   ⚠ Dùng sàn chứ ⛔ không đặt chiều cao cứng: mục tiêu dài xuống nhiều dòng thì cứ để ô cao theo.
   🔴 Điều kiện là CÁI CHẠM VÀO, ⛔ không phải bề ngang màn hình: cửa sổ thu nhỏ trên máy tính vẫn
     là chuột, còn màn hình rộng vẫn có thể là cảm ứng.
   ⚠ Ngưỡng đọc từ BIẾN khai một chỗ ở `app.css` — ⛔ đừng gõ con số vào tệp này.
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Đo lại: node cong-cu/do-song-o-tick-cham.mjs --trang tong-quan-nhiem-vu */

/* 🖱 NỀN (MÁY TÍNH): THẺ BỌC ⛔ KHÔNG ĐƯỢC SINH HỘP — ⛔ ĐỪNG GỠ DÒNG NÀY.
   📏 Đo sống 06/09/2026 (cùng bản dựng, cùng tài khoản, khung CHUỘT 1369×900, `elementFromPoint`):
     trước khi bọc **17px** · bọc rồi mà ⛔ chưa có luật dưới đây **25px (+8)** · có luật **17px**.
   🔑 Thẻ `label` là markup ⛔ KHÔNG có điều kiện, nên ngay ở chế độ chuột — nơi câu media bên dưới
     ⛔ không áp — nó vẫn sinh một hộp dòng cao 24px (cao theo `line-height`, ⛔ không theo ô vuông
     16px). Hộp ấy ⛔ không nhìn thấy nhưng BẤM ĐƯỢC ⇒ người ngồi chuột bấm hụt lên/xuống 4px là đổi
     trạng thái một mục tiêu. Đó là đổi HÀNH VI, dù bố cục nhìn thấy vẫn trùng khít từng pixel (đo
     06/09: ô tick · hàng · cột chữ · chiều cao trang đều y nguyên trước và sau).
   ⇒ `display:contents` (chú thích CỐ Ý viết LIỀN, ⛔ không gõ nguyên văn dòng mã: bộ đo đột biến
     neo bằng chuỗi thô, gõ y hệt là nó gieo trúng CHÚ THÍCH rồi báo "bắt được" — đèn xanh giả)
     ⇒ thẻ bọc ⛔ không sinh hộp nào, ô tick trở lại làm mục flex đúng như
     trước khi vá; câu media bên dưới đặt lại `inline-flex` nên màn cảm ứng ⛔ không mất gì.
   ⚠ Dòng này phải đứng TRƯỚC câu media: hai luật cùng độ đặc hiệu, thắng thua theo THỨ TỰ. */
.o-tick-cham[b-qc5318lijf] {
    display: contents;
}

@media (pointer: coarse) {
    .o-tick-cham[b-qc5318lijf] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: flex-start;
    }
}

/* ═══ 👆 Ô KỲ PHẢI ĐỦ SÀN CHẠM NGÓN TAY — đợt 2046, việc o1330 ═══

   📏 ĐO SỐNG TRƯỚC KHI SỬA (Chrome thật, khung 390×844, chế độ cảm ứng, đăng nhập tienvm, app dựng
     từ worktree riêng cổng 5287 — `node cong-cu/do-song-khung-hep.mjs --trang tong-quan-nhiem-vu`):
     ô kỳ «Tuần 31/08 – 06/09/2026 ▾» (`button.badge.bg-secondary.border-0`) đo **163,8 × 20,4px**
     ⇒ chiều cao chỉ bằng **46%** ngưỡng ngón tay. Nó là một trong ĐÚNG HAI vùng bấm dưới ngưỡng của
     cả trang; chỗ còn lại là ô tick kiểu ③, phải đo bằng `do-song-o-tick-cham.mjs` nên ⛔ không vá ở
     đây.

   🔴 ⛔ ĐỪNG ĐỌC THÀNH «bản vá đợt 2030 đẻ ra lỗi này». Trước đợt 2030, dải nút đầu trang ⛔ không
     xuống dòng được nên nó BÓP ô kỳ còn 75,7px bề ngang và chữ rơi xuống 3 dòng ⇒ hộp cao 56,4px,
     tức nó «đạt» 44px chỉ nhờ CHÍNH cái tràn ngang 83px mà đợt 2030 đi chữa. Ô kỳ vốn là một huy
     hiệu cao 20,4px: thiếu sàn chạm là lỗi CÓ SẴN, đợt 2030 chỉ gỡ tấm che đi.

   🔑 VÌ SAO ĐẶT Ở ĐÂY chứ ⛔ không sửa luật .badge của app.css: luật ấy chạy ở MỌI phân hệ và phần
     lớn huy hiệu là chữ ĐỌC chứ ⛔ không bấm được — nong nó lên 44px là đổi bố cục toàn app để chữa
     đúng một cái nút (rủi ro lan truyền, quy tắc #18). Đây là CSS cách ly của riêng trang, biên dịch
     ra `.tqnv-nut-ky[b-…]` (0,2,0) nên thắng `.badge` (0,1,0) mà ⛔ không tranh với ai.
     Khuôn lấy nguyên từ `.tknv-nut-ky` của `ThongKeNhiemVu.razor.css` (việc o1276) — cùng một loại ô
     kỳ, ⛔ đừng nghĩ ra cách thứ hai cho cùng một bài.

   ⚠ `min-height` chứ ⛔ không `height`: ngày nhãn kỳ dài ra và xuống hai dòng thì `height` cắt mất
     dòng dưới mà ⛔ không câu lỗi nào.
   ⚠ `inline-flex` cộng `align-items: center` là BẮT BUỘC đi kèm: chỉ nâng `min-height` thôi thì hộp
     cao 44px nhưng con chữ dính mép trên, để lại một dải trống 24px — vùng bấm đủ mà mắt đọc ra là
     lỗi. `inline-flex` chứ ⛔ không `flex`: ô kỳ đứng trong dải nút, cùng hàng với nhóm 4 nút chọn kỳ.
   🔒 Toàn bộ nằm trong `@media (pointer: coarse)` — máy tính (chuột) ⛔ không đổi một pixel. Đo chốt
     ngược 1369×900 chế độ chuột trước/sau: ô kỳ 163,8 × 20,4px, mép phải 1345 — phải TRÙNG KHÍT.
   ⚠ Nhóm 4 nút chọn kỳ (Tuần/Tháng/Quý/Năm) ĐÃ ĐỦ SÀN sẵn — đo cùng lượt: 49,4 / 57,7 / 44 / 47,6
     nhân 44px, do luật `.btn` trong khối chạm của `app.css` (đợt 1518) lo. ⇒ ⛔ KHÔNG khai lại ở đây:
     một luật chép thừa là ngày `app.css` đổi ngưỡng thì trang này lặng lẽ đi một đường riêng.
   🚧 Hàng rào: TKNB.Tests/VungChamOKyTongQuanNhiemVuTests.cs
   📏 Đo lại: node cong-cu/do-song-khung-hep.mjs --trang tong-quan-nhiem-vu --cong <cổng> */
@media (pointer: coarse) {
    .tqnv-nut-ky[b-qc5318lijf] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }
}

/* ═══ ⭐ ĐỢT 2193 (việc o1635) — KHỔ ĐIỆN THOẠI: MỖI DÒNG BẢNG THÀNH MỘT THẺ ═══

   Anh Tiến giao 07/09/2026: "tiếp tục làm phân hệ nhiệm vụ, chú trọng đến giao diện điện thoại".

   📏 ĐO SỐNG 375×812 (bản dựng từ HEAD, cổng riêng) TRƯỚC khi vá — bảng «Nhiệm vụ đang thực hiện»:
     · trang ⛔ KHÔNG trôi ngang (375 = 375), nhưng bảng phải cuộn NGANG trong khung của nó:
       bề ngang thật 405px / khung 349px ⇒ 56px nằm ngoài tầm mắt;
     · cột TÊN còn 57px ⇒ tên nhiệm vụ xuống dòng thành 10 DÒNG, chiều cao một dòng bảng 250px.

   🔑 VÌ SAO: `colgroup` ghim ba cột cuối ở 110 + 130 + 120px và cột Người nhận ở 22%, cộng thêm
     `table-layout: fixed` của `.tqnv-bang-viec` ⇒ cột tên chỉ được ăn phần CÒN LẠI. Ở màn hình rộng
     đó chính là thứ giữ cho bảng thôi nhảy; ở 375px thì phần còn lại gần như bằng không.

   🔑 CÁCH SỬA — đúng khuôn đợt 2169 (việc o1613) đã chữa hộp «Nhiệm vụ của tôi» ở trang chủ: xếp
     mỗi DÒNG thành một THẺ. Tên trọn dòng; các ô còn lại thành dòng «nhãn — giá trị» lấy nhãn từ
     `data-nhan` của chính ô ấy; ô huy hiệu trạng thái tự nói lên mình nên nối ngang, ⛔ không nhãn.
     Luật này áp cho CẢ BA bảng của trang (hộp «đang thực hiện» · hộp ô thông số · hộp chọn nhiệm vụ
     để link mục tiêu) — chúng cùng một component nên cùng một dấu cách ly.

   ⚠ Mốc 576px = `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx` — hàng rào `BangNhiemVuHoaTheTests`
     đối chiếu con số này với CẢ BA tệp CSS của phân hệ nhiệm vụ. ⛔ Đừng sửa một mình ở đây.
   ⚠ MỘT điều kiện thôi, ⛔ đừng ghép `and` (bài học đợt 1481 với tệp `.razor.css`).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — cặp ký tự ấy lọt vào giữa câu văn là chú
     thích đóng sớm và trình duyệt nuốt nguyên luật ngay dưới, ⛔ không một câu lỗi nào. */
@media screen and (max-width: 576px) {

    thead[b-qc5318lijf] {
        display: none;
    }

    /* `table` cũng phải khai lại: luật chung của app.css đã cho nó `overflow-x: auto`, mà khung
       cuộn ⛔ không còn việc gì để làm khi ⛔ không còn cột nào. */
    table[b-qc5318lijf],
    tbody[b-qc5318lijf],
    tr[b-qc5318lijf],
    td[b-qc5318lijf] {
        display: block;
        width: 100%;
    }

    tbody tr[b-qc5318lijf] {
        margin-bottom: .75rem;
        padding: .5rem .75rem;
        border: 1px solid var(--bs-border-color);
        border-radius: .5rem;
    }

    tbody td[b-qc5318lijf] {
        display: flex;
        gap: .5rem;
        align-items: baseline;
        flex-wrap: wrap;
        width: auto;
        padding: .2rem 0;
        border: 0;
        /* Tô hover của `table-hover` vẽ theo từng ô — ô đã thành khối rời thì thành mảng loang lổ;
           điện thoại ⛔ không có hover nên bỏ hẳn. */
        box-shadow: none;
        background-color: transparent;
        /* Lớp `text-end` căn phải, hợp với bảng nhưng ⛔ không hợp với thẻ: mọi giá trị phải bắt đầu
           thẳng một cột.
           🔴 `!important` ở đây là BẮT BUỘC, ⛔ không phải thói quen xấu: lớp tiện ích của Bootstrap
             (`.text-end`) tự nó đã khai `!important`, nên luật thường — dù đặc hiệu hơn — vẫn THUA
             trong im lặng. Đo sống 375px trước khi thêm: ô Hạn chót vẫn ra `text-align: right`. */
        text-align: start !important;
    }

    tbody td[data-nhan]:not([data-nhan=""])[b-qc5318lijf]::before {
        content: attr(data-nhan);
        flex: 0 0 6.5em;
        color: var(--bs-secondary-color);
        font-size: .85em;
    }

    /* Ô TÊN NHIỆM VỤ — tiêu đề của thẻ: trọn dòng, đậm hơn, ⛔ không nhãn (chính nó là tên). */
    td.tqnv-o-ten-viec[b-qc5318lijf] {
        display: block;
        padding-bottom: .35rem;
        font-weight: 600;
        overflow-wrap: anywhere;
    }

    /* 🔴 Ô TÊN NGƯỜI phải THÔI cắt ba chấm ở khổ thẻ: luật `.tqnv-o-ten` ở đầu tệp sinh ra để cứu
       một CỘT hẹp có bề ngang cố định; thẻ thì rộng trọn màn hình, cắt ở đây là giấu chữ mà ⛔ không
       vì lý do gì. `min-width: 0` là thứ BẮT BUỘC đi kèm `flex`, ⛔ đừng gỡ. */
    td.tqnv-o-ten[b-qc5318lijf] {
        white-space: normal;
        overflow: visible;
        text-overflow: clip;
        min-width: 0;
    }

    /* Huy hiệu trạng thái nối ngang, ⛔ không chiếm trọn một dòng cho một con chữ. */
    td.tqnv-o-nhan[b-qc5318lijf] {
        display: inline-flex;
        width: auto;
        margin-right: .5rem;
    }

    /* Thanh tiến độ nở hết phần còn lại của dòng, sau nhãn. */
    td.tqnv-o-tien-do > .d-flex[b-qc5318lijf] {
        flex: 1 1 8rem;
    }

    /* Dải nút «Link / Gỡ» xuống dòng riêng và trải trọn bề ngang — vùng bấm rộng hơn hẳn so với
       hai nút nép bên mép phải. */
    td.tqnv-o-nut[b-qc5318lijf] {
        justify-content: flex-start;
        padding-top: .35rem;
    }
}
/* /Components/Pages/DanhGia/TrangDanhGiaQuy.razor.rz.scp.css */
/* ═══ KHỔ HẸP: BẢNG XẾP LOẠI QUÝ HOÁ THẺ MỖI DÒNG MỘT KHỐI (đợt 1989, việc o1260) ═══

   ⛔ ĐÂY ⛔ KHÔNG PHẢI KHUÔN MỚI — dùng lại NGUYÊN khuôn đợt 1701 (bảng tài khoản,
   `Components/Pages/QuanTri/NguoiDung/BangTaiKhoan.razor.css`) và đợt 1972 (danh mục thiết bị mạng,
   `Components/Pages/Mang/TrangQuanTriMang.razor.css`): cùng ngưỡng, cùng lối `data-nhan`, cùng luật
   thuần `TKNB.Tests/LuatBangTheKhoHep.cs`, cùng bộ đo sống. Sửa một bên thì đọc các bên kia trước;
   ⛔ đừng để các bản trôi xa nhau.

   📏 SỐ ĐO SINH RA VIỆC NÀY — đo sống 04/09/2026, Chrome thật, khung 390×844 cảm ứng, đăng nhập
     `tienvm`, chốt `d-flex` ra `flex`:
     `node cong-cu/do-song-khung-hep.mjs --trang /danh-gia-quy`
     · vùng bấm nhỏ hơn ngưỡng 44px: **14 chỗ**, tất cả là ô gõ «Lý do»
       (`input.form-control.form-control-sm`);
     · từng ô đo ra **27,5px bề ngang** × 81–105px chiều cao — tức THỪA chiều cao mà HỤT bề ngang.
   🔴 VÌ SAO ⛔ KHÔNG VÁ BẰNG `min-width` CHO Ô NHẬP: luật dùng chung của đợt 1518 ở `app.css` ĐANG
     CHẠY và nó đã nâng chiều cao (đó là lý do ô cao 81–105px). Cái hụt là BỀ NGANG, mà bề ngang ấy
     do Ô BẢNG bóp — nong ô nhập trong một cái bảng 6 cột nhét vào 390px chỉ đẩy chỗ chật sang cột
     khác. Bệnh gốc là BẢNG, ⛔ không phải Ô NHẬP. Xếp dòng thành THẺ là ô gõ tự có trọn bề ngang.
     ⓘ Cùng kết luận đã ghi ở việc `o962` (ô tick) và `o1260` (⛔ đừng thêm `min-width` vào luật
       dùng chung — 44px bề ngang cho mọi ô nhỏ là nong vỡ bảng ở mọi màn hình cảm ứng, quy tắc #18).

   🔑 NGƯỠNG 820px LÀ NGƯỠNG CỦA ĐỢT 400 Ở `app.css`, ⛔ ĐỪNG ĐẶT MỘT CON SỐ KHÁC. Ở đúng mốc ấy luật
     dùng chung biến MỌI bảng thành khung cuộn ngang; khối này nhận việc ngay tại đó và thay bằng thẻ.
     Đặt lệch ngưỡng là chừa ra một dải khổ mà bảng VỪA là thẻ VỪA là khung cuộn. Hàng rào
     `Nguong_cua_khoi_the_phai_trung_nguong_app_css` đọc ngưỡng từ chính `app.css` nên gõ số khác vào
     đây là ĐỎ ngay, ⛔ không phải hỏng lặng lẽ.

   🔴 ĐIỀU KIỆN LÀ `max-width`, ⛔ KHÔNG PHẢI `pointer: coarse` — bệnh ở đây là BỀ NGANG (hết chỗ mà
     bày), ⛔ không phải NGÓN TAY. Máy tính thu cửa sổ còn 380px thì cột «Lý do» cũng bị bóp y hệt dù
     đang dùng chuột; ngược lại máy tính bảng 1024px là ngón tay mà bảng bày vừa đủ, hoá thẻ chỉ tổ
     phí chỗ. ⛔ Đừng chép nhầm khuôn của khối vùng chạm (khối ấy hỏi «cái chạm vào là gì»).
   ⚠ PHẢI CÓ CHỮ `screen`: bản IN đứng ngoài (khối đợt 400 cũng gate `screen`). In tờ xếp loại quý mà
     đầu bảng bị giấu và mỗi ô mọc thêm một nhãn là hỏng bản in — mà tờ ấy là tờ đem đi ký.
     ⓘ Dạng `screen and (…)` là ngoại lệ CÓ LÝ DO của lời dặn "một điều kiện mỗi câu media" (đợt 1481):
     đợt 1500 đã ĐO SỐNG và bác bỏ điều cấm ấy cho đúng dạng này — dấu scope vẫn nằm đúng bộ chọn bên
     trong. Chốt ngược: đọc `obj/Debug/net10.0/scopedcss/bundle/TKNB.Web.styles.css`.

   🔑 MỌI BỘ CHỌN NEO VÀO LỚP `.bang-the-kho-hep`: hôm nay tệp `.razor` này có ĐÚNG MỘT thẻ bảng nên
     viết trần cũng chạy, nhưng CSS cách ly áp theo CẢ COMPONENT — ngày ai thêm một bảng thứ hai vào
     tệp (hộp chi tiết, bảng gợi ý…) thì bộ chọn trần giấu luôn đầu bảng ấy, mà build vẫn xanh. Neo
     vào lớp là chuyện MỘT LẦN, ⛔ không phải nhớ mãi. Hàng rào canh đúng chiều này.

   🔒 ⛔ KHÔNG ĐI ĐÈ `display:block; overflow-x:auto` MÀ `app.css` ĐẶT CHO THẺ `table`: bộ chọn bên ấy
     là `.content table.table:not(.bang-dinh-dau)` = (0,3,1), còn luật cách ly ở đây biên dịch ra
     `.bang-the-kho-hep tbody[b-xxx]` nên ⛔ không chạm tới chính thẻ `table`. ⛔ Đừng nống độ đặc hiệu
     để thắng nó: khi các dòng đã xếp DỌC thì ⛔ không còn gì tràn ngang để mà cuộn, `overflow-x: auto`
     nằm đó ⛔ không vẽ ra một thanh cuộn nào — và nó là lưới an toàn cho ngày một ô mang chuỗi quá dài.

   ⚠ NHÃN CỘT LẤY TỪ `data-nhan` KHAI Ở CHÍNH THẺ `td` TRONG `.razor`, ⛔ không chép tay vào CSS:
     chép tay (kiểu `td:nth-child(n)::before`) là ngày ai chèn một cột vào giữa thì mọi nhãn lệch đi
     một ô — hỏng LẶNG LẼ, build xanh test xanh, và màn hình dán tên cột này lên giá trị cột kia.
   ⚠ Chỉ `td[data-nhan]` mới mọc nhãn: ô ⛔ không khai thì `attr()` trả chuỗi rỗng nhưng hộp `::before`
     VẪN chiếm chỗ ⇒ thụt lề vô cớ. Cả 6 ô của bảng này đều đã khai, hàng rào canh chuyện ấy.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — cặp ký tự ấy lọt vào giữa câu văn là chú thích
     đóng sớm, phần chữ còn lại dính vào bộ chọn của luật ngay dưới và trình duyệt bỏ NGUYÊN luật đó,
     ⛔ không một câu lỗi nào. Vì thế ở đây gọi tên nó bằng CHỮ.
     Soi: node cong-cu/soi-chu-thich-css.mjs
   🚧 Hàng rào: TKNB.Tests/BangTheKhoHepDanhGiaQuyTests.cs
   📏 Đo sống: node cong-cu/do-song-khung-hep.mjs --trang /danh-gia-quy   (chốt ngược: thêm --chuot) */

@media screen and (max-width: 820px) {

    /* ① ĐẦU BẢNG BIẾN MẤT — tên cột nay đi theo từng ô (xem ④), nên hàng tiêu đề chỉ còn là một dải
       chữ dính trên cùng ⛔ không ăn nhập với thứ bên dưới. */
    .bang-the-kho-hep thead[b-yc8t0xrg1h] { display: none; }

    .bang-the-kho-hep tbody[b-yc8t0xrg1h] { display: block; }

    /* ② MỖI DÒNG LÀ MỘT THẺ. Viền + bo góc để mắt tách được người này với người kia — mất hàng tiêu
       đề rồi thì đường kẻ ngang của bảng ⛔ không đủ nói "hết một người".
       ⚠ `background` đọc BIẾN `--bs-table-bg` của Bootstrap chứ ⛔ không tô màu cứng: dòng bị xếp DƯỚI
         MỨC ĐẠT mang lớp `table-warning`, lớp ấy đổi đúng biến này. Tô cứng là mọi dòng trông y hệt
         nhau — mất đúng dấu hiệu người xếp loại cần thấy. */
    .bang-the-kho-hep tbody tr[b-yc8t0xrg1h] {
        display: block;
        margin-bottom: .75rem;
        padding: .5rem .75rem;
        border: 1px solid var(--bs-border-color);
        border-radius: .5rem;
        background: var(--bs-table-bg, transparent);
    }

    /* ③ MỖI Ô LÀ MỘT DÒNG «nhãn — giá trị».
       ⚠ `align-items: baseline` chứ ⛔ không `center`: ô «Xếp loại» chứa một dải nút cao hơn hẳn con
         chữ của nhãn; canh giữa là nhãn trôi xuống lưng chừng dải nút. */
    .bang-the-kho-hep tbody td[b-yc8t0xrg1h] {
        display: flex;
        gap: .5rem;
        align-items: baseline;
        flex-wrap: wrap;
        width: auto;
        padding: .25rem 0;
        border: 0;
    }

    /* ④ NHÃN CỘT — lấy nguyên văn từ `data-nhan` của chính ô ấy.
       ⚠ `flex: 0 0 8.5em` cho mọi nhãn cùng một bề ngang ⇒ cột giá trị thẳng hàng nhau. Để nhãn tự co
         là mỗi dòng một chỗ bắt đầu, đọc còn mệt hơn bảng cũ. */
    .bang-the-kho-hep tbody td[data-nhan][b-yc8t0xrg1h]::before {
        content: attr(data-nhan);
        flex: 0 0 8.5em;
        color: var(--bs-secondary-color);
        font-size: .85em;
    }

    /* ⑤ Ô GÕ «Lý do» — thứ cả việc o1260 đi chữa.
       Ở khổ thẻ, ô nhập là phần con của một hộp flex đã xuống dòng nên nó nhận TRỌN bề ngang còn lại
       của thẻ. `flex: 1 1 100%` nói thẳng điều đó thay vì trông chờ vào `width: 100%` của Bootstrap
       ⚠ ⛔ Đừng gõ số 44px ở đây: ngưỡng vùng chạm nằm ở biến `--cham-toi-thieu` của `app.css`, và ở
         khổ này ô đã rộng cả trăm pixel nên ⛔ không cần ai ép sàn nữa. */
    .bang-the-kho-hep tbody td .form-control[b-yc8t0xrg1h] { flex: 1 1 100%; }

    /* ⑥ Ô THAO TÁC — lớp `text-end` của Bootstrap căn phải, hợp với bảng nhưng ⛔ không hợp với thẻ:
       nút phải bắt đầu thẳng cột với mọi giá trị khác. */
    .bang-the-kho-hep tbody td.text-end[b-yc8t0xrg1h] { text-align: start; }
}
/* /Components/Pages/DanhGia/TrangDanhGiaThang.razor.rz.scp.css */
/* ═══ KHỔ HẸP: BẢNG XẾP LOẠI THÁNG HOÁ THẺ MỖI DÒNG MỘT KHỐI (đợt 1989, việc o1260) ═══

   ⛔ ĐÂY ⛔ KHÔNG PHẢI KHUÔN MỚI — dùng lại NGUYÊN khuôn đợt 1701 (bảng tài khoản,
   `Components/Pages/QuanTri/NguoiDung/BangTaiKhoan.razor.css`) và đợt 1972 (danh mục thiết bị mạng,
   `Components/Pages/Mang/TrangQuanTriMang.razor.css`), cùng nhịp với bảng QUÝ
   (`TrangDanhGiaQuy.razor.css`): cùng ngưỡng, cùng lối `data-nhan`, cùng luật thuần
   `TKNB.Tests/LuatBangTheKhoHep.cs`, cùng bộ đo sống. Sửa một bên thì đọc các bên kia trước.

   📏 SỐ ĐO SINH RA VIỆC NÀY — đo sống 04/09/2026, Chrome thật, khung 390×844 cảm ứng, đăng nhập
     `tienvm`, chốt `d-flex` ra `flex`:
     `node cong-cu/do-song-khung-hep.mjs --trang /danh-gia-thang`
     · vùng bấm nhỏ hơn ngưỡng 44px: **13 chỗ**, tất cả là ô gõ «Lý do»
       (`input.form-control.form-control-sm`);
     · từng ô đo ra **27,5px bề ngang** × 93px chiều cao — THỪA chiều cao, HỤT bề ngang.
   🔴 Bảng này có **9 cột** — nhiều nhất trong ba bảng đi theo khuôn tính tới đợt 1989 — nên ở khổ
     390px nó là ca nặng nhất: bảng vốn được chia bề rộng cho khung 1900px (đợt 908 · 924), riêng các
     cột KHOÁ CỨNG đã cộng lại 76+210+150+112+170+160+124+46 = **1.048px**, tức cột co giãn duy nhất
     («Lý do») bị bóp xuống còn đúng vài chục pixel. ⇒ Bệnh gốc là BẢNG, ⛔ không phải Ô NHẬP; nong ô
     nhập chỉ đẩy chỗ chật sang cột khác. Xếp dòng thành THẺ là ô gõ tự có trọn bề ngang.
     ⓘ Cùng kết luận đã ghi ở việc `o962` (ô tick) và `o1260` (⛔ đừng thêm `min-width` vào luật dùng
       chung ở `app.css` — 44px bề ngang cho mọi ô nhỏ là nong vỡ bảng ở mọi màn hình cảm ứng, đúng
       rủi ro lan truyền quy tắc #18).

   🔑 NGƯỠNG 820px LÀ NGƯỠNG CỦA ĐỢT 400 Ở `app.css`, ⛔ ĐỪNG ĐẶT MỘT CON SỐ KHÁC. Ở đúng mốc ấy luật
     dùng chung biến MỌI bảng thành khung cuộn ngang; khối này nhận việc ngay tại đó và thay bằng thẻ.
     Đặt lệch ngưỡng là chừa ra một dải khổ mà bảng VỪA là thẻ VỪA là khung cuộn. Hàng rào
     `Nguong_cua_khoi_the_phai_trung_nguong_app_css` đọc ngưỡng từ chính `app.css`.

   🔴 ĐIỀU KIỆN LÀ `max-width`, ⛔ KHÔNG PHẢI `pointer: coarse` — bệnh ở đây là BỀ NGANG (hết chỗ mà
     bày), ⛔ không phải NGÓN TAY. Máy tính thu cửa sổ còn 380px thì cột «Lý do» cũng bị bóp y hệt dù
     đang dùng chuột; ngược lại máy tính bảng 1024px là ngón tay mà bảng bày vừa đủ, hoá thẻ chỉ tổ
     phí chỗ.
   ⚠ PHẢI CÓ CHỮ `screen`: bản IN đứng ngoài (khối đợt 400 cũng gate `screen`). Tờ xếp loại tháng là
     tờ ĐEM ĐI KÝ — in ra mà đầu bảng bị giấu và mỗi ô mọc thêm một nhãn là hỏng đúng thứ phải nộp.
     ⓘ Dạng `screen and (…)` là ngoại lệ CÓ LÝ DO của lời dặn "một điều kiện mỗi câu media" (đợt 1481):
     đợt 1500 đã ĐO SỐNG và bác bỏ điều cấm ấy cho đúng dạng này. Chốt ngược: đọc
     `obj/Debug/net10.0/scopedcss/bundle/TKNB.Web.styles.css`.

   🔑 MỌI BỘ CHỌN NEO VÀO LỚP `.bang-the-kho-hep`: hôm nay tệp `.razor` này có ĐÚNG MỘT thẻ bảng nên
     viết trần cũng chạy, nhưng CSS cách ly áp theo CẢ COMPONENT — ngày ai thêm một bảng thứ hai vào
     tệp thì bộ chọn trần giấu luôn đầu bảng ấy, mà build vẫn xanh. Neo vào lớp là chuyện MỘT LẦN.

   🔒 ⛔ KHÔNG ĐI ĐÈ `display:block; overflow-x:auto` MÀ `app.css` ĐẶT CHO THẺ `table` — bộ chọn bên ấy
     là `.content table.table:not(.bang-dinh-dau)` = (0,3,1). Khi các dòng đã xếp DỌC thì ⛔ không còn
     gì tràn ngang để mà cuộn; `overflow-x: auto` nằm đó là lưới an toàn cho ngày một ô mang chuỗi dài.

   ⚠ NHÃN CỘT LẤY TỪ `data-nhan` KHAI Ở CHÍNH THẺ `td` TRONG `.razor`, ⛔ không chép tay vào CSS (kiểu
     `td:nth-child(n)::before`): chép tay là ngày ai chèn một cột vào giữa thì mọi nhãn lệch một ô —
     hỏng LẶNG LẼ, và ở bảng này nó dán «Mức lương» lên con số của cột khác.
   ⚠ Chỉ `td[data-nhan]` mới mọc nhãn: ô ⛔ không khai thì `attr()` trả chuỗi rỗng nhưng hộp `::before`
     VẪN chiếm chỗ ⇒ thụt lề vô cớ. Cả 9 ô của bảng này đều đã khai, hàng rào canh chuyện ấy.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — cặp ký tự ấy lọt vào giữa câu văn là chú thích
     đóng sớm, phần chữ còn lại dính vào bộ chọn của luật ngay dưới và trình duyệt bỏ NGUYÊN luật đó,
     ⛔ không một câu lỗi nào. Vì thế ở đây gọi tên nó bằng CHỮ.
     Soi: node cong-cu/soi-chu-thich-css.mjs
   🚧 Hàng rào: TKNB.Tests/BangTheKhoHepDanhGiaThangTests.cs
   📏 Đo sống: node cong-cu/do-song-khung-hep.mjs --trang /danh-gia-thang   (chốt ngược: thêm --chuot) */

@media screen and (max-width: 820px) {

    /* ① ĐẦU BẢNG BIẾN MẤT — tên cột nay đi theo từng ô (xem ④). */
    .bang-the-kho-hep thead[b-f991y823lz] { display: none; }

    .bang-the-kho-hep tbody[b-f991y823lz] { display: block; }

    /* ② MỖI DÒNG LÀ MỘT THẺ.
       ⚠ `background` đọc BIẾN `--bs-table-bg` chứ ⛔ không tô màu cứng: dòng bị xếp DƯỚI MỨC ĐẠT mang
         lớp `table-warning`, lớp ấy đổi đúng biến này. Tô cứng là mọi dòng trông y hệt nhau — mất đúng
         dấu hiệu người xếp loại cần thấy. */
    .bang-the-kho-hep tbody tr[b-f991y823lz] {
        display: block;
        margin-bottom: .75rem;
        padding: .5rem .75rem;
        border: 1px solid var(--bs-border-color);
        border-radius: .5rem;
        background: var(--bs-table-bg, transparent);
    }

    /* ③ MỖI Ô LÀ MỘT DÒNG «nhãn — giá trị».
       ⚠ `align-items: baseline` chứ ⛔ không `center`: ô «Xếp loại» chứa một dải nút, ô «Đã bị trừ»
         chứa các huy hiệu — cả hai cao hơn hẳn con chữ của nhãn.
       ⚠ `text-nowrap` của ba cột (Họ tên · Chức danh · Mức lương) ⛔ không phải gỡ: ở thẻ mỗi ô đã có
         trọn bề ngang nên chuỗi vẫn đủ chỗ, mà giữ nguyên thì khổ máy tính ⛔ không đổi gì. */
    .bang-the-kho-hep tbody td[b-f991y823lz] {
        display: flex;
        gap: .5rem;
        align-items: baseline;
        flex-wrap: wrap;
        width: auto;
        padding: .25rem 0;
        border: 0;
    }

    /* ④ NHÃN CỘT — lấy nguyên văn từ `data-nhan` của chính ô ấy.
       ⚠ `flex: 0 0 8.5em` cho mọi nhãn cùng một bề ngang ⇒ cột giá trị thẳng hàng nhau. Nhãn dài nhất
         của bảng này là «Đã bị trừ (12 tháng)» nên nó xuống hai dòng — ĐÚNG như mong muốn, ⛔ đừng nới
         con số lên cho vừa: nới là cột giá trị của CẢ CHÍN ô tụt sang phải, mất chỗ của thứ đáng đọc. */
    .bang-the-kho-hep tbody td[data-nhan][b-f991y823lz]::before {
        content: attr(data-nhan);
        flex: 0 0 8.5em;
        color: var(--bs-secondary-color);
        font-size: .85em;
    }

    /* ⑤ Ô GÕ «Lý do» — thứ cả việc o1260 đi chữa. Ở khổ thẻ, ô nhập là phần con của một hộp flex đã
       xuống dòng nên nó nhận TRỌN bề ngang còn lại của thẻ.
       ⚠ ⛔ Đừng gõ số 44px ở đây: ngưỡng vùng chạm nằm ở biến `--cham-toi-thieu` của `app.css`, và ở
         khổ này ô đã rộng cả trăm pixel nên ⛔ không cần ai ép sàn nữa. */
    .bang-the-kho-hep tbody td .form-control[b-f991y823lz] { flex: 1 1 100%; }

    /* ⑥ Ô THAO TÁC — lớp `text-end` căn phải hợp với bảng nhưng ⛔ không hợp với thẻ. */
    .bang-the-kho-hep tbody td.text-end[b-f991y823lz] { text-align: start; }
}
/* /Components/Pages/DienThoai/TrangGuiForm.razor.rz.scp.css */
/* ═══ MÀN HÌNH GỬI BIỂU MẪU TRÊN ĐIỆN THOẠI (đợt 735 — việc f05) ═══

   ⓘ Cố ý là CSS PHẠM VI THÀNH PHẦN (`.razor.css`) chứ không thêm luật vào app.css: Blazor gắn thêm
   thuộc tính `b-xxxxx` nên mọi luật dưới đây CHỈ chạm tới trang này. Đúng tinh thần quy tắc #18 —
   sửa app.css là chạm chỗ dùng chung của mọi màn hình, còn tệp này thì rủi ro lan truyền bằng 0.

   ⓘ Màu lấy từ biến Bootstrap (`--bs-*`) mà app.css đã đặt lại cho từng giao diện ⇒ trang tự đúng
   màu ở cả 4 giao diện, kể cả nền tối, mà không phải viết một luật `[data-theme]` nào. Mọi biến đều
   có giá trị dự phòng phía sau dấu phẩy phòng khi biến chưa được khai. */

.gbm[b-9lsaswlmcp] {
    /* Khổ hẹp: bề ngang tối đa cho dễ đọc, nhưng vẫn tràn viền trên điện thoại. */
    max-width: 640px;
    margin: 0 auto;
    padding: 0.5rem 0 2rem;
}

.gbm-tieu-de[b-9lsaswlmcp] {
    font-size: 1.35rem;
    font-weight: 600;
    margin-bottom: 1rem;
}

/* MỘT cột trên điện thoại — quan trọng hơn nó nghe: ô càng rộng thì càng dễ trúng khi bấm một tay,
   và ba mục thì cuộn dọc vẫn ngắn. Từ 480px trở lên mới xếp hai cột. */
.gbm-luoi[b-9lsaswlmcp] {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.75rem;
}

@media (min-width: 480px) {
    .gbm-luoi[b-9lsaswlmcp] {
        grid-template-columns: 1fr 1fr;
    }
}

.gbm-o[b-9lsaswlmcp] {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    /* 64px là cỡ vùng bấm tối thiểu cho ngón tay cái — nhỏ hơn là bấm trượt sang ô bên cạnh. */
    min-height: 64px;
    padding: 0.9rem 1rem;
    border: 1px solid var(--bs-border-color, #d5d9de);
    border-radius: 0.75rem;
    background: var(--bs-body-bg, #fff);
    color: var(--bs-body-color, #212529);
    text-decoration: none;
    font-weight: 600;
    /* Chạm tay không có trạng thái "rê chuột" nên phản hồi phải tới ngay khi bấm — xem .gbm-o:active */
    transition: border-color .15s ease, transform .08s ease;
}

.gbm-o:hover[b-9lsaswlmcp] {
    border-color: var(--bs-primary, #0a3d91);
    color: var(--bs-body-color, #212529);
    text-decoration: none;
}

.gbm-o:active[b-9lsaswlmcp] {
    transform: scale(0.985);
}

/* Viền rõ khi đi bằng bàn phím / trình đọc màn hình — đừng bỏ, đây là đường duy nhất của người
   không dùng chuột để biết mình đang đứng ở ô nào. */
.gbm-o:focus-visible[b-9lsaswlmcp] {
    outline: 3px solid var(--bs-primary, #0a3d91);
    outline-offset: 2px;
}

.gbm-icon[b-9lsaswlmcp] {
    font-size: 1.7rem;
    line-height: 1;
    flex: 0 0 auto;
}

.gbm-ten[b-9lsaswlmcp] {
    font-size: 1.02rem;
}

.gbm-trong[b-9lsaswlmcp],
.gbm-kenh[b-9lsaswlmcp] {
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.92rem;
    margin-top: 1rem;
}

.gbm-trong[b-9lsaswlmcp] {
    margin-top: 0;
}

/* ⭐ ĐỢT 1563 (việc o866) — DẢI "CHẾ ĐỘ NGOÀI MẠNG CHƯA BẬT".
   Phần ẩn/hiện KHÔNG nằm ở đây mà ở khối style toàn cục trong App.razor
   (`:root[data-sw="co"] .tknb-can-sw { display: none }`) — vì nó bám vào dấu đặt trên thẻ html,
   mà CSS cách ly thì ⛔ không với tới `:root`. Ở đây chỉ lo dáng vẻ.
   ⚠ Màu theo chiều BẤT LỢI (luật màu của app): đây là câu cảnh báo về một tính năng đang TẮT,
   ⛔ không phải một lời mách nước. */
.gbm-canh-bao[b-9lsaswlmcp] {
    margin: 0 0 .85rem;
    padding: .6rem .75rem;
    border: 1px solid var(--bs-warning-border-subtle, #ffe69c);
    border-radius: .5rem;
    background: var(--bs-warning-bg-subtle, #fff3cd);
    color: var(--bs-warning-text-emphasis, #664d03);
    font-size: .92rem;
    line-height: 1.45;
}
/* /Components/Pages/DuAn/BangTienDoDuAn.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 1435 (việc o630) — TÊN ĐẦU MỤC LÀ LỐI BẤM ═══

   Anh Tiến 28/08/2026: "các biên bản không cần phải nhấn nút mới mở ra được, chỉ cần nhấn vào tên
   biên bản là được". Huy hiệu "📝 Soạn / in" đứng cạnh tên đã bỏ; dòng nào là tờ giấy soạn được thì
   chính TÊN đầu mục thành thẻ liên kết.

   🔑 Gạch chân ĐỨT nét, hiện THƯỜNG TRỰC chứ ⛔ không đợi rê chuột. Bảng này có tới 135 đầu mục mà
      chỉ một nhúm là tờ giấy soạn được, nên dấu hiệu phải nhìn phát thấy — đó đúng là công dụng mà
      huy hiệu vừa bỏ đang gánh. Chỉ gạch chân lúc hover là người dùng màn hình cảm ứng ⛔ KHÔNG BAO
      GIỜ thấy được dòng nào bấm được.

   🎨 MÀU CHỮ ⛔ KHÔNG khai ở đây — xem `LopTenBuoc` trong code-behind. Đầu mục ĐÃ XONG mang
      `text-body-secondary` (0,1,0), thắng luật `a` của Bootstrap (0,0,1) ⇒ giữ nguyên chữ xám như
      đời <span> trước đợt 1435; đầu mục còn lại ⛔ không mang lớp màu nào ⇒ rơi về màu liên kết của
      app. ⛔ Đừng đặt `color` ở đây: đặt là nuốt luôn màu xám của dòng đã xong, mà build vẫn xanh.

   ⛔ Cố ý chép khuôn từ `KhoiBangVanBan.razor.css` chứ ⛔ không gom vào `app.css`: CSS của Blazor là
      CSS CÁCH LY theo component, lớp của khối kia ⛔ không với sang đây được. Gom vào tệp dùng chung
      thì đổi một chỗ là đụng cả app (rủi ro lan truyền, quy tắc #18). */
.btd-ten[b-vhsxctxi06] {
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: .18em;
    text-decoration-color: var(--bs-secondary-color);
}

.btd-ten:hover[b-vhsxctxi06],
.btd-ten:focus-visible[b-vhsxctxi06] {
    text-decoration-style: solid;
    text-decoration-color: var(--bs-primary);
}
/* /Components/Pages/DuAn/DanhSachDuAn.razor.rz.scp.css */
/* ═══ VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (đợt 1605, việc o737) ═══
   ô tick TRẦN chọn hồ sơ để xóa hàng loạt — đứng ngay cạnh một NÚT xóa.

   🔑 Nới VÙNG CHẠM QUANH ô tick, ⛔ KHÔNG phóng to chính ô vuông: ô tick 44px đứng cạnh dòng chữ
     15px trông như lỗi phần mềm, và nó cũng ⛔ không giải đúng bài — thứ ngón tay bấm trượt là
     khoảng trống quanh ô. Thẻ bọc `label.o-tick-cham` khai ngay trong `.razor`.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là ngón
     tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ
     quyết định vùng bấm là CÁI CHẠM VÀO, ⛔ không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, ⛔ không một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css`: bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi: node cong-cu/soi-o-tick-tran.mjs · node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ⚠ Cần CẢ `min-width`: ô tick chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt NGANG.
       ⚠ `inline-flex` chứ ⛔ không `flex`: thẻ bọc nằm trong một ô bảng (hoặc một hàng `d-flex`)
         cạnh nút và ô gõ khác — hoá `flex` là nó chiếm trọn một dòng, hàng đang gọn bỗng vỡ ra.
       ⚠ `min-height` chứ ⛔ không `height`: hàng nào cao hơn thì cứ để cao — đặt `height` là bóp
         chúng lại, sửa một lỗi rồi đẻ một lỗi. */
    .o-tick-cham[b-bb10qk03kt] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/DuAn/GoiYThietBiHoSo.razor.rz.scp.css */
/* ═══ VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (đợt 2791b, việc o1555) ═══
   ô tick TRẦN chọn từng dòng gợi ý thiết bị để gắn hàng loạt.

   🔑 Nới VÙNG CHẠM QUANH ô tick, ⛔ KHÔNG phóng to chính ô vuông — cùng lẽ và cùng ngưỡng với
     `XepLoaiHoSo.razor.css`; CSS cách ly theo component nên mỗi trang phải khai lấy một lần,
     ⛔ đừng dồn vào `app.css` (một luật chung cho mọi ô tick là việc riêng, ⛔ làm lẻ ở đây).
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ có nghĩa là ngón tay.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ vẽ ra một pixel nào — mọi khai báo nằm TRONG câu media.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`, ⛔ gõ số 44px vào đây.
   ⚠ MỘT điều kiện mỗi câu media (bài học 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs` */

@media (pointer: coarse) {

    .o-tick-cham[b-kzykukvtmj] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/DuAn/KhoiBangHangMuc.razor.rz.scp.css */
/* ═══ VÙNG CHẠM NGÓN TAY CHO Ô RADIO «TRÚNG» (đợt 1605, việc o737) ═══
   Ô radio chọn ĐƠN VỊ TRÚNG THẦU, nằm trong một `.input-group-text` cạnh ô gõ tên đơn vị.

   🔑 Nới VÙNG CHẠM QUANH ô, ⛔ KHÔNG phóng to chính ô tròn: ô 44px đứng cạnh dòng chữ 15px trông
     như lỗi phần mềm, và nó cũng ⛔ không giải đúng bài — thứ ngón tay bấm trượt là khoảng trống
     quanh ô. Thẻ bọc `label.o-tick-cham` khai ngay trong `.razor`, bọc CẢ ô lẫn chữ «trúng».
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là ngón
     tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ
     quyết định vùng bấm là CÁI CHẠM VÀO, ⛔ không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, ⛔ không một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css`: bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi: node cong-cu/soi-o-tick-tran.mjs · node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ⚠ Cần CẢ `min-width`: ô radio chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt NGANG.
         Ở đây bề ngang tự nhiên đã lớn hơn (có chữ «trúng» đi kèm) nên dòng này là SÀN phòng khi
         chữ ấy bị rút gọn về sau, ⛔ không phải luật thừa.
       ⚠ `inline-flex` chứ ⛔ không `flex`: nhãn là con của `.input-group-text` — hoá `flex` là nó
         chiếm trọn một dòng và cụm nhập đang gọn bỗng vỡ ra.
       ⚠ `min-height` chứ ⛔ không `height`: `.input-group-text` co giãn theo ô gõ bên cạnh; đặt
         `height` là bóp nhãn lại lệch khỏi cụm — sửa một lỗi rồi đẻ một lỗi. */
    .o-tick-cham[b-u4w92cfo6t] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/DuAn/KhoiBangVanBan.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 1422 (việc o606) — RAY MŨI TÊN CHẠY TRONG BẢNG VĂN BẢN ═══

   Anh Tiến chốt 27/08/2026 (phương án (c) của dòng h44): mang mũi tên chạy VÀO chính bảng văn bản
   rồi bỏ dải "Hồ sơ giấy của chặng" ở tab giai đoạn — giữ được cả hai thứ anh xin (bảng đủ cột và
   mũi tên động) mà mỗi tờ giấy chỉ còn MỘT chỗ bấm.

   🎨 MÀU LẤY TỪ TOKEN BOOTSTRAP 5.3 (app nạp 5.3.8 từ đợt 831) nên ray tự đúng ở CẢ nền sáng lẫn
      nền tối — cầu nối "data-theme ⇄ data-bs-theme" ở App.razor lo phần đổi bảng màu.
      ⛔ ĐỪNG gõ mã màu thẳng vào đây: nền tối sẽ giữ nguyên màu của nền sáng, mà build vẫn xanh.

   🔑 CÙNG NGÔN NGỮ HÌNH VỚI DẢI CHẶNG ở tab 📌 Tổng quan (`KhoiLuongThucHien.razor.css`): vạch đứt
      trôi = luồng đã đi qua, vạch mờ đứng im = chưa tới. Hai chỗ nằm trên cùng một trang nên dùng
      hai ngôn ngữ hình khác nhau là bắt người đọc học hai bảng chú giải cho một thứ.
      ⛔ Cố ý ⛔ KHÔNG dùng chung tệp CSS: CSS của Blazor là CSS CÁCH LY theo component, lớp của khối
        kia ⛔ không với sang được khối này. Chép sang là đúng cách làm, ⛔ không phải trùng lặp thừa. */

/* Ô số thứ tự — mốc neo cho ray đặt tuyệt đối. Ray chạy DỌC chính giữa ô, số đè lên nó. */
.bvb-o-tt[b-1lv8il1yde] {
    position: relative;
}

/* Con số phải ĐỤC một khoảng trên ray, không thì vạch đứt chạy xuyên qua chữ số.
   Nền lấy đúng nền thân bảng; dòng "đang làm" tô `table-primary` nên nền ở đó do Bootstrap phủ —
   vì thế dùng `transparent` cộng một khoảng đệm ray (xem `top`/`bottom` của hai ray dưới) thay vì
   tô một màu nền cứng: tô nền cứng là dòng đang làm bị một vệt trắng giữa ô. */
.bvb-so[b-1lv8il1yde] {
    position: relative;
    z-index: 1;
    display: inline-block;
    min-width: 1.15rem;
    line-height: 1.15rem;
    font-variant-numeric: tabular-nums;
}

/* ── Ray nối ────────────────────────────────────────────────────────────────
   Mỗi chỗ nối được vẽ bằng HAI nửa: nửa DƯỚI của dòng trên và nửa TRÊN của dòng dưới. Hai nửa hỏi
   CÙNG một phép so (`LuatBangVanBanHoSo.MuiTenChay`) nên ⛔ không thể một nửa sáng một nửa mờ. */
.bvb-ray[b-1lv8il1yde] {
    position: absolute;
    left: 50%;
    width: 3px;
    margin-left: -1.5px;
    border-radius: 2px;
    color: var(--bs-secondary-color);
    opacity: .5;
    background-image: repeating-linear-gradient(180deg, currentColor 0 6px, transparent 6px 12px);
    background-size: 3px 12px;
}

.bvb-ray-tren[b-1lv8il1yde] {
    top: 0;
    bottom: calc(50% + .72rem);
}

.bvb-ray-duoi[b-1lv8il1yde] {
    top: calc(50% + .72rem);
    bottom: 0;
}

/* Đầu mũi tên đứng ở MÉP DƯỚI ô, tức ngay chỗ giáp dòng kế tiếp — nó chỉ chiều đi xuống.
   ⚠ Cỡ ≥ 0,65rem là NGƯỠNG ĐỌC ĐƯỢC của app (hàng rào `CoChuONgayTests`). Tuy đây là dấu trang trí
     (`aria-hidden`) nhưng vẫn là một ký tự vẽ ra màn hình — nâng cho đủ ngưỡng rẻ hơn hẳn việc khai
     một dòng miễn trừ, mà miễn trừ thì lượt sau lại phải đọc để hiểu vì sao có nó. */
.bvb-dinh[b-1lv8il1yde] {
    position: absolute;
    left: 50%;
    bottom: -.34rem;
    margin-left: -.3rem;
    font-size: .7rem;
    line-height: 1;
    color: var(--bs-secondary-color);
    opacity: .5;
}

/* Chỗ nối mà LUỒNG ĐÃ ĐI QUA thì vạch SÁNG màu và trôi — đây là "mũi tên động" anh Tiến nói.
   ⛔ Cố ý ⛔ KHÔNG cho mọi mũi tên cùng chạy: chạy tất thì nó thôi mang tin, chỉ còn là hoạt hoạ. */
.bvb-chay[b-1lv8il1yde] {
    color: var(--bs-primary);
    opacity: 1;
}

.bvb-ray.bvb-chay[b-1lv8il1yde] {
    animation: bvb-troi-b-1lv8il1yde .9s linear infinite;
}

@keyframes bvb-troi-b-1lv8il1yde {
    from {
        background-position: 0 0;
    }

    to {
        background-position: 0 12px;
    }
}

/* Người tắt hiệu ứng chuyển động ở hệ điều hành thì ray đứng yên — vạch vẫn SÁNG màu nên tin
   "luồng đã đi qua đây" ⛔ không mất. */
@media (prefers-reduced-motion: reduce) {
    .bvb-ray.bvb-chay[b-1lv8il1yde] {
        animation: none;
    }
}

/* ═══ ⭐ ĐỢT 1435 (việc o630) — TÊN VĂN BẢN LÀ LỐI BẤM ═══

   Anh Tiến 28/08/2026: "các biên bản không cần phải nhấn nút mới mở ra được, chỉ cần nhấn vào tên
   biên bản là được". Ô nút riêng ở cuối dòng đã bỏ, nên chính cái tên phải TRÔNG RA là bấm được.

   🔑 Gạch chân ĐỨT nét, hiện THƯỜNG TRỰC chứ ⛔ không đợi rê chuột: bỏ nút đi rồi mà chỉ gạch chân
      lúc hover thì người dùng ⛔ không có cách nào biết dòng ấy bấm được — nhất là người dùng chuột
      cảm ứng, thứ ⛔ không có trạng thái hover nào cả. Rê chuột vào thì gạch chân LIỀN nét, tức nấc
      "đang nhắm vào cái này".

   🎨 MÀU CHỮ ⛔ KHÔNG khai ở đây, và đó là CHỦ Ý — nó do lớp tiện dụng trên thẻ quyết định
      (`LopTenVanBan`). Tờ ĐÃ XONG mang `text-body-secondary` (0,1,0) nên thắng luật `a` của
      Bootstrap (0,0,1) ⇒ chữ XÁM, đúng như đời <span> trước đợt 1435. Tờ CÒN LẠI chỉ mang
      `fw-medium` (⛔ không đặt màu) ⇒ rơi về màu liên kết của app, tức chữ XANH NAVY — và đó
      chính là tín hiệu "bấm được" thay cho cái nút vừa bỏ.
      ⛔ Đừng đặt `color` ở đây: đặt là nuốt luôn màu xám của tờ đã xong, mà build vẫn xanh. */
.bvb-ten[b-1lv8il1yde] {
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: .18em;
    text-decoration-color: var(--bs-secondary-color);
}

.bvb-ten:hover[b-1lv8il1yde],
.bvb-ten:focus-visible[b-1lv8il1yde] {
    text-decoration-style: solid;
    text-decoration-color: var(--bs-primary);
}

/* ─────────── ⭐ SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY (việc o1519) ───────────
   `.bvb-dinh` đo tĩnh .7rem = 11,2px, dưới sàn `--cham-chu-toi-thieu` (12px, khai ở `app.css`).
   📏 ⛔ Không tràn: đầu mũi tên là một ký tự `position: absolute` neo vào mép dưới ô, nó nằm NGOÀI
      luồng bố cục nên nâng cỡ ⛔ không đẩy được thứ gì; `margin-left: -.3rem` vẫn canh giữa vì
      ký tự chỉ nhích thêm chưa tới 1px mỗi bên.
   🔑 Đọc BIẾN, ⛔ đừng gõ thẳng 12px. ⛔ CHỈ trong `@media (pointer: coarse)` — xem `app.css` (đợt 1518). */
@media (pointer: coarse) {
    .bvb-dinh[b-1lv8il1yde] { font-size: max(.7rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/Pages/DuAn/KhoiDocBanDaKy.razor.rz.scp.css */
/* ═══ VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (đợt 1605, việc o737) ═══
   ô tick TRẦN chọn trường sẽ điền từ bản đã ký.

   🔑 Nới VÙNG CHẠM QUANH ô tick, ⛔ KHÔNG phóng to chính ô vuông: ô tick 44px đứng cạnh dòng chữ
     15px trông như lỗi phần mềm, và nó cũng ⛔ không giải đúng bài — thứ ngón tay bấm trượt là
     khoảng trống quanh ô. Thẻ bọc `label.o-tick-cham` khai ngay trong `.razor`.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là ngón
     tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ
     quyết định vùng bấm là CÁI CHẠM VÀO, ⛔ không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, ⛔ không một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css`: bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi: node cong-cu/soi-o-tick-tran.mjs · node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ⚠ Cần CẢ `min-width`: ô tick chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt NGANG.
       ⚠ `inline-flex` chứ ⛔ không `flex`: thẻ bọc nằm trong một ô bảng (hoặc một hàng `d-flex`)
         cạnh nút và ô gõ khác — hoá `flex` là nó chiếm trọn một dòng, hàng đang gọn bỗng vỡ ra.
       ⚠ `min-height` chứ ⛔ không `height`: hàng nào cao hơn thì cứ để cao — đặt `height` là bóp
         chúng lại, sửa một lỗi rồi đẻ một lỗi. */
    .o-tick-cham[b-17yr1yiihf] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/DuAn/KhoiLichCongViecDuAn.razor.rz.scp.css */
/* ⭐ ĐỢT 2154 (việc o1577) — khối 🗓 Tiến độ công việc của trang chi tiết hồ sơ dự án.
   ⚠ Mọi màu lấy từ token Bootstrap 5.3 đang chạy, KHÔNG gõ mã màu cứng: khai lại bảng màu bằng tay
     là đúng bệnh đợt 831, và ở nền tối thì chip sẽ chìm mà không ai kêu.
   ⚠ Chú thích CSS đóng sớm là bệnh đợt 852 và 1481 — chạy node cong-cu/soi-chu-thich-css.mjs sau
     mỗi lượt sửa tệp này. */

.klcv-lich[b-ec18nfaet7] {
    table-layout: fixed;
}

/* Ô một ngày. Chiều cao tối thiểu để ô rỗng không dẹp thành một vạch, còn cuộn thì để cho cả bảng
   (thẻ bọc .table-responsive) chứ không cho từng ô. */
.klcv-o[b-ec18nfaet7] {
    height: 92px;
    vertical-align: top;
    padding: 2px 3px;
    overflow: hidden;
}

.klcv-so-ngay[b-ec18nfaet7] {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--bs-secondary-color, #6c757d);
    line-height: 1.1;
}

/* Ngày của tháng khác — vẫn hiện (tuần bị cắt ngang nhìn rất khó chịu) nhưng chìm hẳn xuống. */
.klcv-ngoai-thang[b-ec18nfaet7] {
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

.klcv-ngoai-thang .klcv-so-ngay[b-ec18nfaet7] {
    opacity: 0.45;
}

/* Hôm nay — viền, KHÔNG tô nền: tô nền thì chip việc nằm trong đó đổi tương phản theo, mà chip mới
   là thứ người ta đọc. */
.klcv-hom-nay[b-ec18nfaet7] {
    box-shadow: inset 0 0 0 2px var(--bs-primary, #0d6efd);
}

/* Chip một việc. Cắt một dòng bằng dấu ba chấm — tên nhiệm vụ dài tới 200 ký tự, để tràn là một ô
   ngày đẩy cao cả hàng. Tên đầy đủ đọc được ở lối xem danh sách ngay cạnh. */
.klcv-chip[b-ec18nfaet7] {
    display: block;
    margin-top: 2px;
    padding: 1px 5px;
    border-radius: 4px;
    font-size: 0.75rem;
    line-height: 1.25;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-decoration: none;
}

.klcv-chip:hover[b-ec18nfaet7] {
    text-decoration: underline;
}

.klcv-chip-thuong[b-ec18nfaet7] {
    background-color: var(--bs-primary-bg-subtle, #cfe2ff);
    color: var(--bs-primary-text-emphasis, #052c65);
}

.klcv-chip-cao[b-ec18nfaet7] {
    background-color: var(--bs-warning-bg-subtle, #fff3cd);
    color: var(--bs-warning-text-emphasis, #664d03);
}

.klcv-chip-thap[b-ec18nfaet7] {
    background-color: var(--bs-secondary-bg-subtle, #e9ecef);
    color: var(--bs-secondary-text-emphasis, #495057);
}

/* Khẩn cấp — mức cao nhất, dùng viền để phân biệt với chip TRỄ ngay dưới (cùng họ đỏ nhưng hai
   nghĩa khác hẳn: một cái nói "phải làm trước", cái kia nói "đã lỡ hạn"). */
.klcv-chip-khan[b-ec18nfaet7] {
    background-color: var(--bs-danger-bg-subtle, #f8d7da);
    color: var(--bs-danger-text-emphasis, #58151c);
    box-shadow: inset 0 0 0 1px var(--bs-danger-border-subtle, #f1aeb5);
}

/* Trễ hạn — ĐỎ, theo luật màu của app: số bất lợi thì đỏ. */
.klcv-chip-tre[b-ec18nfaet7] {
    background-color: var(--bs-danger-bg-subtle, #f8d7da);
    color: var(--bs-danger-text-emphasis, #58151c);
    font-weight: 600;
}

/* 👆 Ngón tay: nới chip và ô ngày cho vừa tầm chạm — CHỈ trong pointer: coarse, đúng luật đợt 1518.
   Khổ hẹp KHÔNG có nghĩa là ngón tay (cửa sổ thu nhỏ vẫn dùng chuột), nên điều kiện phải là cái
   chạm vào chứ không phải bề ngang. */
@media (pointer: coarse) {
    .klcv-chip[b-ec18nfaet7] {
        min-height: var(--cham-toi-thieu, 44px);
        display: flex;
        align-items: center;
        font-size: var(--cham-chu-toi-thieu, 12px);
    }

    .klcv-o[b-ec18nfaet7] {
        height: auto;
    }
}
/* /Components/Pages/DuAn/KhoiLuongThucHien.razor.rz.scp.css */
/* ═══ DẢI LUỒNG THỰC HIỆN HỒ SƠ — ĐỢT 1362 (việc o526) ═══

   Anh Tiến 26/08/2026: các nút của luồng, nối nhau bằng "mũi tên động".

   🎨 MÀU LẤY TỪ TOKEN BOOTSTRAP 5.3 (app nạp 5.3.8 từ đợt 831) nên dải tự đúng ở CẢ nền sáng lẫn
      nền tối — cầu nối "data-theme ⇄ data-bs-theme" đặt ở App.razor lo phần đổi bảng màu.
      ⛔ ĐỪNG gõ mã màu thẳng vào đây: nền tối sẽ giữ nguyên màu của nền sáng, mà build vẫn xanh.

   ⛔ Bốn nấc phân biệt bằng CẢ MÀU LẪN HÌNH: nấc "chưa đo được" dùng viền ĐỨT, ⛔ không chỉ đổi màu.
      Nó là nấc KHÔNG BIẾT chứ không phải một nấc sớm hơn "chưa làm" (xem lời dẫn ở NacLuong), nên
      người xem phải nhận ra nó ngay cả khi ⛔ không phân biệt được màu.                              */

.ltl-dai[b-21v2erdivw] {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: .4rem .3rem;
}

/* Một đốt: nút dẫn tới chặng hoặc tờ giấy. */
.ltl-dot[b-21v2erdivw] {
    display: flex;
    align-items: center;
    gap: .55rem;
    flex: 1 1 12rem;
    min-width: 10rem;
    padding: .5rem .7rem;
    border: 1px solid var(--bs-border-color);
    border-radius: .55rem;
    background-color: var(--bs-body-bg);
    color: var(--bs-body-color);
    text-decoration: none;
    transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}

.ltl-dot:hover[b-21v2erdivw],
.ltl-dot:focus-visible[b-21v2erdivw] {
    transform: translateY(-2px);
    box-shadow: 0 .35rem .8rem rgba(0, 0, 0, .12);
    border-color: var(--bs-primary);
}

.ltl-bieu[b-21v2erdivw] {
    font-size: 1.15rem;
    line-height: 1;
}

.ltl-chu[b-21v2erdivw] {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.ltl-nhan[b-21v2erdivw] {
    font-weight: 600;
    font-size: .9rem;
    line-height: 1.2;
}

.ltl-phu[b-21v2erdivw] {
    font-size: .76rem;
    color: var(--bs-secondary-color);
}

/* ── Bốn nấc ───────────────────────────────────────────────────────────── */

.ltl-xong[b-21v2erdivw] {
    border-color: var(--bs-success-border-subtle);
    background-color: var(--bs-success-bg-subtle);
}

.ltl-xong .ltl-phu[b-21v2erdivw] {
    color: var(--bs-success-text-emphasis);
}

.ltl-dang-lam[b-21v2erdivw] {
    border-color: var(--bs-primary-border-subtle);
    background-color: var(--bs-primary-bg-subtle);
    border-left-width: .25rem;
}

.ltl-dang-lam .ltl-phu[b-21v2erdivw] {
    color: var(--bs-primary-text-emphasis);
}

.ltl-chua-toi[b-21v2erdivw] {
    background-color: var(--bs-secondary-bg);
}

/* Nấc KHÔNG BIẾT — viền đứt, đọc được cả khi ⛔ không phân biệt được màu. */
.ltl-chua-do[b-21v2erdivw] {
    border-style: dashed;
    background-color: transparent;
}

/* Đốt người xem ĐANG ĐỨNG. Vòng ngoài, ⛔ không đổi nền: nền đang chở nấc tiến độ, đè lên là mất
   một trong hai tin. */
.ltl-dang-dung[b-21v2erdivw] {
    box-shadow: 0 0 0 .17rem var(--bs-primary-border-subtle);
    border-color: var(--bs-primary);
}

/* ── Mũi tên nối ───────────────────────────────────────────────────────── */

.ltl-noi[b-21v2erdivw] {
    display: flex;
    align-items: center;
    gap: 1px;
    color: var(--bs-secondary-color);
    opacity: .55;
}

.ltl-vach[b-21v2erdivw] {
    width: 1.5rem;
    height: 3px;
    border-radius: 2px;
    background-image: repeating-linear-gradient(90deg, currentColor 0 6px, transparent 6px 12px);
    background-size: 12px 3px;
}

/* ⚠ Cỡ ≥ 0,65rem là NGƯỠNG ĐỌC ĐƯỢC của app (hàng rào `CoChuONgayTests`). Đầu mũi tên tuy là dấu
   trang trí (`aria-hidden`) nhưng vẫn là một ký tự vẽ ra màn hình — nâng cho đủ ngưỡng rẻ hơn hẳn
   việc khai một dòng miễn trừ, mà miễn trừ thì lượt sau lại phải đọc để hiểu vì sao có nó. */
.ltl-dinh[b-21v2erdivw] {
    font-size: .7rem;
    line-height: 1;
}

/* Chỗ nối mà LUỒNG ĐÃ ĐI QUA thì vạch trôi — đây là "mũi tên động" anh Tiến nói.
   ⛔ Cố ý KHÔNG cho mọi mũi tên cùng chạy: chạy tất thì nó thôi mang tin, chỉ còn là hoạt hoạ. */
.ltl-chay[b-21v2erdivw] {
    color: var(--bs-primary);
    opacity: 1;
}

.ltl-chay .ltl-vach[b-21v2erdivw] {
    animation: ltl-troi-b-21v2erdivw .9s linear infinite;
}

@keyframes ltl-troi-b-21v2erdivw {
    from {
        background-position: 0 0;
    }

    to {
        background-position: 12px 0;
    }
}

/* Người tắt hiệu ứng chuyển động ở hệ điều hành thì dải đứng yên — vạch vẫn SÁNG màu nên tin
   "luồng đã đi qua đây" ⛔ không mất. */
@media (prefers-reduced-motion: reduce) {
    .ltl-chay .ltl-vach[b-21v2erdivw] {
        animation: none;
    }

    .ltl-dot[b-21v2erdivw] {
        transition: none;
    }
}

/* Màn hẹp: dải xếp DỌC, mũi tên quay xuống. */
@media (max-width: 575.98px) {
    .ltl-dai[b-21v2erdivw] {
        flex-direction: column;
    }

    .ltl-noi[b-21v2erdivw] {
        justify-content: center;
        transform: rotate(90deg);
    }
}

/* ─────────── ⭐ SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY (việc o1519) ───────────
   `.ltl-dinh` đo tĩnh .7rem = 11,2px, dưới sàn `--cham-chu-toi-thieu` (12px, khai ở `app.css`).
   📏 ⛔ Không tràn: đầu mũi tên đứng trong `.ltl-noi` — một ô flex chỉ chứa vạch nối co giãn và
      đúng một ký tự; ký tự rộng thêm chưa tới 1px thì vạch nối co lại đúng chừng ấy. Ở khổ ≤575,98px
      dải xếp DỌC (`.ltl-noi` xoay 90°) nên phần rộng thêm rơi vào chiều CAO của dải, ⛔ không phải bề ngang.
   🔑 Đọc BIẾN, ⛔ đừng gõ thẳng 12px. ⛔ CHỈ trong `@media (pointer: coarse)` — xem `app.css` (đợt 1518). */
@media (pointer: coarse) {
    .ltl-dinh[b-21v2erdivw] { font-size: max(.7rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/Pages/DuAn/KhoiSoanToTrinhChuTruong.razor.rz.scp.css */
/* ═══ ĐỢT 1978 (việc o1262) — PHIẾU SOẠN HAI CỘT: Ô ĐIỀN bên trái, TỜ GIẤY bên phải ═══
   Anh Tiến 04/09/2026: "bên trái là ô điền, bên phải là bản word khi nội dung điền vào".

   ⚠ CSS CÁCH LY: mỗi bộ chọn ở đây biên dịch ra dạng có dấu scope, tức độ đặc hiệu (0,2,0).
     Vì thế lớp tiện dụng của Bootstrap mang sẵn dấu quan trọng vẫn thắng — đừng chồng hai lối.
   ⚠ Thân hộp thoại được dọn đệm bằng lớp tiện dụng "p-0" truyền qua tham số LopThanHop, KHÔNG
     bằng luật ở tệp này: thẻ ấy nằm trong component HopThoai nên mang dấu scope KHÁC, một luật
     viết ở đây sẽ không bao giờ chạy mà build vẫn xanh. */

/* ── Khung hai cột ─────────────────────────────────────────────────────────────────────── */
.pst-khung[b-2kqbmctms9] {
    display: grid;
    grid-template-columns: minmax(320px, 5fr) minmax(360px, 7fr);
    max-height: min(78vh, 900px);
    max-height: min(78dvh, 900px);
}

.pst-trai[b-2kqbmctms9] {
    overflow-y: auto;
    padding: .75rem 1rem 1rem;
}

.pst-phai[b-2kqbmctms9] {
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: var(--bs-tertiary-bg, #f4f5f7);
    border-left: 1px solid var(--bs-border-color, #dee2e6);
}

/* Khổ hẹp thì xếp dọc: tờ giấy xuống dưới ô điền, và bỏ trần chiều cao để trang tự cuộn.
   Một điều kiện duy nhất — câu media ghép bằng "and" đã có lượt trả giá ở đợt 1481. */
@media (max-width: 991.98px) {
    .pst-khung[b-2kqbmctms9] {
        grid-template-columns: 1fr;
        max-height: none;
    }

    .pst-phai[b-2kqbmctms9] {
        border-left: 0;
        border-top: 1px solid var(--bs-border-color, #dee2e6);
    }
}

/* ── Thanh tiến độ điền ────────────────────────────────────────────────────────────────── */
.pst-tien-do[b-2kqbmctms9] {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .5rem;
}

.pst-tien-do-chu[b-2kqbmctms9] {
    font-size: .82rem;
    white-space: nowrap;
}

.pst-tien-do-so[b-2kqbmctms9] {
    font-size: .78rem;
    color: var(--bs-secondary-color, #6c757d);
    min-width: 2.6rem;
    text-align: right;
}

.pst-thanh[b-2kqbmctms9] {
    flex: 1;
    height: 8px;
}

/* ── Dải chip nhóm ô ───────────────────────────────────────────────────────────────────── */
.pst-dai-chip[b-2kqbmctms9] {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin-bottom: .7rem;
}

.pst-chip[b-2kqbmctms9] {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .28rem .62rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 999px;
    background: var(--bs-body-bg, #fff);
    color: var(--bs-body-color, #212529);
    font-size: .78rem;
    line-height: 1.25;
}

.pst-chip:hover[b-2kqbmctms9] {
    border-color: var(--bs-primary, #0a3d91);
}

.pst-chip.dang-chon[b-2kqbmctms9] {
    background: var(--bs-primary, #0a3d91);
    border-color: var(--bs-primary, #0a3d91);
    color: #fff;
}

.pst-chip-icon[b-2kqbmctms9] {
    font-size: .9rem;
}

.pst-chip-dem[b-2kqbmctms9] {
    padding: 0 .38rem;
    border-radius: 999px;
    background: var(--bs-secondary-bg, #e9ecef);
    color: var(--bs-secondary-color, #6c757d);
    font-size: .72rem;
    font-variant-numeric: tabular-nums;
}

.pst-chip.dang-chon .pst-chip-dem[b-2kqbmctms9] {
    background: rgba(255, 255, 255, .22);
    color: #fff;
}

/* Nhóm đã điền đủ ô: huy hiệu xanh — số CÓ LỢI theo luật màu của dự án. */
.pst-chip.du-o .pst-chip-dem[b-2kqbmctms9] {
    background: var(--bs-success, #198754);
    color: #fff;
}

/* ── Khối ô của nhóm đang chọn ─────────────────────────────────────────────────────────── */
.pst-khoi-o[b-2kqbmctms9] {
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: .5rem;
    padding: .7rem .8rem .3rem;
    background: var(--bs-body-bg, #fff);
}

.pst-khoi-dau[b-2kqbmctms9] {
    margin-bottom: .6rem;
    padding-bottom: .45rem;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
}

.pst-khoi-ten[b-2kqbmctms9] {
    display: block;
    font-weight: 600;
    font-size: .92rem;
}

.pst-khoi-mo-ta[b-2kqbmctms9] {
    display: block;
    margin-top: .1rem;
    font-size: .74rem;
    color: var(--bs-secondary-color, #6c757d);
}

.pst-o[b-2kqbmctms9] {
    margin-bottom: .6rem;
    padding: .1rem 0 .1rem .6rem;
    border-left: 3px solid var(--bs-border-color, #dee2e6);
}

.pst-o.da-dien[b-2kqbmctms9] {
    border-left-color: var(--bs-primary, #0a3d91);
}

.pst-nhan[b-2kqbmctms9] {
    display: block;
    margin-bottom: .18rem;
    font-size: .82rem;
    font-weight: 600;
}

.pst-sao[b-2kqbmctms9] {
    margin-left: .15rem;
    color: var(--bs-danger, #dc3545);
}

/* Tên chỗ trống trên biểu mẫu — cho người lập biết ô này in vào đâu trên tờ giấy. */
.pst-the[b-2kqbmctms9] {
    margin-top: .16rem;
    font-family: ui-monospace, Consolas, "Courier New", monospace, "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    font-size: .7rem;
    color: var(--bs-secondary-color, #6c757d);
    opacity: .85;
    word-break: break-all;
}

.pst-goi-y[b-2kqbmctms9] {
    margin-top: .12rem;
    font-size: .72rem;
    line-height: 1.35;
    color: var(--bs-secondary-color, #6c757d);
}

.pst-tien-ok[b-2kqbmctms9] {
    margin-top: .16rem;
    font-size: .74rem;
    color: var(--bs-success-text-emphasis, #146c43);
}

.pst-tien-sai[b-2kqbmctms9] {
    margin-top: .16rem;
    font-size: .74rem;
    color: var(--bs-danger, #dc3545);
}

/* ── Cột phải: đầu cột và chú giải ─────────────────────────────────────────────────────── */
.pst-phai-dau[b-2kqbmctms9] {
    display: flex;
    align-items: center;
    gap: .4rem;
    flex-wrap: wrap;
    padding: .55rem .8rem .4rem;
}

.pst-phai-ten[b-2kqbmctms9] {
    font-weight: 600;
    font-size: .9rem;
}

.pst-chu-giai[b-2kqbmctms9] {
    padding: 0 .8rem .5rem;
    font-size: .72rem;
    line-height: 1.4;
    color: var(--bs-secondary-color, #6c757d);
}

.pst-chu-giai-nhac[b-2kqbmctms9] {
    display: block;
    margin-top: .2rem;
}

.pst-mau[b-2kqbmctms9] {
    display: inline-block;
    width: .8rem;
    height: .8rem;
    margin: 0 .12rem 0 .5rem;
    border-radius: 2px;
    vertical-align: -1px;
}

.pst-mau:first-child[b-2kqbmctms9] {
    margin-left: 0;
}

.pst-mau-dien[b-2kqbmctms9] {
    background: rgba(10, 61, 145, .22);
    border: 1px solid rgba(10, 61, 145, .55);
}

.pst-mau-trong[b-2kqbmctms9] {
    background: #fff3cd;
    border: 1px solid #d9a800;
}

/* ── Tờ giấy ───────────────────────────────────────────────────────────────────────────── */
.pst-nen-giay[b-2kqbmctms9] {
    flex: 1;
    overflow: auto;
    padding: 0 .8rem 1rem;
}

/* Tờ giấy CỐ Ý luôn trắng, kể cả ở nền tối: nó mô phỏng tờ sẽ IN RA. Lật nó theo bộ màu là
   người lập soát một tờ giấy khác tờ mình sắp cầm trên tay. */
.pst-giay[b-2kqbmctms9] {
    width: min(100%, 794px);
    margin: 0 auto;
    padding: 30px 34px 34px;
    background: #fff;
    color: #111;
    border: 1px solid #d5d7db;
    border-radius: 3px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .12);
    font-family: "Times New Roman", Times, serif, "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    font-size: 13.5px;
    line-height: 1.5;
}

.pst-doan[b-2kqbmctms9] {
    margin: 0 0 .2rem;
    white-space: pre-wrap;
    tab-size: 6;
    word-break: break-word;
}

.pst-doan-trong[b-2kqbmctms9] {
    min-height: .95em;
}

.pst-le-trai[b-2kqbmctms9] {
    text-align: left;
}

.pst-le-giua[b-2kqbmctms9] {
    text-align: center;
}

.pst-le-phai[b-2kqbmctms9] {
    text-align: right;
}

.pst-le-deu[b-2kqbmctms9] {
    text-align: justify;
}

/* Đoạn nằm trong danh sách Word TỰ ĐÁNH SỐ: chữ số không có trong nội dung tệp nên ở đây chỉ
   đặt một dấu mờ giữ chỗ. Câu nói thẳng điều này nằm trong khối chú giải của cột phải. */
.pst-tu-danh-so[b-2kqbmctms9]::before {
    content: "▪ ";
    color: #b4b7bb;
}

.pst-dam[b-2kqbmctms9] {
    font-weight: 700;
}

.pst-nghieng[b-2kqbmctms9] {
    font-style: italic;
}

.pst-chu-san[b-2kqbmctms9] {
    color: inherit;
}

.pst-chu-dien[b-2kqbmctms9] {
    color: #0a3d91;
    background: rgba(10, 61, 145, .08);
    border-radius: 2px;
}

.pst-chu-trong[b-2kqbmctms9] {
    background: #fff3cd;
    color: #7a5b00;
    border-bottom: 1px dashed #d9a800;
}

/* ─────────── ⭐ SÀN CỠ CHỮ 12px KHI CẦM BẰNG NGÓN TAY (việc o1519) ───────────
   Bảy chỗ đo tĩnh dưới sàn `--cham-chu-toi-thieu` (12px, khai ở `app.css`):
   `.pst-chip-dem` .72rem = 11,52px · `.pst-khoi-mo-ta` .74rem = 11,84px · `.pst-the` .7rem = 11,2px ·
   `.pst-goi-y` .72rem · `.pst-tien-ok` .74rem · `.pst-tien-sai` .74rem · `.pst-chu-giai` .72rem.
   📏 ⛔ Không tràn: sáu chỗ là khối `block` chiếm trọn bề ngang cột, chữ chỉ xuống dòng thêm; riêng
      `.pst-chip-dem` là viên đếm nằm trong nút `.pst-chip` — nó chỉ chứa 1–2 chữ số, đệm ngang
      .38rem và nút tự nong theo nội dung nên nâng một nấc cỡ chữ ⛔ không đẩy dải nút vỡ hàng.
   🔑 Đọc BIẾN `--cham-chu-toi-thieu` chứ ⛔ đừng gõ thẳng 12px — đổi sàn thì đổi ở MỘT chỗ.
   ⛔ CHỈ trong `@media (pointer: coarse)` — máy tính ⛔ không đổi một pixel; lý do đầy đủ ở
      `app.css` (đợt 1518): khổ hẹp ⛔ KHÔNG có nghĩa là ngón tay. */
@media (pointer: coarse) {
    .pst-chip-dem[b-2kqbmctms9] { font-size: max(.72rem, var(--cham-chu-toi-thieu)); }
    .pst-khoi-mo-ta[b-2kqbmctms9] { font-size: max(.74rem, var(--cham-chu-toi-thieu)); }
    .pst-the[b-2kqbmctms9] { font-size: max(.7rem, var(--cham-chu-toi-thieu)); }
    .pst-goi-y[b-2kqbmctms9] { font-size: max(.72rem, var(--cham-chu-toi-thieu)); }
    .pst-tien-ok[b-2kqbmctms9] { font-size: max(.74rem, var(--cham-chu-toi-thieu)); }
    .pst-tien-sai[b-2kqbmctms9] { font-size: max(.74rem, var(--cham-chu-toi-thieu)); }
    .pst-chu-giai[b-2kqbmctms9] { font-size: max(.72rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/Pages/DuAn/NapDuAnDauTu.razor.rz.scp.css */
/* ═══ VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (đợt 1605, việc o737) ═══
   hai ô tick TRẦN của bảng quét dự án: ô “chọn tất cả” ở đầu bảng và ô chọn từng dòng.

   🔑 Nới VÙNG CHẠM QUANH ô tick, ⛔ KHÔNG phóng to chính ô vuông: ô tick 44px đứng cạnh dòng chữ
     15px trông như lỗi phần mềm, và nó cũng ⛔ không giải đúng bài — thứ ngón tay bấm trượt là
     khoảng trống quanh ô. Thẻ bọc `label.o-tick-cham` khai ngay trong `.razor`.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là ngón
     tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ
     quyết định vùng bấm là CÁI CHẠM VÀO, ⛔ không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, ⛔ không một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css`: bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi: node cong-cu/soi-o-tick-tran.mjs · node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ⚠ Cần CẢ `min-width`: ô tick chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt NGANG.
       ⚠ `inline-flex` chứ ⛔ không `flex`: thẻ bọc nằm trong một ô bảng (hoặc một hàng `d-flex`)
         cạnh nút và ô gõ khác — hoá `flex` là nó chiếm trọn một dòng, hàng đang gọn bỗng vỡ ra.
       ⚠ `min-height` chứ ⛔ không `height`: hàng nào cao hơn thì cứ để cao — đặt `height` là bóp
         chúng lại, sửa một lỗi rồi đẻ một lỗi. */
    .o-tick-cham[b-hk9h4u6l58] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/DuAn/NapHoSoTuPdf.razor.rz.scp.css */
/* ⭐ ĐỢT 1142 (việc o321) — trang 📥 Nạp hồ sơ dự án từ tệp PDF.
   Chỉ vài luật rất hẹp; mọi thứ còn lại dùng Bootstrap 5.3 sẵn có.
   ⚠ Tuyệt đối KHÔNG gõ cặp ký tự đóng chú thích vào giữa câu văn: chú thích đóng ngay tại đó và phần
     còn lại dính vào bộ chọn của luật ngay dưới, làm trình duyệt bỏ nguyên luật ấy — hỏng LẶNG LẼ,
     build xanh test xanh (bài học đợt 852, có hàng rào ChuThichCssDongSomTests canh). */

/* Thanh tiến độ đọc trang: đặt chiều cao ở đây thay vì gõ style nội tuyến, để phần trăm là thứ DUY
   NHẤT đi vào thuộc tính style của thẻ. */
.nht-thanh-tien-do[b-iig710v3to] {
    height: 1.15rem;
}

/* Nguyên văn đoạn chữ máy đọc được — có thể rất dài và không có dấu cách (OCR dính chữ), phải cho
   xuống dòng chứ đừng để nó đẩy ngang cả thẻ. */
.nht-nguyen-van code[b-iig710v3to] {
    overflow-wrap: anywhere;
    word-break: break-word;
}
/* /Components/Pages/DuAn/XepLoaiHoSo.razor.rz.scp.css */
/* ═══ VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (đợt 1605, việc o737) ═══
   ô tick TRẦN chọn hồ sơ để xếp loại hàng loạt.

   🔑 Nới VÙNG CHẠM QUANH ô tick, ⛔ KHÔNG phóng to chính ô vuông: ô tick 44px đứng cạnh dòng chữ
     15px trông như lỗi phần mềm, và nó cũng ⛔ không giải đúng bài — thứ ngón tay bấm trượt là
     khoảng trống quanh ô. Thẻ bọc `label.o-tick-cham` khai ngay trong `.razor`.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là ngón
     tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ
     quyết định vùng bấm là CÁI CHẠM VÀO, ⛔ không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, ⛔ không một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css`: bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi: node cong-cu/soi-o-tick-tran.mjs · node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ⚠ Cần CẢ `min-width`: ô tick chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt NGANG.
       ⚠ `inline-flex` chứ ⛔ không `flex`: thẻ bọc nằm trong một ô bảng (hoặc một hàng `d-flex`)
         cạnh nút và ô gõ khác — hoá `flex` là nó chiếm trọn một dòng, hàng đang gọn bỗng vỡ ra.
       ⚠ `min-height` chứ ⛔ không `height`: hàng nào cao hơn thì cứ để cao — đặt `height` là bóp
         chúng lại, sửa một lỗi rồi đẻ một lỗi. */
    .o-tick-cham[b-rb08icaq2h] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/HanhChinh/KhoiToLichCongTacTuan.razor.rz.scp.css */
/* ═══ ⭐ VIỆC o1568 (đợt 2149) — TỜ LỊCH CÔNG TÁC TUẦN ═══
   Dựng lại dáng tờ giấy thật: đầu tờ căn giữa, bảng tám cột viền kín, chữ nhỏ như bản in khổ ngang.

   🔴 MÀU LẤY TỪ TOKEN --bs-*, KHÔNG gõ mã màu: app có nền TỐI, một tờ giấy trắng đóng đinh là dải sáng
      chói giữa màn hình tối, và chữ đen trên nền tối thì mất hẳn. Token đã theo hai trục sáng/tối. */

.tlct-to[b-azvxpfzssx] {
    border: 1px solid var(--bs-border-color);
    border-radius: .35rem;
    padding: .9rem .9rem .7rem;
    background: var(--bs-body-bg);
}

/* ── ĐẦU TỜ ── */
.tlct-don-vi[b-azvxpfzssx] {
    font-size: .95rem;
    margin-bottom: .35rem;
}

.tlct-tieu-de[b-azvxpfzssx] {
    text-align: center;
    font-weight: 700;
    font-size: 1.05rem;
    margin-bottom: .6rem;
}

/* Cụm "(Bổ sung lần N)" — bản giấy in ĐỎ, đây là dấu hiệu người đọc nhận ra tờ mình cầm không còn là
   bản phát lần đầu. Dùng token nguy hiểm của Bootstrap để nó vẫn đọc được ở nền tối. */
.tlct-bo-sung[b-azvxpfzssx] {
    color: var(--bs-danger);
}

/* 🗓 ĐỢT 2277b (việc o1797) — HÀNG CỦA NGÀY HÔM NAY. Anh Tiến 10/09/2026: *"view mặc định tua đến
   ngày hiện tại"*; tua tới nơi mà ⛔ không đánh dấu thì người đọc vẫn phải dò xem mình đang ở đâu.
   ⚠ Dùng nền MỜ (`--bs-primary-bg-subtle`) chứ ⛔ không tô đậm: tờ này in ra giấy, và bảng đang có
     sẵn viền ô — thêm một mảng màu đậm là mắt đọc ra "ô được chọn" chứ ⛔ không phải "ngày hôm nay".
   ⚠ `> td` để luật ⛔ không rỉ xuống bảng lồng bên trong ô, nếu về sau có ai đặt bảng vào đó. */
.tlct-hang-hom-nay > td[b-azvxpfzssx] {
    background-color: var(--bs-primary-bg-subtle);
}

/* ── BẢNG TÁM CỘT ── */
.tlct-bang[b-azvxpfzssx] {
    font-size: .82rem;
    /* Cột đã khai phần trăm ở markup ⇒ ép bảng chia theo đúng khai báo, đừng để trình duyệt tự co giãn
       theo nội dung (một ô "Thành phần tham gia" dài là nuốt hết bề ngang của bảy cột kia). */
    table-layout: fixed;
    min-width: 1100px;
}

.tlct-bang > thead th[b-azvxpfzssx] {
    text-align: center;
    vertical-align: middle;
    font-size: .8rem;
}

.tlct-bang > tbody td[b-azvxpfzssx] {
    vertical-align: top;
    word-break: break-word;
}

.tlct-giua[b-azvxpfzssx] {
    text-align: center;
}

/* ⭐ ĐỢT 2326 (việc o1888) — ô «Ngày · Giờ · Địa điểm»: tên ngày (chỉ hàng đầu của ngày) · giờ đậm · địa điểm. */
.tlct-cot-tgdd .tlct-ten-ngay[b-azvxpfzssx] {
    white-space: nowrap;
    margin-bottom: .25rem;
}

.tlct-gio[b-azvxpfzssx] {
    font-weight: 600;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.tlct-o-dia-diem[b-azvxpfzssx] {
    margin-top: .25rem;
}

/* ⭐ VIỆC o1602 (đợt 2163) — MỖI MỤC LÀ MỘT HÀNG THẬT nên phép căn giữa các cột do chính <tr> lo.
   ⛔ ĐỪNG dựng lại lớp `.tlct-dong` cũ (mỗi ô một chồng div): nó là nguyên nhân giờ trôi lệch khỏi
   việc của nó — ô nội dung cao hai dòng, ô giờ cao một dòng, ⛔ không CSS nào cứu được chuyện đó. */

/* Ô nhập nằm giữa ô của bảng, ⛔ không dính sát viền — tờ giấy thật cũng chừa mép. */
.tlct-bang > tbody td > .form-control[b-azvxpfzssx] {
    min-width: 0;
}

/* Hàng cuối mỗi ngày, chỗ đặt nút ➕. Để trống hẳn cho mắt nghỉ giữa hai ngày. */
.tlct-hang-them[b-azvxpfzssx] {
    background: var(--bs-tertiary-bg);
    height: 6px;
    padding: 0;
}

/* Dòng CHƯA BAN HÀNH — bản nháp chỉ người soạn/duyệt nhìn thấy; in nghiêng mờ để phân biệt ngay trên tờ
   với dòng cả cơ quan đang đọc. */
.tlct-nhap[b-azvxpfzssx] {
    font-style: italic;
    color: var(--bs-secondary-color);
}

/* ⭐ ĐỢT 2325 (việc o1887) — CHỮ «NỘI DUNG CÔNG VIỆC» IN ĐẬM. Anh Tiến 12/09/2026: «nội dung công việc thì phải được
   in đậm». Cả lúc XEM lẫn lúc SỬA (ô gõ cũng đậm) — thứ người soạn gõ trông đúng như thứ cả cơ quan sẽ đọc. Dòng nháp
   vẫn nghiêng + nhạt như trên, chỉ thêm đậm.
   ⓘ Bản Word tải về đi đường form gốc (quy tắc #19), ⛔ đổi theo luật này. */
.tlct-noi-dung[b-azvxpfzssx],
.tlct-noi-dung > textarea[b-azvxpfzssx] {
    font-weight: 700;
}

.tlct-trong[b-azvxpfzssx] {
    text-align: center;
    color: var(--bs-secondary-color);
}

/* ⭐ ĐỢT 2356 (việc o1941) — Ô «TRỰC PHÒNG BAN» LÚC SOẠN. Anh Tiến 12/09/2026: «khi sửa lịch tổng kho không cho phép
   sửa trực phòng ban (muốn sửa thì vào từng phòng sửa theo quyền hạn)». Ô vốn chỉ đọc; lúc soạn nó xám đi và tên
   người (thẻ gọi điện) ⛔ bấm được, để người soạn thấy ngay ô này ⛔ thuộc phần đang sửa. */
.tlct-o-khoa[b-azvxpfzssx] {
    background-color: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
}

.tlct-o-khoa a[b-azvxpfzssx] {
    pointer-events: none;
    color: inherit;
    text-decoration: none;
}

/* Cột thứ chín — CÔNG CỤ SOẠN, không thuộc tờ giấy và không đi vào bản Word. */
.tlct-cot-soan[b-azvxpfzssx] {
    width: 62px;
    text-align: center;
}

/* ── CHÂN TỜ ── */
.tlct-ghi-chu[b-azvxpfzssx] {
    margin-top: .55rem;
    font-size: .88rem;
}

.tlct-o-ghi-chu[b-azvxpfzssx] {
    max-width: 520px;
    vertical-align: middle;
}

/* 🔁 ĐỢT 2313 (việc o1869) — luật `.tlct-o-bo-sung` (ô gõ tay «Bổ sung lần») gỡ theo chính cái ô: số nay tự đánh. */
/* /Components/Pages/HanhChinh/OGioLich.razor.rz.scp.css */
/* ⭐ ĐỢT 2325 (việc o1887) — hai ô «từ … đến …».
   ⭐ ĐỢT 2356 (việc o1934) — anh Tiến 12/09/2026: «thu gọn ô giờ này hiện trên cùng 1 dòng dạng 24h cho tiết kiệm
   không gian ví dụ: 11:00 - 14:00». Hai ô nay là ô CHỌN 24 giờ nằm trong .ogl-hang và ⛔ xuống dòng; riêng .ogl vẫn
   cho xuống dòng để hai dòng chữ nhỏ (giờ cũ gõ tay · giờ kết thúc sai) rơi xuống dưới hai ô. */
.ogl[b-0qjnxzipc9] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .2rem .35rem;
}

/* Cột «Ngày · Giờ · Địa điểm» của tờ tuần chỉ 13% bề ngang (~159px ở khung 1400px) ⇒ hai ô CO lại được (min-width)
   chứ ⛔ đẩy nhau xuống dòng. */
.ogl-hang[b-0qjnxzipc9] {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: .25rem;
    min-width: 0;
}

/* Ô chọn gọn: chữ số đều bề ngang, mũi tên thu nhỏ sát mép phải (Bootstrap mặc định chừa 2.25rem cho mũi tên).
   Bộ chọn cách ly (0,2,0) thắng .form-select · .form-select-sm (0,1,0) của Bootstrap. */
.ogl-o[b-0qjnxzipc9] {
    flex: 1 1 4.4rem;
    min-width: 3.9rem;
    max-width: 4.8rem;
    font-size: .8125rem;
    font-variant-numeric: tabular-nums;
    padding: .2rem 1.15rem .2rem .35rem;
    background-position: right .25rem center;
    background-size: 10px 8px;
}

.ogl-den[b-0qjnxzipc9] {
    font-size: .75rem;
    color: var(--bs-secondary-color);
}

/* Hai dòng chữ nhỏ dưới hai ô: chiếm trọn một hàng của dải flex. */
.ogl-cu[b-0qjnxzipc9],
.ogl-loi[b-0qjnxzipc9] {
    flex-basis: 100%;
    font-size: .75rem;
}

/* Chuỗi cũ gõ tay mà ⛔ đọc ra giờ — in ra để người soạn biết ô đang giữ gì; chọn giờ là thay. */
.ogl-cu[b-0qjnxzipc9] {
    font-style: italic;
    color: var(--bs-secondary-color);
}

.ogl-loi[b-0qjnxzipc9] {
    color: var(--bs-danger-text-emphasis);
}
/* /Components/Pages/HanhChinh/OTrucPhongBan.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 2326 (việc o1888) — Ô «TRỰC PHÒNG BAN» ═══
   Mỗi phòng một dòng: tên phòng (nhạt, nhỏ) · người trực (đậm, như tên ở ô «Trực GĐ») · số điện thoại (thẻ neo
   bấm gọi, số dàn đều). Nhiều người cùng phòng thì nối trên cùng dòng bằng chấm giữa.
   🔁 ĐỢT 2352 (việc o1933) — số điện thoại thôi in ra tờ: tên là thẻ neo gọi điện, số nằm trong `title` (anh Tiến
     12/09/2026: «hiển thị tên ẩn số điện thoại đi, di chuột đến mới hiện rõ ra»). Bản in vẫn in số.
   ⓘ Màu chỉ từ token --bs-*: app có nền tối (luật đợt 831). */

.otpb-ds[b-ex84spd22w] {
    list-style: none;
    margin: 0;
    padding: 0;
}

.otpb-phong + .otpb-phong[b-ex84spd22w] {
    margin-top: .3rem;
}

.otpb-ten-phong[b-ex84spd22w] {
    margin-right: .25rem;
    font-size: .78rem;
    color: var(--bs-secondary-color);
}

.otpb-ten[b-ex84spd22w] {
    font-weight: 600;
}

/* 🔁 ĐỢT 2352 — tên mang số điện thoại là thẻ neo nhưng ĐỌC như tên thường (cùng màu chữ, ⛔ gạch chân): cả cột mà
   xanh gạch chân là một bảng liên kết, mắt ⛔ còn tách được tên với tên phòng. Gạch chấm hiện khi rê chuột = dấu
   hiệu «ở đây có số». */
.otpb-ten-goi[b-ex84spd22w] {
    color: inherit;
    text-decoration: none;
}

.otpb-ten-goi:hover[b-ex84spd22w],
.otpb-ten-goi:focus-visible[b-ex84spd22w] {
    text-decoration: underline dotted;
    text-underline-offset: .2em;
}

/* ⭐ ĐỢT 2343 (việc o1914) — ca trực viết tắt ngay sau tên: nhạt và nhỏ như tên phòng, ⛔ gãy dòng giữa ngoặc. */
.otpb-ca[b-ex84spd22w] {
    margin-left: .2rem;
    font-size: .78rem;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}

/* 🔁 ĐỢT 2352 — một người là MỘT KHỐI ⛔ gãy dòng: bỏ số đi thì dòng ngắn lại, nhưng để trình duyệt tự gãy thì vẫn
   đứt đôi tên («M.T.» cuối dòng này, «Hương» đầu dòng sau — đúng như ảnh anh gửi). Chỗ gãy chỉ còn GIỮA hai người.
   🔑 `inline-block` là BẮT BUỘC, ⛔ chỉ `nowrap`: Blazor BỎ khoảng trắng quanh `@foreach`, nên giữa tên phòng và người
     đầu tiên (và giữa hai người) ⛔ có dấu cách nào để ngắt ⇒ chỉ `nowrap` thì chỗ ngắt duy nhất còn lại nằm TRONG
     «P. ĐĐKC:» — đo ảnh 12/09/2026: «P.» đứng một mình một dòng. Theo luật ngắt dòng của CSS, trước và sau một khối
     `inline-block` LUÔN có chỗ ngắt, dù ⛔ có dấu cách. */
.otpb-nguoi[b-ex84spd22w] {
    display: inline-block;
    white-space: nowrap;
}

/* Người thứ hai trở đi của cùng một phòng: chấm giữa làm dấu ngăn — là nét trang trí, ⛔ phải chữ.
   🔁 ĐỢT 2352 — chấm đi SAU người đứng trước (⛔ trước người đứng sau): mỗi người đã ⛔ gãy dòng, nên chấm gắn vào
   người sau sẽ dắt theo xuống đầu dòng mới. */
.otpb-nguoi:not(:last-child)[b-ex84spd22w]::after {
    content: "·";
    margin: 0 .3rem;
    color: var(--bs-secondary-color);
}

/* 🖨 Số chỉ dành cho giấy. */
.otpb-sdt-in[b-ex84spd22w] {
    display: none;
}

@media print {
    .otpb-sdt-in[b-ex84spd22w] {
        display: inline;
        margin-left: .3rem;
        font-variant-numeric: tabular-nums;
    }
}

.otpb-trong[b-ex84spd22w] {
    font-style: italic;
    color: var(--bs-secondary-color);
}

/* ── NGÓN TAY: thẻ neo gọi điện (nay là chính cái TÊN) phải đủ 44px (biến chung, đợt 1518) ──
   ⚠ Điều kiện là CÁI CHẠM VÀO, ⛔ phải bề ngang. Thẻ này ⛔ mang phép cắt ba chấm nên `inline-flex` là an toàn
     (bài học ⑫ đợt 2198). */
@media (pointer: coarse) {
    .otpb-ten-goi[b-ex84spd22w] {
        display: inline-flex;
        align-items: center;
        min-height: var(--cham-toi-thieu);
    }

    /* Tên phòng lên dòng riêng ⇒ những người trực của phòng đứng chung một dòng, cùng dáng ô «Trực GĐ». Để tên phòng
       chung dòng thì chuỗi «P. …: Họ tên» gần như luôn đẩy người đầu tiên xuống dòng dưới, để lại một dải trống (đo
       trên thẻ điện thoại đợt 2326: tên ở y 717, số ở y 814 — ảnh khổ gấp đôi). */
    .otpb-ten-phong[b-ex84spd22w] {
        display: block;
        margin-right: 0;
    }
}
/* /Components/Pages/HanhChinh/TrangCapPhatVatTu.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — HAI Ô CÔNG TẮC CỦA TRANG CẤP PHÁT VẬT TƯ (đợt 1672, việc o962) ═══

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài — nâng chính ô lên 44px thì nó to bằng cả dòng chữ bên cạnh. Thứ phải nới là VÙNG CHẠM
   QUANH ô, mà vùng ấy mỗi màn hình một khác nên phải nới tại chỗ, bằng CSS cách ly.

   📏 ĐO SỐNG TRƯỚC KHI VÁ — 01/09/2026, Chrome thật, khung 390×844 cảm ứng:
     node cong-cu/do-song-o-tick-cham.mjs --trang /hanh-chinh/cap-phat-vat-tu
     Ô lọc «Hiện cả vật tư đã ngưng» trên thanh tiêu đề thẻ: cấu trúc ③ hai anh em · ô vuông 32×16
     (công tắc) · nhãn cao 24 · hộp `.form-check` cao 24 (display block) · VÙNG BẤM THẬT tại cột
     công tắc 17px · tại cột chữ 25px · lệch tâm dọc 0px.
   ⚠ Ô thứ hai («Đang dùng», trong biểu mẫu soạn vật tư) ⛔ CHƯA đo sống được: biểu mẫu ấy chỉ
     hiện sau khi bấm Thêm mới, mà bộ đo ⛔ không bấm. Nó cùng cấu trúc `div.form-check.form-switch`
     và cùng ăn luật này — nhưng con số của nó là SUY RA, ⛔ đừng đọc thành số đã đo.

   🔴 HAI HẰNG SỐ CỦA `form-switch` LÀ 2,5em, ⛔ KHÔNG PHẢI 1,5em NHƯ Ô TICK VUÔNG. Đọc thẳng
     trong `wwwroot/bootstrap/bootstrap.min.css`: `.form-switch { padding-left: 2.5em }` và
     `.form-switch .form-check-input { width: 2em; margin-left: -2.5em }`. Chép cặp 1,5em của
     khuôn cũ sang đây là nhãn hở đúng 1em ngay cạnh công tắc, mà mọi bài kiểm vẫn xanh.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width` — khổ hẹp KHÔNG có nghĩa là ngón tay.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; ⛔ đừng gõ số 44px vào tệp này.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng ghép bằng chữ `and` (bài học đợt 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs` */

@media (pointer: coarse) {

    /* ① HỘP BỌC `div.form-check.form-switch` — nâng sàn chiều cao, đổi sang hộp flex để công tắc
       thôi dính mép trên (trong hộp flex thuộc tính `float` bị BỎ QUA).
       🔑 BỀ NGANG KHÔNG ĐỔI: hộp mở đầu ở 2,5em · công tắc mang lề trái −2,5em nên nằm ở 0…2em ·
         bề rộng ngoài −0,5em, cộng `gap` 0,5em ⇒ nhãn vẫn bắt đầu đúng ở 2,5em.
       ⚠ `inline-flex` chứ ⛔ không `flex`: ô lọc nằm trong thanh `card-header d-flex gap-2
         align-items-center flex-wrap` xếp ngang cùng các nút, còn ô thứ hai đứng một mình trong
         cột của biểu mẫu ⇒ mức inline an toàn cho cả hai chỗ. */
    .form-check[b-hng8jyw8c5] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    /* ② NHÃN CHỮ — vùng bấm thật (nối với công tắc bằng `for`).
       Cặp `margin-left` + `padding-left` 2,5em kéo nhãn PHỦ TRỌN cột công tắc: khoảng ngay trên
       và ngay dưới công tắc vốn thuộc `div.form-check`, một thẻ `div` ⛔ KHÔNG bấm được.
       `align-self: stretch` giữ nhãn cao hết hộp; thiếu nó là vùng bấm hụt lại còn cỡ con chữ
       trong khi hộp đã 44px — đúng ô đột biến số 23 của đợt 1619.
       ⚠ Nhãn ở đây là MỘT mẩu chữ trơn nên `display: flex` là an toàn và cần thiết để canh giữa
         dọc; nhãn NHIỀU mẩu thì ⛔ không được làm thế (xem `TrangDocHoSoPdf.razor.css`). */
    .form-check-label[b-hng8jyw8c5] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -2.5em;
        padding-left: 2.5em;
    }
}
/* /Components/Pages/HoSo/TrangDocHoSoPdf.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — HAI Ô TICK CỦA TRANG ĐỌC HỒ SƠ PDF (đợt 1672, việc o962) ═══

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài, và chính nó ghi lý do: nâng chính ô vuông lên 44px thì ô tick to đúng bằng cả dòng chữ
   bên cạnh, trông như lỗi giao diện. Thứ phải nới là VÙNG CHẠM QUANH ô — mỗi màn hình một khác.

   📏 ĐO SỐNG TRƯỚC KHI VÁ — 01/09/2026, Chrome thật, khung 390×844 cảm ứng:
     node cong-cu/do-song-o-tick-cham.mjs --trang /doc-ho-so-pdf
     Cả HAI ô cho cùng một bộ số: cấu trúc ③ hai anh em · ô vuông 16×16 · nhãn cao 42 · hộp
     `.form-check` cao 43 (display block) · VÙNG BẤM THẬT tại cột ô tick 17px · tại cột chữ 43px ·
     lệch tâm dọc −9px (ô vuông thả nổi nên dính MÉP TRÊN của nhãn hai dòng).
   🔑 ĐỌC BẢNG TRÊN CHO ĐÚNG — ĐÂY LÀ CA ĐÈN XANH GIẢ RÕ NHẤT TỪ TRƯỚC TỚI NAY: hộp `.form-check`
     đã cao 43px, tức bộ đo nào đọc HỘP CHA cũng sẽ báo *"gần đạt, chỉ thiếu 1px"*. Nhưng cột ô
     tick vẫn 17px, vì hộp ấy là `div` — ⛔ KHÔNG bấm được. Hai ô này cao sẵn ⛔ không phải nhờ ai
     nới, mà chỉ vì nhãn của chúng dài tới hai dòng chữ.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width` — khổ hẹp KHÔNG có nghĩa là ngón tay
     (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, ⛔ không một dòng nào ở ngoài.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; ⛔ đừng gõ số 44px vào tệp này.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng ghép bằng chữ `and` (bài học đợt 1481).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo; cặp ký tự ấy lọt vào giữa câu văn là chú
     thích đóng sớm và trình duyệt bỏ nguyên luật ngay dưới, ⛔ không một câu lỗi nào.
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs` */

@media (pointer: coarse) {

    /* ① HỘP BỌC `div.form-check` — nâng sàn chiều cao và đổi sang hộp flex để ô vuông thôi dính
       mép trên: trong hộp flex thuộc tính `float` bị BỎ QUA, nên ô nhận phép canh giữa dọc.

       🔑 BỀ NGANG KHÔNG ĐỔI — hộp mở đầu ở 1,5em · ô tick mang lề trái −1,5em nên khung của nó
         nằm ở 0…1em (Y HỆT chỗ cũ) · bề rộng ngoài là −0,5em, cộng `gap` 0,5em ⇒ nhãn bắt đầu
         đúng ở 1,5em, cũng Y HỆT chỗ cũ.
       ⚠ KHÔNG đụng `padding-left` của Bootstrap — chính phần đệm ấy giữ bề ngang.
       ⚠ `flex` chứ ⛔ KHÔNG `inline-flex`: hai hộp này là HAI THẺ ANH EM LIỀN NHAU, mỗi ô một
         dòng. Hoá `inline-flex` là chúng thành hộp mức inline rồi DỒN LÊN CÙNG MỘT DÒNG khi còn
         chỗ. (Ngược với `TrangCongToCongNghe`, nơi ô nằm trong một hàng công cụ xếp ngang.)
       ⚠ `min-height` chứ ⛔ không `height`: nhãn ở đây vốn đã hai dòng, đặt `height` là bóp lại. */
    .form-check[b-10r4egf7jm] {
        min-height: var(--cham-toi-thieu);
        display: flex;
        align-items: center;
        gap: .5em;
    }

    /* ② NHÃN CHỮ — vùng bấm thật (nối với ô tick bằng `for`, bấm vào nhãn là đổi tick).

       🔴 ⛔ TUYỆT ĐỐI KHÔNG THÊM `display: flex` CHO NHÃN NÀY, dù hai tệp vùng chạm làm trước
         (`TrangBangGiaMucXdcb`, `Nuoc/TabHoaDonNuoc`) đều có dòng ấy. Nhãn của chúng là MỘT mẩu
         chữ trơn; nhãn ở đây gồm ba mẩu — chữ thường, một thẻ in đậm, rồi một thẻ chú trong
         ngoặc. Hoá `flex` là ba mẩu ấy thành BA PHẦN TỬ CON XẾP NGANG: dòng chữ thôi tự xuống
         dòng, và khoảng trắng giữa các mẩu bị bỏ đi nên chữ dính vào nhau. ⇒ Chép khuôn của tệp
         khác vào đây là vỡ đúng cái nhãn mình đang cứu.
       🔑 Nhãn ⛔ KHÔNG cần khai `display` gì cả: nó là phần con của một hộp flex, mà phần con của
         hộp flex được đổi sang mức khối sẵn — đủ để `margin`, `padding` và `min-height` ăn.
       🔑 CẶP `margin-left` + `padding-left` 1,5em: nhãn bắt đầu SAU ô vuông (hộp có đệm trái
         1,5em, ô tick nằm ở 0…1em nhờ lề âm), nên khoảng NGAY TRÊN và NGAY DƯỚI ô vuông thuộc
         `div.form-check` — một thẻ `div` ⛔ KHÔNG bấm được. Kéo nhãn sang trái đúng 1,5em rồi trả
         lại bằng phần đệm cùng cỡ: nhãn PHỦ TRỌN cột ô tick mà dòng chữ vẫn đứng nguyên chỗ cũ.
       ⚠ Hai con số 1,5em là hằng số của Bootstrap (`.form-check { padding-left: 1.5em }` và
         `.form-check .form-check-input { margin-left: -1.5em }`) — ⛔ đừng đoán, nâng đời
         Bootstrap thì soi lại đúng hai luật ấy.
       ⚠ `align-self: stretch` là dòng ⛔ KHÔNG ĐƯỢC BỎ: hộp cha canh giữa các phần con, nên để
         nhãn tự co theo chiều cao con chữ thì vùng bấm thật hụt lại so với hộp 44px — đúng ô đột
         biến số 23 mà đợt 1619 đã bắt được. */
    .form-check-label[b-10r4egf7jm] {
        align-self: stretch;
        margin-left: -1.5em;
        padding-left: 1.5em;
    }
}
/* /Components/Pages/HoSo/TrangThongTinCaNhan.razor.rz.scp.css */
/* ═══ TRANG THÔNG TIN CÁ NHÂN — việc o978 ═══

   ⚠ CSS CÁCH LY: bộ biên dịch của Blazor gắn dấu scope vào từng bộ chọn, và gói scoped nạp SAU
     app.css nên nó THẮNG ở cùng độ đặc hiệu. Vì thế mọi thứ ở đây chỉ nói về trang này, ⛔ không một
     bộ chọn nào chạm tới lớp dùng chung của app.

   ⚠ Màu lấy từ token --bs-* của Bootstrap 5.3, ⛔ đừng đóng đinh mã màu: app có hai giao diện
     (sáng/tối) cộng nhiều chủ đề, đóng đinh là chữ chìm vào nền ở đúng những chủ đề ⛔ không ai thử.

   ⛔ ĐỪNG viết câu media có "and" ở tệp này: bộ biên dịch CSS cách ly từng gắn dấu scope nhầm chỗ với
     câu ghép (đợt 1481). Lượt đo lại 29/08 (đợt 1500) ⛔ không tái hiện được, nhưng một điều kiện thì
     luôn đúng, nên ⛔ không có lý do đi mạo hiểm ở một trang mới.  */

/* Lưới thẻ tự xuống dòng theo bề ngang — ⛔ không đóng đinh số cột: trang này mở trên cả màn 1920 lẫn
   điện thoại 390, mà số khối thì đổi theo từng người (ai không có hồ sơ nhân sự thì thiếu hẳn năm
   khối). */
.tcn-luoi[b-4oase97ppv] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 1rem;
    align-items: start;
}

/* ⭐ ĐỢT 1967 (việc o1024) — NHÓM KHỐI.
   Trang lên 20 khối; một lưới thẻ phẳng 20 ô là bức tường, nên khối xếp theo nhóm và mỗi nhóm có một
   dòng tiêu đề để mắt bám vào.
   ⚠ Khoảng cách trên LỚN hơn khoảng cách dưới (2rem/.6rem) — đó là phép "gần nhau thì thuộc về nhau":
     tiêu đề phải dính vào lưới NGAY DƯỚI nó, ⛔ không đứng lửng giữa hai nhóm. Cho hai khoảng bằng
     nhau là người đọc ⛔ không biết tiêu đề nói về lưới trên hay lưới dưới. */
.tcn-nhom + .tcn-nhom[b-4oase97ppv],
.tcn-luoi + .tcn-nhom[b-4oase97ppv] {
    margin-top: 2rem;
}

.tcn-ten-nhom[b-4oase97ppv] {
    font-size: .95rem;
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
    color: var(--bs-secondary-color, #6c757d);
    margin: 0 0 .6rem;
}

.tcn-mo-dau[b-4oase97ppv] {
    max-width: 72ch;
}

.tcn-dau[b-4oase97ppv] {
    max-width: 900px;
}

.tcn-khoi[b-4oase97ppv] {
    height: 100%;
}

.tcn-tieu-de[b-4oase97ppv] {
    font-size: 1rem;
    font-weight: 600;
    margin: 0 0 .6rem;
    padding-bottom: .4rem;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
}

/* Bảng "nhãn — giá trị": hai cột, nhãn co theo chữ dài nhất NHƯNG có TRẦN.
   ⚠ `fit-content(45%)` chứ ⛔ không phải `max-content` trần: nhãn dài nhất của mỗi khối một khác, và
     khối 🛡 Bảo hiểm có nhãn "Nơi khám chữa bệnh ban đầu" — để `max-content` là cột nhãn nuốt gần hết
     bề ngang thẻ, còn cột giá trị bị bóp tới mức câu "— chưa khai —" vỡ làm ba dòng (đo sống 01/09).
   ⚠ Cũng ⛔ không gõ một con số rem cố định: khối này thừa chỗ thì khối kia vỡ dòng. */
.tcn-bang[b-4oase97ppv] {
    display: grid;
    grid-template-columns: fit-content(45%) 1fr;
    column-gap: .9rem;
    row-gap: .4rem;
    margin: 0;
    font-size: .92rem;
}

.tcn-bang dt[b-4oase97ppv] {
    font-weight: 500;
    color: var(--bs-secondary-color, #6c757d);
}

.tcn-bang dd[b-4oase97ppv] {
    margin: 0;
    overflow-wrap: anywhere;
}

/* ⭐ ĐỢT 2003 — nút "Chi tiết" (anh Tiến 05/09/2026: *"đổi các đường link như thế này thành nút chi
   tiết"*). Chữ + màu + đệm + bo góc nay do `.btn .btn-sm .btn-outline-primary` của Bootstrap lo; ở đây
   CHỈ còn khoảng cách với bảng số phía trên.
   🔴 ⛔ ĐỪNG khai lại `display` / `font-size` / `text-decoration` ở tệp này: CSS cách ly biên dịch ra
     `.tcn-di-tiep[b-…]` (0,2,0) và gói scoped nạp SAU `app.css` ⇒ mỗi thuộc tính viết ở đây là một
     thuộc tính GIÀNH ĐƯỢC khỏi `.btn`, kể cả những thứ mình ⛔ không định đụng. Ca cụ thể: `app.css`
     đặt `.btn { display: inline-flex; min-height: var(--cham-toi-thieu) }` trong `@media (pointer:
     coarse)` — viết `display: inline-block` ở đây là **nuốt luôn** vùng chạm 44px của điện thoại
     (luật đợt 1518), mà build xanh và màn hình máy tính ⛔ không đổi một pixel. */
.tcn-di-tiep[b-4oase97ppv] {
    margin-top: .7rem;
}

/* Khổ hẹp: nhãn xuống dòng riêng thay vì ép hai cột vào 320px.
   ⚠ Một điều kiện duy nhất — xem lời dặn ở đầu tệp. */
@media (max-width: 480px) {
    .tcn-bang[b-4oase97ppv] {
        grid-template-columns: 1fr;
        row-gap: .15rem;
    }

    .tcn-bang dt[b-4oase97ppv] {
        margin-top: .35rem;
    }
}
/* /Components/Pages/KeHoach/TrangKeHoachDauTu.razor.rz.scp.css */
/* ═══ VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (đợt 1605, việc o737) ═══
   ô tick TRẦN “đang dùng” của bảng nhóm hạng mục.

   🔑 Nới VÙNG CHẠM QUANH ô tick, ⛔ KHÔNG phóng to chính ô vuông: ô tick 44px đứng cạnh dòng chữ
     15px trông như lỗi phần mềm, và nó cũng ⛔ không giải đúng bài — thứ ngón tay bấm trượt là
     khoảng trống quanh ô. Thẻ bọc `label.o-tick-cham` khai ngay trong `.razor`.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là ngón
     tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ
     quyết định vùng bấm là CÁI CHẠM VÀO, ⛔ không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, ⛔ không một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css`: bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi: node cong-cu/soi-o-tick-tran.mjs · node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ⚠ Cần CẢ `min-width`: ô tick chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt NGANG.
       ⚠ `inline-flex` chứ ⛔ không `flex`: thẻ bọc nằm trong một ô bảng (hoặc một hàng `d-flex`)
         cạnh nút và ô gõ khác — hoá `flex` là nó chiếm trọn một dòng, hàng đang gọn bỗng vỡ ra.
       ⚠ `min-height` chứ ⛔ không `height`: hàng nào cao hơn thì cứ để cao — đặt `height` là bóp
         chúng lại, sửa một lỗi rồi đẻ một lỗi. */
    .o-tick-cham[b-t2v9sy6fbp] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}

/* ═══ KHỔ HẸP: BẢNG NHÓM HẠNG MỤC HOÁ THẺ MỖI DÒNG MỘT KHỐI (đợt 1989, việc o1260) ═══

   ⛔ ĐÂY ⛔ KHÔNG PHẢI KHUÔN MỚI — dùng lại NGUYÊN khuôn đợt 1701 (bảng tài khoản,
   `Components/Pages/QuanTri/NguoiDung/BangTaiKhoan.razor.css`) và đợt 1972 (danh mục thiết bị mạng,
   `Components/Pages/Mang/TrangQuanTriMang.razor.css`), cùng nhịp với hai bảng xếp loại
   (`TrangDanhGiaQuy.razor.css` · `TrangDanhGiaThang.razor.css`): cùng ngưỡng, cùng lối `data-nhan`,
   cùng luật thuần `TKNB.Tests/LuatBangTheKhoHep.cs`, cùng bộ đo sống.

   📏 SỐ ĐO SINH RA VIỆC NÀY — đo sống 04/09/2026, Chrome thật, khung 390×844 cảm ứng, đăng nhập
     `tienvm`, chốt `d-flex` ra `flex`:
     `node cong-cu/do-song-khung-hep.mjs --trang /ke-hoach-dau-tu`
     · vùng bấm nhỏ hơn ngưỡng 44px: **15 chỗ**, tất cả nằm trong bảng ⚙ «Nhóm hạng mục của biểu mẫu»
       (`input.form-control.form-control-sm` của ba cột Mã · Tên nhóm · Thứ tự);
     · từng ô đo ra **41,1px bề ngang** × 53px chiều cao — THỪA chiều cao, HỤT bề ngang đúng 3px.
   🔴 ⛔ ĐỪNG VÁ BẰNG `min-width` CHO Ô NHẬP dù chỉ hụt 3px: sáu cột khoá cứng của bảng này cộng lại
     90+160+90+90+80 = 510px cho một khung nhìn 390px, nên nong ô nào cũng chỉ đẩy chỗ chật sang cột
     bên cạnh. Bệnh gốc là BẢNG, ⛔ không phải Ô NHẬP.
     ⓘ Cùng kết luận đã ghi ở việc `o962` (ô tick) và `o1260` (⛔ đừng thêm `min-width` vào luật dùng
       chung ở `app.css` — quy tắc #18).

   🔑 MỌI BỘ CHỌN PHẢI NEO VÀO LỚP `.bang-the-kho-hep` — ở tệp này đó là điều BẮT BUỘC chứ ⛔ không
     phải cẩn thận thừa: tệp `.razor` có HAI thẻ bảng (bảng đăng ký kế hoạch ở trên · bảng nhóm hạng
     mục ở đây), mà CSS cách ly áp theo CẢ COMPONENT. Viết `thead { display: none }` trần là giấu đầu
     của cả hai — hỏng một thứ ⛔ không ai nhờ sửa, mà build vẫn xanh.
     ⚠ Bảng đăng ký kế hoạch CỐ Ý đứng ngoài đợt này: nó ⛔ không có ô bấm nào dưới ngưỡng, và hoá thẻ
       một bảng 7 cột toàn chữ là việc phải ĐO TRƯỚC rồi mới làm. Đã ghi thành việc riêng.

   🔑 NGƯỠNG 820px LÀ NGƯỠNG CỦA ĐỢT 400 Ở `app.css`, ⛔ ĐỪNG ĐẶT MỘT CON SỐ KHÁC — hàng rào
     `Nguong_cua_khoi_the_phai_trung_nguong_app_css` đọc ngưỡng từ chính `app.css`.
   🔴 ĐIỀU KIỆN LÀ `max-width`, ⛔ KHÔNG PHẢI `pointer: coarse` — khác hẳn khối `o-tick-cham` ở đầu tệp
     này, ⛔ đừng chép nhầm khuôn của nhau: khối trên hỏi «cái chạm vào là ngón tay hay chuột» vì bệnh
     của nó là NGÓN TAY TO; khối này hỏi «còn bao nhiêu chỗ mà bày» vì bệnh của nó là BỀ NGANG.
   ⚠ PHẢI CÓ CHỮ `screen`: bản IN đứng ngoài (khối đợt 400 cũng gate `screen`).
     ⓘ Dạng `screen and (…)` là ngoại lệ CÓ LÝ DO của lời dặn "một điều kiện mỗi câu media" (đợt 1481):
     đợt 1500 đã ĐO SỐNG và bác bỏ điều cấm ấy cho đúng dạng này. Chốt ngược: đọc
     `obj/Debug/net10.0/scopedcss/bundle/TKNB.Web.styles.css`.

   ⚠ NHÃN CỘT LẤY TỪ `data-nhan` KHAI Ở CHÍNH THẺ `td` TRONG `.razor`, ⛔ không chép tay vào CSS.
   ⚠ Chỉ `td[data-nhan]` mới mọc nhãn: ô ⛔ không khai thì `attr()` trả chuỗi rỗng nhưng hộp `::before`
     VẪN chiếm chỗ ⇒ thụt lề vô cớ. Cả 6 ô của bảng này đều đã khai, hàng rào canh chuyện ấy.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — cặp ký tự ấy lọt vào giữa câu văn là chú thích
     đóng sớm và trình duyệt nuốt NGUYÊN luật ngay dưới, ⛔ không một câu lỗi nào. Vì thế gọi tên nó
     bằng CHỮ. Soi: node cong-cu/soi-chu-thich-css.mjs
   🚧 Hàng rào: TKNB.Tests/BangTheKhoHepNhomHangMucTests.cs
   📏 Đo sống: node cong-cu/do-song-khung-hep.mjs --trang /ke-hoach-dau-tu   (chốt ngược: --chuot) */

@media screen and (max-width: 820px) {

    /* ① ĐẦU BẢNG BIẾN MẤT — tên cột nay đi theo từng ô (xem ④). */
    .bang-the-kho-hep thead[b-t2v9sy6fbp] { display: none; }

    .bang-the-kho-hep tbody[b-t2v9sy6fbp] { display: block; }

    /* ② MỖI DÒNG LÀ MỘT THẺ.
       ⚠ `background` đọc BIẾN `--bs-table-bg` chứ ⛔ không tô màu cứng: nhóm ĐÃ NGƯNG DÙNG mang lớp
         `table-secondary`, lớp ấy đổi đúng biến này. Tô cứng là nhóm ngưng dùng trông y hệt nhóm đang
         dùng — mất đúng dấu hiệu người quản trị cần thấy. */
    .bang-the-kho-hep tbody tr[b-t2v9sy6fbp] {
        display: block;
        margin-bottom: .75rem;
        padding: .5rem .75rem;
        border: 1px solid var(--bs-border-color);
        border-radius: .5rem;
        background: var(--bs-table-bg, transparent);
    }

    /* ③ MỖI Ô LÀ MỘT DÒNG «nhãn — giá trị».
       ⚠ `align-items: baseline` chứ ⛔ không `center`: ô «Thuộc nhóm cha» chứa một ô chọn cao hơn hẳn
         con chữ của nhãn; canh giữa là nhãn trôi xuống lưng chừng ô chọn. */
    .bang-the-kho-hep tbody td[b-t2v9sy6fbp] {
        display: flex;
        gap: .5rem;
        align-items: baseline;
        flex-wrap: wrap;
        width: auto;
        padding: .25rem 0;
        border: 0;
    }

    /* ④ NHÃN CỘT — lấy nguyên văn từ `data-nhan` của chính ô ấy.
       ⚠ `flex: 0 0 8.5em` cho mọi nhãn cùng một bề ngang ⇒ cột giá trị thẳng hàng nhau. */
    .bang-the-kho-hep tbody td[data-nhan][b-t2v9sy6fbp]::before {
        content: attr(data-nhan);
        flex: 0 0 8.5em;
        color: var(--bs-secondary-color);
        font-size: .85em;
    }

    /* ⑤ Ô GÕ VÀ Ô CHỌN — thứ cả việc o1260 đi chữa. Ở khổ thẻ chúng là phần con của một hộp flex đã
       xuống dòng nên nhận TRỌN bề ngang còn lại của thẻ.
       ⚠ ⛔ Đừng gõ số 44px ở đây: ngưỡng vùng chạm nằm ở biến `--cham-toi-thieu` của `app.css`, và ở
         khổ này ô đã rộng cả trăm pixel nên ⛔ không cần ai ép sàn nữa. */
    .bang-the-kho-hep tbody td .form-control[b-t2v9sy6fbp],
    .bang-the-kho-hep tbody td .form-select[b-t2v9sy6fbp] { flex: 1 1 100%; }

    /* ⑥ Ô CĂN LỀ THEO BẢNG — `text-end` và `text-center` hợp với bảng nhưng ⛔ không hợp với thẻ: mọi
       giá trị phải bắt đầu thẳng một cột thì mắt mới dò được. */
    .bang-the-kho-hep tbody td.text-end[b-t2v9sy6fbp],
    .bang-the-kho-hep tbody td.text-center[b-t2v9sy6fbp] { text-align: start; }
}
/* /Components/Pages/Mang/TrangQuanTriMang.razor.rz.scp.css */
/* ═══ KHỔ HẸP: BẢNG DANH MỤC THIẾT BỊ HOÁ THẺ MỖI DÒNG MỘT KHỐI (đợt 1972, việc o1235) ═══

   ⛔ ĐÂY ⛔ KHÔNG PHẢI KHUÔN MỚI — nó dùng lại NGUYÊN khuôn đợt 1701 (việc o986) đã dựng cho bảng
   tài khoản ở `Components/Pages/QuanTri/NguoiDung/BangTaiKhoan.razor.css`: cùng ngưỡng, cùng lối
   `data-nhan`, cùng luật thuần `TKNB.Tests/LuatBangTheKhoHep.cs`, cùng bộ đo sống. Sửa một bên thì
   đọc bên kia trước; ⛔ đừng để hai bản trôi xa nhau.

   📏 SỐ ĐO SINH RA VIỆC NÀY — đo sống 04/09/2026, Chrome thật, khung 375×812 cảm ứng, đăng nhập
     `tienvm`, chốt `d-flex` ra `flex`:
     `node cong-cu/do-song-bang-hep.mjs --trang '/quan-tri-mang?tab=danh-muc' --rong 375 --cao 812`
     · bảng 68 dòng · 8 cột có chữ · bề ngang hộp 317px · NỘI DUNG 628px;
     · khung cuộn thật là CHÍNH THẺ `table` (luật đợt 400 ở app.css) ⇒ PHẢI VUỐT NGANG 311px;
     · 4 cột ⛔ KHÔNG đọc được nếu ⛔ không kéo ngang: «Hãng» 417px · «Vị trí» 465px ·
       «Kiểm gần nhất» 513px · «Sửa» 561px;
     · 1 cột bị CẮT ĐÔI ở mép phải: «Loại» đoạn 366…417px.
   🔴 CỘT CẮT ĐÔI LÀ CA TỆ NHẤT, ⛔ không phải cột khuất hẳn: cột khuất còn có mép bảng nói cho biết
     "bên phải còn nữa", còn một cột hiện ra ĐÚNG MỘT NỬA thì trông như PHẦN MỀM VẼ HỎNG — người dùng
     ⛔ không nghĩ tới chuyện vuốt ngang để tìm nốt phần còn lại, vì họ ⛔ không biết là còn.

   🔒 CHỐT NGƯỢC ĐÃ ĐO CÙNG NGÀY, CÙNG BẢNG, CÙNG BỘ ĐO — máy tính ⛔ KHÔNG có bệnh này:
       768×900 → nội dung 695px · thừa 0px · 0 cột khuất · 0 cột cắt
       900×900 → nội dung 803px · thừa 0px · 0 cột khuất · 0 cột cắt
     ⇒ Bệnh chỉ có ở khổ điện thoại, nên bản vá cũng chỉ được vẽ ra ở khổ điện thoại.

   🔑 NGƯỠNG 820px LÀ NGƯỠNG CỦA ĐỢT 400 Ở `app.css`, ⛔ ĐỪNG ĐẶT MỘT CON SỐ KHÁC. Ở đúng mốc ấy luật
     dùng chung biến MỌI bảng thành khung cuộn ngang; khối này nhận việc ngay tại đó và thay bằng thẻ.
     Đặt lệch là chừa ra một dải khổ mà bảng VỪA là thẻ VỪA là khung cuộn.
     ⓘ Đo được ở 768px là bảng đã vừa khung (695px, thừa 0px) nên nghĩ tới mốc 767.98px là phản xạ tự
       nhiên — ĐÃ CÂN NHẮC VÀ BỎ: con số 695px ấy là số của DỮ LIỆU HÔM NAY (68 thiết bị, tên và vị
       trí dài chừng ấy). Thêm vài thiết bị tên dài là nó vượt, mà ⛔ không ai đo lại. Ngưỡng phải nằm
       ở ĐÚNG MỘT chỗ và nó đã nằm ở `app.css`; hàng rào `Nguong_cua_khoi_the_phai_trung_nguong_app_css`
       đọc từ nguồn ấy nên gõ số khác vào đây là ĐỎ ngay, ⛔ không phải hỏng lặng lẽ.

   🔴 ĐIỀU KIỆN LÀ `max-width`, ⛔ KHÔNG PHẢI `pointer: coarse` — bệnh ở đây là BỀ NGANG (hết chỗ mà
     bày), ⛔ không phải NGÓN TAY. Máy tính thu cửa sổ còn 380px thì cũng cụt đúng bốn cột ấy dù đang
     dùng chuột; ngược lại máy tính bảng 1024px là ngón tay mà bảng bày vừa đủ, hoá thẻ chỉ tổ phí chỗ.
   ⚠ PHẢI CÓ CHỮ `screen`: bản IN đứng ngoài (khối đợt 400 cũng gate `screen`). In một tờ danh mục
     thiết bị mà đầu bảng bị giấu và mỗi ô mọc thêm một nhãn là hỏng bản in.
     ⓘ Dạng `screen and (…)` là ngoại lệ CÓ LÝ DO của lời dặn "một điều kiện mỗi câu media" (đợt 1481):
     đợt 1500 đã ĐO SỐNG và bác bỏ điều cấm ấy cho đúng dạng này — dấu scope vẫn nằm đúng bộ chọn bên
     trong. Chốt ngược: đọc `obj/Debug/net10.0/scopedcss/bundle/TKNB.Web.styles.css`.

   🔑 VÌ SAO MỌI BỘ CHỌN ĐỀU NEO VÀO LỚP `.bang-the-kho-hep`, trong khi bên bảng tài khoản viết TRẦN:
     tệp `.razor` này có SÁU thẻ `<table>` (hộp chi tiết thiết bị · tab quyền · nhật ký…), mà CSS cách
     ly áp theo COMPONENT chứ ⛔ không theo từng thẻ. Viết `thead { display: none }` trần ở đây là giấu
     đầu của cả sáu bảng — hỏng năm thứ ⛔ không ai nhờ sửa, mà build vẫn xanh. Bảng tài khoản chỉ có
     ĐÚNG MỘT bảng trong tệp nên bên ấy viết trần được; ⛔ đừng chép ngược lối viết ấy sang đây.

   🔒 ⛔ KHÔNG ĐI ĐÈ `display:block; overflow-x:auto` MÀ `app.css` ĐẶT CHO THẺ `table`: bộ chọn bên ấy
     là `.content table.table:not(.bang-dinh-dau)` = (0,3,1), còn luật cách ly ở đây biên dịch ra
     `.bang-the-kho-hep[b-xxx]` = (0,2,0) ⇒ có viết cũng thua. ⛔ Đừng nống độ đặc hiệu để thắng nó:
     khi các dòng đã xếp DỌC thì ⛔ không còn gì tràn ngang để mà cuộn, `overflow-x: auto` nằm đó
     ⛔ không vẽ ra một thanh cuộn nào — và nó là lưới an toàn cho ngày một ô mang chuỗi liền quá dài.

   ⚠ NHÃN CỘT LẤY TỪ `data-nhan` KHAI Ở CHÍNH THẺ `td` TRONG `.razor`, ⛔ không chép tay vào CSS:
     chép tay (kiểu `td:nth-child(n)::before`) là ngày ai chèn một cột vào giữa thì mọi nhãn lệch đi
     một ô — hỏng LẶNG LẼ, build xanh test xanh, và màn hình dán tên cột này lên giá trị cột kia.
   ⚠ Chỉ `td[data-nhan]` mới mọc nhãn: ô ⛔ không khai thì `attr()` trả chuỗi rỗng nhưng hộp `::before`
     VẪN chiếm chỗ ⇒ thụt lề vô cớ. Cả 8 ô của bảng này đều đã khai, hàng rào canh chuyện ấy.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — cặp ký tự ấy lọt vào giữa câu văn là chú
     thích đóng SỚM, phần chữ còn lại dính vào bộ chọn ngay dưới và trình duyệt bỏ nguyên luật đó,
     ⛔ không một câu lỗi nào. Vì thế ở đây gọi tên nó bằng CHỮ. Soi: node cong-cu/soi-chu-thich-css.mjs
   🚧 Hàng rào: TKNB.Tests/BangTheKhoHepDanhMucMangTests.cs · đo sống: cong-cu/do-song-bang-hep.mjs */

@media screen and (max-width: 820px) {

    /* ① ĐẦU BẢNG BIẾN MẤT — tên cột nay đi theo từng ô (xem ④), nên hàng tiêu đề chỉ còn là một dải
       chữ dính trên cùng ⛔ không ăn nhập với thứ bên dưới. */
    .bang-the-kho-hep thead[b-y2xv9m4r98] { display: none; }

    .bang-the-kho-hep tbody[b-y2xv9m4r98] { display: block; }

    /* ② MỖI DÒNG LÀ MỘT THẺ. Viền + bo góc để mắt tách được dòng này với dòng kia — mất hàng tiêu đề
       rồi thì đường kẻ ngang của bảng ⛔ không đủ nói "hết một thiết bị".
       ⚠ `background` đọc BIẾN `--bs-table-bg` chứ ⛔ không tô màu cứng, y như bên bảng tài khoản:
         tô cứng là ngày nào có ai tô màu một dòng theo trạng thái thì màu ấy chết ngay.
       ⓘ Dòng THÔI THEO DÕI mang lớp `opacity-50` đặt ở thẻ `tr` trong `.razor` — lớp ấy vẫn ăn
         nguyên vẹn ở đây, thẻ mờ đi cả khối đúng như dòng mờ đi cả hàng. */
    .bang-the-kho-hep tbody tr[b-y2xv9m4r98] {
        display: block;
        margin-bottom: .75rem;
        padding: .5rem .75rem;
        border: 1px solid var(--bs-border-color);
        border-radius: .5rem;
        background: var(--bs-table-bg, transparent);
    }

    /* ③ MỖI Ô LÀ MỘT DÒNG «nhãn — giá trị».
       ⚠ `align-items: baseline` chứ ⛔ không `center`: ô «Trạng thái» chứa huy hiệu và ô «Tên thiết
         bị» chứa một NÚT cao hơn con chữ; canh giữa là nhãn trôi xuống lưng chừng nút. */
    .bang-the-kho-hep tbody td[b-y2xv9m4r98] {
        display: flex;
        gap: .5rem;
        align-items: baseline;
        flex-wrap: wrap;
        width: auto;
        padding: .25rem 0;
        border: 0;
    }

    /* ④ NHÃN CỘT — lấy nguyên văn từ `data-nhan` của chính ô ấy.
       ⚠ `flex: 0 0 8.5em` cho mọi nhãn cùng một bề ngang ⇒ cột giá trị thẳng hàng nhau. Để nhãn tự
         co là mỗi dòng một chỗ bắt đầu, đọc còn mệt hơn bảng cũ. Con số 8.5em lấy ĐÚNG của đợt 1701
         để hai bảng của cùng một phần mềm ⛔ không thụt lề khác nhau. */
    .bang-the-kho-hep tbody td[data-nhan][b-y2xv9m4r98]::before {
        content: attr(data-nhan);
        flex: 0 0 8.5em;
        color: var(--bs-secondary-color);
        font-size: .85em;
    }

    /* ⑤ Ô THAO TÁC — lớp `text-end` của Bootstrap căn phải, hợp với bảng nhưng ⛔ không hợp với thẻ:
       hai nút phải bắt đầu thẳng cột với mọi giá trị khác, và được xuống dòng khi hết chỗ. */
    .bang-the-kho-hep tbody td.text-end[b-y2xv9m4r98] { text-align: start; }
}
/* /Components/Pages/MucIn/BangVatTuMayPhoto.razor.rz.scp.css */
/* ═══ BẢNG VẬT TƯ HỒ SƠ MÁY PHOTOCOPY (đợt 2051, việc o1340) ═══

   🔴 Ô CHỌN MÁY PHẢI CÓ TRẦN BỀ NGANG, ⛔ ĐỪNG ĐỂ NÓ TỰ CO THEO NỘI DUNG.
     Nhãn máy là chuỗi `MÃ — tên phòng` (LuatMayCuaHoSo.NhanMayChon) và tên phòng thật của Công ty
     dài tới «Phòng Bảo vệ - Phòng cháy chữa cháy». Thẻ `select` mặc định nong ra theo option DÀI
     NHẤT ⇒ ở khổ 390px cả bảng bị đẩy rộng ra, sinh CUỘN NGANG toàn trang — mà bảng thì đã nằm
     trong `.table-responsive` nên nhìn qua vẫn "trông có lý", chỉ người dùng điện thoại chịu.
   ⚠ Cần CẢ `min-width: 0`: `select` là flex/grid item ở một số bố cục, mà tấm chắn `min-width: auto`
     của flex ⛔ không cho nó co lại dưới bề rộng nội dung — thiếu dòng này thì `max-width` bên trên
     ⛔ không có tác dụng gì, và build vẫn xanh.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css` (bài học đợt 1481).
   📏 Soi: node cong-cu/soi-chu-thich-css.mjs */

.bvt-o-chon[b-9bzdj1gzz2] {
    /* ⭐ ĐỢT 2693 (việc o2170) — TRẦN THẬT, ⛔ PHẢI `max-width: 100%`.
       `100%` là 100% của Ô CHỨA, mà ô chứa của một bảng `auto` lại nở ra theo chính thẻ `select`
       ⇒ họ đuổi nhau và ⛔ ai chặn ai: nhãn «MÃ — Phòng Bảo vệ - Phòng cháy chữa cháy» vẫn nong cột.
       Trần phải là một độ dài THẬT. */
    max-width: 16rem;
    min-width: 0;
    /* ⚠ Ba dòng này đi LIỀN NHAU mới cắt được chữ: thiếu `overflow: hidden` thì `text-overflow`
       ⛔ có tác dụng gì cả, và build vẫn xanh. */
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* Khổ điện thoại: 16rem đã là gần nửa khung nhìn 390px ⇒ siết thêm một nấc. */
@media (max-width: 575.98px) {
    .bvt-o-chon[b-9bzdj1gzz2] {
        max-width: 10rem;
    }
}
/* /Components/Pages/MucIn/KhoiDanhSachHoSoMayPhoto.razor.rz.scp.css */
/* ═══ DÒNG HAI HÀNG CỦA DANH SÁCH HỒ SƠ MÁY PHOTOCOPY (đợt 2051, việc o1340) ═══

   ⚠ Thanh tiến độ CỐ Ý MỎNG: nó là dấu hiệu phụ đọc bằng liếc mắt, ⛔ không phải nội dung chính
     của dòng. Để nguyên chiều cao mặc định của Bootstrap (1rem) thì mỗi dòng cao thêm đáng kể,
     và danh sách 10 dòng phải cuộn thêm một màn hình.

   👆 VÙNG CHẠM: cả dòng là một thẻ `a` nên nó VỐN ĐÃ cao hơn 44px (hai hàng chữ + thanh tiến độ);
     ⛔ không khai thêm `min-height` ở đây — thừa, và `@media (pointer: coarse)` chỉ nên mọc ra khi
     có SỐ ĐO nói rằng thiếu (bài học đợt 1518: bộ đo bản đầu suýt cử người đi vá 115 chỗ không hỏng).

   📏 Soi: node cong-cu/soi-chu-thich-css.mjs */

.khs-tien-do[b-36rhdkje4p] {
    height: 0.35rem;
}

/* Nội dung dài ⛔ không được đẩy dòng rộng ra sinh cuộn ngang ở khổ 390px.

   ⭐ ĐỢT 2693 (việc o2170) — `break-word` CHỨ ⛔ PHẢI `anywhere`.
     Hai giá trị cùng cắt được một TỪ quá dài, nhưng `anywhere` còn bảo trình duyệt tính cả chỗ cắt
     giữa từ vào bề rộng NHỏ NHẤT của dòng ⇒ ở khổ hẹp nó cắt giữa một tiếng tiếng Việt
     («Phòng cháy chữa chá» / «y») dù dòng còn chỗ xuống hàng bình thường. Tên hồ sơ ở đây là câu
     tiếng Việt có dấu cách, ⛔ phải một chuỗi liền ⇒ `break-word` đủ chặn cuộn ngang mà ⛔ cắt giữa tiếng. */
.khs-dong[b-36rhdkje4p] {
    overflow-wrap: break-word;
}
/* /Components/Pages/MucIn/KhoiDanhSachHoSoThanhToanMuc.razor.rz.scp.css */
/* ═══ DÒNG CỦA DANH SÁCH HỒ SƠ THANH TOÁN MỰC (đợt 2703, việc o2198) ═══

   ⚠ Thanh tiến độ CỐ Ý MỎNG — nó là dấu hiệu phụ đọc bằng liếc mắt, ⛔ không phải nội dung chính của
     dòng. Để nguyên chiều cao mặc định của Bootstrap (1rem) thì danh sách 10 dòng phải cuộn thêm gần
     một màn hình. Cùng con số với khối của app 📠 để hai app ⛔ lệch nhau một nấc.

   👆 VÙNG CHẠM: cả dòng là một thẻ `a` nên nó VỐN ĐÃ cao hơn 44px (hai hàng chữ + thanh tiến độ);
     ⛔ không khai thêm `min-height` ở đây.

   📏 Soi: node cong-cu/soi-chu-thich-css.mjs */

.khtt-tien-do[b-y3wut1kylr] {
    height: 0.35rem;
}

/* Nội dung dài ⛔ không được đẩy dòng rộng ra sinh cuộn ngang ở khổ 390px.

   ⚠ `break-word` CHỨ ⛔ PHẢI `anywhere` (bài học đợt 2693): `anywhere` tính cả chỗ cắt giữa từ vào bề
     rộng nhỏ nhất của dòng ⇒ ở khổ hẹp nó cắt giữa một tiếng tiếng Việt dù dòng còn chỗ xuống hàng
     bình thường. Tên hồ sơ ở đây là câu tiếng Việt có dấu cách, ⛔ phải một chuỗi liền. */
.khtt-dong[b-y3wut1kylr] {
    overflow-wrap: break-word;
}
/* /Components/Pages/MucIn/KhoiLapHoSoMayPhoto.razor.rz.scp.css */
/* ⭐ ĐỢT 2683 (việc o2187) — khung danh sách ô tích máy của hộp «Lập hồ sơ máy photocopy mới».
   Danh mục máy có vài chục dòng ⇒ để trôi tự do thì hộp thoại dài quá màn hình và hai nút ở chân hộp
   bị đẩy khuất. Cuộn RIÊNG trong khung này, ⛔ cuộn cả hộp.
   ⚠ Chú thích CSS phải đóng ĐÚNG MỘT LẦN — dấu đóng lạc giữa tệp làm phần sau chết lặng (quy tắc #5). */
.klh-khung-may[b-rub0stm8x6] {
    max-height: 14rem;
    overflow-y: auto;
}
/* /Components/Pages/MucIn/KhoiLichSuMuc.razor.rz.scp.css */
/* ═══ ĐỢT 2703 (việc o2179) — VÙNG CHẠM NGÓN TAY CHO Ô LỌC «CHƯA VÀO ĐỢT THANH TOÁN» ═══

   📏 ĐO SỐNG 17/09/2026 trên bản HEAD db4defe6 (Chrome thật, khung 390×844 cảm ứng, app riêng cổng
     5295, node cong-cu/do-song-o-tick-cham.mjs --trang /muc-in/so-xuat):
       ô vuông 16×16 thả nổi trái · nhãn cao 21 · hộp `.form-check` cao 24 ở chế độ khối
       VÙNG BẤM THẬT: cột ô tick 17px · cột chữ 22px — ngưỡng ngón tay là 44px.

   🔑 ĐÂY LÀ KIỂU ③ — ô tick và nhãn là HAI ANH EM trong `div.form-check`, nối nhau bằng thuộc tính
     `for`. Thẻ bọc là một `div`, mà `div` thì ⛔ KHÔNG bấm được: nâng chiều cao thẻ bọc thôi là ĐÈN
     XANH GIẢ — bộ đo nào đọc hộp cha sẽ báo «đã giải» trong khi ngón tay đặt cạnh ô vuông ⛔ không
     thấy gì xảy ra. Khuôn đúng đã trả giá ở đợt 1555 · 1619 · 1627, bản mẫu là
     `TrangBangGiaMucXdcb.razor.css` cùng thư mục — đọc tệp ấy trước khi sửa tệp này.

   🔑 VÌ SAO ĐẶT Ở TỆP CÁCH LY, ⛔ KHÔNG PHẢI `app.css`: `app.css` dùng chung cho mọi màn hình của
     433 tài khoản, phần lớn ngồi chuột — nới `.form-check-label` từ bên đó là đổi bố cục cả app để
     giải bài của một khối, đúng loại rủi ro lan truyền quy tắc #18 dặn phải tránh. CSS cách ly biên
     dịch ra `.form-check-label[b-xxx]` (0,2,0) nên nó thắng bộ chọn cùng tên ở `app.css` (0,1,0), và
     chỉ chạm đúng phần tử viết trong `KhoiLichSuMuc.razor`.
   ⚠ Khối này ⛔ KHÔNG dùng được cho `.form-check-inline`: thẻ ở đây mang cả hai lớp, mà
     `.form-check-inline` chỉ đặt `display: inline-block` (0,1,0) nên luật `display` dưới đây thắng.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ có nghĩa là ngón tay
     (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ vẽ ra một pixel nào — mọi khai báo đều nằm trong câu
     media. Đo được bằng `--chuot`, bảng trước và sau phải TRÙNG KHÍT.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; ⛔ đừng gõ số 44px vào tệp này.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng ghép bằng chữ `and` trong tệp `.razor.css` (đợt 1481).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo; cặp ký tự ấy lọt vào GIỮA câu văn là chú
     thích đóng sớm và trình duyệt nuốt nguyên luật ngay dưới mà ⛔ một câu lỗi nào.
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Đo lại: node cong-cu/do-song-o-tick-cham.mjs --trang /muc-in/so-xuat */

@media (pointer: coarse) {

    /* ① HỘP BỌC Ô TICK — nâng sàn chiều cao và đổi sang hộp flex để ô vuông thôi dính mép trên:
       trong hộp flex thuộc tính `float` bị BỎ QUA nên ô vuông nhận phép canh giữa dọc.
       ⚠ `inline-flex` chứ ⛔ `flex`: hộp này nằm trong hàng `d-flex flex-wrap gap-2` xếp ngang cùng
         hai ô chọn của thanh công cụ; hoá `flex` là nó chiếm trọn một dòng.
       ⚠ `min-height` chứ ⛔ `height`: nhãn có lúc phải xuống hai dòng, lúc ấy cứ để nó cao hơn.
       ⚠ ⛔ đụng `padding-left` của Bootstrap — chính phần đệm 1,5em ấy giữ nguyên bề ngang cũ. */
    .form-check[b-7qmo41yc1v] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    /* ② NHÃN CHỮ — vùng bấm THẬT (bấm vào nhãn là đổi tick nhờ thuộc tính `for`).
       `align-self: stretch` là dòng ⛔ ĐƯỢC BỎ: hộp cha canh giữa các phần con, để nhãn tự co theo
       con chữ thì vùng bấm chỉ còn ~21px trong khi hộp đã 44px — đúng kiểu đèn xanh giả đợt 1563.
       🔴 Cặp `margin-left` + `padding-left` cùng cỡ là nửa thứ HAI, và là dòng dễ bị gỡ nhất: nhãn
         bắt đầu SAU ô vuông nên khoảng ngay trên và ngay dưới ô vuông thuộc `div.form-check` — một
         thẻ ⛔ bấm được. Kéo nhãn sang trái đúng 1,5em rồi trả lại bằng phần đệm cùng cỡ thì nhãn
         PHỦ TRỌN cột ô tick, còn dòng chữ vẫn đứng nguyên chỗ cũ ở 1,5em.
       ⚠ 1,5em là hằng số Bootstrap (`.form-check{padding-left:1.5em}` và
         `.form-check .form-check-input{margin-left:-1.5em}`) — nâng đời Bootstrap thì soi lại đúng
         hai luật ấy, ⛔ đừng đoán. */
    .form-check-label[b-7qmo41yc1v] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -1.5em;
        padding-left: 1.5em;
    }
}
/* /Components/Pages/MucIn/KhoiMayCuaHoSo.razor.rz.scp.css */
/* ═══ KHỐI MÁY GẮN VỚI HỒ SƠ MÁY PHOTOCOPY (đợt 2051, việc o1340) ═══

   🔴 Ô CHỌN «GẮN THÊM MÁY» PHẢI CÓ TRẦN BỀ NGANG.
     Option của nó là chuỗi `MÃ — tên phòng`, mà tên phòng thật dài tới «Phòng Bảo vệ - Phòng cháy
     chữa cháy». Thẻ `select` mặc định nong theo option DÀI NHẤT ⇒ ở khổ 390px nó đẩy cả hàng tiêu
     đề rộng ra và sinh CUỘN NGANG toàn trang. Ô này nằm ngoài `.table-responsive` nên ⛔ không có
     lưới nào đỡ.
   ⚠ Cần CẢ `min-width: 0`: ô nằm trong một hàng `d-flex`, mà tấm chắn `min-width: auto` của flex
     ⛔ không cho nó co dưới bề rộng nội dung — thiếu dòng này thì `max-width` ⛔ không có tác dụng,
     và build vẫn xanh.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css` (bài học đợt 1481).
   📏 Soi: node cong-cu/soi-chu-thich-css.mjs */

.kmh-o-chon[b-2gqwtgq1j6] {
    max-width: 18rem;
    min-width: 0;
}
/* /Components/Pages/MucIn/KhoiNhapPhieuMuc.razor.rz.scp.css */
/* ═══════════ ✍ HỘP «GHI NHẬN MỰC» — VÙNG CUỘN RIÊNG CỦA CÁC DÒNG MỰC ═══════════

   🔴 Anh Tiến 16/09/2026, kèm ảnh chụp: *"khung điền quá lớn và quá thừa, không thêm được nhiều
     dòng mực"*. Đo sống trước khi sửa: hộp cao 844px, nội dung thật 140px ⇒ 560px TRỐNG.

   🔑 VÌ SAO CẦN ĐÚNG MỘT LUẬT Ở ĐÂY, VÀ VÌ SAO NÓ ĐI THÀNH CẶP VỚI `CuonTrongThan="false"`:
     `app.css` có luật CHUNG «mọi hộp thoại cao cố định theo cỡ» — nó khóa `.modal-content` ở
     `height: 100%` cho cỡ Rong, tức KÍN MÀN TRỪ LỀ, bất kể trong hộp có gì. Đó chính là mảng trắng
     anh thấy. Luật chung ấy CHỈ ÁP khi khung mang lớp `modal-dialog-scrollable`, tức khi tham số
     `CuonTrongThan` còn BẬT; tắt tham số là hộp thôi bị khóa và co lại bằng nội dung thật.
     ⚠ Nhưng chú thích của chính tham số ấy đòi một điều kiện: **chỉ tắt khi bên trong đã có vùng
     cuộn riêng** — tắt mà ⛔ chỗ nào cho nội dung thừa đi thì bảng 20 dòng mực sẽ đẩy dải nút
     «Thôi / Ghi nhận» ra ngoài tầm nhìn. Khối dưới đây LÀ vùng cuộn ấy. ⛔ Gỡ một trong hai vế là
     hỏng — gỡ luật này thì hộp trôi theo số dòng, gỡ `CuonTrongThan="false"` thì trống trở lại.

   📐 Mốc `46vh` là chỗ cân giữa hai phía, ⛔ không phải con số tiện tay: phần CỦA CẢ PHIẾU cộng dải
     đầu hộp và dải chân đã ăn khoảng 300px, nên 46vh còn chừa lề ở màn 900px và vẫn đủ bày khoảng
     tám dòng mực — quá tám thì cuộn. ⛔ Đừng nâng lên sát 100vh: hộp sẽ lại kín màn, đúng thứ anh
     kêu; ⛔ cũng đừng hạ xuống dưới ba dòng, người nhập mất cảm giác về bảng mình đang gõ.

   ⚠ Cặp ký tự đóng chú thích CSS là dấu sao rồi dấu gạch chéo; ⛔ đừng để nó lọt vào giữa câu văn —
     chú thích đóng sớm thì phần chữ còn lại dính vào bộ chọn ngay dưới và trình duyệt bỏ NGUYÊN
     luật đó, ⛔ không một câu báo nào. Soi lại bằng `node cong-cu/soi-chu-thich-css.mjs`.
   ⚠ Câu media ở đây chỉ MỘT điều kiện (⛔ không `and`) — bộ biên dịch CSS cách ly của Blazor gắn dấu
     scope vào đuôi câu media ghép, cho ra một câu hỏng cú pháp mà build vẫn xanh.
   📏 Đo lại: `node cong-cu/do-song-hop-ghi-muc.mjs --cong 5293` */

.gnm-vung-dong[b-cime58sp1a] {
    max-height: 46vh;
    overflow-y: auto;
    /* Chừa chỗ cho thanh cuộn, ⛔ để nó đè lên nút ✕ của dòng ngoài cùng bên phải. */
    padding-right: .25rem;
}

/* Màn hình thấp (cửa sổ chia đôi, laptop 768px): hạ mốc để dải nút dưới hộp luôn còn trong tầm
   nhìn. Một điều kiện, đúng luật tệp cách ly. */
@media (max-height: 700px) {
    .gnm-vung-dong[b-cime58sp1a] {
        max-height: 38vh;
    }
}

/* 📏 KHỔ ĐIỆN THOẠI PHẢI HẠ SÂU HƠN — số đo, ⛔ phải phòng xa: ở khổ 390×844 mỗi dòng mực chiếm
   BA hàng (các cột xuống dòng) chứ ⛔ một hàng như trên máy tính, nên cùng một mốc 46vh cho ra hộp
   cao 850px trên khung nhìn 844px — dải nút «Thôi / Ghi nhận» vừa đúng bị đẩy khỏi mép dưới.
   Hạ về 34vh thì 4 dòng ra ~749px, còn chừa lề. ⛔ Đừng gộp hai câu media này thành một câu ghép:
   bộ biên dịch CSS cách ly gắn dấu scope vào ĐUÔI câu media có `and` ⇒ cả khối bị bỏ trong im lặng. */
@media (max-width: 575.98px) {
    .gnm-vung-dong[b-cime58sp1a] {
        max-height: 34vh;
    }
}
/* /Components/Pages/MucIn/KhoiSoanToMayPhoto.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 2961 (việc o2483) — SOẠN TỪNG TỜ CỦA BỘ HỒ SƠ MÁY PHOTOCOPY ═══

   Khuôn chép theo `KhoiSoanToTrinhChuTruong.razor.css` (đợt 1978) — bố cục anh Tiến đã duyệt: trái là
   ô điền, phải là tờ giấy. Tiền tố đổi sang `smp-` vì CSS cách ly gắn DẤU RIÊNG cho từng component,
   nên lớp của tệp kia ⛔ với tới thẻ của tệp này được.

   ⚠ Chú thích CSS: một dấu `*` cộng `/` lạc giữa thân luật là đóng chú thích SỚM — phần sau thành mã
     CSS hỏng mà trình duyệt bỏ im lặng. */

/* ── Dải tab con: mỗi tờ một tab ────────────────────────────────────────────────────────── */
.smp-dai-tab[b-4mofip74wj] {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    padding: .6rem .8rem;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
}

.smp-tab[b-4mofip74wj] {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .3rem .7rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 999px;
    background: var(--bs-body-bg, #fff);
    color: var(--bs-body-color, #212529);
    font-size: .8rem;
    line-height: 1.25;
}

.smp-tab:hover[b-4mofip74wj] {
    border-color: var(--bs-primary, #0d6efd);
}

.smp-tab.dang-chon[b-4mofip74wj] {
    background: var(--bs-primary, #0d6efd);
    border-color: var(--bs-primary, #0d6efd);
    color: #fff;
}

.smp-tab-dem[b-4mofip74wj] {
    padding: 0 .38rem;
    border-radius: 999px;
    background: var(--bs-secondary-bg, #e9ecef);
    color: var(--bs-secondary-color, #6c757d);
    font-size: .72rem;
    font-variant-numeric: tabular-nums;
}

.smp-tab.dang-chon .smp-tab-dem[b-4mofip74wj] {
    background: rgba(255, 255, 255, .22);
    color: #fff;
}

/* Tờ đã đủ ô: huy hiệu XANH — số có lợi. Tờ còn thiếu ô bắt buộc: dấu ⚠ và viền ĐỎ — số bất lợi. */
.smp-tab.du-o .smp-tab-dem[b-4mofip74wj] {
    background: var(--bs-success, #198754);
    color: #fff;
}

.smp-tab.con-thieu[b-4mofip74wj] {
    border-color: var(--bs-danger, #dc3545);
}

.smp-tab-thieu[b-4mofip74wj] {
    color: var(--bs-danger, #dc3545);
    font-size: .8rem;
}

.smp-tab.dang-chon .smp-tab-thieu[b-4mofip74wj] {
    color: #ffd9dd;
}

/* ── Khung hai cột ──────────────────────────────────────────────────────────────────────── */
.smp-khung[b-4mofip74wj] {
    display: grid;
    grid-template-columns: minmax(320px, 5fr) minmax(360px, 7fr);
    max-height: min(78vh, 900px);
    max-height: min(78dvh, 900px);
}

.smp-trai[b-4mofip74wj] {
    min-width: 0;
    overflow-y: auto;
    padding: .75rem 1rem 1rem;
}

.smp-phai[b-4mofip74wj] {
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: var(--bs-tertiary-bg, #f4f5f7);
    border-left: 1px solid var(--bs-border-color, #dee2e6);
}

/* Khổ hẹp thì xếp dọc, tờ giấy XUỐNG DƯỚI ô điền, và bỏ trần chiều cao để trang tự cuộn — hai vùng
   cuộn lồng nhau trên màn hình bé là người dùng cuộn ô này lại tưởng cả trang đứng im.
   MỘT điều kiện duy nhất: câu `@media` ghép bằng `and` đã có lượt trả giá ở đợt 1481. */
@media (max-width: 991.98px) {
    .smp-khung[b-4mofip74wj] {
        grid-template-columns: 1fr;
        max-height: none;
    }

    .smp-trai[b-4mofip74wj] {
        overflow-y: visible;
    }

    .smp-phai[b-4mofip74wj] {
        border-left: 0;
        border-top: 1px solid var(--bs-border-color, #dee2e6);
    }
}

/* ── Cột trái: thanh tiến độ, nhóm ô, hàng nút ──────────────────────────────────────────── */
.smp-tien-do[b-4mofip74wj] {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .5rem;
}

.smp-tien-do-chu[b-4mofip74wj] {
    font-size: .82rem;
    white-space: nowrap;
}

.smp-tien-do-so[b-4mofip74wj] {
    min-width: 2.6rem;
    color: var(--bs-secondary-color, #6c757d);
    font-size: .78rem;
    text-align: right;
}

.smp-thanh[b-4mofip74wj] {
    flex: 1;
    min-width: 0;
    height: 8px;
}

.smp-thieu[b-4mofip74wj] {
    margin-bottom: .55rem;
    font-size: .76rem;
    line-height: 1.35;
    color: var(--bs-danger, #dc3545);
}

.smp-nhom[b-4mofip74wj] {
    margin: .55rem 0 .3rem;
    font-size: .8rem;
    font-weight: 600;
    color: var(--bs-secondary-color, #6c757d);
    text-transform: uppercase;
    letter-spacing: .02em;
}

/* Ô đã có chữ: một vạch xanh mảnh bên trái — đọc lướt là thấy còn ô nào trống. */
.smp-o-da-dien[b-4mofip74wj] {
    border-left: 3px solid var(--bs-primary, #0d6efd);
    padding-left: .5rem;
}

.smp-bang-ngoai[b-4mofip74wj] {
    padding: .32rem .5rem;
    border: 1px dashed var(--bs-border-color, #dee2e6);
    border-radius: .35rem;
    background: var(--bs-tertiary-bg, #f4f5f7);
    font-size: .78rem;
    line-height: 1.35;
    color: var(--bs-secondary-color, #6c757d);
}

.smp-hang-nut[b-4mofip74wj] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .8rem;
}

.smp-bao-ok[b-4mofip74wj] {
    font-size: .78rem;
    color: var(--bs-success-text-emphasis, #146c43);
}

.smp-bao-loi[b-4mofip74wj] {
    font-size: .78rem;
    color: var(--bs-danger, #dc3545);
}

/* ── Cột phải: đầu cột và chú giải ──────────────────────────────────────────────────────── */
.smp-phai-dau[b-4mofip74wj] {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .4rem;
    padding: .55rem .8rem .4rem;
}

.smp-phai-ten[b-4mofip74wj] {
    font-weight: 600;
    font-size: .9rem;
}

.smp-chu-giai[b-4mofip74wj] {
    padding: 0 .8rem .5rem;
    font-size: .72rem;
    line-height: 1.4;
    color: var(--bs-secondary-color, #6c757d);
}

.smp-chu-giai-nhac[b-4mofip74wj] {
    display: block;
    margin-top: .2rem;
}

/* ── Tờ giấy ────────────────────────────────────────────────────────────────────────────── */
.smp-nen-giay[b-4mofip74wj] {
    flex: 1;
    min-height: 0;
    overflow: auto;
    padding: 0 .8rem 1rem;
}

/* Tờ giấy CỐ Ý luôn trắng, kể cả ở nền tối: nó mô phỏng tờ sẽ IN RA. Lật nó theo bộ màu là người lập
   soát một tờ giấy khác tờ mình sắp cầm trên tay. */
.smp-giay[b-4mofip74wj] {
    width: min(100%, 794px);
    margin: 0 auto;
    padding: 30px 34px 34px;
    background: #fff;
    color: #111;
    border: 1px solid #d5d7db;
    border-radius: 3px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .12);
    font-family: "Times New Roman", Times, serif, "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    font-size: 13.5px;
    line-height: 1.5;
}

.smp-doan[b-4mofip74wj] {
    margin: 0 0 .2rem;
    white-space: pre-wrap;
    tab-size: 6;
    word-break: break-word;
}

.smp-doan-trong[b-4mofip74wj] {
    min-height: .95em;
}

.smp-le-trai[b-4mofip74wj] {
    text-align: left;
}

.smp-le-giua[b-4mofip74wj] {
    text-align: center;
}

.smp-le-phai[b-4mofip74wj] {
    text-align: right;
}

.smp-le-deu[b-4mofip74wj] {
    text-align: justify;
}

/* Đoạn nằm trong danh sách Word TỰ ĐÁNH SỐ: chữ số ⛔ có trong nội dung tệp nên ở đây chỉ đặt một dấu
   mờ giữ chỗ. */
.smp-tu-danh-so[b-4mofip74wj]::before {
    content: "▪ ";
    color: #b4b7bb;
}

.smp-dam[b-4mofip74wj] {
    font-weight: 700;
}

.smp-nghieng[b-4mofip74wj] {
    font-style: italic;
}

.smp-chu-san[b-4mofip74wj] {
    color: inherit;
}

.smp-chu-dien[b-4mofip74wj] {
    color: #0a3d91;
    background: rgba(10, 61, 145, .08);
    border-radius: 2px;
}

.smp-chu-trong[b-4mofip74wj] {
    background: #fff3cd;
    color: #7a5b00;
    border-bottom: 1px dashed #d9a800;
}

/* Tờ Excel về thành BẢNG — cùng tờ giấy trắng, chỉ khác cách xếp chữ. */
.smp-bang[b-4mofip74wj] {
    width: 100%;
    border-collapse: collapse;
}

.smp-o-bang[b-4mofip74wj] {
    padding: 2px 5px;
    border: 1px solid #c9ccd1;
    vertical-align: top;
    word-break: break-word;
}

/* ── Vùng chạm ngón tay ─────────────────────────────────────────────────────────────────── */
@media (pointer: coarse) {
    .smp-tab[b-4mofip74wj] {
        min-height: 44px;
        padding: .45rem .85rem;
    }

    .smp-tab-dem[b-4mofip74wj],
    .smp-chu-giai[b-4mofip74wj],
    .smp-bang-ngoai[b-4mofip74wj],
    .smp-thieu[b-4mofip74wj] {
        font-size: max(.76rem, var(--cham-chu-toi-thieu, 12px));
    }
}
/* /Components/Pages/MucIn/KhoiTaoDotMuc.razor.rz.scp.css */
/* ĐỢT 2359 (việc o1936) — bảng dòng chờ gom: cột tên loại mực to nhất, cột phụ co theo nội dung
   (luật bảng đợt 2154). Khung cuộn riêng để thẻ tạo đợt ⛔ đẩy danh sách đợt xuống quá xa. */
.tdm-bang[b-3robgx0kxm] {
    max-height: 24rem;
    overflow: auto;
}

.tdm-hep[b-3robgx0kxm] {
    width: 1%;
    white-space: nowrap;
}
/* /Components/Pages/MucIn/TrangBangGiaMucXdcb.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — Ô TICK "HIỆN CẢ MÃ ĐÃ NGỪNG DÙNG" (việc o859) ═══

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài, và chính nó ghi rõ lý do: nâng chính ô vuông lên 44px thì ô tick to đúng bằng cả dòng chữ
   bên cạnh, trông như lỗi. Thứ phải nới là VÙNG CHẠM QUANH ô tick, mà vùng ấy mỗi màn hình một
   khác nên phải làm tại chỗ. Đây là một trong những chỗ ấy — đo 30/08/2026: đúng MỘT ô tick trong
   tệp này, và nó không nằm trong vòng lặp nào nên một dòng mã là đúng một ô trên màn hình.

   🔴 ĐỢT 1619 (việc o905) — SỬA LẠI LỜI KHAI KIỂU CỦA ĐỢT 1563, VÀ ĐO SỐNG MỚI THẤY.
     Đợt 1563 khai ô này là KIỂU ① (nhãn bọc cả ô tick lẫn chữ) rồi chỉ nới `.form-check-label`.
     Đọc mắt markup thì cấu trúc THẬT là KIỂU ③: `div.form-check` chứa `input` và `label[for]` là
     HAI ANH EM — cái `div` ấy KHÔNG bấm được. Đo sống 01/09/2026 (Chrome thật, khung 390×844 cảm
     ứng, `node cong-cu/do-song-o-tick-cham.mjs --trang muc-in/bang-gia`) trên bản CHƯA vá:
       · vùng bấm thật tại cột Ô TICK: 17px — tức đúng bằng chính ô vuông, dưới ngưỡng 44px;
       · vùng bấm thật tại cột CHỮ: 45px (nhãn đã được nới, phần ấy vốn đã đạt);
       · lệch tâm dọc giữa ô vuông và dòng chữ: −10px, ô tick dính MÉP TRÊN.
     ⇒ Đúng ĐÈN XANH GIẢ mà `VungChamNgonTayTests` đã ghi cho `TabHoaDonNuoc`: hộp cha cao 44px nên
       bộ đo đọc hộp báo "đã giải", còn ngón tay đặt vào khoảng trống thì không có gì xảy ra.
     ⇒ Khuôn đúng vì thế lấy từ `Components/Pages/Nuoc/TabHoaDonNuoc.razor.css` (kiểu ③), KHÔNG
       phải mục ① của `TabDanhMucNuoc.razor.css` như đợt 1563 đã chép. Mục ② của tệp ấy (thẻ bọc
       `o-tick-cham`) thì dành cho ô tick TRẦN, không dính gì tới đây.
     🔒 Số đo SAU khi vá nằm ở cuối tệp, ngay dưới khối media — đọc cả hai rồi hãy sửa tiếp.

   🔑 VÌ SAO ĐẶT Ở TỆP CÁCH LY, KHÔNG PHẢI Ở `app.css`:
     · `app.css` dùng chung cho MỌI màn hình của 433 tài khoản, phần lớn ngồi chuột — nới
       `.form-check-label` từ bên đó là đổi bố cục cả app để giải bài toán của một trang, đúng loại
       rủi ro lan truyền quy tắc #18 dặn phải tránh.
     · CSS cách ly biên dịch ra `.form-check-label[b-xxx]` (0,2,0) nên nó thắng bộ chọn cùng tên ở
       `app.css` (0,1,0), và chỉ chạm đúng phần tử viết trong `TrangBangGiaMucXdcb.razor`.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width` — khổ hẹp KHÔNG có nghĩa là ngón tay
     (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ quyết
     định vùng bấm là CÁI CHẠM VÀO, không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này không vẽ ra một pixel nào — mọi khai báo đều nằm trong câu
     media, không có một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — đừng viết câu media ghép bằng chữ `and` trong tệp `.razor.css`:
     bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học đợt 1481).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo. Cặp ký tự đó lọt vào GIỮA câu văn là chú
     thích đóng sớm, phần chữ còn lại dính vào bộ chọn của luật ngay dưới và trình duyệt bỏ nguyên
     luật đó — không một câu lỗi nào. Vì thế ở đây gọi tên cặp ký tự ấy bằng CHỮ, không gõ ra.
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ① HỘP BỌC Ô TICK — `div.form-check` của ô lọc "Hiện cả N mã đã ngừng dùng" trên thanh công cụ.
       Nâng sàn chiều cao và đổi sang hộp flex để ô tick thôi dính mép trên: trong hộp flex thuộc
       tính `float` bị BỎ QUA, nên ô vuông nhận phép canh giữa dọc như mọi phần con khác.

       🔑 BỀ NGANG KHÔNG ĐỔI — phần lề âm Bootstrap gán cho ô tick ăn khớp đúng với phần đệm trái
         1,5em của hộp: hộp mở đầu ở 1,5em · ô tick mang lề trái −1,5em nên khung của nó nằm ở
         0…1em (Y HỆT chỗ cũ) · bề rộng ngoài của ô tick vì thế là −0,5em, cộng `gap` 0,5em ⇒ nhãn
         bắt đầu đúng ở 1,5em, cũng Y HỆT chỗ cũ. Con số 0,5em của `gap` là thứ giữ khe hở giữa ô
         vuông và chữ; bỏ nó là chữ dính sát ô tick. Lý lẽ đầy đủ ở `TabHoaDonNuoc.razor.css`.
       ⚠ KHÔNG đụng `padding-left` của Bootstrap — chính phần đệm ấy giữ bề ngang.
       ⚠ `inline-flex` chứ không `flex`: hộp này nằm trong một hàng `d-flex flex-wrap gap-3` xếp
         ngang cùng ô tìm và câu đếm số mã đang hiện; hoá `flex` là nó chiếm trọn một dòng, thanh
         công cụ đang gọn bỗng vỡ ra nhiều dòng.
       ⚠ `min-height` chứ không `height`: câu chữ mang số đếm nên có lúc dài phải xuống hai dòng —
         lúc ấy cứ để nó cao hơn; đặt `height` là bóp lại, sửa một lỗi lại đẻ một lỗi. */
    .form-check[b-hswhn5nld0] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    /* ② NHÃN CHỮ — vùng bấm thật (nối với ô tick bằng thuộc tính `for`, bấm vào nhãn là đổi tick).
       `align-self: stretch` là dòng KHÔNG ĐƯỢC BỎ: hộp cha canh giữa các phần con, nên để nhãn tự
       co theo chiều cao con chữ thì vùng bấm thật chỉ còn ~21px trong khi hộp đã 44px — đúng kiểu
       đèn xanh giả mà đợt 1563 đã dính.

       🔴 CẶP `margin-left` + `padding-left` LÀ PHẦN ĐỢT 1619 PHẢI THÊM, VÀ ĐÃ ĐO MỚI THẤY CẦN.
         Bản vá kiểu ③ đầu tiên (`TabHoaDonNuoc.razor.css`, đợt 1555) chỉ có `align-self: stretch`,
         và đo sống 01/09/2026 trên chính trang này cho thấy nó giải được MỘT NỬA:
           · cột CHỮ 45px ✅ — phần nhãn thì đạt;
           · cột Ô TICK vẫn 17px 🔴 — vì nhãn bắt đầu SAU ô tick (hộp có đệm trái 1,5em, ô tick nằm
             ở 0…1em nhờ lề âm), nên khoảng trống NGAY TRÊN và NGAY DƯỚI ô vuông ⛔ không thuộc nhãn
             mà thuộc `div.form-check` — một thẻ `div` ⛔ KHÔNG bấm được.
         ⇒ Kéo nhãn sang trái đúng 1,5em rồi trả lại bằng phần đệm cùng cỡ: nhãn PHỦ TRỌN cả cột ô
           tick (bấm quanh ô vuông là trúng nhãn ⇒ đổi tick), còn dòng chữ vẫn đứng nguyên chỗ cũ ở
           1,5em. Phép cộng: input chiếm 0…1em · `gap` 0,5em · nhãn lùi 1,5em ⇒ nhãn mở đầu đúng ở 0.
         ⚠ Con số 1,5em là hằng số của Bootstrap (`.form-check { padding-left: 1.5em }` và
           `.form-check .form-check-input { margin-left: -1.5em }`). Nâng đời Bootstrap thì phải soi
           lại đúng hai luật ấy — ⛔ đừng đoán.
         ⚠ Nhãn đứng SAU ô tick trong markup nên nó vẽ ĐÈ lên ô vuông. Điều đó ⛔ không làm mất cú
           bấm: bấm vào nhãn là trình duyệt chuyển thẳng cho ô tick nối bằng `for`.
       ⚠ Ngưỡng nằm ở `.form-check` bên trên (biến `--cham-toi-thieu`), ⛔ đừng gõ lại số ở đây. */
    .form-check-label[b-hswhn5nld0] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -1.5em;
        padding-left: 1.5em;
    }
}

/* ═══ ĐỢT 2680 (việc o1980) — THẺ BỌC CUỘN PHẢI CÓ VỊ TRÍ NEO, ⛔ KHÔNG THÌ CHỮ ẨN THOÁT RA NGOÀI ═══

   📏 ĐO SỐNG 16/09/2026 (Chrome thật, khung 390×844, app riêng cổng 5303, chốt tiền đề `d-flex` ĐẠT):
     bề ngang tài liệu 819px trên khung 390px — trang GIÃN NGANG 429px, cắt mất cột «Tên loại mực».

   🔴 GIẢ THUYẾT CỦA DÒNG VIỆC ĐÃ BỊ SỐ ĐO BÁC BỎ, ⛔ đừng đi vá lại theo nó. Dòng việc đoán
     «có luật CSS làm .table-responsive hoặc .card mất overflow, hoặc bảng có min-width». Đo tận tay
     thì BẢNG ⛔ HỀ TRÀN: `.table-responsive` rộng 364 · cuộn 364 (khít) · `table.table` rộng 364 ·
     cuộn 854 (cuộn TRONG khung của nó, đúng như luật đợt 400 hứa). Khung cuộn ⛔ hỏng chỗ nào.

   🔑 THỦ PHẠM LÀ MỘT THẺ 1 PIXEL: `span.visually-hidden` («Sửa») nằm trong ô tiêu đề cuối bảng.
     Bootstrap dựng lớp ấy bằng `position: absolute`, mà ⛔ tổ tiên nào của nó mang vị trí neo
     (`.table-responsive` · `table` · `.card` đều ở `position: static`) ⇒ khối chứa của nó là KHỐI
     CHỨA BAN ĐẦU, tức nó ⛔ hề là con của khung cuộn theo nghĩa bố cục.
     ⛔ `overflow` CHỈ CẮT phần tử tuyệt đối khi khung cuộn CHÍNH LÀ khối chứa của nó — đây là chỗ
     dễ hiểu ngược nhất: khung cuộn vẫn cắt mọi thứ khác, chỉ riêng thẻ này thoát ra. Thẻ đứng ở
     x = 818 trong hệ toạ độ TRONG bảng (đã cuộn) nên nó kéo tài liệu ra đúng 819px.
   🔴 HỎNG HOÀN TOÀN LẶNG LẼ: thẻ rộng 1px, `clip` về 0, mắt ⛔ bao giờ thấy; build xanh, test xanh,
     và trên máy tính thì bảng ⛔ tràn nên ⛔ có gì để thấy. Chỉ người dùng điện thoại chịu.

   ✅ VÁ: cho thẻ bọc một VỊ TRÍ NEO. Có neo thì khối chứa của thẻ ẩn là chính thẻ bọc ⇒ `overflow`
     của thẻ bọc cắt được nó. ⛔ Đừng vá bằng cách xoá thẻ ẩn: nó là TÊN của cột thao tác cho người
     dùng bộ đọc màn hình, xoá đi là đổi bề ngang trang bằng cách bỏ một phần trợ năng.
   📏 Thử tận tay trên trang đang chạy, cả hai chiều (⛔ suy luận):
     · gỡ thẻ ẩn khỏi DOM  ⇒ 819 → 390 · trả thẻ về chỗ cũ ⇒ 390 → 819 (ĐẢO ĐƯỢC, ⛔ ăn may);
     · đặt `position: relative` cho thẻ bọc ⇒ 819 → 390.
     🔒 Chốt ngược cùng lượt: `div.page` (thứ chắc chắn ⛔ giãn) ra đúng 390px.

   🔴 VÌ SAO LUẬT NÀY ĐỨNG NGOÀI CÂU MEDIA — và ĐÍNH CHÍNH lời hứa viết ở đầu tệp.
     Câu «ở chế độ chuột tệp này ⛔ vẽ một pixel nào» của đợt 1619 nói về KHỐI CHẠM, và khối ấy vẫn
     nguyên vẹn trong câu media. Luật dưới đây thì ⛔ được bọc: bảng cuộn ngang ở BẤT KỲ khổ nào đủ
     hẹp, kể cả cửa sổ máy tính thu nhỏ — mà `(pointer: coarse)` hỏi CÁI CHẠM VÀO chứ ⛔ hỏi bề
     ngang, nên bọc vào đó là để nguyên lỗi cho người dùng chuột ở cửa sổ hẹp.
   🔒 `position: relative` KHÔNG vẽ một pixel nào: ⛔ có `top`/`left` nên thẻ ⛔ xê dịch, và
     `z-index: auto` nên nó ⛔ dựng ngữ cảnh xếp chồng. Thứ duy nhất nó đổi là KHỐI CHỨA của phần tử
     tuyệt đối bên trong — đúng thứ đang hỏng. Lời hứa ấy đã ĐO ở chế độ chuột, ⛔ phải suy ra.
   ⚠ Bảng này ⛔ có tiêu đề DÍNH (`position: sticky`) nên vị trí neo mới ⛔ đụng vào ai. Trang nào có
     tiêu đề dính thì phải đo lại trước khi chép khuôn này sang.
   ⚠ Đây là luật CỦA RIÊNG TRANG NÀY (CSS cách ly biên dịch ra `.table-responsive[b-xxx]`). Cùng loại
     lỗi còn ở 6 tệp `.razor` khác đang dùng `visually-hidden` trong bảng — vá chung phải sửa CSS GỐC
     nên ⛔ làm ở đây, đã báo phiên chính.
   🚧 Hàng rào: `TKNB.Tests/NeoKhungCuonBangTests` · luật thuần `TKNB.Tests/LuatNeoKhungCuonBang.cs`
   📏 Chốt ngược (bật/tắt vị trí neo trên CHÍNH bản đang chạy): node cong-cu/do-song-neo-khung-cuon.mjs
   📏 Đo lại: node cong-cu/do-song-khung-hep.mjs --trang muc-in/bang-gia --cong <cổng> --rong 390
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */
.table-responsive[b-hswhn5nld0] {
    position: relative;
}

/* 🔒 SỐ ĐO SAU KHI VÁ — đợt 1619, cùng lệnh và cùng khung với số "trước" ở đầu tệp (app dựng riêng
   ở cổng 5281 từ một `git worktree` để ⛔ không giành cổng 5270 của phiên khác):
     · vùng bấm thật tại cột Ô TICK: 17px → 45px (đạt ngưỡng 44px);
     · vùng bấm thật tại cột CHỮ: 45px → 45px (vốn đã đạt, ⛔ không đổi);
     · lệch tâm dọc: −10px → +2px, ô tick thôi dính mép trên (2px còn lại là phần chênh của
       khoảng dòng chữ, mắt ⛔ không thấy).
   ⚠ LƯỢT ĐO GIỮA CHỪNG ĐÃ BẮT ĐƯỢC MỘT ĐÈN XANH GIẢ, ghi lại vì nó là bài học chứ ⛔ không phải
     chuyện vặt: bản vá lúc mới chỉ có `.form-check` + `align-self: stretch` đo ra cột chữ 45px mà
     cột ô tick VẪN 17px. Nếu đợt này tin vào bộ đo cũ (đọc hộp cha) thì đã đóng việc ở đúng chỗ ấy.
   🔒 CHỐT NGƯỢC Ở CHẾ ĐỘ CHUỘT (`--chuot`, 1369×900): trước và sau khi vá đều là ô vuông 16×16 ·
     nhãn cao 21 · hộp cao 24 (display block) · vùng bấm 17px tại ô tick và 22px tại chữ · lệch tâm
     −0,5px — TRÙNG KHÍT từng con số, tức máy tính KHÔNG đổi một pixel nào. Đó là lời hứa của câu
     media, và nó đã được ĐO chứ ⛔ không phải được suy ra. */
/* /Components/Pages/MucIn/TrangChiTietHoSoMayPhoto.razor.rz.scp.css */
/* ===== ĐỢT 2274 (việc o1789) — TIÊU ĐỀ NHÓM TRONG FORM «THÔNG TIN HỒ SƠ» =====
   14 ô đổ một mạch thì mắt ⛔ không biết đang điền phần nào; chia bốn nhóm nghiệp vụ, mỗi nhóm một
   dòng tiêu đề nhỏ. Đây là CHỮ GIẢI THÍCH nội dung đang hiện, ⛔ không phải câu hướng dẫn thao tác
   (luật ngôn từ 30/07 chỉ cấm dải hướng dẫn). */

.hsp-nhom[b-ocv30goqx1] {
    margin: 1.1rem 0 .45rem;
    padding-bottom: .25rem;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--bs-secondary-color, #6c757d);
}

/* Nhóm ĐẦU TIÊN nằm sát mép trên thân thẻ ⇒ ⛔ không cần lề trên. */
.hsp-nhom:first-child[b-ocv30goqx1] {
    margin-top: 0;
}

/* ===== ĐỢT 2559b (việc o2061 pha 2) — Ô PHẦN MỀM ĐỌC CHỮ VỪA ĐIỀN SẴN, CHƯA LƯU =====
   Xanh nhạt = điền từ chữ gốc của tệp; vàng = đọc từ bản quét (nhận dạng ảnh) nên phải soát kỹ.
   Lớp đặt trên ô ODien (component con) nên phải đi qua ::deep; .card-body là thẻ của chính trang nên mang
   dấu cách ly của trang.
   ⚠ Viền đổi bằng BIẾN --bs-border-color đặt ngay trên ô nhập, ⛔ border-color longhand: bộ chọn này ở (0,4,0)
   nên longhand sẽ thắng .form-control.is-invalid (0,2,0) và nuốt viền đỏ báo lỗi (hàng rào VienONhapTuongPhanTests). */
.card-body[b-ocv30goqx1]  .hsp-may-dien .form-control {
    background-color: var(--bs-info-bg-subtle);
    --bs-border-color: var(--bs-info-border-subtle);
}

.card-body[b-ocv30goqx1]  .hsp-can-soat .form-control {
    background-color: var(--bs-warning-bg-subtle);
    --bs-border-color: var(--bs-warning);
}

.card-body[b-ocv30goqx1]  .hsp-can-soat .lde-them {
    color: var(--bs-warning-text-emphasis);
    font-weight: 600;
}
/* /Components/Pages/MucIn/TrangDanhSachHoSoMayPhoto.razor.rz.scp.css */
/* ═══ ĐỢT 2703 (việc o2179) — VÙNG CHẠM NGÓN TAY CHO Ô LỌC «HIỆN CẢ HỒ SƠ ĐÃ BỎ» ═══

   📏 ĐO SỐNG 17/09/2026 trên bản HEAD db4defe6 (Chrome thật, khung 390×844 cảm ứng, app riêng cổng
     5295, node cong-cu/do-song-o-tick-cham.mjs --trang /muc-in/photo/ho-so):
       ô vuông 16×16 thả nổi trái · nhãn cao 21 · hộp `.form-check` cao 24 ở chế độ khối
       VÙNG BẤM THẬT: cột ô tick 17px · cột chữ 22px — ngưỡng ngón tay là 44px.

   🔑 ĐÂY LÀ KIỂU ③ — ô tick và nhãn là HAI ANH EM trong `div.form-check`, nối nhau bằng thuộc tính
     `for`. Thẻ bọc là một `div`, mà `div` thì ⛔ KHÔNG bấm được: nâng chiều cao thẻ bọc thôi là ĐÈN
     XANH GIẢ — bộ đo nào đọc hộp cha sẽ báo «đã giải» trong khi ngón tay đặt cạnh ô vuông ⛔ không
     thấy gì xảy ra. Khuôn đúng đã trả giá ở đợt 1555 · 1619 · 1627, bản mẫu là
     `TrangBangGiaMucXdcb.razor.css` cùng thư mục — đọc tệp ấy trước khi sửa tệp này.

   🔑 VÌ SAO ĐẶT Ở TỆP CÁCH LY, ⛔ KHÔNG PHẢI `app.css`: `app.css` dùng chung cho mọi màn hình của
     433 tài khoản, phần lớn ngồi chuột — nới `.form-check-label` từ bên đó là đổi bố cục cả app để
     giải bài của một trang, đúng loại rủi ro lan truyền quy tắc #18 dặn phải tránh. CSS cách ly biên
     dịch ra `.form-check-label[b-xxx]` (0,2,0) nên nó thắng bộ chọn cùng tên ở `app.css` (0,1,0).
   ⚠ Thẻ ở đây mang cả `.form-check-inline`, mà lớp ấy chỉ đặt `display: inline-block` (0,1,0) nên
     luật `display` dưới đây thắng — ⛔ đừng đi gỡ lớp `form-check-inline` khỏi markup.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ có nghĩa là ngón tay
     (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT khối này ⛔ vẽ ra một pixel nào. Đo được bằng `--chuot`, bảng trước
     và sau phải TRÙNG KHÍT.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; ⛔ đừng gõ số 44px vào tệp này.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng ghép bằng chữ `and` trong tệp `.razor.css` (đợt 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Đo lại: node cong-cu/do-song-o-tick-cham.mjs --trang /muc-in/photo/ho-so */

@media (pointer: coarse) {

    /* ① HỘP BỌC Ô TICK — nâng sàn chiều cao và đổi sang hộp flex để ô vuông thôi dính mép trên:
       trong hộp flex thuộc tính `float` bị BỎ QUA nên ô vuông nhận phép canh giữa dọc.
       ⚠ `inline-flex` chứ ⛔ `flex`: hộp này nằm trong hàng `d-flex flex-wrap align-items-end gap-2`
         xếp ngang cùng câu đếm số dòng; hoá `flex` là nó chiếm trọn một dòng.
         (Đợt 2892 — việc o2373: ô chọn loại hồ sơ đứng cạnh nó đã gỡ ở đợt 2857, câu này sửa theo.)
       ⚠ `min-height` chứ ⛔ `height`: nhãn có lúc phải xuống hai dòng, lúc ấy cứ để nó cao hơn.
       ⚠ ⛔ đụng `padding-left` của Bootstrap — chính phần đệm 1,5em ấy giữ nguyên bề ngang cũ. */
    .form-check[b-z0c2e9yzip] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    /* ② NHÃN CHỮ — vùng bấm THẬT (bấm vào nhãn là đổi tick nhờ thuộc tính `for`).
       `align-self: stretch` là dòng ⛔ ĐƯỢC BỎ: hộp cha canh giữa các phần con, để nhãn tự co theo
       con chữ thì vùng bấm chỉ còn ~21px trong khi hộp đã 44px — đúng kiểu đèn xanh giả đợt 1563.
       🔴 Cặp `margin-left` + `padding-left` cùng cỡ là nửa thứ HAI, và là dòng dễ bị gỡ nhất: nhãn
         bắt đầu SAU ô vuông nên khoảng ngay trên và ngay dưới ô vuông thuộc `div.form-check` — một
         thẻ ⛔ bấm được. Kéo nhãn sang trái đúng 1,5em rồi trả lại bằng phần đệm cùng cỡ thì nhãn
         PHỦ TRỌN cột ô tick, còn dòng chữ vẫn đứng nguyên chỗ cũ ở 1,5em.
       ⚠ 1,5em là hằng số Bootstrap (`.form-check{padding-left:1.5em}` và
         `.form-check .form-check-input{margin-left:-1.5em}`) — nâng đời Bootstrap thì soi lại đúng
         hai luật ấy, ⛔ đừng đoán. */
    .form-check-label[b-z0c2e9yzip] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -1.5em;
        padding-left: 1.5em;
    }
}
/* /Components/Pages/MucIn/TrangMayPhotoXdcb.razor.rz.scp.css */
/* ═══ ĐỢT 2688 (việc o2193) — THU GỌN DASHBOARD 📠 MÁY PHOTOCOPY CHO VỪA MỘT KHUNG NHÌN ═══
   Anh Tiến 16/09/2026: *"dashboard của máy photo phải làm tương tự như quản lý nước"*. Khuôn lấy
   NGUYÊN của `Nuoc/TabTongQuanNuoc.razor.css` (đợt 694) — cùng tên lớp `.gon-mot-khung-nhin`, cùng
   cách thu nhỏ, để hai dashboard của app nội bộ nhìn giống nhau.

   ⛔ VÌ SAO ĐỂ Ở ĐÂY MÀ KHÔNG PHẢI `app.css`: đây là CSS CÁCH LY — Blazor tự gắn dấu phạm vi nên mọi
      luật dưới đây CHỈ ăn trong trang này. Cùng những khối ấy (`KhoiDuBaoMayXdcb`,
      `KhoiDanhSachMayXdcb`) nếu mai kia dùng ở trang khác vẫn giữ cỡ cũ. Đưa các luật này vào
      `app.css` là đụng chỗ dùng chung, đúng diện rủi ro lan truyền của quy tắc #18.

   ⓘ `::deep` là BẮT BUỘC với mọi thứ nằm trong component CON: dấu phạm vi chỉ được gắn cho phần tử
      viết trong chính `TrangMayPhotoXdcb.razor`.

   ⚠ BẪY ĐÃ TRẢ GIÁ MỘT LƯỢT BÊN NƯỚC (đợt 694): mọi lớp TIỆN ÍCH của Bootstrap (`py-2`, `mb-3`,
     `fs-3`…) đều mang `!important`. Luật ở đây tuy trọng số cao hơn nhưng KHÔNG kèm `!important` thì
     vẫn thua, mà thua LẶNG LẼ (⛔ một câu lỗi nào). ⇒ Chỗ nào đè lên lớp tiện ích thì phải kèm
     `!important`; chỗ nào đè luật thường (`max-height`) thì không cần. */

/* ───── 1. BỐN Ô SỐ LỚN: con số nhỏ lại một nấc, ô bớt lề trên–dưới ───── */
.gon-mot-khung-nhin .the-so .card-body[b-4wwremk7jf] {
    padding-top: .25rem !important;
    padding-bottom: .25rem !important;
}

/* Con số chính: Bootstrap `.fs-3` = 1.75rem → 1.4rem. Vẫn là chữ lớn nhất màn hình (dòng quanh nó là
   `.small` ≈ .875rem) nên vai trò "đọc 10 giây nắm được năm này" giữ nguyên. */
.gon-mot-khung-nhin .the-so .fs-3[b-4wwremk7jf] {
    font-size: 1.4rem !important;
    line-height: 1.15;
}

/* Tên hồ sơ trong ô 📌 dài tùy người gõ ⇒ cắt còn HAI DÒNG, ⛔ để nó đẩy cao cả hàng thẻ.
   ⚠ `min-width: 0` là bắt buộc trên mọi cấp flex trên đường xuống phần tử cắt chữ. */
.gon-mot-khung-nhin .the-so-chu-cat[b-4wwremk7jf] {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    min-width: 0;
}

/* Nút «Chi tiết →»: đứng nép phải, ⛔ chiếm thêm một hàng của thẻ. */
.gon-mot-khung-nhin .nut-chi-tiet[b-4wwremk7jf] {
    margin-top: .15rem;
    float: right;
}

/* ───── 2. ĐẦU THẺ + THÂN THẺ: bớt lề, giữ nguyên cỡ chữ tiêu đề ───── */
.gon-mot-khung-nhin .card-header[b-4wwremk7jf],
.gon-mot-khung-nhin[b-4wwremk7jf]  .card-header {
    padding-top: .1rem !important;
    padding-bottom: .1rem !important;
}

.gon-mot-khung-nhin[b-4wwremk7jf]  .card-body {
    padding-top: .3rem !important;
    padding-bottom: .3rem !important;
}

/* ───── 3. BẢNG: hàng thấp lại, chữ nhỏ một nấc ───── */
.gon-mot-khung-nhin .table-sm > :not(caption) > * > *[b-4wwremk7jf],
.gon-mot-khung-nhin[b-4wwremk7jf]  .table-sm > :not(caption) > * > * {
    padding-top: .1rem !important;
    padding-bottom: .1rem !important;
    font-size: .8125rem;
}

/* ───── 4. VÙNG CUỘN — bảng dài ở lại TRONG thẻ, ⛔ kéo cả trang dài ra ─────
   🔑 Đây là chỗ đổi "trang dài bốn màn hình" thành "một khung nhìn": bảng vẫn đủ dòng (⛔ mất dòng
      nào, ⛔ phải phân trang), chỉ là cuộn trong lòng thẻ.
   ⚠ Trần đo bằng `rem` chứ ⛔ bằng `vh`: thẻ nằm trong lưới 3 cột, buộc theo chiều cao khung nhìn thì
     khổ điện thoại (một cột, bốn thẻ chồng nhau) lại thành bốn vùng cuộn cao bằng cả màn hình. */
.gon-mot-khung-nhin .vung-cuon[b-4wwremk7jf],
.gon-mot-khung-nhin[b-4wwremk7jf]  .vung-cuon {
    max-height: 10.5rem;
    overflow-y: auto;
}

/* Khối 🖥 Máy theo đơn vị là component con ⇒ phải với qua `::deep` để đặt trần cho bảng của nó. */
.gon-mot-khung-nhin[b-4wwremk7jf]  .table-responsive {
    max-height: 10.5rem;
    overflow-y: auto;
}

/* Cột 4 bọc một component con có sẵn thẻ `.card` — thẻ ấy ⛔ mang `h-100` (nó vốn dựng cho một hàng
   riêng) nên phải kéo cao ở ĐÂY, ⛔ thì đáy bốn cột lởm chởm. */
.gon-mot-khung-nhin[b-4wwremk7jf]  .cot-tung-may > .card {
    height: 100%;
}

/* ───── 5. DẢI 🔮 DỰ BÁO (component con): sáu thẻ nhỏ bớt cao, chữ tiền nhỏ một nấc ───── */
.gon-mot-khung-nhin[b-4wwremk7jf]  .fs-5 {
    font-size: 1.05rem !important;
}

/* Lề dưới của các thẻ trong component con: `.mb-3` = 1rem → .5rem, gom lại được ~2 hàng chữ. */
.gon-mot-khung-nhin[b-4wwremk7jf]  .card.mb-3,
.gon-mot-khung-nhin[b-4wwremk7jf]  .row.mb-3 {
    margin-bottom: .5rem !important;
}

/* ───── 6. KHỔ ĐIỆN THOẠI: bỏ trần cuộn, cho trang cuộn tự nhiên ─────
   Ở khổ hẹp mọi thẻ xếp CHỒNG nhau nên "một khung nhìn" ⛔ còn là mục tiêu đạt được; giữ vùng cuộn
   lồng trong trang cuộn là hai tầng cuộn tranh nhau ngón tay — thứ người dùng ⛔ bao giờ thắng. */
@media (max-width: 820px) {
    .gon-mot-khung-nhin .vung-cuon[b-4wwremk7jf],
    .gon-mot-khung-nhin[b-4wwremk7jf]  .vung-cuon,
    .gon-mot-khung-nhin[b-4wwremk7jf]  .table-responsive {
        max-height: none;
    }
}

/* ───── 7. ĐẦU THẺ CỦA CỘT 4 (component con): tên khối ⛔ được gãy dòng ─────
   Câu chú thích dài đứng cạnh tên khối trong một hộp `d-flex`; ở cột hẹp nó ép tên khối xuống ba dòng
   (đo 16/09 khổ 1536: «🖥 Máy / theo đơn / vị»). Ghim tên khối lại một dòng, câu chú thích tự xuống
   dòng — nó là phần ĐƯỢC PHÉP dài. */
.gon-mot-khung-nhin[b-4wwremk7jf]  .cot-tung-may .card-header > strong {
    white-space: nowrap;
}
/* /Components/Pages/NghiPhep.razor.rz.scp.css */
/* ⭐ ĐỢT 911 (việc o135) — BỐ CỤC HAI CỘT "VỪA ĐÚNG MỘT KHUNG NHÌN" cho trang Quản lý nghỉ phép.

   Anh Tiến 20/08/2026: *"Cấu trúc trang được chia thành 2 cột, tất cả chỉ vừa đủ trang nhìn không cần
   cuồn để xem"*.

   🔑 CÁCH LÀM: trang KHÔNG cuộn, nhưng bên trong mỗi ô thì được. Nội dung ở đây có phần dài tùy dữ liệu
   (danh sách đơn chờ duyệt của một phòng đông người), nên "không cuộn" không thể hiểu là "cắt cụt" —
   hiểu đúng là: bộ khung (tiêu đề · ô nộp đơn · thẻ hồ sơ phép) luôn nhìn thấy được mà không phải kéo
   trang, còn danh sách dài thì cuộn TRONG ô của nó.

   ⚠ SỐ TRỪ 163px là chiều cao phần trên (thanh nav + lề). ⭐ ĐỢT 938 hạ từ 200px xuống: dải tiêu
     đề "🏖 Quản lý nghỉ phép + 🔄 Làm mới" đã gỡ theo chỉ đạo anh Tiến 21/08/2026, giữ nguyên số
     trừ cũ là chừa một dải trắng ≈ 37px ở đáy trang. Số 37 = chiều cao thẻ `h1.h4` (1,5rem × 1,2
     ≈ 28,8px, luật hai cột chỉ chạy từ 1200px trở lên nên `h4` không co theo khung nhìn) + lề `mb-2`
     8px. ⛔ Không đo bằng JS:
     một biến CSS đọc được bằng mắt vẫn hơn một phép đo chạy lúc chạy mà không ai kiểm được. Lệch vài
     chục px thì thanh cuộn của ô nhích lên xuống chứ không vỡ gì.

   ⚠ `min-height` giữ sàn 420px: khung nhìn thấp (cửa sổ bị kéo bẹp, máy chiếu 768px) mà tính ra 150px
     thì hai cột hóa hai khe hẹp không đọc nổi — lúc đó thà để trang cuộn như thường. */
.np-hai-cot[b-dlehec0pvm] {
    height: calc(100vh - 163px);
    height: calc(100dvh - 163px);   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */
    min-height: 420px;
}

/* Mỗi cột tự cuộn. `min-height: 0` là mảnh BẮT BUỘC của flex/grid: thiếu nó, ô con có nội dung dài sẽ
   ĐẨY CAO cả cột thay vì cuộn bên trong — trình duyệt cho hộp flex một chiều cao tối thiểu bằng nội
   dung, và đó là lý do kinh điển khiến `overflow: auto` trong flex "không ăn". */
.np-cot[b-dlehec0pvm] {
    height: 100%;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
}

/* Hai ô của cột phải: nửa trên (nộp đơn) và nửa dưới (duyệt đơn). Chia đôi phần cao còn lại, ô nào ít
   nội dung thì nhường chỗ cho ô kia — `flex: 1 1 0` + `min-height: 0` cho đúng hành vi đó.
   ⚠ Người KHÔNG có quyền duyệt thì nửa dưới không được vẽ ra (markup tự chốt) ⇒ nửa trên chiếm trọn cột,
     không cần luật riêng nào ở đây. */
.np-o[b-dlehec0pvm] {
    flex: 1 1 0;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
}

/* Khung HẸP (<1200px — dưới ngưỡng `xl` của Bootstrap, đúng chỗ hai cột `col-xl-*` xếp chồng): TRẢ VỀ
   trang cuộn như thường. Ép "không cuộn" ở đây là hai vùng cuộn lồng nhau trên một màn hình bé —
   người dùng cuộn chuột trong ô này lại tưởng cả trang đứng im. */
@media (max-width: 1199.98px) {
    .np-hai-cot[b-dlehec0pvm] {
        height: auto;
        min-height: 0;
    }

    .np-cot[b-dlehec0pvm],
    .np-o[b-dlehec0pvm] {
        height: auto;
        overflow: visible;
    }
}
/* /Components/Pages/NghiPhep/DaiTinhNangNghiPhep.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 2230 (việc o1689) — DẢI NÚT TÍNH NĂNG CỦA MÀN HÌNH NGHỈ PHÉP ═══

   ⚠ Tệp CSS cách ly: mọi bộ chọn dưới đây được biên dịch thành `<bộ chọn>[b-xxxx]`, tức chỉ khớp thẻ
     do CHÍNH `DaiTinhNangNghiPhep.razor` viết ra. Muốn với tới thẻ của component con thì phải đi qua
     `::deep` — xem khối cuối tệp. */

.np-dai-tn[b-rkh755te8s] {
    /* Dải nằm ngay trên hai cột của màn hình chính; một vạch mảnh tách nó khỏi phần việc bên dưới. */
    padding-bottom: .4rem;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
}

/* Ruột dải — bốn thuộc tính này CỐ Ý khai ở đây chứ ⛔ không mượn lớp tiện dụng
   `d-flex flex-wrap align-items-center gap-1` của Bootstrap: những lớp ấy mang `!important` nên lượt
   THU ở khổ điện thoại (`display: none`) sẽ ⛔ không bao giờ thắng được chúng (bài học đợt 2181 ⑤). */
.np-dai-tn-ruot[b-rkh755te8s] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem;
}

.np-dai-tn-nhan[b-rkh755te8s] {
    /* Nhãn nhóm — không phải nút, nên giữ nguyên cỡ chữ nhỏ và ⛔ không nong vùng chạm. */
    white-space: nowrap;
}

/* Nút thu/bung CHỈ dành cho khổ điện thoại — luật NỀN giấu nó đi.
   ⚠ Giấu ở luật NỀN chứ ⛔ không phải trong `@media (min-width: …)`: viết kiểu kia thì trình duyệt
     ⛔ không hiểu câu media sẽ bày ra một nút thừa ngay giữa màn hình máy tính (chiều fail sai). */
.np-dai-tn-nut-thu[b-rkh755te8s] {
    display: none;
}

.np-dai-tn-vach[b-rkh755te8s] {
    /* Vạch đứng ngăn nhóm TÍNH NĂNG với nhóm CẤU HÌNH. Vẽ bằng một thẻ trống có `aria-hidden` chứ
       ⛔ không dùng ký tự gạch đứng: ký tự thì trình đọc màn hình đọc lên thành chữ. */
    display: inline-block;
    width: 1px;
    height: 1.25rem;
    margin: 0 .25rem;
    background: var(--bs-border-color, #dee2e6);
}

/* ── Ngón tay: nút của dải phải đủ 44px, đúng biến dùng chung của đợt 1518 ──
   ⛔ Điều kiện là CÁI CHẠM VÀO (`pointer: coarse`), ⛔ KHÔNG phải bề ngang: cửa sổ thu nhỏ trên máy
      tính vẫn dùng chuột, còn màn rộng vẫn có thể là cảm ứng. */
@media (pointer: coarse) {
    .np-dai-tn .btn[b-rkh755te8s] {
        min-height: var(--cham-toi-thieu, 44px);
        display: inline-flex;
        align-items: center;
    }
}

/* ── Khổ điện thoại: dải THU lại, chỉ còn một nút bung ──
   Mốc 576px là mốc dùng chung của các khung điền trang chủ (đợt 2176/2181) — giữ CÙNG một bề ngang
   để mọi khối của app đổi dáng ở cùng một chỗ, ⛔ đừng chế thêm mốc riêng.
   ⚠ Điều kiện ở đây là BỀ NGANG (⛔ không phải `pointer: coarse`) vì thứ đang thiếu là CHỖ, ⛔ không
     phải kích thước ngón tay — máy tính bảng nằm ngang vẫn đủ rộng để bày cả dải. */
@media (max-width: 576px) {
    .np-dai-tn-nut-thu[b-rkh755te8s] {
        display: inline-flex;
        align-items: center;
        min-height: var(--cham-toi-thieu, 44px);
    }

    /* Dải đang THU thì giấu ruột. ⛔ Chỉ giấu RUỘT, ⛔ đừng giấu cả `.np-dai-tn` — thẻ ngoài còn giữ
       nút bung, giấu nó là ⛔ không còn đường nào mở lại. */
    .np-dai-tn-thu .np-dai-tn-ruot[b-rkh755te8s] {
        display: none;
    }

    /* Bung ra thì mỗi nút chiếm trọn một dòng: tám nhãn dài xếp ngang ở 375px cho ra dải cao 339px
       mà chữ vẫn gãy đôi. Trọn dòng thì đọc được và ngón cái ⛔ không trượt. */
    .np-dai-tn-ruot .btn[b-rkh755te8s] {
        flex: 1 1 100%;
        justify-content: flex-start;
    }

    .np-dai-tn-vach[b-rkh755te8s] {
        display: none;
    }
}

/* ── Trang bày TRONG hộp thoại ──
   Tiêu đề `<h1>` của trang con nói đúng cái tên đã in trên đầu hộp thoại ⇒ in hai lần là chiếm chỗ.
   Giấu bằng `display: none` là ĐỦ và ĐÚNG ở đây: cái tên ấy ⛔ không mất khỏi màn hình, nó đã nằm ở
   `modal-title` do `<HopThoai>` dựng, và trình đọc màn hình vẫn đọc được câu ấy.
   ⚠ CHỈ giấu `h1` — mọi tiêu đề nhỏ hơn (`h2`, `h5`, tiêu đề thẻ) là cấu trúc bên trong trang. */
.np-hop-tn[b-rkh755te8s]  h1 {
    display: none;
}
/* /Components/Pages/NghiPhep/TraCuuPhepMotNguoi.razor.rz.scp.css */
/* ═══ 🔴 ĐỢT 2245 (việc o1724) — Ô TÌM NGƯỜI NẤP SAU MỘT NÚT BIỂU TƯỢNG 🔍 ═══

   Anh Tiến 09/09/2026, nguyên văn: *"đưa thông tin này ra ngoài ở cột trái, bỏ nút thông tin phép
   từng người vì thừa. Ô tìm người đổi bằng icon tìm kiếm và lương xuân lưu là đội trưởng thì được
   phép chấm công và duyệt phép cho các thành viên trong đội thì phải được tìm những người thuộc
   thẩm quyền của mình"*.

   ⚠ Tệp CSS CÁCH LY: mọi bộ chọn dưới đây được biên dịch thành `<bộ chọn>[b-xxxx]`, tức chỉ khớp thẻ
     do CHÍNH `TraCuuPhepMotNguoi.razor` viết ra. Muốn với tới thẻ của component con thì phải đi qua
     `::deep` — ở đây ⛔ không cần, cả ba bộ chọn đều là thẻ của chính khung này. */

.np-tim-cum[b-zgnf8iwbqo] {
    /* Giữ ĐÚNG bề ngang cũ của ô tìm (đợt 945 ghi thẳng `style="width: 285px;"` vào markup) — dải đầu
       thẻ vì thế ⛔ không đổi dáng giữa lúc mở và lúc đóng ô.
       ⚠ `max-width: 100%` để ở khổ điện thoại ô co lại theo thẻ chứ ⛔ không đẩy tràn ngang.
       ⭐ ĐỢT 2362 (việc o1952) — thêm `flex: 0 1 auto` + `min-width: 0`: dải đầu thẻ nay ⛔ xuống dòng (nút
       «📝 Nộp đơn» đứng cùng dòng, anh Tiến chốt 13/09/2026) nên khi hẹp thì ô này phải CO lại được — và
       ⛔ nở quá 285px ở máy tính. Ô gõ bên trong co theo (Bootstrap sẵn cho ô gõ trong cụm `min-width: 0`),
       nút ✖ giữ nguyên cỡ. */
    width: 285px;
    max-width: 100%;
    flex: 0 1 auto;
    min-width: 0;
}

/* ⭐ ĐỢT 2362 (việc o1952) — THỨ NHƯỜNG CHỖ KHI DẢI ĐẦU THẺ HẸP LÀ TÊN NGƯỜI / Ô GÕ, ⛔ PHẢI HAI CÁI NÚT.
   Markup đã bỏ lớp `flex-wrap` của dải đầu thẻ và của thẻ bọc này (lớp Bootstrap cầm `!important`, CSS
   ⛔ đè nổi) ⇒ dải ⛔ còn xuống dòng, nút «📝 Nộp đơn» trang cha đưa vào đứng cùng dòng cả ở khổ 375px.
   · Thẻ bọc `min-width: 0`: nó là phần tử flex của dải đầu thẻ, mặc định ⛔ co dưới bề ngang nội dung
     (tên người `nowrap` tới 285px) ⇒ thiếu dòng này thì tên ⛔ bao giờ cắt ba chấm mà đẩy nút nộp đơn
     tràn khỏi thẻ — luật ⑪ đợt 2198: `min-width: 0` phải có ở MỌI cấp trên đường xuống chỗ cắt chữ.
   · Nút 🔍 `flex: none`: ⛔ để nút bị bóp khi thẻ bọc co. */
.np-tim-boc[b-zgnf8iwbqo] {
    min-width: 0;
}

.np-tim-nut[b-zgnf8iwbqo] {
    flex: none;
}

.np-tim-ten[b-zgnf8iwbqo] {
    /* Tên người đang mở, bày khi ô ĐÓNG. `min-width: 0` là điều kiện để `text-truncate` cắt được chữ
       bên trong một thẻ flex — thiếu nó thì tên dài đẩy cả dải rộng ra thay vì hiện dấu ba chấm. */
    min-width: 0;
    max-width: 285px;
}

/* ── Ngón tay: nút 🔍 phải đủ vùng chạm, đúng biến dùng chung của đợt 1518 ──
   ⛔ Điều kiện là CÁI CHẠM VÀO (`pointer: coarse`), ⛔ KHÔNG phải bề ngang: cửa sổ thu nhỏ trên máy
      tính vẫn dùng chuột, còn màn rộng vẫn có thể là cảm ứng.
   ⛔ Đừng gõ con số vào đây — ngưỡng khai ĐÚNG MỘT CHỖ ở `app.css` (`--cham-toi-thieu`); số trong
      `var(…, …)` chỉ là đường lui khi tệp ấy chưa nạp, ⛔ không phải bản chép thứ hai của ngưỡng. */
@media (pointer: coarse) {
    .np-tim-nut[b-zgnf8iwbqo] {
        min-width: var(--cham-toi-thieu, 44px);
        min-height: var(--cham-toi-thieu, 44px);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Ô nhập và nút ✖ trong cụm cũng là chỗ ngón tay chạm vào — nới cùng một ngưỡng, ⛔ đừng để mỗi
       thứ một cỡ trong cùng một cụm. */
    .np-tim-cum .form-control[b-zgnf8iwbqo],
    .np-tim-cum .btn[b-zgnf8iwbqo] {
        min-height: var(--cham-toi-thieu, 44px);
    }
}
/* /Components/Pages/NhanSu/BangHoSoNhanSu.razor.rz.scp.css */
/* ═══ BẢNG HỒ SƠ CBCNV TỔNG KHO — đợt 2305 (việc o1844) ═══
   Dời từ TrangDanhSachCbcnv.razor.css (đợt 2276): bảng nay là MỘT thành phần dùng chung cho trang danh
   sách lẫn tab 🧑 Hồ sơ lý lịch, mà CSS cách ly chỉ ăn vào markup của đúng tệp .razor cùng tên.

   Luật bảng của dự án (07/09/2026): cột TÊN là cột to nhất, mọi cột phụ nhường chỗ.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — ⛔ đừng gõ cặp ấy vào giữa câu văn (đợt 852). */

/* ───── Bộ lọc ───── */
.dsc-loc[b-yeeff80b0i] {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem .8rem;
}

.dsc-o-loc[b-yeeff80b0i] {
    flex: 1 1 190px;
    min-width: 0;
}

/* Ô tìm rộng hơn ô chọn — đó là ô người ta gõ vào nhiều nhất. */
.dsc-o-tim[b-yeeff80b0i] {
    flex: 2 1 260px;
}

/* ───── Dải số liệu + nút đổi bộ cột ───── */
.dsc-so-lieu[b-yeeff80b0i] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
}

.dsc-nut-cot[b-yeeff80b0i] {
    margin-left: auto;
}

/* ───── Khung cuộn của bảng ─────
   Có TRẦN chiều cao nên cuộn dọc diễn ra TRONG khung ⇒ dòng tiêu đề dính được đỉnh khung; cuộn ngang
   cũng trong khung ⇒ cột họ tên dính được mép trái. Không có trần thì cả trang cuộn còn khung đứng
   yên, và dòng tiêu đề trôi mất theo trang.
   ⚠ Dòng 100vh là dòng LUI; dòng 100dvh phải đứng NGAY SAU (đợt 1500 — thanh địa chỉ điện thoại).
   ⚠ Khung ⛔ mang lớp card (xem chú thích ở markup) ⇒ viền + bo góc tự khai tại đây. */
.dsc-khung-bang[b-yeeff80b0i] {
    overflow: auto;
    max-height: calc(100vh - 6.5rem);
    max-height: calc(100dvh - 6.5rem);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    background-color: var(--bs-body-bg);
    /* Ô vừa mở ô nhập được trình duyệt cuộn tới lúc nhận con trỏ — phải CHỪA bề ngang cột tên dính và
       chiều cao dòng tiêu đề dính, không thì ô nhập nằm khuất một nửa dưới cột tên (ảnh đo đợt 2305 ở
       390px: ô «Giám đốc» chỉ còn thấy «m đốc»). */
    scroll-padding-left: 12.5rem;
    scroll-padding-top: 2.75rem;
}

.dsc-bang[b-yeeff80b0i] {
    font-size: .88rem;
}

/* ───── Bề ngang CỐ ĐỊNH theo từng cột ─────
   Bảng dựng ảo chỉ giữ vài chục dòng trong DOM: để cột co theo nội dung thì cuộn tới dòng có chữ dài
   hơn là cả cột nhảy rộng ra, bảng giật ngang ngay lúc đang cuộn dọc. ⇒ Tiêu đề giữ bề ngang tối thiểu,
   chữ trong ô bị chặn đúng bề ngang ấy và cắt ba chấm. Cùng một con số ở cả hai đầu nên cột đứng yên. */
.dsc-c-hep[b-yeeff80b0i] {
    --dsc-rong: 4.5rem;
}

.dsc-c-vua[b-yeeff80b0i] {
    --dsc-rong: 7.5rem;
}

.dsc-c-rong[b-yeeff80b0i] {
    --dsc-rong: 11rem;
}

.dsc-c-rat-rong[b-yeeff80b0i] {
    --dsc-rong: 15rem;
}

/* Cột phụ: ⛔ cho gãy chữ, nhường phần còn lại cho cột tên. */
.dsc-bang .dsc-cot-phu[b-yeeff80b0i] {
    width: 1%;
    white-space: nowrap;
}

/* ⚠ Trần của TIÊU ĐỀ phải cộng thêm đệm ô (.5rem của table-sm): hộp ô tính cả đệm, còn chữ trong ô bị
   chặn ở đúng --dsc-rong. Thiếu phần đệm thì một dòng có chữ dài vừa cuộn tới là cột nở thêm 8px — đo
   sống đợt 2305 ở 1400px: «Chức danh» 176 → 184px ngay lượt cuộn đầu. */
.dsc-bang th.dsc-cot-phu[b-yeeff80b0i] {
    min-width: calc(var(--dsc-rong, 4.5rem) + .5rem);
}

.dsc-chu[b-yeeff80b0i] {
    display: block;
    max-width: var(--dsc-rong, 12rem);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ───── Cột TÊN: to nhất, DÍNH mép trái khi cuộn ngang ─────
   Nền phải ĐẶC — ô dính mà trong suốt thì chữ của các cột trượt qua bên dưới lộ ra xuyên qua tên. */
.dsc-bang .dsc-cot-ten[b-yeeff80b0i] {
    min-width: 190px;
    position: sticky;
    left: 0;
    z-index: 2;
    background-color: var(--bs-body-bg);
    box-shadow: inset -1px 0 0 var(--bs-border-color);
}

/* Dòng tiêu đề DÍNH đỉnh khung. */
.dsc-bang thead th[b-yeeff80b0i] {
    position: sticky;
    top: 0;
    z-index: 3;
    background-color: var(--bs-body-bg);
    box-shadow: inset 0 -1px 0 var(--bs-border-color);
}

/* Góc trên trái (tiêu đề của cột tên) nằm trên CẢ HAI lớp dính. */
.dsc-bang thead th.dsc-cot-ten[b-yeeff80b0i] {
    z-index: 4;
    box-shadow: inset -1px -1px 0 var(--bs-border-color);
}

/* Tên là một NÚT (mở hộp xem đủ) nhưng trông như chữ gạch dưới nhạt. */
.dsc-nut-ten[b-yeeff80b0i] {
    min-width: 11rem;
    border: 0;
    background: none;
    padding: 0;
    color: var(--bs-link-color);
    font: inherit;
    font-weight: 500;
    text-align: left;
    text-decoration: underline;
    text-decoration-color: rgba(var(--bs-link-color-rgb), .35);
    text-underline-offset: 2px;
}

.dsc-nut-ten:hover[b-yeeff80b0i] {
    text-decoration-color: currentColor;
}

/* ───── Dải tên phòng ─────
   Chữ bên trong DÍNH mép trái — cuộn ngang sang cột thứ 20 vẫn đọc được mình đang ở phòng nào. */
.dsc-bang .dsc-hang-phong th[b-yeeff80b0i] {
    background-color: var(--bs-tertiary-bg);
    font-size: .85rem;
    text-transform: uppercase;
    letter-spacing: .02em;
}

.dsc-ten-phong[b-yeeff80b0i] {
    position: sticky;
    left: .5rem;
    display: inline-block;
}

/* ───── Sọc hàng — TỰ TÔ theo số thứ tự trong phòng (lý do ở LopHang, BangHoSoNhanSu.razor.cs) ─────
   Hàng mang lớp dsc-table-hang nên luật sọc chung của app.css CHỪA bảng này; ở đây tô lại đúng độ đậm
   của luật ấy (rgba .028 nền sáng · .045 nền tối) — hai con số đi CÙNG luật gốc, đổi một bên là bảng
   này lệch tông với mọi bảng khác của app. */
.dsc-bang > tbody > tr.dsc-hang-soc > td[b-yeeff80b0i] {
    background-color: rgba(0, 0, 0, .028);
}

:root[data-theme="dark"] .dsc-bang > tbody > tr.dsc-hang-soc > td[b-yeeff80b0i] {
    background-color: rgba(255, 255, 255, .045);
}

/* Ô tên DÍNH ở hàng sọc: nền vẫn ĐẶC, sọc vẽ bằng một lớp ảnh phủ lên — nền rgba trần là chữ các cột
   trượt bên dưới lộ xuyên qua tên (ảnh đo đợt 2305: đúng các dòng chẵn). */
.dsc-bang > tbody > tr.dsc-hang-soc > td.dsc-cot-ten[b-yeeff80b0i] {
    background-color: var(--bs-body-bg);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .dsc-bang > tbody > tr.dsc-hang-soc > td.dsc-cot-ten[b-yeeff80b0i] {
    background-color: var(--bs-body-bg);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}

/* Người đã nghỉ: nhạt đi bằng MÀU CHỮ, ⛔ bằng opacity — opacity làm ô tên dính hóa trong suốt. */
.dsc-bang .dsc-da-nghi td[b-yeeff80b0i],
.dsc-bang .dsc-da-nghi .dsc-nut-ten[b-yeeff80b0i] {
    color: var(--bs-secondary-color);
}

/* ───── Ô sửa được ───── */
.dsc-bang .dsc-o-sua-duoc[b-yeeff80b0i] {
    cursor: pointer;
}

.dsc-bang .dsc-o-sua-duoc:focus-visible[b-yeeff80b0i] {
    outline: 2px solid var(--bs-primary);
    outline-offset: -2px;
}

@media (hover: hover) {
    .dsc-bang .dsc-o-sua-duoc:hover[b-yeeff80b0i] {
        box-shadow: inset 0 0 0 9999px var(--bs-primary-bg-subtle);
    }
}

/* Ô vừa lưu xong: nền xanh nhạt để mắt thấy lượt lưu đã trôi. */
.dsc-bang .dsc-o-vua-luu[b-yeeff80b0i] {
    box-shadow: inset 0 0 0 9999px var(--bs-success-bg-subtle);
}

/* Ô ĐANG mở ô nhập: nong ra cho đủ chỗ gõ — chỉ MỘT ô một lúc nên chỉ một cột giãn tạm. */
.dsc-bang .dsc-o-dang-sua[b-yeeff80b0i] {
    min-width: 15rem;
    white-space: normal;
}

.dsc-o-sua[b-yeeff80b0i] {
    display: flex;
    align-items: center;
    gap: .25rem;
}

.dsc-o-sua > .form-control[b-yeeff80b0i],
.dsc-o-sua > .form-select[b-yeeff80b0i] {
    flex: 1 1 auto;
    min-width: 0;
}

/* Ô ngày là thành phần con ONgayCoThe — nó ⛔ có CSS cách ly nên phải với tới qua ::deep. */
.dsc-o-sua[b-yeeff80b0i]  .o-ngay-boc {
    flex: 1 1 auto;
    min-width: 0;
}

.dsc-nut-o[b-yeeff80b0i] {
    flex: none;
    line-height: 1;
    padding: .25rem .45rem;
}

.dsc-o-loi[b-yeeff80b0i] {
    margin-top: .2rem;
    font-size: .8rem;
    color: var(--bs-danger-text-emphasis);
}

/* ───── Ngón tay: vùng chạm 44px, ngưỡng đọc từ BIẾN --cham-toi-thieu (đợt 1518) ─────
   Chỉ trong pointer: coarse — máy tính ⛔ đổi một pixel. */
@media (pointer: coarse) {
    .dsc-bang > tbody > tr > td[b-yeeff80b0i] {
        height: var(--cham-toi-thieu);
    }

    .dsc-nut-ten[b-yeeff80b0i] {
        min-height: var(--cham-toi-thieu);
    }

    .dsc-nut-o[b-yeeff80b0i],
    .dsc-nut-cot[b-yeeff80b0i] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
    }

    .dsc-loc .form-select[b-yeeff80b0i],
    .dsc-loc .form-control[b-yeeff80b0i] {
        min-height: var(--cham-toi-thieu);
    }

    /* Ngón tay ⛔ có chuột để rê ⇒ ô sửa được mang gạch chấm nhạt dưới chữ, để biết ô nào chạm vào được. */
    .dsc-bang .dsc-o-sua-duoc .dsc-chu[b-yeeff80b0i] {
        text-decoration: underline dotted;
        text-decoration-color: var(--bs-secondary-color);
        text-underline-offset: 3px;
    }
}

/* ───── Khổ điện thoại ───── */
@media (max-width: 576px) {
    /* Bộ lọc: lưới hai cột, ô tìm trọn dòng ĐẦU. Nhãn ẩn khỏi MẮT nhưng vẫn còn cho trình đọc màn hình —
       lựa chọn đầu của mỗi ô đã tự nói tên nó («Tất cả phòng ban»). */
    .dsc-loc[b-yeeff80b0i] {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: .4rem;
    }

    .dsc-o-tim[b-yeeff80b0i] {
        grid-column: 1 / -1;
    }

    .dsc-nhan-loc[b-yeeff80b0i] {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }

    .dsc-bang[b-yeeff80b0i] {
        font-size: .84rem;
    }

    /* Cột tên dính mà rộng 190px là ăn nửa màn hình — co về ~8rem, tên dài xuống hai dòng. */
    .dsc-bang .dsc-cot-ten[b-yeeff80b0i] {
        min-width: 8rem;
        white-space: normal;
    }

    .dsc-nut-ten[b-yeeff80b0i] {
        min-width: 7.5rem;
        max-width: 9.5rem;
    }

    .dsc-khung-bang[b-yeeff80b0i] {
        max-height: calc(100vh - 5rem);
        max-height: calc(100dvh - 5rem);
        scroll-padding-left: 8.5rem;
    }

    .dsc-bang .dsc-o-dang-sua[b-yeeff80b0i] {
        min-width: 13rem;
    }
}
/* /Components/Pages/NhanSu/KhoiQuanHeGiaDinh.razor.rz.scp.css */
/* ═══ QUAN HỆ GIA ĐÌNH TRONG HỘP THOẠI MỘT NGƯỜI — đợt 2305 (việc o1844) ═══
   Khối nằm ngay dưới tờ xem đủ của KhoiSuaDongNhanSu trong cùng một hộp thoại — vạch trên ngăn hai khối. */

.kqh-khung[b-11dv7m7mtx] {
    margin-top: 1rem;
    padding-top: .75rem;
    border-top: 1px solid var(--bs-border-color);
}

.kqh-tieu-de[b-11dv7m7mtx] {
    font-weight: 600;
    margin-bottom: .4rem;
}

/* Ngón tay: nút xóa chỉ mang biểu tượng + nút thêm đủ vùng chạm 44px — ngưỡng đọc từ BIẾN (đợt 1518). */
@media (pointer: coarse) {
    .kqh-nut[b-11dv7m7mtx],
    .kqh-nut-them[b-11dv7m7mtx] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
    }
}

/* Khổ điện thoại: nút chính trọn dòng — ⛔ để nút chính là nút bé nhất hàng (luật ③ đợt 2176). */
@media (max-width: 576px) {
    .kqh-nut-them[b-11dv7m7mtx] {
        width: 100%;
    }
}
/* /Components/Pages/NhanSu/KhoiSuaDongNhanSu.razor.rz.scp.css */
/* HÀNG CHI TIẾT / SỬA của một người trong bảng danh sách CBCNV.
   Lưới tự co theo bề ngang: máy tính nhiều cột, điện thoại một cột. */

.ksd-khung[b-9ps2o61i27] {
    padding: .6rem .2rem;
}

.ksd-luoi[b-9ps2o61i27] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: .5rem .9rem;
}

.ksd-o[b-9ps2o61i27] {
    min-width: 0;   /* thiếu dòng này là ô dài nở ra kéo giãn cả lưới */
}

.ksd-o-rong[b-9ps2o61i27] {
    grid-column: 1 / -1;
}

/* Dòng tiêu đề nhóm chiếm trọn bề ngang, tách hai khối cho mắt dễ bám. */
.ksd-o-nhom[b-9ps2o61i27] {
    grid-column: 1 / -1;
    margin-top: .35rem;
    border-top: 1px solid var(--bs-border-color, #dee2e6);
    padding-top: .35rem;
}

.ksd-tieude-nhom[b-9ps2o61i27] {
    font-weight: 600;
    font-size: .85rem;
    text-transform: uppercase;
    letter-spacing: .02em;
    color: var(--bs-secondary-color, #6c757d);
}

.ksd-nhan[b-9ps2o61i27] {
    display: block;
    font-size: .78rem;
    color: var(--bs-secondary-color, #6c757d);
    margin-bottom: .1rem;
}

.ksd-giatri[b-9ps2o61i27] {
    font-size: .9rem;
    overflow-wrap: anywhere;   /* số căn cước, địa chỉ dài ⛔ không được kéo giãn lưới */
}

.ksd-tick[b-9ps2o61i27] {
    padding-top: .15rem;
}

.ksd-chan[b-9ps2o61i27] {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin-top: .7rem;
}

/* Ngón tay: nút và ô nhập đủ 44px, nút chính chiếm trọn dòng ở khổ hẹp. */
@media (pointer: coarse) {
    .ksd-chan .btn[b-9ps2o61i27] {
        min-height: var(--cham-toi-thieu, 44px);
    }
}

@media (max-width: 576px) {
    .ksd-chan .btn[b-9ps2o61i27] {
        flex: 1 1 auto;
    }
}
/* /Components/Pages/NhanSu/OChonDanhMuc.razor.rz.scp.css */
/* Ô chọn danh mục (việc o2054) — nằm được trong ô bảng dạng flex lẫn trong lưới hộp sửa dòng. */
.ocdm[b-a0n28qydrh] {
    flex: 1 1 auto;
    min-width: 0;
}

.ocdm-them[b-a0n28qydrh] {
    display: flex;
    gap: 0.25rem;
    min-width: 0;
}

.ocdm-them .form-control[b-a0n28qydrh] {
    flex: 1 1 auto;
    min-width: 0;
}

.ocdm-nut[b-a0n28qydrh] {
    flex: 0 0 auto;
    white-space: nowrap;
}

.ocdm-loi[b-a0n28qydrh] {
    color: var(--bs-danger-text-emphasis);
    font-size: 0.8125rem;
    margin-top: 0.125rem;
}
/* /Components/Pages/NhanSu/TrangDanhSachCanBo.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — Ô TICK «Chỉ người đang làm việc» (việc o1276, 05/09/2026) ═══

   📏 ĐO SỐNG TRƯỚC KHI VÁ — 05/09/2026, Chrome thật, khung 390×844 cảm ứng, `tienvm`,
      `node cong-cu/do-song-o-tick-cham.mjs --trang danh-sach-can-bo` (hỏi thẳng
      `document.elementFromPoint`, ⛔ không đọc CSS):
        · KIỂU ③ «hai anh em»: `div.form-check` chứa `input` và `label[for]` — cái `div` ⛔ KHÔNG bấm được;
        · ô vuông 16×16 (float: left) · nhãn cao 24 · hộp `.form-check` cao 24 (display: block);
        · VÙNG BẤM THẬT: tại cột ô tick **17px** · tại cột chữ **25px** · lệch tâm dọc 0px.
      Trang có HAI ô tick cùng kiểu (ô lọc trên thanh công cụ + ô «Đang làm» trong tờ sửa người);
      lượt đo chỉ thấy ô lọc vì tờ sửa phải bấm mới mở — ⛔ đừng đọc «1 ô» thành «trang chỉ có 1 ô».

   🔑 KHUÔN KIỂU ③ CHÉP NGUYÊN TỪ `Components/Pages/MucIn/TrangBangGiaMucXdcb.razor.css` (đợt 1619,
      việc o905) — lý lẽ đầy đủ của từng dòng (vì sao `gap: .5em`, vì sao CẶP `margin-left` +
      `padding-left` 1,5em, vì sao `align-self: stretch` là dòng ⛔ không được bỏ) nằm ở tệp ấy,
      ⛔ không chép lại đây để hai bản khỏi lệch nhau. Tóm: hộp cha cao 44px thôi chưa đủ — khoảng
      trống trên/dưới ô vuông thuộc `div` nên ⛔ không bấm được; nhãn phải kéo sang trái 1,5em
      để PHỦ TRỌN cột ô tick (bấm quanh ô vuông là trúng nhãn ⇒ đổi tick).
   🔑 Khối chạm dùng chung ở `app.css` (đợt 1518) CỐ Ý để `.form-check-input` đứng ngoài: nâng chính
      ô vuông lên 44px là ô tick to bằng cả dòng chữ. Vùng chạm QUANH ô tick nới ở từng màn hình.
   ⚠ `inline-flex` chứ ⛔ không `flex`: ô lọc nằm trong hàng `d-flex gap-2 flex-wrap align-items-end`
      cùng ô tìm và ô chọn phòng; là phần con của hộp flex nên hai giá trị ấy như nhau, nhưng ô
      «Đang làm» trong tờ sửa đứng trong một `<td>` thường — `flex` ở đó là nó chiếm trọn dòng.
   🔴 Điều kiện là `pointer: coarse`, ⛔ không `max-width`; MỘT điều kiện mỗi câu media (bài học 1481).
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu`, ⛔ không gõ số 44px vào đây.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — ⛔ đừng gõ cặp ấy vào giữa câu văn (đợt 852).
   🚧 Hàng rào: TKNB.Tests/VungChamPhanTrangVaThongKeTests.cs
   📏 Đo lại: node cong-cu/do-song-o-tick-cham.mjs --trang danh-sach-can-bo   (chốt ngược: --chuot) */

@media (pointer: coarse) {
    .form-check[b-s8v1w6i33r] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    .form-check-label[b-s8v1w6i33r] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -1.5em;
        padding-left: 1.5em;
    }
}
/* /Components/Pages/NhanSu/TrangQuanTriNhanSu.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — LỐI VÀO «🧮 Hiệu chuẩn danh sách CBCNV» (việc o1276, 05/09/2026) ═══

   📏 Đo sống 05/09/2026 (Chrome thật, khung 390×844, cảm ứng, `tienvm`,
      `node cong-cu/do-song-khung-hep.mjs --toan-app`): trang `/nhan-su` còn đúng MỘT vùng bấm dưới
      ngưỡng — thẻ `a` này đo **366 × 24px**: liên kết đứng một mình trong một `<p>`, cao đúng bằng
      con chữ. Ngưỡng ngón tay 44px nằm ở biến `--cham-toi-thieu` (`app.css`, khối đợt 1518).

   🔑 `inline-flex` + `min-height`: hộp bấm cao lên tới ngưỡng, chữ canh giữa, ⛔ không đổi bề ngang
      và ⛔ không đổi cách dòng chữ đứng trong đoạn. ⛔ Đừng dùng `display: block` — bề ngang trọn dòng
      khiến chạm hụt bên phải chữ vẫn mở trang, người ta ⛔ hiểu vì sao.
   🔒 Toàn bộ trong `@media (pointer: coarse)` — máy tính ⛔ không đổi một pixel (chốt ngược `--chuot`).
   ⚠ Ngưỡng đọc từ BIẾN, ⛔ không gõ số 44px vào đây.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — ⛔ đừng gõ cặp ấy vào giữa câu văn (đợt 852).
   🚧 Hàng rào: TKNB.Tests/VungChamPhanTrangVaThongKeTests.cs
   ⭐ Đợt 2538 (việc o2055): cùng khối nới luôn các nút của dải khổ điện thoại `.qtns-dai-dt` (khối dưới). */

@media (pointer: coarse) {
    .qtns-loi-hieu-chuan[b-w2ka1n8w16] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }

    .qtns-dai-dt > a[b-w2ka1n8w16] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }
}

/* 🪦 ĐỢT 2538 (việc o2055) — khối «dải tab một dòng ở khổ điện thoại» (đợt 2305, việc o1844) ĐÃ GỠ cùng
   dải tab con của trang: anh Tiến 15/09/2026 «đưa danh sách tab này lên nav» ⇒ mỗi tab nay là một nút
   trên dải tab của thanh nav. ⛔ Dựng lại dải tab trong trang ở khổ MÁY TÍNH: hai dải cùng nội dung trên
   một màn hình (hàng rào BangHoSoNhanSuTests ⑥). */

/* ═══ DẢI NÚT KHỔ ĐIỆN THOẠI `.qtns-dai-dt` — đợt 2538 (việc o2055) ═══

   📏 Đo mã: dải tab app trên thanh nav (`.tta-dai`) bị ẩn HẲN ở khổ ≤ 820px bằng luật chung của
      `CoDien/TabTrangCuaApp.razor.css` (dải tab của TRANG được ưu tiên ở điện thoại). Đưa mười tab con lên
      nav mà ⛔ bù chỗ này là người dùng điện thoại mất lối bấm sang Hợp đồng, Lương… (trước đó họ có dải
      tab một dòng trong trang, đợt 2305).
   🔑 Dải này hiện ĐÚNG ở khổ mà dải nav ẩn (≤ 820px) và ẩn ở khổ còn lại ⇒ ⛔ bao giờ hai dải cùng nội
      dung trên một màn hình. Nút dựng từ ĐÚNG LuatMenuNhanSu.MucDuocThay (luật của thanh nav).
   🔑 Một dòng cuộn ngang: mỗi nút ⛔ co và ⛔ gãy chữ (flex: none + nowrap — thiếu vế nào cũng hỏng, bài học
      đợt 2285 ①). Mép phải mờ dần là dấu hiệu còn nút phía sau; đệm phải bằng đúng bề mờ. */
.qtns-dai-dt[b-w2ka1n8w16] {
    display: none;
}

@media (max-width: 820px) {
    .qtns-dai-dt[b-w2ka1n8w16] {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        gap: .3rem;
        padding-right: 1.5rem;
        padding-bottom: .2rem;
        scrollbar-width: thin;
        -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent);
        mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent);
    }

    .qtns-dai-dt > a[b-w2ka1n8w16] {
        flex: none;
        white-space: nowrap;
    }
}
/* /Components/Pages/Nuoc/KhoiDongHoVatLy.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — Ô TICK "ĐANG DÙNG" CỦA ĐỒNG HỒ VẬT LÝ (việc o737) ═══

   Ô tick ở dòng đang sửa là ô TRẦN: không có dòng chữ nào bên cạnh để mà bấm. Bỏ tick ở đây nghĩa
   là "đã tháo đồng hồ khỏi vị trí" — một thao tác GHI, nên bấm trượt là tốn của người dùng một
   lượt sửa lại.

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài: nâng chính ô vuông lên 44px là ô tick to bằng cả dòng chữ, xấu và không giải đúng bài.
   Thứ phải nới là VÙNG CHẠM QUANH ô tick, mà vùng ấy mỗi màn hình một khác nên làm tại chỗ.
   Lời giải thích đầy đủ (vì sao bọc thêm thẻ chứ không phủ một lớp vô hình tràn ra ngoài) nằm ở
   `TabDanhMucNuoc.razor.css` — đọc bên đó trước khi đổi cách làm ở đây.

   🔑 Đặt ở tệp CÁCH LY chứ không ở `app.css`: `app.css` dùng chung cho 433 tài khoản phần lớn ngồi
     chuột (rủi ro lan truyền quy tắc #18), còn CSS cách ly biên dịch ra `.o-tick-cham[b-xxx]`
     (0,2,0) nên chỉ chạm đúng phần tử viết trong `KhoiDongHoVatLy.razor`.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width`: khổ hẹp KHÔNG có nghĩa là ngón tay
     (cửa sổ thu nhỏ vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này không vẽ ra một pixel nào — không một khai báo nào nằm
     ngoài câu media. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; đừng gõ số 44px vào đây.
   ⚠ MỘT điều kiện mỗi câu media — đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css` (bài học đợt 1481).
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* Thẻ `label` bọc ngoài ô tick, thêm ở `.razor` chỉ để làm vùng chạm. Nằm TRONG dòng chảy nên
       hàng của bảng chỉ cao lên, không có lớp phủ nào đè sang ô gõ ghi chú đứng ngay bên cạnh.
       ⚠ Cần CẢ `min-width`: ô tick chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt ngang. */
    .o-tick-cham[b-m2ti4zyy57] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/Nuoc/TabAnhDongHoNuoc.razor.rz.scp.css */
/* Ô ảnh nhỏ trong bảng — GIỮ CHỖ CỐ ĐỊNH kể cả lúc ảnh chưa tải xong.
   ⛔ Đừng để trống chiều cao: ảnh tải xong mới đẩy chiều cao lên là cả bảng NHẢY một nhịp dưới tay
   người đang bấm, đúng bệnh vừa chữa cho ô xem camera. */
.adh-thumb[b-jqyxv05etd] {
    width: 96px;
    height: 72px;
    object-fit: cover;
    border-radius: 4px;
    border: 1px solid var(--bs-border-color, #dee2e6);
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

/* Ô gõ chỉ số — đủ rộng cho số đồng hồ tổng 8 chữ số kèm ba số lẻ, ⛔ không rộng tới mức đẩy cột
   ghi chú ra khỏi màn hình ở khổ hẹp. */
.adh-o-so[b-jqyxv05etd] {
    max-width: 9rem;
}
/* /Components/Pages/Nuoc/TabDanhMucNuoc.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — Ô TICK CỦA TAB DANH MỤC NƯỚC (việc o737) ═══

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài, và chính nó ghi rõ lý do: nâng ô vuông lên 44px thì ô tick to đúng bằng dòng chữ bên
   cạnh, trông như lỗi. Thứ cần nới là VÙNG CHẠM QUANH ô tick, mà vùng ấy mỗi màn hình một khác
   nên phải làm tại chỗ. Đây là một trong những chỗ ấy — đo 30/08/2026: 9 ô tick trong tệp này.

   🔑 VÌ SAO ĐẶT Ở TỆP CÁCH LY, KHÔNG PHẢI Ở `app.css`:
     · `app.css` dùng chung cho MỌI màn hình của 433 tài khoản, phần lớn ngồi chuột — nới
       `.form-check-label` từ bên đó là đổi bố cục cả app để giải bài toán của một trang, đúng loại
       rủi ro lan truyền quy tắc #18 dặn phải tránh.
     · CSS cách ly biên dịch ra `.form-check-label[b-xxx]` (0,2,0) nên nó thắng bộ chọn cùng tên ở
       `app.css` (0,1,0), và chỉ chạm đúng những phần tử viết trong `TabDanhMucNuoc.razor`.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width` — khổ hẹp KHÔNG có nghĩa là ngón tay
     (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ quyết
     định vùng bấm là CÁI CHẠM VÀO, không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này không vẽ ra một pixel nào — mọi khai báo đều nằm trong câu
     media, không có một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css`: bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ① Ô TICK CÓ CHỮ ĐI KÈM — 8 ô: 7 ô của biểu mẫu thêm/sửa đồng hồ và ô lọc "Hiện cả đồng hồ đã
       ngưng dùng". Thẻ `label` bọc CẢ ô tick lẫn dòng chữ, nên chính NÓ là vùng bấm thật (bấm vào
       chữ cũng đổi tick). Nâng sàn chiều cao của nhãn là ngón tay bấm trúng, mà ô vuông giữ nguyên.

       ⚠ Phải đổi `display`: `min-height` KHÔNG có tác dụng gì trên một hộp inline, đó là lý do
         dòng này đi kèm chứ không phải để trang trí.
       ⚠ `inline-flex` chứ không `flex`: bảy nhãn đầu là con của một hàng `d-flex flex-wrap gap-3`,
         hoá `flex` là mỗi nhãn chiếm trọn một dòng — hàng tick đang gọn bỗng dài ra bảy dòng.
       ⚠ `min-height` chứ không `height`: nhãn nào chữ dài phải xuống hai dòng thì cứ cao hơn, đặt
         `height` là bóp chúng lại — sửa một lỗi, đẻ một lỗi. */
    .form-check-label[b-me7apf9nub] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }

    /* ② Ô TICK TRẦN TRONG BẢNG — cột "không sử dụng", không có chữ nào bên cạnh để mà bấm. Ô này
       được bọc thêm một thẻ `label.o-tick-cham` ngay trong `.razor`; ở chế độ chuột thẻ ấy là hộp
       inline ôm sát ô tick nên KHÔNG đổi một pixel nào, còn ở đây nó nở thành vùng chạm bao quanh.

       🔑 VÌ SAO BỌC THÊM THẺ chứ không dùng vùng chạm ảo bằng lớp phủ tràn ra ngoài ô tick: ô này
         nằm trong một `td`, mà `td` bên cạnh chứa ô gõ chữ. Một lớp phủ 44px vô hình tràn khỏi ô
         tick sẽ đè lên mép ô gõ ấy và NUỐT cú bấm của nó — cách chữa lại đẻ ra một lỗi khó thấy
         hơn. Thẻ bọc thì nằm TRONG dòng chảy: hàng chỉ cao lên, không có gì chồng lên gì.
       ⚠ Cần CẢ `min-width`: ô tick chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt ngang. */
    .o-tick-cham[b-me7apf9nub] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/Nuoc/TabHoaDonNuoc.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — HAI Ô TICK LỌC CỦA BẢNG HÓA ĐƠN NƯỚC (việc o855) ═══

   Hai ô lọc trên thanh công cụ: "Chỉ dòng lệch tiền" và "Chỉ dòng chưa rõ kho". Đây là ô CHỈ LỌC
   (không ghi gì xuống CSDL) nên bấm trượt chỉ tốn một lượt bấm lại — nhưng chúng nằm chen giữa một
   hàng toàn ô chọn năm/tháng/kho và ô gõ tìm, tức đúng chỗ ngón tay hay trượt sang ô bên cạnh nhất.

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài, và chính nó ghi rõ lý do: nâng ô vuông lên 44px thì ô tick to đúng bằng dòng chữ bên cạnh,
   trông như lỗi giao diện. Thứ phải nới là VÙNG CHẠM QUANH ô tick, mà vùng ấy mỗi màn hình một khác
   nên làm tại chỗ. Bối cảnh đầy đủ nằm ở `TabDanhMucNuoc.razor.css` — đọc bên đó trước khi đổi cách
   làm ở đây.

   🔴 CẤU TRÚC Ở ĐÂY KHÁC HẲN BA TỆP `.razor.css` LÀM TRƯỚC (o737) — đừng chép khuôn của chúng sang.
     Bên kia thẻ `label` BỌC ô tick, còn ở đây ô tick và nhãn là HAI ANH EM cạnh nhau trong một thẻ
     `div.form-check` của Bootstrap, nối với nhau bằng thuộc tính `for`. Hệ quả: nâng sàn chiều cao
     cho mỗi cái nhãn là CHƯA ĐỦ — nhãn cao 44px thì hộp `div` cũng cao theo, nhưng ô tick là phần
     tử THẢ NỔI (Bootstrap cho nó `float: left`) nên nó dính lên MÉP TRÊN của hộp, lệch hẳn khỏi
     dòng chữ đã được canh giữa. Vì thế phải nới ở chính thẻ `div` bọc ngoài.

   🔑 VÌ SAO ĐỔI `div.form-check` THÀNH HỘP FLEX: trong một hộp flex, thuộc tính `float` bị BỎ QUA,
     nên ô tick thôi dính mép trên và nhận phép canh giữa dọc như mọi phần con khác. Phần lề âm mà
     Bootstrap gán cho ô tick thì vẫn còn, và nó ăn khớp đúng với phần đệm trái 1,5em của hộp:
       · hộp mở đầu ở 1,5em (đệm trái Bootstrap, luật này KHÔNG đụng tới);
       · ô tick mang lề trái -1,5em nên khung của nó nằm ở 0…1em — Y HỆT chỗ nó đứng hôm nay;
       · bề rộng ngoài của ô tick vì thế là -0,5em, cộng `gap` 0,5em ⇒ nhãn bắt đầu đúng ở 1,5em,
         cũng Y HỆT chỗ nó đứng hôm nay.
     ⇒ Bề ngang KHÔNG đổi, chỉ có chiều cao và phép canh giữa dọc là mới. Con số 0,5em của `gap` là
       thứ giữ nguyên khe hở giữa ô tick và chữ; bỏ nó đi là chữ dính sát vào ô vuông.
     ✅ ĐỢT 1627 (việc o922) — ĐOẠN SUY LUẬN TRÊN NAY ĐÃ ĐO SỐNG, VÀ PHẦN BỀ NGANG THÌ ĐÚNG. Đo
       01/09/2026 bằng `node cong-cu/do-song-o-tick-cham.mjs --trang nuoc?tab=hoa-don` (Chrome
       thật, khung 390×844 cảm ứng, hỏi thẳng `document.elementFromPoint` chứ ⛔ KHÔNG đọc CSS):
       hai ô tick đều ra ô vuông 16×16 · hộp `.form-check` cao 44 (display: flex) · lệch tâm dọc
       +2px ⇒ phần vá của đợt 1555 ĂN THẬT, ô vuông thôi dính mép trên.
     🔴 NHƯNG CÙNG LƯỢT ĐO ẤY LỘI RA NỬA CÒN THIẾU — xem mục ② dưới đây trước khi sửa tiếp.

   🔑 `align-self: stretch` trên nhãn là phần KHÔNG ĐƯỢC BỎ, và đây là chỗ dễ làm hỏng nhất: hộp cha
     canh giữa các phần con, nên nếu để nhãn tự co theo chiều cao con chữ thì vùng bấm thật vẫn chỉ
     cao ~19px trong khi hộp đã 44px — bộ đo đọc hộp cha rồi báo "đã giải", còn ngón tay thì vẫn
     trượt. Đúng kiểu ĐÈN XANH GIẢ. Cho nhãn kéo giãn hết chiều cao thì cả 44px mới bấm được thật.

   🔑 Đặt ở tệp CÁCH LY chứ không ở `app.css`: `app.css` dùng chung cho 433 tài khoản phần lớn ngồi
     chuột (rủi ro lan truyền quy tắc #18), và `.form-check` là bộ chọn của Bootstrap có mặt ở rất
     nhiều màn hình — sửa từ bên ấy là đổi bố cục cả app để giải bài toán của một trang. CSS cách ly
     biên dịch ra `.form-check[b-xxx]` (0,2,0) nên vừa chỉ chạm đúng phần tử viết trong
     `TabHoaDonNuoc.razor`, vừa thắng được `.form-check-inline` (0,1,0) của Bootstrap.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width`: khổ hẹp KHÔNG có nghĩa là ngón tay
     (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này không vẽ ra một pixel nào — không một khai báo nào nằm ngoài
     câu media. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; đừng gõ số 44px vào tệp này.
   ⚠ MỘT điều kiện mỗi câu media — đừng viết câu media ghép bằng chữ `and` trong tệp `.razor.css`:
     bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học đợt 1481).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo. Cặp ký tự ấy lọt vào GIỮA câu văn là chú
     thích đóng sớm, phần chữ còn lại dính vào bộ chọn của luật ngay dưới và trình duyệt bỏ nguyên
     luật đó — không một câu lỗi nào. Vì thế ở đây gọi tên cặp ký tự ấy bằng CHỮ, không gõ ra.
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ① HỘP BỌC Ô TICK — nâng sàn chiều cao và đổi sang hộp flex để ô tick thôi dính mép trên.
       ⚠ KHÔNG đụng `padding-left` của Bootstrap: chính phần đệm ấy ăn khớp với lề âm của ô tick để
         giữ nguyên bề ngang — xem đoạn suy luận ở đầu tệp.
       ⚠ `inline-flex` chứ không `flex`: hai hộp này là con của một hàng `d-flex flex-wrap gap-2`
         xếp ngang cùng bốn ô lọc khác; hoá `flex` là mỗi hộp chiếm trọn một dòng, thanh lọc đang
         gọn bỗng vỡ ra nhiều dòng.
       ⚠ `min-height` chứ không `height`: câu chữ của nhãn có lúc phải xuống hai dòng thì cứ để nó
         cao hơn; đặt `height` là bóp lại — sửa một lỗi, đẻ một lỗi. */
    .form-check[b-ztumvsw1cs] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    /* ② NHÃN CHỮ — vùng bấm thật (nối với ô tick bằng thuộc tính `for`, bấm vào chữ là đổi tick).
       `align-self: stretch` cho nó kéo giãn hết 44px của hộp cha, rồi tự canh giữa con chữ bên
       trong. Bỏ dòng ấy là rơi vào đèn xanh giả — xem lý do đầy đủ ở đầu tệp.

       🔴 ĐỢT 1627 (việc o922) — CẶP `margin-left` + `padding-left` LÀ NỬA CÒN THIẾU CỦA ĐỢT 1555,
         VÀ CHỈ ĐO SỐNG MỚI THẤY. Bản vá 1555 dừng ở `align-self: stretch`; đo 01/09/2026 trên
         chính trang này, bản CHƯA vá, ra hai con số nói ngược nhau:
           · vùng bấm thật tại cột CHỮ: 45px ✅ — phần nhãn thì đạt;
           · vùng bấm thật tại cột Ô TICK: 17px 🔴 — đúng bằng chính ô vuông, dưới ngưỡng 44px.
         Gốc: hộp mang đệm trái 1,5em còn ô tick mang lề âm cùng cỡ, nên nhãn bắt đầu SAU ô vuông ⇒
         khoảng NGAY TRÊN và NGAY DƯỚI ô vuông ⛔ không thuộc nhãn mà thuộc `div.form-check` — một
         thẻ `div` thì ⛔ KHÔNG bấm được. Ngón tay đặt cạnh ô vuông là ⛔ không có gì xảy ra, trong
         khi hộp cha vẫn 44px nên bộ đo nào đọc HỘP cũng báo "đã giải". Đúng ĐÈN XANH GIẢ mà đầu tệp
         này đã cảnh báo — lần này nó nằm ở nửa còn lại của chính hai ô tick ấy.
       ⇒ Kéo nhãn sang trái đúng 1,5em rồi trả lại bằng phần đệm cùng cỡ: nhãn PHỦ TRỌN cột ô tick
         (bấm quanh ô vuông là trúng nhãn ⇒ đổi tick), còn dòng chữ vẫn đứng nguyên chỗ cũ ở 1,5em.
         Phép cộng: ô tick chiếm 0…1em · `gap` 0,5em · nhãn lùi 1,5em ⇒ nhãn mở đầu đúng ở 0.
       ⚠ Con số 1,5em là hằng số của Bootstrap (`.form-check { padding-left: 1.5em }` và
         `.form-check .form-check-input { margin-left: -1.5em }`). Nâng đời Bootstrap thì phải soi
         lại đúng hai luật ấy — ⛔ đừng đoán.
       ⚠ Nhãn đứng SAU ô tick trong markup nên nó vẽ ĐÈ lên ô vuông. Điều đó ⛔ không làm mất cú
         bấm: bấm vào nhãn là trình duyệt chuyển thẳng cho ô tick nối bằng `for`.
       ⚠ Khuôn này chép từ `MucIn/TrangBangGiaMucXdcb.razor.css` (đợt 1619) — hai màn hình cùng
         KIỂU ③, nên sửa một bên thì soi lại bên kia. */
    .form-check-label[b-ztumvsw1cs] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -1.5em;
        padding-left: 1.5em;
    }
}

/* 🔒 SỐ ĐO SAU KHI VÁ — đợt 1627 (việc o922), cùng lệnh và cùng khung với số "trước" ở mục ② trên:
     node cong-cu/do-song-o-tick-cham.mjs --trang nuoc?tab=hoa-don
   (app dựng riêng ở cổng 5281 từ một git worktree — cây chung lúc ấy ⛔ không biên dịch được vì một
    phiên khác đang để thực thể nửa chừng trên đĩa, và ⛔ không được giành cổng 5270 của anh Tiến.)
     · vùng bấm thật tại cột Ô TICK: 17px → 45px — CẢ HAI ô lọc, đạt ngưỡng 44px;
     · vùng bấm thật tại cột CHỮ: 45px → 45px (vốn đã đạt từ đợt 1555, ⛔ không đổi);
     · lệch tâm dọc: +2px → +2px (⛔ không đổi — phần ấy đợt 1555 đã giải xong, và lượt đo này là
       lần đầu tiên có SỐ cho lời suy luận trên giấy của họ).
   🔒 CHỐT NGƯỢC Ở CHẾ ĐỘ CHUỘT (--chuot, 1369×900): trước và sau khi vá đều là ô vuông 16×16 ·
     nhãn cao 21 · hộp cao 24 (display: block) · vùng bấm 17px tại ô tick và 22px tại chữ · lệch tâm
     −0,5px — TRÙNG KHÍT từng con số, tức máy tính KHÔNG đổi một pixel nào. Đó là lời hứa của câu
     media, và nó đã được ĐO chứ ⛔ không phải được suy ra.
   🧬 Đo đột biến: node cong-cu/do-dot-bien-vung-cham.mjs — 26 ô, bắt 26/26 (hai ô mới mang dấu ⑨). */
/* /Components/Pages/Nuoc/TabKyNuoc.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — Ô TICK "THAY ĐỒNG HỒ" TRONG BẢNG CHỈ SỐ (việc o737) ═══

   Ô tick ở cột "thay đồng hồ" là ô TRẦN: không có dòng chữ nào bên cạnh để mà bấm, và nó nằm giữa
   một bảng nhập liệu — đúng chỗ ngón tay hay trượt sang ô bên cạnh nhất.

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài: nâng chính ô vuông lên 44px là ô tick to bằng cả dòng chữ, xấu và không giải đúng bài.
   Thứ phải nới là VÙNG CHẠM QUANH ô tick, mà vùng ấy mỗi màn hình một khác nên làm tại chỗ.
   Lời giải thích đầy đủ (vì sao bọc thêm thẻ chứ không phủ một lớp vô hình tràn ra ngoài) nằm ở
   `TabDanhMucNuoc.razor.css` — đọc bên đó trước khi đổi cách làm ở đây.

   🔑 Đặt ở tệp CÁCH LY chứ không ở `app.css`: `app.css` dùng chung cho 433 tài khoản phần lớn ngồi
     chuột (rủi ro lan truyền quy tắc #18), còn CSS cách ly biên dịch ra `.o-tick-cham[b-xxx]`
     (0,2,0) nên chỉ chạm đúng phần tử viết trong `TabKyNuoc.razor`.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width`: khổ hẹp KHÔNG có nghĩa là ngón tay
     (cửa sổ thu nhỏ vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này không vẽ ra một pixel nào — không một khai báo nào nằm
     ngoài câu media. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; đừng gõ số 44px vào đây.
   ⚠ MỘT điều kiện mỗi câu media — đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css` (bài học đợt 1481).
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* Thẻ `label` bọc ngoài ô tick, thêm ở `.razor` chỉ để làm vùng chạm. Nằm TRONG dòng chảy nên
       hàng của bảng chỉ cao lên, không có lớp phủ nào đè sang ô gõ ghi chú đứng ngay bên cạnh.
       ⚠ Cần CẢ `min-width`: ô tick chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt ngang. */
    .o-tick-cham[b-7kvuhvaupk] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Ô lọc "Chỉ hiện đồng hồ chưa ghi số" (đợt 1805) — khác ô tick ở trên đúng MỘT điểm: nó CÓ
       dòng chữ đi kèm, mà thẻ label bọc cả chữ nên bề ngang đã vượt xa ngưỡng chạm.
       ⚠ Vì thế ⛔ KHÔNG đặt min-width ở đây: ép 44px lên một nhãn đã rộng sẵn thì không thêm được
         gì, còn dán nhầm luật của ô TRẦN sang ô CÓ CHỮ là mở đường cho lượt sau chép ngược lại.
       ⚠ Ngưỡng vẫn đọc từ biến chung, ⛔ đừng gõ số 44px vào đây. */
    .o-loc-cham[b-7kvuhvaupk] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }
}
/* /Components/Pages/Nuoc/TabTongQuanNuoc.razor.rz.scp.css */
/* ═══ ĐỢT 694 — THU GỌN TAB 📊 TỔNG QUAN CHO VỪA MỘT KHUNG NHÌN ═══
   Anh Tiến 16/08/2026 (kèm ảnh màn hình): *"thu nhỏ cho vừa khung nhìn"*. Đây cũng đúng yêu cầu GỐC của
   đợt 217 khi dựng dashboard: *"tóm tắt … trong 1 trang màn hình máy tính mà KHÔNG CẦN KÉO XUỐNG"* —
   theo thời gian mỗi đợt thêm một khối (hóa đơn cả năm 361 · biểu đồ dịch vụ 469 · thu phí 636) nên
   trang dài dần ra mà không ai đo lại.

   ⛔ VÌ SAO ĐỂ Ở ĐÂY MÀ KHÔNG PHẢI `app.css`: đây là CSS ISOLATION — Blazor tự gắn dấu phạm vi nên mọi
      luật dưới đây CHỈ ăn trong tab Tổng quan của phân hệ nước. Cùng những component này
      (`KhoiTyLeNuoc`, `BieuDoTronNuoc`, `BieuDoMienNuoc`) khi dùng ở tab 📈 Phân tích số liệu vẫn giữ
      nguyên cỡ cũ — ở đó người ta xem kỹ và còn để IN. Đưa các luật này vào `app.css` là đụng chỗ dùng
      chung, đúng diện rủi ro lan truyền của quy tắc #18.

   ⓘ `::deep` là bắt buộc với mọi thứ nằm trong component CON (thẻ, biểu đồ, bảng hóa đơn): dấu phạm vi
      chỉ được gắn cho phần tử viết trong chính `TabTongQuanNuoc.razor`.

   📏 SỐ ĐO (bản tĩnh dựng từ HTML thật, bề ngang 1536 = màn 1920 ở mức phóng 125% của anh):
      chiều cao phần nội dung 815 px → 606 px. Xem nhật ký đợt 694 trong KE-HOACH.md. */

/* ⚠ BẪY ĐÃ DÍNH MỘT LƯỢT (đợt 694): mọi lớp TIỆN ÍCH của Bootstrap (`py-2`, `mb-3`, `fs-3`…) đều mang
   `!important`. Luật ở đây tuy trọng số cao hơn nhưng KHÔNG có `!important` thì vẫn thua — lượt đo đầu
   chỉ số hình tròn nhỏ đi, còn lề và cỡ chữ y nguyên mà không có lỗi nào báo. ⇒ Chỗ nào đè lên một lớp
   tiện ích thì phải kèm `!important`; chỗ nào đè lên luật thường (như `max-height` của hình) thì không cần. */

/* ───── 1. BỐN Ô SỐ LỚN: con số nhỏ lại một nấc, ô bớt lề trên–dưới ───── */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .the-so .card-body {
    padding-top: .25rem !important;
    padding-bottom: .25rem !important;
}

/* Con số chính: Bootstrap `.fs-3` = 1.75rem → 1.4rem. Vẫn là chữ lớn nhất màn hình (dòng chú thích
   quanh nó là `.small` ≈ .875rem) nên vai trò "đọc 10 giây nắm được kỳ này" giữ nguyên. */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .the-so .fs-3 {
    font-size: 1.4rem !important;
    line-height: 1.15;
}

/* ───── 2. ĐẦU THẺ + THÂN THẺ: bớt lề, giữ nguyên cỡ chữ tiêu đề ───── */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .card-header {
    padding-top: .1rem !important;
    padding-bottom: .1rem !important;
}

.gon-mot-khung-nhin[b-n0wsra2cc4]  .card-body {
    padding-top: .3rem !important;
    padding-bottom: .3rem !important;
}

/* ───── 3. BẢNG 3 KHO: dòng sát nhau hơn (bảng chỉ 4 dòng, không cần thoáng) ─────
   Đi qua BIẾN của Bootstrap (`--bs-table-cell-padding-y`) nên không phải chọi với `!important`. */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .table-sm {
    --bs-table-cell-padding-y: .12rem;
}

/* ───── 4. HÌNH MIỀN "Lượng nước 3 kho": chặn trần chiều cao ─────
   Hình vẽ theo tỷ lệ khung (viewBox 640×250, `height:auto`) nên cột càng rộng hình càng cao — ở màn
   1920 nó ăn ~295 px. Chặn trần + `height:auto` giữ nguyên tỷ lệ, chỉ hẹp lại. */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .o-bieu-do-mien svg {
    max-height: 105px;
}

/* Đầu thẻ của hình miền có ba phần (tên · huy hiệu "5 tháng dự báo" · "Cả năm 71.731 m³") — ở cột hẹp
   nó xuống hai dòng, ăn thêm ~25 px. Cho chữ nhỏ một nấc để ba phần đứng gọn một dòng. */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .o-bieu-do-mien .card-header {
    font-size: .84rem;
}

/* ───── 5. CHỮ CHÚ THÍCH / CẢNH BÁO: nhỏ một nấc ─────
   ⚠ CHỈ nhắm dòng chú thích và danh sách cảnh báo — KHÔNG đụng con số nào. */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .list-unstyled {
    font-size: .82rem;
    line-height: 1.25;
}

/* ───── 6. HAI CỘT PHẢI: bớt khe dọc giữa các thẻ ───── */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .row.g-3 {
    --bs-gutter-y: .5rem;
}

.gon-mot-khung-nhin[b-n0wsra2cc4]  .card.mb-3 {
    margin-bottom: .5rem !important;
}

/* ═══ 7. ĐÁY BA CỘT THẲNG HÀNG — đợt 747, anh Tiến 17/08/2026 ═══
   Nguyên văn: *"2 cột ở giữa và bên phải đang cao hơn so với chân trang, điều chỉnh lại cho đều đẹp
   với cột bên trái"*. Đợt 441 đã cân MỘT lượt cho hàng cuối, nhưng sau đó cột phải thêm biểu đồ dịch
   vụ bán nước (đợt 469) và luật thu gọn đổi khe dọc (đợt 694) nên đáy lệch lại mà không ai đo lại.

   📏 ĐO THẬT trước khi sửa (bản tĩnh dựng từ HTML thật của kỳ 07/2026, khổ 1400 — vùng nội dung
      1369px, lấy ĐÁY CỘT TRÁI làm mốc):
        chủ đề cổ điển / cổ điển tối : cột giữa hụt   8px · cột phải hụt 60px
        chủ đề hiện đại sáng / tối   : cột giữa hụt   8px · cột phải hụt 90px
      Sau khi sửa: cả bốn chủ đề đều 0px. Đo lại được ở 6 bề ngang (1536→700) và ở cả hai ngưỡng
      lưới xếp chồng (xl 1200 · lg 992) — không chỗ nào tràn đáy hay sinh thanh cuộn ngang. */

/* (a) HÀNG TRONG CỦA `KhoiTyLeNuoc` HỤT ĐÚNG MỘT KHE DỌC.
   `.row` của Bootstrap mang `margin-top: calc(-1 * var(--bs-gutter-y))` (ở đây = −8px do luật 6 ngay
   trên), trong khi `h-100` chỉ đặt chiều cao BẰNG cột cha. Hàng bắt đầu cao hơn 8px thì cũng kết thúc
   sớm 8px — đúng con số đo được. Cộng trả lại phần margin âm đó là hai đáy trùng nhau.
   ⚠ Phải có `!important`: `.h-100` của Bootstrap là `height: 100% !important` (bẫy đã ghi ở đầu tệp). */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .row.g-3.h-100 {
    height: calc(100% + var(--bs-gutter-y)) !important;
}

/* (b) CỘT PHẢI KHÔNG CÓ GÌ CO GIÃN nên phần dư đọng lại thành khoảng trắng ở đáy: cột giữa còn thẻ
   danh mục hóa đơn mang `flex-grow-1` tự nở, cột trái thì đợt 441 đã đẩy biểu đồ miền xuống bằng
   `mt-auto` — riêng cột phải là hai thẻ biểu đồ tròn cao tự nhiên.
   Cho CẢ HAI thẻ cùng nở (đo được: mỗi thẻ +30px) thay vì dồn hết vào một thẻ, để hai hình tròn giữ
   cỡ tương đương nhau như trước. */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .row.g-3.h-100 > .col-lg-6:last-child > .card {
    flex-grow: 1;
}

/* Căn giữa theo TRỤC DỌC để phần dư chia đều trên–dưới thay vì đọng hết dưới đáy thẻ (đo: dải tab kho
   và hình tròn thứ nhất chỉ dịch xuống 15px). ⛔ ĐỪNG đổi thành `align-items: center` — đó là trục
   NGANG của khối này và làm hình tụt khỏi mép quen thuộc, đúng lỗi đã vá ở đợt 172. */
.gon-mot-khung-nhin[b-n0wsra2cc4]  .row.g-3.h-100 > .col-lg-6:last-child > .card > .card-body {
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* ═══ 8. DẤU CHẤM THAN 3D ĐỘNG — "ĐÃ ĐẾN LÚC NHẬP KỲ NƯỚC" (đợt 1004, anh Tiến 22/08/2026) ═══
   Nguyên văn: *"bỏ dòng ghi chú … giữ lại nút tắt nhập kỳ nước và bổ sung thêm dấu chấm than 3d động
   để cảnh báo đã đến lúc cần nhập kỳ nước"*.

   BA LỚP LÀM NÊN CẢM GIÁC 3D — đủ ba thì mới ra khối cầu, thiếu một là ra hình tròn phẳng:
     · KHỐI: nền `radial-gradient` lệch tâm (nguồn sáng trên–trái) + viền trong sáng ở đỉnh;
     · ĐỘ DÀY: `box-shadow` xếp tầng — vành sẫm sát mép làm "thành" của khối, bóng đổ tách khối
       khỏi mặt nền, `inset` trắng ở đỉnh và `inset` sẫm ở đáy làm mặt cong;
     · CHUYỂN ĐỘNG: `perspective()` + `rotateX/rotateY` xoay khối quanh trục đứng — đây mới là thứ
       chứng minh nó có CHIỀU SÂU, vì bóng và vệt sáng chạy theo góc xoay.

   ⛔ ĐỪNG đổi thành vòng xoay TRỌN 360° quanh trục Y: qua mốc 90° dấu "!" quay lưng lại thành ẢNH
      GƯƠNG (không có `backface`/khối chữ thật để che), trông như lỗi hiển thị. Lắc ±26° là góc đo được
      còn giữ nét chữ đọc thẳng mà vẫn thấy rõ chiều dày.
   ⚠ Chỉ chạy `transform` + `opacity` ⇒ trình duyệt xử ở tầng hợp ảnh, KHÔNG dựng lại bố cục — dashboard
     này đang bị siết cho vừa một khung nhìn (mục 1–7 ở trên), thêm một hiệu ứng gây reflow là hỏng cả tab.
   ⓘ Không cần `::deep`: thẻ nằm ngay trong `TabTongQuanNuoc.razor` nên đã mang dấu phạm vi.
   ⓘ ĐO THẬT trong gói scoped (đợt 1004): Blazor CÓ gắn dấu phạm vi cho `@keyframes` — tên hóa
     `ntk-cham-than-lac-b-n0wsra2cc4` và dòng `animation:` được đổi theo KHỚP NHAU, nên hoạt hình chạy
     đúng mà không đụng tệp khác. ⛔ Đừng "sửa lại cho chắc" bằng cách bê hai khối `@keyframes` sang
     `app.css`: ở đó chúng thành tên TOÀN CỤC, còn dòng `animation:` trong tệp này vẫn bị gắn hậu tố
     ⇒ trỏ vào một cái tên không tồn tại và khối đứng im, KHÔNG một câu lỗi nào. */
.cham-than-3d[b-n0wsra2cc4] {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    font-weight: 900;
    font-size: 1.15rem;
    line-height: 1;
    color: #fff;
    /* Nguồn sáng chếch trên–trái: vệt sáng ở 32%/26% rồi tối dần xuống mép dưới–phải. */
    background: radial-gradient(circle at 32% 26%, #ffd766 0%, #f7a90b 42%, #b96f00 100%);
    border-top: 1px solid rgba(255, 255, 255, .55);
    box-shadow:
        0 .16rem 0 #96590a,                       /* thành khối — phần "dày" nhìn thấy được */
        0 .42rem .7rem rgba(0, 0, 0, .34),        /* bóng đổ xuống mặt nền */
        inset 0 .18rem .28rem rgba(255, 255, 255, .5),  /* mặt cong hắt sáng ở đỉnh */
        inset 0 -.22rem .3rem rgba(120, 66, 0, .55);    /* đáy khối tối lại */
    text-shadow: 0 -1px 0 rgba(90, 50, 0, .6), 0 1px 1px rgba(255, 255, 255, .35);
    /* `will-change` giữ khối ở tầng hợp ảnh riêng — hoạt hình chạy liên tục nên đáng đánh đổi. */
    will-change: transform;
    animation: ntk-cham-than-lac-b-n0wsra2cc4 2.6s ease-in-out infinite;
}

/* Sóng lan ra ngoài: nhắc "đến lúc rồi" ngay cả khi mắt đang nhìn chỗ khác trên dashboard.
   Đặt ở `::after` để nó KHÔNG xoay theo khối (sóng là vệt trên mặt nền, xoay theo là lộ ra hai
   chuyển động chọi nhau). `pointer-events:none` cho khỏi nuốt cú rê chuột đọc `title`. */
.cham-than-3d[b-n0wsra2cc4]::after {
    content: "";
    position: absolute;
    inset: -.18rem;
    border-radius: 50%;
    border: .12rem solid rgba(247, 169, 11, .75);
    pointer-events: none;
    animation: ntk-cham-than-song-b-n0wsra2cc4 2.6s ease-out infinite;
}

@keyframes ntk-cham-than-lac-b-n0wsra2cc4 {
    /* 0–58%: đứng yên — nhịp NGHỈ dài làm cú lắc sau đó bắt mắt hơn hẳn lắc đều liên tục. */
    0%, 58%, 100% { transform: perspective(340px) rotateY(0deg) rotateX(0deg) translateY(0); }
    64%  { transform: perspective(340px) rotateY(-26deg) rotateX(9deg)  translateY(-2px); }
    72%  { transform: perspective(340px) rotateY(24deg)  rotateX(-7deg) translateY(-3px); }
    80%  { transform: perspective(340px) rotateY(-15deg) rotateX(5deg)  translateY(-2px); }
    88%  { transform: perspective(340px) rotateY(8deg)   rotateX(-2deg) translateY(0); }
    94%  { transform: perspective(340px) rotateY(-3deg)  rotateX(0deg)  translateY(0); }
}

@keyframes ntk-cham-than-song-b-n0wsra2cc4 {
    0%, 52%  { transform: scale(1);    opacity: .85; }
    82%      { transform: scale(1.75); opacity: 0; }
    100%     { transform: scale(1.75); opacity: 0; }
}

/* Người tắt hiệu ứng chuyển động thì DỪNG HẲN — nhưng khối vẫn giữ nguyên hình 3D và màu cảnh báo,
   nên thông tin "đã đến lúc nhập kỳ" không mất đi cùng hoạt hình. */
@media (prefers-reduced-motion: reduce) {
    .cham-than-3d[b-n0wsra2cc4] { animation: none; }
    .cham-than-3d[b-n0wsra2cc4]::after { animation: none; opacity: .55; }
}

/* ═══ 9. CÁC LỐI "Chi tiết →" / "Xem thêm … cảnh báo →": ĐỒ HỌA DẠNG NÚT ═══
   🚚 **KHUÔN ĐÃ DỜI LÊN `wwwroot/app.css` ở đợt 1050** (việc `o237`) — anh Tiến nhắc LẦN HAI
   *"các link chi tiết em đổi thành dạng nút"*, mà để ở đây thì ba trang dashboard còn lại (công nghệ ·
   mạng · các trang sau này) **không với tới được**: CSS cách ly chỉ áp cho đúng component này.
   ⛔ ĐỪNG khai lại `.nut-chi-tiet` ở tệp này — CSS cách ly nạp SAU `app.css` và độ đặc hiệu cao hơn,
      nên bản chép tay sẽ THẮNG lặng lẽ rồi hai chỗ trôi khỏi nhau. Sửa khuôn thì sửa ở `app.css`. */
/* /Components/Pages/QuanLyPhong/ToLichTrucDepXem.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 1975 (việc o1252) — TỜ LỊCH TRỰC BẢN ĐẸP: MÀN HÌNH + BẢN IN ═══

   🔒 Anh Tiến 04/09/2026: "lịch trực không cần tuân thủ theo form gốc, em đồ họa lại cho đẹp,
      bản tải xuống và in ra cũng cần đẹp".

   ⚠ CSS SCOPED (`.razor.css`): mọi luật ở đây CHỈ chạm tới thẻ của ToLichTrucDepXem.razor. Muốn
     ẩn menu/nút lúc in thì phải khai ở CHÍNH trang chứa (TrangLichTruc.razor.css) — luật scoped
     ⛔ không với ra ngoài khối của mình. Đây là chỗ dễ mất một buổi nhất khi sửa CSS Blazor.

   ⚠ MÃ MÀU CỦA CỘT ⛔ KHÔNG khai ở đây — nó đi vào bằng thuộc tính `style` từ LuatToLichTrucDep,
     đúng con số mà tệp Excel dùng. Gõ lại ở đây là hai bảng màu lệch nhau dần. */

.to-lich[b-u53frz7v7y] {
    background: #fff;
    color: #1b1b1b;
    border: 1px solid #d8dde5;
    border-radius: .5rem;
    padding: 1.25rem 1.25rem 1rem;
}

/* ── Đầu tờ ─────────────────────────────────────────────────────────── */
.to-lich-dau[b-u53frz7v7y] {
    text-align: center;
    margin-bottom: 1rem;
    padding-bottom: .75rem;
    border-bottom: 2px solid #1f3864;
}

.to-lich-ten[b-u53frz7v7y] {
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: .02em;
    color: #1f3864;
}

.to-lich-phu[b-u53frz7v7y] {
    font-size: .85rem;
    color: #5b6472;
    margin-top: .15rem;
}

/* ── Bảng ngày × vị trí ─────────────────────────────────────────────── */
/* Bảng rộng phải CUỘN TRONG KHUNG CỦA NÓ, ⛔ không được đẩy cả trang trượt ngang
   (luật bề ngang của skill luat-giao-dien-razor-css). */
.to-lich-cuon[b-u53frz7v7y] {
    overflow-x: auto;
}

.to-lich-bang[b-u53frz7v7y] {
    width: 100%;
    border-collapse: collapse;
    font-size: .9rem;
}

.to-lich-bang th[b-u53frz7v7y],
.to-lich-bang td[b-u53frz7v7y] {
    border: 1px solid #c9d1dc;
    padding: .3rem .5rem;
    vertical-align: middle;
}

.to-lich-bang thead th[b-u53frz7v7y] {
    background: #1f3864;
    color: #fff;
    text-align: center;
    font-weight: 600;
    /* Đầu bảng bám lại khi cuộn danh sách 31 ngày — ⛔ không có nó thì cuộn tới ngày 20 là
       ⛔ không còn biết cột nào là cột nào. */
    position: sticky;
    top: 0;
    z-index: 2;
}

.th-phu[b-u53frz7v7y] {
    font-size: .7rem;
    font-weight: 400;
    opacity: .75;
}

.c-stt[b-u53frz7v7y] {
    width: 2.6rem;
    text-align: center;
    color: #6b7280;
}

.c-ngay[b-u53frz7v7y] {
    width: 9.5rem;
    white-space: nowrap;
}

.ngay-so[b-u53frz7v7y] {
    font-weight: 600;
}

.ngay-thu[b-u53frz7v7y] {
    color: #6b7280;
    font-size: .8rem;
    margin-left: .35rem;
}

.o-truc[b-u53frz7v7y] {
    text-align: center;
    min-width: 8rem;
}

.ten-nguoi[b-u53frz7v7y] {
    font-weight: 500;
    line-height: 1.35;
}

/* Cuối tuần: chỉ tô HAI cột đầu — tô đè cả hàng là nuốt mất màu riêng của từng cột vị trí. */
tr.cn .c-stt[b-u53frz7v7y],
tr.cn .c-ngay[b-u53frz7v7y] {
    background: #fde7e7;
    color: #b02418;
}

tr.t7 .c-stt[b-u53frz7v7y],
tr.t7 .c-ngay[b-u53frz7v7y] {
    background: #f3f5f8;
}

/* ── Bảng tổng kết + số điện thoại ──────────────────────────────────── */
.to-lich-tk[b-u53frz7v7y] {
    margin-top: 1.1rem;
}

.tk-tieu[b-u53frz7v7y] {
    font-weight: 600;
    color: #1f3864;
    margin-bottom: .5rem;
}

.tk-luoi[b-u53frz7v7y] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    gap: .5rem;
}

.tk-the[b-u53frz7v7y] {
    border: 1px solid #d8dde5;
    border-left: 3px solid #1f3864;
    border-radius: .35rem;
    padding: .4rem .6rem;
    background: #fbfcfe;
}

.tk-ten[b-u53frz7v7y] {
    font-weight: 600;
    line-height: 1.3;
}

.tk-so[b-u53frz7v7y] {
    font-variant-numeric: tabular-nums;
    letter-spacing: .02em;
}

.tk-thieu[b-u53frz7v7y] {
    color: #9aa2ad;
    font-style: italic;
}

.tk-dem[b-u53frz7v7y] {
    font-size: .78rem;
    color: #6b7280;
}

.to-lich-nhac[b-u53frz7v7y] {
    margin-top: .9rem;
    font-size: .85rem;
    color: #8a5a00;
}

/* ── Khối ký ────────────────────────────────────────────────────────── */
.to-lich-ky[b-u53frz7v7y] {
    display: flex;
    justify-content: flex-end;
    margin-top: 1.5rem;
}

.ky-cot[b-u53frz7v7y] {
    text-align: center;
    min-width: 14rem;
}

.ky-chuc[b-u53frz7v7y] {
    font-weight: 700;
}

.ky-phu[b-u53frz7v7y] {
    font-size: .8rem;
    font-style: italic;
    color: #6b7280;
}

/* ═══ BẢN IN ════════════════════════════════════════════════════════════
   ⚠ Ép nền in ra giấy: trình duyệt MẶC ĐỊNH bỏ màu nền khi in, mà màu cột chính là thứ phân biệt
     các vị trí trực ⇒ thiếu `print-color-adjust` là tờ in ra trắng trơn, mất hết ý nghĩa màu.
   ⚠ Bảng in ⛔ KHÔNG được cuộn (`overflow: visible`): khối cuộn khi in bị cắt cụt ở mép giấy. */
@media print {
    .to-lich[b-u53frz7v7y] {
        border: 0;
        padding: 0;
        color: #000;
    }

    .to-lich-cuon[b-u53frz7v7y] {
        overflow: visible;
    }

    .to-lich-bang[b-u53frz7v7y] {
        font-size: 9.5pt;
    }

    .to-lich-bang thead th[b-u53frz7v7y] {
        position: static;
    }

    /* Đầu bảng lặp lại ở MỌI trang giấy — sang trang 2 vẫn biết cột nào là cột nào. */
    .to-lich-bang thead[b-u53frz7v7y] {
        display: table-header-group;
    }

    .to-lich-bang tr[b-u53frz7v7y] {
        break-inside: avoid;
    }

    .to-lich-bang th[b-u53frz7v7y],
    .to-lich-bang td[b-u53frz7v7y],
    .tk-the[b-u53frz7v7y] {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    .to-lich-tk[b-u53frz7v7y],
    .to-lich-ky[b-u53frz7v7y] {
        break-inside: avoid;
    }
}
/* /Components/Pages/QuanLyPhong/ToLichTrucTheoNguoiXem.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 2345 (việc o1915) — TỜ LỊCH TRỰC THEO NGƯỜI × NGÀY (ĐỘI TÀU KÉO) ═══
   🔁 ĐỢT 2378 (việc o1983) — ĐẢO CHIỀU: mỗi DÒNG một người, mỗi CỘT một ngày (anh Tiến 13/09/2026: «dòng là tên
   còn các cột là ngày»).

   Tờ giấy trắng chữ đen như tờ theo cột (ToLichTrucXem) nhưng thân là LƯỚI. 30–35 cột ngày ⇒ ⛔ trần 900px của
   tờ theo cột; bảng cuộn TRONG khung của nó, hàng đầu (thứ + ngày) và cột HỌ TÊN (tên + số điện thoại) dính lại
   khi cuộn để ô nào cũng biết là của ai, ngày nào.

   ⚠ CSS SCOPED: luật ở đây CHỈ chạm tới thẻ của ToLichTrucTheoNguoiXem.razor. Lớp khong-in dùng TRONG tờ
     này phải khai lại ở đây — luật của TrangLichTruc.razor.css ⛔ với xuống được.
   ⚠ Bảng dùng border-collapse: separate — viền của ô DÍNH biến mất khi cuộn nếu để collapse (viền thuộc
     về bảng chứ ⛔ thuộc về ô). Mỗi ô tự kẻ viền phải + dưới, hàng/cột đầu kẻ thêm viền trên/trái.
   ⚠ Ô dính phải có NỀN ĐẶC — nền trong suốt là chữ các cột khác lộ xuyên qua khi cuộn ngang (bài học
     đợt 2305). Nền cột T7/CN/lễ vì thế tô lên TỪNG Ô của cột (lớp loại ngày gắn lên từng th/td), ⛔ tô lên thẻ
     tr: thẻ tr nay là một NGƯỜI, ⛔ còn là một ngày. */

.to-giay[b-ct0fciba6d] {
    background: #fff;
    color: #000;
    border: 1px solid #d8dde5;
    padding: 1.25rem 1.5rem 1rem;
    margin: 0 auto;
    font-family: "Times New Roman", Times, serif, "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    font-size: .95rem;
}

.to-giay.dang-tai[b-ct0fciba6d] {
    opacity: .55;
    pointer-events: none;
}

.to-dau[b-ct0fciba6d] {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    text-align: center;
}

.to-dau-trai[b-ct0fciba6d],
.to-dau-phai[b-ct0fciba6d] {
    flex: 1;
}

.dam[b-ct0fciba6d] {
    font-weight: 700;
}

.gach-duoi[b-ct0fciba6d] {
    text-decoration: underline;
}

.to-tieu-de[b-ct0fciba6d] {
    text-align: center;
    font-weight: 700;
    font-size: 1.15rem;
    margin-top: 1rem;
}

.to-thang[b-ct0fciba6d] {
    text-align: center;
    font-weight: 700;
    margin-bottom: .75rem;
}

/* ── Lưới người × ngày ─────────────────────────────────────────────────────── */
.tn-cuon[b-ct0fciba6d] {
    overflow: auto;
    max-height: calc(100vh - 11rem);
    max-height: calc(100dvh - 11rem);
}

.tn-bang[b-ct0fciba6d] {
    border-collapse: separate;
    border-spacing: 0;
    min-width: 100%;
}

.tn-bang th[b-ct0fciba6d],
.tn-bang td[b-ct0fciba6d] {
    border-right: 1px solid #000;
    border-bottom: 1px solid #000;
    padding: .15rem .3rem;
    vertical-align: middle;
    background: #fff;
}

.tn-bang tr > :first-child[b-ct0fciba6d] {
    border-left: 1px solid #000;
}

.tn-bang thead tr > *[b-ct0fciba6d] {
    border-top: 1px solid #000;
}

/* Hàng đầu (thứ + ngày) dính trên, hàng cuối (số người trực) dính dưới, cột HỌ TÊN dính trái. */
.tn-bang thead th[b-ct0fciba6d] {
    position: sticky;
    top: 0;
    z-index: 2;
    text-align: center;
    font-weight: 700;
}

.tn-bang tfoot th[b-ct0fciba6d],
.tn-bang tfoot td[b-ct0fciba6d] {
    position: sticky;
    bottom: 0;
    z-index: 2;
    font-weight: 700;
    text-align: center;
}

.tn-bang .c-ten[b-ct0fciba6d] {
    position: sticky;
    left: 0;
    z-index: 1;
    text-align: left;
    min-width: 10rem;
    max-width: 14rem;
}

.tn-bang thead th.c-ten[b-ct0fciba6d],
.tn-bang tfoot th.c-ten[b-ct0fciba6d] {
    z-index: 3;
}

/* Đầu cột ngày: thứ ở trên (chữ đầu viết hoa), ngày ở dưới — hai dòng gọn để 30–35 cột vừa khung. */
.tn-bang thead th.c-ngay[b-ct0fciba6d] {
    min-width: 2.6rem;
    padding: .15rem .2rem;
    line-height: 1.15;
}

.tn-thu[b-ct0fciba6d],
.tn-ngay[b-ct0fciba6d] {
    font-size: .78rem;
    white-space: nowrap;
}

.tn-ngay[b-ct0fciba6d] {
    font-weight: 400;
}

.tn-kip[b-ct0fciba6d] {
    font-family: system-ui, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    font-size: .72rem;
    font-weight: 600;
    color: #0a3d91;
}

.tn-ten[b-ct0fciba6d] {
    white-space: normal;
    line-height: 1.15;
}

/* Số điện thoại ngay DƯỚI TÊN — nhỏ hơn tên một nấc, màu dịu, chữ thường. */
.tn-so[b-ct0fciba6d] {
    font-size: .78rem;
    font-weight: 400;
    color: #4a5260;
    white-space: nowrap;
}

.o-tick[b-ct0fciba6d] {
    text-align: center;
    font-weight: 700;
    font-size: 1.05rem;
    min-width: 2.2rem;
}

.c-so[b-ct0fciba6d],
.c-so-nguoi[b-ct0fciba6d] {
    text-align: center;
    white-space: nowrap;
    font-weight: 700;
}

.c-so[b-ct0fciba6d] {
    min-width: 4.5rem;
}

.tn-bang thead th.c-so[b-ct0fciba6d] {
    white-space: normal;
    max-width: 5rem;
}

/* Ngày nghỉ / lễ — cùng bảng màu với tờ theo cột, tô theo CỘT (mỗi ô của cột ngày ấy); ngày lễ đậm hơn CN. */
.tn-bang .t7[b-ct0fciba6d] {
    background: #f3f5f8;
}

.tn-bang .cn[b-ct0fciba6d] {
    background: #fde7e7;
}

.tn-bang .ngay-le[b-ct0fciba6d] {
    background: #fbd5d0;
}

.tn-bang thead th.ngay-le[b-ct0fciba6d] {
    color: #8a1c12;
}

/* Ô có dấu trực — nền xanh nhạt để liếc là thấy hai nửa đội so le; độ đặc hiệu cao hơn nền cột nghỉ nên luôn thắng. */
.tn-bang .o-tick.co[b-ct0fciba6d] {
    background: #dcefe0;
    color: #14532d;
}

.tn-bang .o-tick.sua-duoc[b-ct0fciba6d] {
    cursor: pointer;
}

.tn-bang .o-tick.sua-duoc:hover[b-ct0fciba6d] {
    background: #eef4ff;
}

.tn-bang .o-tick.co.sua-duoc:hover[b-ct0fciba6d] {
    background: #c7e5ce;
}

/* Chú giải dấu sao ngày lễ — ngay dưới bảng, ra giấy cùng tờ. */
.to-chu-giai[b-ct0fciba6d] {
    margin: .35rem 0 0;
    font-size: .85rem;
}

/* ── Chân tờ ───────────────────────────────────────────────────────────────── */
.to-chan[b-ct0fciba6d] {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 1rem;
}

.to-noi-nhan[b-ct0fciba6d] {
    font-size: .9rem;
}

.to-ky[b-ct0fciba6d] {
    text-align: center;
    min-width: 16rem;
}

.to-nhac[b-ct0fciba6d] {
    margin-top: .75rem;
    font-size: .85rem;
    color: #8a5a00;
    font-family: system-ui, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}

/* ═══ VÙNG CHẠM NGÓN TAY — ô bấm được đủ 44px khi dùng cảm ứng (biến chung --cham-toi-thieu) ═══ */
@media (pointer: coarse) {
    .tn-bang .o-tick.sua-duoc[b-ct0fciba6d] {
        height: var(--cham-toi-thieu, 44px);
        min-width: var(--cham-toi-thieu, 44px);
    }
}

/* ═══ KHỔ ĐIỆN THOẠI — lưới GIỮ NGUYÊN (nó chính là tờ lịch), chỉ thu đệm + đầu tờ về một cột ═══
   ⛔ Đừng hoá thẻ như tờ theo cột: 17 người × 30 ngày hoá thẻ là 17 thẻ mỗi thẻ 30 dòng — mất đúng thứ lưới
   nói được (hai nửa đội so le). Bảng cuộn ngang trong khung, cột HỌ TÊN dính trái. */
@media (max-width: 576px) {
    .to-giay[b-ct0fciba6d] {
        padding: .9rem .6rem .75rem;
    }

    .to-dau[b-ct0fciba6d] {
        flex-direction: column;
        gap: .6rem;
    }

    .to-tieu-de[b-ct0fciba6d] {
        font-size: 1.05rem;
        margin-top: .85rem;
    }

    .tn-bang[b-ct0fciba6d] {
        font-size: .85rem;
    }

    .tn-bang .c-ten[b-ct0fciba6d] {
        min-width: 7.5rem;
        max-width: 9.5rem;
    }

    .to-chan[b-ct0fciba6d] {
        flex-direction: column;
        gap: .9rem;
    }

    .to-ky[b-ct0fciba6d] {
        min-width: 0;
    }
}

/* ═══ BẢN IN ═══════════════════════════════════════════════════════════════════
   ⚠ Bảng in ⛔ được cuộn, ⛔ được dính: khối cuộn khi in bị cắt cụt ở mép giấy. Khổ A4 NGANG do thẻ style trong
   ToLichTrucTheoNguoiXem.razor đặt (chỉ sống khi tờ này đang mở). Chữ và đệm thu nhỏ để 30–35 cột ngày + cột họ
   tên nằm gọn bề ngang một trang. Nền bỏ hết, dấu ✓ giữ nguyên. */
@media print {
    .khong-in[b-ct0fciba6d] {
        display: none !important;
    }

    .to-giay[b-ct0fciba6d] {
        border: 0;
        padding: 0;
        font-size: 8pt;
    }

    .tn-cuon[b-ct0fciba6d] {
        overflow: visible;
        max-height: none;
    }

    .tn-bang thead th[b-ct0fciba6d],
    .tn-bang tfoot th[b-ct0fciba6d],
    .tn-bang tfoot td[b-ct0fciba6d],
    .tn-bang .c-ten[b-ct0fciba6d] {
        position: static;
    }

    .tn-bang thead[b-ct0fciba6d] {
        display: table-header-group;
    }

    .tn-bang tr[b-ct0fciba6d] {
        break-inside: avoid;
    }

    .tn-bang th[b-ct0fciba6d],
    .tn-bang td[b-ct0fciba6d] {
        padding: .05rem .12rem;
    }

    .tn-bang .c-ten[b-ct0fciba6d] {
        min-width: 0;
        max-width: none;
    }

    .tn-bang thead th.c-ngay[b-ct0fciba6d],
    .o-tick[b-ct0fciba6d],
    .c-so[b-ct0fciba6d] {
        min-width: 0;
    }

    .tn-thu[b-ct0fciba6d],
    .tn-ngay[b-ct0fciba6d],
    .tn-so[b-ct0fciba6d] {
        font-size: 7pt;
    }

    .o-tick[b-ct0fciba6d] {
        font-size: 9pt;
    }

    .tn-bang .t7[b-ct0fciba6d],
    .tn-bang .cn[b-ct0fciba6d],
    .tn-bang .ngay-le[b-ct0fciba6d],
    .tn-bang .o-tick.co[b-ct0fciba6d] {
        background: transparent !important;
    }

    .tn-bang thead th.ngay-le[b-ct0fciba6d] {
        color: inherit;
    }

    .tn-bang .o-tick.sua-duoc[b-ct0fciba6d] {
        cursor: default;
    }

    .to-chan[b-ct0fciba6d] {
        break-inside: avoid;
    }
}
/* /Components/Pages/QuanLyPhong/ToLichTrucXem.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 2016 (việc f51, gộp f49) — TỜ LỊCH TRỰC ĐÚNG HÌNH TỜ GIẤY: MÀN HÌNH + BẢN IN A4 DỌC ═══

   🔒 Anh Tiến 05/09/2026: "lịch trực bản trên web phải làm giống bản in này" · "bỏ nút in bản đẹp vì
      sai form". Viền đen mảnh, phông có chân, đầu tờ hai cột — y tờ giấy phòng đang ký gửi 5 nơi.

   ⚠ CSS SCOPED (`.razor.css`): mọi luật ở đây CHỈ chạm tới thẻ của ToLichTrucXem.razor. Muốn giấu thẻ
     soạn lịch + dải nút lúc in thì luật phải ở TrangLichTruc.razor.css (trang sở hữu các thẻ ấy). Ngược
     lại, lớp `khong-in` dùng TRONG tờ này (câu nhắc thiếu số) phải khai lại ở đây — luật của trang cha
     ⛔ không với xuống được. Đây là chỗ dễ mất một buổi nhất khi sửa CSS Blazor. */

.to-giay[b-m4ucvdyasi] {
    background: #fff;
    color: #000;
    border: 1px solid #d8dde5;
    padding: 1.25rem 1.5rem 1rem;
    max-width: 900px;
    margin: 0 auto;
    font-family: "Times New Roman", Times, serif, "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    font-size: .95rem;
}

/* ⭐ ĐỢT 2024 (o1308) — trang cha đang nạp lại lưới: tờ mờ đi, ô không bấm được; hộp thoại đang mở nằm NGOÀI
   .to-giay nên vẫn dùng bình thường (nút trong hộp tự khóa bằng cờ DangBan). */
.to-giay.dang-tai[b-m4ucvdyasi] {
    opacity: .55;
    pointer-events: none;
}

/* ── Đầu tờ: hai cột, cơ quan bên trái, quốc hiệu bên phải ─────────────────── */
.to-dau[b-m4ucvdyasi] {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    text-align: center;
}

.to-dau-trai[b-m4ucvdyasi],
.to-dau-phai[b-m4ucvdyasi] {
    flex: 1;
}

.dam[b-m4ucvdyasi] {
    font-weight: 700;
}

.gach-duoi[b-m4ucvdyasi] {
    text-decoration: underline;
}

.to-tieu-de[b-m4ucvdyasi] {
    text-align: center;
    font-weight: 700;
    font-size: 1.15rem;
    margin-top: 1rem;
}

.to-thang[b-m4ucvdyasi] {
    text-align: center;
    font-weight: 700;
    margin-bottom: .75rem;
}

/* ── Bảng ngày × vị trí ────────────────────────────────────────────────────── */
/* Bảng rộng phải CUỘN TRONG KHUNG CỦA NÓ, ⛔ không được đẩy cả trang trượt ngang. */
.to-cuon[b-m4ucvdyasi] {
    overflow-x: auto;
}

.to-bang[b-m4ucvdyasi] {
    width: 100%;
    border-collapse: collapse;
}

.to-bang th[b-m4ucvdyasi],
.to-bang td[b-m4ucvdyasi] {
    border: 1px solid #000;
    padding: .15rem .4rem;
    vertical-align: middle;
}

.to-bang thead th[b-m4ucvdyasi] {
    text-align: center;
    font-weight: 700;
}

.c-stt[b-m4ucvdyasi] {
    width: 2.5rem;
    text-align: center;
}

.c-ngay[b-m4ucvdyasi] {
    width: 11rem;
    white-space: nowrap;
}

/* ⭐ ĐỢT 2335 (việc o1906) — SỐ ĐIỆN THOẠI NẰM NGAY DƯỚI TÊN NGƯỜI TRỰC, ĐÃ BỎ CỘT SỐ RIÊNG.
   🔒 Anh Tiến 12/09/2026: «bỏ cột số điện thoại, đưa hẳn số điện thoại vào dưới tên người trực» — ô
      hai người thì cột số cũ in hai số chồng nhau, ⛔ biết số nào của ai.
   Mỗi người một khối «tên / số»: số nhỏ hơn tên một nấc (giữ đúng cỡ .85rem của cột số cũ), màu dịu,
   căn theo tên. Hai người trong một ô cách nhau một khoảng nhỏ để hai cặp tên-số ⛔ dính vào nhau. */
.nguoi-truc + .nguoi-truc[b-m4ucvdyasi] {
    margin-top: .3rem;
}

.so-truc[b-m4ucvdyasi] {
    font-size: .85rem;
    color: #4a5260;
}

.o-truc[b-m4ucvdyasi] {
    text-align: center;
    min-width: 8rem;
}

.o-truc.sua-duoc[b-m4ucvdyasi] {
    cursor: pointer;
}

.o-truc.sua-duoc:hover[b-m4ucvdyasi] {
    background: #eef4ff;
}

/* ═══ ⭐ ĐỢT 2217 (việc o1671) — NGÀY NGHỈ VÀ NGÀY LỄ ═══════════════════════════
   🔒 Anh Tiến 08/09/2026: *"lịch trực chưa nạp vào với ngày lễ và ngày nghỉ, kiểm tra cả bản in và
      bản excel"*. Trước đợt này chỉ có `tr.cn` (in đậm hai cột đầu), tức thứ Bảy và ngày lễ ⛔ không
      có một dấu hiệu nào trên tờ.
   🔑 CHIA HAI CHẾ ĐỘ, ⛔ đừng gộp:
     · TRÊN MÀN HÌNH — tô nền, vì đây là chỗ người ta XẾP lịch và cần liếc là thấy hàng nghỉ;
     · KHI IN — bỏ nền, chỉ giữ IN ĐẬM + chữ "(nghỉ lễ)" ở cột NGÀY. Câu ghi ở đây từ đợt 2016
       (*"tờ giấy thật ⛔ không tô màu"*) VẪN ĐÚNG và vẫn phải tuân: tờ đem đi ký chép đúng tờ giấy
       đã phát hành, mà máy in văn phòng cũng biến mọi nền ấy thành một vệt xám như nhau.
   ⚠ Lớp `cn` GIỮ NGUYÊN TÊN (LuatNgayNghiToLichTruc.LopCss) — đổi tên bên C# mà quên tệp này là hàng
     Chủ nhật MẤT DẤU trong im lặng. */
tr.cn .c-stt[b-m4ucvdyasi],
tr.cn .c-ngay[b-m4ucvdyasi],
tr.t7 .c-stt[b-m4ucvdyasi],
tr.t7 .c-ngay[b-m4ucvdyasi],
tr.ngay-le .c-stt[b-m4ucvdyasi],
tr.ngay-le .c-ngay[b-m4ucvdyasi] {
    font-weight: 700;
}

tr.t7[b-m4ucvdyasi] {
    background: #f3f5f8;
}

tr.cn[b-m4ucvdyasi] {
    background: #fde7e7;
}

/* Ngày lễ đậm hơn Chủ nhật — lễ RƠI VÀO chủ nhật vẫn phải phân biệt được với một chủ nhật thường. */
tr.ngay-le[b-m4ucvdyasi] {
    background: #fbd5d0;
}

tr.ngay-le .c-ngay[b-m4ucvdyasi] {
    color: #8a1c12;
}

/* ── Chân tờ: nơi nhận bên trái, chữ ký bên phải ───────────────────────────── */
.to-chan[b-m4ucvdyasi] {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 1rem;
}

.to-noi-nhan[b-m4ucvdyasi] {
    font-size: .9rem;
}

.to-ky[b-m4ucvdyasi] {
    text-align: center;
    min-width: 16rem;
}

.to-nhac[b-m4ucvdyasi] {
    margin-top: .75rem;
    font-size: .85rem;
    color: #8a5a00;
    font-family: system-ui, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}

/* ═══ BẢN IN ═══════════════════════════════════════════════════════════════════
   ⚠ Bảng in ⛔ KHÔNG được cuộn (`overflow: visible`): khối cuộn khi in bị cắt cụt ở mép giấy.
   Khổ A4 dọc là mặc định của trình duyệt; tờ giấy thật cũng dọc. */
@media print {
    .khong-in[b-m4ucvdyasi] {
        display: none !important;
    }

    .to-giay[b-m4ucvdyasi] {
        border: 0;
        padding: 0;
        max-width: none;
        font-size: 10.5pt;
    }

    .to-cuon[b-m4ucvdyasi] {
        overflow: visible;
    }

    /* Đầu bảng lặp lại ở MỌI trang giấy — sang trang 2 vẫn biết cột nào là cột nào. */
    .to-bang thead[b-m4ucvdyasi] {
        display: table-header-group;
    }

    .to-bang tr[b-m4ucvdyasi] {
        break-inside: avoid;
    }

    .o-truc.sua-duoc[b-m4ucvdyasi] {
        cursor: default;
    }

    .o-truc.sua-duoc:hover[b-m4ucvdyasi] {
        background: transparent;
    }

    .to-chan[b-m4ucvdyasi] {
        break-inside: avoid;
    }

    /* ⭐ ĐỢT 2217 (việc o1671) — BẢN IN BỎ NỀN NGÀY NGHỈ, GIỮ IN ĐẬM.
       🔑 Tờ giấy đã phát hành ⛔ không có màu (câu đã ghi từ đợt 2016), và máy in văn phòng biến cả
         ba nền trên thành một vệt xám như nhau ⇒ tô nền khi in vừa lệch tờ gốc vừa ⛔ không phân biệt
         được gì. Thông tin ⛔ KHÔNG mất: cột NGÀY vẫn in "thứ tư - ngày 2/9 (nghỉ lễ)", và hai cột
         đầu vẫn IN ĐẬM (quy tắc đậm nằm ngoài khối @media nên nó theo cả hai chế độ).
       ⚠ `!important` là cần: quy tắc nền ở trên cùng độ đặc hiệu, ⛔ không có nó thì thứ tự khai
         quyết định — mà khối in đứng sau nên trông như đã thắng, thực ra chỉ thắng nhờ may. */
    tr.t7[b-m4ucvdyasi],
    tr.cn[b-m4ucvdyasi],
    tr.ngay-le[b-m4ucvdyasi] {
        background: transparent !important;
    }

    tr.ngay-le .c-ngay[b-m4ucvdyasi] {
        color: inherit;
    }
}

/* ═══ ⭐ ĐỢT 2304 (việc o1842) — KHỔ ĐIỆN THOẠI: TỜ LỊCH HOÁ THẺ, MỖI NGÀY MỘT THẺ ═══
   🔒 Anh Tiến 10/09/2026, gửi ảnh tờ lịch trên điện thoại: "em làm lịch trực, tối ưu lại app điện
      thoại cho đẹp". Đo sống 375×812 trước khi sửa: khung cuộn 302px mà bảng rộng 598px ⇒ hai cột
      vị trí + cột số điện thoại nằm NGOÀI màn hình; đầu tờ hai cột 143px gãy chữ bốn dòng; chân tờ
      ép «Nơi nhận» còn một vệt hẹp vì ô chữ ký giữ min-width 16rem.
   🔑 Khuôn «khổ điện thoại 576px» (sổ BangTheKhoHepSoKhaiTests.KhuonKhac): STT + ngày là ĐẦU THẺ,
      mỗi vị trí trực một dòng «nhãn — tên», số điện thoại ngay dưới tên (⭐ ĐỢT 2335 việc o1906 bỏ
      dòng số điện thoại cuối thẻ cùng với cột số). Nhãn lấy từ data-nhan (tên vị
      trí của chính bộ vị trí) nên Tổ thống kê xe bồn năm vị trí vẫn đúng mà ⛔ không gõ chữ nào.
   ⚠ Ô vị trí TRỐNG vẽ một gạch ngang (lớp o-trong), ⛔ KHÔNG giấu: người xếp lịch phải bấm được vào
      chính ô trống ấy để mở hộp xếp người.
   ⛔ Chỉ trong khối max-width 576px: bản in khổ A4 rộng hơn thế nên ⛔ bao giờ khớp, tờ giấy vẫn là
      bảng như cũ; máy tính ⛔ đổi một pixel. */
@media (max-width: 576px) {
    .to-giay[b-m4ucvdyasi] {
        padding: .9rem .75rem .75rem;
    }

    .to-dau[b-m4ucvdyasi] {
        flex-direction: column;
        gap: .6rem;
    }

    .to-tieu-de[b-m4ucvdyasi] {
        font-size: 1.05rem;
        margin-top: .85rem;
    }

    .to-cuon[b-m4ucvdyasi] {
        overflow-x: visible;
    }

    .to-bang[b-m4ucvdyasi],
    .to-bang tbody[b-m4ucvdyasi] {
        display: block;
    }

    .to-bang thead[b-m4ucvdyasi] {
        display: none;
    }

    .to-bang tr[b-m4ucvdyasi] {
        display: grid;
        grid-template-columns: auto minmax(0, 1fr);
        margin-bottom: .6rem;
        border: 1px solid #c9ced6;
        border-radius: 10px;
        overflow: hidden;
        background: #fff;
    }

    .to-bang td[b-m4ucvdyasi] {
        border: 0;
        padding: .45rem .7rem;
        width: auto;
    }

    .to-bang td.c-stt[b-m4ucvdyasi],
    .to-bang td.c-ngay[b-m4ucvdyasi] {
        background: #eef1f5;
    }

    .to-bang td.c-stt[b-m4ucvdyasi] {
        min-width: 2.6rem;
        font-weight: 700;
    }

    .to-bang td.c-ngay[b-m4ucvdyasi] {
        white-space: normal;
        padding-left: 0;
    }

    .to-bang td[data-nhan][b-m4ucvdyasi] {
        grid-column: 1 / -1;
        display: grid;
        grid-template-columns: 7.2rem minmax(0, 1fr);
        column-gap: .6rem;
        align-items: center;
        min-width: 0;
        text-align: left;
        white-space: normal;
        font-size: inherit;
        border-top: 1px solid #e3e6ea;
    }

    .to-bang td[data-nhan][b-m4ucvdyasi]::before {
        content: attr(data-nhan);
        font-family: system-ui, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
        font-size: .75rem;
        font-weight: 600;
        letter-spacing: .02em;
        text-transform: uppercase;
        color: #5b6472;
    }

    /* ⭐ ĐỢT 2335 (việc o1906): mỗi khối «tên / số» của một người nằm ở cột giá trị của thẻ; ô hai người
       thì hai khối xếp chồng dưới cùng một nhãn vị trí. */
    .to-bang td.o-truc > .nguoi-truc[b-m4ucvdyasi] {
        grid-column: 2;
    }

    .to-bang td.o-truc.o-trong[b-m4ucvdyasi]::after {
        content: "—";
        color: #9aa1ab;
    }

    .o-truc.sua-duoc[b-m4ucvdyasi] {
        min-height: var(--cham-toi-thieu, 44px);
    }

    /* Ngày nghỉ / lễ: chỉ tô ĐẦU THẺ, thân thẻ để trắng cho tên dễ đọc. */
    tr.t7[b-m4ucvdyasi],
    tr.cn[b-m4ucvdyasi],
    tr.ngay-le[b-m4ucvdyasi] {
        background: #fff;
    }

    tr.t7 .c-stt[b-m4ucvdyasi],
    tr.t7 .c-ngay[b-m4ucvdyasi] {
        background: #e6e9ee;
    }

    tr.cn .c-stt[b-m4ucvdyasi],
    tr.cn .c-ngay[b-m4ucvdyasi] {
        background: #fde7e7;
    }

    tr.ngay-le .c-stt[b-m4ucvdyasi],
    tr.ngay-le .c-ngay[b-m4ucvdyasi] {
        background: #fbd5d0;
    }

    .to-chan[b-m4ucvdyasi] {
        flex-direction: column;
        gap: .9rem;
    }

    .to-ky[b-m4ucvdyasi] {
        min-width: 0;
    }
}
/* /Components/Pages/QuanLyPhong/TrangLichTruc.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 1975 (việc o1252) — TRANG LỊCH TRỰC: PHẦN KHÔNG RA GIẤY KHI IN ═══

   🔒 Anh Tiến 04/09/2026: "lịch trực không cần tuân thủ theo form gốc, em đồ họa lại cho đẹp,
      bản tải xuống và in ra cũng cần đẹp".

   🔑 VÌ SAO LUẬT NÀY PHẢI Ở ĐÂY chứ ⛔ không ở ToLichTrucDepXem.razor.css: CSS scoped của Blazor
      gắn một mã riêng cho từng component, nên luật khai trong component CON ⛔ không bao giờ chạm
      tới thẻ của trang CHA. Muốn giấu thẻ soạn lịch + dải nút lúc in thì luật phải nằm ở trang
      sở hữu chính những thẻ ấy. Đây là chỗ dễ mất cả buổi nhất khi sửa CSS Blazor.

   ⚠ `!important` là CẦN ở đây: `.card` của Bootstrap có `display: flex` với độ đặc hiệu ngang
     ngửa, mà thứ tự nạp giữa bundle scoped và bootstrap ⛔ không được bảo đảm. */

@media print {
    .khong-in[b-5cc52g7qlv] {
        display: none !important;
    }
}

/* ─────────── ⭐ THỨ IN DƯỚI SỐ NGÀY Ở ĐẦU CỘT (đợt 1975 · dời chỗ ở việc o1397) ───────────
   Trước việc o1397, ba thuộc tính này nằm trong `style=` NỘI TUYẾN của `TrangLichTruc.razor`.
   Style nội tuyến thắng mọi luật CSS (trừ `!important`) ⇒ sàn cỡ chữ của `@media (pointer: coarse)`
   ⛔ không bao giờ với tới được, mà build xanh và ⛔ không một câu lỗi nào (bài học đợt 1979). */
.tlt-thu-ngay[b-5cc52g7qlv] {
    font-size: .65rem;
    font-weight: 400;
    opacity: .7;
}

/* 👆 Sàn cỡ chữ 12px khi cầm bằng ngón tay — ngưỡng đọc từ biến `--cham-chu-toi-thieu` của
   `app.css` (đợt 1518), ⛔ đừng gõ thẳng 12px.
   📏 ⛔ Không nong cột: đầu cột đã đặt `min-width: 32px`, mà chuỗi thứ chỉ có HAI ký tự
      ("T2" … "CN") — ở 12px hai ký tự ≈ 16px, còn dư nửa cột. */
@media (pointer: coarse) {
    .tlt-thu-ngay[b-5cc52g7qlv] { font-size: max(.65rem, var(--cham-chu-toi-thieu)); }
}

/* ═══ ⭐ ĐỢT 2304 (việc o1842) — DẢI CHỌN KỲ + BA NÚT Ở KHỔ ĐIỆN THOẠI ═══
   🔒 Anh Tiến 10/09/2026, gửi ảnh trang này trên điện thoại: "em làm lịch trực, tối ưu lại app điện
      thoại cho đẹp". Đo sống 375×812 trước khi sửa: ô phòng 300px hụt mép phải 52px · hàng Tháng ·
      Năm · Xem hụt 56px · hàng hai nút hụt 21px · nút ⚙ đứng MỘT MÌNH một hàng — bốn hàng, bốn bề ngang.
   🔑 Máy tính ⛔ đổi một pixel: ba bề ngang cũ của ô chọn chỉ DỜI từ style nội tuyến về ba lớp dưới
      đây (nội tuyến thắng mọi luật CSS nên khối điện thoại ⛔ bao giờ nới được — bài học đợt 2285).
   📱 Khổ ≤576px vẫn là dải flex cũ, chỉ đặt order + bề ngang cơ sở: hàng 1 ô phòng + nút ⚙ thu về
      một ô vuông · hàng 2 Tháng · Năm · Xem chia ba · hàng 3 Xuất Excel · In. Mọi hàng kín hai mép.
   ⚠ Nút ⚙ mang lớp ms-auto (lề trái tự động, cầm !important) cho máy tính. Ở khổ này ô phòng có
      flex-grow nên ăn hết chỗ trống TRƯỚC, lề tự động còn 0 — ⛔ đừng đi gỡ lớp ấy khỏi markup. */
.tlt-o-phong[b-5cc52g7qlv] {
    max-width: 300px;
}

.tlt-o-thang[b-5cc52g7qlv] {
    width: 110px;
}

.tlt-o-nam[b-5cc52g7qlv] {
    width: 100px;
}

/* ⭐ ĐỢT 2820 (việc o1907) — hai ô «Từ ngày» – «Đến ngày»: nhãn chữ đứng cạnh ô <ONgayCoThe> (khung 150px khai ở
   app.css). Khổ điện thoại xếp nhãn lên trên ô — xem khối max-width 576px ngay dưới. */
.tlt-o-ngay[b-5cc52g7qlv] {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
}

.tlt-nhan-ngay[b-5cc52g7qlv] {
    white-space: nowrap;
    font-size: .875rem;
}

@media (max-width: 576px) {
    .tlt-o-phong[b-5cc52g7qlv] {
        order: 1;
        flex: 1 1 calc(100% - var(--cham-toi-thieu, 44px) - .5rem);
        max-width: none;
    }

    /* Thu về một ô vuông: chữ «⚙ Cấu hình» vẫn nằm trong nút cho máy đọc màn hình, chỉ biểu tượng vẽ
       bằng ::before. ⛔ Đừng thêm title — luật ghi chú 02/09 chỉ cho title ở nút ẩn chữ, mà điện thoại
       ⛔ có rê chuột để đọc nó. */
    .tlt-nut-cau-hinh[b-5cc52g7qlv] {
        order: 2;
        flex: 0 0 var(--cham-toi-thieu, 44px);
        position: relative;
        padding-inline: 0;
    }

    .tlt-nut-cau-hinh[b-5cc52g7qlv]::before {
        content: "⚙";
        font-size: 1.25rem;
        line-height: 1;
    }

    .tlt-chu-cau-hinh[b-5cc52g7qlv] {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    /* ⭐ ĐỢT 2820 (việc o1907) — nút Xem đã gỡ (đổi ô là tự nạp) ⇒ hàng 2 còn Tháng · Năm chia đôi; hàng 3 là
       hai ô ngày chia đôi, nhãn đứng TRÊN ô cho khỏi hụt bề ngang; hàng 4 Tải xuống · In. */
    .tlt-o-thang[b-5cc52g7qlv],
    .tlt-o-nam[b-5cc52g7qlv] {
        flex: 1 1 40%;
    }

    .tlt-o-thang[b-5cc52g7qlv] { order: 3; }
    .tlt-o-nam[b-5cc52g7qlv] { order: 4; }

    .tlt-o-ngay[b-5cc52g7qlv] {
        flex: 1 1 40%;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
    }

    .tlt-o-ngay[b-5cc52g7qlv]  .o-ngay-boc {
        width: 100%;
    }

    .tlt-o-tu-ngay[b-5cc52g7qlv] { order: 5; }
    .tlt-o-den-ngay[b-5cc52g7qlv] { order: 6; }

    .tlt-nut-excel[b-5cc52g7qlv],
    .tlt-nut-in[b-5cc52g7qlv] {
        flex: 1 1 auto;
    }

    .tlt-nut-excel[b-5cc52g7qlv] { order: 7; }
    .tlt-nut-in[b-5cc52g7qlv] { order: 8; }
}
/* /Components/Pages/QuanTri/BanDoPhanQuyen.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 2475 (việc o1859) — BẢN ĐỒ PHÂN QUYỀN Ở KHỔ ĐIỆN THOẠI + VÙNG CHẠM ═══

   🔴 Mọi luật vùng chạm nằm TRONG câu media «pointer: coarse»: máy chuột ⛔ đổi một pixel (luật đợt 1518).
     Khổ hẹp ⛔ có nghĩa là ngón tay — thứ quyết định vùng bấm là CÁI CHẠM VÀO, ⛔ phải bề ngang.
   ⓘ Nút .btn của trang (nút tick «Cấp quyền» / «Được xem» ở tab Quyền theo trang, nút «Sửa quyền này») ĐÃ đủ
     44px nhờ luật chung của .btn trong app.css (khối vùng chạm, mục ②) — tệp này ⛔ lặp lại luật ấy.
   ⚠ Ngưỡng đọc từ biến --cham-toi-thieu, ⛔ gõ số 44px vào đây. */

@media (pointer: coarse) {

    /* ① DÒNG TIÊU ĐỀ KHỐI PHÂN HỆ — chỗ bấm mở/gấp khối, cao đủ một ngón tay. Giữ nguyên kiểu hiển thị
       mặc định của thẻ summary để còn dấu tam giác mở/gấp; min-height chứ ⛔ height (tên phân hệ dài xuống
       hai dòng thì cứ để nó cao hơn). */
    summary[b-o11u4n5bfj] {
        min-height: var(--cham-toi-thieu);
    }

    /* ② Ô TICK «Chỉ dòng Không» — khuôn kiểu ③ (div bọc input và label là hai anh em) của
       TrangBangGiaMucXdcb.razor.css đợt 1619: hộp inline-flex cao đủ ngưỡng, nhãn kéo sang trái 1,5em rồi
       trả lại bằng phần đệm cùng cỡ để PHỦ TRỌN cột ô tick (bấm quanh ô vuông là trúng nhãn nối bằng for).
       ⚠ ⛔ áp cho công tắc form-switch: Bootstrap dựng nó bằng lề 2,5em nên con số 1,5em sẽ lệch. */
    .form-check:not(.form-switch)[b-o11u4n5bfj] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    .form-check:not(.form-switch) .form-check-label[b-o11u4n5bfj] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -1.5em;
        padding-left: 1.5em;
    }
}

@media (max-width: 576px) {

    /* ③ ĐƯỜNG DẪN DÀI trong cột đầu của bảng — cho xuống dòng ngay trong ô thay vì kéo cả bảng cuộn
       ngang (đường dẫn như /muc-in/photo/xdcb/may/{Id} dài hơn nửa khung 390px). */
    code[b-o11u4n5bfj] {
        word-break: break-all;
    }
}
/* /Components/Pages/QuanTri/CauHinhMailPage.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — 8 Ô CÔNG TẮC CỦA TRANG CẤU HÌNH MAIL (đợt 1672, việc o962) ═══

   Khối luật chạm dùng chung ở `wwwroot/app.css` (đợt 1518/1521) CỐ Ý để `.form-check-input` đứng
   ngoài, và chính nó ghi lý do: nâng chính ô vuông lên 44px thì ô tick to đúng bằng cả dòng chữ
   bên cạnh, trông như lỗi giao diện. Thứ phải nới là VÙNG CHẠM QUANH ô, mà vùng ấy mỗi màn hình
   một khác nên phải nới tại chỗ, bằng CSS cách ly.

   🔴 ĐÂY LÀ CA `form-switch` ĐẦU TIÊN CỦA DỰ ÁN — HAI HẰNG SỐ BOOTSTRAP KHÁC HẲN Ô TICK THƯỜNG.
     Sáu tệp vùng chạm làm trước (đợt 1518 → 1627) đều là ô tick vuông: Bootstrap cho hộp
     `padding-left: 1.5em` và cho ô `margin-left: -1.5em`. Công tắc thì đọc được ngay trong
     `wwwroot/bootstrap/bootstrap.min.css`: `.form-switch { padding-left: 2.5em }` và
     `.form-switch .form-check-input { width: 2em; margin-left: -2.5em }`.
     ⇒ Chép nguyên cặp 1,5em của khuôn cũ sang đây là nhãn CHỈ phủ được 1,5em trong khi cột công
       tắc rộng 2em: còn hở đúng 1em ngay cạnh ô, mà mọi bài kiểm vẫn xanh và màn hình máy tính
       vẫn y nguyên. Đúng họ đèn xanh giả mà `VungChamNgonTayTests` đã ghi cho kiểu ③.

   📏 ĐO SỐNG TRƯỚC KHI VÁ — 01/09/2026, Chrome thật, khung 390×844 cảm ứng:
     node cong-cu/do-song-o-tick-cham.mjs --trang /quan-tri/mail
     8 ô CÔNG TẮC hiện trên màn hình (2 ô cố định + 6 ô sinh ra từ vòng lặp loại thông báo), cả 8
     cho CÙNG một bộ số: cấu trúc ③ hai anh em · ô vuông 32×16 · nhãn cao 24 · hộp `.form-check`
     cao 24 (display block) · VÙNG BẤM THẬT tại cột ô tick 17px · tại cột chữ 25px.
     ⇒ Cả hai cột đều dưới xa ngưỡng 44px. Hộp `div.form-check` bọc ngoài ⛔ KHÔNG bấm được, nên
       nâng riêng cái nhãn cũng ⛔ không cứu được cột công tắc — phải kéo nhãn trùm sang cột ấy.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width` — khổ hẹp KHÔNG có nghĩa là ngón tay
     (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, ⛔ không một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết câu media ghép bằng chữ `and` trong tệp
     `.razor.css`: bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (đợt 1481).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo. Cặp ký tự đó lọt vào GIỮA câu văn là chú
     thích đóng sớm, phần chữ còn lại dính vào bộ chọn ngay dưới và trình duyệt bỏ nguyên luật đó
     — ⛔ không một câu lỗi nào. Vì thế ở đây gọi tên cặp ký tự ấy bằng CHỮ, ⛔ không gõ ra.
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ① HỘP BỌC `div.form-check.form-switch` — nâng sàn chiều cao và đổi sang hộp flex để công tắc
       thôi dính mép trên: trong hộp flex thuộc tính `float` bị BỎ QUA, nên ô nhận phép canh giữa
       dọc như phần chữ bên cạnh.

       🔑 BỀ NGANG KHÔNG ĐỔI — lề âm Bootstrap ăn khớp đúng với phần đệm trái của hộp: hộp mở đầu
         ở 2,5em · công tắc mang lề trái −2,5em nên khung của nó nằm ở 0…2em (Y HỆT chỗ cũ) · bề
         rộng ngoài vì thế là −0,5em, cộng `gap` 0,5em ⇒ nhãn bắt đầu đúng ở 2,5em, cũng Y HỆT chỗ
         cũ. Con số 0,5em của `gap` là thứ giữ khe hở giữa công tắc và chữ; bỏ nó là chữ dính sát.
       ⚠ KHÔNG đụng `padding-left` của Bootstrap — chính phần đệm ấy giữ bề ngang.
       ⚠ `flex` chứ ⛔ KHÔNG `inline-flex` — ngược hẳn với `TrangCongToCongNghe` và
         `TrangBangGiaMucXdcb`. Sáu ô loại thông báo ở cuối trang là SÁU THẺ ANH EM LIỀN NHAU
         trong cùng một hộp; hoá `inline-flex` là chúng thành hộp mức inline và DỒN LÊN CÙNG MỘT
         DÒNG khi còn chỗ — danh sách đang mỗi loại một dòng bỗng vỡ thành bảng chữ chạy ngang.
         Hai tệp kia phải dùng `inline-flex` vì ô của chúng nằm trong một hàng công cụ xếp ngang;
         ở đây thì ngược lại. ⇒ Đây đúng câu mà việc o962 đã dặn: ⛔ KHÔNG có một luật dùng chung
         nào giải được, chọn `flex` hay `inline-flex` phụ thuộc bố cục quanh ô ở CHÍNH màn hình.
       ⚠ `min-height` chứ ⛔ không `height`: nhãn nào chữ dài phải xuống hai dòng thì cứ để nó cao
         hơn; đặt `height` là bóp lại, sửa một lỗi lại đẻ một lỗi. */
    .form-check[b-h2u6q49rzg] {
        min-height: var(--cham-toi-thieu);
        display: flex;
        align-items: center;
        gap: .5em;
    }

    /* ② NHÃN CHỮ — vùng bấm thật (nối với công tắc bằng thuộc tính `for`, bấm vào nhãn là gạt).
       `align-self: stretch` là dòng ⛔ KHÔNG ĐƯỢC BỎ: hộp cha canh giữa các phần con, nên để nhãn
       tự co theo chiều cao con chữ thì vùng bấm thật chỉ còn ~24px trong khi hộp đã 44px — đúng
       kiểu đèn xanh giả mà ô đột biến số 23 của đợt 1619 đã lọt qua.

       🔴 CẶP `margin-left` + `padding-left` DÙNG 2,5em, ⛔ KHÔNG PHẢI 1,5em NHƯ SÁU TỆP TRƯỚC.
         Nhãn bắt đầu SAU công tắc (hộp có đệm trái 2,5em, công tắc nằm ở 0…2em nhờ lề âm), nên
         khoảng NGAY TRÊN và NGAY DƯỚI công tắc ⛔ không thuộc nhãn mà thuộc `div.form-check` —
         một thẻ `div` ⛔ KHÔNG bấm được. Kéo nhãn sang trái đúng 2,5em rồi trả lại bằng phần đệm
         cùng cỡ: nhãn PHỦ TRỌN cột công tắc mà dòng chữ vẫn đứng nguyên chỗ cũ ở 2,5em.
       ⚠ Hai con số 2,5em là hằng số của Bootstrap cho `.form-switch` (`padding-left: 2.5em` và
         `.form-switch .form-check-input { margin-left: -2.5em }`) — nâng đời Bootstrap thì soi
         lại đúng hai luật ấy rồi sửa cả hàng rào, ⛔ đừng đoán.
       ⚠ Nhãn đứng SAU công tắc trong markup nên nó vẽ ĐÈ lên. Điều đó ⛔ không làm mất cú bấm:
         bấm vào nhãn là trình duyệt chuyển thẳng cho ô nối bằng `for`.
       ⚠ Ngưỡng nằm ở `.form-check` bên trên (biến `--cham-toi-thieu`), ⛔ đừng gõ lại số ở đây. */
    .form-check-label[b-h2u6q49rzg] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -2.5em;
        padding-left: 2.5em;
    }
}
/* /Components/Pages/QuanTri/ChanDoanAD.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — Ô TICK «Gửi đúng tên nguyên văn» (việc o1276, 05/09/2026) ═══

   📏 ĐO SỐNG TRƯỚC KHI VÁ — 05/09/2026, Chrome thật, khung 390×844 cảm ứng, `tienvm`,
      `node cong-cu/do-song-o-tick-cham.mjs --trang quan-tri/chan-doan-ad` (hỏi thẳng
      `document.elementFromPoint`, ⛔ không đọc CSS):
        · KIỂU ③ «hai anh em»: `div.form-check.form-check-inline` chứa `input` và `label[for]` —
          cái `div` ⛔ KHÔNG bấm được;
        · ô vuông 16×16 (float: left) · nhãn cao 21 · hộp `.form-check` cao 24 (display: block);
        · VÙNG BẤM THẬT: tại cột ô tick **17px** · tại cột chữ **22px** · lệch tâm dọc −0,5px.

   🔑 KHUÔN KIỂU ③ CHÉP NGUYÊN TỪ `Components/Pages/MucIn/TrangBangGiaMucXdcb.razor.css` (đợt 1619,
      việc o905) — lý lẽ từng dòng nằm ở tệp ấy, ⛔ không chép lại đây. Tóm: hộp cha cao 44px thôi
      chưa đủ — khoảng trống trên/dưới ô vuông thuộc `div` nên ⛔ không bấm được; nhãn kéo sang trái
      1,5em để PHỦ TRỌN cột ô tick.
   ⚠ `form-check-inline` của Bootstrap đặt `display: inline-block` (0,1,0); luật cách ly ở đây biên
      dịch ra (0,2,0) nên `inline-flex` thắng — ⛔ đừng thêm `!important`.
   ⚠ `inline-flex` chứ ⛔ không `flex`: ô này đứng cạnh ô gõ tên trong một hàng `d-flex`, hoá `flex`
      là nó xuống dòng riêng.
   🔴 Điều kiện là `pointer: coarse`, ⛔ không `max-width`; MỘT điều kiện mỗi câu media (bài học 1481).
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu`, ⛔ không gõ số 44px vào đây.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — ⛔ đừng gõ cặp ấy vào giữa câu văn (đợt 852).
   🚧 Hàng rào: TKNB.Tests/VungChamPhanTrangVaThongKeTests.cs
   📏 Đo lại: node cong-cu/do-song-o-tick-cham.mjs --trang quan-tri/chan-doan-ad   (chốt ngược: --chuot) */

@media (pointer: coarse) {
    .form-check[b-299ty2zea8] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    .form-check-label[b-299ty2zea8] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -1.5em;
        padding-left: 1.5em;
    }
}
/* /Components/Pages/QuanTri/Mail/MaTranMail.razor.rz.scp.css */
/* ═══ MA TRẬN MAIL THÔNG BÁO — anh Tiến giao 10/09/2026 ═══
   Bảng có 9 cột (người · địa chỉ thư · nhận mail · 6 loại) và một ô NHẬP trên mỗi dòng, nên nó cuộn
   ngang ở khổ hẹp. Hai thứ phải neo lại thì bảng mới đọc được:
     · cột NGƯỜI neo bên trái — cuộn sang cột loại mà mất tên thì mọi ô tick thành vô nghĩa;
     · hàng TIÊU ĐỀ neo bên trên — cuộn xuống dòng thứ 30 mà mất tên loại cũng vậy.
   ⚠ Khung cuộn phải có TRẦN CHIỀU CAO, không thì `position: sticky` của hàng tiêu đề ⛔ không có gì để
     bám: nó neo theo khung cuộn gần nhất, mà khung ⛔ không cuộn thì neo cũng ⛔ không xảy ra. */

.mtm-khung[b-stjjpe1qh7] {
    max-height: 68vh;
    max-height: 68dvh; /* dòng lui cho điện thoại — luật đợt 1500 */
    overflow: auto;
}

.mtm-bang[b-stjjpe1qh7] {
    /* Cột phụ co theo nội dung, phần dôi ra dồn cho cột tên + cột địa chỉ thư (luật ① đợt 2154). */
    width: auto;
    min-width: 100%;
}

.mtm-bang thead th[b-stjjpe1qh7] {
    position: sticky;
    top: 0;
    z-index: 3;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
    vertical-align: bottom;
}

/* Cột NGƯỜI: cột to nhất của bảng, và là cột duy nhất được xuống dòng. */
.mtm-cot-ten[b-stjjpe1qh7] {
    position: sticky;
    left: 0;
    z-index: 2;
    min-width: 14rem;
    background-color: var(--bs-body-bg, #fff);
}

/* Góc trên bên trái vừa neo trái vừa neo trên ⇒ phải nổi trên cả hai. */
.mtm-bang thead th.mtm-cot-ten[b-stjjpe1qh7] {
    z-index: 4;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

/* Cột địa chỉ thư: đủ rộng để đọc trọn một địa chỉ cơ quan (ten.xy@petrolimex.com.vn ≈ 26 ký tự). */
.mtm-cot-mail[b-stjjpe1qh7] {
    min-width: 16rem;
}

.mtm-o-mail[b-stjjpe1qh7] {
    min-width: 15rem;
}

/* Cột NHẬN MAIL + 6 cột loại: hẹp hết mức, chữ ⛔ không xuống dòng. Tên loại nằm trong `title` của
   biểu tượng đầu cột (luật ② đợt 2154); nghĩa cột ✉ và ba ký hiệu ô không tick được nằm ở dòng chú
   giải dưới bảng (đợt 2309 — luật ghi chú #23). */
.mtm-cot-nhan[b-stjjpe1qh7],
.mtm-cot-loai[b-stjjpe1qh7] {
    width: 1%;
    white-space: nowrap;
}

.mtm-bieu[b-stjjpe1qh7] {
    font-size: 1.15rem;
}

.mtm-dau[b-stjjpe1qh7] {
    color: var(--bs-secondary-color, #6c757d);
}

/* Cột của một LOẠI đang tắt cho toàn hệ thống — làm mờ để người quản trị thấy ngay là tick ở đó
   ⛔ chưa có tác dụng gì. ⛔ Đừng ẩn hẳn cột: ẩn là giấu mất chính thứ cần bật lại. */
.mtm-loai-tat[b-stjjpe1qh7] {
    background-color: var(--bs-secondary-bg, #e9ecef);
    opacity: .75;
}

/* Vùng chạm cho ngón tay — chỉ ở thiết bị cảm ứng (luật đợt 1518: điều kiện là CÁI CHẠM VÀO,
   ⛔ không phải bề ngang màn hình). Ô tick nong bằng ĐỆM CỦA Ô, ⛔ không phóng to ô vuông:
   một ô tick 44px cạnh dòng chữ 15px trông như lỗi. */
@media (pointer: coarse) {
    .mtm-bang tbody td[b-stjjpe1qh7] {
        min-width: var(--cham-toi-thieu, 44px);
        height: var(--cham-toi-thieu, 44px);
    }

    .mtm-o-mail[b-stjjpe1qh7] {
        min-height: var(--cham-toi-thieu, 44px);
    }
}

/* CHỮ NỔI TỰ ẨN thay cho dải xanh "Đã ghi." — khuôn đợt 2281 của bảng phân quyền.
   🔑 `position: fixed` là điểm mấu chốt: một dải nằm trong luồng bố cục sẽ chen vào và ĐẨY CẢ BẢNG
   XUỐNG mỗi lượt tick, làm ô người ta vừa bấm trượt khỏi chỗ con trỏ đang đứng. */
.mtm-chu-noi[b-stjjpe1qh7] {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    max-width: 22rem;
    padding: .5rem .9rem;
    border-radius: .5rem;
    background: var(--bs-success-bg-subtle, #d1e7dd);
    color: var(--bs-success-text-emphasis, #0a3622);
    border: 1px solid var(--bs-success-border-subtle, #a3cfbb);
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
    pointer-events: none;
    animation: mtm-hien-b-stjjpe1qh7 .18s ease-out;
}

@keyframes mtm-hien-b-stjjpe1qh7 {
    from { opacity: 0; transform: translateY(.5rem); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .mtm-chu-noi[b-stjjpe1qh7] { animation: none; }
}

/* ─── Sọc dòng TỰ TÔ (việc o1875) ───
   Hàng mang lớp mtm-table-hang nên luật sọc chung của app.css CHỪA bảng này: luật ấy tô nền BÁN TRONG
   SUỐT lên mọi ô dòng chẵn, kể cả ô Họ tên dính, nên cuộn ngang là chữ và ô tick các cột trượt bên dưới
   LỘ XUYÊN qua tên (đo sống 11/09/2026: ô tên dòng chẵn ra rgba(0, 0, 0, .027)). Ở đây tô lại đúng độ đậm
   của luật chung (rgba .028 nền sáng · .045 nền tối); ô dính giữ nền ĐẶC, sọc vẽ bằng một lớp ảnh phủ lên.
   Khuôn chép từ MaTranChucNang.razor.css (cùng đợt). */
.mtm-bang > tbody > tr:nth-child(even) > td[b-stjjpe1qh7] {
    background-color: rgba(0, 0, 0, .028);
}

:root[data-theme="dark"] .mtm-bang > tbody > tr:nth-child(even) > td[b-stjjpe1qh7] {
    background-color: rgba(255, 255, 255, .045);
}

.mtm-bang > tbody > tr:nth-child(even) > td.mtm-cot-ten[b-stjjpe1qh7] {
    background-color: var(--bs-body-bg, #fff);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtm-bang > tbody > tr:nth-child(even) > td.mtm-cot-ten[b-stjjpe1qh7] {
    background-color: var(--bs-body-bg, #212529);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}

/* Cột loại đang tắt giữ nền mờ ở CẢ dòng chẵn — sọc phủ lên bằng lớp ảnh, ⛔ thay nền. */
.mtm-bang > tbody > tr:nth-child(even) > td.mtm-loai-tat[b-stjjpe1qh7] {
    background-color: var(--bs-secondary-bg, #e9ecef);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtm-bang > tbody > tr:nth-child(even) > td.mtm-loai-tat[b-stjjpe1qh7] {
    background-color: var(--bs-secondary-bg, #343a40);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}
/* /Components/Pages/QuanTri/NguoiDung/MaTranTaiKhoan.razor.rz.scp.css */
/* ═══ MA TRẬN TÀI KHOẢN — thân chính của /quan-tri/nguoi-dung (anh Tiến 10/09/2026, việc o1821) ═══
   Cùng khuôn với ma trận phân quyền (`PhanQuyen/MaTranQuyenApp.razor.css`): bảng rộng theo SỐ CỘT
   nên CỐ Ý cuộn ngang, và hai thứ phải neo thì bảng mới đọc được:
     · cột HỌ TÊN neo bên trái — cuộn tới cột «Quẹt cuối» mà mất tên thì mọi ô tick thành vô nghĩa;
     · hàng TIÊU ĐỀ neo bên trên — cuộn xuống dòng thứ 300 mà mất tên cột cũng vậy.
   ⚠ ⛔ KHÔNG phân trang (anh dặn: hiện hết, cho cuộn) ⇒ khung cuộn PHẢI có trần chiều cao: vừa để
     `position: sticky` của hàng tiêu đề có chỗ bám, vừa để trang ⛔ không dài bằng hơn 400 dòng.
   ⚠ Bảng mang thêm lớp `bang-dinh-dau` để luật khổ hẹp 820px của `app.css` (hoá mọi bảng thành một
     khối tự cuộn) CHỪA nó ra: bảng tự cuộn thì hàng tiêu đề neo theo chính bảng, mà bảng ⛔ không cuộn dọc
     ⇒ neo chết lặng, ⛔ không một câu lỗi nào.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo; cặp ký tự ấy lọt vào GIỮA câu văn là chú thích
     đóng sớm và trình duyệt bỏ nguyên luật ngay dưới. Vì thế ở đây gọi tên cặp ký tự ấy bằng CHỮ.
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs` (vùng chạm) · `MaTranTaiKhoanTests.cs` */

.mtt-khung[b-0y8tvocn87] {
    max-height: 68vh;
    max-height: 68dvh; /* dòng lui cho điện thoại — luật đợt 1500 */
    overflow: auto;
}

.mtt-bang[b-0y8tvocn87] {
    /* Cột phụ co theo nội dung, phần dôi ra dồn hết cho cột tên (luật ① đợt 2154). */
    width: auto;
    min-width: 100%;
}

.mtt-bang thead th[b-0y8tvocn87] {
    position: sticky;
    top: 0;
    z-index: 3;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
    vertical-align: bottom;
    white-space: nowrap;
}

/* Cột HỌ TÊN: cột to nhất và là cột DUY NHẤT được xuống dòng. Nền ô để Bootstrap tự vẽ
   (`--bs-table-bg` — luôn đục) nên dòng tài khoản đã khóa vẫn giữ màu xám của `table-secondary`. */
.mtt-cot-ten[b-0y8tvocn87] {
    position: sticky;
    left: 0;
    z-index: 2;
    min-width: 15rem;
}

/* Góc trên bên trái vừa neo trái vừa neo trên ⇒ phải nổi trên cả hai. */
.mtt-bang thead th.mtt-cot-ten[b-0y8tvocn87] {
    z-index: 4;
}

/* Cột phụ: hẹp hết mức, chữ ⛔ không xuống dòng. Tên đầy đủ của cột chỉ-biểu-tượng nằm trong `title`. */
.mtt-cot[b-0y8tvocn87],
.mtt-bang tbody td:not(.mtt-cot-ten)[b-0y8tvocn87] {
    width: 1%;
    white-space: nowrap;
}

.mtt-o-chon[b-0y8tvocn87] {
    display: inline-block;
    width: auto;
    max-width: 11rem;
}

.mtt-o-ngay[b-0y8tvocn87] {
    width: auto;
}

.mtt-dau[b-0y8tvocn87] {
    color: var(--bs-secondary-color, #6c757d);
}

/* CHỮ NỔI TỰ ẨN — cùng khuôn «Đã ghi.» của ma trận phân quyền (anh Tiến 10/09/2026).
   🔑 `position: fixed` là cả điểm mấu chốt: một dải báo nằm trong luồng bố cục thì mỗi lượt tick nó
   chen vào và ĐẨY CẢ BẢNG XUỐNG — ô vừa bấm trượt khỏi con trỏ, lượt tick sau rơi nhầm dòng.
   ⚠ `pointer-events: none` để chữ nổi ⛔ không nuốt cú bấm rơi trúng nó. */
.mtt-chu-noi[b-0y8tvocn87] {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    max-width: 24rem;
    padding: .5rem .9rem;
    border-radius: .5rem;
    background: var(--bs-success-bg-subtle, #d1e7dd);
    color: var(--bs-success-text-emphasis, #0a3622);
    border: 1px solid var(--bs-success-border-subtle, #a3cfbb);
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
    pointer-events: none;
    animation: mtt-hien-b-0y8tvocn87 .18s ease-out;
}

@keyframes mtt-hien-b-0y8tvocn87 {
    from { opacity: 0; transform: translateY(.5rem); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .mtt-chu-noi[b-0y8tvocn87] { animation: none; }
}

/* ═══ VÙNG CHẠM NGÓN TAY ═══
   Hai kiểu ô tick trong tệp này:
     · KIỂU ② — ô tick TRẦN trong ô bảng (quản trị · khóa · dự phòng AD · vô hiệu, cả ô bấm được lẫn ô
       MỜ của tài khoản kỹ thuật / dòng của chính mình), bọc bằng `label.o-tick-cham`. MỘT dòng mã
       trong `foreach` sinh ra hơn 400 ô trên màn hình thật.
     · KIỂU ③ — hai ô lọc có chữ («Ẩn tài khoản đã khóa» · «Chỉ người cần rà»): `div.form-check` chứa
       `input` và `label.form-check-label[for]` là hai anh em.
   ⚠ LUẬT NỀN `.o-tick-cham { display: contents }` phải đứng TRƯỚC khối chạm: ở chế độ CHUỘT thẻ bọc
     ⛔ không sinh hộp nào (một hộp dòng vô hình mà bấm được là bấm hụt cạnh ô cũng đổi trạng thái); khối chạm
     bên dưới bật lại `inline-flex`. Hai luật cùng độ đặc hiệu ⇒ thắng thua theo THỨ TỰ, ⛔ đừng đảo.
     Hệ quả: tên kết cục của ô tick MỜ phải nằm ở `title` của ô bảng (`td`), ⛔ không ở thẻ bọc.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — và MỘT điều kiện mỗi câu media (⛔ đừng
     ghép bằng chữ `and` trong tệp `.razor.css`, bài học đợt 1481).
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css`; ⛔ đừng gõ số 44px vào tệp này.
   ⚠ ⛔ Đừng đặt luật lên chính `.form-check-input`: ô tick 44px cạnh dòng chữ 15px trông như lỗi. */
.o-tick-cham[b-0y8tvocn87] {
    display: contents;
}

@media (pointer: coarse) {

    /* KIỂU ② — thẻ bọc ô tick trần: nới CẢ bề ngang lẫn chiều cao (ô vuông chỉ 16px, ⛔ không có chữ
       bên cạnh để mượn bề ngang). `inline-flex` chứ ⛔ không `flex`: hoá `flex` là thẻ chiếm trọn ô
       bảng và cột giãn ra. */
    .o-tick-cham[b-0y8tvocn87] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* KIỂU ③ — hộp bọc ô lọc: nâng sàn chiều cao và đổi sang hộp flex để ô vuông thôi dính mép trên.
       ⚠ ⛔ KHÔNG đụng `padding-left` của Bootstrap — chính phần đệm 1,5em ấy giữ bề ngang. */
    .form-check[b-0y8tvocn87] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    /* KIỂU ③ — nhãn chữ là vùng bấm thật. `align-self: stretch` ⛔ KHÔNG ĐƯỢC BỎ (bỏ là nhãn co về cỡ
       con chữ giữa một hộp 44px); cặp lề âm + đệm 1,5em (hằng số `.form-check` của Bootstrap) kéo nhãn
       PHỦ TRỌN cột ô vuông mà dòng chữ ⛔ không xê dịch. */
    .form-check-label[b-0y8tvocn87] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -1.5em;
        padding-left: 1.5em;
    }

    /* Nút chỉ-biểu-tượng trong ô bảng (🔑 · ✏️ · 🗑): thu chữ ⛔ không được thu vùng chạm (luật ④ đợt 2154). */
    .mtt-nut[b-0y8tvocn87] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
    }
}

/* ─── Sọc dòng TỰ TÔ (việc o1875) ───
   Hàng mang lớp mtt-table-hang nên luật sọc chung của app.css CHỪA bảng này: luật ấy tô nền BÁN TRONG
   SUỐT lên mọi ô dòng chẵn, kể cả ô Họ tên dính, nên cuộn ngang là chữ và ô tick các cột trượt bên dưới
   LỘ XUYÊN qua tên (đo sống 11/09/2026: ô tên dòng chẵn ra rgba(0, 0, 0, .027)). Ở đây tô lại đúng độ đậm
   của luật chung (rgba .028 nền sáng · .045 nền tối); ô dính giữ nền ĐẶC, sọc vẽ bằng một lớp ảnh phủ lên.
   Khuôn chép từ MaTranChucNang.razor.css (cùng đợt). */
.mtt-bang > tbody > tr:nth-child(even):not(.table-secondary) > td[b-0y8tvocn87] {
    background-color: rgba(0, 0, 0, .028);
}

:root[data-theme="dark"] .mtt-bang > tbody > tr:nth-child(even):not(.table-secondary) > td[b-0y8tvocn87] {
    background-color: rgba(255, 255, 255, .045);
}

.mtt-bang > tbody > tr:nth-child(even):not(.table-secondary) > td.mtt-cot-ten[b-0y8tvocn87] {
    background-color: var(--bs-body-bg, #fff);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtt-bang > tbody > tr:nth-child(even):not(.table-secondary) > td.mtt-cot-ten[b-0y8tvocn87] {
    background-color: var(--bs-body-bg, #212529);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}
/* /Components/Pages/QuanTri/PhanQuyen/KhoiNhomChamCong.razor.rz.scp.css */
/* ═══ BẢNG XẾP NGƯỜI VÀO NHÓM CHẤM CÔNG — việc o1944 (đợt 2471), khuôn lấy từ nhánh việc o1898 ═══
   Ma trận nhỏ: dòng = người trong phòng, cột = các nhóm của phòng + «Chưa xếp nhóm», mỗi dòng đúng MỘT ô chọn.
   ⚠ Khung cuộn có TRẦN CHIỀU CAO để hàng tiêu đề `position: sticky` có chỗ bám — phòng đông (BV-PCCC ~120 người)
     cuộn xuống mà mất tên nhóm là mọi ô chọn thành vô nghĩa. */

.xep-khung[b-gxcswr6j1x] {
    max-height: 460px;
    overflow: auto;
}

.xep-bang[b-gxcswr6j1x] {
    /* Cột nhóm co theo nội dung, phần dôi ra dồn cho cột tên (luật ① đợt 2154). */
    width: auto;
    min-width: 100%;
}

.xep-bang thead th[b-gxcswr6j1x] {
    position: sticky;
    top: 0;
    z-index: 2;
    vertical-align: bottom;
}

.xep-cot-ten[b-gxcswr6j1x] {
    min-width: 15rem;
}

/* Cột nhóm: hẹp hết mức, ô chọn nằm giữa. Tên nhóm dài thì tự xuống dòng ở đầu cột. */
.xep-cot-nhom[b-gxcswr6j1x] {
    width: 1%;
    text-align: center;
}

/* ─── VÙNG CHẠM NGÓN TAY CHO Ô CHỌN TRẦN (khuôn o737 · o962 — MaTranChucNang.razor.css) ───
   Ô chọn trong bảng ⛔ có chữ nào bên cạnh để mà bấm, nên được bọc thẻ `label.o-tick-cham`.
   🔑 LUẬT NỀN tắt hộp của thẻ bọc ở chế độ CHUỘT; ⚠ luật nền đứng TRƯỚC khối chạm (cùng độ đặc hiệu, thắng thua
   theo THỨ TỰ); ⚠ điều kiện là CÁI CHẠM VÀO (pointer coarse), ngưỡng đọc từ biến dùng chung khai ở app.css. */
.o-tick-cham[b-gxcswr6j1x] {
    display: contents;
}

@media (pointer: coarse) {
    .o-tick-cham[b-gxcswr6j1x] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/QuanTri/PhanQuyen/MaTranChucNang.razor.rz.scp.css */
/* ═══ MA TRẬN PHÂN QUYỀN THEO CHỨC NĂNG NGHIỆP VỤ — việc o1820 ═══
   Cùng khuôn MaTranQuyenApp.razor.css (đợt 2250): bảng cố ý cuộn ngang, nên phải neo HAI thứ:
     · cột HỌ TÊN neo bên trái — cuộn sang cột quyền thứ 15 mà mất tên thì mọi ô tick thành vô nghĩa;
     · hàng TIÊU ĐỀ neo bên trên — cuộn xuống dòng thứ 300 mà mất tên cột cũng vậy.
   ⚠ Khung cuộn phải có TRẦN CHIỀU CAO, không thì `position: sticky` của hàng tiêu đề ⛔ không có gì
     để bám. Bảng bày HẾT mọi dòng (⛔ không phân trang, anh Tiến 10/09/2026) — trần ấy giữ nó gọn. */

.mtcn-khung[b-dfvtailb0b] {
    max-height: 68vh;
    max-height: 68dvh; /* dòng lui cho điện thoại — luật đợt 1500 */
    overflow: auto;
}

.mtcn-bang[b-dfvtailb0b] {
    /* Cột phụ co theo nội dung, phần dôi ra dồn cho cột tên (luật ① đợt 2154). */
    width: auto;
    min-width: 100%;
}

.mtcn-bang thead th[b-dfvtailb0b] {
    position: sticky;
    top: 0;
    z-index: 3;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
    vertical-align: bottom;
}

/* Cột HỌ TÊN: cột to nhất, và là cột duy nhất được xuống dòng. */
.mtcn-cot-ten[b-dfvtailb0b] {
    position: sticky;
    left: 0;
    z-index: 2;
    min-width: 15rem;
    background-color: var(--bs-body-bg, #fff);
}

/* Góc trên bên trái vừa neo trái vừa neo trên ⇒ phải nổi trên cả hai. */
.mtcn-bang thead th.mtcn-cot-ten[b-dfvtailb0b] {
    z-index: 4;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

/* Cột lĩnh vực và cột quyền: hẹp hết mức, chữ ⛔ không xuống dòng. ⛔ Không có `title` bù (luật ghi
   chú 02/09) nên chữ đầu cột phải tự đọc được. */
.mtcn-cot-nhom[b-dfvtailb0b],
.mtcn-cot-quyen[b-dfvtailb0b] {
    width: 1%;
    white-space: nowrap;
}

/* Cột quyền đang bung tô nền khác cột lĩnh vực. Màu ⛔ không phải kênh duy nhất: đầu cột lĩnh vực đổi
   mũi tên ▸ thành ▾, và cột quyền mang chữ riêng của nó. */
.mtcn-cot-quyen[b-dfvtailb0b],
.mtcn-o-quyen[b-dfvtailb0b] {
    background-color: var(--bs-secondary-bg, #e9ecef);
}

.mtcn-o-nhom[b-dfvtailb0b] {
    color: var(--bs-secondary-color, #6c757d);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Hai nút «cả lĩnh vực» trong ô gộp (việc o1875) — cùng cỡ hai nút cả cột, đứng liền sau con số k/n. */
.mtcn-nut-gop[b-dfvtailb0b] {
    display: inline-flex;
    gap: .15rem;
    margin-left: .3rem;
    vertical-align: middle;
}

.mtcn-nut-gop .btn[b-dfvtailb0b] {
    font-size: .7rem;
    line-height: 1.1;
}

/* ─── Sọc dòng TỰ TÔ (việc o1875) ───
   Hàng mang lớp mtcn-table-hang nên luật sọc chung của app.css CHỪA bảng này: luật ấy tô nền BÁN TRONG
   SUỐT lên mọi ô dòng chẵn, kể cả ô Họ tên dính, nên cuộn ngang là chữ và ô tick các cột trượt bên dưới
   LỘ XUYÊN qua tên (đúng ảnh anh Tiến gửi 11/09/2026 — các dòng nền xám). Ở đây tô lại đúng độ đậm của
   luật chung (rgba .028 nền sáng · .045 nền tối); ô dính giữ nền ĐẶC, sọc vẽ bằng một lớp ảnh phủ lên.
   Khuôn chép từ BangHoSoNhanSu.razor.css (đợt 2305). */
.mtcn-bang > tbody > tr:nth-child(even) > td[b-dfvtailb0b] {
    background-color: rgba(0, 0, 0, .028);
}

:root[data-theme="dark"] .mtcn-bang > tbody > tr:nth-child(even) > td[b-dfvtailb0b] {
    background-color: rgba(255, 255, 255, .045);
}

.mtcn-bang > tbody > tr:nth-child(even) > td.mtcn-cot-ten[b-dfvtailb0b] {
    background-color: var(--bs-body-bg, #fff);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtcn-bang > tbody > tr:nth-child(even) > td.mtcn-cot-ten[b-dfvtailb0b] {
    background-color: var(--bs-body-bg, #212529);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}

/* Cột quyền đang bung giữ nền riêng ở CẢ dòng chẵn — sọc phủ lên bằng lớp ảnh, ⛔ thay nền. */
.mtcn-bang > tbody > tr:nth-child(even) > td.mtcn-o-quyen[b-dfvtailb0b] {
    background-color: var(--bs-secondary-bg, #e9ecef);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtcn-bang > tbody > tr:nth-child(even) > td.mtcn-o-quyen[b-dfvtailb0b] {
    background-color: var(--bs-secondary-bg, #343a40);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}

.mtcn-nut-nhom[b-dfvtailb0b] {
    text-decoration: none;
    line-height: 1.1;
}

.mtcn-bieu[b-dfvtailb0b] {
    display: block;
    font-size: 1.15rem;
}

.mtcn-ten-nhom[b-dfvtailb0b] {
    display: block;
    font-size: .75rem;
}

.mtcn-mui[b-dfvtailb0b] {
    display: block;
    font-size: .7rem;
}

.mtcn-nhan[b-dfvtailb0b] {
    font-size: .8rem;
}

/* Hai nút ✔ / ✕ tick cả cột — ép nhỏ để ⛔ không nới bề ngang cột quyền. */
.mtcn-nut-cot[b-dfvtailb0b] {
    display: flex;
    gap: .15rem;
    justify-content: center;
    margin-top: .15rem;
}

.mtcn-nut-cot .btn[b-dfvtailb0b] {
    font-size: .7rem;
    line-height: 1.1;
}

/* Chữ nổi tự ẩn (khuôn đợt 2281): `position: fixed` nên ⛔ không chiếm chỗ trong bố cục — bảng đứng
   yên dưới con trỏ; `pointer-events: none` để ⛔ không nuốt cú bấm rơi trúng nó. */
.mtcn-chu-noi[b-dfvtailb0b] {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    max-width: 22rem;
    padding: .5rem .9rem;
    border-radius: .5rem;
    background: var(--bs-success-bg-subtle, #d1e7dd);
    color: var(--bs-success-text-emphasis, #0a3622);
    border: 1px solid var(--bs-success-border-subtle, #a3cfbb);
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
    pointer-events: none;
    animation: mtcn-hien-b-dfvtailb0b .18s ease-out;
}

@keyframes mtcn-hien-b-dfvtailb0b {
    from { opacity: 0; transform: translateY(.5rem); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .mtcn-chu-noi[b-dfvtailb0b] { animation: none; }
}

/* ─── VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (khuôn o737 · o962) ───
   Ô tick trong bảng ⛔ không có chữ nào bên cạnh để mà bấm, nên được bọc thêm thẻ `label.o-tick-cham`.
   🔑 LUẬT NỀN tắt hộp của thẻ bọc ở chế độ CHUỘT — thẻ bọc là markup ⛔ không điều kiện, để nó sinh hộp
     dòng là mở một vùng bấm vô hình quanh ô vuông mà ⛔ không ai nhìn thấy (đo sống 06/09/2026).
   ⚠ Luật nền phải đứng TRƯỚC khối chạm: hai luật cùng độ đặc hiệu, thắng thua theo THỨ TỰ.
   ⚠ Điều kiện là CÁI CHẠM VÀO (pointer coarse), ⛔ không phải bề ngang; ngưỡng đọc từ biến dùng chung
     khai ở app.css, ⛔ không gõ số vào đây. Hàng rào: VungChamNgonTayTests. */
.o-tick-cham[b-dfvtailb0b] {
    display: contents;
}

@media (pointer: coarse) {
    .mtcn-nut-nhom[b-dfvtailb0b],
    .mtcn-nut-cot .btn[b-dfvtailb0b],
    .mtcn-nut-gop .btn[b-dfvtailb0b] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
    }

    .o-tick-cham[b-dfvtailb0b] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/QuanTri/PhanQuyen/MaTranNhomChamCong.razor.rz.scp.css */
/* ═══ MA TRẬN QUYỀN CHẤM CÔNG — việc o1939 + o1898 (đợt 2354) ═══
   Chép khuôn MaTranChucNang.razor.css: bảng cố ý cuộn ngang, nên phải neo HAI thứ:
     · cột HỌ TÊN neo bên trái — cuộn sang cột nhóm thứ mười mà mất tên thì mọi ô tick thành vô nghĩa;
     · hàng TIÊU ĐỀ neo bên trên — cuộn xuống dòng thứ 300 mà mất tên cột cũng vậy.
   ⚠ Khung cuộn phải có TRẦN CHIỀU CAO, không thì `position: sticky` của hàng tiêu đề ⛔ có gì để bám. */

.mtnc-khung[b-4ngqz1fzaz] {
    max-height: 68vh;
    max-height: 68dvh; /* dòng lui cho điện thoại — luật đợt 1500 */
    overflow: auto;
}

/* ⭐ ĐỢT 2450 (việc o1969) — PHẦN ĐẦU THẺ GỌN MỘT DÒNG (anh Tiến 13/09/2026: «làm gọn trên 1 dòng»): tiêu đề và chú
   giải ngắn đứng cùng một dòng flex; khổ hẹp thì `flex-wrap` cho chú giải xuống dòng dưới tiêu đề — ⛔ cắt chữ. Bốn ô
   lọc nằm ở MỘT hàng lưới Bootstrap (col-md-3 × 4) ngay trong markup (đợt 2432), ⛔ cần luật ở đây. */
.mtnc-dau[b-4ngqz1fzaz] {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: .75rem;
    row-gap: .15rem;
}

.mtnc-bang[b-4ngqz1fzaz] {
    /* Cột phụ co theo nội dung, phần dôi ra dồn cho cột tên (luật ① đợt 2154). */
    width: auto;
    min-width: 100%;
}

.mtnc-bang thead th[b-4ngqz1fzaz] {
    position: sticky;
    top: 0;
    z-index: 3;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
    vertical-align: bottom;
}

/* Cột HỌ TÊN: cột to nhất, và là cột duy nhất được xuống dòng. */
.mtnc-cot-ten[b-4ngqz1fzaz] {
    position: sticky;
    left: 0;
    z-index: 2;
    min-width: 15rem;
    background-color: var(--bs-body-bg, #fff);
}

/* Góc trên bên trái vừa neo trái vừa neo trên ⇒ phải nổi trên cả hai. */
.mtnc-bang thead th.mtnc-cot-ten[b-4ngqz1fzaz] {
    z-index: 4;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

/* Cột phòng và cột nhóm: hẹp hết mức. Tên NHÓM thì được xuống dòng trong một bề ngang có trần (xem
   .mtnc-nhan) — tên nhóm do người khai tự đặt, có thể dài cả câu. */
.mtnc-cot-phong[b-4ngqz1fzaz],
.mtnc-cot-nhom[b-4ngqz1fzaz] {
    width: 1%;
    white-space: nowrap;
}

/* Cột nhóm đang bung tô nền khác cột phòng. Màu ⛔ phải kênh duy nhất: đầu cột phòng đổi mũi tên ▸ thành
   ▾, và cột nhóm mang tên riêng của nó. */
.mtnc-cot-nhom[b-4ngqz1fzaz],
.mtnc-o-nhom[b-4ngqz1fzaz] {
    background-color: var(--bs-secondary-bg, #e9ecef);
}

.mtnc-o-phong[b-4ngqz1fzaz] {
    color: var(--bs-secondary-color, #6c757d);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ─── Sọc dòng TỰ TÔ (khuôn việc o1875 của ma trận nghiệp vụ) ───
   Hàng mang lớp mtnc-table-hang nên luật sọc chung của app.css CHỪA bảng này: luật ấy tô nền BÁN TRONG
   SUỐT lên mọi ô dòng chẵn, kể cả ô Họ tên dính, nên cuộn ngang là ô tick các cột trượt bên dưới LỘ XUYÊN
   qua tên. Ở đây tô lại đúng độ đậm của luật chung; ô dính giữ nền ĐẶC, sọc vẽ bằng một lớp ảnh phủ lên. */
.mtnc-bang > tbody > tr:nth-child(even) > td[b-4ngqz1fzaz] {
    background-color: rgba(0, 0, 0, .028);
}

:root[data-theme="dark"] .mtnc-bang > tbody > tr:nth-child(even) > td[b-4ngqz1fzaz] {
    background-color: rgba(255, 255, 255, .045);
}

.mtnc-bang > tbody > tr:nth-child(even) > td.mtnc-cot-ten[b-4ngqz1fzaz] {
    background-color: var(--bs-body-bg, #fff);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtnc-bang > tbody > tr:nth-child(even) > td.mtnc-cot-ten[b-4ngqz1fzaz] {
    background-color: var(--bs-body-bg, #212529);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}

/* Cột nhóm đang bung giữ nền riêng ở CẢ dòng chẵn — sọc phủ lên bằng lớp ảnh, ⛔ thay nền. */
.mtnc-bang > tbody > tr:nth-child(even) > td.mtnc-o-nhom[b-4ngqz1fzaz] {
    background-color: var(--bs-secondary-bg, #e9ecef);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtnc-bang > tbody > tr:nth-child(even) > td.mtnc-o-nhom[b-4ngqz1fzaz] {
    background-color: var(--bs-secondary-bg, #343a40);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}

.mtnc-nut-phong[b-4ngqz1fzaz] {
    text-decoration: none;
    line-height: 1.1;
}

.mtnc-bieu[b-4ngqz1fzaz] {
    display: block;
    font-size: 1.15rem;
}

.mtnc-ten-phong[b-4ngqz1fzaz] {
    display: block;
    font-size: .8rem;
    font-weight: 600;
}

.mtnc-so-nhom[b-4ngqz1fzaz] {
    display: block;
    font-size: .7rem;
    color: var(--bs-secondary-color, #6c757d);
}

/* ⭐ ĐỢT 2450 (việc o1968) — phòng CHƯA khai nhóm nay cũng có cột (anh Tiến 13/09/2026: «nhóm chấm công thiếu các
   phòng ban khác»): MỘT cột «cả phòng» (luật anh chốt 14/09/2026 — cả phòng là MỘT nhóm), ⛔ bung nên đầu cột là chữ
   thường chứ ⛔ phải nút; giữ đúng nhịp dòng của nút bung (.mtnc-nut-phong) để hai kiểu đầu cột thẳng hàng. */
.mtnc-phong-chua-khai[b-4ngqz1fzaz] {
    display: block;
    line-height: 1.1;
}

/* Việc o2072 — dòng «bảng công đang chia: Văn phòng · Đội Tàu kéo» dưới chữ «cả phòng»: chữ nhỏ, xuống dòng trong một
   bề ngang chặn trần để cột phòng chưa khai ⛔ phình ngang theo số tab. Màu phụ như .mtnc-so-nhom — chú thích, ⛔ cảnh báo. */
.mtnc-khoi-gom[b-4ngqz1fzaz] {
    display: block;
    max-width: 8.5rem;
    margin: .1rem auto 0;
    font-size: .65rem;
    font-weight: normal;
    white-space: normal;
    color: var(--bs-secondary-color, #6c757d);
}

.mtnc-mui[b-4ngqz1fzaz] {
    display: block;
    font-size: .7rem;
}

/* Nút ⚙ mở hộp khai nhóm — đứng dưới tên phòng, ép nhỏ để ⛔ nới bề ngang cột phòng. */
.mtnc-nut-khai[b-4ngqz1fzaz] {
    display: block;
    margin: .2rem auto 0;
    font-size: .7rem;
    line-height: 1.1;
}

.mtnc-nhan[b-4ngqz1fzaz] {
    display: block;
    font-size: .8rem;
    white-space: normal;
    max-width: 9rem;
    margin: 0 auto;
}

/* Hai ô tick của một ô nhóm (anh Tiến 12/09/2026: «mỗi ô 2 tick: chấm · xem»). Mỗi ô tick đi kèm biểu tượng
   của nó (✍ · 👁) để hai ô ⛔ chỉ phân biệt bằng vị trí; khoảng cách giữa HAI cặp lớn hơn khoảng cách trong
   một cặp, mắt mới gom đúng biểu tượng với ô tick của nó. */
.mtnc-hai-tick[b-4ngqz1fzaz] {
    display: inline-flex;
    gap: .6rem;
    align-items: center;
    justify-content: center;
}

.mtnc-tick[b-4ngqz1fzaz] {
    display: inline-flex;
    align-items: center;
    gap: .1rem;
}

.mtnc-bieu-tick[b-4ngqz1fzaz] {
    font-size: .75rem;
}

.mtnc-chu-tick[b-4ngqz1fzaz] {
    display: block;
    font-size: .7rem;
    white-space: nowrap;
    color: var(--bs-secondary-color, #6c757d);
}

/* Nhóm chưa ai chấm: bảng công của nhóm ĐỨNG IM — số bất lợi ⇒ màu ĐỎ (luật màu 30/07). */
.mtnc-canh[b-4ngqz1fzaz] {
    display: block;
    font-size: .7rem;
    white-space: nowrap;
    color: var(--bs-danger-text-emphasis, #b02a37);
}

/* Chữ nổi tự ẩn (khuôn đợt 2281): `position: fixed` nên ⛔ chiếm chỗ trong bố cục — bảng đứng yên dưới con
   trỏ; `pointer-events: none` để ⛔ nuốt cú bấm rơi trúng nó. */
.mtnc-chu-noi[b-4ngqz1fzaz] {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    max-width: 22rem;
    padding: .5rem .9rem;
    border-radius: .5rem;
    background: var(--bs-success-bg-subtle, #d1e7dd);
    color: var(--bs-success-text-emphasis, #0a3622);
    border: 1px solid var(--bs-success-border-subtle, #a3cfbb);
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
    pointer-events: none;
    animation: mtnc-hien-b-4ngqz1fzaz .18s ease-out;
}

@keyframes mtnc-hien-b-4ngqz1fzaz {
    from { opacity: 0; transform: translateY(.5rem); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .mtnc-chu-noi[b-4ngqz1fzaz] { animation: none; }
}

/* ─── VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (khuôn o737 · o962) ───
   Ô tick trong bảng ⛔ có chữ nào bên cạnh để mà bấm, nên được bọc thẻ `label.o-tick-cham`.
   🔑 LUẬT NỀN tắt hộp của thẻ bọc ở chế độ CHUỘT — để nó sinh hộp dòng là mở một vùng bấm vô hình quanh ô
     vuông mà ⛔ ai nhìn thấy.
   ⚠ Luật nền phải đứng TRƯỚC khối chạm: hai luật cùng độ đặc hiệu, thắng thua theo THỨ TỰ.
   ⚠ Điều kiện là CÁI CHẠM VÀO (pointer coarse), ⛔ phải bề ngang; ngưỡng đọc từ biến dùng chung khai ở
     app.css, ⛔ gõ số vào đây. */
.o-tick-cham[b-4ngqz1fzaz] {
    display: contents;
}

@media (pointer: coarse) {
    .mtnc-nut-phong[b-4ngqz1fzaz],
    .mtnc-nut-khai[b-4ngqz1fzaz] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
    }

    .o-tick-cham[b-4ngqz1fzaz] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}
/* /Components/Pages/QuanTri/PhanQuyen/MaTranQuyenApp.razor.rz.scp.css */
/* ═══ MA TRẬN PHÂN QUYỀN — đợt 2250, việc o1739 ═══
   Bảng này rộng theo SỐ APP (hơn 20 cột, bung thêm cột trang nữa) nên nó là một trong số ít bảng của
   app CỐ Ý cuộn ngang. Hai thứ phải neo lại thì bảng mới đọc được:
     · cột TÊN NGƯỜI neo bên trái — cuộn tới cột thứ 15 mà mất tên thì mọi ô tick thành vô nghĩa;
     · hàng TIÊU ĐỀ neo bên trên — cuộn xuống dòng thứ 30 mà mất tên app cũng vậy.
   ⚠ Khung cuộn phải có TRẦN CHIỀU CAO, không thì `position: sticky` của hàng tiêu đề ⛔ không có gì để
     bám: nó neo theo khung cuộn gần nhất, mà khung ⛔ không cuộn thì neo cũng ⛔ không xảy ra. */

.mtq-khung[b-gf9zrijrw8] {
    max-height: 68vh;
    max-height: 68dvh; /* dòng lui cho điện thoại — luật đợt 1500 */
    overflow: auto;
}

.mtq-bang[b-gf9zrijrw8] {
    /* Cột phụ co theo nội dung, phần dôi ra dồn hết cho cột tên (luật ① đợt 2154). */
    width: auto;
    min-width: 100%;
}

/* ⭐ ĐỢT 2296 (việc o1788) — NEO CẢ KHỐI ĐẦU BẢNG, ⛔ neo từng ô th như trước.
   Đầu bảng nay có HAI hàng: hàng tiêu đề + hàng tổng. Neo từng ô th ở top 0 thì hàng tổng cũng neo ở 0 và
   đè lên hàng tiêu đề; muốn neo nó ngay dưới hàng một thì phải biết chiều cao hàng một, mà chiều cao ấy đổi
   theo cột đang bung (thêm cặp nút tick cả cột) và theo chế độ cảm ứng (nút 44px). Neo cả khối thead thì
   hai hàng đi cùng nhau, ⛔ phải đo gì. Cột Họ tên trong đầu bảng vẫn neo TRÁI bằng luật .mtq-cot-ten. */
.mtq-bang thead[b-gf9zrijrw8] {
    position: sticky;
    top: 0;
    z-index: 3;
}

.mtq-bang thead th[b-gf9zrijrw8] {
    background-color: var(--bs-tertiary-bg, #f8f9fa);
    vertical-align: bottom;
}

/* ⭐ ĐỢT 2296 — HÀNG TỔNG: số người đang được tick ở từng cột. Nền đậm hơn hàng tiêu đề một nấc để mắt
   tách được hai hàng; chữ số cùng bề ngang để các cột số đọc thẳng hàng. */
.mtq-bang thead .mtq-hang-tong th[b-gf9zrijrw8] {
    background-color: var(--bs-secondary-bg, #e9ecef);
    vertical-align: middle;
    font-variant-numeric: tabular-nums;
}

/* Cột TÊN: cột to nhất của bảng, và là cột duy nhất được xuống dòng. */
.mtq-cot-ten[b-gf9zrijrw8] {
    position: sticky;
    left: 0;
    z-index: 2;
    min-width: 15rem;
    background-color: var(--bs-body-bg, #fff);
}

/* Góc trên bên trái vừa neo trái vừa neo trên ⇒ phải nổi trên cả hai. */
.mtq-bang thead th.mtq-cot-ten[b-gf9zrijrw8] {
    z-index: 4;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

/* Cột app và cột trang: hẹp hết mức, chữ ⛔ không xuống dòng. Nhãn đầy đủ nằm trong `title`
   (luật ② đợt 2154: huy hiệu nhỏ, rê chuột mới hiện tên). */
.mtq-cot-app[b-gf9zrijrw8],
.mtq-cot-trang[b-gf9zrijrw8] {
    width: 1%;
    white-space: nowrap;
}

.mtq-cot-trang[b-gf9zrijrw8] {
    background-color: var(--bs-secondary-bg, #e9ecef);
}

.mtq-o-trang[b-gf9zrijrw8] {
    background-color: var(--bs-secondary-bg, #e9ecef);
}

.mtq-nut-app[b-gf9zrijrw8] {
    text-decoration: none;
    line-height: 1.1;
}

.mtq-bieu[b-gf9zrijrw8] {
    display: block;
    font-size: 1.15rem;
}

.mtq-mui[b-gf9zrijrw8] {
    display: block;
    font-size: .7rem;
}

.mtq-nhan-trang[b-gf9zrijrw8] {
    font-size: 1rem;
}

.mtq-dau[b-gf9zrijrw8] {
    color: var(--bs-secondary-color, #6c757d);
}

/* Vùng chạm cho ngón tay — chỉ ở thiết bị cảm ứng (luật đợt 1518: điều kiện là CÁI CHẠM VÀO,
   ⛔ không phải bề ngang màn hình). Ô tick nong bằng ĐỆM CỦA Ô, ⛔ không phóng to ô vuông:
   một ô tick 44px cạnh dòng chữ 15px trông như lỗi. */
@media (pointer: coarse) {
    .mtq-nut-app[b-gf9zrijrw8] {
        min-width: var(--cham-toi-thieu, 44px);
        min-height: var(--cham-toi-thieu, 44px);
    }

    .mtq-bang tbody td[b-gf9zrijrw8] {
        min-width: var(--cham-toi-thieu, 44px);
        height: var(--cham-toi-thieu, 44px);
    }
}

/* ⭐ ĐỢT 2281 — CHỮ NỔI TỰ ẨN thay cho dải xanh "Đã ghi." (anh Tiến 10/09/2026).
   🔑 `position: fixed` là cả điểm mấu chốt: dải cũ nằm trong luồng bố cục nên mỗi lượt tick nó chen
   vào và ĐẨY CẢ BẢNG XUỐNG — ô người ta vừa bấm trượt khỏi chỗ con trỏ, lượt tick sau rơi nhầm ô.
   ⚠ `pointer-events: none` để chữ nổi ⛔ không nuốt cú bấm rơi trúng nó. */
.mtq-chu-noi[b-gf9zrijrw8] {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    max-width: 22rem;
    padding: .5rem .9rem;
    border-radius: .5rem;
    background: var(--bs-success-bg-subtle, #d1e7dd);
    color: var(--bs-success-text-emphasis, #0a3622);
    border: 1px solid var(--bs-success-border-subtle, #a3cfbb);
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
    pointer-events: none;
    animation: mtq-hien-b-gf9zrijrw8 .18s ease-out;
}

@keyframes mtq-hien-b-gf9zrijrw8 {
    from { opacity: 0; transform: translateY(.5rem); }
    to   { opacity: 1; transform: none; }
}

/* ⚠ Người đặt hệ điều hành ở chế độ giảm chuyển động thì ⛔ không chạy hiệu ứng — luật khả dụng chung. */
@media (prefers-reduced-motion: reduce) {
    .mtq-chu-noi[b-gf9zrijrw8] { animation: none; }
}

/* Hai nút ✔ / ✕ tick cả cột, chỉ hiện khi cột đang bung. Ép nhỏ để ⛔ không nới bề ngang cột app. */
.mtq-nut-cot[b-gf9zrijrw8] {
    display: flex;
    gap: .15rem;
    justify-content: center;
    margin-top: .15rem;
}

.mtq-nut-cot .btn[b-gf9zrijrw8] {
    font-size: .7rem;
    line-height: 1.1;
}

/* ─── Sọc dòng TỰ TÔ (việc o1875) ───
   Hàng mang lớp mtq-table-hang nên luật sọc chung của app.css CHỪA bảng này: luật ấy tô nền BÁN TRONG
   SUỐT lên mọi ô dòng chẵn, kể cả ô Họ tên dính, nên cuộn ngang là chữ và ô tick các cột trượt bên dưới
   LỘ XUYÊN qua tên (đo sống 11/09/2026: ô tên dòng chẵn ra rgba(0, 0, 0, .027)). Ở đây tô lại đúng độ đậm
   của luật chung (rgba .028 nền sáng · .045 nền tối); ô dính giữ nền ĐẶC, sọc vẽ bằng một lớp ảnh phủ lên.
   Khuôn chép từ MaTranChucNang.razor.css (cùng đợt). */
.mtq-bang > tbody > tr:nth-child(even) > td[b-gf9zrijrw8] {
    background-color: rgba(0, 0, 0, .028);
}

:root[data-theme="dark"] .mtq-bang > tbody > tr:nth-child(even) > td[b-gf9zrijrw8] {
    background-color: rgba(255, 255, 255, .045);
}

.mtq-bang > tbody > tr:nth-child(even) > td.mtq-cot-ten[b-gf9zrijrw8] {
    background-color: var(--bs-body-bg, #fff);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtq-bang > tbody > tr:nth-child(even) > td.mtq-cot-ten[b-gf9zrijrw8] {
    background-color: var(--bs-body-bg, #212529);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}

/* Ô cột trang giữ nền riêng ở CẢ dòng chẵn — sọc phủ lên bằng lớp ảnh, ⛔ thay nền. */
.mtq-bang > tbody > tr:nth-child(even) > td.mtq-o-trang[b-gf9zrijrw8] {
    background-color: var(--bs-secondary-bg, #e9ecef);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtq-bang > tbody > tr:nth-child(even) > td.mtq-o-trang[b-gf9zrijrw8] {
    background-color: var(--bs-secondary-bg, #343a40);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}
/* /Components/Pages/QuanTri/PhanQuyen/MaTranQuyenXem.razor.rz.scp.css */
/* ═══ MA TRẬN QUYỀN XEM TICK THEO NGƯỜI — việc o1876 (báo cáo) + o1877 (khối trang chủ) ═══
   Cùng khuôn MaTranChucNang.razor.css: bảng cố ý cuộn, nên phải neo HAI thứ:
     · cột HỌ TÊN neo bên trái — cuộn sang cột cuối mà mất tên thì mọi ô tick thành vô nghĩa;
     · hàng TIÊU ĐỀ neo bên trên — cuộn xuống dòng thứ 300 mà mất tên cột cũng vậy.
   ⚠ Khung cuộn phải có TRẦN CHIỀU CAO, không thì `position: sticky` của hàng tiêu đề ⛔ có gì để bám.
     Bảng bày HẾT mọi dòng (⛔ phân trang) — trần ấy giữ nó gọn. */

.mtqx-khung[b-1fyq1mdwe1] {
    max-height: 68vh;
    max-height: 68dvh; /* dòng lui cho điện thoại — luật đợt 1500 */
    overflow: auto;
}

.mtqx-bang[b-1fyq1mdwe1] {
    /* Cột ô tick co theo nội dung, phần dôi ra dồn cho cột tên (luật ① đợt 2154). */
    width: auto;
    min-width: 100%;
}

.mtqx-bang thead th[b-1fyq1mdwe1] {
    position: sticky;
    top: 0;
    z-index: 3;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
    vertical-align: bottom;
}

/* Cột HỌ TÊN: cột to nhất. Nền ĐẶC — ô dính mà nền trong suốt là chữ các cột cuộn bên dưới lộ xuyên qua. */
.mtqx-cot-ten[b-1fyq1mdwe1] {
    position: sticky;
    left: 0;
    z-index: 2;
    min-width: 15rem;
    background-color: var(--bs-body-bg, #fff);
}

/* Góc trên bên trái vừa neo trái vừa neo trên ⇒ phải nổi trên cả hai. */
.mtqx-bang thead th.mtqx-cot-ten[b-1fyq1mdwe1] {
    z-index: 4;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

/* Cột ô tick: co hết mức (width 1%), nhưng tên báo cáo dài như «Chấm công (bảng công tháng / theo người)»
   thì cho XUỐNG DÒNG trong một khung 7,5–10rem — để một dòng là mỗi cột rộng cả gang tay. ⛔ Không có
   `title` bù (luật ghi chú 02/09) nên chữ đầu cột phải tự đọc được trọn. */
.mtqx-cot-quyen[b-1fyq1mdwe1] {
    width: 1%;
}

.mtqx-nhan[b-1fyq1mdwe1] {
    display: inline-block;
    min-width: 7.5rem;
    max-width: 10rem;
    font-size: .8rem;
    line-height: 1.2;
    white-space: normal;
}

/* Hai nút ✔ / ✕ tick cả cột — ép nhỏ để ⛔ nới bề ngang cột. */
.mtqx-nut-cot[b-1fyq1mdwe1] {
    display: flex;
    gap: .15rem;
    justify-content: center;
    margin-top: .15rem;
}

.mtqx-nut-cot .btn[b-1fyq1mdwe1] {
    font-size: .7rem;
    line-height: 1.1;
}

/* ─── VIỆC o1901 — CỘT CON THEO PHÒNG ───
   Tên báo cáo chia theo phòng là NÚT bung/thu (khuôn MaTranQuyenApp). Nút mang lớp btn-link nên phải trả màu
   chữ + bỏ gạch chân để đầu cột trông như mọi đầu cột khác, chỉ thêm mũi tên ▸/▾. */
.mtqx-nut-bung[b-1fyq1mdwe1] {
    color: inherit;
    text-decoration: none;
    font-weight: 600;
}

.mtqx-nut-bung:hover .mtqx-nhan[b-1fyq1mdwe1],
.mtqx-nut-bung:focus-visible .mtqx-nhan[b-1fyq1mdwe1] {
    text-decoration: underline;
}

.mtqx-mui[b-1fyq1mdwe1] {
    font-size: .75rem;
    margin-left: .15rem;
}

/* Cột phòng: hẹp hơn cột báo cáo — tên phòng xuống dòng trong khung 4,5–6,5rem, ⛔ title bù (quy tắc #23) nên
   chữ phải tự đọc được trọn. Viền trái nét đứt để mắt thấy các cột con thuộc cột báo cáo đứng trước chúng. */
.mtqx-cot-phong[b-1fyq1mdwe1] {
    width: 1%;
}

.mtqx-nhan-phong[b-1fyq1mdwe1] {
    display: inline-block;
    min-width: 4.5rem;
    max-width: 6.5rem;
    font-size: .72rem;
    font-weight: 500;
    line-height: 1.15;
    white-space: normal;
}

.mtqx-cot-phong[b-1fyq1mdwe1],
.mtqx-o-phong[b-1fyq1mdwe1] {
    border-left-style: dashed;
}

/* Chữ «3/12 phòng» dưới ô «cả báo cáo» — người đó chỉ xem được một phần số phòng. */
.mtqx-dem-phong[b-1fyq1mdwe1] {
    display: block;
    font-size: .7rem;
    line-height: 1.1;
    color: var(--bs-secondary-color, #6c757d);
    white-space: nowrap;
}

/* ─── SỌC HÀNG TỰ TÔ (khuôn BangHoSoNhanSu.razor.css, đợt 2305) ───
   🔴 Lỗi thấy trong ảnh anh Tiến gửi 11/09/2026 ở ma trận nghiệp vụ: luật sọc dùng chung của app.css
   (hàng chẵn, nền rgba .028) có độ ưu tiên CAO hơn CSS cách ly ⇒ ô Họ tên DÍNH ở dòng chẵn hóa bán trong
   suốt, chữ và ô tick của các cột cuộn bên dưới lộ xuyên qua tên.
   ⇒ Hàng tbody mang lớp «mtqx-table-hang» (chứa chuỗi table-) nên luật chung CHỪA bảng này; ở đây tô lại
     đúng độ đậm của luật ấy (.028 nền sáng · .045 nền tối) — hai con số đi CÙNG luật gốc, đổi một bên là
     bảng này lệch tông với mọi bảng khác của app. */
.mtqx-bang > tbody > tr:nth-child(even) > td[b-1fyq1mdwe1] {
    background-color: rgba(0, 0, 0, .028);
}

:root[data-theme="dark"] .mtqx-bang > tbody > tr:nth-child(even) > td[b-1fyq1mdwe1] {
    background-color: rgba(255, 255, 255, .045);
}

/* Ô tên DÍNH ở hàng chẵn: nền vẫn ĐẶC, sọc vẽ bằng một lớp ảnh phủ lên trên nền. */
.mtqx-bang > tbody > tr:nth-child(even) > td.mtqx-cot-ten[b-1fyq1mdwe1] {
    background-color: var(--bs-body-bg, #fff);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .mtqx-bang > tbody > tr:nth-child(even) > td.mtqx-cot-ten[b-1fyq1mdwe1] {
    background-color: var(--bs-body-bg, #fff);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}

/* ─── Ô ĐÃ CHỈ ĐỊNH RIÊNG ───
   Viền cam quanh ô tick: người đó đã có dòng chỉ định riêng ở cột này, ⛔ còn theo mặc định vai trò.
   Màu ⛔ là kênh duy nhất: viền là một HÌNH (vòng dày quanh ô), và ô mang thêm vế «chỉ định riêng» trong
   tên đọc `aria-label`. Viền bằng `outline` nên ⛔ đổi kích thước ô, hàng ⛔ nhảy khi ô đổi trạng thái. */
.mtqx-rieng[b-1fyq1mdwe1] {
    outline: 2px solid var(--bs-warning, #ffc107);
    outline-offset: 1px;
}

/* Chữ nổi tự ẩn (khuôn đợt 2281): `position: fixed` nên ⛔ chiếm chỗ trong bố cục — bảng đứng yên dưới
   con trỏ; `pointer-events: none` để ⛔ nuốt cú bấm rơi trúng nó. */
.mtqx-chu-noi[b-1fyq1mdwe1] {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    max-width: 22rem;
    padding: .5rem .9rem;
    border-radius: .5rem;
    background: var(--bs-success-bg-subtle, #d1e7dd);
    color: var(--bs-success-text-emphasis, #0a3622);
    border: 1px solid var(--bs-success-border-subtle, #a3cfbb);
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
    pointer-events: none;
    animation: mtqx-hien-b-1fyq1mdwe1 .18s ease-out;
}

@keyframes mtqx-hien-b-1fyq1mdwe1 {
    from { opacity: 0; transform: translateY(.5rem); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .mtqx-chu-noi[b-1fyq1mdwe1] { animation: none; }
}

/* ─── VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (khuôn o737 · o962) ───
   Ô tick trong bảng ⛔ có chữ nào bên cạnh để mà bấm, nên được bọc thêm thẻ `label.o-tick-cham`.
   🔑 LUẬT NỀN tắt hộp của thẻ bọc ở chế độ CHUỘT — thẻ bọc là markup ⛔ điều kiện, để nó sinh hộp dòng là
     mở một vùng bấm vô hình quanh ô vuông mà ⛔ ai nhìn thấy (đo sống 06/09/2026).
   ⚠ Luật nền phải đứng TRƯỚC khối chạm: hai luật cùng độ đặc hiệu, thắng thua theo THỨ TỰ.
   ⚠ Điều kiện là CÁI CHẠM VÀO (pointer coarse), ⛔ phải bề ngang; ngưỡng đọc từ biến dùng chung khai ở
     app.css, ⛔ gõ số vào đây. Nới VÙNG CHẠM quanh ô tick, ⛔ phóng to chính ô vuông.
   Hàng rào: VungChamNgonTayTests · MaTranQuyenXemTests. */
.o-tick-cham[b-1fyq1mdwe1] {
    display: contents;
}

@media (pointer: coarse) {
    .o-tick-cham[b-1fyq1mdwe1] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Chữ trên hai nút ✔ / ✕ ⛔ được nhỏ hơn sàn cỡ chữ của thiết bị chạm (khuôn .qr-th-vai). */
    .mtqx-nut-cot .btn[b-1fyq1mdwe1] {
        font-size: max(.7rem, var(--cham-chu-toi-thieu));
    }

    /* Việc o1901 — tên phòng đầu cột con và chữ «3/12 phòng» cũng ⛔ được nhỏ hơn sàn ấy. */
    .mtqx-nhan-phong[b-1fyq1mdwe1],
    .mtqx-dem-phong[b-1fyq1mdwe1] {
        font-size: max(.72rem, var(--cham-chu-toi-thieu));
    }
}
/* /Components/Pages/QuanTri/PhanQuyen/TabQuyenReport.razor.rz.scp.css */
/* ═══ VÙNG CHẠM NGÓN TAY CHO Ô TICK TRẦN (đợt 1605, việc o737) ═══
   ô tick TRẦN của MA TRẬN phân quyền — mỗi ô là giao của một vai trò với một báo cáo/khối trang chủ.

   🔑 Nới VÙNG CHẠM QUANH ô tick, ⛔ KHÔNG phóng to chính ô vuông: ô tick 44px đứng cạnh dòng chữ
     15px trông như lỗi phần mềm, và nó cũng ⛔ không giải đúng bài — thứ ngón tay bấm trượt là
     khoảng trống quanh ô. Thẻ bọc `label.o-tick-cham` khai ngay trong `.razor`.
   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là ngón
     tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Thứ
     quyết định vùng bấm là CÁI CHẠM VÀO, ⛔ không phải bề ngang.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media, ⛔ không một dòng nào ở ngoài. Hàng rào `VungChamNgonTayTests` canh đúng điều đó.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết `(min-width: …) and (max-width: …)` trong tệp
     `.razor.css`: bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học 1481).
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi: node cong-cu/soi-o-tick-tran.mjs · node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ⚠ Cần CẢ `min-width`: ô tick chỉ rộng 24px, nới mỗi chiều cao là ngón tay vẫn trượt NGANG.
       ⚠ `inline-flex` chứ ⛔ không `flex`: thẻ bọc nằm trong một ô bảng (hoặc một hàng `d-flex`)
         cạnh nút và ô gõ khác — hoá `flex` là nó chiếm trọn một dòng, hàng đang gọn bỗng vỡ ra.
       ⚠ `min-height` chứ ⛔ không `height`: hàng nào cao hơn thì cứ để cao — đặt `height` là bóp
         chúng lại, sửa một lỗi rồi đẻ một lỗi. */
    .o-tick-cham[b-51h307qh5p] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}

/* ─────────── ⭐ ĐỢT 1979 (việc o1261) — ĐẦU CỘT VAI TRÒ: BỎ STYLE NỘI TUYẾN, ĐẶT SÀN 12px ───────────
   📏 Đo sống 04/09/2026 (Chrome thật, 390×844, cảm ứng, `tienvm`,
      `do-song-khung-hep.mjs --trang /quan-tri/quyen-report`): **20/20** chỗ chữ nhỏ của cả trang là
      hai hàng `<th>` tên vai trò, đo ra **11,5px**, dưới sàn `--cham-chu-toi-thieu` = 12px.

   🩸 BÀI HỌC ĐẮT NHẤT CỦA ĐỢT NÀY — VÌ SAO LUẬT DÙNG CHUNG ĐỢT 1518 ⛔ KHÔNG VỚI TỚI ĐÂY:
      cỡ chữ ấy ⛔ KHÔNG do một luật CSS nào đặt, nó là **`style="font-size: .72rem"` NỘI TUYẾN**
      ngay trong `.razor`. Style nội tuyến thắng **mọi** luật trong tệp CSS (trừ `!important`) ⇒
      sàn 12px của `@media (pointer: coarse)` trong `app.css` có viết đúng đến mấy cũng vô hiệu, mà
      ⛔ **không một câu lỗi nào**. Đo toàn app cùng ngày: **82/185 chỗ chữ nhỏ (44%) là style nội
      tuyến** — tức gần một nửa vấn đề nằm ngoài tầm với của mọi luật CSS dùng chung.
      ⇒ **Cách chữa đúng là GỠ style nội tuyến ra thành lớp**, ⛔ không phải đắp thêm `!important`:
      `!important` chữa được một chỗ rồi để nguyên cái bẫy cho chỗ sau.
   ⚠ Hai bảng nằm trong `div.table-responsive` sẵn có nên nâng cỡ chữ ⛔ không làm CẢ TRANG tràn
      ngang — nó chỉ kéo dài dải cuộn riêng của bảng. Đo lại sau khi sửa: trang vẫn **tràn ngang 0**. */
.qr-th-vai[b-51h307qh5p] {
    font-size: .72rem;
    vertical-align: bottom;
}

@media (pointer: coarse) {
    .qr-th-vai[b-51h307qh5p] { font-size: max(.72rem, var(--cham-chu-toi-thieu)); }
}
/* /Components/Pages/QuanTri/PhanQuyen/TabQuyenXemLich.razor.rz.scp.css */
/* ═══ TAB «📅 QUYỀN XEM LỊCH» — việc o1881 ═══
   Chép khuôn MaTranQuyenXem.razor.css (CSS cách ly ⛔ dùng chung được giữa hai component): bảng cố ý cuộn, nên
   neo HAI thứ — cột HỌ TÊN bên trái và hàng TIÊU ĐỀ bên trên. Khung cuộn phải có TRẦN CHIỀU CAO, không thì
   `position: sticky` của hàng tiêu đề ⛔ có gì để bám. */

.tqxl-khung[b-woi8taf0tk] {
    max-height: 68vh;
    max-height: 68dvh; /* dòng lui cho điện thoại — luật đợt 1500 */
    overflow: auto;
}

.tqxl-bang[b-woi8taf0tk] {
    width: auto;
    min-width: 100%;
}

.tqxl-bang thead th[b-woi8taf0tk] {
    position: sticky;
    top: 0;
    z-index: 3;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
    vertical-align: bottom;
}

/* Cột HỌ TÊN: cột to nhất (luật ① đợt 2154). Nền ĐẶC — ô dính mà nền trong suốt là chữ cột khác lộ xuyên qua. */
.tqxl-cot-ten[b-woi8taf0tk] {
    position: sticky;
    left: 0;
    z-index: 2;
    min-width: 15rem;
    background-color: var(--bs-body-bg, #fff);
}

.tqxl-bang thead th.tqxl-cot-ten[b-woi8taf0tk] {
    z-index: 4;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

.tqxl-cot-quyen[b-woi8taf0tk] {
    width: 1%;
}

.tqxl-nhan[b-woi8taf0tk] {
    display: inline-block;
    min-width: 7.5rem;
    max-width: 10rem;
    font-size: .8rem;
    line-height: 1.2;
    white-space: normal;
}

.tqxl-nut-cot[b-woi8taf0tk] {
    display: flex;
    gap: .15rem;
    justify-content: center;
    margin-top: .15rem;
}

.tqxl-nut-cot .btn[b-woi8taf0tk] {
    font-size: .7rem;
    line-height: 1.1;
}

/* Sọc hàng tự tô — hàng mang lớp «tqxl-table-hang» nên luật sọc chung của app.css chừa bảng này. Hai con số
   .028 / .045 đi CÙNG luật gốc, đổi một bên là bảng này lệch tông với mọi bảng khác của app. */
.tqxl-bang > tbody > tr:nth-child(even) > td[b-woi8taf0tk] {
    background-color: rgba(0, 0, 0, .028);
}

:root[data-theme="dark"] .tqxl-bang > tbody > tr:nth-child(even) > td[b-woi8taf0tk] {
    background-color: rgba(255, 255, 255, .045);
}

.tqxl-bang > tbody > tr:nth-child(even) > td.tqxl-cot-ten[b-woi8taf0tk] {
    background-color: var(--bs-body-bg, #fff);
    background-image: linear-gradient(rgba(0, 0, 0, .028), rgba(0, 0, 0, .028));
}

:root[data-theme="dark"] .tqxl-bang > tbody > tr:nth-child(even) > td.tqxl-cot-ten[b-woi8taf0tk] {
    background-color: var(--bs-body-bg, #fff);
    background-image: linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .045));
}

/* Ô đã bỏ tick riêng: viền cam quanh ô tick. Viền bằng `outline` nên ⛔ đổi kích thước ô. Màu ⛔ là kênh duy
   nhất — ô mang thêm vế «đã bỏ tick riêng» trong tên đọc. */
.tqxl-rieng[b-woi8taf0tk] {
    outline: 2px solid var(--bs-warning, #ffc107);
    outline-offset: 1px;
}

.tqxl-khoa[b-woi8taf0tk] {
    margin-left: .25rem;
    font-size: .8rem;
}

/* Chữ nổi tự ẩn (khuôn đợt 2281): `position: fixed` nên ⛔ chiếm chỗ trong bố cục; `pointer-events: none` để
   ⛔ nuốt cú bấm rơi trúng nó. */
.tqxl-chu-noi[b-woi8taf0tk] {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    max-width: 22rem;
    padding: .5rem .9rem;
    border-radius: .5rem;
    background: var(--bs-success-bg-subtle, #d1e7dd);
    color: var(--bs-success-text-emphasis, #0a3622);
    border: 1px solid var(--bs-success-border-subtle, #a3cfbb);
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
    pointer-events: none;
    animation: tqxl-hien-b-woi8taf0tk .18s ease-out;
}

@keyframes tqxl-hien-b-woi8taf0tk {
    from { opacity: 0; transform: translateY(.5rem); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .tqxl-chu-noi[b-woi8taf0tk] { animation: none; }
}

/* Vùng chạm ngón tay cho ô tick trần (khuôn o737 · o962): luật NỀN tắt hộp của thẻ bọc ở chế độ CHUỘT, khối
   `pointer: coarse` nới vùng chạm quanh ô vuông. Luật nền phải đứng TRƯỚC khối chạm (cùng độ đặc hiệu, thắng
   theo THỨ TỰ); ngưỡng đọc từ biến dùng chung ở app.css, ⛔ gõ số vào đây. */
.o-tick-cham[b-woi8taf0tk] {
    display: contents;
}

@media (pointer: coarse) {
    .o-tick-cham[b-woi8taf0tk] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    .tqxl-nut-cot .btn[b-woi8taf0tk] {
        font-size: max(.7rem, var(--cham-chu-toi-thieu));
    }
}
/* /Components/Pages/QuanTri/QuanLyNguoiDung.razor.rz.scp.css */
/* ═══ VÙNG CHẠM NGÓN TAY — HAI CÔNG TẮC CỦA TRANG /quan-tri/nguoi-dung (đợt 1690, việc o971) ═══

   Hai công tắc trong tệp này, cả hai đều KIỂU ③ (ô tick và nhãn là HAI ANH EM trong
   `div.form-check.form-switch`, nối nhau bằng thuộc tính `for`):
     · «🔐 Bắt buộc đăng nhập qua AD» — đổi đường xác thực của CẢ CƠ QUAN;
     · «🔓 Mở nút đặt lại mật khẩu hàng loạt» — mở nút đặt lại mật khẩu cho toàn bộ tài khoản.
   Cả hai đều nằm trong một hàng `d-flex align-items-center gap-3 flex-wrap` cùng các huy hiệu
   trạng thái ⇒ hộp bọc phải là `inline-flex`, hoá `flex` là mỗi công tắc chiếm trọn một dòng.

   🔴 ĐÍNH CHÍNH MỘT NỬA LỜI KHAI CỦA VIỆC o971 — SỐ THÌ ĐÚNG, TỆP THÌ SAI.
     Mô tả việc ghi *"39 ô tick hiện lúc chạy, 38 ô TRẦN 32×16, sinh ra từ VÒNG LẶP trong
     `Pages/QuanTri/NguoiDung/BangTaiKhoan.razor`"*. Đo lại tận tay 01/09/2026 (Chrome thật,
     khung 390×844, `node cong-cu/do-song-o-tick-cham.mjs --trang /quan-tri/nguoi-dung`):
     **39 ô là ĐÚNG**, **ô trần 32×16 sinh ra từ vòng lặp cũng ĐÚNG** (36 ô, số đổi theo dữ
     liệu) — nhưng chúng ⛔ **KHÔNG nằm trong `BangTaiKhoan.razor`**. Bảng tài khoản chỉ có nút
     bấm và ô chọn vai trò, ⛔ không một ô tick nào trong `foreach` của nó.
     🔑 **Chỗ THẬT là `Pages/ChamCong/KhoiRaSoatTaiKhoan.razor`** — khối «🔎 Rà soát tài khoản
       lâu không dùng», gọi vào trang này ở dòng `<KhoiRaSoatTaiKhoan />`. Nó ⛔ không nằm trong
       thư mục `QuanTri` nên ai đi tìm bằng cách mở thư mục của trang sẽ ⛔ không thấy.
     ⚠ **Và đây là lý do lượt đo ĐẦU TIÊN của chính đợt này chỉ ra 3 ô**: khối rà soát nạp
       BẤT ĐỒNG BỘ (một câu truy vấn nặng trên 890k lượt quẹt), lúc chưa xong nó chỉ hiện dòng
       *"Đang rà…"* ⇒ 36 công tắc ⛔ chưa có mặt trong DOM. Một lượt đo bắt đúng nhịp ấy ra con
       số **trông có lý mà thiếu 92% số ô**. ⇒ Trang nào có khối nạp chậm thì **đo ít nhất hai
       lượt** và đối chiếu số ô, ⛔ đừng tin lượt đầu.
     ⓘ Màn hình nhiều ô tick nhất app vẫn là `/quan-tri/phan-quyen` (đo cùng lượt: **90 ô**, cả
       90 đã nới xong từ trước).

   📏 SỐ ĐO TRƯỚC KHI VÁ (cùng lệnh, cùng khung) — hai công tắc cho cùng một bộ số:
     · ô vuông 32×16 (`float: left`) · nhãn cao 24 · hộp `.form-check` cao 24 (`display: block`);
     · vùng bấm thật tại cột CÔNG TẮC: 17px · tại cột CHỮ: 25px — cả hai dưới ngưỡng 44px.
   ⇒ Bệnh y hệt ca `TrangBangGiaMucXdcb` (đợt 1619) nhưng với hằng số của `.form-switch`:
     Bootstrap khai `.form-switch { padding-left: 2.5em }` và
     `.form-switch .form-check-input { width: 2em; margin-left: -2.5em }` ⇒ cột công tắc rộng
     2em, nhãn phải lùi đúng 2,5em mới phủ trọn. ⛔ ĐỪNG chép cặp 1,5em của ô tick VUÔNG sang
     đây: nhãn khi ấy còn hở 0,5em ngay cạnh công tắc, mà ⛔ không bài test nào ngoài
     `Kieu_ba_dang_cong_tac_phu_tron_cot_rong_2em` bắt được.

   🔑 VÌ SAO ĐẶT Ở TỆP CÁCH LY, ⛔ KHÔNG PHẢI Ở `app.css`:
     · `app.css` dùng chung cho MỌI màn hình của 433 tài khoản, phần lớn ngồi chuột — nới
       `.form-check` từ bên đó là đổi bố cục cả app để giải bài của một trang, đúng loại rủi ro
       lan truyền quy tắc #18 dặn phải tránh.
     · CSS cách ly biên dịch ra `.form-check[b-xxx]` (0,2,0) nên nó thắng bộ chọn cùng tên ở
       `app.css` (0,1,0), và chỉ chạm đúng phần tử viết trong `QuanLyNguoiDung.razor`.
     · ⚠ Ô tick của MA TRẬN TÀI KHOẢN ⛔ KHÔNG thuộc phạm vi tệp này — `MaTranTaiKhoan` là component
       con nên nó mang dấu scope RIÊNG. Luật cho nó nằm ở `NguoiDung/MaTranTaiKhoan.razor.css`
       (10/09/2026 — thay lưới `BangTaiKhoan` + khối rà soát, cả hai đã gỡ).

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là
     ngón tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm
     trong câu media, ⛔ không một dòng nào ở ngoài. Số đo `--chuot` nằm ở cuối tệp.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng ghép bằng chữ `and` trong tệp `.razor.css`: bộ biên
     dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học đợt 1481).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo. Cặp ký tự đó lọt vào GIỮA câu văn là chú
     thích đóng sớm, phần chữ còn lại dính vào bộ chọn của luật ngay dưới và trình duyệt bỏ
     nguyên luật đó — ⛔ không một câu lỗi nào. Vì thế ở đây gọi tên cặp ký tự ấy bằng CHỮ.
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* ① HỘP BỌC — `div.form-check.form-switch` của hai công tắc nói ở đầu khối.
       Nâng sàn chiều cao và đổi sang hộp flex để công tắc thôi dính mép trên: trong hộp flex
       thuộc tính `float` bị BỎ QUA, nên ô nhận phép canh giữa dọc như mọi phần con khác.

       🔑 BỀ NGANG ⛔ KHÔNG ĐỔI — lề âm Bootstrap gán cho công tắc ăn khớp đúng với phần đệm trái
         2,5em của hộp: hộp mở đầu ở 2,5em · công tắc mang lề trái −2,5em nên khung của nó nằm ở
         0…2em (Y HỆT chỗ cũ) · bề rộng ngoài vì thế là −0,5em, cộng `gap` 0,5em ⇒ nhãn bắt đầu
         đúng ở 2,5em, cũng Y HỆT chỗ cũ. Con số 0,5em của `gap` là thứ giữ khe hở giữa công tắc
         và chữ; bỏ nó là chữ dính sát công tắc.
       ⚠ ⛔ KHÔNG đụng `padding-left` của Bootstrap — chính phần đệm ấy giữ bề ngang.
       ⚠ `inline-flex` chứ ⛔ không `flex`: hai hộp này nằm trong hàng `d-flex align-items-center
         gap-3 flex-wrap` xếp ngang cùng các huy hiệu «Đang dùng: …» / «Đang khóa»; hoá `flex` là
         mỗi hộp chiếm trọn một dòng và thẻ đang gọn bỗng cao gấp đôi.
       ⚠ `min-height` chứ ⛔ không `height`: nhãn của công tắc thứ hai dài, khổ hẹp là xuống hai
         dòng — lúc ấy cứ để nó cao hơn; đặt `height` là bóp lại, sửa một lỗi lại đẻ một lỗi. */
    .form-check[b-7b8gbbpcq5] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        gap: .5em;
    }

    /* ② NHÃN CHỮ — vùng bấm thật (nối với công tắc bằng thuộc tính `for`, bấm vào nhãn là gạt).

       `align-self: stretch` là dòng ⛔ KHÔNG ĐƯỢC BỎ: hộp cha canh giữa các phần con, nên để
       nhãn tự co theo chiều cao con chữ thì vùng bấm thật chỉ còn ~21px trong khi hộp đã 44px —
       đúng kiểu đèn xanh giả mà ô đột biến số 23 của đợt 1619 đã bắt được.

       🔴 CẶP `margin-left` + `padding-left` LÀ HAI CON SỐ CỦA `.form-switch`, ⛔ KHÔNG PHẢI
         1,5em: nhãn bắt đầu SAU công tắc (hộp có đệm trái 2,5em, công tắc nằm ở 0…2em nhờ lề
         âm), nên khoảng NGAY TRÊN và NGAY DƯỚI công tắc ⛔ không thuộc nhãn mà thuộc
         `div.form-check` — một thẻ `div` ⛔ KHÔNG bấm được. Kéo nhãn sang trái đúng 2,5em rồi
         trả lại bằng phần đệm cùng cỡ: nhãn PHỦ TRỌN cả cột công tắc, còn dòng chữ vẫn đứng
         nguyên chỗ cũ ở 2,5em.
         ⚠ Nâng đời Bootstrap thì soi lại đúng hai luật `.form-switch` nói ở đầu tệp — ⛔ đừng đoán.
         ⚠ Nhãn đứng SAU công tắc trong markup nên nó vẽ ĐÈ lên. Điều đó ⛔ không làm mất cú bấm:
           bấm vào nhãn là trình duyệt chuyển thẳng cho ô tick nối bằng `for`.
       ⚠ Ngưỡng nằm ở `.form-check` bên trên (biến `--cham-toi-thieu`), ⛔ đừng gõ lại số ở đây. */
    .form-check-label[b-7b8gbbpcq5] {
        align-self: stretch;
        display: flex;
        align-items: center;
        margin-left: -2.5em;
        padding-left: 2.5em;
    }
}

/* 🔒 SỐ ĐO SAU KHI VÁ — đợt 1690, cùng lệnh và cùng khung với số "trước" ở đầu tệp (app dựng
   riêng từ một `git worktree` ở cổng 5294 để ⛔ không giành cổng 5270 của phiên khác):
     · vùng bấm thật tại cột CÔNG TẮC: 17px → 45px (đạt ngưỡng 44px);
     · vùng bấm thật tại cột CHỮ: 25px → 45px;
     · lệch tâm dọc: 0px → 2px (phần chênh của khoảng dòng chữ, mắt ⛔ không thấy).
   🔒 CHỐT NGƯỢC Ở CHẾ ĐỘ CHUỘT (`--chuot`, 1369×900): trước và sau khi vá đều là ô vuông 32×16 ·
   nhãn cao 24 · hộp cao 24 (`display: block`) · vùng bấm 17px tại công tắc và 25px tại chữ ·
   lệch tâm 0px — TRÙNG KHÍT từng con số, tức máy tính ⛔ KHÔNG đổi một pixel nào. Đó là lời hứa
   của câu media, và nó đã được ĐO chứ ⛔ không phải được suy ra. */
/* /Components/Pages/QuenMatKhau.razor.rz.scp.css */
/* ═══ VÙNG CHẠM CHO NGÓN TAY — LIÊN KẾT «← Quay lại đăng nhập» (việc o1276, 05/09/2026) ═══

   📏 Đo sống 05/09/2026 (Chrome thật, khung 390×844, cảm ứng,
      `node cong-cu/do-song-khung-hep.mjs --toan-app`): trang `/quen-mat-khau` còn đúng MỘT vùng bấm
      dưới ngưỡng — thẻ `a` này đo **308,8 × 24px**, cao đúng bằng con chữ. Đây là lối lui DUY NHẤT
      của trang: gõ hụt là kẹt ở màn hình quên mật khẩu.

   🔑 Bộ luật `.dn-*` của màn hình này nằm trong khối `<style>` của `Layout/LayoutTrong.razor`
      (style THƯỜNG, cố ý — xem lời dẫn đầu tệp `.razor`); tệp cách ly này CHỈ mang khối vùng chạm,
      ⛔ đừng dời màu/bố cục vào đây.
   🔑 `inline-flex` + `min-height`: hộp bấm cao lên tới ngưỡng, chữ canh giữa; hộp `text-center` bọc
      ngoài vẫn canh giữa được vì thẻ vẫn là INLINE.
   🔒 Toàn bộ trong `@media (pointer: coarse)` — máy tính ⛔ không đổi một pixel (chốt ngược `--chuot`).
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` (`app.css`, khối đợt 1518), ⛔ không gõ số 44px vào đây.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo — ⛔ đừng gõ cặp ấy vào giữa câu văn (đợt 852).
   🚧 Hàng rào: TKNB.Tests/VungChamPhanTrangVaThongKeTests.cs */

@media (pointer: coarse) {
    .qmk-ve-dang-nhap[b-n3ca211elj] {
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
    }
}
/* /Components/Pages/RaVao/BieuDoLuotNgay.razor.rz.scp.css */
/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   BIỂU ĐỒ CỘT "LƯỢT RA VÀO 14 NGÀY" — ĐỢT 1979 (việc o1261)
   ═══════════════════════════════════════════════════════════════════════════════════════════════

   📏 VÌ SAO CÓ TỆP NÀY: đo sống 04/09/2026 (Chrome thật, 390×844, cảm ứng, `tienvm`,
      `node cong-cu/do-song-khung-hep.mjs --trang /dong-bo-ra-vao`) ⇒ **28/28** chỗ chữ nhỏ của cả
      trang nằm ở đúng hai dòng nhãn của biểu đồ này: số lượt **11,2px** (`0.7rem`) và ngày
      **10,4px** (`0.65rem`), trong khi sàn của dự án `--cham-chu-toi-thieu` = **12px**.
      Đây là **toàn bộ** chữ trên biểu đồ — đọc không ra thì cái biểu đồ ⛔ không nói được gì.

   🩸 CHỖ DỄ HIỂU NGƯỢC NHẤT: cỡ chữ ấy ⛔ KHÔNG do luật CSS nào đặt, nó là **style NỘI TUYẾN**
      viết thẳng trong `.razor`. Style nội tuyến thắng mọi luật trong tệp CSS ⇒ sàn 12px của
      `@media (pointer: coarse)` trong `app.css` (đợt 1518) **có chạy mà vẫn thua**, và ⛔ không một
      câu lỗi nào. ⇒ Chữa bằng cách **gỡ style nội tuyến ra thành lớp**, ⛔ đừng đắp `!important`.

   ⚠ `min-width` của hàng cột: bản cũ ghi thẳng `420px` trong style nội tuyến — con số ấy tính cho
      nhãn `dd/MM` cỡ 10,4px. Nâng nhãn lên 12px thì 14 nhãn cần rộng hơn, ⛔ không thì chúng chạm
      nhau. Nới lên **490px** ĐÚNG trong `pointer: coarse`; ⛔ không đụng khổ chuột (chốt ngược
      `--chuot` ở 1369×900 phải trùng khít từng con số).
   ⚠ Cả khối nằm trong khung `overflow-x: auto` của `card-body` (đợt 163) ⇒ nới bề ngang tối thiểu
      **chỉ kéo dài dải cuộn riêng của biểu đồ**, ⛔ KHÔNG làm cả trang tràn ngang. Đã đo lại: 0.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */

.bdln-hang[b-herjefxgc2] {
    height: 160px;
    /* 14 cột × ~26px vượt bề ngang điện thoại 375px — cột giữ bề ngang tối thiểu để ⛔ không bị bóp
       dẹp; khung cuộn ngang riêng nằm ở `card-body` (đợt 163). */
    min-width: 420px;
}

.bdln-so[b-herjefxgc2] {
    font-size: .7rem;
}

.bdln-ngay[b-herjefxgc2] {
    font-size: .65rem;
}

@media (pointer: coarse) {
    .bdln-so[b-herjefxgc2] { font-size: max(.7rem, var(--cham-chu-toi-thieu)); }
    .bdln-ngay[b-herjefxgc2] { font-size: max(.65rem, var(--cham-chu-toi-thieu)); }
    /* 14 nhãn `dd/MM` ở 12px cần ≈ 34px mỗi cột — xem chú thích đầu tệp. */
    .bdln-hang[b-herjefxgc2] { min-width: 490px; }
}
/* /Components/Pages/RaVao/TrangDuLieuTho.razor.rz.scp.css */
/* Vùng cuộn của hai bảng trên trang soi dữ liệu thô (đợt 671).

   Bảng dòng thô rộng theo SỐ TRƯỜNG của nguồn (mỗi khóa một cột) và dài theo số dòng đã chọn ⇒ phải cuộn
   được CẢ HAI CHIỀU trong lòng khối, kèm đầu bảng dính — cùng khuôn `.bang-cuon` của bảng chấm công
   (đợt 670), lý do đầy đủ ghi ở đó.
   ⚠ `sticky` đặt trên `th` chứ không trên `thead` (trình duyệt không ghim được cả `thead` khi bảng dùng
     `border-collapse`), và ô ghim phải có nền ĐỤC kẻo chữ dòng cuộn chạy xuyên qua. */

.bang-cuon-tho[b-kjg7upfqz4] {
    overflow: auto;
    max-height: 60vh;
}

.bang-cuon-tho > table > thead > tr > th[b-kjg7upfqz4] {
    position: sticky;
    top: 0;
    z-index: 2;
    background-color: var(--bs-table-bg, var(--bs-body-bg, #fff));
    box-shadow: inset 0 1px 0 var(--bs-border-color, rgba(0, 0, 0, .175)),
                inset 0 -1px 0 var(--bs-border-color, rgba(0, 0, 0, .175));
}
/* /Components/Pages/TinhLuong.razor.rz.scp.css */
/* ═══ THỐNG KÊ CHẤM CÔNG (/tinh-luong) — KHỐI CUỘN CỦA BẢNG CÔNG ═══
   ⭐ ĐỢT 982 — bảng phải hiện VỪA ĐỦ trong khung màn hình rồi cho cuộn trong lòng nó.

   Trước đợt này hai bảng chỉ bọc trong `.table-responsive` của Bootstrap — lớp đó CHỈ cho cuộn NGANG,
   không giới hạn chiều cao. Phòng đông người là bảng đẩy dài cả trang: cuộn tới giữa bảng thì mất hàng
   tiêu đề ngày, không còn biết ô mình đang nhìn là ngày mấy, mà chân trang cũng trôi mất hút.
   ⇒ Cùng khuôn `.bang-cuon` của bảng công theo tháng (đợt 670 · 683) — hai màn hình bày cùng một thứ
     thì phải cuộn giống nhau.

   ⚠ Chiều cao dùng `vh` (theo màn hình) chứ ⛔ không phải số điểm ảnh cứng: máy anh dùng và máy chiếu
     phòng họp cao khác nhau, ghim cứng là màn hình lớn thừa chỗ mà vẫn phải cuộn.
   ⚠ `overflow: auto` (shorthand) cố ý ĐÈ luôn `overflow-x` của `.table-responsive` — hai chiều cùng
     cuộn trong MỘT khối, ⛔ đừng để hai khối cuộn lồng nhau. */
.bang-cuon-tk[b-cxwfwu2h9c] {
    overflow: auto;
    max-height: 68vh;
}

/* ĐẦU BẢNG DÍNH — chỉ đặt cho bảng của phòng ĐÃ DUYỆT (hàng tiêu đề MỘT tầng).
   ⛔ CỐ Ý không đặt cho bảng "phòng chưa duyệt": tiêu đề bên đó là HAI tầng (thứ ở trên, số ngày ở
     dưới), mà `top: 0` cho cả hai tầng thì chúng chồng lên nhau — hỏng ngay mắt nhìn. Bảng ấy lại
     đang để trắng nên đầu bảng dính cũng không giải quyết gì.

   ⚠ `::deep` là BẮT BUỘC, ⛔ đừng viết `… > tr > th` trần: từ đợt 982 các ô đầu cột ngày do COMPONENT
     `OCotNgayLich` sinh ra nên chúng mang mã scope CỦA COMPONENT, luật viết trần sẽ trượt sạch dải
     ngày mà hai cột đầu vẫn dính — nhìn thoáng qua tưởng đã xong. Đây đúng bài học đợt 683 của bảng
     công theo tháng. */
.dau-bang-dinh > table > thead > tr[b-cxwfwu2h9c]  th {
    position: sticky;
    top: 0;
    z-index: 2;
    /* Nền đục: `sticky` chỉ nhấc ô lên trên, nền trong suốt thì chữ dòng đang cuộn chạy xuyên qua. */
    background-color: var(--bs-table-bg, var(--bs-body-bg, #fff));
    /* Viền trên/dưới vẽ lại bằng shadow — viền của `border-collapse` không cuộn theo ô sticky. */
    box-shadow: inset 0 1px 0 var(--bs-border-color, rgba(0, 0, 0, .175)),
                inset 0 -1px 0 var(--bs-border-color, rgba(0, 0, 0, .175));
}
/* /Components/Pages/TongQuan/DayNutMauTenNhiemVu.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 2028 (việc o1312) — DÃY NÚT MẪU TÊN NHIỆM VỤ ═══

   ⚠ CSS SCOPED: chỉ áp cho đúng markup của DayNutMauTenNhiemVu.razor. Sửa tên lớp ở một trong hai tệp
     mà quên tệp kia thì luật rơi lặng lẽ — build vẫn xanh (luật `luat-giao-dien-razor-css`).
   📱 Dãy nút PHẢI xuống dòng được ở khổ hẹp: markup đã `flex-wrap`, ở đây chỉ lo VÙNG CHẠM. */

/* Vùng chạm ≥ 32px chiều cao (luật vùng chạm đợt 1518) — `btn-sm` của Bootstrap 5.3 chỉ cho ~31px với
   phông mặc định, thiếu đúng một hạt. ⛔ Đừng hạ xuống cho "gọn": dãy này nằm ngay trên ô nhập, bấm
   trượt là đổ nhầm chữ vào ô tên. */
.dnm-nut[b-j6hjf2eb5g] {
    min-height: 32px;
    /* Mẫu dài (200 ký tự cho phép) ⛔ không được kéo dãy tràn ngang màn hình điện thoại — cắt bằng
       ba chấm, chữ đầy đủ vẫn đổ đúng vào ô tên khi bấm. */
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Dòng chữ đứng lẫn trong dãy nút — canh giữa theo chiều dọc để nó ⛔ không tụt so với hàng nút.
   🪦 ⭐ ĐỢT 2058 (việc o1362): lớp này TRƯỚC ĐÂY mang HAI câu; câu ô rỗng («Chưa có nút nhanh …») anh
     Tiến đã bảo bỏ ⇒ nay chỉ còn phục vụ NHÁNH LỖI (⚠ ⛔ không đọc được kho nút). ⛔ Đừng xoá luật này
     theo — nhánh cảnh báo vẫn dùng, và cảnh báo là ngoại lệ được in thường trực của luật ngôn từ. */
.dnm-cau-trong[b-j6hjf2eb5g] {
    align-self: center;
    margin: 0;
}

/* Nút ✏️ là nút PHỤ — ⛔ không được to ngang các nút mẫu, kẻo mắt đọc nó thành một nút điền nhanh nữa. */
.dnm-nut-sua[b-j6hjf2eb5g] {
    min-height: 32px;
    min-width: 32px;
    line-height: 1;
    text-decoration: none;
}

/* Ba nút xếp/bỏ trong hộp sửa — vuông, ⛔ không co lại khi ô tên dài ra. */
.dnm-nut-xep[b-j6hjf2eb5g] {
    min-height: 32px;
    min-width: 32px;
    flex: 0 0 auto;
    line-height: 1;
}

/* Ô sửa tên chiếm phần còn lại của dòng; `min-width: 0` là chốt để flexbox được phép co nó — thiếu nó
   thì ô giữ bề ngang nội dung và ba nút bên phải bị đẩy khỏi khung ở khổ 390px. */
.dnm-o-sua[b-j6hjf2eb5g] {
    flex: 1 1 auto;
    min-width: 0;
}

/* Danh sách trong hộp: dài thì cuộn TRONG hộp, ⛔ không đẩy hộp dài quá màn hình. */
.dnm-ds[b-j6hjf2eb5g] {
    max-height: 45vh;
    overflow-y: auto;
}

/* ═══ ⭐ ĐỢT 2042 (việc o1334) — TRẢ LẠI VÙNG CHẠM 44px CHO NGÓN TAY ═══

   📏 ĐO SỐNG 05/09/2026 (Chrome thật, khổ 390×844, `pointer: coarse` đã xác nhận bằng `matchMedia`,
     `node cong-cu/do-song-day-nut-mau-ten.mjs --cong 5283`): năm nút mẫu đo được **32/32/32/32/32 px**
     và nút ✏️ **32×32 px** — dưới hẳn ngưỡng `--cham-toi-thieu: 44px` mà app.css đặt ra.

   🩸 VÌ SAO app.css KHÔNG với tới đây: khối `@media (pointer: coarse)` của `app.css` viết `.btn`
     (độ đặc hiệu 0,1,0), còn ba luật ngay trên là CSS **cách ly** nên trình biên dịch Razor đổi chúng
     thành `.dnm-nut[b-…]` (0,2,0) ⇒ luật 32px **thắng** và ngón tay mất 12px vùng bấm. Đây đúng cái
     bẫy mà chính chú thích app.css đã kể với `.ma-nut` (*"sửa từ đây là viết một luật ⛔ không bao giờ
     chạy"*) — hôm nay nó tái diễn ở dãy nút này, và chỉ một lượt ĐO SỐNG mới nhìn thấy: build xanh,
     61 bài hàng rào của đợt 2028 xanh, màn hình máy tính ⛔ không đổi một pixel.

   🔑 NỚI TRONG `@media (pointer: coarse)`, ⛔ KHÔNG nới thẳng ở ba luật trên: 433 tài khoản phần lớn
     ngồi máy tính, và 32px là con số đợt 1518 đã chọn cho chuột — nâng cả hai chỗ là đổi bố cục toàn
     app để giải một bài toán của điện thoại (quy tắc #18).
   🔑 Dùng ĐÚNG biến `--cham-toi-thieu`, ⛔ đừng gõ lại số 44: ngày anh Tiến đổi ngưỡng thì chỗ này
     phải đi theo, mà một con số chép tay thì ⛔ không ai nhớ ra nó nằm đây.
   🚧 Hàng rào canh: `LuatMauTenNhiemVuTests.Ba_lop_vung_cham_deu_duoc_noi_o_che_do_NGON_TAY`. */
@media (pointer: coarse) {

    .dnm-nut[b-j6hjf2eb5g] {
        min-height: var(--cham-toi-thieu);
    }

    /* Nút ✏️ và ba nút ▲▼✕ là nút VUÔNG chỉ có biểu tượng ⇒ phải nới CẢ hai chiều, không thì ngón cái
       bấm trượt sang ô tên ngay bên cạnh — mà ô ấy đang mang chữ người ta vừa gõ. */
    .dnm-nut-sua[b-j6hjf2eb5g],
    .dnm-nut-xep[b-j6hjf2eb5g] {
        min-height: var(--cham-toi-thieu);
        min-width: var(--cham-toi-thieu);
    }
}

/* ═══ 📱 ĐỢT 2181 (việc o1624) — DÁNG RỖNG: NÚT ✏️ ⛔ KHÔNG CÒN ĐỨNG LẺ LOI Ở MÉP TRÁI ═══

   📏 ĐO SỐNG 375px (đợt 2176, việc o1620) — người dùng CHƯA có nút mẫu nào thì cả hàng này chỉ chứa
     mỗi nút ✏️, mà nút ấy nay cao 44px theo luật vùng chạm (đợt 2042 ngay trên) ⇒ **gần 48px chiều
     dọc cho một biểu tượng**, nằm lẻ ở mép TRÁI ngay dưới hàng [Hạn][ngày][Tạo]. Mắt đọc nó thành
     một nút hành động mồ côi chứ ⛔ không thành "chỗ sửa dãy nút nhanh".

   🔑 VÌ SAO PHẢI ĐỤNG MARKUP mới làm được (đợt 2176 chỉ sửa CSS nên đã bỏ qua): khoảng cách trên
     của hàng do lớp tiện dụng `mt-1` của Bootstrap đặt, mà lớp ấy mang `!important` ⇒ mọi luật CSS
     viết ở đây đều **⛔ không bao giờ thắng** nó — bản vá kiểu ấy build xanh, test xanh, màn hình
     ⛔ không đổi một pixel. Đường đúng là **⛔ đừng gắn `mt-1` lúc rỗng** rồi trả lại khoảng cách ấy
     bằng luật dưới đây; xem `RongTron` trong `DayNutMauTenNhiemVu.razor.cs`.

   ⛔ ĐỪNG "chữa" bằng cách in lại một câu chú thích cạnh nút — đợt 2058 anh Tiến đã ra lệnh bỏ đúng
     câu ấy («Chưa có nút nhanh …»), và một dòng chữ ăn thêm một hàng trên chính màn hình đang thiếu
     chỗ. Đường ra vẫn là cái nút, chỉ đổi chỗ đứng.
   ⛔ ĐỪNG hạ nút xuống dưới 44px cho "gọn": đó là ngưỡng `--cham-toi-thieu`, và nút này nằm ngay
     trên ô tên — bấm trượt là đổ nhầm chữ vào thứ người ta vừa gõ. */

/* Máy tính ⛔ KHÔNG ĐỔI MỘT PIXEL: `.25rem` ở đây đúng bằng `mt-1` vừa gỡ khỏi markup. */
.dnm-day-rong[b-j6hjf2eb5g] {
    margin-top: .25rem;
}

/* 📐 Mốc 576px là `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx` — mọi khung của trang chủ đổi dáng
   ở CÙNG một bề ngang. Đổi mốc thì đổi ở luật ấy, ⛔ đừng sửa lẻ một tệp. */
@media (max-width: 576px) {

    /* Nút ✏️ về sát mép PHẢI ⇒ nó nằm ngay dưới nút Tạo (nút ấy đã nở hết phần còn lại của dòng ở
       đợt 2176) và đọc thành phần đuôi của hàng trên, ⛔ không còn là một nút mồ côi mép trái.
       Bỏ luôn khoảng cách trên: hàng này lúc rỗng ⛔ không chở nội dung nào để mà tách khỏi ô tên. */
    .dnm-day-rong[b-j6hjf2eb5g] {
        margin-top: 0;
        justify-content: flex-end;
    }
}
/* /Components/Pages/TongQuan/HopLichTuanTrangChu.razor.rz.scp.css */
/* ═══════════ ⭐ VIỆC o1786 (đợt 2272) → ĐỢT 2313 (việc o1866) — NẮN TỜ LỊCH CHO VỪA HỘP THOẠI ═══════════
   🔁 Đợt 2313 dời nguyên khối này từ `NutLichTuanTrangChu.razor.css` sang đây theo cái hộp (hộp nay là
     component riêng). Lớp bọc giữ tên cũ `.nlt-hop` — công cụ đo `cong-cu/do-song-lich-hom-nay-trang-chu.mjs`
     tìm hộp bằng đúng lớp ấy.
   ⚠ `::deep` là BẮT BUỘC ở đây: `KhoiLichTuanTrangChu` có tệp `.razor.css` RIÊNG nên các thẻ của nó
     mang dấu cách ly của CHÍNH NÓ, ⛔ không mang dấu của tệp này. Viết trần `.nlt-hop .ltc-the` sẽ
     biên dịch thành `.nlt-hop .ltc-the[b-xxxx]` và ⛔ không bao giờ khớp — build xanh, test xanh,
     màn hình ⛔ không đổi một pixel (bài học đợt 2176 ②). */

/* ── Tờ lịch nằm TRONG hộp thoại thì thôi làm thẻ nữa: hộp đã có viền, có bóng, có tiêu đề riêng ──
   ⓘ `!important` ở đây là ca CHÍNH ĐÁNG hiếm hoi: `mb-4` là lớp tiện dụng của Bootstrap, mà lớp
     tiện dụng mang sẵn `!important` ⇒ ⛔ không luật nào của tệp cách ly thắng nổi nó. Luật ⑤ đợt 2181. */
/* 🔑 ĐỘ ĐẶC HIỆU LÀ THỨ QUYẾT ĐỊNH LUẬT NÀY CÓ CHẠY HAY KHÔNG — đã ĐO và đã trả giá một lượt:
     bản đầu viết `.nlt-hop ::deep .ltc-the` ⇒ biên dịch ra `.nlt-hop[b-xxxx] .ltc-the` = **(0,3,0)**,
     mà `app.css` dựng viền thẻ bằng `:root[data-theme="light"] .card:not(.trang-chu *)` = **(0,4,0)**
     ⇒ THUA (bài học ⑪ đợt 2197). ⇒ Thêm `.card` vào bộ chọn cho đủ **(0,4,0)**; hòa độ đặc hiệu thì
     gói scoped `TKNB.Web.styles.css` THẮNG vì nó nạp SAU `app.css`. Vế `:hover` phải viết riêng: luật
     đổi màu viền lúc rê chuột ở `app.css` là (0,5,0).
   🩸 ĐỢT 2313 (việc o1866) — thêm `transform: none`: thẻ tờ lịch trong hộp vẫn là `.card` trong
     `.trang-chu`, nên luật nhấc thẻ `.trang-chu .card:not(.tt3d):hover` (0,4,0, `trang-chu.css` nạp SAU gói
     scoped) nhấc CẢ TỜ LỊCH lên 2px mỗi lần rê chuột vào hộp — dòng tên cột đang dính đỉnh khung cuộn giật
     theo. Vế `:hover` ở đây là (0,5,0) nên thắng dù nạp trước. */
.nlt-hop[b-qkt7uhvv0t]  .card.ltc-the,
.nlt-hop[b-qkt7uhvv0t]  .card.ltc-the:hover {
    border: 0;
    box-shadow: none;
    background: transparent;
    transform: none;
    margin-bottom: 0 !important;
}
/* Thân thẻ thôi đệm — hộp thoại đã có đệm của nó, cộng hai lớp là tờ lịch tụt vào giữa một khung
   trống dày, mà bề ngang thì đúng thứ bảy ngày đang cần. */
.nlt-hop[b-qkt7uhvv0t]  .ltc-the > .card-body {
    padding: 0;
}
/* 🔁 ĐỢT 2286 (việc o1816) — hộp bày cả tuần đủ TÁM CỘT: dòng tên cột ĐỨNG YÊN khi cuộn, không thì tới thứ
   năm đã ⛔ còn biết cột nào là «Chủ trì», cột nào là «Phân công chuẩn bị». Khung cuộn là thân hộp thoại.
   ⚠ Chỉ đặt trong hộp, ⛔ đặt ở tệp của khối: trên thân trang chủ, dòng tên cột dính đỉnh cửa sổ khi cuộn
     qua khối là một dải lạ trôi theo người đọc. */
/* 🩸 ĐỢT 2352 (việc o1862) — `top` ÂM ĐÚNG BẰNG ĐỆM TRÊN CỦA THÂN HỘP. Khung cuộn là `.modal-body`, mà Chrome tính
     chỗ dính từ mép trong LỚP ĐỆM của khung cuộn ⇒ `top: 0` để dòng tên cột dừng cách đỉnh khung `1rem`, và dải
     `1rem` ấy lộ chữ của dòng đã cuộn qua ngay TRÊN dòng tên cột (ảnh anh Tiến 12/09/2026: «Phòng họp GĐ» · «Hương
     (N) 0937396233» chen giữa tiêu đề hộp và dòng tên cột).
   ⚠ Đọc biến `--bs-modal-padding` (Bootstrap 5.3 đặt đệm thân hộp bằng chính biến ấy), ⛔ gõ con số: ai đổi đệm
     hộp thì dòng dính đi theo, ⛔ hở lại một dải mới. */
.nlt-hop[b-qkt7uhvv0t]  .ltc-bang > thead > tr > th {
    position: sticky;
    top: calc(-1 * var(--bs-modal-padding, 1rem));
    z-index: 2;
}
/* Lượt tự tua tới hôm nay đặt ngày ấy ở ĐỈNH khung cuộn — chừa đúng chỗ cho dòng tên cột đang dính ở đó,
   không thì chính hàng hôm nay nằm khuất dưới dòng tên cột.
   🔁 ĐỢT 2352 (việc o1862) — dòng tên cột nay dính sát đỉnh khung (⛔ còn cách `1rem`), nên phần chừa là ĐÚNG
   chiều cao dòng tên cột: cao nhất khi «Phân công chuẩn bị» gãy hai dòng (đo sống 12/09/2026). */
.nlt-hop[b-qkt7uhvv0t]  .ltc-ngay {
    scroll-margin-top: 4.2rem;
}

/* ✏️ ĐỢT 2325 (việc o1887) — CHẾ ĐỘ SỬA TRONG HỘP: dải nút «📢 Lưu và ban hành · ✕ Hủy» ĐỨNG YÊN ở đỉnh khung cuộn.
   Tờ bảy ngày dài hơn hộp; sửa tới chủ nhật mà phải cuộn ngược lên đầu mới thấy nút lưu là bắt người ta đi tìm nút.
   ⚠ `::deep` bắt buộc — dải nút thuộc `KhoiToLichCongTacTuan` (tệp CSS cách ly riêng), cùng lẽ đoạn đầu tệp.
   ⚠ Nền phải ĐẶC (token nền trang): dải trong suốt thì hàng lịch cuộn lên chui qua sau chữ nút, đọc lẫn vào nhau. */
.nlt-hop[b-qkt7uhvv0t]  .tlct-thanh {
    position: sticky;
    top: 0;
    z-index: 3;
    padding: .4rem 0;
    background: var(--bs-body-bg);
}
/* /Components/Pages/TongQuan/KhoiBangCongPhongTrangChu.razor.rz.scp.css */
/* ═══════════ ⭐ ĐỢT 684c — NÚT MỞ BẢNG CHẤM CÔNG Ở TRANG CHỦ ═══════════

   Anh Tiến 15/08/2026: *"nút xem bảng chấm công tháng này ở trang chủ em căn giữa và đồ họa cho thật đẹp,
   khung nổi bật có hiệu ứng"*.

   ⓘ VÌ SAO NÚT NÀY ĐÁNG NỔI BẬT: từ đợt 684 bảng công trên trang chủ **thu lại mặc định** (anh chốt
     *"bảng chấm công thu lại khi nào cần mới bung ra"*) ⇒ đây là lối vào DUY NHẤT của bảng ở trang chủ.

   ═══ 🔴 ĐỢT 957 — CÁI ÁO 3D DỜI HẲN SANG KHỐI DÙNG CHUNG `.tt3d` CỦA `app.css` ═══
   Anh Tiến 21/08/2026, hai tin liền nhau: *"nút bảng chấm công 3d nổi lên ở trang chủ"* · *"nút bảng
   chấm công cũng cùng màu"* (cùng màu với "Lý do đi trễ / về sớm" + "Xin nghỉ phép").
   ⇒ Markup nay mang `class="nut-mo-bang tt3d tt3d-cong"`. Nền, viền, bóng nổi, vệt sáng góc, vệt quét
     ngang, nhún khi nhấn, vòng focus, luật "giảm chuyển động" — **TẤT CẢ do khối đợt 931 trong
     `app.css` lo**, đúng một bản cho cả sáu chỗ dùng ở trang chủ.
   ⛔ ĐỪNG CHÉP CÔNG THỨC 3D TRỞ LẠI ĐÂY. CSS scoped của Blazor không dùng chung được giữa các
     component, nên chép là ba bản phải sửa cùng lúc mỗi lần anh Tiến đổi một câu về màu — đúng thứ
     khối đợt 931 sinh ra để tránh. Bản cũ của tệp này từng tự khai một bộ gradient navy + bóng +
     vệt sáng riêng: bỏ rồi, ⛔ đừng dựng lại.
   🔑 ĐỘ ĐẶC HIỆU — chỗ phải hiểu đúng nếu muốn sửa: luật ở `app.css` viết
     `:root:is([data-theme="light"],[data-theme="dark"]) .nut-mo-bang.tt3d` = **(0,4,0)**, còn luật
     trong tệp này Blazor biên dịch thành `.nut-mo-bang[b-xxxx]` = **(0,2,0)** ⇒ khối chung LUÔN thắng
     bất kể thứ tự nạp. Vì vậy tệp này chỉ còn giữ **HÌNH DÁNG** của nút (viên thuốc, cỡ chữ, trạng
     thái đang tải) — thêm nền/bóng vào đây là viết một luật không bao giờ có tác dụng.

   ⚠ HAI GIAO DIỆN CỔ ĐIỂN GIỮ DÁNG PHẲNG (luật "cổ điển phẳng nguyên trạng" — quy tắc #20): khối
     `.tt3d` cố ý chỉ mở cho `light`/`dark`, nên phần dưới đây vẫn phải tự lo cái áo phẳng cho chúng.
   ⚠ Cỡ chữ nhỏ nhất ở đây là .95rem = 15,2px, trên ngưỡng .65rem của `CoChuONgayTests`. */

/* ═══ 🔴 ĐỢT 1253 — HÌNH DÁNG NÚT ĐÃ DỜI HẲN SANG `app.css`, TỆP NÀY KHÔNG CÒN GIỮ BẢN NÀO ═══
   Anh Tiến 25/08/2026 xin thêm hai nút lớn nữa đứng cạnh nút này (🎯 Mục tiêu · 🗃 Công việc). Hai nút
   ấy nằm ở HAI COMPONENT KHÁC, mà **CSS scoped của Blazor không dùng chung được giữa các component**
   ⇒ giữ `.nut-mo-bang` ở đây thì hai nút mới không hưởng một dòng nào, còn chép sang là ba bản phải
   sửa cùng lúc mỗi lần anh đổi một câu về dáng.
   ⇒ Các luật `.dai-nut-lon` · `.nut-mo-bang` · `.nmb-*` nay khai MỘT chỗ trong `app.css`, ngay dưới
     khối `.tt3d` — đúng đường mà đợt 957 đã đi với cái áo 3D.
   ⛔ ĐỪNG chép chúng trở lại đây: luật scoped biên dịch ra (0,2,0) sẽ THẮNG luật (0,1,0) của `app.css`
     trong im lặng, và phiên sau sẽ sửa vào bản không có tác dụng.
   ⓘ Lớp bọc `.khung-nut-bang` cũng bỏ luôn: dải bọc nay là `.dai-nut-lon` ở trang chủ, vì nó phải ôm
     CẢ BA nút — một lớp bọc nằm trong component chỉ ôm được nút của chính nó, tức ba nút ba hàng.

   ⚠ Mục cổ điển bên dưới CỐ Ý Ở LẠI (quy tắc #20 — cổ điển đóng băng, không xóa): nó là luật dành
     riêng cho `data-theme^="co-dien"`, không phải bản chép của thứ vừa dời. */

/* ── HAI GIAO DIỆN CỔ ĐIỂN: phẳng, vuông, viền rõ ── */
:root[data-theme^="co-dien"] .nut-mo-bang[b-lpyboxpbr8] {
    border-radius: 4px;
    padding: .45rem 1rem;
    background: linear-gradient(180deg, #0a3d91, #002169);
    border: 1px solid #001744;
    box-shadow: none;
    transition: none;
}

:root[data-theme^="co-dien"] .nut-mo-bang:hover:not(:disabled)[b-lpyboxpbr8] {
    transform: none;
    box-shadow: none;
    filter: brightness(1.1);
}

:root[data-theme^="co-dien"] .nut-mo-bang[b-lpyboxpbr8]::after { display: none; }
:root[data-theme^="co-dien"] .nmb-icon[b-lpyboxpbr8] { font-size: 1.15rem; filter: none; }
:root[data-theme^="co-dien"] .nmb-ten[b-lpyboxpbr8] { font-size: .92rem; font-weight: bold; }

/* ── Khổ hẹp: nút xuống hai dòng chữ vẫn đọc được, không tràn ── */
@media (max-width: 576px) {
    .nut-mo-bang[b-lpyboxpbr8] { padding: .65rem 1rem; gap: .6rem; }
    .nmb-ten[b-lpyboxpbr8] { font-size: .95rem; }
}
/* /Components/Pages/TongQuan/KhoiBanLamViec.razor.rz.scp.css */
/* ═══════════ BÀN LÀM VIỆC Ở TRANG CHỦ (đợt 668) ═══════════

   Anh Tiến 14/08/2026: *"trang chủ định nghĩa như BÀN LÀM VIỆC… nhanh gọn lẹ, bớt thao tác rườm rà"*,
   kèm yêu cầu trước đó: *"decord cho đẹp và hiện đại"*.

   🎨 NGÔN NGỮ HÌNH — cùng bộ với ô lối tắt của đợt 450 (khối nổi, bo góc, nhấc nhẹ khi rê chuột) để trang
      chủ không có hai phong cách nút cạnh nhau.
   🎨 MÀU: dùng `currentColor` + nền bán trong suốt ⇒ tự đúng ở CẢ 4 GIAO DIỆN mà không khai màu 4 lần
      (khuôn IconApp đợt 648). ⛔ Đừng ghim mã màu chữ vào đây: nền sáng và nền tối có màu chữ khác hẳn.
   📏 Chữ nhỏ nhất .75rem = 12px, trên ngưỡng .65rem của `CoChuONgayTests`.

   ⚠ THẺ ĐÓNG SẴN là quyết định về HIỆU NĂNG, không phải về thẩm mỹ: dựng khối tốn 0 truy vấn, chỉ lượt
     bấm mở mới đi hỏi CSDL (lý do đầy đủ ở đầu tệp .razor). Đừng đổi thành mở sẵn. */

.ban-lam-viec[b-oxh4m1e2lt] {
    margin-bottom: 1rem;
}

.blv-day[b-oxh4m1e2lt] {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: flex-start;
}

/* Mỗi thẻ là một VIỆC. Đóng lại thì chỉ là một nút; mở ra thì nở rộng nhưng KHÔNG đẩy thẻ khác đi. */
/* ═══ ⭐ ĐỢT 688 — LÀM ĐẬM BA THẺ (anh Tiến 15/08/2026: *"3 nút này em làm đậm lên nhé, khó thấy quá"*) ═══

   📏 GỐC LỖI ĐO ĐƯỢC, không phải chuyện gu thẩm mỹ: thẻ đang dùng **nền bán trong suốt**
     `rgba(128,128,128,.06)` — tức gần như trong veo. Cách này đúng và tiện suốt từ đợt 668 vì nó tự hợp
     với cả 4 giao diện mà không phải khai màu 4 lần… **cho tới khi đợt 687 đặt ẢNH KHO làm nền trang chủ**.
     Nền hết phẳng thì thẻ trong veo hết ranh giới: chữ nằm trên trời xanh, viền `.28` mờ tan vào ảnh.
   ⇒ Ba thẻ này nay phải **ĐỤC** — chúng là chỗ khai đơn, không phải trang trí.

   ⛔ VẪN KHÔNG ghim mã màu CHỮ (giữ nguyên `currentColor` như ghi ở đầu tệp): chỉ khai NỀN + VIỀN, mỗi
     giao diện một dòng. Ghim màu chữ là hỏng ở hai giao diện tối.
   ⚠ Đây là bài học đáng nhớ: **thêm một lớp nền ở trang chủ là đổi ngầm điều kiện của MỌI thứ nằm trên
     nó**. Thứ trong suốt đang dựa vào giả định "phía sau là màu phẳng" — giả định đó vừa hết đúng. */
.blv-the[b-oxh4m1e2lt] {
    flex: 1 1 20rem;
    min-width: 0;
    border: 2px solid rgba(10, 61, 145, .30);
    border-radius: .75rem;
    background: rgba(255, 255, 255, .97);
    overflow: hidden;
    transition: box-shadow .15s ease, transform .15s ease;
    box-shadow: 0 .2rem .6rem rgba(0, 23, 68, .12);
}

.blv-the:hover[b-oxh4m1e2lt] { box-shadow: 0 .45rem 1.1rem rgba(0, 23, 68, .20); }
.blv-the.mo[b-oxh4m1e2lt]    { box-shadow: 0 .45rem 1.1rem rgba(0, 23, 68, .20); }

/* Hai giao diện TỐI: nền đục theo tông nền tối, viền sáng lên cho tách khỏi ảnh. */
:root[data-theme="dark"] .blv-the[b-oxh4m1e2lt] {
    background: rgba(24, 27, 34, .97);
    border-color: rgba(120, 180, 255, .34);
    box-shadow: 0 .2rem .6rem rgba(0, 0, 0, .45);
}
:root[data-theme="co-dien-toi"] .blv-the[b-oxh4m1e2lt] {
    background: rgba(34, 34, 34, .97);
    border-color: rgba(160, 160, 160, .40);
    box-shadow: none;
}
/* Cổ điển SÁNG: giữ nếp phẳng — nền đục, viền rõ, KHÔNG đổ bóng, góc vuông hơn. */
:root[data-theme="co-dien"] .blv-the[b-oxh4m1e2lt] {
    background: #fff;
    border-color: rgba(10, 61, 145, .38);
    border-radius: .25rem;
    box-shadow: none;
}
:root[data-theme^="co-dien"] .blv-the:hover[b-oxh4m1e2lt],
:root[data-theme^="co-dien"] .blv-the.mo[b-oxh4m1e2lt] { box-shadow: none; }

/* Nút đầu thẻ — cả dải là vùng bấm (đích lớn, đúng tinh thần "bớt thao tác"). */
.blv-dau[b-oxh4m1e2lt] {
    display: flex;
    align-items: center;
    gap: .6rem;
    width: 100%;
    padding: .65rem .85rem;
    border: 0;
    background: none;
    color: inherit;
    /* ⭐ ĐỢT 688 — 600 → 700: đậm thêm một nấc cho tách khỏi nền ảnh (anh: "khó thấy quá"). */
    font-weight: 700;
    text-align: left;
    cursor: pointer;
}

.blv-icon[b-oxh4m1e2lt] { font-size: 1.45rem; line-height: 1; flex: none; }
/* ⭐ ĐỢT 688 — nhích cỡ chữ lên .98rem (≈15,7px). ⚠ KHÔNG hạ xuống dưới .65rem — ngưỡng của
   `CoChuONgayTests`; đây là nhích LÊN nên an toàn. */
.blv-ten[b-oxh4m1e2lt]  { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
            font-size: .98rem; }
/* ⛔ ĐỢT 1303 — LUẬT `.blv-mui` ĐÃ GỠ CÙNG VỚI CHÍNH HAI DẤU MŨI ▾.
   Anh Tiến 25/08/2026: *"3 nút hàng bảng chấm công phải đều nhau và chung format"* — nút 📆 đứng
   đầu hàng ⛔ KHÔNG có mũi, nên hai thẻ còn mũi là ba nút ⛔ không bao giờ giống nhau được.
   🔑 Gỡ mũi còn là ĐÍNH CHÍNH một lời hứa sai, đúng lý lẽ đợt 873 đã ghi cho chính nút 📆:
     *"Mũi ▾ cũng bỏ: nó hứa «bung xuống dưới», còn hộp thoại thì nổi lên trên"*. Từ đợt 679 thân
     hai thẻ này là `position: fixed` nổi GIỮA MÀN HÌNH — tức chúng cũng là hộp thoại, ⛔ không phải
     khối bung xuống. Mũi ▾ ở đây đã nói sai từ hồi ấy.
   ⛔ Đừng khai lại một luật cho lớp ⛔ không còn ai mang: đó là mã sống mà ⛔ không chạy — phiên sau
     sửa nó rồi ⛔ không hiểu vì sao màn hình ⛔ không đổi (đúng bài học đợt 875 ngay dưới).
   ↩ Muốn trả mũi về thì thêm lại CẢ HAI: `<span class="blv-mui">` trong `.razor` VÀ luật này. */

.blv-than[b-oxh4m1e2lt] {
    padding: .1rem .8rem .8rem;
    border-top: 1px solid rgba(128, 128, 128, .22);
}

.blv-hang[b-oxh4m1e2lt] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
}

.blv-toi[b-oxh4m1e2lt] { opacity: .7; }

/* ═══ 📱 ĐỢT 2181 (việc o1624) — DÒNG TIÊU ĐỀ TRONG THÂN HỘP KHAI ĐƠN ═══

   🔴 VÌ SAO CẦN: thân hộp khi mở là HỘP NỔI giữa màn hình (`position: fixed`, đợt 679) và nút đầu thẻ
     — chỗ DUY NHẤT ghi tên đơn — nằm DƯỚI nền phủ nên bị làm mờ. Trên điện thoại hộp chiếm gần trọn
     màn hình, người ta ⛔ không còn manh mối nào để biết mình đang khai đơn ⏰ hay đơn 🌴.
   🔑 Chữ do markup đọc từ `TenTheDangMo`, tức ĐÚNG chuỗi của nhãn ngoài `.blv-ten`.
   ⚠ `padding-right` là BẮT BUỘC: nút ✖ là `position: absolute` ở góc trên phải (luật `.blv-nut-dong`
     dưới đây) nên tiêu đề dài mà ⛔ không chừa chỗ là chữ chui thẳng xuống dưới biểu tượng. */
.blv-tieu-de[b-oxh4m1e2lt] {
    font-weight: 600;
    font-size: 1.02rem;
    line-height: 1.25;
    padding-right: 2.4rem;
    margin-bottom: .55rem;
}

/* Một dòng = một đơn chờ duyệt. Hai nút ✔ ✖ luôn ở cuối dòng, không bị chữ dài đẩy đi. */
.blv-dong[b-oxh4m1e2lt] {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .35rem 0;
    border-bottom: 1px dashed rgba(128, 128, 128, .25);
}

.blv-dong:last-of-type[b-oxh4m1e2lt] { border-bottom: 0; }

.blv-nhan[b-oxh4m1e2lt]  { flex: none; }
.blv-mo-ta[b-oxh4m1e2lt] { flex: 1 1 auto; min-width: 0; font-size: .82rem; }

/* Lý do xuống dòng riêng, mờ hơn: nó là chi tiết, còn ai-ngày-mấy mới là thứ quyết định. */
.blv-ly-do[b-oxh4m1e2lt] {
    display: block;
    font-size: .75rem;
    opacity: .75;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Dải báo kết quả — xanh khi xong, đỏ khi bị từ chối. Đây là MÀU NGHĨA nên được ghim mã màu. */
.blv-bao[b-oxh4m1e2lt] {
    margin-top: .5rem;
    padding: .4rem .7rem;
    border-radius: .5rem;
    font-size: .85rem;
    background: rgba(25, 135, 84, .18);
    border: 1px solid rgba(25, 135, 84, .35);
}

.blv-bao.loi[b-oxh4m1e2lt] {
    background: rgba(220, 53, 69, .16);
    border-color: rgba(220, 53, 69, .38);
}

/* Người khai "giảm chuyển động" trong Windows: bỏ hiệu ứng, giữ nguyên hình khối và màu. */
@media (prefers-reduced-motion: reduce) {
    .blv-the[b-oxh4m1e2lt] { transition: none; }
}

/* ── ĐỢT 676 — HÀNG LỰA CHỌN NHANH (nút điền sẵn ngày / lý do) ──
   Anh Tiến: *"tại ô xin nghỉ phép phải có thêm vài lựa chọn nhanh"*.
   ⚠ Các nút phải XUỐNG DÒNG được (`flex-wrap`): thẻ rộng khoảng 20rem, bốn nút ngày đứng một hàng sẽ
     tràn ở khổ hẹp và đẩy vỡ cả thẻ. */
.blv-nhanh[b-oxh4m1e2lt] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
}

/* Nhãn "Nhanh:" / "Lý do:" — chữ dẫn, cố ý mờ hơn nút để mắt đi thẳng vào các lựa chọn. */
.blv-nhan-nhanh[b-oxh4m1e2lt] {
    font-size: .75rem;
    opacity: .7;
    flex: none;
}

/* Nút nhanh nhỏ hơn nút thường một chút: chúng là lối tắt, không phải hành động chính (nút Gửi mới là). */
.blv-nhanh .btn[b-oxh4m1e2lt] {
    padding: .1rem .45rem;
    font-size: .75rem;
}

/* ═══ ĐỢT 679 — THÂN THẺ BÀY RA NHƯ HỘP HỘI THOẠI NỔI GIỮA MÀN HÌNH ═══
   Anh Tiến 15/08/2026: *"khi nhấn vào 3 tab con này thì không mở kiểu đẩy hết xuống dưới như thế này
   mà hiển thị ra 1 hộp hội thoại để điền"*.

   📏 Vì sao cách cũ vướng: thân thẻ mở ra NGAY TRONG dòng thẻ ⇒ đẩy bảng chấm công và mọi thứ bên dưới
      tụt xuống, người dùng mất chỗ đang nhìn và phải cuộn lại sau khi gửi xong.
   ⇒ Nay thân nổi giữa màn hình trên một lớp phủ mờ: nội dung phía dưới ĐỨNG YÊN.

   🔴 ĐỢT 853 — LỚP PHỦ KHÔNG CÒN VẼ BẰNG `::after` NỮA, và câu dặn cũ ở đây đã HẾT hiệu lực.
     Câu cũ: *"Lớp phủ vẽ bằng ::after của chính thẻ — KHÔNG bắt được sự kiện bấm của Blazor, nên hộp
     phải có NÚT ĐÓNG riêng. ⛔ Đừng trông vào bấm ra ngoài để đóng."* Nó mô tả đúng hiện trạng lúc đó:
     `::after` là phần tử GIẢ, không có nút nào trong cây DOM để mà gắn sự kiện.
     Anh Tiến 18/08/2026: *"Tất cả hộp hội thoại hiện ra khi nhấn ra ngoài sẽ tự động đóng."*
     ⇒ Nay nền phủ là THẺ THẬT do `<NenHopThoai>` (Components/ChungTu) dựng, khai ngay trong markup của
     từng thẻ; nó kiêm luôn việc làm tối màn hình mà `::after` từng làm.
     ⛔ ĐỪNG dựng lại luật `.blv-the.mo::after`: hai tấm phủ chồng nhau thì tấm giả nằm trên sẽ nuốt
       đúng những cú bấm mà tấm thật cần nhận — tức dựng lại y nguyên lỗi vừa chữa.
     ✅ Nút đóng riêng `.blv-nut-dong` thì VẪN GIỮ: nó là đường đóng chính thức (bàn phím và trình đọc
       màn hình dùng được), nền phủ chỉ là lối tắt cho chuột.
   ⚠ `position: fixed` neo theo khung nhìn — KHÔNG đặt `backdrop-filter` lên tổ tiên của nó: bộ lọc đó
     đổi KHỐI CHỨA của `position: fixed`, đúng cái bẫy đã trả giá ở đợt 649. */
.blv-the.mo .blv-than[b-oxh4m1e2lt] {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: min(94vw, 34rem);
    max-height: 86vh;
    overflow: auto;
    z-index: 1060;
    padding: 1.1rem 1.1rem .9rem;
    border: 1px solid rgba(128, 128, 128, .3);
    border-radius: .9rem;
    /* Nền ĐỤC bắt buộc: hộp nổi trên nội dung trang, nền trong suốt là chữ chồng chữ. */
    background: var(--bs-body-bg, #fff);
    box-shadow: 0 1.5rem 3rem rgba(0, 0, 0, .35);
}

/* ⛔ Luật `.blv-the.mo::after` (tấm phủ vẽ bằng phần tử giả) ĐÃ GỠ ở đợt 853 — nay nền phủ do
   `<NenHopThoai>` dựng bằng thẻ thật để nó bắt được cú bấm ra ngoài. Xem khối chú thích ở trên. */

/* Nút đóng — góc trên phải trong hộp.
   ⛔ ĐỢT 683 — ĐỔI TÊN LỚP `blv-dong` → `blv-nut-dong`. Đợt 679 đặt nút đóng trùng tên với lớp của MỘT DÒNG
      ĐƠN CHỜ DUYỆT (khai ở trên), mà luật này nằm SAU nên nó thắng: mọi dòng đơn trong hộp "Đơn chờ tôi
      duyệt" bị `position: absolute` ném hết lên góc trên phải, chồng lên nhau, lại mất luôn viền ngăn dòng.
      Build sạch, mắt chỉ thấy khi mở đúng hộp đó và đang có từ hai đơn trở lên. */
.blv-nut-dong[b-oxh4m1e2lt] {
    position: absolute;
    top: .45rem;
    right: .55rem;
    border: 0;
    background: none;
    color: inherit;
    opacity: .6;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

.blv-nut-dong:hover[b-oxh4m1e2lt] { opacity: 1; }

/* ⛔ ĐỢT 875 (việc o108) — LUẬT `.blv-hang-lanh-dao` ĐÃ GỠ CÙNG DẢI LÃNH ĐẠO.
   Dải ấy nay không còn thẻ nào: duyệt đơn dời sang ô `OViecChoToiDuyet` (đợt 873), giao việc dời sang
   ô `OGiaoViec` (đợt 875) — cả hai nằm trên DÃY THẺ của trang chủ. Giữ lại một luật cho lớp bọc không
   còn ai mang là mã sống mà không chạy: phiên sau sửa nó rồi không hiểu vì sao màn hình không đổi.
   ⓘ Ý của đợt 683 (việc lãnh đạo tách khỏi việc khai đơn) và thứ tự hai dòng của đợt 850 KHÔNG mất —
     nay do chính sự phân tách hai khối giữ. Cần dựng lại thì lấy nguyên vẹn từ lịch sử git (đợt 830).
   🔴 BÀI HỌC ĐỢT 852 PHẢI GIỮ (nó nói về chú thích CSS nói chung, không riêng luật vừa gỡ): viết tắt
     một bộ thuộc tính thành `display` + `flex-` + DẤU SAO + DẤU GẠCH CHÉO + `gap` là ĐÓNG CHÚ THÍCH
     NGAY TẠI ĐÓ; phần văn còn lại dính vào bộ chọn nằm dưới ⇒ trình duyệt bỏ NGUYÊN luật ấy. Hỏng
     LẶNG LẼ tuyệt đối: build xanh, test xanh, chỉ mắt người mới thấy. Đó là lý do hai dấu ấy phải gọi
     tên bằng CHỮ. */

/* Huy hiệu SỐ ĐƠN ở cuối nút. Có đơn thì tô màu nhắc việc; 0 đơn thì để xám — "0 việc phải làm" là tin
   tốt, tô đỏ/vàng vào đó là báo động giả (đúng luật màu theo chiều lợi/bất lợi ở CLAUDE.md). */
.blv-dem[b-oxh4m1e2lt] {
    flex: none;
    min-width: 1.5rem;
    padding: 0 .35rem;
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 700;
    line-height: 1.35;
    text-align: center;
    background: rgba(128, 128, 128, .22);
    opacity: .75;
}

.blv-dem.co[b-oxh4m1e2lt] {
    background: rgba(220, 53, 69, .22);
    border: 1px solid rgba(220, 53, 69, .45);
    opacity: 1;
}

/* ═══ ⭐ ĐỢT 2176 (việc o1620) — HAI THẺ KHAI ĐƠN TRÊN ĐIỆN THOẠI ═══
   Anh Tiến 07/09/2026: *"tiếp tục tối ưu các khung điền ở trang chủ cho đẹp và thân thiện, đặc biệt chú
   ý đến giao diện điện thoại"*. Đo sống 375px trên bản đang chạy (hộp nổi rộng 353px, ruột 316px):
   · Thẻ 🏖 Xin nghỉ phép: hàng ngày = [ô ngày 150px][→][ô ngày 150px] = 336px > 316px ⇒ ô «đến ngày»
     RỚT xuống dòng dưới, dấu → đứng MỒ CÔI cuối dòng trên — y hệt bệnh nhãn «Hạn» của o1615.
   · Nút 📨 Gửi (hành động chính) chỉ 64px, đứng cạnh lối ra «Xem tất cả đơn» 162px ⇒ nút phụ to hơn
     nút chính.
   🔑 Ô ngày là `<ONgay>` mang `style="width: 150px"` NGAY TRÊN THẺ, mà tệp `.razor` của khối này đang có
     phiên khác sửa dở ⇒ ⛔ không đụng markup. CSS vẫn thắng được: trong flexbox, `flex-basis: 0` (⛔ không
     phải `auto`) là kích thước ban đầu THAY CHO `width`, nên hai ô tự chia đôi phần còn lại (đo: 150 → 140px,
     cả ba mảnh cùng một dòng). `min-width: 0` là chốt để flexbox được phép co ô xuống dưới bề ngang nội dung.
   ⚠ `::deep` BẮT BUỘC: `.o-ngay-boc` là thẻ của component `ONgay` (⛔ không có `.razor.css`) nên ⛔ không
     mang dấu cách ly của tệp này — viết trần là luật biên dịch ra `.o-ngay-boc[b-…]` và ⛔ không bao giờ khớp,
     build xanh, test xanh (bẫy đã ghi ở skill `luat-giao-dien-razor-css`).
   ⛔ Chỉ ở khổ ≤576px (cùng mốc `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx` của o1613/o1615): máy
     tính hộp rộng 34rem, hàng ngày vừa một dòng sẵn, đổi là đổi bố cục anh đã duyệt. */
@media (max-width: 576px) {
    .blv-hang[b-oxh4m1e2lt]  .o-ngay-boc {
        flex: 1 1 0;
        min-width: 0;
    }

    /* Nút chính nở hết phần còn lại của dòng nút (đo: 64 → 138px); lối ra bên cạnh giữ nguyên cỡ. */
    .blv-hang > .btn-primary[b-oxh4m1e2lt] {
        flex: 1 1 auto;
    }
}

/* ═══ ⭐ ĐỢT 2176 (việc o1620) — VÙNG CHẠM CHO NGÓN TAY TRONG HAI HỘP KHAI ĐƠN ═══
   Đo sống 375px: nút ✖ đóng hộp chỉ 34×18px — nhỏ nhất trong hộp, lại nằm đúng góc hay bấm nhất; bốn
   nút nhanh «Hôm nay · Ngày mai · 2 ngày · Tới hết tuần» đã được `app.css` nới cao 44px nhưng chữ vẫn
   .75rem (12px) ⇒ nút cao gấp ba lần chữ, trông như ô trống có chữ rơi vào.
   🔑 Điều kiện là `pointer: coarse` (cái CHẠM vào), ⛔ không phải bề ngang — luật vùng chạm đợt 1518;
     ngưỡng đọc từ BIẾN `--cham-toi-thieu`, ⛔ không gõ số 44 (sửa cỡ thì sửa biến).
   ⚠ Nút ✖ là `position: absolute` góc trên phải ⇒ nới nó lên 44px thì phải chừa cho nó một DẢI TRỐNG ở
     đỉnh thân hộp, không thì góc nút đè lên ô đầu tiên của hàng đầu. Đợt 2176 chừa dải ấy bằng
     `padding-top: 3rem`; ⭐ ĐỢT 2181 (việc o1624) đưa DÒNG TIÊU ĐỀ vào ở đúng dải ấy nên `padding-top`
     ⛔ không cần nới nữa (thân hộp về `1.1rem` như chế độ chuột, tức hộp ⛔ KHÔNG cao thêm dòng nào mà
     vẫn nói được nó là đơn gì). ⇒ Đọc tiếp cặp luật `.blv-tieu-de` ngay dưới, ⛔ đừng gỡ một mình.
     Máy chuột giữ nguyên `1.1rem` (chốt ngược đo ở chế độ chuột: ⛔ không đổi một pixel). */
@media (pointer: coarse) {
    /* ⭐ ĐỢT 2181 (việc o1624) — DÒNG TIÊU ĐỀ LẤP VÀO CHÍNH DẢI TRỐNG MÀ NÚT ✖ ĐANG ĐÒI.
       🔑 Đợt 2176 chừa `padding-top: 3rem` để nút ✖ 44px ⛔ không đè lên ô đầu tiên. Nay dải ấy có
         người ở: tiêu đề cao đúng bằng nút, nên hộp ⛔ KHÔNG cao thêm một dòng nào mà vẫn nói được nó
         là đơn gì — vì thế luật nới `padding-top` của đợt 2176 nay GỠ HẲN, thân hộp về `1.1rem` của
         luật nền như chế độ chuột. ⛔ Đừng dựng lại nó: hai dải chồng nhau là một hố trống 3rem.
       🔴 TRẦN `min-height` LÀ THỨ THAY CHỖ CHO `padding-top: 3rem`, ⛔ ĐỪNG GỠ NÓ MỘT MÌNH: gỡ xong
         là góc nút ✖ đè lên ô đầu tiên — đúng lỗi đợt 2176 vừa vá, và nó chỉ hiện ra trên máy CẢM ỨNG.
       ⓘ Chốt giữ cho cặp này đứng vững: thân hộp CHỈ được dựng khi một thẻ đang mở, mà thẻ nào cũng có
         tên ⇒ ⛔ không có ca "thân hộp mở mà ⛔ không có tiêu đề". Xem `TenTheDangMo`.
       ⚠ Chừa chỗ cho nút ✖ phải đọc `--cham-toi-thieu`, ⛔ đừng gõ số: ngày anh Tiến đổi ngưỡng chạm
         thì nút to ra mà câu tiêu đề vẫn nghĩ nó bé — chữ chui xuống dưới biểu tượng, ⛔ không ai báo. */
    .blv-tieu-de[b-oxh4m1e2lt] {
        display: flex;
        align-items: center;
        min-height: var(--cham-toi-thieu);
        padding-right: calc(var(--cham-toi-thieu) + .3rem);
        margin-bottom: .35rem;
    }

    .blv-nut-dong[b-oxh4m1e2lt] {
        top: .25rem;
        right: .25rem;
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Nút nhanh: chữ lên .85rem cho cân với chiều cao 44px, bo tròn thành viên chọn nhanh — dáng
       quen trên điện thoại (một dãy viên bấm-là-chọn), tách hẳn khỏi nút hành động vuông bên dưới. */
    .blv-nhanh .btn[b-oxh4m1e2lt] {
        font-size: .85rem;
        padding: .3rem .8rem;
        border-radius: 999px;
    }

    .blv-nhan-nhanh[b-oxh4m1e2lt] {
        font-size: .85rem;
    }
}
/* /Components/Pages/TongQuan/KhoiChamCong.razor.rz.scp.css */
/* ═══════════ Ô LÝ DO NHÁY DẤU ❗ 3D — đợt 948 ═══════════ */
/* Anh Tiến 21/08/2026: "khi bỏ tick hoặc tick chấm công em cứ hiện nút lưu lên nhưng chưa cho lưu,
   mỗi lần nhấn thì ô lý do nháy dấu chấm than 3D động".
   🔑 Khối duyệt công của trang Tổng quan là bản RIÊNG, không đi qua `HopThoaiNgay` — nên nó phải có
     bản luật riêng ở đây. ⛔ Đừng gộp sang tệp của hộp thoại: CSS scoped chỉ áp cho đúng component
     khai nó (`.kcc-…[b-xxxx]`), gộp là luật không với tới được phần tử nào và hỏng LẶNG LẼ.
   ⚠ HAI lớp `kcc-nhay-a` / `kcc-nhay-b` giống hệt nhau, chỉ khác TÊN hoạt hoạ — trình duyệt chỉ chạy
     lại `animation` khi tên luật đổi. Một tên duy nhất thì lượt bấm thứ hai trở đi câm lặng.
   ⚠ Tên lớp mang tiền tố `kcc-` chứ không dùng lại `htn-` của hộp thoại: `@keyframes` KHÔNG bị CSS
     scoped đổi tên, nên hai tệp cùng khai một tên hoạt hoạ là hai bản đè nhau trên phạm vi toàn app. */

.kcc-o-lydo[b-n6d9mfub6d] {
    position: relative;
    display: inline-block;
}

/* Dấu ❗ nổi khối: đổ bóng nhiều tầng + xoay quanh trục Y để có chiều sâu thật. */
.kcc-cham-than[b-n6d9mfub6d] {
    position: absolute;
    top: 50%;
    right: 0.4rem;
    transform-origin: 50% 50%;
    font-size: 1.15rem;
    line-height: 1;
    cursor: help;
    text-shadow:
        0 1px 0 #b02a2a,
        0 2px 0 #8f2020,
        0 3px 4px rgba(0, 0, 0, 0.45);
}

/* ⚠ `perspective()` gọi NGAY TRONG `transform`, ⛔ không khai thuộc tính `perspective` rời:
   thuộc tính rời chỉ tạo chiều sâu cho phần tử CON, không ăn vào phép xoay của chính phần tử này
   ⇒ dấu ❗ sẽ dẹt như chữ phóng to, mất hẳn chiều sâu mà vẫn chạy trơn tru (hỏng LẶNG LẼ). */
/* ⚠ Hoạt hoạ của dấu ❗ cũng phải LUÂN PHIÊN theo lớp cha `-nhay-a`/`-nhay-b`, y hệt ô nhập.
   🧨 Bản đầu của đợt 948 gắn cứng MỘT tên hoạt hoạ ngay trên chính dấu ❗ — đo sống thấy lượt bấm
     THỨ HAI chỉ còn ô rung, dấu ❗ ĐỨNG IM (phần tử đã có sẵn, lớp không đổi ⇒ trình duyệt không
     chạy lại). Anh Tiến đặt hàng "MỖI lần nhấn thì nháy", nên đứng im từ lượt thứ hai là hỏng đúng
     chỗ người dùng gặp nhiều nhất. */
.kcc-nhay-a .kcc-cham-than[b-n6d9mfub6d] {
    animation: kcc-cham-than-3d-a-b-n6d9mfub6d 0.9s ease-out both;
}

.kcc-nhay-b .kcc-cham-than[b-n6d9mfub6d] {
    animation: kcc-cham-than-3d-b-b-n6d9mfub6d 0.9s ease-out both;
}

@keyframes kcc-cham-than-3d-a-b-n6d9mfub6d {
    0%   { transform: perspective(220px) translateY(-50%) rotateY(-180deg) scale(0.3); opacity: 0; }
    45%  { transform: perspective(220px) translateY(-50%) rotateY(20deg)   scale(1.45); opacity: 1; }
    70%  { transform: perspective(220px) translateY(-50%) rotateY(-10deg)  scale(1.05); opacity: 1; }
    100% { transform: perspective(220px) translateY(-50%) rotateY(0deg)    scale(1.15); opacity: 1; }
}

@keyframes kcc-cham-than-3d-b-b-n6d9mfub6d {
    0%   { transform: perspective(220px) translateY(-50%) rotateY(-180deg) scale(0.3); opacity: 0; }
    45%  { transform: perspective(220px) translateY(-50%) rotateY(20deg)   scale(1.45); opacity: 1; }
    70%  { transform: perspective(220px) translateY(-50%) rotateY(-10deg)  scale(1.05); opacity: 1; }
    100% { transform: perspective(220px) translateY(-50%) rotateY(0deg)    scale(1.15); opacity: 1; }
}

/* ⛔ Đặt `--bs-border-color` chứ KHÔNG đặt `border-color`: Bootstrap 5.3 dựng viền `.form-control` bằng
   SHORTHAND đọc biến này, còn `.form-control.is-invalid` mới dùng longhand. Viết `border-color` thẳng
   là nuốt luôn viền đỏ báo lỗi (quy tắc #5, bài học đợt 838/840). */
.kcc-nhay-a .form-control[b-n6d9mfub6d],
.kcc-nhay-b .form-control[b-n6d9mfub6d] {
    --bs-border-color: #dc3545;
    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
}

.kcc-nhay-a .form-control[b-n6d9mfub6d] {
    animation: kcc-rung-a-b-n6d9mfub6d 0.5s ease-in-out;
}

.kcc-nhay-b .form-control[b-n6d9mfub6d] {
    animation: kcc-rung-b-b-n6d9mfub6d 0.5s ease-in-out;
}

@keyframes kcc-rung-a-b-n6d9mfub6d {
    0%, 100% { transform: translateX(0); }
    15%      { transform: translateX(-6px); }
    30%      { transform: translateX(6px); }
    45%      { transform: translateX(-4px); }
    60%      { transform: translateX(4px); }
    80%      { transform: translateX(-2px); }
}

@keyframes kcc-rung-b-b-n6d9mfub6d {
    0%, 100% { transform: translateX(0); }
    15%      { transform: translateX(-6px); }
    30%      { transform: translateX(6px); }
    45%      { transform: translateX(-4px); }
    60%      { transform: translateX(4px); }
    80%      { transform: translateX(-2px); }
}

/* ♿ Giảm chuyển động: bỏ rung + bỏ xoay, nhưng ⛔ GIỮ dấu ❗ và viền đỏ — đó là NỘI DUNG (chỉ chỗ còn
   thiếu), chỉ phần chuyển động mới là trang trí. */
@media (prefers-reduced-motion: reduce) {
    .kcc-nhay-a .kcc-cham-than[b-n6d9mfub6d],
    .kcc-nhay-b .kcc-cham-than[b-n6d9mfub6d] {
        animation: none;
        transform: translateY(-50%);
    }

    .kcc-nhay-a .form-control[b-n6d9mfub6d],
    .kcc-nhay-b .form-control[b-n6d9mfub6d] {
        animation: none;
    }
}
/* /Components/Pages/TongQuan/KhoiCongViecTrangChu.razor.rz.scp.css */
/* ═══ Khối "🗃 CÔNG VIỆC" ba cột trên trang chủ (việc o350; đời một cột là o314) ═══

   Giữ đúng ngôn ngữ thị giác của các khối trang chủ khác: thẻ card, chữ nhỏ cho dòng phụ, không tự đặt
   màu nền riêng. Mọi màu lấy từ token Bootstrap 5.3 để nền SÁNG và nền TỐI đều đúng — app có hai trục
   giao diện, cầu nối data-theme sang data-bs-theme đặt ở App.razor. */

/* Vạch ngăn dọc giữa ba cột — CHỈ từ lg trở lên, vì dưới ngưỡng đó ba cột xếp CHỒNG và một vạch bên
   trái khi đang xếp dọc thì vô nghĩa. Ngưỡng phải khớp `col-lg-4` của markup. */
@media (min-width: 992px) {
    .kdt-cot + .kdt-cot[b-9jnyroxf5n],
    .col-lg-4 + .col-lg-4 > .kdt-cot[b-9jnyroxf5n] {
        border-left: 1px solid var(--bs-border-color);
        padding-left: .75rem;
    }
}

/* Tiêu đề cột: đậm vừa phải — nó là tiêu đề CON, ⛔ không được to ngang tên khối phía trên. */
.kdt-tieu-de[b-9jnyroxf5n] {
    font-weight: 600;
}

.kdt-dong[b-9jnyroxf5n] {
    padding: .5rem 0;
    border-top: 1px solid var(--bs-border-color);
}

/* ═══ ⭐ ĐỢT 1299 (việc o490) — Ô ④ CỦA DÃY THẺ ═══
   Khối này thôi là "nút lớn" viên thuốc, nay là một THẺ trong dãy bốn ô. Nút trần mang sẵn viền, nền và
   canh giữa chữ của trình duyệt ⇒ phải gỡ đúng ba thứ ấy thì nó mới trông y hệt ba ô còn lại.
   ⛔ Màu / bóng / gradient vẫn ở `.tt3d` + `.tt3d-duan` của `app.css` — ⛔ đừng chép sang đây (luật
     scoped biên dịch ra (0,2,0), luật chung là (0,4,0) nên bản chép KHÔNG bao giờ thắng). */
.kdt-the[b-9jnyroxf5n] {
    appearance: none;
    font: inherit;
    cursor: pointer;
}

/* Mã hồ sơ đứng TRƯỚC tên — cột đầu của ba cột anh Tiến chốt (mã · tên · tiến độ).
   🔑 Chữ đều nét (`tabular-nums`) để dãy mã xếp thẳng cột khi đọc lướt; ⛔ không cho co lại
     (`flex-shrink: 0`) vì mã là thứ ngắn nhất mà lại là thứ người ta dò theo. */
.kdt-ma[b-9jnyroxf5n] {
    flex: 0 0 auto;
    font-size: .78rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: .01em;
    padding: .05rem .3rem;
    border-radius: .3rem;
    border: 1px solid rgba(128, 128, 128, .35);
    background: rgba(128, 128, 128, .10);
    /* ⛔ KHÔNG ghim mã màu chữ: nền sáng và nền tối có màu chữ khác hẳn (khuôn `currentColor` của
       bàn làm việc, đợt 668). */
    color: inherit;
    opacity: .85;
}

/* Dòng đầu tiên không cần vạch ngăn với tiêu đề khối. */
.kdt-dong:first-of-type[b-9jnyroxf5n] {
    border-top: 0;
    padding-top: .25rem;
}

/* Tên hồ sơ: đậm và mang màu chữ thường, KHÔNG lấy màu link xanh cùng gạch chân — anh Tiến chốt
   23/08/2026 rằng kiểu gạch chân trông xấu. Dấu hiệu bấm được để dành cho lúc rê chuột. */
/* ⭐ ĐỢT 2198 (việc o1640) — CỤM [mã · tên] PHẢI CO ĐƯỢC, không thì phép cắt gọn ngay dưới nằm chơi.
   🔑 Đây là bệnh flexbox kinh điển: `min-width: 0` phải có ở MỌI cấp flex item trên đường xuống tới
     phần tử cắt chữ, ⛔ không phải chỉ ở phần tử ấy. Đặt ở đây (CSS cách ly, (0,2,0)) là đủ — cụm này
     ⛔ không đeo lớp tiện dụng Bootstrap nào mang `!important` cho `min-width`. */
.kdt-cum-ten[b-9jnyroxf5n] {
    min-width: 0;
    /* 🩸 ĐỢT 2202 — `flex: 1 1 auto` LÀ VẾ THỨ HAI CỦA BẢN VÁ «tên hồ sơ biến mất», ⛔ đừng gỡ.
       Từ đợt này thẻ tên bên trong khai `flex: 1 1 0` (bề ngang đến từ CHA — xem chú thích ở
       `.kdt-ten`), nên nội dung của nó THÔI đóng góp vào bề ngang cụm. Cụm mà vẫn để `flex-basis:
       auto` thì bề ngang của chính cụm cũng suy từ nội dung ⇒ cụm chỉ còn rộng bằng dấu mã «—», và
       thẻ tên nhận đúng **0px**. Hai luật này là MỘT CẶP: sửa một mình luật kia là tên mất sạch. */
    flex: 1 1 auto;
}

.kdt-ten[b-9jnyroxf5n] {
    font-weight: 600;
    color: var(--bs-body-color);
    text-decoration: none;
    /* Tên hồ sơ dài phải cắt gọn, ⛔ không cho đẩy vỡ hàng phần trăm bên phải. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}

.kdt-ten:hover[b-9jnyroxf5n],
.kdt-ten:focus-visible[b-9jnyroxf5n] {
    text-decoration: underline;
}

.kdt-phantram[b-9jnyroxf5n] {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    opacity: .85;
}

.kdt-thanh[b-9jnyroxf5n] {
    height: 8px;
    margin: .3rem 0;
    border-radius: 999px;
}

/* Hồ sơ CHƯA lập bảng tiến độ: dải gạch mờ, ⛔ KHÔNG phải thanh 0%. Hai thứ trông phải khác nhau —
   thanh rỗng nghĩa là đã đo và kết quả bằng không, còn ở đây thì chưa hề đo được gì. */
.kdt-chua-do[b-9jnyroxf5n] {
    background-image: repeating-linear-gradient(
        45deg,
        var(--bs-secondary-bg) 0,
        var(--bs-secondary-bg) 4px,
        transparent 4px,
        transparent 8px);
    opacity: .7;
}

.kdt-phu[b-9jnyroxf5n] {
    opacity: .85;
}

/* ── HÀNG TẠO NHANH ở ĐẦU hộp (đợt 1253 — anh Tiến: *"đưa form nhập nhanh lên đầu và làm gọn thành
      1 hàng"*) ── */

/* Hai ô chọn vừa đủ chữ, ⛔ không kéo dài: trong `input-group` mọi ô chia đều bề ngang, để nguyên là ô
   loại rộng ngang ô tên — thứ người ta gõ nhiều nhất lại hẹp nhất. */
.kdt-o-loai[b-9jnyroxf5n],
.kdt-o-duyet[b-9jnyroxf5n] {
    width: auto;
    flex: none;
}

/* Ô chọn người duyệt có tên người + vai trò nên dài hơn ô loại; chặn trần để nó ⛔ không đẩy nút Tạo
   xuống dòng trên màn hình vừa. */
.kdt-o-duyet[b-9jnyroxf5n] {
    max-width: 16rem;
}

/* Ô tên là thứ giãn ra chiếm phần còn lại của hàng. Đặt sàn bề ngang để khi hẹp nó xuống dòng nguyên
   khối thay vì co lại còn vài ký tự. */
.kdt-hang-tao .form-control[b-9jnyroxf5n] {
    min-width: 14rem;
}

/* Ô ngày do COMPONENT dùng chung `ONgay` dựng ra; trong `input-group` phải bỏ bo góc để nó dính liền
   nhãn "Hạn" đứng ngay trước.
   ⚠ `::deep` là BẮT BUỘC: Blazor chỉ đóng mã scope lên phần tử do CHÍNH tệp này sinh ra, còn thẻ bên
     trong `ONgay` thì không mang mã ấy ⇒ bộ chọn trần khớp 0 phần tử, hỏng LẶNG LẼ (cùng bẫy đã ghi ở
     `ChamCongTheoThang.razor.css` và `TinhLuong.razor.css`).
   ⛔ Đừng đụng vào chính component `ONgay`: nó dùng ở hàng chục màn hình khác, sửa ở đó là đổi hình của
     tất cả — trong khi việc cần làm chỉ nằm ở đúng một hàng nhập này. */
.kdt-hang-tao[b-9jnyroxf5n]  .o-ngay-boc,
.kdt-hang-tao[b-9jnyroxf5n]  .o-ngay-go {
    border-radius: 0;
}

/* ── ⭐ ĐỢT 1266 — MỖI CỘT TỰ CUỘN, NỘI DUNG THU NHỎ MỘT ÍT ──
      Anh Tiến 25/08/2026: *"hiển thị kích thước hộp thoại cố định và hợp lý, nội dung trong từng cột
      cho phép cuộn xuống xem khi vượt ngoài, nội dung trong cột có thể thu nhỏ lại một ít cho vừa
      khung xem"*.
      Giữ ĐÚNG bộ số của khối 🎯 Mục tiêu — hai hộp mở ra từ hai nút cạnh nhau, lệch một chút là nhìn
      ra ngay.

   🔑 CHIỀU CAO ĐI TỪ TRÊN XUỐNG QUA BA NẤC, ⛔ đứt một nấc là cả luật nằm im (hỏng LẶNG LẼ, màn hình
     vẫn "trông có lý", chỉ là cột không cuộn):
       ① thẻ hộp `.modal-content.hop-cao-co-dinh` — chiều cao cố định, khai ở `app.css`;
       ② thân hộp `.modal-body` — nhận lớp `d-flex flex-column` từ chỗ gọi trong `.razor`;
       ③ hàng cột `.kdt-hang-cot` ngay dưới — ăn hết phần còn lại.
     Sửa một nấc thì đi soát hai nấc kia.

   ⚠ `min-height: 0` là dòng KHÔNG ĐƯỢC BỎ dù trông như thừa: flex item mặc định `min-height: auto`,
     tức nó TỪ CHỐI co nhỏ hơn nội dung ⇒ hàng cột phình ra đúng bằng cột dài nhất và đẩy tràn khỏi
     hộp, `overflow-y` bên dưới không bao giờ có việc để làm. */
@media (min-width: 992px) {
    .kdt-hang-cot[b-9jnyroxf5n] {
        flex: 1 1 auto;
        min-height: 0;
    }

    /* Cột phải được phép co thì chiều cao của hàng mới truyền xuống tới `.kdt-cot`. */
    .kdt-hang-cot > [class*="col-"][b-9jnyroxf5n] {
        min-height: 0;
        max-height: 100%;
    }

    /* ⇐ Đây mới là vùng cuộn anh Tiến xin: cuộn TRONG cột, ⛔ không phải cuộn cả hộp.
       ⚠ Chừa đệm phải để thanh cuộn ⛔ không đè lên con số phần trăm nằm sát mép phải mỗi dòng. */
    .kdt-cot[b-9jnyroxf5n] {
        max-height: 100%;
        overflow-y: auto;
        padding-right: .35rem;
    }
}

/* Thu nhỏ nội dung trong cột — ⛔ CHỈ trong cột, ⛔ không đụng hàng nhập nhanh ở đầu hộp: ô người ta
   GÕ VÀO mà nhỏ hơn cỡ chuẩn là khó gõ, còn ba cột là thứ để ĐỌC LƯỚT.
   ⓘ Tiêu đề cột giữ cỡ chuẩn để vẫn có mốc phân biệt với phần chữ bên dưới sau khi thu nhỏ. */
.kdt-cot[b-9jnyroxf5n] {
    font-size: .875rem;
}

.kdt-tieu-de[b-9jnyroxf5n] {
    font-size: 1rem;
}

/* Dòng hồ sơ ken lại một chút cho vừa khung — vẫn còn vạch ngăn nên ⛔ không dính vào nhau.
   ⓘ Dòng ở cột này cao hơn dòng của khối 🎯 Mục tiêu (có thêm thanh tiến độ và hai dòng phụ) nên số
     đệm ở đây lớn hơn một chút là ĐÚNG, ⛔ không phải chỗ cần gò cho bằng nhau. */
.kdt-dong[b-9jnyroxf5n] {
    padding: .4rem 0;
}

/* ═══ ⭐ ĐỢT 2198 (việc o1640) — TÊN HỒ SƠ TRONG HỘP THOẠI PHẢI BẤM ĐƯỢC BẰNG NGÓN TAY ═══
   📏 Đo sống 375×812 (08/09/2026, SAU khi min-width ở trên đã chữa xong lượt cuộn ngang 804px):
     5 thẻ tên còn cao đúng 21px — một dòng chữ — trong khi ngưỡng chạm là 44px.
   🔴 Ở ĐÂY DÙNG `inline-block` + `line-height`, ⛔ KHÔNG `inline-flex` như hai hộp kia: thẻ này mang
     `text-overflow: ellipsis`, mà phép cắt ba chấm ấy ⛔ KHÔNG áp cho con của một hộp flex — đổi sang
     `inline-flex` là nong đúng vùng chạm rồi làm mất phép cắt gọn vừa dựng ở trên, mà build vẫn xanh.
   ⛔ CHỈ trong `pointer: coarse` — máy tính giữ nguyên dáng một dòng chữ 21px. */
@media (pointer: coarse) {
    .kdt-ten[b-9jnyroxf5n] {
        display: inline-block;
        min-height: var(--cham-toi-thieu);
        line-height: var(--cham-toi-thieu);
    }
}

/* ═══════ 🎞 ĐỢT 2202 (việc o1646) — TÊN HỒ SƠ: CẮT BA CHẤM, RÊ CHUỘT THÌ CHỮ CHẠY ═══════

   Anh Tiến 08/09/2026: *"cách trình bày ở các dòng hồ sơ là mã - tên (phần lố thì ẩn cho chạy chữ khi
   di chuột vào) - xx% tiến độ (tất cả trên cùng 1 dòng)"*.

   🔑 KHUNG NGẮM VÀ THỨ CHẠY LÀ HAI THẺ KHÁC NHAU, ⛔ đừng gộp: `overflow: hidden` + `text-overflow`
   phải ở thẻ `a.kdt-ten` (khung), còn thứ dịch chuyển là thẻ con `.kdt-ten-chay`. Đặt `transform`
   lên chính thẻ mang `overflow: hidden` thì cả khung trượt đi — chữ vẫn bị cắt ở đúng chỗ cũ, chỉ
   thêm việc là nó lấn sang ô phần trăm bên cạnh.

   🔴 QUÃNG CHẠY ĐO BẰNG `100cqw - 100%`, ⛔ KHÔNG PHẢI MỘT SỐ ĐOÁN: `100cqw` là bề ngang KHUNG (thẻ
   `a` khai `container-type: inline-size`), `100%` là bề ngang CHỮ. Hiệu của chúng đúng bằng phần lố
   — tên dài bao nhiêu thì chạy bấy nhiêu, ⛔ không tên nào chạy hụt hay chạy quá.
   ⛔ `min(0px, …)` là chốt bắt buộc: tên NGẮN hơn khung cho ra số DƯƠNG ⇒ chữ trượt sang phải, chui
   ra khỏi khung, để lại một khoảng trắng đúng chỗ đáng lẽ có chữ.

   ⏸ CHIỀU FAIL PHẢI LÀ «ĐỨNG YÊN»: trình duyệt ⛔ không hiểu `cqw` thì bỏ nguyên khai báo
   `animation` ⇒ còn lại đúng phép cắt ba chấm — vẫn đọc được tên, chỉ mất phần động. ⛔ Đừng thay
   bằng một quãng chạy gõ tay để "chạy được ở mọi nơi": chạy sai quãng còn tệ hơn đứng yên.
   ⚠ `text-overflow: clip` lúc rê chuột — giữ ba chấm thì chữ chạy tới đâu cũng có ba chấm đè lên
   đuôi câu, đọc dở dang.
   ⚠ Người đặt máy GIẢM CHUYỂN ĐỘNG thì ⛔ không chạy (cùng nếp với `.dau-phep-cho-duyet`): họ vẫn
   đọc đủ tên bằng cách bấm vào hồ sơ, ⛔ không mất đường nào. */
/* 🩸 BA DÒNG NÀY LÀ MỘT BỘ, ⛔ ĐỪNG GIỮ MỖI DÒNG ĐẦU — đã trả giá ngay trong đợt 2202.
   `container-type: inline-size` bật `contain: layout inline-size`, tức **nội dung THÔI đóng góp vào
   bề ngang phần tử**. Thẻ này là flex item trong `.kdt-cum-ten` với nền `flex-basis: auto` ⇒ bề ngang
   suy từ nội dung ⇒ nội dung bị contain là thẻ co về **0px** và **TÊN HỒ SƠ BIẾN MẤT SẠCH**.
   📏 Đo sống 08/09/2026 (ảnh chụp bản chạy thật): hai dòng dự án chỉ còn dấu mã «—» và con số %,
   chỗ tên trắng trơn. Build XANH, test XANH — chỉ ảnh chụp mới lòi ra.
   ⇒ `flex: 1 1 0` cấp cho thẻ một bề ngang ĐẾN TỪ CHA (phần còn lại của hàng), thứ mà containment
   ⛔ không đụng tới; `min-width: 0` để nó co được thật (bài học ⑪ đợt 2198). */
.kdt-ten[b-9jnyroxf5n] {
    container-type: inline-size;
    flex: 1 1 0;
    min-width: 0;
}

.kdt-ten-chay[b-9jnyroxf5n] {
    display: inline-block;
    white-space: nowrap;
}

.kdt-ten:hover[b-9jnyroxf5n],
.kdt-ten:focus-visible[b-9jnyroxf5n] {
    text-overflow: clip;
}

.kdt-ten:hover .kdt-ten-chay[b-9jnyroxf5n],
.kdt-ten:focus-visible .kdt-ten-chay[b-9jnyroxf5n] {
    animation: kdt-chay-ten-b-9jnyroxf5n 6s linear infinite alternate;
}

@keyframes kdt-chay-ten-b-9jnyroxf5n {
    0%, 12% {
        transform: translateX(0);
    }

    88%, 100% {
        transform: translateX(min(0px, calc(100cqw - 100%)));
    }
}

@media (prefers-reduced-motion: reduce) {
    .kdt-ten:hover .kdt-ten-chay[b-9jnyroxf5n],
    .kdt-ten:focus-visible .kdt-ten-chay[b-9jnyroxf5n] {
        animation: none;
    }
}
/* /Components/Pages/TongQuan/KhoiLichTuanTrangChu.razor.rz.scp.css */
/* ═══ ⭐ VIỆC o1774 (đợt 2269) → 🔁 VIỆC o1816 (đợt 2286) — LỊCH CÔNG TÁC TRÊN TRANG CHỦ, TÁM CỘT ═══

   🎨 Màu lấy TRỌN từ token Bootstrap 5.3 — ⛔ đừng gõ mã màu: khối nằm trên trang chủ, nơi bộ màu chủ đề
      đổi theo sự kiện, và một mã màu đóng đinh là một vệt lạc ở nền tối mà build vẫn xanh.

   📐 NỀN NGÀY HÔM NAY đặt qua BIẾN `--bs-table-bg` trên thẻ bọc `tbody`, ⛔ không tô thẳng từng ô: Bootstrap
      dựng nền ô bằng biến ấy, tô thẳng là đè luôn lớp trạng thái của bảng (cùng lẽ luật hàng danh sách
      đợt 1524: biến đặt ở THẺ BỌC, ⛔ đặt trên chính ô).

   📱 KHỔ ĐIỆN THOẠI (576px — cùng mốc `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx` của mọi khung trang
      chủ): mỗi NGÀY hoá MỘT THẺ, giờ + nội dung là dòng tên, ô rỗng giấu. Khuôn này KHÁC khuôn 820px của
      sổ khai `BangTheKhoHepSoKhaiTests.SoKhai` nên nó khai ở danh sách `KhuonKhac` của lớp ấy. */

.ltc-the .card-body[b-6uqq0xl7ps] {
    padding: .9rem 1.05rem 1rem;
}

/* ── ĐỈNH KHỐI ── */

.ltc-dinh[b-6uqq0xl7ps] {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: .4rem .8rem;
    margin-bottom: .7rem;
}

.ltc-danh[b-6uqq0xl7ps] {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .25rem .6rem;
    min-width: 0;
}

.ltc-ten-khoi[b-6uqq0xl7ps] {
    font-weight: 600;
    font-size: 1.02rem;
}

/* Dòng đầu tờ trong hộp thoại — chữ HOA như bản giấy, phần «(Bổ sung lần N)» chữ đỏ như bản giấy. */
.ltc-tieu-de-to[b-6uqq0xl7ps] {
    font-weight: 700;
    font-size: 1.02rem;
}

.ltc-bo-sung[b-6uqq0xl7ps] {
    color: var(--bs-danger-text-emphasis);
}

.ltc-tuan[b-6uqq0xl7ps] {
    font-size: .9rem;
    color: var(--bs-secondary-color);
}

.ltc-so-viec[b-6uqq0xl7ps] {
    font-size: .74rem;
    font-weight: 500;
}

.ltc-toi-to[b-6uqq0xl7ps] {
    font-size: .87rem;
    text-decoration: none;
    white-space: nowrap;
}

.ltc-toi-to:hover[b-6uqq0xl7ps] {
    text-decoration: underline;
}

/* ✏️ ĐỢT 2325 (việc o1887) — nút ✏️ Sửa ở đầu tờ trong hộp. `margin-left: auto` đẩy nút sang phải, đứng sát liên kết
   «Xem tờ đầy đủ →»: dải đầu khối chia `space-between`, ⛔ có dòng này thì nút trôi ra GIỮA dải. */
.ltc-nut-sua[b-6uqq0xl7ps] {
    margin-left: auto;
    white-space: nowrap;
}

/* 🔁 ĐỢT 2312 (việc o1861) → 🩸 ĐỢT 2313 (việc o1866) — nút 📅 gọn ở đầu khối hôm nay: giữ chữ trên MỘT dòng.
   Đầu khối là dải flex tự xuống dòng, nên ở khổ hẹp cả nút rớt xuống dòng dưới — chữ gãy đôi bên trong nút mới
   là thứ trông hỏng. Luật dời từ tệp của `NutLichTuanTrangChu` sang đây theo chính cái nút (nút nay do khối này
   dựng); lớp giữ tên `.nlt-nut-gon` vì công cụ đo `do-song-lich-hom-nay-trang-chu.mjs` tìm nút bằng tên ấy.
   Cỡ, màu, vùng chạm 44px đều là của `.btn` chung (đợt 1518), ⛔ khai lại ở đây. */
.nlt-nut-gon[b-6uqq0xl7ps] {
    white-space: nowrap;
}

.ltc-dang-tai[b-6uqq0xl7ps],
.ltc-tuan-trong[b-6uqq0xl7ps] {
    font-size: .9rem;
}

/* ── BẢNG TÁM CỘT ── */

.ltc-bang[b-6uqq0xl7ps] {
    font-size: .88rem;
}

.ltc-bang > thead > tr > th[b-6uqq0xl7ps] {
    text-align: center;
    vertical-align: middle;
    font-size: .82rem;
    font-weight: 600;
    background-color: var(--bs-tertiary-bg);
}

.ltc-bang > tbody > tr > td[b-6uqq0xl7ps] {
    vertical-align: top;
    overflow-wrap: anywhere;
}

/* Bề ngang cột — «Nội dung công việc» là cột TO NHẤT (luật ① của bốn luật bảng, 07/09/2026).
   ⭐ ĐỢT 2326 (việc o1888) — BẢY cột, tổng đúng 100%: ba cột Ngày · Giờ · Địa điểm gộp thành MỘT (12%), cột
   «Trực phòng ban» (18%) đứng cuối. Tên phòng + người + số điện thoại xuống dòng trong ô, ⛔ đòi rộng thêm. */
.ltc-c-truc[b-6uqq0xl7ps] { width: 9%; }
.ltc-c-tgdd[b-6uqq0xl7ps] { width: 12%; }
.ltc-c-noi-dung[b-6uqq0xl7ps] { width: 24%; }
.ltc-c-chuan-bi[b-6uqq0xl7ps] { width: 9%; }
.ltc-c-chu-tri[b-6uqq0xl7ps] { width: 9%; }
.ltc-c-thanh-phan[b-6uqq0xl7ps] { width: 19%; }
.ltc-c-truc-phong-ban[b-6uqq0xl7ps] { width: 18%; }

/* Ranh giới giữa hai NGÀY đậm hơn ranh giới giữa hai việc trong cùng một ngày. */
.ltc-bang > tbody + tbody > tr:first-child > td[b-6uqq0xl7ps] {
    border-top: 2px solid var(--bs-border-color);
}

.ltc-o-truc[b-6uqq0xl7ps],
.ltc-o-tgdd[b-6uqq0xl7ps] {
    text-align: center;
}

/* ⭐ ĐỢT 2326 — ô «Ngày · Giờ · Địa điểm»: ba mảnh xếp dọc — tên thứ + ngày (chỉ hàng đầu của ngày) · giờ · địa điểm. */
.ltc-gio[b-6uqq0xl7ps],
.ltc-dia-diem[b-6uqq0xl7ps] {
    display: block;
}

.ltc-gio[b-6uqq0xl7ps] {
    font-weight: 600;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.ltc-o-dau-ngay .ltc-ngay-boc + .ltc-gio[b-6uqq0xl7ps],
.ltc-o-dau-ngay .ltc-ngay-boc + .ltc-dia-diem[b-6uqq0xl7ps] {
    margin-top: .3rem;
}

.ltc-truc[b-6uqq0xl7ps],
.ltc-ngay-boc[b-6uqq0xl7ps] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .1rem;
}

.ltc-truc-ten[b-6uqq0xl7ps],
.ltc-ten-thu[b-6uqq0xl7ps] {
    font-weight: 600;
}

.ltc-truc-sdt[b-6uqq0xl7ps],
.ltc-nhan-ngay[b-6uqq0xl7ps] {
    text-decoration: none;
    font-variant-numeric: tabular-nums;
}

/* ✏️ ĐỢT 2313 (việc o1867) — ô chọn người trực trong hộp cả tuần. Cột «Trực GĐ» hẹp (tên + số) nên ô chọn
   chiếm trọn bề ngang ô và ⛔ đòi rộng hơn: tên dài thì trình duyệt cắt trong ô chọn, ⛔ đẩy bảng tràn ngang. */
.ltc-chon-truc[b-6uqq0xl7ps] {
    width: 100%;
    min-width: 0;
    margin-bottom: .2rem;
}

.ltc-ngay-boc[b-6uqq0xl7ps] {
    white-space: nowrap;
}

.ltc-chip[b-6uqq0xl7ps] {
    padding: .02rem .38rem;
    border-radius: .35rem;
    font-size: .66rem;
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--bs-primary-text-emphasis);
    background-color: var(--bs-primary-border-subtle);
}

/* Chip NGÀY MAI tông trung tính để ⛔ tranh chỗ với chip HÔM NAY. */
.ltc-chip.ltc-chip-mai[b-6uqq0xl7ps] {
    color: var(--bs-secondary-color);
    background-color: var(--bs-secondary-bg);
}

/* Ô nhiều dòng giữ đúng chỗ xuống dòng người lập đã gõ. Lớp này đặt trên thẻ con chỉ chứa chữ, ⛔ đặt trên
   ô bảng: ô bảng có khoảng trắng của mã nguồn quanh thẻ con, `pre-line` sẽ biến nó thành dòng trống. */
.ltc-nhieu-dong[b-6uqq0xl7ps] {
    white-space: pre-line;
}

/* ⭐ ĐỢT 2325 (việc o1887) — CHỮ «NỘI DUNG CÔNG VIỆC» IN ĐẬM. Anh Tiến 12/09/2026: «nội dung công việc thì phải được
   in đậm». Đặt trên Ô (⛔ trên thẻ con) để mục nháp — nghiêng + nhạt — cũng đậm như mục đã phát.
   ⚠ Khổ điện thoại có luật riêng cho ô này ở cuối tệp (cùng giá trị) — sửa một chỗ nhớ sửa chỗ kia. */
.ltc-o-noi-dung[b-6uqq0xl7ps] {
    font-weight: 700;
}

/* Mục CHƯA ban hành (chỉ người soạn/duyệt thấy) — nghiêng + nhạt, đúng cách tờ Hành chính đang bày. */
.ltc-nhap[b-6uqq0xl7ps] {
    font-style: italic;
    color: var(--bs-secondary-color);
}

.ltc-o-trong[b-6uqq0xl7ps] {
    font-style: italic;
    color: var(--bs-secondary-color);
}

.ltc-con-lai[b-6uqq0xl7ps] {
    font-size: .82rem;
    text-decoration: none;
}

.ltc-con-lai:hover[b-6uqq0xl7ps] {
    text-decoration: underline;
}

/* ── BỐN CHẶNG CỦA NGÀY ── */

/* 🔁 ĐỢT 2312 (việc o1860) — NỀN HÔM NAY PHẢI PHỦ MỌI DÒNG CỦA NGÀY. Anh Tiến 11/09/2026 gửi ảnh: *"ô lịch tô
   không hết dòng"* — ngày có hai việc thì dòng đầu xanh, dòng thứ hai XÁM.
   🔑 Gốc bệnh ⛔ nằm ở tệp này: luật sọc dùng chung của `app.css` (`tr:nth-child(even) > *`) đặt THẲNG
     `background-color` lên ô của dòng chẵn ở (0,5,2) — nền tối (0,7,2) — nên nó đè biến `--bs-table-bg`
     đặt ở đây. Mỗi ngày lại là một `<tbody>` riêng ⇒ dòng thứ hai của MỌI ngày đều là "dòng chẵn".
   ⇒ Mọi `<tr>` của bảng mang lớp `ltc-table-hang` (hằng `LopHang` ở code-behind), CỐ Ý chứa chuỗi `table-`
     để luật sọc CHỪA — đúng khuôn `dsc-table-hang` của đợt 2305. ⛔ Đừng chữa bằng cách nâng độ đặc hiệu ở
     đây: CSS cách ly chỉ cộng MỘT lớp, phải chồng thêm bốn lớp nữa mới thắng nổi luật nền tối.
   ⓘ Bảng này bỏ luôn sọc ở các ngày khác — cũng là chủ ý: ô «Trực GĐ» + «Ngày» trải `rowspan`, sọc chỉ tô
     được sáu ô bên phải của dòng chẵn nên đọc ra một mảng xám cụt, ⛔ phải một dải xen kẽ. Ranh giới giữa
     các dòng đã có viền `table-bordered`. */
.ltc-ngay.ltc-hom-nay[b-6uqq0xl7ps] {
    --bs-table-bg: var(--bs-primary-bg-subtle);
}

.ltc-ngay.ltc-da-qua .ltc-ngay-boc[b-6uqq0xl7ps],
.ltc-ngay.ltc-da-qua .ltc-truc-ten[b-6uqq0xl7ps] {
    color: var(--bs-secondary-color);
}

/* ⭐ ĐỢT 2326 — tên người trực phòng ban của ngày đã qua cũng nhạt theo (ruột ô là component con ⇒ qua `::deep`). */
.ltc-ngay.ltc-da-qua[b-6uqq0xl7ps]  .otpb-ten {
    color: var(--bs-secondary-color);
}

/* ── KHỔ ĐIỆN THOẠI: mỗi NGÀY hoá một THẺ ── */

@media (max-width: 576px) {
    .ltc-bang[b-6uqq0xl7ps],
    .ltc-bang > tbody[b-6uqq0xl7ps],
    .ltc-bang > tbody > tr[b-6uqq0xl7ps],
    .ltc-bang > tbody > tr > td[b-6uqq0xl7ps] {
        display: block;
        width: auto;
    }

    .ltc-bang > thead[b-6uqq0xl7ps] {
        display: none;
    }

    .ltc-bang > tbody[b-6uqq0xl7ps] {
        margin-bottom: .6rem;
        border: 1px solid var(--bs-border-color);
        border-radius: .5rem;
        overflow: hidden;
    }

    .ltc-bang > tbody.ltc-hom-nay[b-6uqq0xl7ps] {
        border-color: var(--bs-primary-border-subtle);
        background-color: var(--bs-primary-bg-subtle);
    }

    .ltc-bang > tbody > tr[b-6uqq0xl7ps] {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        padding: .35rem .6rem;
    }

    .ltc-bang > tbody > tr + tr[b-6uqq0xl7ps] {
        border-top: 1px dashed var(--bs-border-color);
    }

    .ltc-bang > tbody > tr > td[b-6uqq0xl7ps],
    .ltc-bang > tbody + tbody > tr:first-child > td[b-6uqq0xl7ps] {
        flex: 1 1 100%;
        order: 3;
        padding: .1rem 0;
        border: 0;
        box-shadow: none;
        background-color: transparent;
        text-align: start;
    }

    .ltc-bang > tbody > tr > td.ltc-o-rong[b-6uqq0xl7ps] {
        display: none;
    }

    /* Nhãn đứng trước giá trị — chữ nhãn đọc từ `data-nhan`, TRÙNG tên cột ở thead. */
    .ltc-bang > tbody > tr > td[data-nhan][b-6uqq0xl7ps]::before {
        content: attr(data-nhan);
        display: inline-block;
        min-width: 6.6rem;
        margin-right: .4rem;
        font-size: .78rem;
        color: var(--bs-secondary-color);
    }

    /* Ngày là ĐẦU THẺ, giờ + nội dung là DÒNG TÊN — hai ô này tự nói lên mình nên ⛔ in nhãn. */
    .ltc-bang > tbody > tr > td.ltc-o-tgdd[b-6uqq0xl7ps]::before,
    .ltc-bang > tbody > tr > td.ltc-o-noi-dung[b-6uqq0xl7ps]::before {
        content: none;
    }

    /* ⭐ ĐỢT 2326 (việc o1888) — ô «Ngày · Giờ · Địa điểm» TAN vào thẻ (`display: contents`): ba mảnh của nó thành
       phần tử của hàng và tự đứng đúng chỗ — tên thứ + ngày làm ĐẦU THẺ, giờ đứng trước nội dung, địa điểm thành
       một dòng có nhãn. Máy tính ⛔ đổi gì: luật chỉ nằm trong khối khổ điện thoại này. */
    .ltc-bang > tbody > tr > td.ltc-o-tgdd[b-6uqq0xl7ps] {
        display: contents;
    }

    .ltc-bang .ltc-o-tgdd > .ltc-ngay-boc[b-6uqq0xl7ps] {
        flex: 1 1 100%;
        order: -2;
        padding-bottom: .2rem;
    }

    .ltc-bang .ltc-o-tgdd > .ltc-gio[b-6uqq0xl7ps] {
        flex: 0 0 auto;
        order: 1;
        margin: 0 .45rem 0 0;
    }

    .ltc-bang .ltc-o-tgdd > .ltc-dia-diem[b-6uqq0xl7ps] {
        flex: 1 1 100%;
        order: 3;
        margin: 0;
        padding: .1rem 0;
    }

    /* Nhãn «Địa điểm» đọc từ `data-nhan` của chính mảnh ấy — cùng dáng nhãn của các ô khác trong thẻ. */
    .ltc-bang .ltc-o-tgdd > .ltc-dia-diem[b-6uqq0xl7ps]::before {
        content: attr(data-nhan);
        display: inline-block;
        min-width: 6.6rem;
        margin-right: .4rem;
        font-size: .78rem;
        color: var(--bs-secondary-color);
    }

    /* Người trực (GĐ + mọi phòng) ngay dưới đầu thẻ, trước các việc. Ô «Trực phòng ban» đứng cuối hàng đầu trong
       DOM nên cùng số `order` vẫn xếp SAU ô «Trực GĐ». */
    .ltc-bang > tbody > tr > td.ltc-o-truc[b-6uqq0xl7ps],
    .ltc-bang > tbody > tr > td.ltc-o-truc-phong-ban[b-6uqq0xl7ps] {
        order: -1;
    }

    .ltc-ngay-boc[b-6uqq0xl7ps],
    .ltc-truc[b-6uqq0xl7ps] {
        display: inline-flex;
        flex-direction: row;
        flex-wrap: wrap;
        align-items: baseline;
        gap: .1rem .45rem;
    }

    .ltc-bang > tbody > tr > td.ltc-o-gio[b-6uqq0xl7ps] {
        flex: 0 0 auto;
        order: 1;
        margin-right: .45rem;
        font-weight: 600;
    }

    .ltc-bang > tbody > tr > td.ltc-o-noi-dung[b-6uqq0xl7ps] {
        flex: 1 1 0;
        order: 2;
        min-width: 0;
        font-weight: 700;   /* ⭐ ĐỢT 2325 (việc o1887) — in đậm như bảng máy tính (luật ở giữa tệp) */
    }
}

/* ── NGÓN TAY: thẻ neo đứng một mình phải đủ 44px (biến chung, đợt 1518) ──
   ⚠ Điều kiện là CÁI CHẠM VÀO, ⛔ không phải bề ngang: cửa sổ thu nhỏ vẫn dùng chuột, còn màn rộng vẫn có
     thể là cảm ứng. Ba thẻ này ⛔ mang phép cắt ba chấm nên "inline-flex" là an toàn (bài học ⑫ đợt 2198). */

@media (pointer: coarse) {
    .ltc-toi-to[b-6uqq0xl7ps],
    .ltc-con-lai[b-6uqq0xl7ps],
    .ltc-truc-sdt[b-6uqq0xl7ps] {
        display: inline-flex;
        align-items: center;
        min-height: var(--cham-toi-thieu);
    }

    .ltc-chip[b-6uqq0xl7ps],
    .ltc-so-viec[b-6uqq0xl7ps] {
        font-size: var(--cham-chu-toi-thieu);
    }
}
/* /Components/Pages/TongQuan/KhoiMucTieuTrangChu.razor.rz.scp.css */
/* ═══ Khối "🎯 Mục tiêu" — ba cột trong HỘP THOẠI (việc o350; gom về một nút ở đợt 1253) ═══

   Giữ đúng ngôn ngữ thị giác của khối 🗃 Công việc — hai hộp mở ra từ hai nút cạnh nhau nên lệch một
   chút là nhìn ra ngay. Mọi màu lấy từ token Bootstrap 5.3 để nền SÁNG và nền TỐI đều đúng; app có hai
   trục giao diện, cầu nối data-theme sang data-bs-theme đặt ở App.razor.

   ⛔ HÌNH DÁNG + ÁO 3D CỦA NÚT KHÔNG Ở ĐÂY: `.nut-mo-bang` và `.tt3d` khai một chỗ trong `app.css`
     (dời về đó ở đợt 1253 — CSS scoped không dùng chung được giữa các component). Chép chúng về đây là
     dựng bản (0,2,0) không bao giờ thắng luật (0,4,0) của `app.css`, chỉ tổ làm phiên sau sửa nhầm chỗ. */

/* ═══ ⭐ ĐỢT 1299 (việc o490) — Ô ② CỦA DÃY THẺ ═══
   Khối thôi là "nút lớn" viên thuốc, nay là một THẺ trong dãy bốn ô. Nút trần mang sẵn viền, nền và
   canh giữa chữ của trình duyệt ⇒ phải gỡ đúng ba thứ ấy thì nó mới trông y hệt ba ô còn lại.
   ⛔ Màu / bóng / gradient vẫn ở `.tt3d` + `.tt3d-muc-tieu` của `app.css` — ⛔ đừng chép sang đây. */
.kmt-the[b-nkha9bbdjc] {
    appearance: none;
    font: inherit;
    cursor: pointer;
}

/* ⛔ ĐỢT 1976 (việc o1255) — LUẬT `.kmt-khoi-giao` ĐÃ GỠ, ⛔ ĐỪNG DỰNG LẠI.
   Nó là một vạch ngăn `border-top` cho khối 📋 Giao việc, sinh ra ở đợt 1299 khi khối ấy nằm NỐI ĐUÔI
   bốn cột mục tiêu trong cùng một lượt cuộn. Từ khi anh Tiến cho chia hộp thành hai tab (04/09/2026)
   thì hai nửa ⛔ KHÔNG BAO GIỜ cùng hiện ⇒ vạch ấy ngăn một thứ với hư không.
   ⚠ Vạch của `.kmt-da-giao` ở cuối tệp thì CÒN — nó ngăn hai phần nằm THẬT SỰ cạnh nhau trong cùng
   một tab (ô khai việc ↔ danh sách việc đã giao). */

/* Dải tab hai nửa của hộp, dựng trong `modal-header` qua khe `DauHop` của khung chung (đợt 1976).
   ⚠ `margin-top` nhỏ vì nó đứng ngay dưới chỗ tiêu đề — mà tiêu đề của hộp có tab là chuỗi RỖNG, nên
   ⛔ đừng chừa khoảng như thể còn một dòng chữ ở trên. */
.kmt-tab-hop[b-nkha9bbdjc] {
    margin-top: .1rem;
}

/* 🪦 ⭐ ĐỢT 1970k (việc o1254) — LỚP `.kmt-o-nguoi` ĐÃ GỠ, ⛔ ĐỪNG DỰNG LẠI.
   Ba ô của hàng "ba vai" nay dùng CHUNG một lớp bề ngang `.kmt-o-vai` (khai ở dưới) — anh Tiến
   04/09/2026: *"3 ô phải có kích thước bằng nhau trên hàng đó"*. Một lớp riêng cho một ô là một ô
   lệch, tức đúng thứ anh vừa bảo bỏ.
   🧠 SỔ ĐO CỦA ĐỢT 1303 VÀ 1448 GIỮ NGUYÊN Ở DƯỚI — ⛔ đừng xoá: hai bài học ấy (bẫy `width: 1%` của
   `.input-group`, và phép đo bề rộng CHỮ bằng `canvas.measureText`) vẫn đúng, chỉ là hàng này ⛔ không
   còn là `input-group` nên bẫy ⛔ không còn chỗ bám. Ngày nào có người đưa một ô `.form-select` trở
   lại vào `.input-group` thì đọc lại đúng hai khối này.

   ── 🕰 SỔ ĐO CŨ (luật đã gỡ, lý lẽ còn nguyên) ──
   ⭐ ĐỢT 1303 — Ô CHỌN NGƯỜI NHẬN NAY ĐỦ RỘNG CHO CẢ CÁI TÊN.
   Anh Tiến 25/08/2026: *"trong ô giao việc, ô tên người cho to ra vừa đủ tên"*.

   📏 ĐO THẬT, ⛔ không ước lượng bằng mắt:
     · Tên dài nhất trong `AspNetUsers` của máy dev — **24 ký tự** ("Nguyễn Giang Thanh Hương").
     · Nhãn một dòng là `HoTen — VaiTroHienThi`; vai trò dài nhất còn hay gặp là "Chuyên viên chính".
     · Đo bề rộng CHỮ bằng `canvas.measureText` đúng phông của `.form-select-sm` (14px):
       **319px**; đệm của ô select là 8px trái + **36px phải** (chỗ mũi tên) ⇒ cần **363px ≈ 22,7rem**.
   ⇒ Trần cũ **16rem = 256px** cắt mất ~107px, tức khoảng **15 ký tự cuối** — đúng chỗ có phần TÊN
     RIÊNG của người ta, nên nhìn vào ô chỉ còn thấy họ và đệm.

   ⚠ Giữ `flex: 0 0 auto` + trần, ⛔ đừng gõ `width` cứng: ô tự co theo nhãn dài nhất ĐANG CÓ (phòng
     ít người thì ô ngắn lại), trần chỉ chặn ca dữ liệu bất thường. "Vừa đủ tên" là thế.
   ⚠ Hộp là `modal-xl` (~1140px) nên 23rem ⛔ không bóp nghẹt ô "Tên việc" bên cạnh: đo còn ~456px
     cho nó sau khi trừ ô ngày (150px) + nhãn Hạn + nút Giao. `.input-group` lại tự xuống dòng ở khổ
     hẹp ⇒ ⛔ không cần luật khổ hẹp riêng.

   ⭐ ĐỢT 1448 (việc o642) — anh Tiến 28/08/2026 gửi ảnh: *"ô tên người được giao việc quá nhỏ không
   thấy"*. Ô chọn co lại còn đúng bề ngang cái mũi tên, ⛔ không đọc được một chữ nào của tên người.

   🔴 GỐC BỆNH — và nó ⛔ KHÔNG nằm ở `max-width` như vẻ ngoài: Bootstrap khai
        `.input-group > .form-select { flex: 1 1 auto; width: 1%; min-width: 0 }`
      Ba giá trị ấy đi cùng nhau mới có nghĩa: `width: 1%` là một CHỖ NEO cho `flex-grow: 1` kéo ra.
      Markup ở đây gắn thêm `flex-grow-0` (đúng chủ ý: ⛔ không muốn ô nuốt cả dòng), còn tệp này ghi
      `flex: 0 0 auto` ⇒ vế KÉO RA biến mất mà `width: 1%` + `min-width: 0` thì CÒN NGUYÊN ⇒ ô tụt về
      gần bằng 0. Hỏng LẶNG LẼ: build xanh, test xanh, luật CSS nào cũng "trông có lý".
   ⇒ Phải ghi đè CẢ HAI vế còn lại (`width` + `min-width`), ⛔ đừng chỉ nới `max-width`: trần trên ⛔
      không kéo nổi một ô đang bị neo ở 1%.
   ⚠ `.input-group` của Bootstrap có `flex-wrap: wrap` ⇒ màn hình hẹp thì ô tự xuống dòng, nên
      `min-width` ở đây ⛔ không đẩy tràn dòng.
   ── 🕰 HẾT SỔ ĐO CŨ ── */

/* ⛔ ĐỢT o1058 — luật `.col-lg-4 + .col-lg-4 > .kmt-cot` (vạch ngăn dọc giữa các cột KỲ) ĐÃ BỎ.
   🔴 Nó là một LUẬT CHẾT từ việc `o452`: markup đổi sang `col-md-6 col-xl-3` mà luật này vẫn hỏi
     `col-lg-4` ⇒ bộ chọn trúng 0 phần tử suốt từ đó, ⛔ không một câu lỗi nào (đúng họ với bệnh
     `.blv-hang-lanh-dao` đợt 852). Nay bốn kỳ là bốn TAB nên ⛔ không còn cột kỳ nào để mà ngăn.
   ⛔ Đừng dựng lại một bản "cho khớp lưới mới": ngăn cách giữa các cột NỘI DUNG nay do đường viền của
     `.kmt-o-muc-tieu` lo — vạch dọc chỉ có nghĩa khi hai bên nó là hai thứ KHÁC LOẠI (tuần ≠ tháng),
     còn ba mục tiêu cùng một kỳ thì cùng loại. */

/* ⛔ ĐỢT o1058 — lớp `.kmt-tieu-de` ĐÃ BỎ cùng dòng tiêu đề trong markup: nút tab đang sáng đã mang
   đúng chữ "🎯 Mục tiêu tuần", in lại lần nữa là một hàng dọc nói điều màn hình vừa nói. */

/* ── ⭐ ĐỢT o1058 — DẢI TAB KỲ (trong hộp, ⛔ KHÔNG phải trên thanh nav) ──
   ⛔ Dải này CỐ Ý ⛔ không mang lớp `dai-tab-nav`, nên nó ⛔ không đi qua bộ biến `--nav-nut-*` của đợt
     976 — khuôn ấy dành cho nút trên THANH NAV. Ở đây chỉ cần một vạch ngăn với phần thân bên dưới. */
.kmt-dai-tab[b-nkha9bbdjc] {
    border-bottom: 1px solid var(--bs-border-color);
    padding-bottom: .4rem;
}

/* Nhãn kỳ ("Tuần 24/08 – 30/08/2026"): mờ hơn tiêu đề, nó là chú thích chứ không phải tiêu đề thứ hai. */
.kmt-nhan-ky[b-nkha9bbdjc] {
    opacity: .75;
    margin-bottom: .35rem;
}

.kmt-thanh[b-nkha9bbdjc] {
    height: 8px;
    margin: .3rem 0;
    border-radius: 999px;
}

/* ── ⭐ ĐỢT o1058 — MỘT MỤC TIÊU = MỘT Ô TRONG LƯỚI ──
   🔴 Thay `.kmt-dong` của đời một-cột-dọc (vạch `border-top` ngăn dòng nọ với dòng kia). Trong một
     LƯỚI thì vạch trên đầu ⛔ không còn nói được gì: ba ô nằm CẠNH nhau chứ ⛔ không nối tiếp nhau, nên
     ranh giới phải bao QUANH từng ô.
   ⚠ `h-100` ở markup + viền ở đây là thứ giữ cho ba ô trên một hàng CAO BẰNG NHAU dù nội dung dài
     ngắn khác nhau — thiếu nó thì lưới trông như xếp hụt. */
.kmt-o-muc-tieu[b-nkha9bbdjc] {
    padding: .4rem .5rem;
    border: 1px solid var(--bs-border-color);
    border-radius: .375rem;
}

.kmt-phu[b-nkha9bbdjc] {
    opacity: .85;
}

/* Nội dung mục tiêu dài phải xuống dòng trong ô hẹp, ⛔ không được đẩy vỡ lưới. `min-w-0` của
   Bootstrap lo phần flex, luật này lo phần từ dài không có dấu cách (đường dẫn, mã hồ sơ). */
.kmt-o-muc-tieu .flex-grow-1[b-nkha9bbdjc] {
    overflow-wrap: anywhere;
}

/* ── Ô TẠO NHANH ở ĐẦU hộp, một hàng (đợt 1253 — anh Tiến: *"đưa form nhập nhanh lên đầu và làm gọn
      thành 1 hàng"*) ── */

/* Ô chọn kỳ vừa đủ chữ "Tháng"/"Quý", ⛔ không kéo dài: trong `input-group` mọi ô chia đều bề ngang,
   để nguyên là ô chọn kỳ rộng ngang ô nội dung — thứ người ta gõ nhiều nhất lại hẹp nhất. */
.kmt-o-ky[b-nkha9bbdjc] {
    width: auto;
    flex: none;
}

/* ⭐ ĐỢT 1472 (việc o674) — Ô "MỤC TIÊU CHO AI": tự đăng ký (Cá nhân · &lt;tên phòng&gt; · Tổng kho)
   HOẶC tên một người được giao.
   🔄 ⭐ ĐỢT 1968 (việc o1216) — Ô NAY LÀ THẺ BỌC CỦA `OGoTim`, ⛔ KHÔNG còn là một `<select>`.
     Anh Tiến 03/09/2026: *"ô lọc tên và ô chọn cá nhân là thừa chỉ để 1 ô điền"*.
   🕰 Hai câu cũ ở đây (*"giữ TRẦN vì tên phòng dài"* · *"có SÀN vì `width: auto` của select co theo
     dòng đang chọn"*) nói về hành vi của thẻ `<select>` ⇒ HẾT hiệu lực: ô gõ nay là `<input>`, bề
     ngang của nó ⛔ không phụ thuộc dòng đang chọn nữa. Thứ CÒN hiệu lực là con số: ô phải đủ chứa
     một họ tên đầy đủ hoặc tên riêng của phòng, nên đặt thẳng 17rem thay cho cặp sàn/trần.
   ⚠ `max-width: 100%` là đường lui khổ hẹp — thiếu nó thì 17rem cứng làm tràn ngang trên điện thoại. */
.kmt-o-cho-ai[b-nkha9bbdjc] {
    flex: 0 0 auto;
    width: 17rem;
    max-width: 100%;
}

/* ═══ 📐 ĐỢT 2202 (việc o1646) — Ô «CÁ NHÂN» DẠNG CHỮ PHẢI CAO BẰNG CỤM KHAI BÊN CẠNH ═══

   Anh Tiến 08/09/2026 (ảnh chụp đúng hàng này): *"ở hộp mục tiêu, hộp cá nhân và hộp điền kế bên
   kích thước không đều, em phải đưa vào skill kỹ năng cơ bản này"*.

   🔑 GỐC BỆNH — HAI NHÁNH CỦA CÙNG MỘT Ô ĐI HAI CỠ KHÁC NHAU. Người có nhiều phạm vi thì ô là
   `<OGoTim>`, mà component ấy tự bọc `input-group-sm` ⇒ cao 31px, khớp cụm bên phải. Người CHỈ có
   một phạm vi thì nhánh `else` in ra `<span class="input-group-text">` **đứng ngoài mọi
   `input-group`** ⇒ ⛔ không ai hạ cỡ cho nó: đệm .375/.75rem và chữ 1rem cho ra **38px**, lệch
   **7px** so với cụm [kỳ · nội dung · Thêm] ngay cạnh.
   🔴 Vì thế lỗi này CHỈ NGƯỜI THƯỜNG THẤY — tài khoản lãnh đạo (nhiều phạm vi) đi nhánh kia và
   hàng của họ đều tăm tắp. Đo bằng tài khoản của mình rồi kết luận "⛔ không lệch" là đo nhầm ca.

   ⚠ Chép ĐÚNG bộ số của `.input-group-sm > .input-group-text` (Bootstrap 5.3), ⛔ đừng gõ một
   `height` cố định: cụm bên cạnh cao theo cỡ chữ nền, đóng đinh 31px là ngày nào đổi cỡ chữ thì
   hàng lệch trở lại mà ⛔ không câu báo nào.
   ⛔ Bộ chọn phải là `span.kmt-o-cho-ai` — thẻ `div` bọc `OGoTim` cũng mang đúng lớp ấy, mà nó
   ⛔ KHÔNG được nhận đệm nào (đệm của nó nằm ở ô bên trong). */
span.kmt-o-cho-ai[b-nkha9bbdjc] {
    padding: .25rem .5rem;
    font-size: .875rem;
    border-radius: var(--bs-border-radius-sm, .25rem);
}

/* Ô nội dung là thứ giãn ra chiếm phần còn lại của hàng. Đặt sàn bề ngang để khi hẹp nó xuống dòng
   nguyên khối thay vì co lại còn vài ký tự.
   🔴 ⭐ ĐỢT o1058 — bộ chọn ĐỔI từ `.kmt-hang-them .form-control` sang `.kmt-o-noi-dung`, ⛔ đừng
     lùi lại: từ đợt này hàng nhập còn đựng CẢ ô lọc 🔎 (cũng là `.form-control`), nên bộ chọn cũ sẽ
     đặt sàn 14rem cho luôn ô lọc — tức phá đúng thứ anh Tiến vừa xin (*"chừa 1 khoảng vừa đủ"*). */
.kmt-o-noi-dung[b-nkha9bbdjc] {
    min-width: 14rem;
}

/* ── 🪦 ĐỢT 1968 (việc o1216) — LỚP `.kmt-o-loc` ĐÃ BỎ CÙNG Ô LỌC 🔎 CỦA NÓ, ⛔ ĐỪNG DỰNG LẠI ──
      Anh Tiến 03/09/2026: *"ô lọc tên và ô chọn cá nhân là thừa chỉ để 1 ô điền"* ⇒ phép lọc dời vào
      trong `OGoTim`, ⛔ không còn một ô riêng nào để cấp bề ngang.
   🔑 BÀI HỌC BA VẾ VẪN CÒN NGUYÊN GIÁ TRỊ, chỉ đổi chỗ áp: Bootstrap khai
     `.input-group > .form-control { flex: 1 1 auto; width: 1%; min-width: 0 }` và ba giá trị ấy chỉ có
     nghĩa khi đi CÙNG NHAU — đặt `width` mà quên `flex` là ô vẫn bị kéo giãn, đặt `flex: none` mà quên
     `width` là ô tụt về gần bằng 0. Cả hai hỏng LẶNG LẼ. Xem `.kmt-o-nguoi` và `.kmt-o-lanh-dao`.

   ── ⭐ ĐỢT 1968 — HÀNG THÊM MỤC TIÊU NAY LÀ `d-flex`, ⛔ KHÔNG PHẢI MỘT `input-group` DUY NHẤT ──
   🔑 Ô gõ-để-chọn TỰ NÓ đã là một `input-group` hoàn chỉnh (ô gõ + nút ✕ + nút ▼); lồng nó vào một
     `input-group` khác là vỡ bo góc và vỡ cả phép đặt bảng gợi ý. Ba thứ còn lại (kỳ · nội dung · nút
     Thêm) ở lại chung một nhóm để dính liền nhau thành một khối khai.
   👆 ⛔ Đừng ép hàng vào một dòng cứng (`flex-nowrap`): `flex-wrap` là đường lui duy nhất cho khổ điện
     thoại (luật vùng chạm đợt 1518). */
.kmt-nhom-noi-dung[b-nkha9bbdjc] {
    flex: 1 1 22rem;
    min-width: 0;
}

/* ══ ⭐ ĐỢT 1970k (việc o1254) — MỘT LỚP BỀ NGANG DUY NHẤT CHO CẢ SÁU Ô CỦA KHỐI 📋 GIAO VIỆC ══
   🔴 Anh Tiến 04/09/2026 (kèm ảnh chụp), nguyên văn: *"ô người liên quan phải làm giống ô người thực
     hiện và lãnh đạo, đặt kế ô lãnh đạo, thu gọn 2 ô người thực hiện và người lãnh đạo, **3 ô phải có
     kích thước bằng nhau trên hàng đó**"* · *"ô chọn hồ sơ và chọn thiết bị liên quan cũng làm giống ô
     người thực hiện"*.

   📏 SỐ ĐO TRƯỚC KHI SỬA (Chrome thật, khung 1400px): người thực hiện 320 · lãnh đạo 320 · người liên
     quan **1091,4** (một hàng riêng) · hồ sơ **557,7** · thiết bị **525,7** · ưu tiên 179,3. Sáu ô,
     năm bề ngang khác nhau.

   🔑 `flex: 1 1 0` LÀ CẢ CÁI LUẬT, ⛔ ĐỪNG ĐỔI SANG `auto`. Nền (`flex-basis`) bằng **0** thì phần
     trống của hàng chia ĐỀU ⇒ ba ô bằng nhau tới từng pixel. Đổi thành `1 1 auto` là nền chạy theo
     NỘI DUNG (tên người dài ngắn khác nhau, "Trung bình" ngắn hơn "Chọn hồ sơ liên quan…") ⇒ ba ô
     lệch nhau, mà **build xanh, test xanh, màn hình vẫn trông có lý**. Hàng rào
     `KhoiGiaoViecTrangChuTests` canh đúng dòng này.
   🔑 SÀN `min-width` là đường lui cho khổ hẹp: hết chỗ thì cả ô XUỐNG DÒNG nguyên khối, ⛔ không co
     lại còn mấy ký tự. ⛔ Đừng viết `min-width: 0` ở đây — số 0 là lời cho phép co tới vô cùng, đúng
     lỗi mà đợt 1448 đã trả giá (ô chọn người tụt còn bằng cái mũi tên).
   ⚠ `max-width: 100%` để một mảnh dài ⛔ không đẩy tràn ngang cả hộp thoại.

   🪦 BA LỚP CŨ ĐÃ GỠ Ở ĐỢT NÀY — ⛔ ĐỪNG DỰNG LẠI: `.kmt-o-nguoi` · `.kmt-o-lanh-dao` ·
     `.kmt-o-uu-tien` (đợt 1448 + o1058) và `.kmt-o-ho-so` · `.kmt-o-thiet-bi` (đợt 1968). Chúng sinh
     ra để chống bẫy `width: 1%` mà Bootstrap khai cho con của `.input-group`
     (`.input-group > .form-select { flex: 1 1 auto; width: 1%; min-width: 0 }` — ba giá trị chỉ có
     nghĩa khi đi CÙNG NHAU). Hàng ba vai nay là `d-flex` chứ ⛔ không còn `input-group`, nên bẫy ấy
     ⛔ không còn chỗ bám; giữ lại năm lớp là giữ năm bề ngang khác nhau — đúng thứ anh vừa bảo bỏ.
     ⛔ Và ⛔ đừng cho ô nào của hai hàng ấy một lớp bề ngang RIÊNG: một lớp riêng là một ô lệch. */
/* 🪦 ⭐ ĐỢT 1992 (việc o1287) — LUẬT `.kmt-o-vai` ĐÃ DỜI sang `KhungTaoNhiemVu.razor.css` (tên mới
   `.ktn-o-vai`) cùng lượt dời khung giao việc sang ô ③ 📋 Nhiệm vụ. Sổ đo ở trên GIỮ NGUYÊN — nó là
   lý lẽ của luật ấy, đọc ở đây rồi sửa ở bên kia. ⛔ Đừng dựng lại: tab 📋 ở ô ② không còn ô nào. */

/* 🪦 ⭐ ĐỢT 1970m (việc o1270) — LỚP `.kmt-the-chon` ĐÃ DỜI, ⛔ ĐỪNG DỰNG LẠI Ở ĐÂY.
   Sáu ô nay là `<OGoTim>`, mà dải thẻ 👤/🧰 do CHÍNH component ấy dựng ⇒ nơi gọi ⛔ không còn thẻ nào
   để trang điểm. Luật (và trọn sổ đo của nó) nằm ở **`.ogt-the`** trong
   `Components/ChungTu/OGoTim.razor.css`, nên nay nó che luôn cả `HopThoaiTaoNhiemVu`.
   🩸 BÀI HỌC ⛔ ĐỪNG QUÊN — nó là lý do luật ấy tồn tại: `.badge` của Bootstrap là `d-inline-flex` một
     hàng, chữ ⛔ không tự xuống dòng ⇒ thẻ cứ dài ra mãi và đẩy tràn CẢ HỘP. Đo sống 04/09/2026 (đợt
     1970k), khổ **1024px**: thân hộp tràn ngang **18px** vì hai thẻ thiết bị rộng **272,2** và
     **283,6px** nằm trong một ô chỉ **250,1px**. */

/* ── ⭐ ĐỢT 1944 (việc o1216) — Ô TÊN VIỆC LÀ THỨ DUY NHẤT CÒN GIÃN TRÊN HÀNG 1 ──
      Anh Tiến 03/09/2026: *"ở trang chủ anh vào giao việc cho chính mình vẫn không có giao diện điền
      ví dụ: sửa chữa công tơ C321"*.

   📏 SỐ ĐO TRƯỚC KHI SỬA (Chrome thật, khung 1400px): hàng 1 cõng NĂM ô — ô chọn người 354px, ô tên
     việc 477px, cộng nhãn "Hạn" 43px + ô ngày 150px + nút. Ba ô chọn nay dời xuống hàng 2 nên hàng 1
     còn ĐÚNG ba thứ, và ô tên việc — thứ người ta gõ nhiều nhất — lấy trọn phần giãn.
   ⚠ Đặt SÀN chứ ⛔ không đặt trần: chữ chìm của ô này dài (`LuatBonOTrangChu.ChuChimTenViec` chở cả
     một ví dụ thật), thiếu sàn thì ở khổ hẹp nó co lại còn mấy chữ đầu — mà chữ chìm bị cắt thì đúng
     bằng ⛔ không có chữ chìm.
   👆 ⛔ Đừng ép hàng vào một dòng cứng: `.input-group` tự xuống dòng khi hẹp, đó là đường lui duy nhất
     cho khổ điện thoại (luật vùng chạm đợt 1518). */
/* 🪦 ⭐ ĐỢT 1992 (việc o1287) — `.kmt-o-ten-viec` ĐÃ DỜI sang `KhungTaoNhiemVu.razor.css` (`.ktn-o-ten`),
   cùng lý do với `.kmt-o-vai` ở trên. */

/* ── ⭐ ĐỢT 1266 — NỘI DUNG TỰ CUỘN TRONG HỘP CAO CỐ ĐỊNH, VÀ THU NHỎ MỘT ÍT ──
      Anh Tiến 25/08/2026: *"hiển thị kích thước hộp thoại cố định và hợp lý, nội dung trong từng cột
      cho phép cuộn xuống xem khi vượt ngoài, nội dung trong cột có thể thu nhỏ lại một ít cho vừa
      khung xem"*.

   🔑 CHIỀU CAO ĐI TỪ TRÊN XUỐNG QUA BA NẤC, ⛔ đứt một nấc là cả luật nằm im (hỏng LẶNG LẼ, màn hình
     vẫn "trông có lý", chỉ là nội dung không cuộn):
       ① thẻ hộp `.modal-content.hop-cao-co-dinh` — chiều cao cố định, khai ở `app.css`;
       ② thân hộp `.modal-body` — nhận lớp `d-flex flex-column` từ chỗ gọi trong `.razor`;
       ③ vùng cuộn `.kmt-vung-cuon` — ăn hết phần còn lại (⭐ việc `o1699` đổi nấc này, xem dưới).
     Sửa một nấc thì đi soát hai nấc kia.

   ⚠ `min-height: 0` là dòng KHÔNG ĐƯỢC BỎ dù trông như thừa: flex item mặc định `min-height: auto`,
     tức nó TỪ CHỐI co nhỏ hơn nội dung ⇒ vùng cuộn phình ra đúng bằng nội dung dài nhất và đẩy tràn
     khỏi hộp, `overflow-y` bên dưới không bao giờ có việc để làm.

   🩸 ⭐ ĐỢT 2231d (việc `o1699`) (09/09/2026) — NẤC ③ ĐỔI CHỦ: NAY LÀ `.kmt-vung-cuon`, ⛔ KHÔNG CÒN LÀ
     `.kmt-hang-cot`. Anh Tiến gửi ảnh hộp này: *"ô quá nhỏ không hiển thị được nội dung"*.
   🔑 GỐC BỆNH — LUẬT CŨ VIẾT KHI THÂN HỘP CHỈ CÓ **MỘT** KHỐI CO ĐƯỢC. Đợt 2218 thêm nửa 📊 Kế hoạch
     năm (`.kmt-ke-hoach`) làm em ruột của cùng thân hộp; khối ấy để `min-height: auto` nên nó **TỪ
     CHỐI co**, còn hàng cột thì khai sàn `0` nên nó **nhận trọn** phần thiếu ⇒ co về **ĐÚNG 0px**.
     📏 Đo sống 09/09 (app 5270, tài khoản chưa đăng ký mục tiêu nào): khổ **1280×620** hàng cột ra
     **0px** (nội dung 42px) · khổ **1100×640** cũng **0px** ⇒ nhãn kỳ *"Tuần 07/09 – 13/09/2026"* rơi
     ra ngoài hộp cuộn cao 0px rồi bị `.kmt-ke-hoach` (bắt đầu sau đó 8px) **phủ mất nửa dưới** — đúng
     chữ *"cắt ngang thân chữ"* anh mô tả; cả danh sách mục tiêu cũng ⛔ không còn một pixel để hiện.
   🔴 Hỏng **LẶNG LẼ và CHỈ Ở MÀN HÌNH THẤP**: khổ 1400×900 hàng cột còn 144px nên nhìn vẫn bình
     thường; máy xách tay 1366×768 (hay cửa sổ trình duyệt bẹp) thì `min(72vh, 700px)` tụt xuống và
     phần thiếu đổ hết vào đây. Build xanh · test xanh · ảnh chụp ở khổ rộng cũng xanh.
   ⇒ Chữa bằng cách **BỎ HẲN cuộc giành chiều cao**: hai nửa nay nằm trong MỘT thẻ bọc
     `.kmt-vung-cuon` (khai ở `.razor`), và **chỉ thẻ bọc ấy** là flex item co giãn. Bên trong nó
     ⛔ không còn ai phải nhường ai — ai cao bao nhiêu cứ cao, cuộn xuống là thấy.
   ⛔ **HAI ĐƯỜNG ĐÃ THỬ VÀ ĐÃ ĐO, ⛔ ĐỪNG ĐI LẠI**:
     · *Đặt SÀN `min-height` cho hàng cột* — hết co về 0 thật, nhưng `min-height` là sàn **vô điều
       kiện**: đo lại ở 1280×620 với danh sách mục tiêu RỖNG vẫn ra đúng **128px**, tức chừa **86px
       trắng** rồi đẩy nửa kế hoạch xuống dưới mép hộp. Vá xong mà nhìn tệ hơn.
     · *Bỏ vế nở (`flex-grow: 0`) kèm sàn* — `min-height` vẫn thắng, số đo **⛔ không đổi một pixel**
       (vẫn 128px). Đây là chỗ dễ suy luận sai nhất: `flex-grow` ⛔ không liên quan gì tới sàn.
   🔑 Hai hàng ĐIỀU KHIỂN (ô nhập nhanh + dải tab kỳ) CỐ Ý **ở ngoài** vùng cuộn — chúng phải bấm được
     mọi lúc; kéo chúng vào trong là muốn đổi kỳ phải cuộn ngược lên.
   🪦 Ba luật cũ `.kmt-hang-cot { flex … }` · `.kmt-hang-cot > .kmt-cot` · `.kmt-cot { max-height:
     100%; overflow-y: auto }` (đợt 1266 + o1058) ĐÃ GỠ — vùng cuộn nay ở thẻ bọc, để nguyên chúng là
     dựng **hai hộp cuộn lồng nhau**, thứ ⛔ không ai lăn chuột trúng được. `.kmt-cot` chỉ còn luật cỡ
     chữ ở bên dưới, và lớp `h-100` trên nó nay là vô hại (cha cao theo nội dung).
   📏 ĐO SỐNG SAU KHI VÁ (09/09, tiêm chính bộ luật này vào bản chạy 5270 — bản dựng do phiên chính
     làm): **1280×620** thân hộp hết tràn (`scrollHeight` = `clientHeight` = 341), hàng cột về **34,3px
     đúng bằng nội dung**, nhãn kỳ hiện ĐỦ · **1100×640** thân hộp 356 = 356, vùng cuộn 230/377 ·
     **1400×900** ⛔ không phải cuộn một dòng nào (543 = 543, 0 mảnh chữ khuất) · chốt ngược **375×812**
     (khối media này ⛔ không áp): mọi con số TRÙNG bản trước, chỉ nhích 8px do rãnh `g-2` → `g-3`. */
@media (min-width: 992px) {
    .kmt-vung-cuon[b-nkha9bbdjc] {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        /* 🔴 `overflow-x: hidden` là dòng BẮT BUỘC ĐI KÈM, ⛔ đừng bỏ vì trông như thừa: thẻ `.row` của
           Bootstrap dựng rãnh bằng **lề ÂM** (`g-3` ⇒ −8px mỗi bên), mà lề ấy trước đây rơi vào phần
           đệm 1rem của `.modal-body`. Từ lúc có thẻ bọc này thì nó thò ra ngoài hộp cuộn ⇒ trình duyệt
           mọc thêm một **THANH CUỘN NGANG** cho đúng 2px thừa (đo sống 09/09: vùng cuộn `scrollWidth`
           1093 / `clientWidth` 1091) — một thanh cuộn ⛔ không cuộn cái gì, nằm chắn ngay dưới bảng.
           ⛔ Cắt 8px lề âm ấy ⛔ KHÔNG mất chữ nào: con của `.row` có sẵn 8px đệm nên nội dung thật vẫn
           bắt đầu đúng ở mép vùng cuộn. */
        overflow-x: hidden;
        /* Chừa một chút đệm phải để thanh cuộn ⛔ không đè lên chữ và lên ô tick của dòng cuối. */
        padding-right: .35rem;
    }
}

/* Thu nhỏ nội dung trong cột — ⛔ CHỈ trong cột, ⛔ không đụng hàng nhập nhanh ở đầu hộp: ô người ta
   GÕ VÀO mà nhỏ hơn cỡ chuẩn là khó gõ, trong khi ba cột là thứ để ĐỌC LƯỚT.
   ⓘ Tiêu đề cột giữ nguyên cỡ chuẩn (`1rem` = cỡ thân trang) để ba cột vẫn có mốc phân biệt rõ với
     phần chữ bên dưới sau khi thu nhỏ. */
.kmt-cot[b-nkha9bbdjc] {
    font-size: .875rem;
}

.kmt-tieu-de[b-nkha9bbdjc] {
    font-size: 1rem;
}

/* ── ⭐ ĐỢT 1857 (việc o1115) — DANH SÁCH "VIỆC TÔI ĐÃ GIAO" ở cuối khối 📋 Giao việc ──
      Anh Tiến 03/09/2026: *"phải có danh sách việc đã giao ở hộp hội thoại tại trang chủ"*.

   Một vạch ngăn mảnh để mắt tách được phần ĐỌC (danh sách) khỏi phần GÕ (ô khai) ngay trên nó.
   ⛔ Màu vạch lấy token Bootstrap, ⛔ đừng ghim mã màu: nền sáng và nền tối có màu viền khác hẳn nhau.
   ⚠ Vạch NÀY ở lại sau đợt 1976 (chỗ ⛔ đừng dọn nhầm theo `.kmt-khoi-giao`): hai phần nó ngăn nằm
   THẬT SỰ cạnh nhau trong cùng một tab, ⛔ không phải hai nửa loại trừ nhau của dải tab. */
/* ═══ 🪦 ĐỢT 2202b (việc o1659) — LUẬT CỦA DANH SÁCH «VIỆC ĐÃ GIAO» ĐÃ DỜI, ⛔ ĐỪNG DỰNG LẠI ═══
   Anh Tiến 08/09/2026: *"em đưa giao việc gom vào nhiệm vụ, mục tiêu vẫn để riêng"*. Bộ luật
   `.kmt-da-giao` · `.kmt-dong-giao` · `.kmt-luoi-giao` (kể cả khối `@media (max-width: 820px)` thu về
   một cột) nay ở `KhoiGiaoViecTrangChu.razor.css` với tiền tố `kgv-`.
   ⛔ Giữ một bản ở đây là luật CHẾT: CSS cách ly chỉ ăn trên thẻ do CHÍNH component này sinh ra, mà
   markup ấy đã đi — nên bản chép sẽ ⛔ không bao giờ chạy, chỉ tổ làm phiên sau sửa nhầm tệp.
   🚧 `TrangChuDanhSachHaiCotTests` nay đọc tệp mới; mốc 820px vẫn là
   `LuatHaiCotDanhSachTrangChu.KhoHepToiDaPx` và vẫn đối chiếu CẢ HAI tệp CSS. */

/* ═══ ⭐ VIỆC o962 — VÙNG CHẠM NGÓN TAY QUANH Ô TICK CỦA TỪNG MỤC TIÊU ═══

   📏 ĐO SỐNG 03/09/2026 (Chrome thật, khung 390×844 cảm ứng, app dựng riêng từ một `git worktree`
     ở cổng 5304, phép đo hỏi thẳng `document.elementFromPoint` — ⛔ không đọc CSS, ⛔ không đọc hộp
     cha): ô tick trong hộp 🎯 Mục tiêu ra **ô vuông 14×14 · vùng bấm thật 15px**, ngưỡng là 44px.
   ✅ ĐO LẠI 06/09/2026 TRÊN BẢN ĐÃ VÁ (cùng khung, cùng phép đo): **45px**, 0 ô dưới ngưỡng. Lượt
     ấy cũng dựng lại bản CHƯA VÁ trong cùng một worktree và đo ra đúng **15px** — tức con số 15px
     ở dòng trên tái hiện được, ⛔ không phải một lời khai cũ chép lại.
   ⚠ Khối này nằm SAU MỘT CÚ BẤM (nút 🎯 mở hộp thoại) nên bộ đo phải bấm nút rồi mới đo được. Lượt
     đo thẳng vào trang chủ trả về *"0 ô tick"* — đó là **⛔ KHÔNG ĐO ĐƯỢC**, ⛔ đừng đọc thành
     *"màn hình này đã đạt"* (bẫy sàn đếm, đợt 1014).

   🔴 Ô TICK TRẦN ⛔ KHÔNG CHỮA ĐƯỢC BẰNG CSS CÁCH LY MỘT MÌNH. Thứ đứng cạnh ô là một thẻ `div`
     đựng nội dung mục tiêu, ⛔ không phải một nhãn nối bằng `for` ⇒ bấm vào chữ ⛔ không đổi tick,
     và ⛔ không có thẻ nào bấm được để mà nới. Vì thế tệp `.razor` đi kèm bọc ô vào một thẻ `label`
     mang lớp dưới đây; luật ở đây chỉ cấp cho thẻ ấy cái cỡ.

   ⚠ `align-items: flex-start` chứ ⛔ KHÔNG `center` — đây là chỗ tệp này KHÁC khuôn kiểu ② của đợt
     1690 (ở đó ô tick đứng một mình trong một ô bảng). Hàng ở đây là `d-flex align-items-start`:
     cho `center` thì ô vuông rơi xuống GIỮA hộp cao 44px, tức tụt khỏi dòng chữ đầu tiên chừng một
     dòng — một lượt vá VÙNG CHẠM mà lại xê dịch đúng thứ người ta nhìn thấy.
   ⚠ Cần CẢ bề ngang: ô tick trần ⛔ không có chữ bên cạnh để mượn (chữ là thẻ `div`, bấm ⛔ không
     ăn), nới mỗi chiều cao là ngón tay vẫn trượt NGANG ra khỏi vùng bấm.
   ⚠ Dùng sàn chứ ⛔ không đặt chiều cao cứng: mục tiêu dài xuống nhiều dòng thì cứ để ô cao theo.
   🔴 Điều kiện là CÁI CHẠM VÀO, ⛔ không phải bề ngang màn hình: cửa sổ thu nhỏ trên máy tính vẫn
     là chuột, còn màn hình rộng vẫn có thể là cảm ứng.
   ⚠ Ngưỡng đọc từ BIẾN khai một chỗ ở `app.css` — ⛔ đừng gõ con số vào tệp này.
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo; cặp ký tự ấy lọt vào giữa câu văn là chú
     thích đóng sớm và trình duyệt bỏ nguyên luật nằm ngay dưới (bài học đợt 852) — nên ở đây gọi
     tên cặp ấy bằng CHỮ.
   🚧 Hàng rào: `TKNB.Tests/VungChamNgonTayTests.cs`
   📏 Đo lại: node cong-cu/do-song-o-tick-cham.mjs — nhớ bấm nút 🎯 trước, xem ghi chú ở trên. */

/* 🖱 NỀN (MÁY TÍNH): THẺ BỌC ⛔ KHÔNG ĐƯỢC SINH HỘP — ⛔ ĐỪNG GỠ DÒNG NÀY.
   📏 Đo sống 06/09/2026 (cùng bản dựng, cùng tài khoản, khung CHUỘT 1369×900, `elementFromPoint`):
     trước khi bọc **15px** · bọc rồi mà ⛔ chưa có luật dưới đây **22px (+7)** · có luật **15px**.
   🔑 Thẻ `label` là markup ⛔ KHÔNG có điều kiện, nên ngay ở chế độ chuột — nơi câu media bên dưới
     ⛔ không áp — nó vẫn sinh một hộp dòng cao 21px (cao theo `line-height`, ⛔ không theo ô vuông
     14px). Hộp ấy ⛔ không nhìn thấy nhưng BẤM ĐƯỢC ⇒ người ngồi chuột bấm hụt lên/xuống 3–4px là
     đổi trạng thái một mục tiêu. Đó là đổi HÀNH VI, dù bố cục nhìn thấy vẫn trùng khít từng pixel
     (đo 06/09: ô tick · hàng · cột chữ · chiều cao trang đều y nguyên trước và sau).
   ⇒ `display:contents` (chú thích CỐ Ý viết LIỀN, ⛔ không gõ nguyên văn dòng mã: bộ đo đột biến
     neo bằng chuỗi thô, gõ y hệt là nó gieo trúng CHÚ THÍCH rồi báo "bắt được" — đèn xanh giả)
     ⇒ thẻ bọc ⛔ không sinh hộp nào, ô tick trở lại làm mục flex đúng như
     trước khi vá; câu media bên dưới đặt lại `inline-flex` nên màn cảm ứng ⛔ không mất gì.
   ⚠ Dòng này phải đứng TRƯỚC câu media: hai luật cùng độ đặc hiệu, thắng thua theo THỨ TỰ. */
.o-tick-cham[b-nkha9bbdjc] {
    display: contents;
}

@media (pointer: coarse) {
    .o-tick-cham[b-nkha9bbdjc] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: flex-start;
    }
}

/* ═══ ⭐ ĐỢT 2176 (việc o1620) — HÀNG ĐĂNG KÝ MỤC TIÊU TRÊN ĐIỆN THOẠI: BA DÒNG GỌN, ⛔ KHÔNG NÚT MỒ CÔI ═══
   Anh Tiến 07/09/2026: *"tối ưu các khung điền ở trang chủ … đặc biệt chú ý đến giao diện điện thoại"*.
   Đo sống 375px (ruột hộp 326px): ô «mục tiêu cho ai» cứng 17rem = 272px nên chừa một khoảng trống
   54px vô nghĩa bên phải; hàng dưới [kỳ 93px][nội dung 234px] vừa khít 327px ⇒ nút «Thêm» 53px RỚT xuống
   một dòng riêng, đứng lẻ loi sát mép trái — cùng bệnh nhãn «Hạn» mồ côi của o1615.
   🔑 Ba dòng có chủ ý: (1) «cho ai» trọn dòng · (2) [kỳ][nội dung] kín dòng · (3) nút Thêm TRỌN DÒNG —
     nút chính rộng cả hàng là dáng quen của điện thoại, ngón cái bấm ⛔ không trượt. `min-width: 0` +
     `flex: 1 1 0` cho ô nội dung để nó co giãn theo phần còn lại thay vì giữ sàn 14rem của máy tính.
   ⚠ Mốc 576px = `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx` — cùng mốc với o1613/o1615/o1620 ở ô ③
     và khung tạo nhiệm vụ, để bốn khung điền của trang chủ ĐỔI DÁNG Ở CÙNG MỘT BỀ NGANG. */
@media (max-width: 576px) {
    .kmt-o-cho-ai[b-nkha9bbdjc] {
        width: 100%;
    }

    .kmt-o-noi-dung[b-nkha9bbdjc] {
        flex: 1 1 0;
        min-width: 0;
    }

    .kmt-nhom-noi-dung > .btn-primary[b-nkha9bbdjc] {
        flex: 1 1 100%;
    }
}

/* ═══ ⭐ ĐỢT 2198 (việc o1640) — TÊN VIỆC TRONG HỘP «MỤC TIÊU» CŨNG PHẢI ĐỦ 44px ═══
   Cùng một luật, cùng một lý do và cùng một phép đo với `ONhiemVu.razor.css` — xem chú thích đầy đủ ở
   đó. 📏 Đo sống 375×812 (08/09/2026): 4 thẻ tên trong hộp này cao 17px và 41px.
   ⛔ Hai tệp cố ý mỗi bên một bản: CSS cách ly chỉ áp cho phần tử do CHÍNH component ấy dựng, nên một
     bản dùng chung phải dời lên `app.css` — mà đó đúng là chỗ ⛔ không được nới (lớp dùng chung). */
@media (pointer: coarse) {
    a.ten-ban-ghi[b-nkha9bbdjc] {
        display: inline-flex;
        align-items: center;
        min-height: var(--cham-toi-thieu);
    }
}

/* 📊 ĐỢT 2218 (việc o1674) — nửa KẾ HOẠCH NĂM của ô ②.
   ⚠ Thanh tiến độ mỏng có CHỦ Ý: khối này là phần TÓM TẮT nằm cuối một hộp đã dài, thanh dày bằng
     mặc định Bootstrap (1rem) đẩy hộp cao thêm hai dòng mà ⛔ không nói thêm điều gì. */
.kmt-thanh[b-nkha9bbdjc] {
    height: 6px;
    margin: 2px 0;
}

/* Cột số co theo nội dung để cột TÊN giữ phần bề ngang còn lại — luật ① của đợt 2154:
   "cột tên là cột to nhất, mọi cột khác nhường chỗ". */
.kmt-cot-hep[b-nkha9bbdjc] {
    width: 1%;
    white-space: nowrap;
}

/* 📊 ĐỢT 2231d (việc `o1699`) — HAI Ô SỐ CỦA KẾ HOẠCH NĂM: TÁCH NHAU RA CHO RA HAI Ô.
   Anh Tiến 09/09/2026 (cùng lượt báo *"ô quá nhỏ không hiển thị được nội dung"*): hai cột **phí được
   cấp** và **công trình đầu tư** *"chen chật, dính sát nhau"*.
   📏 Đo sống 09/09 khổ 1280×620: ô trái hết ở **632,5px**, ô phải bắt đầu ngay **635px** ⇒ hai thanh
     tiến độ chỉ cách nhau **8px** (`g-2` = rãnh 0,5rem) nên chúng đọc thành **MỘT vạch dài liền** — mắt
     ⛔ không phân biệt được đâu là tiền, đâu là công trình.
   ⇒ Ba vế, thiếu vế nào cũng còn chật:
     · rãnh `g-2` → **`g-3`** (0,5rem → 1rem) ở markup;
     · vạch ngăn dọc giữa hai ô — thứ duy nhất nói *"đây là hai con số khác nhau"* khi cả hai đều là
       một dòng nhãn + một dòng số + một thanh tiến độ trông y hệt nhau;
     · `min-width: 0` — ô lưới của Bootstrap là flex item, để `auto` là một con số dài (đo:
       *"1.297.749.600 / 8.780.000.000"*) **NONG ô ra** rồi đẩy ô bên cạnh tràn khỏi hộp.
   ⛔ Vạch ngăn CHỈ vẽ từ 992px trở lên — dưới mốc ấy `col-lg-6` đã cho hai ô **xếp chồng**, mà một vạch
     dọc bên trái của ô nằm dưới thì ⛔ không ngăn cách gì cả, chỉ là một nét thừa.
   ⚠ Mốc `col-lg-6` (⛔ không phải `col-md-6` như lượt dựng đầu) là vì bề ngang HỘP ⛔ không đi theo bề
     ngang MÀN HÌNH: khổ 768–991px thì `modal-xl` của Bootstrap chỉ còn **500px** ⇒ chia đôi là hai ô
     250px, chật hơn hẳn lúc xếp chồng. */
.kmt-kh-o[b-nkha9bbdjc] {
    min-width: 0;
}

@media (min-width: 992px) {
    .kmt-kh-o + .kmt-kh-o[b-nkha9bbdjc] {
        border-left: 1px solid var(--bs-border-color);
    }
}
/* /Components/Pages/TongQuan/KhungTaoNhiemVu.razor.rz.scp.css */
/* ═══ ⭐ ĐỢT 1992 (việc o1287) — KHUNG «TẠO NHIỆM VỤ» TRONG HỘP 📋 NHIỆM VỤ ═══

   Hai luật dưới đây DỜI từ `KhoiMucTieuTrangChu.razor.css` (`.kmt-o-vai` đợt 1970k · `.kmt-o-ten-viec`
   đợt 1944) cùng lượt dời khung nhập sang ô ③; bên ấy đã gỡ. Sổ đo cũ (bẫy `width: 1%` của
   `.input-group`, phép đo bề rộng chữ bằng `canvas.measureText`) vẫn nằm nguyên trong tệp ấy — đọc ở đó.

   🔑 BA Ô "BA VAI" BẰNG NHAU đi bằng MỘT lớp bề ngang duy nhất với `flex: 1 1 0` — nền basis **0** mới
     cho ba ô chia đều phần trống. `flex: 1 1 auto` là bề ngang chạy theo NỘI DUNG (tên người dài ngắn
     khác nhau) ⇒ ba ô lệch nhau mà ⛔ không câu báo nào. Hàng rào `KhoiGiaoViecTrangChuTests` canh cả hai
     chiều: đủ ba `.ktn-o-vai`, và ⛔ không ô nào mang lớp bề ngang khác.
   ⚠ `min-width: 12rem` để ô ⛔ không bị bóp còn cái mũi tên khi ba ô tranh một hàng hẹp; hàng là
     `flex-wrap` nên ở khổ điện thoại ba ô tự xếp xuống — đó là đường lui duy nhất (luật vùng chạm đợt 1518).
   ⚠ `max-width: 100%` để một mảnh dài ⛔ không đẩy tràn ngang cả hộp thoại. */
.ktn-o-vai[b-ik1hzzyhcu] {
    flex: 1 1 0;
    min-width: 12rem;
    max-width: 100%;
}

/* Ô tên nhiệm vụ — thứ gõ nhiều nhất trên hàng 1 — giữ một sàn bề ngang để ⛔ không bị ô hạn và nút
   đè xuống còn vài chục px. `input-group` tự xuống dòng khi hẹp, ⛔ đừng ép một dòng cứng. */
.ktn-o-ten[b-ik1hzzyhcu] {
    min-width: 16rem;
}

/* ═══ ⭐ ĐỢT 2169 (việc o1615) — KHỔ ĐIỆN THOẠI: Ô TÊN TRỌN DÒNG, NHÃN «HẠN» ĐI CÙNG Ô NGÀY ═══

   Anh Tiến 07/09/2026: *"ô điền với giao diện điện thoại cũng rất xấu"*. Đo sống 375px trên bản đang
   chạy: dòng `input-group` đầu = [ô tên 16rem][nhãn «Hạn»][ngày 150px][nút Tạo] — ô tên + nhãn «Hạn»
   vừa đủ dòng 1, ngày + nút rớt xuống dòng 2 ⇒ nhãn «Hạn» đứng MỒ CÔI cuối dòng 1, tách khỏi ô ngày
   của chính nó.
   🔑 CÁCH SỬA: ở khổ điện thoại cho ô tên chiếm TRỌN dòng 1 (`flex-basis: 100%`) để dòng 2 là
     [Hạn][ngày][Tạo] liền nhau — nhãn về đúng cạnh ô nó chú thích. ⛔ Không ép `flex-nowrap` (luật vùng
     chạm đợt 1518); ⛔ không đụng `ONgay` / `OGoTim` / `DayNutMauTenNhiemVu` (dùng chung nhiều màn hình,
     có hàng rào riêng).
   ⚠ Mốc 576px = `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx` — cùng mốc với thẻ dòng của hộp
     📋 Nhiệm vụ (o1613); hàng rào `TrangChuDanhSachHaiCotTests` đối chiếu cả hai tệp.
   ⚠ Góc trong của ô tên và của nhãn «Hạn» vẫn vuông: luật `.input-group > :not(:first-child)…` của
     Bootstrap có độ ưu tiên (0,7,0), CSS scoped ⛔ không thắng được mà dự án ⛔ không dùng `!important` —
     chấp nhận, đó chính là dáng `input-group` xuống dòng của Bootstrap. */
@media (max-width: 576px) {

    .ktn-o-ten[b-ik1hzzyhcu] {
        flex: 1 1 100%;
        min-width: 0;
        margin-bottom: .25rem;
    }

    /* ⭐ ĐỢT 2176 (việc o1620) — NÚT «TẠO» / «GIAO» NỞ HẾT PHẦN CÒN LẠI CỦA DÒNG 2.
       Đo sống 375px sau o1615: dòng 2 = [Hạn 43px][ngày 150px][Tạo 65px] = 258px trên 326px ⇒ bỏ trống
       68px bên phải, mà nút Tạo là HÀNH ĐỘNG CHÍNH của cả khung lại bé nhất hàng. Cho nó `flex: 1 1 auto`
       thì nó ăn nốt phần trống (đo: 65 → 133px) — ngón cái bấm dễ hơn, mắt thấy ngay nút chính.
       ⛔ Chỉ ở khổ điện thoại: máy tính hàng 1 còn rộng, nút to ra là đẩy ô tên hẹp lại. */
    .ktn-khung .input-group > .btn-primary[b-ik1hzzyhcu] {
        flex: 1 1 auto;
    }
}

/* ═══ ✏ ĐỢT 2202 (việc o1646) → ⭐ ĐỢT 2231 (việc o1695) — DÃY NÚT ĐIỀN NHANH NAY Ở DÒNG TIÊU ĐỀ ═══

   🪦 CÂU CŨ CỦA ĐỢT 2202 — *"DÃY NÚT ĐIỀN NHANH ĐỨNG CUỐI HÀNG VAI"* — ĐÃ BỊ ĐỢT 2231 ĐẢO. Anh Tiến
   09/09/2026: *"đưa bút chì lên cạnh nhiệm vụ"* ⇒ dãy nút nay đứng cạnh nhãn tiêu đề «Nhiệm vụ» ở dòng
   đầu khung, ⛔ không còn nằm trong hàng ba vai. Giữ nguyên văn câu cũ để thấy vì sao luật này có mặt.

   ✅ LỜI DẶN CỦA ĐỢT 2202 THÌ CÒN NGUYÊN HIỆU LỰC: dãy ⛔ KHÔNG ĐƯỢC CHIẾM TRỌN MỘT DÒNG RIÊNG — người
   CHƯA tạo mẫu nào thì nó chỉ còn nút ✏️ ⇒ một dòng gần như trống («mảnh mồ côi», luật ① đợt 2176, mà
   lần ấy mồ côi ở CẢ khổ máy tính). Dùng chung dòng với nhãn tiêu đề chính là cách giữ lời dặn ấy.

   🔑 `flex: 0 0 auto` — dãy ⛔ KHÔNG chia phần với thứ đứng cạnh: ở hàng ba vai (nhà cũ) luật anh chốt
   04/09 là *"3 ô phải có kích thước bằng nhau trên hàng đó"*; ở dòng tiêu đề (nhà mới) dãy giãn ra là
   đẩy chữ «Nhiệm vụ» lệch đi. Một dòng đúng cho cả hai chỗ.
   ⚠ `align-self: center` GIỮ LẠI: dòng tiêu đề đã `align-items: center` nên nay nó là dòng thừa vô hại,
   nhưng nó là ĐƯỜNG LUI cho ngày ai đó dời dãy nút vào một hàng `align-items: start` (đúng bệnh đợt
   2202: nút ✏️ dính mép trên trong khi ô cạnh nó cao gấp đôi). */
.ktn-day-mau[b-ik1hzzyhcu] {
    flex: 0 0 auto;
    align-self: center;
}
/* /Components/Pages/TongQuan/ONhiemVu.razor.rz.scp.css */
/* ═══ ĐỢT 1299 (việc o490) — Ô ③ "NHIỆM VỤ" Ở DÃY THẺ TRANG CHỦ ═══

   Chép đúng khuôn `.ovcd-o` của ô "Phê duyệt / Nghiệm thu" (đợt 873): ô là một NÚT nhưng phải trông y
   hệt các thẻ số còn lại của dãy — nút trần mang sẵn viền, nền và canh giữa chữ của trình duyệt.

   ⭐ Màu, khối 3D và hiệu ứng nằm ở lớp dùng chung `.tt3d` / `.tt3d-duan` / `.tt3d-han` trong `app.css`
   (đợt 931). Ô này ĐỔI bộ màu theo tin xấu/tin lành — xem `ONhiemVu.razor.cs::LopMau`.
   ⛔ Đừng đặt lại màu nền / hover / focus ở đây: luật scoped biên dịch ra (0,2,0) sẽ thắng luật của
     `app.css` trong im lặng, và phiên sau sẽ sửa vào bản không có tác dụng.

   ⓘ Tên lớp đổi từ `.onvda-o` (đời ô "Nhiệm vụ/Dự án") sang `.onv-o` cùng nhịp với lượt tách ô ấy làm
     hai — tên lớp còn nhắc "DuAn" trong khi ô không còn phần dự án là chỉ đường cho phiên sau đi lạc. */
.onv-o[b-pjh99khczo] {
    appearance: none;
    font: inherit;
    cursor: pointer;
}

/* ⭐ ĐỢT 1486 (việc o691) — dải nút đổi giai đoạn trong hộp 📋 Nhiệm vụ.
   Hai nút của giai đoạn "Mới giao" đứng cùng một ô bảng nên phải có khe.
   ⓘ Vế thứ hai của câu này (*"cột thì đã text-nowrap"*) đã LẠC HẬU từ đợt 2058 — xem khối cuối tệp. */
.onv-nut-gd + .onv-nut-gd[b-pjh99khczo] {
    margin-left: .35rem;
}

/* ═══ ⭐ ĐỢT 2058 (việc o1361) — DANH SÁCH NHIỆM VỤ XẾP THÀNH HAI CỘT ═══

   Anh Tiến 05/09/2026: *"danh sách công việc trong ô giao việc/nhiệm vụ chia làm 2 cột"*. Đợt 2019 đã
   nới hộp này lên cỡ `Rong` (~1140px) cho bằng hộp ô ②, nên một cột bỏ trống nửa bề ngang.

   🔑 LƯỚI ĐẶT Ở THẺ BỌC, ⛔ KHÔNG xếp `tbody` thành lưới: hai con của thẻ bọc là HAI BẢNG hoàn chỉnh
     (tệp `.razor` cắt danh sách làm đôi) ⇒ bên trong mỗi bảng mọi thứ giữ nguyên — cột thẳng hàng, và
     dòng xác nhận mang `colspan` vẫn trải ngang đúng bảng của nó.
   ⚠ `minmax(0, 1fr)` chứ ⛔ KHÔNG `1fr` trần: `1fr` là `minmax(auto, 1fr)`, mà `auto` ⛔ không cho ô
     lưới co nhỏ hơn nội dung ⇒ một tên nhiệm vụ dài đẩy TRÀN NGANG cả hộp thoại, im lặng. */
.onv-hai-cot[b-pjh99khczo] {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .75rem;
}

/* 🔴 BẢNG ĐƠN ĐỘC TRẢI HẾT HAI CỘT — danh sách một dòng thì tệp `.razor` cố ý ⛔ không dựng bảng thứ
   hai; thiếu luật này là dòng duy nhất bị bóp còn nửa hộp mà nửa kia trống trơn. */
.onv-hai-cot > :only-child[b-pjh99khczo] {
    grid-column: 1 / -1;
}

/* Tên nhiệm vụ hay mang mã thiết bị / đường dẫn dài ⛔ không có dấu cách, mà cột nay chỉ còn một nửa bề
   ngang ⇒ phải cho ngắt trong từ (cùng lẽ với `.kmt-dong-giao` ở ô ②). */
.onv-hai-cot .ten-ban-ghi[b-pjh99khczo] {
    overflow-wrap: anywhere;
}

/* ═══ ⭐ ĐỢT 2187 (việc o1588) — CỘT TÊN LÀ CỘT RỘNG NHẤT, BỐN CỘT KIA CO VỀ ĐÚNG NỘI DUNG ═══

   Anh Tiến 07/09/2026 (kèm ảnh chụp hộp này): *"cột tên phải là cột to nhất, tiến độ và ưu tiên không
   cần hiện đủ … nút hoàn tất chỉ cần hiện icon. Tóm lại … bản thiết kế này không dùng được"*.

   📏 ĐO TRƯỚC KHI SỬA (bản đang chạy, 1400×900, 7 dòng, tài khoản `tienvm`): cột tên **21,3px** ở CẢ 7
     dòng — tên dài 21–46 ký tự ⇒ **1,4–1,5 ký tự mỗi dòng**, tức chữ rơi dọc từng chữ cái một. Bốn cột
     phụ ăn **471px / 492,3px**: ưu tiên 85,6 · trạng thái 79,6–111,6 · hạn 88,1 · dải nút 217,7.

   🔑 KHUÔN `width:100%` + `width:1%` LÀ CÁCH DUY NHẤT NÓI ĐƯỢC "CHIA PHẦN DƯ" TRONG BẢNG `auto`:
     `width` của ô bảng chỉ là một GỢI Ý TỈ LỆ. Ô nào khai `1%` thì trình duyệt ép về **bề ngang tối
     thiểu của nội dung**, còn ô khai `100%` **hứng trọn phần dư**. ⛔ Đừng gõ px cứng cho cột tên:
     bảng này đứng trong lưới hai cột của một hộp co giãn, số px đúng ở 1400 là sai ở 1024.
   🔴 Bốn cột phụ phải kèm `white-space: nowrap` thì `1%` mới có nghĩa — thiếu nó, "bề ngang tối thiểu"
     của một ô chữ tụt xuống bằng TỪ DÀI NHẤT và cột lại tự vỡ dòng. ⛔ Ngoại lệ CỐ Ý là `.onv-o-nut`:
     hàng rào `TrangChuDanhSachHaiCotTests` đòi ô ấy giữ `white-space: normal` (đợt 2058 — dải nút phải
     xuống dòng được), nên ở đó chỉ đặt `1%`. Nút nay chỉ còn icon nên nó cũng ⛔ không cần nowrap nữa.
   ⚠ Cột tên GIỮ `overflow-wrap: anywhere` ngay trên: nhờ nó bề ngang tối thiểu của cột tên bằng MỘT ký
     tự, tức cột tên ⛔ không bao giờ đẩy bảng tràn ngang — chính là thứ cho phép nó nhận `100%` mà an
     toàn. Bỏ `anywhere` là mã thiết bị dài đẩy bảng tràn khỏi hộp, im lặng. */
.onv-o-ten[b-pjh99khczo] {
    width: 100%;
}

.onv-o-hh[b-pjh99khczo],
.onv-o-han[b-pjh99khczo] {
    width: 1%;
    white-space: nowrap;
}

/* Nhãn "Hạn" đứng trước ngày giờ — chữ lấy từ `LuatBangGonNhiemVu.NhanCotHan`, ⛔ đừng gõ vào CSS.
   Nhạt và nhỏ hơn con số: nhãn là thứ đọc MỘT LẦN cho biết cột này là gì, còn con số mới là thứ mắt
   quét xuống. ⛔ Đừng `display: none` ở bất kỳ khổ nào — giấu nó đi là trả lại đúng câu hỏi
   *"ngày giờ đó là gì anh không biết"*. */
/* ═══ ⭐ ĐỢT 2207 (việc o1654) — NHÃN ĐỨNG **CÙNG DÒNG** VỚI NGÀY GIỜ, KHÔNG CÒN XUỐNG DÒNG RIÊNG ═══

   🔴 ĐỢT 2187 cho nhãn `display: block` để tiết kiệm bề ngang (đo bấy giờ: nhãn cùng dòng đẩy cột hạn
     từ 88,1px lên 160,5px). Luật ấy CHẾT theo bố cục mới: ô nay có **HAI mốc** (Giao rồi Hạn), nhãn
     block là mỗi mốc ăn hai dòng ⇒ **bốn dòng** cho một ô — cao hơn cả dòng bảng, và hai con số ngày
     giờ ⛔ không còn nằm cạnh nhãn của chính nó nữa.
   🔑 Bù bề ngang bằng chỗ khác chứ ⛔ không bằng cách giấu nhãn: đợt này gộp hai cột huy hiệu làm MỘT
     (xếp dọc) nên bảng đã lấy lại ~85px — đủ trả cho phần nhãn đứng cùng dòng.
   ⚠ `inline-block` + `min-width` chứ ⛔ không `inline` trần: hai nhãn dài ngắn khác nhau (*Giao* 4 chữ,
     *Hạn* 3 chữ) thì hai con số ngày giờ so le nhau, mà thẳng cột chính là thứ cho người ta so hai mốc
     bằng mắt. ⛔ Đừng gõ px: `em` co theo cỡ chữ đã thu nhỏ của chính nhãn. */
.onv-nhan-cot[b-pjh99khczo] {
    color: var(--bs-secondary-color);
    font-size: .8em;
    display: inline-block;
    min-width: 2.9em;
    line-height: 1.1;
}

/* Ô ngày giờ có hai dòng — dòng dưới (Hạn) sát dòng trên, hai mốc phải đọc ra như MỘT khối chứ
   ⛔ không phải hai ô rời. */
.onv-o-han > div + div[b-pjh99khczo] {
    margin-top: .1rem;
}

/* Hai huy hiệu xếp dọc trong cùng một ô — khe vừa đủ để mắt tách được hai mốc.
   ⛔ Đừng cho khe rộng bằng khe giữa hai DÒNG bảng: rộng bằng nhau thì huy hiệu thứ hai đọc ra như
     của dòng dưới. */
.onv-o-hh > div + div[b-pjh99khczo] {
    margin-top: .15rem;
}

/* Cột nút — chỗ thay cho lớp `text-nowrap` đã gỡ khỏi `.razor`: DẢI nút được xuống dòng, nhưng TỪNG
   nút thì ⛔ không được ngắt giữa chữ (một nút vỡ làm hai dòng đọc ra như hai nút). */
.onv-o-nut[b-pjh99khczo] {
    white-space: normal;
    width: 1%;
}

.onv-o-nut .btn[b-pjh99khczo] {
    white-space: nowrap;
    /* ⭐ ĐỢT 2187 (việc o1588) — nút nay chỉ còn MỘT biểu tượng nên đệm mặc định của `btn-sm`
       (.5rem) làm nó hẹp gần bằng chính con chữ. Nới đệm ngang cho nút có chỗ đứng.
       🔴 ⛔ TUYỆT ĐỐI ĐỪNG dùng `min-width` ở đây: `app.css` cấp vùng chạm 44×44px cho mọi `.btn`
         trong `@media (pointer: coarse)` với độ đặc hiệu (0,1,0), còn luật cách ly này biên dịch ra
         (0,3,0) ⇒ một `min-width` nhỏ ở đây THẮNG và bóp nút xuống dưới ngưỡng ngón tay — hỏng
         LẶNG LẼ, đúng ca `VungChamNgonTayTests` canh. `padding` thì ⛔ không đụng tới ngưỡng ấy. */
    padding-left: .6rem;
    padding-right: .6rem;
}

/* ═══ ⭐ ĐỢT 2271 (việc o1783) — KHE GIỮA HAI NÚT LÀ `gap`, ⛔ KHÔNG CÒN LÀ `margin-left` ═══

   Anh Tiến 10/09/2026 kèm ảnh hai nút xếp dọc: *"nút trên và nút dưới vẫn bị lệch"* — «vẫn», tức đợt
   2261 (việc o1759) chưa chạm tới gốc bệnh.
   📏 ĐO SỐNG trước lượt vá (app dev 5270, 12 nút thật): mọi nút BẰNG NHAU tuyệt đối **37,3 × 31px**
     (hộp icon cố định của đợt 2261 chạy đúng), mà mép trái nút thứ HAI của một ô ở **46,6px** trong khi
     nút thứ nhất ở **41,0px** ⇒ lệch **5,6px**, đúng bằng `.35rem` — chính là con số khe ngang cũ.
   🔑 GỐC BỆNH: `margin-left` chỉ có nghĩa «khe» khi hai nút NẰM CÙNG DÒNG. Từ đợt 2058 ô nút chỉ rộng
     bằng một nửa hộp nên dải nút xuống dòng THẬT ⇒ lề trái ấy hoá **thụt đầu dòng**. `gap` thì trình
     duyệt chỉ đặt GIỮA các mảnh, ⛔ không bao giờ thành lề ngoài rìa — một con số lo cả khe ngang lẫn
     khe dọc, và ⛔ không có ca nào nó cư xử khác đi.
   🔴 Luật đặt trên THẺ BỌC `.onv-dai-nut`, ⛔ KHÔNG trên `<td class="onv-o-nut">`: ô bảng phải giữ
     `display: table-cell` (đặt flex thẳng lên nó là vỡ phép chia cột), và ba hàng rào
     `BangGonNhiemVuTrangChuTests` · `TrangChuDanhSachHaiCotTests` đang đòi ô ấy giữ nguyên
     `width: 1%` + `white-space: normal`.
   🪦 Luật `.onv-nut-gd + .onv-nut-gd` ở trên vẫn TRÚNG 0 PHẦN TỬ (đợt 1511 bọc mỗi nút vào một `span`)
     — ⛔ đừng xoá: `do-dot-bien-thao-tac-nhanh-giai-doan.mjs` neo vào chuỗi `class="… onv-nut-gd"` trong
     `.razor`, và luật ấy sống lại ngay ngày ai bỏ thẻ bọc. */
.onv-dai-nut[b-pjh99khczo] {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
}

/* ═══ KHỔ HẸP: THU VỀ MỘT CỘT ═══
   🔴 Mốc là BỀ NGANG, ⛔ KHÔNG phải `pointer: coarse` — câu hỏi ở đây là *"hai cột có đủ chỗ đứng cạnh
     nhau không"*, mà cửa sổ máy tính thu nhỏ (vẫn dùng chuột) thì cũng ⛔ không đủ chỗ.
   ⚠ Con số 820 là sổ khai `LuatHaiCotDanhSachTrangChu.KhoHepToiDaPx`; hàng rào
     `TrangChuDanhSachHaiCotTests` đối chiếu nó với CẢ HAI tệp CSS — ⛔ đừng sửa một mình ở đây.
   ⚠ MỘT điều kiện thôi, ⛔ đừng ghép `and` (bài học đợt 1481 với tệp `.razor.css`). */
@media (max-width: 820px) {

    .onv-hai-cot[b-pjh99khczo] {
        grid-template-columns: 1fr;
    }
}

/* ═══ ⭐ ĐỢT 2169 (việc o1613) — KHỔ ĐIỆN THOẠI: MỖI DÒNG BẢNG THÀNH MỘT THẺ ═══

   Anh Tiến gửi ảnh 07/09/2026 14:21 (điện thoại ≈380px): tên nhiệm vụ bị ép thành MỘT CỘT CHỮ DỌC, dải
   nút tràn phải. Đo sống 375px trên bản đang chạy: ô tên còn 21px, bảng 326px trong hộp 358px mà cột
   nút vẫn tràn.
   🔑 VÌ SAO: khối 820 ở trên chỉ thu LƯỚI về một cột, còn bên trong vẫn là BẢNG 5 cột (tên · ưu tiên ·
     trạng thái · hạn · dải nút). `overflow-wrap: anywhere` của ô tên làm bề ngang TỐI THIỂU của cột ấy
     chỉ còn MỘT ký tự ⇒ thuật toán bảng nhường hết chỗ cho ba huy hiệu + ngày + cột nút (từng nút
     `nowrap`), rồi cột nút vẫn không đủ chỗ.
   🔑 CÁCH SỬA: ⛔ không nắn bảng cho khít (khít với 375px là vỡ với 360px) — xếp mỗi DÒNG thành THẺ:
     tên trọn dòng · huy hiệu + hạn nối ngang một dòng · dải nút một dòng. Dòng xác nhận `colspan` vẫn
     là một khối trọn bề ngang. `anywhere` ở ô tên GIỮ NGUYÊN: không còn thuật toán bảng nào đọc bề
     ngang tối thiểu nữa nên nó chỉ còn làm đúng việc ngắt mã thiết bị dài.
   ⚠ Mốc 576px = `LuatHaiCotDanhSachTrangChu.KhoDienThoaiToiDaPx`; hàng rào `TrangChuDanhSachHaiCotTests`
     đối chiếu — ⛔ đừng sửa một mình ở đây. TÁCH RIÊNG khỏi khối 820: hàng rào ấy neo mốc 820 cho luật
     lưới, và ở 577–820px (máy tính bảng dọc) bảng 5 cột vẫn đứng được.
   ⚠ MỘT điều kiện thôi, ⛔ đừng ghép `and` (bài học đợt 1481 với tệp `.razor.css`). */
@media (max-width: 576px) {

    .onv-hai-cot table[b-pjh99khczo],
    .onv-hai-cot tbody[b-pjh99khczo],
    .onv-hai-cot tr[b-pjh99khczo],
    .onv-hai-cot td[b-pjh99khczo] {
        display: block;
        width: 100%;
    }

    .onv-hai-cot tr[b-pjh99khczo] {
        padding: .4rem 0;
        border-bottom: 1px solid var(--bs-border-color);
    }

    .onv-hai-cot tr > td[b-pjh99khczo] {
        border: 0;
        padding: .1rem 0;
        /* Tô hover của `table-hover` vẽ theo từng ô — ô đã thành khối rời thì thành mảng loang lổ;
           điện thoại ⛔ không có hover nên bỏ hẳn, thẻ chỉ còn vạch ngăn dưới. */
        box-shadow: none;
        background-color: transparent;
    }

    /* Ba ô giữa (ưu tiên · trạng thái · hạn) đứng cạnh nhau — thẻ vẫn đọc lướt được như một dòng bảng. */
    .onv-hai-cot td:not(:first-child):not(.onv-o-nut)[b-pjh99khczo] {
        display: inline-block;
        width: auto;
        margin-right: .6rem;
    }

    /* ⭐ ĐỢT 2207 (việc o1654) — Ở KHỔ NÀY HAI HUY HIỆU NẰM NGANG TRỞ LẠI.
       🔑 Xếp DỌC là phép cắt BỀ NGANG, mà ở khổ thẻ thì bề ngang ⛔ không còn khan: mỗi dòng đã trải
         trọn màn hình. Thứ khan ở đây là CHIỀU CAO — đo sống 375×812: hai huy hiệu chồng nhau đẩy mỗi
         thẻ dài thêm một dòng, nhân với số nhiệm vụ là người dùng phải cuộn thêm cả màn.
       ⛔ Đặt trong khối 576 SẴN CÓ, ⛔ đừng mở mốc mới: `TrangChuDanhSachHaiCotTests` đòi cả tệp chỉ
         được mang đúng hai con số 576 và 820 (lấy từ `LuatHaiCotDanhSachTrangChu`). */
    .onv-hai-cot .onv-o-hh > div[b-pjh99khczo] {
        display: inline-block;
    }

    .onv-hai-cot .onv-o-hh > div + div[b-pjh99khczo] {
        margin-top: 0;
        margin-left: .35rem;
    }

    /* ⭐ ĐỢT 2176 (việc o1620) — HAI LỐI RA Ở CHÂN HỘP NỞ ĐỀU, ⛔ KHÔNG XẾP SO LE.
       Đo sống 375px: hai thẻ `<a>` «🗂 Quản lý công việc» · «📊 Tổng quan nhiệm vụ» rộng 175/198px
       ⛔ không cùng vừa một dòng 326px, `modal-footer` (flex-end) đẩy chúng thành hai dòng LỆCH PHẢI, mỗi
       dòng chừa một khoảng trống bên trái khác nhau. Cho mỗi thẻ `flex: 1 1 auto` thì dòng nào cũng kín
       bề ngang — hai nút bằng nhau, thẳng hàng, vùng bấm rộng hơn. Bộ chọn bám `a.btn-outline-secondary`
       vì `ONhiemVu.razor` chỉ có ĐÚNG HAI thẻ neo mang lớp ấy (đều ở `<ChanHop>`); nút giai đoạn trong
       bảng là `<button>` nên ⛔ không dính. */
    a.btn-outline-secondary[b-pjh99khczo] {
        flex: 1 1 auto;
    }
}

/* ═══ ⭐ ĐỢT 2198 (việc o1640) — TÊN NHIỆM VỤ TRONG HỘP THOẠI PHẢI BẤM ĐƯỢC BẰNG NGÓN TAY ═══
   Anh Tiến 08/09/2026: *"tiếp tục hoàn thiện và tinh chỉnh các hộp hội thoại tại trang chủ"*.
   📏 Đo sống 375×812 trên bản chạy thật (08/09): 6 thẻ tên trong hộp «Nhiệm vụ của tôi» chỉ cao
     **17px** — đúng bằng một dòng chữ — trong khi ngưỡng WCAG 2.5.5 / Apple HIG là 44px. Đây là
     đường bấm CHÍNH của hộp: mở một hồ sơ ra xem.
   🔑 Khuôn lấy nguyên của họ ② đợt 1989 (thẻ `a` đứng một mình): `inline-flex` + `min-height`, ⛔ không
     `padding` — đệm dọc đẩy cả dòng huy hiệu bên cạnh lệch nhịp, còn `min-height` chỉ nong đúng thẻ.
   ⛔ CHỈ trong `@media (pointer: coarse)`: 433 tài khoản phần lớn ngồi máy tính, nong 44px cho chuột là
     đổi bố cục danh sách của cả app để giải bài toán của điện thoại (rủi ro lan truyền, quy tắc #18).
   ⛔ Đặt ở đây chứ ⛔ KHÔNG ở `app.css`: `.ten-ban-ghi` là lớp DÙNG CHUNG (đo 08/09: 6 phân hệ dùng),
     nới nó ở tệp chung là mọi bảng của app dài thêm một nửa — thứ ⛔ không ai đo và ⛔ không ai yêu cầu. */
@media (pointer: coarse) {
    a.ten-ban-ghi[b-pjh99khczo] {
        display: inline-flex;
        align-items: center;
        min-height: var(--cham-toi-thieu);
    }
}

/* 📨 ĐỢT 2202b (việc o1659) — KHOẢNG THỞ TRÊN KHỐI GIAO VIỆC.
   Khối này đứng ngay dưới khung «Tạo nhiệm vụ», hai khung khai dính nhau thì mắt ⛔ không tách được
   *"cái nào là việc của mình"* với *"cái nào là việc giao xuống"*.
   ⛔ Chừa khoảng chứ ⛔ ĐỪNG kẻ vạch: mỗi khung đã tự có tiêu đề đậm, thêm vạch là ba tầng ranh giới
   cho hai khối — đúng thứ đợt 1976 đã gỡ ở ô ② (vạch `.kmt-khoi-giao`). */
.kgv-tach-tren[b-pjh99khczo] {
    margin-top: 1rem;
}

/* ═══ ⭐ ĐỢT 2231 (việc o1696) — THÂN HỘP 📋 NHIỆM VỤ LUÔN CAO KỊCH KHUNG, ĐỂ BẢNG GỢI Ý CÓ CHỖ SỔ ═══

   Anh Tiến 09/09/2026, nguyên văn: *"hộp thoại nhiệm vụ cho to ra, danh sách sổ xuống không có không
   gian để xem"*.

   🔑 GỐC BỆNH ĐỌC ĐƯỢC TỪ CHÍNH `bootstrap.min.css` (đọc 09/09/2026, ba luật đi liền một bộ):
     · `.modal-dialog-scrollable { height: calc(100% - var(--bs-modal-margin) * 2) }` — KHUNG NGOÀI cao
       kịch màn hình;
     · `.modal-dialog-scrollable .modal-content { max-height: 100%; overflow: hidden }` — nhưng THẺ HỘP
       chỉ có TRẦN, ⛔ không có chiều cao ⇒ nó **CO THEO NỘI DUNG**;
     · `.modal-dialog-scrollable .modal-body { overflow-y: auto }` — thân hộp là một MÉP CẮT.
   ⇒ Ngày người dùng còn ít nhiệm vụ (hoặc màn hình thấp), thẻ hộp lùn lại ⇒ thân hộp lùn theo ⇒ bảng
     gợi ý của `OGoTim` (`position: absolute`, `.ogt-cuon` cao tới 220px) **rơi ra ngoài mép cắt** và
     phải cuộn mới thấy. Đúng câu anh nói. Hai mép cắt cùng cắn: `overflow: hidden` của thẻ hộp và
     `overflow-y: auto` của thân hộp.
   ✅ CÁCH CHỮA: cho thẻ hộp `height: 100%` — thẻ hộp nay cao **kịch khung ngoài** bất kể nội dung dài
     ngắn, nên thân hộp có bề cao NHÌN THẤY lớn nhất có thể ⇒ bảng gợi ý sổ xuống trong lòng nó.
     Cùng lẽ với đợt 1266 (`.hop-cao-co-dinh` ở `app.css`), nhưng ⛔ KHÔNG dùng lại lớp ấy: nó đặt
     `min(72vh, 700px)`, tức trên màn cao là **THU NHỎ** hộp — ngược đúng câu *"cho to ra"*.

   ⚠ `::deep` + THẺ BỌC `.onv-boc-hop` LÀ BẮT BUỘC, ⛔ không có đường nào khác từ tệp này: `.modal-content`
     do component `HopThoai` sinh ra nên nó mang mã cách ly của KHUNG, ⛔ không mang mã của trang gọi ⇒
     một luật `.modal-content { … }` viết trần ở đây biên dịch ra `.modal-content[b-…]` và **⛔ KHÔNG BAO
     GIỜ KHỚP** (build xanh, test xanh, màn hình ⛔ không đổi một pixel). Mà `::deep` cần một thẻ TỔ TIÊN
     do CHÍNH tệp này dựng để bám — trước đợt 2231 thẻ `<HopThoai>` ⛔ không nằm trong thẻ bọc nào, đó
     mới là lý do chú thích đợt 1266 ở `app.css` viết *"`::deep` không có gốc để bám"*. Nay đã có gốc.
   ⛔ ĐỪNG bỏ thẻ bọc `.onv-boc-hop` trong `ONhiemVu.razor`, và ⛔ đừng đặt lớp có chữ `card` lên nó —
     `HopThoaiKhungChungTests` bắt ĐỎ nút đóng nằm trong thẻ khớp `\bcard\b`.
   ⛔ Luật CỐ Ý bám vào thẻ bọc RIÊNG của hộp này, ⛔ không bám `.modal-content` toàn app: đo 03/09/2026
     có **64 tệp `.razor`** dựng `<HopThoai>`; ép cả 64 hộp cao kịch màn hình là đổi bố cục của mọi hộp
     xác nhận một câu (rủi ro lan truyền, quy tắc #18).
   ⚠ Thẻ bọc chỉ chứa một khối `position: fixed` (nền phủ của `NenHopThoai`) nên nó là một thẻ cao 0px
     trong dòng chảy — ⛔ đừng gán nền/đệm/lề cho nó, và ⛔ đừng gán `transform`/`filter`/`will-change`
     (ba thứ ấy dựng một khung quy chiếu mới ⇒ nền phủ thôi neo vào khung nhìn, hỏng LẶNG LẼ).
   🔴 CÒN MỘT NỬA ⛔ CHƯA LÀM ĐƯỢC Ở ĐÂY — BỀ NGANG: hộp đang ở nấc rộng nhất của khung chung
     (`CoHopThoai.Rong` = `.modal-xl` = `--bs-modal-width: 1140px`). Muốn rộng hơn thì phải THÊM một nấc
     vào enum `CoHopThoai` (`HopThoai.razor.cs`) + luật bề ngang cho nấc ấy, và áp cho CẢ ô ② để hai hộp
     ⛔ không lệch nhau (điều đợt 2019 đã dặn: *"đi bằng ENUM cỡ chung, ⛔ tuyệt đối đừng gõ số px"*).
     ⛔ ĐỪNG chữa bằng một dòng `--bs-modal-width` gõ tay ở đây — đó đúng là thứ đợt 2019 cấm. */
.onv-boc-hop[b-pjh99khczo]  .modal-content {
    height: 100%;
}

/* ═══ ⭐ ĐỢT 2261 (việc o1760) — CHỈ DANH SÁCH CUỘN, THÂN HỘP ĐỨNG YÊN ═══

   Anh Tiến 10/09/2026 (kèm ảnh hộp): *"khung ngoài cùng để cố định như vậy chỗ danh sách nhiệm vụ thì
   cho cuộn chỉ trong danh sách đó, không cuộn cả khung hội thoại"*.

   🔴 GỐC BỆNH: `.modal-body` của Bootstrap mang `overflow-y: auto` ⇒ mép cuộn nằm ở THÂN HỘP, tức
     cuộn xuống xem dòng thứ mười là khung điền «Tạo nhiệm vụ» trôi hẳn khỏi màn hình. Nay thân hộp
     thành một CỘT không cuộn, phần cao còn lại giao trọn cho `.onv-vung-cuon`.
   ⚠ `min-height: 0` là vế SỐNG CÒN: flex item mặc định `min-height: auto` = không co nhỏ hơn nội
     dung ⇒ thiếu dòng ấy thì thẻ nở theo cả danh sách, thân hộp lại tràn và mép cuộn rơi về đúng chỗ
     cũ — bản vá trông đủ mà ⛔ không đổi một pixel (cùng họ bài học `min-width: 0` đợt 2198).
   🔑 Luật đi kèm `height: 100%` của `.modal-content` (đợt 2231) chứ ⛔ KHÔNG thay nó: thẻ hộp phải cao
     kịch khung thì phần cao còn lại mới có nghĩa; gỡ luật kia là hộp co theo nội dung và vùng cuộn
     hoá vô dụng.
   ⛔ `::deep` + thẻ bọc `.onv-boc-hop` là bắt buộc — `.modal-body` do component `HopThoai` sinh nên nó
     mang mã cách ly của KHUNG, một luật `.modal-body { … }` viết trần ở đây ⛔ KHÔNG BAO GIỜ khớp. */
.onv-boc-hop[b-pjh99khczo]  .modal-body {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Mọi khối khác của thân hộp giữ đúng bề cao tự nhiên — ⛔ đừng để chúng co lại nhường chỗ cho danh
   sách: khung điền co là các ô nhập méo đi, mà đó mới là thứ anh dặn phải ĐỨNG YÊN. */
.onv-boc-hop[b-pjh99khczo]  .modal-body > * {
    flex: 0 0 auto;
}

.onv-boc-hop[b-pjh99khczo]  .modal-body > .onv-vung-cuon {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* ═══ ⭐ ĐỢT 2261 (việc o1759) — HAI NÚT GIAI ĐOẠN: CĂN ĐỀU · NỔI KHỐI · CÓ HIỆU ỨNG ═══

   Anh Tiến 10/09/2026, kèm ảnh chụp đúng hai nút: *"2 nút này em căn cho đều, đồ họa đẹp, nút hoàn
   thành đổi icon khác, làm 2 nút này thành 3D có hiệu ứng"*.

   ① CĂN ĐỀU — bề ngang nút = đệm + bề rộng CON CHỮ, mà hai icon ⛔ không bao giờ rộng bằng nhau (đo
     10/09 trên bản chạy: nút ▶ hẹp hơn nút emoji rõ rệt ở mọi dòng). Hộp icon bề ngang CỐ ĐỊNH nên
     hai nút bằng nhau tuyệt đối.
     🔴 ⛔ TUYỆT ĐỐI ĐỪNG chuyển phép này thành `min-width` trên chính NÚT: `app.css` cấp vùng chạm
       44×44px cho mọi `.btn` trong `@media (pointer: coarse)` ở (0,1,0), còn luật cách ly ở đây ra
       (0,3,0) ⇒ một con số nhỏ đặt trên nút THẮNG và bóp nút xuống dưới ngưỡng ngón tay — hỏng LẶNG
       LẼ, đúng ca `BangGonNhiemVuTrangChuTests.Nut_icon_KHONG_duoc_dat_min_width_hay_min_height` cấm.
       Đặt trên thẻ CON thì nút chỉ RỘNG RA chứ ⛔ không bao giờ hẹp lại ⇒ ngưỡng 44px còn nguyên.

   ② NỔI KHỐI (3D) — một dải sáng dọc thân nút + một CẠNH ĐẶC dưới đáy; cạnh đọc màu từ
     `--bs-btn-active-bg` của chính nút (Bootstrap 5.3 khai nó cho từng biến thể) nên nút viền lấy
     cạnh xanh dương, nút xanh lá lấy cạnh xanh lá tối — ⛔ không gõ một mã màu nào, đổi chủ đề là nó
     tự theo. Giá trị lui `rgba(0,0,0,.28)` cho ngày ai đó dùng lớp nút ⛔ không khai biến ấy.

   ③ HIỆU ỨNG — nút NHÔ LÊN khi rê chuột, LÚN XUỐNG khi bấm (cạnh đặc co lại đúng quãng nút lún, đó
     là thứ làm cú bấm "có cảm giác"). ⛔ Đừng đổi `transform` thành `margin`/`top`: hai thứ ấy xô cả
     dòng bảng, còn `transform` ⛔ không đụng bố cục.
     ⚠ `:disabled` phải TẮT HẲN cả bóng lẫn phép dời — nút bị việc con chặn (đợt 1511) mà vẫn nhô lên
       theo chuột là mời người ta bấm vào một cửa chắc chắn từ chối.
     ⚠ `transition` CỐ Ý ⛔ không liệt kê `filter`: hai danh sách `filter` lệch họ hàm thì trình duyệt
       NHẢY PHẮT chứ ⛔ không nội suy, mà ở đây ⛔ không có gì để nội suy (bài học đợt 2239 ②). */
.onv-icon-nut[b-pjh99khczo] {
    display: inline-block;
    width: 1.15em;
    line-height: 1.25;
    text-align: center;
}

.onv-nut-gd[b-pjh99khczo] {
    background-image: linear-gradient(180deg, rgba(255, 255, 255, .22), rgba(0, 0, 0, .07));
    box-shadow: 0 2px 0 var(--bs-btn-active-bg, rgba(0, 0, 0, .28)), 0 3px 6px rgba(0, 0, 0, .18);
    transition: transform .12s ease, box-shadow .12s ease;
}

.onv-nut-gd:hover:not(:disabled)[b-pjh99khczo] {
    transform: translateY(-1px);
    box-shadow: 0 3px 0 var(--bs-btn-active-bg, rgba(0, 0, 0, .28)), 0 6px 10px rgba(0, 0, 0, .22);
}

.onv-nut-gd:active:not(:disabled)[b-pjh99khczo] {
    transform: translateY(2px);
    box-shadow: 0 0 0 var(--bs-btn-active-bg, rgba(0, 0, 0, .28)), 0 1px 2px rgba(0, 0, 0, .18);
}

.onv-nut-gd:disabled[b-pjh99khczo] {
    background-image: none;
    box-shadow: none;
    transform: none;
}

/* Máy nào người dùng đã xin BỚT chuyển động thì bỏ phép dời — khối nổi vẫn còn, chỉ mất cái nhún. */
@media (prefers-reduced-motion: reduce) {

    .onv-nut-gd[b-pjh99khczo] {
        transition: none;
    }

    .onv-nut-gd:hover:not(:disabled)[b-pjh99khczo],
    .onv-nut-gd:active:not(:disabled)[b-pjh99khczo] {
        transform: none;
    }
}

/* ═══ ⭐ ĐỢT 2261 (việc o1763) — TÊN NHIỆM VỤ TRONG HỘP «✔ Báo cáo hoàn thành» PHẢI NỔI BẬT ═══

   Anh Tiến 10/09/2026: *"nội dung công việc thì phải làm nổi bật lên"*. Cùng lượt anh bỏ đoạn hướng
   dẫn dài ở thân hộp, nên chữ DUY NHẤT còn nói *"đang báo xong việc NÀO"* là dòng phụ đề — mà khung
   chung in nó bằng `<small class="text-body-secondary">`, tức cỡ nhỏ + màu mờ.
   🔑 ⛔ ĐỪNG sửa khung chung `HopThoai`: đo 03/09 có 64 tệp `.razor` dựng nó, phụ đề mờ là ĐÚNG cho
     phần lớn (địa chỉ IP, mã hồ sơ…). Luật này bám thẻ bọc RIÊNG của hộp xác nhận.
   ⚠ `!important` ở đây là ca CHÍNH ĐÁNG: `.text-body-secondary` của Bootstrap là lớp tiện dụng mang
     sẵn `!important` nên ⛔ không độ đặc hiệu nào đè nổi (bài học đợt 2181 ⑤). */
.onv-boc-hop-xn[b-pjh99khczo]  .modal-header small {
    display: block;
    margin-top: .1rem;
    font-size: 1.05rem;
    font-weight: 600;
    line-height: 1.35;
    color: var(--bs-body-color) !important;
}

/* ═══ ⭐ ĐỢT 2261 (việc o1766) — CÂU «Đã tạo nhiệm vụ …» TRÊN THANH TIÊU ĐỀ ═══

   Anh Tiến 10/09/2026: *"tick thông báo … đưa lên hiển thị trên thanh tiêu đề Nhiệm vụ"*.
   🔑 Dáng XANH GỌN, ⛔ không phải `alert`: đây là câu THÀNH CÔNG. Một dải cảnh báo nằm trên thanh tiêu
     đề đọc ra như hộp đang có sự cố — mà thứ vừa xảy ra là một nhiệm vụ vừa ra đời.
   ⚠ Nút ✕ phải nằm CÙNG DÒNG và ⛔ không co: câu dài (tên nhiệm vụ + hạn) chiếm gần trọn bề ngang,
     để nó ngắt dòng là nút rơi xuống một dòng riêng, trông như một cái nút mồ côi. */
.onv-cau-da-tao[b-pjh99khczo] {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin-top: .2rem;
    padding: .15rem .5rem;
    border-radius: .35rem;
    background-color: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
    font-size: .9rem;
    line-height: 1.35;
}

/* ⭐ ĐỢT 2955 (việc o1088) — NÂNG .6rem → .65rem cho đúng NGƯỠNG ĐỌC ĐƯỢC của
   `CoChuONgayTests.Khong_con_co_chu_duoi_nguong_doc_duoc` (.65rem = 10,4px). Đây là nút ✕ của
   Bootstrap: `font-size` ở `.btn-close` quyết định cỡ dấu nhân, nên nâng 0,8px chỉ làm dấu ✕ to
   lên chút và vùng chạm rộng thêm — ⛔ không đổi bố cục dòng câu đã tạo (padding giữ nguyên).
   ⛔ Cố ý KHÔNG khai miễn trừ: sổ miễn trừ của bài ấy khai theo TÊN TỆP, khai cả
   ONhiemVu.razor.css là mở cửa cho mọi cỡ chữ nhỏ khác trong chính tệp này về sau. */
.onv-cau-da-tao .btn-close[b-pjh99khczo] {
    flex-shrink: 0;
    padding: .25rem;
    font-size: .65rem;
}

/* ═══ 🔴 ⭐ ĐỢT 2278 (việc o1767) — DẢI TAB NHỎ + DÒNG «HIỆN TẤT CẢ» Ở CUỐI DANH SÁCH ═══

   Anh Tiến 10/09/2026 (kèm ảnh danh sách 19 dòng lẫn cả việc Xong): *"ở danh sách nhiệm vụ, tách thành
   các tab nhỏ các mảng khác nhau và mặc định xem tất cả. ẩn các công việc đã hoàn thành, cuối danh sách
   có thêm chữ hiện tất cả …"*.

   ① DẢI TAB — cỡ NHỎ HƠN MỘT NẤC so với dải tab của một trang, đúng khuôn `HopThoaiTuyenCongNghe`: nó
     nằm trong hộp thoại nên mắt phải đọc ra ngay là tầng thứ hai, ⛔ không tranh chỗ với tiêu đề hộp.
     ⚠ `flex-wrap: wrap` là bắt buộc — bốn nhãn mang cả biểu tượng lẫn con số (*"📤 Tôi giao (12)"*),
       ở khổ điện thoại chúng ⛔ không bao giờ đủ chỗ trên một dòng; cấm ngắt là dải TRÀN NGANG khỏi hộp.
     🔴 ⛔ TUYỆT ĐỐI ĐỪNG gõ `min-height` cho `.nav-link` ở đây: `app.css` đã cấp vùng chạm
       `var(--cham-toi-thieu)` cho MỌI `.nav-link` trong `@media (pointer: coarse)`, mà một con số nhỏ
       viết ở tệp cách ly này THẮNG nó (nạp sau) và bóp nút xuống dưới ngưỡng ngón tay — đúng ca
       `BangGonNhiemVuTrangChuTests` cấm với nút icon, và `HopThoaiTrangChu2198Tests` cấm gõ số 44 trần.

   ② DÒNG «HIỆN TẤT CẢ» — nằm TRONG vùng cuộn, ngay sau dòng cuối của bảng (anh nói *"cuối danh sách"*).
     ⛔ Đừng cho nó `position: sticky` để dính đáy: dính đáy là nó che mất dòng nhiệm vụ cuối cùng, mà
       dòng ấy mới là thứ người ta đang đọc.
     ⚠ Vạch ngăn phía trên đọc màu từ `--bs-border-color` (⛔ không gõ mã màu — đổi chủ đề là nó tự theo,
       luật màu đã ghi ở `TrangChuDanhSachHaiCotTests`). */
.onv-dai-tab[b-pjh99khczo] {
    flex-wrap: wrap;
}

.onv-dai-tab .nav-link[b-pjh99khczo] {
    font-size: .85rem;
}

/* ⭐ ĐỢT 2314 (việc o1871) — tiêu đề «📋 Nhiệm vụ (N)» và dải tab chung MỘT hàng (anh Tiến 11/09/2026:
   "đưa các nút hiện tại lên cùng hàng Nhiệm vụ (13)"). `flex-wrap` để máy hẹp dải tab tự xuống dòng
   dưới tiêu đề thay vì tràn ngang. Khoảng trên giữ đúng `mt-3` cũ để khung điền phía trên ⛔ dính sát. */
.onv-hang-tieu-de[b-pjh99khczo] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem .75rem;
    margin-top: 1rem;
    margin-bottom: .35rem;
}

/* Hàng nút mảng — cùng dáng dải tab nhưng nhạt hơn một bậc để mắt đọc ra đây là bộ lọc PHỤ. */
.onv-dai-mang .nav-link:not(.active)[b-pjh99khczo] {
    background: var(--bs-tertiary-bg);
}

.onv-dong-bung[b-pjh99khczo] {
    margin-top: .35rem;
    padding-top: .35rem;
    border-top: 1px solid var(--bs-border-color);
}

.onv-dong-bung .btn-link[b-pjh99khczo] {
    font-size: .85rem;
}
/* /Components/Pages/TongQuan/OViecChoToiDuyet.razor.rz.scp.css */
/* ═══ ĐỢT 873 (việc o106) — Ô "CHỜ DUYỆT" GỘP + HỘP THOẠI DUYỆT NGAY ═══

   Ô là một NÚT nhưng phải trông y hệt ba thẻ số còn lại của dãy: nút trần mang sẵn viền, nền và canh
   giữa chữ của trình duyệt, đứng cạnh ba thẻ `card` là thành một thứ khác hẳn. Ba dòng dưới chỉ gỡ đúng
   những mặc định ấy.

   ⭐ ĐỢT 931 (việc o150) — MÀU, KHỐI 3D VÀ HIỆU ỨNG NAY Ở LỚP DÙNG CHUNG `.tt3d` trong `app.css`:
   bốn ô của dãy phải giống hệt nhau, mà bốn ô là bốn component nên CSS scoped không dùng chung được.
   ⛔ Đừng đặt lại màu nền / hover / focus ở đây — đặt là ô này lệch khỏi ba ô kia mà không ai kêu.
   Hai dòng `:hover` và `:focus-visible` cũ đã dời sang đó nguyên nghĩa. */
.ovcd-o[b-r61zqphk7h] {
    appearance: none;
    font: inherit;
    cursor: pointer;
}

/* ── Một dòng đơn trong hộp thoại — chép đúng khuôn `.blv-dong` của bàn làm việc (đợt 683), vì đây chính
      là danh sách đã dời từ thẻ ✅ sang. Hai nơi bày cùng một loại dòng thì phải nhìn giống nhau. ── */
.ovcd-dong[b-r61zqphk7h] {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .35rem 0;
    border-bottom: 1px dashed rgba(128, 128, 128, .25);
}

.ovcd-dong:last-of-type[b-r61zqphk7h] { border-bottom: 0; }

.ovcd-nhan[b-r61zqphk7h]  { flex: none; }
.ovcd-mo-ta[b-r61zqphk7h] { flex: 1 1 auto; min-width: 0; font-size: .82rem; }

/* Lý do xuống dòng riêng, mờ hơn: nó là chi tiết, còn ai-ngày-mấy mới là thứ quyết định. */
.ovcd-ly-do[b-r61zqphk7h] {
    display: block;
    font-size: .75rem;
    opacity: .75;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ═══ ⭐ ĐỢT 2198 (việc o1640) — TÊN VIỆC TRONG HỘP «PHÊ DUYỆT / NGHIỆM THU» ═══
   Cùng luật với `ONhiemVu.razor.css` (xem chú thích đầy đủ ở đó).
   ⚠ Hộp này lúc đo 08/09 đang RỖNG danh sách nghiệm thu nên phép đo ⛔ không chạm tới thẻ tên — luật
     đặt trước theo đúng khuôn hai hộp kia, ⛔ đừng đọc thành "đã đo và đạt". Ngày hộp có dữ liệu thì
     `node cong-cu/do-song-hop-thoai-trang-chu.mjs` sẽ nói. */
@media (pointer: coarse) {
    a.ten-ban-ghi[b-r61zqphk7h] {
        display: inline-flex;
        align-items: center;
        min-height: var(--cham-toi-thieu);
    }
}
/* /Components/Pages/ToXe/TrangToXe.razor.rz.scp.css */
/* 🚐 Trang thống kê chuyến đi của tổ xe.
   ⚠ CSS cách ly: mọi bộ chọn ở đây biên dịch thành `.x[b-...]` nên độ đặc hiệu là (0,2,0).
   🔴 Thanh tỷ trọng `.txe-thanh` của bảng xếp hạng đã BỎ cùng bảng ấy (anh Tiến chốt 09/09/2026) —
      ⛔ đừng dựng lại. */

/* 📊 Cột ĐỐI TƯỢNG PHỤC VỤ là cột to nhất; mọi cột phụ co theo nội dung (bốn luật bảng anh Tiến ban
   07/09/2026). `width: 1%` + `nowrap` là khuôn rẻ nhất của bảng HTML để một cột chỉ chiếm đúng phần
   nó cần — phần dư dồn hết cho cột không khai bề ngang. */
.txe-cot-phu[b-4odnsyqffv] {
    width: 1%;
    white-space: nowrap;
}

/* Ô giờ và ô số km trong dòng đang sửa: đủ rộng cho nội dung, ⛔ không kéo cột phụ phình ra. */
.txe-o-gio[b-4odnsyqffv] {
    width: 7.5rem;
}

.txe-o-km[b-4odnsyqffv] {
    width: 6rem;
}

/* 👆 Khổ điện thoại: bảng đã bọc .table-responsive nên nó cuộn TRONG thẻ, ⛔ không kéo giãn cả trang.
   Ô nhập trong dòng sửa thì nhả trần bề ngang ra cho vừa ngón tay. */
@media (max-width: 576px) {
    .txe-o-gio[b-4odnsyqffv],
    .txe-o-km[b-4odnsyqffv] {
        width: 100%;
        min-width: 6rem;
    }

    /* 🔴 SÀN BỀ NGANG PHẢI ĐẶT LÊN Ô, ⛔ KHÔNG LÊN THẺ `<table>` — chỗ đã trả giá một lượt đo ngày
       09/09/2026. `app.css` (đợt 396h) biến MỌI bảng ở khổ ≤ 820px thành khối cuộn và khai
       `min-width: 0 !important` ngay trên `.content table.table` ⇒ một luật `min-width` đặt lên chính
       thẻ bảng **⛔ không bao giờ chạy**, kể cả gán inline bằng JS (đo: computed vẫn ra `0px`).
       Đặt lên Ô thì bảng ẩn danh bên trong giãn theo, và thẻ bảng tự cuộn ngang — đúng ý muốn.

       Vì sao cần sàn: thiếu nó thì cột đối tượng phục vụ bị bóp còn 78px, chữ xuống dòng TỪNG CHỮ MỘT
       ("Đoàn / kiểm tra / Công ty", dòng log thành mười dòng) — đúng thứ anh Tiến bác bỏ ở luật ① của
       bảng, ngày 07/09/2026. Đo sau khi vá: ô rộng 240px, tên nằm gọn MỘT dòng. */
    .txe-bang th:nth-child(2)[b-4odnsyqffv],
    .txe-bang td:nth-child(2)[b-4odnsyqffv] {
        min-width: 15rem;
    }
}
/* /Components/Pages/TrinhChieu/ChieuBuoi.razor.rz.scp.css */
/* ═══ KIỂU RIÊNG CỦA TRANG CHIẾU (đợt 393) — CSS SCOPED, chỉ ăn trong ChieuBuoi.razor ═══
   Cố ý KHÔNG viết vào app.css/co-dien.css: hai file đó dùng chung cả app (đợt 386 đang sửa đồ họa
   toàn app), và trang chiếu là màn hình DUY NHẤT trong phần mềm cần luật "phủ kín màn hình". */

/* Khung chứa nội dung — mọi thứ bên trong đo theo khung này chứ KHÔNG theo 100vh.
   ⚠ Bài học: bản đầu (đợt 379) dùng max-height: calc(100vh - 70px) với số 70 GÕ CỨNG. Thanh đầu thật
   cao 50px, và khi toàn màn hình thì thanh còn tự ẩn đi ⇒ con số cứng luôn sai một ít, lúc thì thừa
   khoảng trắng lúc thì thiếu chỗ. Nay đo theo khung cha nên đúng ở MỌI trạng thái. */
.vung-chieu[b-utku5gmzk5] {
    position: relative;
    min-width: 0;
    min-height: 0;
    outline: none;
    cursor: pointer;
}

/* Ảnh trang PDF · ảnh · video: phủ kín khung, giữ nguyên tỉ lệ, không bao giờ tràn ra ngoài. */
.tc-media[b-utku5gmzk5] {
    position: absolute;
    inset: 8px;
    width: calc(100% - 16px);
    height: calc(100% - 16px);
    object-fit: contain;
}

/* THANH ĐẦU khi TOÀN MÀN HÌNH: nổi lên trên nội dung và tự mờ đi, rê chuột lên mới hiện.
   Người thuyết trình không phải nhìn thấy nút bấm suốt buổi, mà vẫn với tay tới được. */
.thanh-noi[b-utku5gmzk5] {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 5;
    opacity: 0;
    transition: opacity .25s ease;
    background: rgba(5, 13, 31, .82);
    backdrop-filter: blur(4px);
}

.thanh-noi:hover[b-utku5gmzk5],
.thanh-noi:focus-within[b-utku5gmzk5] {
    opacity: 1;
}

/* MÀN HÌNH ĐEN (phím B) — che nội dung khi đang nói, KHÔNG phải tắt máy chiếu.
   Đen tuyệt đối để người xem không đọc lỏm nội dung slide kế tiếp. */
.man-den[b-utku5gmzk5] {
    position: absolute;
    inset: 0;
    z-index: 10;
    background: #000;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

.man-den span[b-utku5gmzk5] {
    color: rgba(255, 255, 255, .16);
    font-size: 1rem;
    letter-spacing: .04em;
}

/* Bảng địa chỉ điều khiển bằng điện thoại — chữ to, dễ gõ lại trên máy khác. */
.o-dia-chi[b-utku5gmzk5] {
    /* ⚠ ĐUÔI EMOJI là BẮT BUỘC theo luật ở app.css mục đầu (đợt 386): khai `font-family` mà thiếu đuôi
       này thì emoji trong chuỗi rơi về font hệ thống, mỗi máy vẽ một kiểu và lệch cỡ hẳn so với chữ.
       Chỗ này là chỗ sót của đợt đó — bắt được ở lượt rà font 14/08/2026. */
    font-family: ui-monospace, Consolas, monospace, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
    font-size: 1.05rem;
    word-break: break-all;
    background: rgba(0, 0, 0, .35);
    border: 1px solid rgba(255, 255, 255, .25);
    border-radius: 10px;
    padding: .5rem .75rem;
}

/* ═══ ĐỢT 423 — XOAY NỘI DUNG + BÚT VẼ (anh Tiến 10/08/2026) ═══ */

/* XOAY: quay chính phần tử media, KHÔNG đụng tệp gốc. Góc do Razor đặt vào biến `--tc-xoay` của
   vùng chiếu (đã qua InvariantCulture — quy tắc #5: vi-VN sẽ ghi "90,5deg" và CSS bỏ nguyên luật). */
.vung-chieu[b-utku5gmzk5] {
    overflow: hidden;   /* hình vừa xoay không được phép thò ra ngoài khung chiếu */
}

.vung-chieu .tc-media[b-utku5gmzk5] {
    transform: rotate(var(--tc-xoay, 0deg));
    transition: transform .2s ease;
}

/* ⚠ CHỖ DỄ SAI NHẤT CỦA VIỆC XOAY (đã dính ở lần dựng đầu, ảnh chụp cho thấy hình lệch hẳn ra ngoài):
   `transform: rotate` KHÔNG đổi Ô BỐ CỤC của phần tử — trình duyệt vẫn dành cho nó đúng khung cũ rồi
   mới quay hình quanh tâm. Khung rộng 1568 × cao 666 mà quay 90° thì hình thành 666 × 1568 ⇒ thò ra
   ngoài hai đầu.
   Cách chữa: khi xoay 90°/270°, ĐỔI CHỖ bề rộng và bề cao của chính phần tử (lấy theo khung nhìn, trừ
   thanh đầu ~52px) rồi căn giữa bằng translate. Ô lúc này là (cao × rộng); quay 90° ra đúng (rộng × cao)
   = vừa khít khung. `object-fit: contain` lo phần giữ tỉ lệ ảnh bên trong. */
.vung-chieu[style*="90deg"] .tc-media[b-utku5gmzk5],
.vung-chieu[style*="270deg"] .tc-media[b-utku5gmzk5] {
    inset: auto;
    top: 50%;
    left: 50%;
    width: calc(100vh - 68px);
    width: calc(100dvh - 68px);   /* ⭐ 1500 — dòng lui cho DI ĐỘNG, xem LuatDonViKhungNhin */
    height: calc(100vw - 16px);
    transform: translate(-50%, -50%) rotate(var(--tc-xoay, 0deg));
}

/* BÚT VẼ: lớp phủ kín vùng chiếu. `touch-action: none` để trên máy bảng vuốt là VẼ chứ không cuộn. */
.tc-lop-ve[b-utku5gmzk5] {
    position: absolute;
    inset: 0;
    z-index: 8;
    width: 100%;
    height: 100%;
    cursor: crosshair;
    touch-action: none;
}

/* Chấm chọn màu bút trên thanh công cụ. */
.tc-cham-mau[b-utku5gmzk5] {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, .55);
    padding: 0;
    cursor: pointer;
}

.tc-cham-mau.dang-chon[b-utku5gmzk5] {
    border-color: #fff;
    box-shadow: 0 0 0 2px rgba(255, 255, 255, .45);
}

/* ═══ ⭐ ĐỢT 2069 (việc o1346) — VÙNG CHẠM 44px CHO BỐN CHẤM CHỌN MÀU BÚT ═══

   📏 Bộ quét toàn kho `VungChamCssCachLyToanKhoTests` (111 tệp `.razor.css`) bắt được chỗ này:
     `.tc-cham-mau` nằm trên một thẻ `<button>` có `@onclick` — tức NGÓN TAY BẤM THẬT — mà chỉ
     **20×20px**, kém hẳn ngưỡng `--cham-toi-thieu: 44px` của app.css. Đây là ca THẬT thứ hai cùng
     họ với lỗ đợt 2028 (dãy nút mẫu tên nhiệm vụ, vá ở đợt 2042): CSS cách ly biên dịch ra
     `.tc-cham-mau[b-…]` (0,2,0) nên khối chạm dùng chung của app.css viết `.btn` (0,1,0) THUA ⇒
     tự tệp này phải nới lại, ⛔ đừng đi sửa app.css (viết ở đó là luật ⛔ không bao giờ chạy).

   🔑 Màn trình chiếu là nơi ngón tay dùng NHIỀU chứ ⛔ không ít: chính nút ✏️ bút vẽ ngay cạnh đã ghi
     *"rê chuột (hoặc VUỐT TAY) để khoanh"*, và lớp vẽ `.tc-lop-ve` khai `touch-action: none`. Bốn
     chấm × 44px = 176px, thanh công cụ đã `d-flex gap-1` nên ⛔ không đẩy tràn ở khổ 390px.
   🔑 Đọc BIẾN `--cham-toi-thieu`, ⛔ đừng gõ lại số 44 — ngưỡng nằm ở ĐÚNG MỘT CHỖ (app.css).
   ⛔ CHỈ trong `@media (pointer: coarse)`: 20px là cỡ CỐ Ý cho chuột (bốn chấm màu đứng cạnh nhau
     trên một thanh công cụ hẹp), nới cho cả máy tính là đổi bố cục để giải bài của điện thoại
     (rủi ro lan truyền, quy tắc #18). */
@media (pointer: coarse) {
    .tc-cham-mau[b-utku5gmzk5] {
        width: var(--cham-toi-thieu, 44px);
        height: var(--cham-toi-thieu, 44px);
    }
}
/* /Components/Pages/YTe/KhoiGhiDotKham.razor.rz.scp.css */
/* ═══ GHI ĐỢT KHÁM ĐỊNH KỲ HÀNG LOẠT — CSS CÁCH LY (đợt 1719, việc o990) ═══

   Tệp này CHỈ có một khối: vùng chạm cho ngón tay quanh ô tích của lưới. Mọi thứ khác (thẻ, bảng,
   ô chọn, nút) dùng thẳng lớp Bootstrap để màn hình không lệch khuôn với ba tab anh em của app.

   🔴 VÌ SAO MÀN HÌNH NÀY CẦN, TRONG KHI BA TAB KIA CHƯA:
     Ô tích ở đây KHÔNG phải một chi tiết phụ — nó là THAO TÁC CHÍNH của cả màn hình, và có tới
     386 cái xếp thành cột. Đo sống 02/09/2026 ở khung 375px trên bản chưa có tệp này: ô tích
     16×16px, tức 387 vùng chạm nằm dưới hẳn ngưỡng ngón tay. Bấm trượt ở đây không phải phiền
     phức nhỏ: trượt sang dòng bên cạnh là ghi một lượt khám cho NGƯỜI KHÁC.

   ⚠ Ô tích của lưới là ô tick TRẦN (không có chữ nào bên cạnh để mà bấm) ⇒ đi khuôn ② của
     `Nuoc/TabDanhMucNuoc.razor.css`: bọc thêm thẻ `label.o-tick-cham` ngay trong `.razor`. Ở chế
     độ chuột thẻ ấy là hộp inline ôm sát ô tick nên KHÔNG đổi một pixel nào; ở chế độ ngón tay nó
     nở thành vùng chạm bao quanh. ⛔ ĐỪNG chép khuôn ① (nới `.form-check-label`) sang đây — khuôn
     ấy dành cho ô tick CÓ CHỮ đi kèm, mà lưới này thì không có chữ nào cả.

   🔑 VÌ SAO BỌC THÊM THẺ chứ không dùng lớp phủ tràn ra ngoài ô tick: ô này nằm trong một `td`,
     và `td` bên cạnh chứa cả ô chọn phân loại. Một lớp phủ 44px vô hình tràn khỏi ô tick sẽ đè lên
     mép ô chọn ấy và NUỐT cú bấm của nó. Thẻ bọc thì nằm TRONG dòng chảy: hàng chỉ cao lên, không
     có gì chồng lên gì.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, KHÔNG PHẢI `max-width` — khổ hẹp KHÔNG có nghĩa là ngón tay
     (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này không vẽ ra một pixel nào — mọi khai báo đều nằm trong câu
     media.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — đừng viết câu media ghép bằng chữ `and` trong tệp `.razor.css`:
     bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học đợt 1481).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo. Cặp ký tự ấy lọt vào GIỮA câu văn là chú
     thích đóng sớm và trình duyệt bỏ nguyên luật ngay dưới — không một câu lỗi nào. Vì thế ở đây
     gọi tên cặp ký tự ấy bằng CHỮ, không gõ ra.
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* Thẻ bọc ô tích của lưới. Cần CẢ `min-width`: ô tích chỉ rộng 16px, nới mỗi chiều cao là
       ngón tay vẫn trượt ngang sang cột mã nhân viên.
       ⚠ `inline-flex` chứ không `flex`: thẻ nằm gọn trong một `td` hẹp, hoá `flex` là nó tự kéo
         trọn bề ngang ô rồi đẩy cột bên cạnh. */
    .o-tick-cham[b-j478d4u0lg] {
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
    }
}
/* /Components/Pages/YTe/KhoiHoSoYTe.razor.rz.scp.css */
/* ═══ HỒ SƠ Y TẾ CỦA MỘT NGƯỜI — CSS CÁCH LY (đợt 1764, việc o987) ═══

   Tệp này CHỈ có một khối: vùng chạm cho ngón tay ở HAI điều khiển mà khối luật chạm dùng chung
   của `app.css` ⛔ KHÔNG với tới. Mọi thứ khác của màn hình (ô nhập, ô chọn, nút, dải tab) dùng
   thẳng lớp Bootstrap và đã ĐO ĐƯỢC là đạt, nên ⛔ không khai lại ở đây.

   📏 ĐO SỐNG 02/09/2026 — Chrome thật, hỏi `document.elementFromPoint` chứ ⛔ không đọc CSS và
      ⛔ không leo hộp cha (hộp cha đủ 44px ⛔ không có nghĩa là bấm được 44px — bài học đợt 1619):

     | điều khiển                          | 375×812 cảm ứng | 768×1024 cảm ứng |
     |-------------------------------------|-----------------|------------------|
     | nút ✕ của dòng thông báo            | 25×24 🔴        | 25×24 🔴         |
     | một dòng gợi ý người trong ô tìm    | 57–81 ✅        | 33–34 🔴         |

   🔴 VÌ SAO PHẢI ĐO Ở HAI KHỔ, và đây là bài học đắt nhất của việc o987: dòng gợi ý người ở khổ
     375px đo được 57–81px, tức ĐẠT — nhưng nó đạt vì chữ trong nút BỊ NGẮT thành hai ba dòng,
     ⛔ không phải vì có luật nào giữ sàn cho nó. Ở khổ 768px cảm ứng (máy tính bảng — cũng là ngón
     tay) chữ vừa MỘT dòng và cả 15 nút tụt xuống 33px. ⇒ Một lượt đo ở đúng một khổ nói được
     *"hôm nay đạt"*, ⛔ không nói được *"có sàn"*. Cùng họ bài học SÀN ĐẾM của đợt 1014.

   🔑 VÌ SAO HAI ĐIỀU KHIỂN NÀY ĐỨNG NGOÀI KHUÔN DÙNG CHUNG: khối chạm ở `app.css` (đợt 1518) phủ
     `.form-control` · `.form-select` · `.btn` · `.nav-link` · `summary` · nút lịch của ô ngày.
     Hai thứ ở đây ⛔ không mang lớp nào trong số ấy — `.btn-close` là một lớp RIÊNG của Bootstrap
     (⛔ không phải `.btn`), còn dòng gợi ý là `.list-group-item-action`. Đo được ⇒ nới tại chỗ,
     đúng đường mà mười ba tệp cách ly làm trước đã đi.

   ⚠ NÚT ✕ PHẢI ĐỔI SANG `border-box`, ⛔ ĐỪNG CHỈ ĐẶT `min-height`: Bootstrap khai nút này là
     `box-sizing: content-box` với `padding: .25em` ⇒ `min-height` áp cho HỘP NỘI DUNG, và hộp
     viền phình thành 52px chứ ⛔ không phải 44px. Thừa 8px ⛔ không cắn ai, nhưng ngưỡng phải ra
     ĐÚNG con số đã chốt thì lượt đo sau mới đối chiếu được.
   ⚠ Nút ✕ cần CẢ `min-width`: nó ⛔ không có chữ nào bên cạnh để mượn bề ngang, nới mỗi chiều cao
     là ngón tay vẫn trượt NGANG ra ngoài (đúng bài học đợt 1690 cho ô tick trần).
   🔴 DÒNG GỢI Ý PHẢI CÓ `flex-shrink: 0` ĐI KÈM — ⛔ ĐỪNG BỎ, và đây là chỗ bản vá đầu ĐÃ HỎNG
     rồi phải đo mới thấy. Thẻ bọc `.list-group` là **flex column cao cứng 280px** (`overflow-y:
     auto`), nên mỗi dòng gợi ý là một **FLEX ITEM** có `flex-shrink: 1`. Với flex item, giá trị
     `min-height: auto` mang nghĩa riêng — *"⛔ không được co xuống dưới cỡ nội dung"* — nên gõ một
     CON SỐ vào `min-height` là **GỠ MẤT tấm chắn ấy**, và phép co của flex lập tức bóp dòng 81px
     xuống đúng 44px. Đo sống 02/09/2026 ở khổ 375, bản chỉ có `min-height`: **15/15 dòng bị cắt
     chữ** (`scrollHeight 52 > clientHeight 44`) — mất đúng phần mã nhân viên và phòng ban, tức là
     mất thứ để phân biệt hai người trùng tên. `flex-shrink: 0` trả lại tấm chắn: cao = max(nội
     dung, 44px). Số đo ba bản, cùng một lượt chạy:
       | khổ | gốc | chỉ `min-height` | + `flex-shrink: 0` |
       |-----|-----|------------------|--------------------|
       | 375 | 57–81px · 0 cắt | 44px · **15 cắt** 🔴 | 57–81px · 0 cắt ✅ |
       | 768 | 33–34px 🔴 · 0 cắt | 44px · 0 cắt | 44px · 0 cắt ✅ |
     ⇒ Bài học: ở TRONG một flex container, `min-height` ⛔ KHÔNG chỉ là "chặn dưới" — nó còn tắt
     một tấm chắn đang có sẵn. Nới vùng chạm cho flex item thì phải hỏi *"nó có co được không"*.
   ⛔ Dòng gợi ý ⛔ TUYỆT ĐỐI không đổi `display` sang `flex`: nội dung nút gồm nhiều mẩu rời (họ tên
     in đậm + mã và phòng ban in nhỏ + huy hiệu tình trạng). Hoá `flex` là ba mẩu ấy thành ba phần
     tử con xếp ngang, dòng chữ thôi tự xuống dòng và khoảng trắng giữa các mẩu bị bỏ đi — đúng
     điều cấm đã ghi ở đợt 1672. Bề ngang của nó là bề ngang thẻ bọc (đo 317px ở khổ 375 và 401px
     ở khổ 768) nên ⛔ không cần `min-width`.

   🔴 ĐIỀU KIỆN LÀ `pointer: coarse`, ⛔ KHÔNG PHẢI `max-width` — khổ hẹp ⛔ không có nghĩa là ngón
     tay (cửa sổ thu nhỏ trên máy tính vẫn dùng chuột) và màn rộng vẫn có thể là cảm ứng. Chính
     lượt đo 768px ở trên là ca chứng minh: đó là một khổ RỘNG mà vẫn là ngón tay.
   🔒 CHỐT NGƯỢC: ở chế độ CHUỘT tệp này ⛔ không vẽ ra một pixel nào — mọi khai báo đều nằm trong
     câu media. Lời hứa ấy phải ĐO, ⛔ đừng suy từ việc đã gõ đúng câu media.
   ⚠ Ngưỡng đọc từ BIẾN `--cham-toi-thieu` khai ở `app.css` (biến kế thừa nên ở đây thấy được);
     ⛔ đừng gõ số 44px vào tệp này, ngưỡng phải nằm ở đúng MỘT chỗ.
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết câu media ghép bằng chữ `and` trong tệp `.razor.css`:
     bộ biên dịch CSS cách ly gắn nhầm dấu scope vào đuôi câu media (bài học đợt 1481).
   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo. Cặp ký tự ấy lọt vào GIỮA câu văn là chú
     thích đóng sớm và trình duyệt bỏ nguyên luật ngay dưới — ⛔ không một câu lỗi nào. Vì thế ở
     đây gọi tên cặp ký tự ấy bằng CHỮ, ⛔ không gõ ra.
   📏 Soi chú thích CSS: node cong-cu/soi-chu-thich-css.mjs */

@media (pointer: coarse) {

    /* Nút ✕ đóng dòng thông báo. Đo 02/09/2026 trước khi vá: hộp 24×24, vùng chạm THẬT 25×24. */
    .btn-close[b-3bjhx51tp7] {
        box-sizing: border-box;
        min-width: var(--cham-toi-thieu);
        min-height: var(--cham-toi-thieu);
    }

    /* Một dòng gợi ý người trong ô tìm. Đo 02/09/2026 trước khi vá: 33px ở khổ 768 cảm ứng.
       Bấm trượt ở đây ⛔ không phải phiền phức nhỏ — trượt sang dòng bên cạnh là MỞ HỒ SƠ Y TẾ
       của người khác, và mỗi lượt mở đều để lại một dòng trong nhật ký mang tên người bấm.
       ⛔ ĐỪNG BỎ `flex-shrink` — thiếu nó thì chính `min-height` bóp dòng 81px xuống 44px và
       CẮT MẤT mã nhân viên + phòng ban (đo: 15/15 dòng bị cắt). Xem khối 🔴 ở đầu tệp. */
    .list-group-item-action[b-3bjhx51tp7] {
        min-height: var(--cham-toi-thieu);
        flex-shrink: 0;
    }
}
/* /Components/Pages/YTe/PhieuNhapHoSoYTe.razor.rz.scp.css */
/* ═══ PHIẾU NHẬP HỒ SƠ Y TẾ — CSS CÁCH LY (đợt 1939, việc o1213) ═══

   Tệp này CHỈ làm một việc: chia tờ phiếu thành các NHÓM đọc được bằng mắt. Mọi thứ khác (ô nhập,
   nút, huy hiệu, dải tiến độ, lưới 12 cột) dùng thẳng lớp Bootstrap 5.3 và bộ biến `--bs-*` của
   nó, nên tự đi theo bộ màu và nền sáng/tối của app — ⛔ không khai lại một mã màu nào ở đây.

   🔑 VÌ SAO DÙNG THẺ NHÓM CỦA BIỂU MẪU (fieldset + legend) CHỨ ⛔ KHÔNG PHẢI HAI THẺ div:
     trình đọc màn hình đọc tên nhóm TRƯỚC mỗi ô nằm trong nhóm ấy, nên người khiếm thị nghe được
     *"Cần biết ngay khi cấp cứu — Nhóm máu"* thay vì chỉ *"Nhóm máu"*. Bằng hai thẻ div thì phải
     tự gõ thêm `role="group"` + `aria-labelledby`, tức làm tay đúng thứ thẻ chuẩn đã có sẵn.
   ⚠ Trình duyệt cho `fieldset` một bộ viền + đệm riêng và cho `legend` bề ngang co theo chữ —
     phải nắn lại, không thì tờ phiếu lĩnh năm cái khung viền lồng nhau.

   ⛔ KHÔNG khai vùng chạm 44px ở đây: mọi điều khiển của phiếu đều mang lớp `.form-control` ·
     `.btn` · `.form-select`, tức đã nằm trong khối `@media (pointer: coarse)` dùng chung của
     `app.css` (đợt 1518 · 1521, ngưỡng ở biến `--cham-toi-thieu`). Khai lại là hai chỗ giữ cùng
     một con số. Hai thứ CỐ Ý đứng ngoài khuôn ấy (nút ✕ · dòng gợi ý người) đã được
     `KhoiHoSoYTe.razor.css` nới, và phiếu này ⛔ không dùng cái nào.

   ⚠ Chú thích CSS đóng bằng dấu sao rồi dấu gạch chéo. Cặp ký tự ấy lọt vào GIỮA câu văn là chú
     thích đóng sớm và trình duyệt bỏ NGUYÊN luật ngay dưới — ⛔ không một câu lỗi nào, nên ở đây
     gọi tên cặp ký tự ấy bằng CHỮ. Soi: node cong-cu/soi-chu-thich-css.mjs
   ⚠ MỘT điều kiện mỗi câu media — ⛔ đừng viết câu media ghép bằng chữ `and` trong tệp
     `.razor.css` (bài học đợt 1481). Tệp này hiện ⛔ không có câu media nào. */

/* Một nhóm ô của tờ phiếu. Viền mảnh + nền hơi lệch để mắt tách được năm nhóm mà ⛔ không cần
   thêm một tiêu đề in đậm nào. Màu lấy từ biến của Bootstrap ⇒ nền tối tự đúng. */
.pnyt-nhom[b-wyiej574gd] {
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: .5rem;
    padding: .5rem .75rem .75rem;
    margin-bottom: .75rem;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}

.pnyt-nhom-cuoi[b-wyiej574gd] {
    margin-bottom: 0;
}

/* Tên nhóm. `float: left` + `width: auto` là cách nắn `legend` của Bootstrap Reboot (nó đặt
   `display: block; width: 100%`) về đúng bề ngang chữ, để chữ nằm gọn trong khung nhóm. */
.pnyt-ten-nhom[b-wyiej574gd] {
    float: none;
    width: auto;
    margin-bottom: .35rem;
    padding: 0;
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--bs-secondary-color, #6c757d);
}

/* Ô DỊ ỨNG — ô quan trọng nhất của cả tờ hồ sơ (chú thích ở HoSoYTe.DiUng). Nó được một vạch đỏ
   bên trái, ⛔ KHÔNG phải nền đỏ: nền đỏ dưới chữ đen là tụt tương phản ở đúng ô người ta phải
   đọc kỹ nhất. Vạch màu chỉ là lớp thứ hai — chữ ngay dưới ô đã nói ra bằng lời, vì báo trạng
   thái CHỈ bằng màu thì 8% đàn ông mù màu đỏ-lục ⛔ không nhận được gì. */
.pnyt-o-canh-bao[b-wyiej574gd] {
    border-left: 3px solid var(--bs-danger, #dc3545);
}

/* Ô chỉ để BÀY một con số phần mềm tự tính (BMI · tình trạng thẻ) — cao bằng ô nhập bên cạnh để
   hàng ⛔ không lệch, nhưng ⛔ không có viền vì nó ⛔ không gõ được. */
.pnyt-o-tinh[b-wyiej574gd] {
    min-height: calc(1.5em + .5rem + 2px);
    padding: .25rem 0;
    line-height: 1.5;
}

/* Tám nút gợi ý nhóm máu. Bề ngang tối thiểu để tám nút thành một khối đều nhau, ⛔ không nhảy
   theo độ dài chữ (AB+ dài hơn O+). */
.pnyt-nut-mau[b-wyiej574gd] {
    min-width: 3.25rem;
    font-variant-numeric: tabular-nums;
}

/* Dải tiến độ mảnh — nó là thông tin phụ, ⛔ không phải thứ chiếm chỗ của các ô nhập. */
.pnyt-dai[b-wyiej574gd] {
    height: .5rem;
}

/* Huy hiệu nhỏ nằm trong nhãn ô dị ứng.
   🔴 CỠ CHỮ ⛔ KHÔNG ĐƯỢC XUỐNG DƯỚI .65rem — hàng rào `CoChuONgayTests` bắt ĐỎ, và nó bắt ĐÚNG:
     bản đầu của đợt này gõ .625rem (10px) cho đúng cái huy hiệu đang nói *"quan trọng nhất"*.
     Một nhãn cảnh báo ⛔ không đọc nổi thì bằng như ⛔ không có, mà chữ bên cạnh vẫn hứa là có. */
.pnyt-huy-hieu[b-wyiej574gd] {
    font-size: .7rem;
    font-weight: 600;
    vertical-align: middle;
}
