/* FQFVN — nền xanh lạnh (kho đông), điểm nhấn xanh rêu cho sổ chi phí,
   nâu hổ phách (vỏ sầu) cho sổ vật tư. Chữ Be Vietnam Pro làm riêng cho
   tiếng Việt; số và mã dùng JetBrains Mono cho thẳng cột. */
:root{
  /* CHỈ giao diện SÁNG — Tấn Phú chốt 15/09/2026, bỏ hẳn nền tối.
     `color-scheme: light` không phải thừa: thiếu nó, điện thoại đang bật nền
     tối sẽ tự vẽ ô nhập, ô chọn và thanh cuộn theo kiểu tối, trong khi trang
     đã cố định sáng — chữ đen trên nền đen ở đúng mấy ô người ta phải gõ. */
  color-scheme: light;
  /* NỀN TRANG #faf9f8 — cùng màu với --nen của hệ Nocturne (16/09/2026).
     Trước đây bộ token cũ dùng #eef1ee (trắng ngả xanh lạnh, ý là kho đông) còn
     màn Nocturne dùng #faf9f8 (trắng ngả ấm, theo quyết định "đỏ trên trắng
     giống cờ Nhật Bản" ngày 14/09). Đi từ Tồn kho sang Phiên duyệt là cả tấm
     nền đổi sắc — thấy rõ khi hai tab mở cạnh nhau.
     Đây là đổi ĐÚNG MỘT token nền, không phải đổ cả bảng màu Nocturne vào
     :root: viền, nền lún, chữ mờ vẫn y như cũ. Không thích thì sửa lại một
     dòng này. Chữ trên nút đỏ lấy màu này (.btn-primary) nên tương phản nhúc
     lên chút: 4,3:1 → 4,55:1 trên #DF301C. */
  --luu:#1f77d0; --bg:#faf9f8; --surface:#fff; --ink:#151d1a; --muted:#5d6b65; --line:#d3dad5; --sunken:#e7ebe8;
  /* TRẮNG — khai Ở ĐÂY dù `nocturne.css` cũng có (18/09/2026). Không phải trùng
     lặp thừa: `fqfvn.css` dùng nó ở năm chỗ (chữ trên nền tối của hộp xem ảnh,
     chip nhà máy đọc), mà màn duyệt chạy bộ token cũ thì KHÔNG nạp nocturne.css
     — nên `var(--trang)` ở đó là giá trị không hợp lệ, `color` bị bỏ, chữ rơi về
     màu thừa hưởng. Chữ trắng hoá chữ ĐEN trên nền tối: nhìn thì như cái nút
     hỏng. Đúng cú `--do` đã ghi ở § "MỌI THỨ MÁY ĐỌC NÓI", lần thứ hai.
     LUẬT: luật trong fqfvn.css chỉ được dùng token khai trong fqfvn.css. */
  --trang:#fff;
  --expense:#DF301C; --expense-soft:#e3efeb; --supply:#8a5a11; --supply-soft:#f6ece0;
  --danger:#a8401c; --danger-soft:#f9e8e2; --warn:#96700a; --warn-soft:#f7eed6;
  /* VÀNG LỢT của dòng ĐÃ GHI SỔ — Tấn Phú chốt 22/09/2026: *"với dòng hoàn
     thành đổi qua màu vàng lợt"*. Phải là một token riêng, không mượn
     `--warn-soft`: cái đó đang làm màu RÊ CHUỘT của mọi bảng, mà hai thứ dùng
     chung một màu thì rê qua một dòng đã ghi sổ là không thấy gì đổi.
     Đo: ΔE 20,3 với màu rê chuột và 33,9 với nền trắng của dòng thường — xa cả
     hai bên. Chữ `--ink` trên nền này được 13,8:1. */
  --xong-vang:#f6e7a8;
  /* Bậc ĐẬM của thang cảnh báo — nền đặc, cho nút chứ không cho khối chữ.
     Trùng hex với --tt-canh-nen là cố ý và không phải chỗ để gộp: thang
     --tt-* nói "một việc đang ra sao" (huy hiệu), thang này là màu vẽ giao
     diện. --supply và --giay-dongthung từng là cặp thứ hai như vậy, tới
     18/09/2026 thì thôi — thang loại giấy sáng lên, --supply đứng yên. Chính
     vì hai token RỜI NHAU mà một thang đổi không kéo thang kia đi theo. */
  --warn-manh:#ffc045;
  --shadow:0 1px 2px rgba(21,29,26,.06), 0 8px 24px -16px rgba(21,29,26,.28);
  /* Thang TRẠNG THÁI — Tấn Phú chốt 14/09/2026, đổi sang NỀN ĐẶC 15/09/2026.
     Năm màu nền lấy đúng từ ảnh mẫu anh gửi (trích pixel, không đoán bằng mắt).
     Khác hẳn thang nhận diện: đỏ/trắng là thương hiệu, còn năm màu này chỉ để
     nói MỘT việc đang ra sao. Đừng dùng chúng cho nhãn dữ liệu, và đừng dùng
     nhãn dữ liệu làm trạng thái.

     CHỮ TRẮNG CẢ NĂM — Tấn Phú chốt 15/09/2026, đúng như ảnh mẫu.

     Ghi lại con số để người sau đừng "sửa giúp": chữ trắng trên hổ phách chỉ
     được 1,61:1 và trên ngọc 2,03:1 (đo trên chính ảnh mẫu), dưới chuẩn 4,5:1
     khá xa. Anh biết và vẫn chọn trắng cho đồng bộ — đây là quyết định, không
     phải sót. Đừng đổi lại mà không hỏi. Muốn chữ đen cho hai nền sáng thì đặt
     --tt-ok và --tt-canh về #14181a, khi đó được 8,66:1 và 10,96:1. */
  --tt-ok:#ffffff;    --tt-ok-nen:#17cab8;      /* thành công — ngọc */
  --tt-loi:#ffffff;   --tt-loi-nen:#fd3995;     /* thất bại — hồng sen */
  --tt-tin:#ffffff;   --tt-tin-nen:#1f97f4;     /* thông báo — xanh da trời */
  --tt-canh:#ffffff;  --tt-canh-nen:#ffc045;    /* cảnh báo — hổ phách */
  --tt-suco:#ffffff;  --tt-suco-nen:#505050;    /* sự cố — than chì */

  /* Thang LOẠI GIẤY — Tấn Phú chốt 15/09/2026, khi app tự nhận ra mình đang đọc
     tờ gì. Tám màu này KHÔNG phải trạng thái (đó là thang --tt-* ở trên) và
     không phải nhận diện (đó là đỏ/trắng): chúng chỉ nói "anh đang ở màn nào".

     Vì sao cần: hai màn dễ nhầm nhau nhất là Sản lượng và Đóng thùng — cùng
     nhãn A/B/C/D/CC/Bi ở cột trái, nên nhìn lướt qua không biết máy đọc tấm ảnh
     thành cái nào. Phân bằng SẮC (xanh dương ≠ cam) chứ không bằng độ sáng, để
     người phân biệt màu kém vẫn tách được.

     SÁNG, KHÔNG ÂM — Tấn Phú chốt 18/09/2026, kèm ảnh mẫu: *"các tag màu sắc
     đang quá âm… tôi cần kiểu sáng"*. Bản cũ là tám màu ĐỤC (bão hoà 40–78%,
     sáng 25–43%), mà hàng thẻ lọc còn phủ thêm opacity .55 nữa nên ra pastel
     xám — trích pixel ảnh anh gửi đo được #9991bb · #95a2c8 · #baa170. Nay bão
     hoà 63–100%, và hàng thẻ bỏ hẳn lớp opacity kia (xem .loc-tag). Đổi Ở ĐÂY
     nên nhãn, dải đầu màn duyệt và vạch trên lưới Chụp giấy đổi cùng một lượt —
     ba chỗ, một bộ màu, không chỗ nào tự pha màu riêng.

     ĐI BA NẤC MỚI TỚI, ghi lại để người sau đừng kéo ngược:
       ① đục    — bão hoà 40–78%, anh nói "quá âm";
       ② chói   — 73–91%, anh nói *"màu giờ quá sáng rồi. giảm lại chút"*;
       ③ hạ một nấc, RỒI TRẢI RA BẢY SẮC — *"đang có 2 màu tím luôn. sửa lại.
          đỏ cam vàng lục lam chàm tím. có rất nhiều màu mà"*.

     Nấc ③ là chỗ bài học nằm: hạ độ chói xong thì tám màu vẫn SAI, vì chúng
     dồn cục. Đo mới thấy rõ — khoảng cách Lab (ΔE) của cặp gần nhau nhất chỉ
     25,5 (tím #ae2fcc ↔ chàm #7352e8), và bốn màu chen trong quãng lam–lục
     176°–220° trong khi cả vùng đỏ–cam–vàng bỏ trống. Nay tám màu rải đều
     vòng màu — góc 0 · 29 · 45 · 146 · 205 · 235 · 289 · 326 — và cặp gần
     nhau nhất lên ΔE 35,8. Thêm màu thì ĐO ΔE với bảy màu kia trước, đừng
     chọn bằng mắt: hai màu cách nhau 36° góc vẫn có thể cùng đọc ra "tím".

     CHỮ THEO TỪNG NỀN, không phải trắng hết — đây là chỗ dễ sửa hỏng nhất.
     Bảy nền kia chữ trắng (4,58 · 4,69 · 4,71 · 4,93 · 5,14 · 5,15 · 5,55:1,
     đều qua chuẩn 4,5:1), riêng VÀNG thì không: chữ trắng trên #edb100 chỉ
     được 1,93:1, không đọc nổi. Nó dùng --ink và được 8,91:1. Muốn vàng mà
     vẫn chữ trắng thì phải dìm nền xuống thành nâu mù tạt — tức quay lại đúng
     cái "âm" đã bị chê. Nên: đổi nền nào thì đổi --giay-*-chu của nền ấy theo,
     và .dai-giay đã bỏ color:#fff ở luật gốc để ăn theo cặp token này.

     Và nhãn LÚC NÀO CŨNG ghi tên loại bằng chữ — màu chỉ là thứ nhắc thêm, bỏ
màu đi vẫn đọc được.

     MÀU THỨ CHÍN (quỹ tạm ứng) chọn bằng phép ĐO, đúng lời dặn ngay trên. Quãng
     45°–146° là chỗ trống rộng nhất của vòng màu, nhưng mọi màu trong đó đọc ra
     "vàng ám" hay "lục ám" — cùng họ với hai màu đã có. Quãng 146°–205° hẹp hơn
     mà tách bạch hơn: #107f84 cách màu gần nhất ΔE 42,1, rộng hơn cả cặp gần
     nhau nhất của tám màu cũ (35,8 — đỏ ↔ cam). Chữ trắng được 4,78:1. */
  --giay-bangluong:#db2f2f;  --giay-bangluong-chu:#fff;  /* đỏ   ·   0° */
  --giay-dongthung:#b45c08;  --giay-dongthung-chu:#fff;  /* cam  ·  29° */
  --giay-valot:#edb100;      --giay-valot-chu:#151d1a;   /* vàng ·  45° — chữ MỰC */
  --giay-thumui:#158746;     --giay-thumui-chu:#fff;     /* lục  · 146° */
  --giay-sanluong:#0a6cb0;   --giay-sanluong-chu:#fff;   /* lam  · 205° */
  --giay-nhaphang:#5560db;   --giay-nhaphang-chu:#fff;   /* chàm · 235° */
  --giay-chungtu:#ae2fcc;    --giay-chungtu-chu:#fff;    /* tím  · 289° */
  --giay-phieuchi:#cc2b86;   --giay-phieuchi-chu:#fff;   /* hồng · 326° */
  --giay-quytamung:#107f84;  --giay-quytamung-chu:#fff;  /* lục lam · 183° */

  /* Ô ĐÃ SỬA SỐ ở chế độ soi chia đôi — nền xanh nhạt (Tấn Phú chốt 15/09/2026).
     Khác hẳn ô ĐÃ TICK, và phải khác: tick nói "tôi đã soi số này", sửa nói
     "con số này do người gõ, không phải từ tờ giấy". Hai việc khác nhau nên
     tách hẳn KÊNH — tick đổi VIỀN, sửa đổi NỀN. Một ô vừa sửa vừa tick thì đọc
     được cả hai, chứ tô nền cả hai là hai nghĩa nhập làm một.
     Đo rồi mới chọn: --sua-chu trên --sua-nen được 6,35:1, --ink được 15,1:1.
     Ở chế độ cũ, ô đã sửa vẫn là NỀN VÀNG như trước — không đụng. */
  --sua-nen:#e2f2fb; --sua-vien:#1f97f4; --sua-chu:#0b5c86;

  /* Ô NGƯỜI GÕ của bảng kiểu sổ (màn Duyệt chi lương) — vàng #FFF2CC của Lark
     dẹt trên nền trắng ở độ đục 0,5, tức ĐÚNG màu ô nhập của màn Sổ. Khai ĐỤC
     chứ không rgba: cột họ tên dính, và ô dính nền trong suốt thì chữ chạy bên
     dưới hiện xuyên qua. */
  --o-nhap:#fff9e6;

  /* KHUNG CỦA MỌI MÀN — một bề rộng, một lề, một chiều cao thanh trên, dùng
     cho CẢ HAI hệ thiết kế (bộ token cũ và Nocturne).

     Khai ở `:root` của file này chứ không ở `nocturne.css` vì `fqfvn.css` LUÔN
     được nạp: màn Nocturne mà file kia không tới được máy người dùng — đã xảy
     ra hai lần, xem cms/CLAUDE.md § "Giao diện" — thì cái vỏ trang vẫn đúng.

     1400px và 51px là số của hệ Nocturne, nay áp cho mọi màn: trước đây
     `.page` khai 1020px nên đi từ Tồn kho sang Phiên duyệt là mép nội dung
     nhảy 380px, và thanh trên bó trong 1020px thì mười hai mục chữ không đủ
     chỗ, vỡ thành ba hàng. */
  --khung-rong:1400px; --khung-le:20px; --cao-thanh-tren:51px;
  /* Đệm trên–dưới của thanh. Là token vì menu con treo theo nó: mép trên của
     menu = mép dưới của thanh, tính bằng chính con số này. Đổi đệm mà menu
     không theo là menu lơ lửng giữa thanh. */
  --dem-thanh:8px;
}
/* Lề hẹp lại trên điện thoại. Khai một chỗ nên thanh trên, nội dung và thanh
   dính co cùng nhau — 20px hai bên ở màn 390px là ăn mất một phần mười bề ngang. */
@media (max-width:560px){ :root{--khung-le:12px} }

/* [hidden] PHẢI THẮNG — một luật cho cả app (15/09/2026).

   Luật `[hidden]{display:none}` nằm trong bảng kiểu của TRÌNH DUYỆT, mà bảng
   đó thua mọi luật của trang. Nên bất cứ lớp nào khai `display` — `flex`,
   `grid`, `inline-block` — là phần tử ấy hiện ra kể cả khi đang mang `hidden`,
   và JS bật tắt `el.hidden` thì nhìn không thấy gì đổi. Từ ghế người dùng thì
   đó là "cái nút không ăn".

   Repo này đã dính năm lần và vá tay năm chỗ rời nhau (`.commitbar-error`,
   `.day`, `.day-toggle`, `.row`, `.tran-noi`), lần thứ năm là khối nút của bộ
   soi ảnh. Theo đúng luật đếm ở CLAUDE.md § "Chống rác" — bản thứ ba thì dừng
   lại gộp — nay chỉ còn MỘT luật, và mọi chỗ dùng `hidden` sau này tự đúng.

   `!important` ở đây là cố ý và là chỗ DUY NHẤT trong file được dùng nó: ẩn thì
   phải là ẩn, không có ngoại lệ nào đáng để một lớp đè lại. Muốn một phần tử
   hiện thì bỏ thuộc tính `hidden`, đừng chữa bằng CSS. */
[hidden]{display:none !important}
*{box-sizing:border-box}
/* iOS TỰ PHÓNG CHỮ — tắt đi, đây là lỗi "chữ siêu to" ngày 16/09/2026.
   Safari trên iPhone có một phép tên là Text Autosizing: gặp một khối rộng hơn
   màn hình, nó TỰ NHÂN cỡ chữ bên trong lên cho "dễ đọc". Bảng duyệt chi lương
   có mười một cột nowrap nằm trong vùng cuộn ngang — đúng cái nó nhắm tới — nên
   Safari thổi cột Họ tên lên gấp đôi trong khi CSS vẫn ghi 13,5px. Nhìn từ ghế
   người dùng: chữ to đến mức không thấy được cột nào khác, mà không có luật CSS
   nào giải thích được.
   Mấy bảng khác hẹp hơn nên chưa bao giờ chạm ngưỡng — đó là lý do chỉ một màn
   bị, và vì sao nhìn bằng mắt không ra.
   100% KHÔNG phải cấm phóng to: người dùng vẫn chụm hai ngón phóng được như
   thường (cấm là `user-scalable=no`, và ta không dùng). Nó chỉ tắt phần TỰ
   ĐỘNG. */
html{-webkit-text-size-adjust:100%; text-size-adjust:100%}
body{margin:0; background:var(--bg); color:var(--ink);
  font-family:"Be Vietnam Pro",system-ui,-apple-system,"Segoe UI",sans-serif; font-size:15px; line-height:1.55}
h1,h2,h3{margin:0; font-weight:800; letter-spacing:-.02em; text-wrap:balance}
/* Tiêu đề trang LUÔN cách phần dưới 20px — Tấn Phú chốt 15/09/2026. Trước đó
   h1 margin:0 nên câu mô tả dính sát tiêu đề. Đặt ở h1 chứ không ở .page-head
   để màn nào cũng được, kể cả màn đăng nhập không có .page-head. */
h1{font-size:clamp(24px,4vw,34px); line-height:1.15; margin-bottom:20px} h2{font-size:19px} h3{font-size:15px; font-weight:600}
p{margin:0; max-width:66ch}
a{color:var(--expense)}
button{font:inherit; cursor:pointer}
:focus-visible{outline:2px solid var(--expense); outline-offset:2px}
.muted{color:var(--muted)} .small{font-size:12.5px}
.eyebrow{font-size:11px; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--muted)}
/* KHUNG NỘI DUNG — `.page` là màn bộ token cũ, `.noi-dung` là màn Nocturne.
   Vẫn hai lớp vì mật độ BÊN TRONG khác nhau (thẻ Nocturne theo thang 0,70×),
   nhưng cái KHUNG thì một: cùng bề rộng, cùng lề, cùng khoảng hở trên và dưới.
   Trước 16/09/2026 hai lớp này khai hai bề rộng (1020 và 1400) và hai lề (20
   và 12), nên chuyển màn là cả trang xê dịch. */
.page,.noi-dung{max-width:var(--khung-rong); margin-inline:auto;
  padding-inline:var(--khung-le); padding-block:20px 64px}
.page{display:flex; flex-direction:column; gap:22px}
.page.narrow{max-width:720px}
/* Chế độ NHÚNG — trang đang nằm trong iframe của hộp "+" ở báo cáo Sản xuất
   theo ngày (?embed=1, xem Views/Shared/_Layout.cshtml). Không có thanh trên
   nên phần đệm 32px/80px của .page chỉ còn là khoảng trắng ăn mất chiều cao
   hộp; và nền phải TRONG SUỐT để mặt hộp không bị hai lớp nền chồng nhau. */
body.nhung{background:transparent}
body.nhung .page{padding-block:4px 20px; padding-inline:2px; max-width:none}
body.nhung h1{font-size:20px; margin-bottom:12px}
body.nhung .page-head .eyebrow{display:none}
.page-head{display:flex; gap:18px; align-items:flex-start; flex-wrap:wrap}
.page-head > div:first-child{flex:1; min-width:min(100%,300px)}

/* ── THANH TRÊN — MỘT bản cho MỌI màn ──────────────────────────────────────
   Trước 16/09/2026 có HAI bản: bản này (bộ token cũ) và một bản đè lại trong
   `nocturne.css`. Hai bản là hai chỗ để lệch nhau, và đã lệch hẳn:

   - màn Nocturne (Tồn kho, bốn báo cáo, Tính giá mua, Lương): thanh 51px, tràn
     cả bề ngang, mười hai mục chữ 13px nằm gọn MỘT hàng;
   - màn còn lại (Phiên duyệt, Duyệt chi phí, Nhập liệu bằng AI, Người dùng,
     Nhật ký, Đồng bộ): thanh bó trong 1020px với chữ 14px — không đủ chỗ nên
     nav bị co, nhãn vỡ hai dòng ("Tồn / kho", "Nhập liệu bằng / AI") và khối
     người dùng tụt xuống hàng thứ ba. Thanh cao gấp ba, nội dung tụt theo.

   Nay bản DUY NHẤT nằm ở đây — `nocturne.css` không đè một dòng nào nữa. Ở đây
   chứ không ở file kia vì `fqfvn.css` luôn được nạp: vỏ trang không được phụ
   thuộc vào một file CSS thứ hai, thứ đã hai lần không tới được iPhone của Tấn
   Phú (xem cms/CLAUDE.md § "Giao diện").

   Thanh KHÔNG dính đỉnh (Tấn Phú chốt 14/09/2026) — đừng thêm position:sticky. */
/* Thanh khai ĐỦ nền chữ của chính nó — phông, cỡ, dòng, màu, cách vẽ nét — chứ
   không thừa hưởng từ body. `body.nocturne` đặt body sang Inter 14px/1,55, màu
   #241f1e và -webkit-font-smoothing:antialiased; bộ token cũ thì Be Vietnam Pro
   15px/1,55 màu #151d1a. Để inherit là hai màn ra hai cái thanh hơi khác nhau ở
   đúng những chỗ khó chỉ tên: chữ đậm hơn một chút, tên người sẫm hơn một chút.
   Đo bằng getComputedStyle chứ không bằng mắt — xem phần kiểm ở cuối commit. */
.topbar{background:var(--surface); border-bottom:1px solid var(--line);
  font-family:"Be Vietnam Pro",system-ui,-apple-system,"Segoe UI",sans-serif;
  font-size:13px; line-height:1.4; color:var(--ink);
  -webkit-font-smoothing:antialiased}
/* Thanh TRÀN cả bề ngang cửa sổ, không thụt vào bằng --khung-rong như nội dung:
   mười hai mục chữ cần trọn chỗ, và bó nó lại chính là cái đã làm nav vỡ hàng. */
.topbar-inner{min-height:var(--cao-thanh-tren); padding:var(--dem-thanh) var(--khung-le);
  display:flex; align-items:center; gap:18px; flex-wrap:nowrap}
/* MỌI luật màu chữ của thanh phải ĐÈ ĐƯỢC `body.nocturne a` và
   `body.nocturne a:hover` trong nocturne.css — đó là màu link của cả trang, và
   file đó nạp SAU file này. `.brand` trần (một lớp) thua nó; `.topbar nav a`
   BẰNG ĐIỂM nên cũng thua vì nạp sau. Hậu quả đã thấy thật: tên app, tên người
   và cả mười hai mục trên thanh hoá ĐỎ ở màn Nocturne.
   Nên: thêm `.topbar` cho con mang lớp, và mục nav mang lớp `nav-item` riêng
   (xem Views/Shared/_NavLink.cshtml). Đừng gỡ mấy tiền tố này ra cho gọn. */
/* Chấm đỏ + chữ hoa giãn: bản nhận diện của hệ Nocturne, nay dùng cho mọi màn. */
.topbar .brand{flex:none; display:flex; align-items:center; gap:8px; white-space:nowrap;
  font-weight:500; font-size:14px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--ink); text-decoration:none}
.topbar .brand::before{content:""; width:7px; height:7px; flex:none; border-radius:50%;
  background:var(--expense)}
/* nav ăn HẾT chỗ còn lại (flex:1) chứ không chỉ rộng bằng nội dung. Hai việc:
   mép mờ ở dưới rơi vào chỗ TRỐNG khi mọi mục còn vừa, nên không màn nào bị mờ
   đuôi chữ; và min-width:0 cho phép nav co lại để CUỘN, thay vì đẩy khối người
   dùng ra khỏi thanh. */
/* gap 12px, không phải 16px: đo ở Chromium với đủ mười hai mục — 16px thì nav
   cần 1095px, laptop 1366px chừa đúng 1095px nên mục cuối dính mép mờ; 12px thì
   cần 1051px, còn thừa 44px, và màn 1280px cũng vừa. Rộng hơn nữa thì đẹp hơn
   một chút mà hai khổ laptop phổ biến nhất phải cuộn — không đáng. */
.topbar nav{flex:1 1 auto; min-width:0; display:flex; gap:12px; font-size:13px;
  overflow-x:auto; overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch; scrollbar-width:none;
  /* Mờ dần mép phải: dấu hiệu duy nhất nói "còn nữa, kéo tiếp" khi đã giấu
     thanh cuộn. Không có nó thì mục ngoài khung coi như không tồn tại. Cửa sổ
     hẹp trên máy tính cũng cần, không riêng điện thoại — nên luật này nằm
     NGOÀI khối @media, khai một lần cho cả hai khổ máy. */
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 26px),transparent);
  mask-image:linear-gradient(90deg,#000 calc(100% - 26px),transparent)}
.topbar nav::-webkit-scrollbar{display:none}
/* nowrap: nhãn thà bị cuộn ra ngoài chứ KHÔNG vỡ hai dòng. */
.topbar a.nav-item{flex:0 0 auto; color:var(--muted); text-decoration:none; white-space:nowrap}
.topbar a.nav-item:hover{color:var(--expense)}
/* Đứng ở mục con thì mục CHA cũng sáng — hai bộ chọn một luật, không chép đôi
   cặp màu ra hai chỗ. `.cum-dang-o` do `_Layout` gắn khi màn đang xem là một
   trong sáu mục con; menu con đóng lại khi rời chuột, nên thiếu nó thì cả thanh
   không có mục nào sáng và màn Chi phí trông như một trang ngoài menu. */
.topbar a.nav-item.dang-o,
.topbar .nav-cha.cum-dang-o a.nav-item{color:var(--expense); font-weight:600}
/* Máy tính KHÔNG hiện icon — thanh chữ đọc nhanh hơn. Icon chỉ bật ở bản điện
   thoại, xem @media bên dưới. */
.topbar a.nav-item .ic{display:none}
.topbar .who{flex:none; margin-left:auto; display:flex; align-items:center; gap:10px; font-size:12.5px}
.topbar .who-name{font-weight:600; color:inherit; text-decoration:none; white-space:nowrap}
.topbar .who-name:hover{color:var(--expense)}
.topbar .who-role{color:var(--muted); font-size:11.5px; white-space:nowrap}
/* MÁY TÍNH HẸP (laptop 1366px) — bỏ nhãn vai, giữ cả thanh trên MỘT hàng.
   Đo ở 1366px: mười hai mục cần 1035px, thanh chỉ chừa 993px — thiếu 42px, nên
   mục "Người dùng" nằm sau mép mờ và phải kéo mới thấy. Nhãn vai ("Chủ / điều
   hành") rộng chừng 105px và là thứ ít cần nhất trên thanh: tên người đã nói ai
   đang đăng nhập, còn vai thì mở màn Người dùng là thấy. Bỏ nó ra là đủ chỗ.
   Dưới 860px nhãn vai vốn đã bị ẩn, xem khối @media của bản điện thoại. */
@media (max-width:1439px){ .topbar .who-role{display:none} }
/* Bo 4px như hệ Nocturne, nhưng GIỮ hover đỏ của bản cũ: bấm là mất phiên, mà
   đỏ là màu duy nhất trong app nói "việc này có hậu quả". */
.topbar .signout{background:none; border:1px solid var(--line); border-radius:4px;
  padding:4px 10px; color:var(--muted); font-size:12px}
.topbar .signout:hover{border-color:var(--danger); color:var(--danger)}
/* In ra giấy thì bỏ thanh điều hướng — giấy không bấm được. Trước đây chỉ màn
   Nocturne có luật này; thanh đã dùng chung thì luật in cũng dùng chung. */
@media print{ .topbar{display:none} }

/* ── MỤC CÓ MENU CON ────────────────────────────────────────────────────────
   Hiện có ĐÚNG MỘT cụm, và nó đứng ĐẦU dải: Báo cáo → Tổng hợp · Tồn kho ·
   Lương · Chi phí · Bán hàng · Trái tươi (xem _Layout.cshtml).

   MẶC ĐỊNH là PHẲNG: cả cụm `display:contents` nên mục cha và mục con thành hai
   mục thường, đứng cạnh nhau trong dải. Đó là kiểu dùng trên điện thoại VÀ trên
   máy tính cửa sổ hẹp — vì dải nav cuộn ngang, và một khối cuộn thì CẮT MẤT mọi
   thứ đổ ra ngoài nó (`overflow-x:auto` kéo theo `overflow-y`, không có cách
   nào xin riêng trục dọc). Menu rê chuột đặt trong đó là menu không bao giờ
   thấy — đúng cái bẫy `overflow` đã ghi trong cms/CLAUDE.md § Giao diện.

   TỪ 1440px TRỞ LÊN mới thành menu rê chuột, và đó là khổ nav KHÔNG cần cuộn.
   Hết cuộn thì hết cắt, nên menu con đổ ra ngoài được. Cùng một mốc 1440px với
   luật ẩn nhãn vai ở trên — một mốc "máy tính rộng" cho cả thanh, đừng thêm mốc
   thứ hai.

   ĐO LẠI MỖI KHI THÊM MỤC: ở đúng 1440px thanh chừa 1055px cho nav, và mười một
   mục hiện tại ăn hết đúng 1055px — hết chỗ trống. Đo bằng
   `nav.scrollWidth - nav.clientWidth` trên màn thật; ra 0 là còn vừa. Vượt là mục cuối đè lên khối
   người dùng, và menu con bị cắt mà không có gì báo. Đó là lý do cụm Báo cáo có
   mặt: trước khi gom, nav cần 1190px và ba mục cuối tràn ra ngoài. Thêm mục nữa
   thì gom tiếp, đừng nới. */
.topbar .nav-group,.topbar .nav-sub{display:contents}
/* MỤC CHA CHỈ CÓ NGHĨA KHI CÓ MENU. Dưới 1440px cụm dàn phẳng và mọi mục con
   nằm thẳng trong dải — lúc đó cái cha là một link THỨ HAI trỏ vào đúng chỗ một
   mục con đã trỏ, tức một mục chiếm chỗ mà không dẫn đi đâu mới. Ẩn nó đi.
   Cụm nào lấy một MÀN THẬT làm cha thì đừng đặt lớp này — cha của nó là một mục
   dẫn đi đâu đó ở mọi khổ. */
.topbar .nav-cha{display:none}
/* Cha KHÔNG có lớp `dang-o` của riêng nó khi ta đứng ở một mục con — luật "đang
   ở đây" của _NavLink chỉ biết mục nó đang vẽ. `_Layout` gắn `cum-dang-o` khi
   màn đang xem nằm trong danh sách mục con; luật màu ở § trên nhận cả hai bộ
   chọn nên không có cặp màu thứ hai để trôi. */
@media (min-width:1440px){
  .topbar .nav-cha{display:flex; align-items:center; align-self:stretch}
  .topbar nav{overflow:visible; -webkit-mask-image:none; mask-image:none;
    align-self:stretch; align-items:center}
  .topbar .nav-group{display:flex; align-items:center; align-self:stretch;
    position:relative; flex:0 0 auto}
  /* Treo đúng MÉP DƯỚI của thanh: cụm đã cao bằng phần ruột (align-self:stretch),
     cộng thêm đệm dưới là tới mép. Thiếu phép cộng đó thì menu phủ lên 8px cuối
     của thanh và che luôn đường kẻ dưới. */
  .topbar .nav-sub{
    display:none; position:absolute; top:calc(100% + var(--dem-thanh)); left:-6px; z-index:40;
    flex-direction:column; min-width:180px; padding:6px;
    background:var(--surface); border:1px solid var(--line); border-radius:9px;
    box-shadow:var(--shadow);
  }
  /* NHỊP CẦU QUA DẢI HỞ — không có nó thì menu con KHÔNG TỚI ĐƯỢC BẰNG CHUỘT.
     Tấn Phú 19/09/2026: *"rê chuột khỏi cái Trái tươi là nó đóng luôn. k kịp rê
     chuột vào menu con"*.

     Dòng `top` ở trên treo menu thấp hơn đáy cụm đúng --dem-thanh (8px), và 8px
     ấy KHÔNG thuộc cụm lẫn menu. Chuột đi thẳng xuống là rơi vào đó, `:hover`
     tắt, menu `display:none` — mà đã none thì nó không còn hit-test được nữa,
     nên tới chỗ menu đáng lẽ nằm thì chuột chạm vào TRANG chứ không chạm menu.
     Đo ở 1600px trước khi sửa: cụm đáy y=43, menu đỉnh y=51, và mọi điểm
     y=43..50 đều làm menu biến mất. Không phải "không kịp" — là không có đường.
     (Vẩy chuột thật nhanh thì thỉnh thoảng qua được, vì con trỏ nhảy cóc qua cả
     dải. Đó là lý do lỗi này trông như lúc được lúc không.)

     Cầu là ::before của chính .nav-sub nên nó nằm TRONG .nav-group — chạm vào
     cầu thì `.nav-group:hover` vẫn đúng. Nó chỉ tồn tại khi menu đang mở, vì
     ::before của một khối display:none thì không dựng ra, nên lúc menu đóng nó
     không chắn cái gì. Rộng bằng menu (left/right:0) nên đi thẳng xuống từ bất
     kỳ đâu trên mục cha đều rơi vào cầu.

     ĐỪNG bỏ cầu này để "cho gọn" khi sửa `top`: hễ menu còn treo cách đáy cụm
     một khoảng thì khoảng đó còn là vùng chết. Bỏ hở (top:100%) thì menu phủ
     lên 8px cuối của thanh và che đường kẻ dưới — xem ghi chú ở `top` trên.

     CAO HƠN DẢI HỞ MỘT CHÚT, cố ý. `bottom:100%` của một khối absolute đo theo
     PADDING BOX của .nav-sub, tức đã trừ viền 1px — nên đúng bằng --dem-thanh
     thì cầu phủ y=44..52 mà vùng chết bắt đầu từ y=43, hụt 1px và hỏng y như
     không có cầu (đo thật, 19/09/2026). Phần dôi ra chỉ chồng lên phần ĐỆM dưới
     của cụm, không chồng lên thẻ <a> của mục cha, nên không cướp cú bấm nào. */
  .topbar .nav-sub::before{
    content:""; position:absolute; left:0; right:0; bottom:100%;
    height:calc(var(--dem-thanh) + 4px);
  }
  /* :focus-within chứ không chỉ :hover — thiếu nó thì người đi bằng bàn phím
     không có đường nào mở menu, mà mục con là màn duy nhất sau nó. */
  .topbar .nav-group:hover .nav-sub,
  .topbar .nav-group:focus-within .nav-sub{display:flex}
  .topbar .nav-sub a.nav-item{padding:6px 8px; border-radius:6px}
  .topbar .nav-sub a.nav-item:hover{background:var(--sunken)}
}

/* ── Thanh điều hướng trên ĐIỆN THOẠI — thu về một dải ICON cuộn ngang ──────
   Mười ba mục chữ ở 390px xuống dòng ba lần, chiếm gần một phần ba màn hình và
   đẩy nội dung tụt hẳn xuống. Thu thành icon + chữ nhỏ thì cả mười hai nằm trong
   một dải cao 48px, cuộn ngang tới mục cuối.

   Cùng MỘT thẻ <a> với bản máy tính, chỉ khác CSS — hai bản menu riêng là hai
   chỗ để lệch nhau khi thêm màn mới. Phần cuộn và mép mờ đã khai ở trên, khối
   này chỉ còn phần KHÁC: xếp dọc icon trên chữ. */
@media (max-width:860px){
  /* Cho xuống hàng trở lại: nav thành một dải riêng chiếm trọn hàng dưới. */
  .topbar-inner{flex-wrap:wrap; gap:10px}
  .topbar nav{order:3; width:100%; gap:2px}
  .topbar a.nav-item{min-width:58px;
    display:flex; flex-direction:column; align-items:center; gap:3px;
    padding:6px 7px 5px; border-radius:8px;
    font-size:10px; line-height:1.15; text-align:center}
  .topbar a.nav-item .ic{
    display:block; width:21px; height:21px;
    fill:none; stroke:currentColor; stroke-width:1.7;
    stroke-linecap:round; stroke-linejoin:round;
  }
  /* Đang ở màn nào thì tô nền — chữ 10px mà chỉ đổi màu thì nhìn không ra. */
  .topbar a.nav-item.dang-o{background:var(--sunken); font-weight:600}
  .topbar .who{gap:7px; font-size:12px}
  .topbar .who-role{display:none}
}

/* ── MENU ĐIỆN THOẠI — danh sách CHA – CON ───────────────────────────────────
   Dải icon cuộn ngang giấu mục cuối sau mép mờ, và dàn phẳng cụm Báo cáo nên
   sáu màn đọc nằm lẫn với mấy màn nhập liệu. Tấn Phú 20/09/2026: *"Cấu trúc lại
   menu cho mobile. Giống cấu trúc desktop cha con."*

   Nút `.nav-nut` mở một PANEL DỌC ngay dưới thanh, giữ nguyên hình cha–con của
   khổ ≥1440px. Không phải `<dialog>`: panel này chỉ là một danh sách link, mà
   `showModal` thì khoá nền và bắt Esc — nặng hơn việc nó làm.

   TÔ THÊM: mọi luật ở đây treo sau `html.co-menu`, lớp do JS đặt. Máy không chạy
   được JS thì không có lớp ấy, nút vẫn `hidden`, và dải cuộn ngang cũ còn
   nguyên — đường đi không mất (§25). */
.nav-nut{display:none}
@media (max-width:860px){
  html.co-menu .topbar .nav-nut{
    display:flex; align-items:center; gap:7px; margin-left:auto;
    min-height:40px; padding:6px 12px;
    background:none; border:1px solid var(--line); border-radius:8px;
    color:var(--ink); font:600 13px/1 inherit;
    -webkit-tap-highlight-color:transparent; touch-action:manipulation}
  html.co-menu .topbar .nav-nut .ic{width:18px; height:18px; fill:none;
    stroke:currentColor; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round}
  /* Một nút, hai mặt: ba gạch khi đóng, dấu nhân khi mở. Đổi ICON chứ không đổi
     chữ — chữ "Menu" đứng yên thì mắt không phải đọc lại nút mỗi lần bấm. */
  html.co-menu .topbar .nav-nut .ic-dong{display:none}
  html.co-menu .topbar.mo-menu .nav-nut{border-color:var(--expense); color:var(--expense)}
  html.co-menu .topbar.mo-menu .nav-nut .ic-mo{display:none}
  html.co-menu .topbar.mo-menu .nav-nut .ic-dong{display:block}
  /* Chỗ người dùng lùi lên hàng trên cùng với nút, nhường trọn hàng dưới cho panel. */
  html.co-menu .topbar .who{margin-left:0}

  html.co-menu .topbar nav{display:none}
  html.co-menu .topbar.mo-menu nav{
    display:flex; flex-direction:column; align-items:stretch; gap:1px;
    order:3; width:100%;
    /* Panel KHÔNG cuộn ngang nữa, nên bỏ mặt nạ mờ mép phải — để lại thì nó
       xoá mờ chữ ở cạnh phải của một danh sách dọc, trông như lỗi vẽ. */
    overflow-x:visible; -webkit-mask-image:none; mask-image:none;
    /* Dài hơn màn thì cuộn TRONG panel, đừng đẩy nội dung trang đi đâu. */
    max-height:min(70vh,520px); overflow-y:auto;
    padding-top:6px; border-top:1px solid var(--line)}

  /* CHA – CON. Ngoài panel cụm vẫn `display:contents` (dàn phẳng vào dải), nên
     ở đây phải dựng lại thành khối, và mục cha — vốn `display:none` ở mọi khổ
     dưới 1440px — hiện lại làm đầu mục. */
  html.co-menu .topbar.mo-menu .nav-group{display:flex; flex-direction:column}
  html.co-menu .topbar.mo-menu .nav-cha{display:block}
  html.co-menu .topbar.mo-menu .nav-cha a.nav-item{font-weight:700; color:var(--ink)}
  html.co-menu .topbar.mo-menu .nav-sub{
    display:flex; flex-direction:column;
    /* Vạch dọc nói "mấy mục này thuộc mục trên" — thụt lề một mình thì ở danh
       sách dài người đọc không còn nhớ chúng thụt so với cái gì. */
    margin-left:13px; padding-left:11px; border-left:2px solid var(--line)}

  /* Mục trong panel là MỘT HÀNG: icon trái, chữ phải, cao 44px cho vừa ngón tay.
     Khác hẳn mục trong dải ngang (icon trên, chữ 10px ở dưới). */
  html.co-menu .topbar.mo-menu a.nav-item{
    flex-direction:row; justify-content:flex-start; align-items:center; gap:11px;
    width:100%; min-height:44px; padding:8px 10px;
    font-size:14px; line-height:1.3; text-align:left; white-space:normal}
  html.co-menu .topbar.mo-menu a.nav-item .ic{width:19px; height:19px; flex:none}
}

/* ── THANH VỊ TRÍ — "đang ở trang nào" ─────────────────────────────────────
   Tấn Phú 21/09/2026: *"Thêm navigation bar (đang ở trang nào) trên top mỗi
   page. Và title trang."* Tám màn Nocturne mở thẳng vào bảng số, không một dòng
   nào nói tên màn; và từ lúc dải nav gấp vào sau nút Menu thì cái mục đang sáng
   — thứ duy nhất nói "bạn đang ở đâu" — cũng không còn nhìn thấy trên điện
   thoại. Dựng markup ở `_ThanhViTri.cshtml`, luật nhóm ở `Models/ViTri.cs`.

   KHÔNG dính đỉnh, như thanh trên (Tấn Phú chốt 14/09/2026). Trên điện thoại
   một thanh dính là một phần màn hình mất hẳn, mà màn hẹp thì bảng số cần từng
   dòng một.

   Thanh khai ĐỦ nền chữ của chính nó — cùng lý do đã ghi ở `.topbar`: để inherit
   thì hai bộ token ra hai cái thanh hơi khác nhau ở đúng chỗ khó chỉ tên. */
.thanh-vi-tri{background:var(--surface); border-bottom:1px solid var(--line);
  font-family:"Be Vietnam Pro",system-ui,-apple-system,"Segoe UI",sans-serif;
  -webkit-font-smoothing:antialiased}
/* Cùng lề với nội dung (--khung-le) nhưng TRÀN cả bề ngang như thanh trên: hai
   vạch kẻ chồng nhau mà một cái thụt vào thì trông như lệch chứ không như tầng. */
.vi-tri-trong{display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 7px;
  padding:7px var(--khung-le) 8px; line-height:1.3}
.thanh-vi-tri .vi-tri-chang{font-size:13px; color:var(--muted); text-decoration:none}
/* Chặng CUỐI là tên màn — đây cũng là TIÊU ĐỀ trang ở tám màn Nocturne, vì
   chúng không có <h1> nào khác. Đậm và sẫm hơn hẳn mấy chặng trước, để mắt bắt
   được tên màn trong một nhịp mà không cần đọc cả dòng. */
.thanh-vi-tri .vi-tri-nay{font-size:15px; font-weight:700; color:var(--ink)}
.thanh-vi-tri a.vi-tri-chang:hover{color:var(--expense); text-decoration:underline}
.thanh-vi-tri .vi-tri-gach{font-size:13px; color:var(--muted); opacity:.55}
/* Chế độ NHÚNG giấu cả thanh này — view đã không dựng nó, luật đây chỉ để thanh
   không mọc lại nếu sau này có ai dựng thẳng markup vào một trang nhúng. */
body.nhung .thanh-vi-tri{display:none}

/* đăng nhập */
.centered{min-height:100vh; display:grid; place-items:center; padding:24px}
.login-card{background:var(--surface); border:1px solid var(--line); border-radius:14px; box-shadow:var(--shadow);
  padding:32px; width:min(100%,400px); display:flex; flex-direction:column; gap:14px}
.login-card h1{font-size:34px; color:var(--expense)}
.login-card label,.field-wide{display:flex; flex-direction:column; gap:5px; font-size:12.5px;
  font-weight:600; color:var(--muted)}
input,textarea,select{font:inherit; font-size:14px; color:var(--ink); background:var(--surface);
  border:1.5px solid var(--line); border-radius:8px; padding:9px 11px; width:100%; font-weight:400}
input:focus,textarea:focus,select:focus{border-color:var(--expense); outline:none}

/* Ô NGÀY — iPhone vẽ kiểu KHÁC, và kiểu đó tràn ra khỏi ô chứa (17/09/2026).

   Tấn Phú chỉ hai ô "Mua ngày" / "Đóng ngày" ở màn Thu Múi: chúng rộng hơn hẳn
   mấy ô chọn nằm ngay trên, thò hẳn sang phải. Chromium KHÔNG dựng lại được —
   đo ở 375 và 390px thì ô ngày rộng đúng bằng ô chọn, không lòi một pixel nào.
   Đây là Safari trên iOS tự vẽ `input[type=date]` thành một điều khiển gốc: nó
   có bề rộng nội tại theo chuỗi ngày đã định dạng, canh GIỮA chữ, và không co
   xuống theo ô chứa. Cùng họ với hai ca đã ghi ở cms/CLAUDE.md — chữ tự phóng,
   tệp CSS thứ hai không tới nơi: chỉ máy thật mới thấy.

   Bốn dòng, mỗi dòng chặn một đường:

   · `appearance:none` — bỏ điều khiển gốc, ô ngày thành một hộp như mọi ô khác
     và nghe `width`. Hộp chọn ngày vẫn mở như thường khi chạm: đó là hành vi
     gốc của trình duyệt, không phải thứ do lớp vẽ giữ.
   · `max-width:100%` — chốt chặn cuối. Dù máy nào còn tính ra bề rộng nội tại
     lớn hơn ô chứa thì nó vẫn bị kẹp lại; đây là dòng DUY NHẤT kiểm được ở
     Chromium, và `kiem-o-ngay.mjs` kiểm đúng nó.
   · `min-width:0` — ô ngày là mục flex của `.field`; mục flex không tự co
     xuống dưới bề rộng nội tại nếu không nói.
   · `min-height` + `::-webkit-date-and-time-value` — bỏ appearance thì phần
     ruột giữ giá trị mất luôn cỡ gốc của nó, có máy vẽ ra ô dẹp lép. Đặt theo
     `em` để mỗi chỗ tự co theo cỡ chữ của chính nó (ô trong `.field` 13,5px,
     ô ngoài 14px). 2,6em bằng đúng chiều cao ô chữ bên cạnh, đo ở Chromium.

     ĐỪNG DỌN HAI DÒNG NÀY ĐI vì thấy bỏ chúng mà `kiem-o-ngay.mjs` vẫn xanh:
     Chromium tự vẽ ô ngày cao 37px, tức đã trên cái sàn 2,6em, nên cái sàn
     không bao giờ chạm tới ở đây. Nó là bảo hiểm cho iOS — đúng cái máy mà
     bài kiểm này không với tới được.

   Canh TRÁI vì mọi ô khác của app canh trái — iOS canh giữa, mà hai ô ngày canh
   giữa nằm giữa một cột ô canh trái thì nhìn như bị lệch. */
input[type="date"],input[type="datetime-local"],input[type="month"],
input[type="time"],input[type="week"]{
  -webkit-appearance:none; appearance:none;
  min-width:0; max-width:100%; min-height:2.6em; text-align:left}
input[type="date"]::-webkit-date-and-time-value,
input[type="datetime-local"]::-webkit-date-and-time-value,
input[type="month"]::-webkit-date-and-time-value,
input[type="time"]::-webkit-date-and-time-value,
input[type="week"]::-webkit-date-and-time-value{text-align:left; min-height:1.2em}

/* Ô NGÀY KIỂU VIỆT — vỏ do JS dựng, xem `_Layout.cshtml` § "Ô NGÀY KIỂU VIỆT".

   Hình: một ô CHỮ đọc/gõ `dd/MM/yyyy`, và cái lịch 📅 ở mép phải. Ô ngày GỐC
   nằm TRONG SUỐT đè lên chính cái lịch đó — chạm vào là bộ chọn ngày gốc của
   máy mở ra, không phải một bộ lịch tự vẽ. Tự vẽ một bộ lịch là bỏ mất bánh xe
   ngày của iPhone, thứ người ở kho quay bằng ngón cái trong hai giây.

   Không có JS thì không có vỏ này, ô ngày gốc hiện y như cũ — nên mọi luật ở
   trên vẫn phải đúng, đừng gộp hai khối làm một.

   `--muted` và `--line` khai ngay trong tệp này (luật ở TokenCssTests). */
.o-ngay{position:relative; display:block; width:100%; min-width:0}
.o-ngay .o-ngay-chu{width:100%; padding-right:40px; font-variant-numeric:tabular-nums}
.o-ngay .o-ngay-chu.hong{border-color:var(--danger)}
.o-ngay .o-ngay-lich{position:absolute; top:0; right:0; bottom:0; width:38px;
  display:flex; align-items:center; justify-content:center;
  font-size:14px; color:var(--muted); border-left:1px solid var(--line); pointer-events:none}
/* Ô gốc: bằng đúng ô lịch, trong suốt, NẰM TRÊN. `min-height:0` gỡ cái sàn
   2,6em ở luật chung — ở đây nó chỉ là một vùng chạm, không phải một ô để đọc. */
.o-ngay input[type="date"]{position:absolute; top:0; right:0; bottom:0; width:38px; height:auto;
  min-height:0; padding:0; margin:0; border:0; background:none; opacity:0; cursor:pointer}
.o-ngay.khoa input[type="date"]{display:none}
.o-ngay.khoa .o-ngay-lich{opacity:.35}
textarea{font-family:"JetBrains Mono",ui-monospace,monospace; font-size:13px; resize:vertical}
.btn-primary{background:var(--expense); color:var(--bg); border:0; border-radius:9px; padding:11px 20px;
  font-weight:600; font-size:14.5px; text-decoration:none; display:inline-block; text-align:center}
.btn-primary:hover{filter:brightness(1.1)}
.btn-primary:disabled{opacity:.5; cursor:not-allowed}
/* Nút LƯU SỬA: cùng khuôn với .btn-primary, chỉ khác màu.

   Cùng khuôn vì nó cũng là "bấm để làm một việc", không phải một mẩu thông báo —
   trước đây nó là <button> trần, trình duyệt tự vẽ thành mẩu bo tròn xam xám
   giống hệt một dải thông báo, nên người duyệt sửa xong tưởng đã lưu rồi bỏ đi.

   Khác màu vì nó KHÁC VIỆC: đỏ (--expense) dành cho "ghi lên sổ" — việc không
   lùi được. Lưu sửa chỉ động vào phiên, lùi được, nên xanh. Hai việc khác hẳn
   nhau thì đừng cho chung một màu, nhất là khi một trong hai không có đường lùi. */
.btn-luu{background:var(--luu); color:#fff; border:0; border-radius:9px; padding:11px 20px;
  font-weight:600; font-size:14.5px; text-decoration:none; display:inline-block; text-align:center}
.btn-luu:hover{filter:brightness(1.1)}
.btn-luu:disabled{opacity:.5; cursor:not-allowed}
/* Nút CẢNH: "thôi, dừng cái đang chạy lại". Chỗ dùng: nút Huỷ / Thôi chờ /
   Thôi dò của hộp tiến trình (Views/Shared/_HopGui.cshtml) — một nút, ba nhãn,
   tám hộp trên năm màn.

   Vàng vì nó KHÁC VIỆC với hai nút kia: đỏ là "ghi lên sổ" (không lùi được),
   xanh là "lưu sửa". Nút này không tạo ra gì cả, nó cắt ngang một việc đang
   chạy — đúng nghĩa cảnh báo.

   Và vì để <button> trần thì trình duyệt tự vẽ thành mẩu xám 28px trông như
   một dải chú thích chứ không như nút bấm. Đây là lần thứ BA repo dính đúng cú
   đó (xem body.nocturne button.xem-them ở nocturne.css).

   Chữ ĐEN trên vàng, không phải trắng: --ink trên #ffc045 đo được 10,9:1, chữ
   trắng chỉ 1,6:1 — cùng con số cms/CLAUDE.md đã ghi ở thang huy hiệu. Rê chuột
   thì SẪM đi chứ không sáng lên như .btn-primary: nền đã sáng sẵn, sáng thêm
   nữa là nhạt đi chứ không ai thấy nó phản hồi. */
.btn-canh{background:var(--warn-manh); color:var(--ink); border:0; border-radius:8px;
  padding:9px 18px; font-weight:600; font-size:14px}
.btn-canh:hover{filter:brightness(.94)}
.btn-canh:disabled{opacity:.5; cursor:not-allowed}
/* Nút PHỤ: việc của nó là KHÔNG tạo ra gì và KHÔNG xoá gì — đóng hộp lại, trả ô
   về số máy đọc, đổi cái đang xem. Viền chứ không tô đặc, đúng lý do hệ Nocturne
   đã chọn cho `button.xem-them`: ba màu tô đặc kia đều nói "bấm là có chuyện xảy
   ra", còn nút này thì không.

   Cùng hình với `body.nocturne button.xem-them` (viền, nền trắng, bo 8px, cao
   40px, chữ 600/13,5px) — cố ý, để hai hệ giao diện có CÙNG một nút phụ. Khác
   đúng một chỗ: viền 1,5px thay vì 1px, cho khớp ô nhập của bộ token cũ đang
   đứng ngay trên nó trong hộp "Sửa số". */
.btn-phu{min-height:40px; padding:0 16px; border:1.5px solid var(--line); border-radius:8px;
  background:var(--surface); color:var(--ink); font-weight:600; font-size:13.5px}
.btn-phu:hover{border-color:var(--expense); color:var(--expense)}
.btn-phu:disabled{opacity:.5; cursor:not-allowed}
/* Nút phụ đứng trên thẻ `<a>` (đường dẫn trông như nút): thẻ `a` là inline nên
   min-height và đệm dọc ở trên không ăn — ra một mẩu chữ gạch chân, đúng họ "nút
   xám" repo đã dính ba lần. Khai RIÊNG cho `a`, không đụng `<button>` nào đang
   chạy: đổi display của luật chung là đổi cách vẽ mọi nút phụ sẵn có. */
a.btn-phu{display:inline-flex; align-items:center; justify-content:center; text-decoration:none}
/* Ô CHỌN TỆP GỌN — MỘT nút + tên tệp đã chọn (19/09/2026).
   Dùng cho form chỉ nhận ĐÚNG MỘT tệp và không cần chụp ảnh: ô đối chiếu .xlsx
   của trang Tình trạng sổ. `_FilePicker` là ô chọn ẢNH — camera, dán clipboard,
   kéo-thả, xem trước nhiều tấm, 472 dòng JS — đưa nó vào đây là bày một nút
   "Dán từ Clipboard" cho người đang đi tìm một file bảng tính.

   Nhưng NÚT THÌ MƯỢN NGUYÊN `.btn-phu` ở trên, không vẽ lại: bấm nó không tạo
   ra gì cũng không xoá gì, đúng nghĩa nút phụ. Vẽ lại là bản thứ hai của một
   kiểu nút, và bản thứ hai thì trôi.

   VÌ SAO KHÔNG ĐỂ `<input type="file">` TRẦN: nó ra nút xám mặc định của trình
   duyệt, chữ ANH ("Choose File / No file chosen") giữa một màn tiếng Việt, và
   mỗi trình duyệt vẽ một kiểu. Đây là lần thứ NĂM repo dính cú "nút xám" —
   xem cms/CLAUDE.md § "BỐN KIỂU NÚT".

   Hai chỗ KHÔNG được sửa, cả hai đã ghi ở khối `.picker` bên trên và cả hai
   hỏng trong im lặng:

   1. Ô thật KHÔNG dùng `display:none`. Chrome coi ô ẩn hẳn là "không focus
      được" nên gặp `required` là nó CHẶN gửi form mà không hiện lời nào —
      mà ô này CÓ `required`. 1px + opacity:0 thì vẫn focus được, và bàn phím
      vẫn tab tới được.
   2. KHÔNG có `pointer-events:none`. Nút là `<label for>`, và trình duyệt chỉ
      chuyển cú chạm từ label sang ô khi ô đó còn chạm được. */
.chon-tep{display:flex; flex-wrap:wrap; gap:10px; align-items:center}
.chon-tep input[type="file"]{position:absolute; width:1px; height:1px;
  opacity:0; margin:0; padding:0; border:0}
/* Ô thật đang có nét thì nút phải sáng lên — nếu không, người dùng bàn phím tab
   tới đây và không thấy nét đâu cả. */
.chon-tep input[type="file"]:focus-visible + label{border-color:var(--expense);
  color:var(--expense); outline:2px solid var(--expense); outline-offset:2px}
/* Tên tệp: rỗng lúc đầu, JS ở _Layout điền vào. Rỗng chứ không phải câu "chưa
   chọn tệp nào" — không có JS thì câu đó nằm đó NÓI DỐI sau khi người ta đã
   chọn, đúng cái bẫy "màn hình bày một đằng". */
.chon-tep-ten{font-size:13px; color:var(--muted); min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:22em}
.chon-tep-ten:empty{display:none}

/* Chip THÊM Ô — viền đứt là "chỗ này thêm được", khác hẳn nút phụ ở trên.
   Ở đây chứ KHÔNG ở khối <style> của Views/Output/Review.cshtml, nơi nó nằm tới
   16/09/2026: `_SoiChiaDoi` — partial dùng chung — đã mượn lớp này cho hai nút
   của hộp "Sửa số", nên bốn màn duyệt KHÔNG phải màn Sản lượng bày ra hai mẩu
   xám mặc định của trình duyệt. Kiểu của một loại NÚT thì thuộc về bảng kiểu
   dùng chung; `<style>` trong view chỉ giữ phần bố cục riêng của màn đó. */
.them-o{flex:none; border:1px dashed var(--line); background:transparent; border-radius:7px;
  padding:2px 8px; color:var(--muted); font-size:13px}
.them-o:hover{border-color:var(--expense); color:var(--expense)}
/* "+ Thêm dòng" và "× Bỏ" của bảng Từng người (màn Duyệt chi lương) mượn đúng
   chip này, không vẽ kiểu nút thứ hai. Nút thêm đứng một mình dưới bảng trong
   một `.card` xếp cột — không chặn thì nó giãn hết bề ngang — và to hơn cho
   ngón tay; nút bỏ đứng cạnh nhãn "thêm tay" trong ô Ghi chú. */
.them-o.them-dong{align-self:flex-start; padding:6px 14px; min-height:34px}
.them-o.bo-dong{margin-left:6px}
.alert{background:var(--danger-soft); color:var(--danger); border:1px solid var(--danger);
  border-radius:9px; padding:11px 14px; font-size:13.5px; font-weight:500}
/* Câu ĐẦU của dải cảnh báo sheet (so_sheet.canh_bao) — khai HẲN 700 chứ đừng
   trông vào `bolder` mặc định: `bolder` tính TƯƠNG ĐỐI với 500 của .alert, nên
   nó phụ thuộc bảng nhảy bậc của từng engine. Chữ đậm ở đây là thứ Tấn Phú yêu
   cầu đích danh, không phải chi tiết trang trí. */
.alert strong{font-weight:700}

/* trang chủ + form nhập */
.card{background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:var(--shadow);
  padding:20px; display:flex; flex-direction:column; gap:16px}
.batch-list{display:flex; flex-direction:column; gap:10px}
.batch-card{background:var(--surface); border:1px solid var(--line); border-radius:11px; padding:14px 18px;
  display:flex; gap:16px; align-items:center; flex-wrap:wrap; text-decoration:none; color:inherit;
  box-shadow:var(--shadow)}
.batch-card:hover{border-color:var(--expense)}
.batch-id{flex:1; min-width:220px; display:flex; flex-direction:column; gap:2px}
.batch-id .code{font-family:"JetBrains Mono",monospace; font-weight:700; font-size:13px; color:var(--expense)}
.batch-id .muted{font-size:12.5px}
.batch-count{display:flex; align-items:baseline; gap:6px}
.batch-count .big{font-family:"JetBrains Mono",monospace; font-weight:700; font-size:20px}
.batch-count .money{font-family:"JetBrains Mono",monospace; font-weight:700; font-size:13.5px; margin-left:10px}
.batch-tags{display:flex; gap:6px}
.empty{text-align:center; padding:60px 20px; display:flex; flex-direction:column; gap:16px;
  align-items:center; color:var(--muted)}
/* ── Ô chọn ảnh (_FilePicker.cshtml + js/file-picker.js) ─────────────────────
   Vỏ dùng chung của mọi màn gửi ảnh — Chụp giấy và bốn màn theo loại.
   Ô `<input type="file">` trần trụi là chỗ tệ nhất của cả app trên điện thoại:
   nút "Choose Files" cao 22px, chữ Anh, và người dùng KHÔNG biết bấm vào đó thì
   ra camera hay ra thư viện. Nên đây là vỏ: một vùng thả to, mấy nút nguồn rõ
   ràng, và ảnh đã chọn hiện ra ngay để soi lại.

   BẬC THANG, đọc theo đúng thứ tự này:

   ① chưa có JS → ô file NGUYÊN BẢN, hiện ra bình thường. Xấu nhưng chọn được.
   ② js/file-picker.js chạy → nó gắn `is-live` (thu ô còn 1px) rồi `is-touch`
     hoặc `is-mouse` (hiện đúng bộ nút).

   Vì sao bày đặt vậy chứ không giấu ô bằng CSS ngay từ đầu: giấu trước thì hôm
   nào tệp .js không tới được máy người dùng là màn gửi ảnh CHẾT HẲN — không nút
   nào bấm được, không ô nào thấy được. Chuyện "tệp thứ hai không tới nơi" đã
   xảy ra một lần với css/gia-mua.css trên iPhone của Tấn Phú, và không ai tìm
   ra vì sao. Ở bậc ① thì lúc đó chỉ là xấu.

   Cũng vì vậy mà touch/mouse chia bằng LỚP do JS gắn, không bằng
   `@media (pointer:coarse)`: điện thoại nào không khớp truy vấn đó — và có —
   là mất luôn nút "Chụp ảnh", đúng cái nút cần nhất ở kho. JS hỏi được ba
   đường (matchMedia, maxTouchPoints, ontouchstart), CSS chỉ hỏi được một.

   Ô thật KHÔNG dùng display:none — Chrome coi ô ẩn hẳn là "không focus được"
   nên gặp `required` là nó CHẶN gửi form mà không hiện lời nào. 1px + opacity:0
   thì vẫn focus được, bong bóng lỗi neo đúng góc vùng thả. */
/* ── CHÂN TRANG ────────────────────────────────────────────────────────────
   Hai dòng, canh giữa: tên xưởng kèm năm ở trên, mã bản đang chạy ở dưới.
   Xem _Layout.cshtml.

   Canh giữa đặt ở CHÍNH `.chan-trang`, không đặt lại ở từng dòng con: hai dòng
   canh theo hai luật riêng là thứ sẽ trôi lệch nhau lúc có người sửa một dòng.

   Mã bản mờ và bé, nhưng KHÔNG ẩn: lúc cần thì phải đọc được ngay mà không
   cần mở công cụ nào. */
.chan-trang{margin:24px 0 10px; text-align:center}
/* `margin-inline:auto` KHÔNG thừa, đừng dọn. Luật chung `p{max-width:66ch}` ở
   trên kẹp mọi đoạn văn lại cho dễ đọc; trên màn rộng thì hai dòng này hẹp hơn
   chân trang, và một khối hẹp hơn khối bao mà `margin` bằng 0 thì nằm SÁT TRÁI.
   `text-align:center` lúc đó chỉ canh chữ bên trong cái hộp lệch trái ấy, nên
   nhìn ra là chữ nằm bên trái màn — mà hai dòng hai phông nên ra hai bề rộng
   khác nhau, lệch nhau luôn. Đo ở 1280px trước khi sửa: 528px và 417px, cả hai
   bắt đầu ở x=0, tâm cách tâm màn hơn 370px.
   Trên điện thoại 66ch rộng hơn cả màn nên không ai thấy — lỗi chỉ hiện ở màn
   rộng. Xem Fqfvn.Cms.Tests/kiem-chan-trang.mjs. */
.chan-trang p{margin-inline:auto}
.chan-trang .hieu-xuong{margin-block:0; font-size:11.5px; font-weight:600; letter-spacing:.08em;
  color:var(--muted); opacity:.8}
.ma-ban{margin-block:3px 0; font-size:10.5px; letter-spacing:.04em;
  color:var(--muted); opacity:.65; font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
/* Chế độ NHÚNG: trang đang nằm trong iframe của hộp "+", mà trang BỌC nó đã có
   sẵn dòng tên xưởng ở chân — hiện thêm một dòng nữa là đọc thấy hai lần trong
   cùng một màn. Mã bản thì GIỮ: nó là thứ duy nhất nói được cái iframe đang
   chạy bản nào. */
body.nhung .chan-trang .hieu-xuong{display:none}

.picker{display:flex; flex-direction:column; gap:10px; min-width:0}
.picker-drop{position:relative; display:flex; align-items:center; gap:14px; flex-wrap:wrap;
  padding:13px 15px; border:1.5px dashed var(--line); border-radius:11px; background:var(--sunken)}
/* Vạch đứt của var(--line) trên nền var(--sunken) gần như không thấy. Trình nào
   không biết color-mix thì bỏ qua dòng này và giữ var(--line) ở trên. */
.picker-drop{border-color:color-mix(in srgb,var(--muted) 38%,var(--line))}
.picker.is-live .picker-drop{cursor:pointer}
.picker.is-live .picker-drop:hover{border-color:var(--expense)}
.picker.is-over .picker-drop{border-color:var(--expense); background:var(--expense-soft)}
/* Bậc ①: ô nguyên bản, đứng CUỐI khung (order) và chiếm cả hàng riêng, để nó
   không chen vào giữa cái icon và câu tiêu đề. */
.picker .picker-input{order:2; flex:0 0 100%}
/* Bậc ②. Hai lớp, không phải một: `.field input` của bảng sửa là (0,1,1) nên
   một lớp trần thua nó, và ô lại ăn padding 6px của cột nhập. */
/* KHÔNG có pointer-events:none ở đây — đừng thêm lại. Ba nút nguồn nay là
   `<label for>`, và trình duyệt chỉ chuyển cú chạm từ label sang ô khi ô đó còn
   chạm được. Đặt pointer-events:none là ba cái nút thành bấm không ăn gì, đúng
   lỗi ngày 16/09/2026. Ô rộng 1px và trong suốt nên chạm trúng nó cũng chỉ mở
   đúng cái hộp chọn mà người ta đang định mở. */
.picker.is-live .picker-input{position:absolute; left:15px; bottom:6px; width:1px; height:1px;
  opacity:0; margin:0; padding:0; border:0}
.picker-ic{flex:0 0 auto; width:26px; height:26px; color:var(--muted);
  fill:none; stroke:currentColor; stroke-width:1.6; stroke-linecap:round; stroke-linejoin:round}
.picker-text{flex:1; min-width:140px; display:flex; flex-direction:column; gap:1px}
.picker-title{font-size:13.5px; font-weight:600; color:var(--ink)}
.picker-hint{font-size:11.5px; font-weight:400; color:var(--muted)}
.picker-actions{display:flex; gap:8px; flex-wrap:wrap}
/* Ba nút nguồn là `<label>`, nút "Dán" là `<button>` — nên khai đủ mấy thứ
   `<button>` vốn có sẵn mà `<label>` thì không: con trỏ bàn tay, không bôi đen
   chữ khi chạm lâu, và căn chữ giữa. */
.picker-btn{display:inline-flex; align-items:center; justify-content:center; gap:7px;
  min-height:40px; padding:8px 13px; border:1.5px solid var(--line); border-radius:9px;
  background:var(--surface); color:var(--ink); font-size:13.5px; font-weight:600; white-space:nowrap;
  cursor:pointer; user-select:none; -webkit-user-select:none; text-align:center}
.picker-btn:hover{border-color:var(--expense); color:var(--expense)}
.picker-btn .ic{flex:0 0 auto; width:17px; height:17px;
  fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round}
/* Nút "Chụp ảnh" là việc thường nhất ở kho, nên đậm hơn hai nút kia một bậc —
   vẫn viền, không tô đặc: nút TÔ ĐẶC duy nhất trên màn phải là nút gửi. */
.picker-btn-camera{border-color:var(--expense); color:var(--expense)}

/* Bậc ①: chưa có JS thì không nút nào, vì bấm cũng không ra gì. */
.picker .picker-btn,.picker .picker-hint{display:none}
/* Bậc ②: máy tính một nút + kéo-thả; điện thoại ba nút nguồn. Máy tính không
   có camera sau, mà `capture` thì bị bỏ qua — ba nút ở đó mở ra đúng một hộp
   thoại, tức là ba nút y nhau. */
.picker.is-touch .picker-btn.picker-touch{display:inline-flex}
.picker.is-touch .picker-hint.picker-touch{display:block}
.picker.is-mouse .picker-btn.picker-mouse{display:inline-flex}
.picker.is-mouse .picker-hint.picker-mouse{display:block}
/* Nút "Dán từ Clipboard" hiện ở CẢ HAI chế độ — khác ba nút trên.
   Trên điện thoại nó là đường DUY NHẤT để dán (không có Ctrl+V); trên máy tính
   nó vẫn tiện cho ai không quen phím tắt. Mặc định nút vẫn để hidden trong HTML
   và chỉ JS bỏ ra khi máy thật sự có Async Clipboard API — luật này chỉ lo phần
   hiện/ẩn theo chế độ chạm hay chuột. */
.picker.is-live .picker-btn.picker-dan{display:inline-flex}

/* ảnh đã chọn */
.picker-files{list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:repeat(auto-fill,minmax(104px,1fr)); gap:10px}
.picker-files:empty{display:none}
.picker-file{position:relative; min-width:0}
.picker-file img{display:block; width:100%; aspect-ratio:1; object-fit:cover;
  border-radius:9px; border:1px solid var(--line); background:var(--sunken)}
.picker-name{display:block; margin-top:3px; font-size:10.5px; font-weight:400; color:var(--muted);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.picker-remove{position:absolute; top:5px; right:5px; width:28px; height:28px; padding:0;
  display:grid; place-items:center; border:1px solid var(--line); border-radius:50%;
  background:var(--surface); color:var(--ink); box-shadow:var(--shadow)}
.picker-remove:hover{border-color:var(--danger); color:var(--danger)}
.picker-remove svg{width:13px; height:13px;
  fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round}
.picker-sum{font-size:11.5px; font-weight:400; color:var(--muted)}
.picker-sum:empty{display:none}
/* Lời nhắc tại chỗ: HEIC của iPhone, tệp nặng quá, chọn quá số tấm. Nói ngay
   lúc chọn, đừng để gửi lên 12 MB qua 4G rồi máy chủ mới trả về lỗi. */
.picker-msg{font-size:12.5px; font-weight:500; color:var(--warn); background:var(--warn-soft);
  border:1px solid var(--warn); border-radius:8px; padding:8px 11px}
.picker-msg:empty{display:none}

@media (max-width:560px){
  /* Dựng đứng: 390px thì icon + chữ + ba nút một hàng là ba nút bị bóp còn 60px. */
  .picker-drop{flex-direction:column; align-items:stretch; gap:11px; padding:13px}
  .picker-ic{display:none}
  .picker-actions{display:grid; grid-template-columns:1fr 1fr}
  /* Chụp ảnh chiếm cả hàng trên: ngón tay ướt, nút 46px rộng hết màn thì khó
     bấm hụt. Hai nút lấy ảnh có sẵn xuống hàng dưới, ít dùng hơn. */
  .picker-btn-camera{grid-column:span 2}
  /* Cho chữ xuống dòng trong nút: hẹp quá thì "Thư viện ảnh" một dòng là tràn cột. */
  .picker-btn{min-height:46px; padding:8px 10px; font-size:14px; white-space:normal; line-height:1.2}
  .picker-files{grid-template-columns:repeat(auto-fill,minmax(92px,1fr)); gap:8px}
  .picker-remove{width:30px; height:30px}
}
@media (max-width:380px){
  /* 320px (iPhone SE) thì hai cột làm chữ trong nút vỡ ba dòng. Xếp dọc cả ba.
     `span 2` PHẢI về `span 1` cùng lúc, không thì lưới sinh cột ngầm thứ hai và
     cả trang cuộn ngang — đúng cái bẫy nocturne.css đã ghi. */
  .picker-actions{grid-template-columns:1fr}
  .picker-btn-camera{grid-column:span 1}
}

/* HÀNG NÚT LUÔN CÁCH PHẦN TRÊN NÓ (16/09/2026).
   Trước đây margin bằng 0, nên nút chỉ có khoảng thở khi thứ nằm trên tình cờ
   có margin-bottom. Sau một ô chọn ảnh thì không có: nút "Thêm phiếu chi" dính
   sát vạch đứt của khung chọn, đo được đúng 0px — trông như nút thuộc về cái
   khung chứ không phải một việc riêng.
   Đặt ở LỚP chứ không vá từng chỗ: `Output/Review` đã tự gõ margin-top ba lần
   và hai view khác gõ `margin:0` để chặn cái margin lẽ ra phải có. Ba bản chép
   là lúc gộp — xem cms/CLAUDE.md § "Chống rác", luật 2.
   Chỗ nào thật sự không muốn khoảng cách thì vẫn đè bằng `style="margin:0"`
   như hai view kia đang làm. */
.button-row{display:flex; gap:14px; align-items:center; flex-wrap:wrap; margin-top:16px}

/* ── Thanh chốt của màn duyệt ────────────────────────────────────────────────
   KHÔNG DÍNH ĐỈNH — Tấn Phú chốt 16/09/2026, đừng thêm lại `position:sticky`.
   Cùng lý do đã bỏ dính ở thanh nav 14/09: trên điện thoại nó ăn mất một khúc
   của màn hình vốn đã chật, mà thứ người ta cuộn xuống để đọc là mấy dòng chi
   phí chứ không phải bộ đếm. Tên lớp đổi theo cho khỏi nói dối — `stickybar`
   mà không sticky là cái bẫy để người sau "sửa giúp". */
.commitbar{background:color-mix(in srgb,var(--bg) 88%,transparent);
  border-bottom:1px solid var(--line)}
/* Đi theo KHUNG của nội dung dưới nó — cùng bề rộng, cùng lề (xem :root
   § "KHUNG CỦA MỌI MÀN"), nên con số "đã chọn" thẳng cột với bảng nó đang đếm. */
.commitbar-inner{max-width:var(--khung-rong); margin-inline:auto; padding:10px var(--khung-le);
  display:flex; align-items:center; gap:14px; flex-wrap:wrap}
.counter{min-width:150px}
.counter-big{font-family:"JetBrains Mono",monospace; font-weight:700; font-size:22px;
  letter-spacing:-.03em; color:var(--expense)}
.counter-money{font-family:"JetBrains Mono",monospace; font-weight:700; font-size:15px; margin-left:8px}
.counter-rest{font-size:12px; color:var(--muted); display:flex; gap:7px; flex-wrap:wrap}
.save-state{font-size:12px; color:var(--muted); margin-left:auto; display:flex; align-items:center; gap:7px}
.dot{width:7px; height:7px; border-radius:50%; background:var(--muted)}
.dot.ok{background:var(--expense)} .dot.bad{background:var(--danger)} .dot.busy{background:var(--warn)}
.btn-commit{background:var(--expense); color:var(--bg); border:0; border-radius:8px; padding:9px 18px;
  font-weight:600; font-size:14px}
.btn-commit:disabled{opacity:.5; cursor:not-allowed}
.commitbar-error{max-width:var(--khung-rong); margin-inline:auto; padding:7px var(--khung-le); font-size:12.5px;
  color:var(--danger); background:var(--danger-soft); font-weight:500}

/* ── HỘP "MÁY ĐỌC NÓI" — CUỘN CHỨ ĐỪNG CẮT ───────────────────────────────────
   Tấn Phú chốt 17/09/2026: *"class notice thêm overflow: overlay, chỉnh ở tất
   cả các page"*. Hai lớp chứ không một: cùng một câu máy đọc trả về, màn Duyệt
   chi phí kho bày bằng `.extractor-note` còn bốn màn kia bằng `.notice` — sửa
   mỗi lớp đầu là "tất cả các page" hụt đúng một màn.

   Vì sao cần: câu ấy có khi là NGUYÊN VĂN lỗi của nhà máy đọc — một chuỗi JSON
   dài không có chỗ nào ngắt được (`{"type":"error","message":"image exceeds 10
   MB maximum: 11217880 bytes…","request_id":"req_011Cf7…"}`). Chuỗi đó dài hơn
   hộp, và không có `overflow` thì nó tràn ra ngoài rồi bị mép hộp cắt mất —
   đúng khúc `request_id`, thứ duy nhất tra lại được lượt gửi hỏng.

   `auto` khai TRƯỚC làm nền: `overlay` là giá trị cũ của WebKit, Firefox chưa
   bao giờ nhận và Chrome nay đã bỏ (coi y như `auto`). Trình duyệt nào không
   hiểu thì bỏ qua đúng dòng sau và vẫn cuộn được — khác nhau chỉ ở chỗ thanh
   cuộn chiếm chỗ hay nổi đè lên chữ. Đừng gộp lại còn một dòng. */
.notice{background:var(--surface); border:1px solid var(--line); border-left:3px solid var(--expense);
  border-radius:10px; padding:15px 18px; display:flex; flex-direction:column; gap:7px; font-size:14px;
  overflow:auto; overflow:overlay}
.extractor-note{background:var(--warn-soft); color:var(--warn); border-radius:8px; padding:9px 12px;
  font-size:13.5px; overflow:auto; overflow:overlay}
.result{border-radius:11px; padding:16px 20px; border:1px solid; display:flex; flex-direction:column; gap:8px}
.result-ok{background:var(--expense-soft); border-color:var(--expense); color:var(--expense)}
.result-bad{background:var(--danger-soft); border-color:var(--danger); color:var(--danger)}
/* Phiên ĐANG MỞ KHOÁ để chủ xưởng sửa lại — xem Views/Shared/_DaGhiSo.cshtml.
   Mức thứ BA, cố ý không dùng lại hai mức trên: `result-ok` nói "xong rồi, yên
   tâm" và `result-bad` nói "hỏng", còn đây là "đang mở, lượt Ghi sau sẽ ĐÈ lên
   sổ" — một trạng thái phải làm người ta dừng lại mà không phải là lỗi. Cùng
   thang vàng đất với `.picker-msg` và `.btn-canh`. */
.result-warn{background:var(--warn-soft); border-color:var(--warn); color:var(--warn)}
.result ul{margin:0; padding-left:18px; font-size:13.5px; display:flex; flex-direction:column; gap:3px}
.result-problems{font-weight:600}

/* bộ lọc */
.filters{display:flex; gap:7px; flex-wrap:wrap; align-items:center}
/* `.doc-chip` (hàng nút chọn tấm chứng từ) đi CHUNG khai báo này, nhưng là một
   LỚP KHÁC — không phải cho đẹp: `review.js` gắn dây cho MỌI `.filter` và đọc
   `data-filter` của nó, nên cho nút chọn tấm mang lớp `.filter` là nó rơi vào
   vòng lọc dòng, `groups[undefined].length` ném ngay lúc dựng trang và cả màn
   duyệt đứng im. Dùng lại một lớp là dùng lại cả JS của lớp đó. */
.filter,.doc-chip{font-size:12.5px; font-weight:500; padding:6px 12px; border-radius:20px; border:1.5px solid var(--line);
  background:var(--surface); color:var(--muted); display:inline-flex; gap:6px; align-items:center}
.filter span{font-family:"JetBrains Mono",monospace; font-size:11px; font-weight:700; background:var(--sunken);
  color:var(--muted); border-radius:20px; padding:0 6px; min-width:20px; text-align:center}
.filter[aria-pressed="true"],.doc-chip[aria-pressed="true"]{border-color:var(--expense);
  background:var(--expense-soft); color:var(--expense)}
.filter[aria-pressed="true"] span{background:var(--expense); color:var(--surface)}
.filters-gap{flex:1}
.link-button{font-size:12.5px; padding:6px 11px; border:0; background:none; color:var(--muted);
  text-decoration:underline; text-underline-offset:3px}
.link-button:hover{color:var(--expense)}
.empty-note{text-align:center; color:var(--muted); padding:26px 0}

/* THANH PHÂN TRANG — dùng chung trang Nhật ký (bộ token cũ) và báo cáo Trái tươi
   (Nocturne). Khai ở đây chứ không ở <style> của một view: lớp nào chỉ sống
   trong một view thì view thứ hai mượn nó về là ra một mẩu không có kiểu, mà
   nhìn ở view đầu vẫn thấy ổn. Repo đã dính đúng cú đó với `.them-o`. */
.trang{display:flex; gap:10px; align-items:center; justify-content:space-between; margin-top:14px}
.trang .muted{margin:0}
/* DẢI SỐ TRANG — 1 2 3 … 5.

   Trang ĐANG XEM tô đặc màu nhận diện, mấy trang kia chỉ là chữ có viền: bấm
   được thì phải nhìn ra là bấm được, mà tô đặc hết thì không còn thấy mình đang
   đứng ở đâu. Dấu "…" không có viền vì nó không bấm được — hình phải nói đúng
   cái nó làm.

   32px, chữ 12,5px — Tấn Phú chốt 19/09/2026: *"thu size và chữ số pagination
   lại cho nó đẹp hơn. Đang to thô quá."* Trước là 40px/14px.

   Cỡ 40px là vùng chạm tối thiểu của màn NHẬP LIỆU (xem màn Thu Múi): ngón tay
   ướt lúc 6h sáng không bắt được nút 30px. Dải số trang thì không nằm ở màn
   nào như thế — nó ở ba màn ĐỌC (báo cáo, tab Phiên duyệt), nơi người dùng là
   chủ xưởng ngồi soi số chứ không phải công nhân đứng cân hàng. Và bấm nhầm ở
   đây không mất gì: bấm lại một cái là về.

   32px cũng đúng cỡ `.chip-thieu` của màn Sản xuất theo ngày, với cùng một lý
   lẽ ghi ở đó. Đổi chỗ này là đổi cho CẢ BA màn — cố ý, dải số trang là khuôn
   dùng chung của cả dự án (Tấn Phú chốt 18/09/2026). */
/* DỒN QUA PHẢI (Tấn Phú chốt 19/09/2026) — `margin-left:auto`, không phải
   `justify-content` của `.trang`.

   Hai lý do, và cả hai đều là ca thật trên màn Sản xuất theo ngày:

   · Dải Ở ĐÁY bảng không có câu đếm đi kèm, tức nó là con MỘT MÌNH trong
     `.trang`. `space-between` chẳng có gì để đẩy nó đi đâu nên nó nằm bên trái;
     `margin-left:auto` thì nuốt hết chỗ trống và đẩy nó sang phải dù có một
     hay hai con.
   · Dưới 560px `.trang` xoay thành CỘT. Ở đó `justify-content` lo trục DỌC,
     không lo trái phải — còn `margin-left:auto` thì đè lên `align-items` và vẫn
     dồn sang phải. Một dòng cho cả hai chiều. */
.trang-so{display:flex; gap:4px; align-items:center; flex-wrap:wrap; margin-left:auto}
.trang-so a,.trang-so b,.trang-so span{
  min-width:32px; height:32px; padding:0 5px; border-radius:7px; font-size:12.5px;
  display:inline-flex; align-items:center; justify-content:center; text-decoration:none}
.trang-so a{border:1px solid var(--line); background:var(--surface); color:var(--ink); font-weight:500}
.trang-so a:hover{border-color:var(--expense); color:var(--expense)}
.trang-so b{background:var(--expense); color:var(--surface); border:1px solid var(--expense); font-weight:600}
.trang-so span{color:var(--muted)}

/* Điện thoại: chữ đếm trang xuống dòng riêng, dải số bám mép trái cho dễ chạm. */
@media (max-width:560px){
  .trang{flex-direction:column; align-items:flex-start; gap:8px}
}

/* mục theo ngày */
#dayList{display:flex; flex-direction:column; gap:16px}
/* KHÔNG `overflow:hidden` ở đây — đừng thêm lại. Danh sách gợi ý của ô chọn có
   tìm kiếm (.combo-list) nằm trong một dòng, nên thẻ ngày mà cắt tràn là dòng
   cuối bấm vào chỉ thấy hai gợi ý đầu, phần còn lại bị xén mất. Bo góc trên và
   dưới khai đích danh ở .day-head và dòng cuối, thay cho cái cắt đó. */
.day{padding:0}
.day-head{display:flex; align-items:center; gap:14px; padding:13px 18px; border-bottom:1px solid var(--line);
  background:var(--expense-soft); flex-wrap:wrap; border-radius:11px 11px 0 0}
.day-head h2{font-size:20px; color:var(--expense); font-family:"JetBrains Mono",monospace;
  font-weight:700; letter-spacing:-.03em}
.day-year{font-size:12px; font-weight:400; opacity:.6}
.day-weekday{font-size:11.5px; color:var(--expense); opacity:.75; display:block; margin-top:-2px}
.day-total{margin-left:auto; font-family:"JetBrains Mono",monospace; font-size:12.5px;
  font-weight:600; color:var(--expense)}
.day-toggle{font-size:10.5px; font-weight:600; letter-spacing:.04em; text-transform:uppercase;
  padding:3px 9px; border-radius:20px; border:1px solid var(--expense); background:none; color:var(--expense)}

/* dòng */
.row{display:grid; grid-template-columns:34px 1fr; gap:0 10px; align-items:start;
  padding:0 14px 0 12px; border-bottom:1px solid var(--line)}
.row:last-child{border-bottom:0; border-radius:0 0 11px 11px}
.row.has-duplicate{background:color-mix(in srgb,var(--danger-soft) 32%,transparent)}
.row.is-selected{background:color-mix(in srgb,var(--expense-soft) 60%,transparent)}
.row.is-selected.has-duplicate{background:color-mix(in srgb,var(--danger-soft) 45%,transparent)}
.row.is-written{background:var(--sunken)}
.row.is-written .summary,.row.is-written .row-money{opacity:.6}
.row.has-problem{box-shadow:inset 3px 0 0 var(--danger)}
.pick{appearance:none; width:22px; height:22px; margin:13px 0 0; border-radius:6px; border:1.5px solid var(--line);
  background:var(--surface); cursor:pointer; position:relative; padding:0}
.pick:checked{background:var(--expense); border-color:var(--expense)}
.pick:checked::after{content:"✓"; position:absolute; inset:0; display:flex; align-items:center;
  justify-content:center; color:var(--surface); font-size:13px; font-weight:700; line-height:1}
.pick:disabled{opacity:.45; cursor:not-allowed}
.row-body{min-width:0}
.row-body > summary{display:grid; grid-template-columns:auto 1fr auto; gap:10px; align-items:start;
  padding:10px 0; cursor:pointer; list-style:none}
.row-body > summary::-webkit-details-marker{display:none}
.row-body[open] > summary{border-bottom:1px dashed var(--line)}
.ledger-tag{font-family:"JetBrains Mono",monospace; font-size:10px; font-weight:700; padding:2px 6px;
  border-radius:4px; margin-top:2px; white-space:nowrap}
.ledger-tag.ledger-Expense{background:var(--expense-soft); color:var(--expense)}
.ledger-tag.ledger-Supply{background:var(--supply-soft); color:var(--supply)}
.summary{min-width:0}
.row-title{font-size:14px; font-weight:500; margin-right:5px}
.row-detail{display:block; font-size:12.5px; color:var(--muted); margin-top:1px;
  font-family:"JetBrains Mono",monospace; font-variant-numeric:tabular-nums}
.row-money{font-family:"JetBrains Mono",monospace; font-variant-numeric:tabular-nums; font-weight:700;
  font-size:13.5px; white-space:nowrap; padding-top:1px}
/* ── Nhãn DỮ LIỆU ────────────────────────────────────────────────────────────
   .tag nói CÁI NÀY LÀ GÌ (một con số, một mã, một phần trăm). Khác hẳn .badge
   nói MỘT VIỆC ĐANG RA SAO. Phân biệt bằng HÌNH, không chỉ bằng màu: .tag là
   viền rỗng bo tròn hết, .badge là nền đặc bo 6px. Nhìn lướt là biết loại nào.

   Trạng thái thì KHÔNG dùng .tag. Trước 15/09/2026 có .tag-done/.tag-pending/
   .tag-off mang nghĩa trạng thái — đã chuyển hết sang .badge, vì hai hệ màu
   song song là người đọc phải học hai bảng màu cho cùng một việc. */
.tag{display:inline-block; font-size:10.5px; font-weight:600; padding:1px 6px; border-radius:20px;
  border:1px solid currentColor; margin-left:3px; white-space:nowrap}
.tag-giam{color:var(--supply)}

/* bảng sửa */
.editor{padding:12px 0 16px; display:flex; flex-direction:column; gap:12px}
/* "× Bỏ" của dòng THÊM TAY — cùng chip với màn Duyệt chi lương, đứng đầu bảng sửa.
   Bảng sửa xếp cột nên con của nó giãn hết bề ngang; chặn lại, và to cho ngón tay. */
.editor > .bo-dong{align-self:flex-start; margin-left:0; padding:6px 14px; min-height:34px}
.ledger-switch{display:inline-flex; border:1.5px solid var(--line); border-radius:8px;
  overflow:hidden; width:fit-content}
.ledger-option{font-size:12.5px; font-weight:600; padding:6px 14px; border:0; background:none; color:var(--muted)}
.ledger-option[aria-pressed="true"]{background:var(--expense); color:var(--surface)}
.ledger-option:disabled{opacity:.5; cursor:not-allowed}
.field-grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(170px,1fr)); gap:9px 12px}
.field{display:flex; flex-direction:column; gap:3px; min-width:0}
.field > span{font-size:10.5px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--muted)}
/* Nhãn dài thì xuống hai dòng, và ô của nó tụt xuống theo — đứng cạnh mấy ô một
   dòng là cả hàng gãy mất đường chân. Đẩy ô xuống đáy thẻ thì mọi ô cùng hàng
   thẳng nhau dù nhãn mấy dòng. (Ô lưới mặc định `stretch` nên thẻ nào cũng cao
   bằng thẻ cao nhất, `margin-top:auto` mới có chỗ mà đẩy.) */
.field > :last-child{margin-top:auto}
.field input,.field select{font-size:13.5px; padding:6px 8px}
/* Ô SỐ nhận ra nhau bằng `data-so`, không bằng `type="number"` — ô số ở đây là
   `type="text"` để bàn phím iPhone gõ được dấu phẩy. Xem numberField trong
   js/review.js. */
.field input[data-so]{font-family:"JetBrains Mono",monospace; font-variant-numeric:tabular-nums}
.field input[readonly],.field select:disabled{background:var(--sunken); color:var(--muted)}
.field.field-span{grid-column:span 2}

/* ── Ô MÃ LOẠI (`input.ma`) — ô GÕ có gợi ý, dùng ở màn Sản lượng và Đóng thùng
   (Tấn Phú chốt 17/09/2026: *"Tên section luôn là Textbox có Autosuggest"*).

   Khai Ở ĐÂY chứ không trong <style> của một view: hai màn dùng chung ô này, mà
   kiểu nằm trong một view thì màn kia ra ô trắng trơn — đúng bài học `.them-o`
   ở § "Kiểu của một loại nút thuộc về fqfvn.css".

   `text-transform:uppercase` chỉ đổi cái MẮT nhìn thấy, giá trị gửi lên vẫn y
   như người ta gõ — mà `MaLoaiGoTay.Doc` gấp hoa thường rồi nên không sao. */
input.ma{font-size:14px; font-weight:600; padding:4px 6px; width:5.6em;
  font-family:"JetBrains Mono",monospace; text-transform:uppercase}
/* Khoá thì `readonly`, KHÔNG `disabled` — ô vẫn phải GỬI LÊN, không thì mảng
   `gMa` ngắn đi một phần tử và mọi dòng phía sau lệch một bậc. Xem CLAUDE.md
   § "`disabled` KHÔNG GỬI Ô LÊN". Tô xám cho giống mọi ô đã khoá khác. */
input.ma[readonly]{background:var(--sunken); color:var(--muted)}

/* Hai ô TÊN SECTION khác, cùng luật "tên section luôn là ô gõ có gợi ý": nhãn lô
   của màn Vào lột, và tên người của màn Chi lương. Khác `input.ma` ở chỗ chúng
   KHÔNG phải khoá ngoại — chữ tự do trên tờ giấy — nên rộng hơn và không ép hoa. */
input.nhan-lo,input.ten-nguoi{font-size:13.5px; font-weight:600; padding:4px 6px; width:100%}
input.nhan-lo{max-width:16em}
input.nhan-lo[readonly],input.ten-nguoi[readonly]{background:var(--sunken); color:var(--muted)}

/* ── Ô CHỌN CÓ TÌM KIẾM (.combo — wwwroot/js/review.js) ──────────────────────
   Danh sách mã CP có mười bốn mã, tên dài, và người duyệt nhớ CHỮ ("cơm",
   "nhúng") chứ không nhớ mã. Nên ô này là một ô GÕ cộng một danh sách lọc theo
   chữ — không dấu cũng ra.

   Danh sách ĐỔ RA NGOÀI (position:absolute) chứ không đẩy nội dung xuống: đẩy
   xuống thì cả cột ô nhập bên dưới nhảy chỗ ngay lúc ngón tay đang ở đó. Vì vậy
   không khối cha nào trên đường đi được `overflow:hidden` — xem chú thích ở .day. */
.combo{position:relative; min-width:0}
.field .combo-input{font-size:13.5px; padding:6px 26px 6px 8px; text-overflow:ellipsis}
.combo-caret{position:absolute; right:9px; top:50%; transform:translateY(-50%);
  font-size:10px; color:var(--muted); pointer-events:none}
.combo-list{position:absolute; z-index:30; left:0; right:0; top:calc(100% + 3px);
  max-height:252px; overflow-y:auto; padding:4px; display:flex; flex-direction:column; gap:1px;
  background:var(--surface); border:1.5px solid var(--expense); border-radius:9px; box-shadow:var(--shadow)}
/* Vùng chạm 38px — ngón tay ướt lúc 6h sáng không bắt được dòng 24px. */
.combo-option{display:flex; align-items:center; min-height:38px; width:100%; text-align:left;
  font-size:13.5px; padding:7px 9px; border:0; border-radius:6px; background:none; color:var(--ink);
  white-space:normal; line-height:1.3}
.combo-option.is-active{background:var(--expense-soft); color:var(--expense)}
.combo-option[aria-selected="true"]{font-weight:700}

/* Nút xoá đứng TRƯỚC ô gõ — Tấn Phú chọn chỗ đó (16/09/2026). Bên phải là chỗ
   Safari trên iPhone tự vẽ nút xoá của nó, hai nút chồng nhau thì không ai biết
   bấm cái nào. */
.with-clear{position:relative; display:block; min-width:0}
.field .with-clear input{padding-left:34px}
.field-clear{position:absolute; left:5px; top:50%; transform:translateY(-50%);
  width:24px; height:24px; padding:0; border:0; border-radius:50%; line-height:1;
  background:var(--sunken); color:var(--muted); font-size:15px;
  display:flex; align-items:center; justify-content:center}
.field-clear:hover{background:var(--expense); color:var(--surface)}
.field-clear:disabled{opacity:.35}

/* HAI CỤM của màn Duyệt chi phí kho — ① thanh toán, ② hoàn tiền; mỗi tờ vào đúng
   cụm của nó ngay từ lúc gửi. Bọc cụm ① còn là để thẻ ảnh DÍNH ĐỈNH (`.doc-card`,
   dưới) hết dính ở cuối cụm: sticky dính trong khối cha, và cha là `.page` thì
   tấm ảnh đè lên cả thẻ hoàn tiền. Khoảng cách theo đúng nhịp của `.page`. */
.cum-duyet{display:flex; flex-direction:column; gap:22px}
.cum-dau{margin:0; padding:2px 0 2px 10px; border-left:3px solid var(--expense);
  font-size:13px; color:var(--muted)}
.cum-dau b{color:var(--ink); font-size:14px}

/* Ba ô dùng chung cả phiên, đứng trên đầu trang duyệt. */
.session-card{gap:12px}
.session-head{display:flex; align-items:baseline; gap:10px; flex-wrap:wrap}
.session-head h2{font-size:16px; color:var(--expense)}

/* ── ẢNH CHỨNG TỪ GỐC — DÍNH ĐỈNH KHI CUỘN ───────────────────────────────────
   Tấn Phú chốt 16/09/2026: *"phải sticky cái này lại nếu kéo xuống giống màn
   Đóng Thùng"*. Cùng việc `.soi > .the-anh` của `_SoiChiaDoi` đang làm cho ba
   màn duyệt có ảnh: người duyệt dò tấm ảnh với mấy chục dòng ở dưới, ảnh trôi
   khỏi màn từ dòng thứ mười là mỗi con số một vòng cuộn lên cuộn xuống.

   Đây là NGOẠI LỆ CÓ Ý THỨC của luật "không dính đỉnh" ghi ở .commitbar phía
   trên. Không mâu thuẫn: luật đó nói đừng ghim thứ người ta KHÔNG cuộn xuống để
   đọc — bộ đếm, thanh nav. Tấm ảnh thì ngược lại, nó chính là thứ mắt phải liếc
   lại ở từng dòng.

   Dính đỉnh chỉ có nghĩa khi thẻ THẤP HƠN MÀN HÌNH: cao hơn thì phần dưới của
   nó không bao giờ lên tới nơi, mà mấy dòng chi phí nằm khuất hẳn sau lưng. Một
   phiên mang được tám tấm chứng từ (`MaxImages`), xếp dọc là chắc chắn vượt màn
   — nên nhiều tấm thì mỗi lúc chỉ bày MỘT, chọn bằng hàng nút ở tiêu đề. Dây
   nối ở `review.js` (`doc-anh` / `data-anh-nut`).

   z-index 6 lấy đúng số của `.soi > .the-anh`: trên mấy dòng đang cuộn qua bên
   dưới, mà vẫn DƯỚI `.combo-list` (z-index 30) — danh sách gợi ý của một dòng
   phải đổ ra ĐÈ LÊN ảnh, không thì chọn mã ở dòng đầu là gợi ý chui xuống dưới
   tấm ảnh và bấm vào chỗ không có gì. */
.doc-card{gap:12px; position:sticky; top:0; z-index:6}
/* Trên điện thoại mỗi pixel là một phần dòng chi phí lọt vào màn — cùng lý do
   `.soi > .the-anh` bóp đệm còn 8px ở khổ này. */
@media (max-width:860px){ .doc-card{padding:10px} }
/* Hàng nút chọn tấm. KHÔNG khai lại kiểu nút ở đây: `.doc-chip` đã ghép vào
   CÙNG khai báo với `.filter` của bộ lọc dòng (xem trên) — cùng hình, cùng cách
   nói "cái nào đang chọn" (aria-pressed), mà vẫn là hai móc JS riêng. Ở đây chỉ
   còn phần XẾP CHỖ. */
.doc-chips{display:flex; gap:7px; flex-wrap:wrap; margin-left:auto}

/* ── BIÊN LAI HOÀN TIỀN (cụm ②) ──────────────────────────────────────────────
   Bằng chứng đã trả lại tiền cho người đứng ra chi. Dòng «Ứng cá nhân» chưa có
   thì nút ghi khoá — xem Docs/doc-bien-lai-chuyen-tien.md. */
.receipt-card{gap:12px}
.receipt-list{display:flex; flex-direction:column; gap:12px}
.receipt{border:1px solid var(--line); border-left:3px solid var(--expense); border-radius:10px;
  padding:12px 14px; display:flex; flex-direction:column; gap:9px; background:var(--surface)}
.receipt-head{display:flex; align-items:center; gap:10px; flex-wrap:wrap}
.receipt-money{font-family:"JetBrains Mono",monospace; font-variant-numeric:tabular-nums;
  font-weight:700; font-size:17px; color:var(--expense)}
.receipt-del{margin-left:auto}
/* Hoàn bằng TIỀN MẶT — gấp sẵn: đa số lần hoàn là chuyển khoản, bốn ô này chỉ
   mở ra khi có phần đưa tận tay. */
.tien-mat{border-top:1px dashed var(--line); padding-top:10px}
.tien-mat > summary{cursor:pointer; font-size:14px; padding:4px 0}
.tien-mat[open] > summary{margin-bottom:10px}
/* Bảy trường bắt buộc, mỗi trường một ô — nhãn nhỏ trên, giá trị dưới. Chưa đọc
   được thì vẫn CHIẾM CHỖ và nói ra: ô trống người duyệt còn thấy mà đi hỏi. */
.receipt-fields{display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:8px 14px; margin:0}
.receipt-fields > div{min-width:0}
.receipt-fields dt{font-size:10.5px; font-weight:600; letter-spacing:.06em;
  text-transform:uppercase; color:var(--muted)}
.receipt-fields dd{margin:0; font-size:13.5px; overflow-wrap:break-word}
.receipt-fields dd.mono{font-family:"JetBrains Mono",monospace; font-size:12.5px}

/* Đối chiếu tổng tick ↔ tổng biên lai. Lệch thì KÊU nhưng không khoá: một lệnh
   chuyển hay trả gộp nhiều ngày.

   XUỐNG DÒNG ĐƯỢC — đừng trả `white-space:nowrap` về (bỏ 16/09/2026). Câu đầy đủ
   dài tới ~90 ký tự ("Đang tick … · biên lai … · lệch … (đã chuyển nhiều hơn
   phần đang tick)") tức ~600px ở cỡ 12px, mà iPhone chỉ có 390px. `nowrap` kéo
   bề rộng TỐI THIỂU của ô lên đúng con số đó, nên nó không đẩy chữ ra ngoài một
   mình — nó đẩy CẢ TRANG rộng ra: thanh trên đứt, mấy thẻ bên dưới cụt mép phải,
   và trang cuộn ngang. Một dòng chữ làm vỡ cả màn.

   Cái THẬT SỰ không được ngắt là con SỐ, và chỉ con số: `.rc-so` lo phần đó.
   Chữ giữa các số thì cứ xuống dòng như văn xuôi bình thường.

   `min-width:0` vì ô này là mục flex của `.counter-rest`; mục flex mặc định
   không co xuống dưới bề rộng nội dung tối thiểu của nó. */
.receipt-check{font-size:12px; min-width:0}
/* Ba con số tiền của cả đơn — cùng khuôn với `.receipt-check`, kể cả `min-width:0`
   (mục flex của `.counter-rest`) và `.rc-so` cho phần số. */
.tien-don{font-size:12px; min-width:0}
.receipt-check-lech{color:var(--warn); font-weight:600}
/* KHỚP là tin mừng và là điều kiện để nút Ghi mở ra — tô xanh cho nó khác hẳn
   dòng lệch, chứ không để người duyệt phải đọc chữ mới biết bên nào. */
.receipt-check-khop{color:var(--expense); font-weight:600}
.rc-so{white-space:nowrap; font-family:"JetBrains Mono",monospace; font-weight:700}

/* Nút thêm chi phí của từng ngày — cùng khuôn với "chọn cả ngày". Nó xin máy
   chủ một dòng trống rồi mở bảng sửa của chính dòng đó; không còn form gõ chữ
   nào ở đây từ 16/09/2026. */
.day-add{font-size:10.5px; font-weight:600; letter-spacing:.04em; text-transform:uppercase;
  padding:3px 9px; border-radius:20px; border:1px dashed var(--expense); background:none;
  color:var(--expense)}
.day-add:hover{background:var(--expense-soft)}
.day-add:disabled{opacity:.55; cursor:progress}
/* Thẻ ngày `padding:0` nên khối báo lỗi phải tự chừa lề, không thì nó dính sát
   ba mép thẻ. Báo TẠI CHỖ chứ không `alert()`: Docs/nhap-lieu-ai.md luật 2. */
.day-loi{margin:12px 18px 16px}

.derived{display:flex; gap:16px; flex-wrap:wrap; align-items:center; font-size:12.5px; color:var(--muted);
  padding-top:6px; border-top:1px solid var(--line)}
.derived b{color:var(--ink); font-family:"JetBrains Mono",monospace; font-weight:700}
.derived-warn{color:var(--warn)}

/* ── BẢNG: MỘT kiểu duy nhất cho cả app ──────────────────────────────────────
   Tấn Phú chốt 16/09/2026: *"table của toàn bộ hệ thống phải luôn theo style
   của table người dùng (cuộn ngang) trên mobile nếu to"*.

   Nên chỉ còn ĐÚNG MỘT cách bày bảng trên điện thoại: bảng giữ nguyên hình
   bảng, vùng chứa nó cuộn ngang. Không còn nhánh "dưới 560px xếp thành thẻ
   chồng" — hai cách bày cùng một dữ liệu là người dùng phải học hai lần, và
   mỗi màn lại rơi vào một nhánh khác nhau tuỳ nó nạp hệ giao diện nào.

   `.table-wrap` và `.cuon` là CÙNG MỘT THỨ, hai cái tên có từ hai hệ giao diện.
   Khai chung một luật ở đây để không màn nào cuộn còn màn nào thì không —
   `.cuon` trước đây chỉ có luật dưới `body.nocturne`, nên màn Duyệt chi lương
   (không phải Nocturne) không cuộn được dòng nào và kéo CẢ TRANG lệch sang
   ngang trên iPhone.

   `overscroll-behavior-x: contain` để quẹt hết mép bảng không kéo luôn cả
   trang (Safari trên iPhone hay bắt nhầm thành cử chỉ "quay lại"). */
.table-wrap,.cuon{overflow-x:auto; overscroll-behavior-x:contain; -webkit-overflow-scrolling:touch}
table.list{width:100%; border-collapse:collapse; font-size:13.5px}
table.list th,table.list td{padding:9px 10px; text-align:left; border-bottom:1px solid var(--line);
  vertical-align:middle; white-space:nowrap}
table.list th{font-size:10.5px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--muted)}
table.list tbody tr:last-child td{border-bottom:0}
table.list tr.is-off td{color:var(--muted)}
.mono{font-family:"JetBrains Mono",monospace; font-variant-numeric:tabular-nums}

/* ── Sắp xếp + lọc (js/bang-sap-xep.js) ──────────────────────────────────────
   Tiêu đề bấm được phải TRÔNG như bấm được, không thì cả tính năng nằm im ở đó
   không ai biết. Mũi tên vẽ bằng ::after theo aria-sort — một nguồn sự thật,
   JS chỉ đặt thuộc tính chứ không đụng vào class trạng thái. */
table.list th.sap-duoc{cursor:pointer; user-select:none; white-space:nowrap}
table.list th.sap-duoc:hover{color:var(--ink)}
/* Vùng chạm 44px cho ngón tay ướt lúc 6h sáng — tiêu đề cao 10,5px thì không
   cách nào bấm trúng. Đệm dọc chứ không đặt height, để hàng tiêu đề của mọi
   bảng vẫn cao bằng nhau. */
table.list th.sap-duoc{padding-top:14px; padding-bottom:14px}
table.list th.sap-duoc::after{content:"↕"; margin-left:5px; opacity:.35; font-size:11px}
table.list th.sap-duoc[aria-sort=ascending]::after{content:"↑"; opacity:1; color:var(--nhan-600,#DF301C)}
table.list th.sap-duoc[aria-sort=descending]::after{content:"↓"; opacity:1; color:var(--nhan-600,#DF301C)}
table.list th.sap-duoc:focus-visible{outline:2px solid var(--nhan-600,#DF301C); outline-offset:-2px}
/* Cột số thứ tự: hẹp, mờ, và là đường về thứ tự gốc. */
table.list td.stt{color:var(--muted); font-family:"JetBrains Mono",monospace; font-size:11.5px;
  text-align:right; width:1%}

/* ── TRẦN BỀ NGANG CHO MỌI Ô CỦA MỌI BẢNG — LUẬT CỨNG ────────────────────────
   Tấn Phú chốt 16/09/2026: *"luôn luôn set max width cho mọi cột của mọi table."*

   Vì sao là luật chứ không phải vá từng cột: bảng ở đây `white-space:nowrap`,
   nên MỘT ô có chữ dài là kéo cả cột rộng ra bằng chữ đó — không cần nhiều, một
   ô là đủ. Đã dính hai lần trong một buổi: cột Họ tên phình vì câu máy đọc giải
   thích, rồi cột Người nhận phình vì dòng tên ngân hàng. Vá từng cột thì cột
   thứ ba sẽ lại phình, vì dữ liệu dài là chuyện của người dùng chứ không phải
   của cột.

   16em ≈ 200px. Quá trần thì CẮT ĐUÔI bằng "…" chứ không đẩy cột — số và ngày
   vốn ngắn hơn nhiều nên không đụng gì tới chúng.

   Ô nào cần đọc ĐỦ chữ thì thêm lớp `o-chu` ở dưới: nó xuống dòng TRONG trần,
   không bị cắt. Đó là cách duy nhất được phép để một ô dài hơn — đừng gỡ trần. */
table.list th,table.list td,table.bang th,table.bang td{
  max-width:16em; overflow:hidden; text-overflow:ellipsis}

/* Ô CHỮ DÀI — xuống dòng trong trần thay vì cắt đuôi.
   Dùng cho ô mà người duyệt phải đọc hết: họ tên kèm chú thích máy đọc, người
   nhận kèm số tài khoản, nội dung chuyển khoản.

   `white-space:normal` phải đi CÙNG max-width: thiếu nó thì chữ không xuống dòng
   được nên cái trần bị bỏ qua và mọi thứ y như cũ.

   `overflow-wrap` là `break-word`, KHÔNG phải `anywhere`: hai cái ngắt chữ giống
   nhau, nhưng `anywhere` còn kéo bề rộng TỐI THIỂU của ô xuống một ký tự — mà
   bảng tự canh cột theo đúng con số đó. Viết `anywhere` thì đo được cột co còn
   81px, hẹp hơn cả cái tên. `min-width` là sàn để mấy cột số bên phải không ép
   nó mỏng thêm. */
table.list td.o-chu,table.bang td.o-chu{
  min-width:11em; max-width:14em; white-space:normal; overflow:visible}
table.list td.o-chu *,table.bang td.o-chu *{overflow-wrap:break-word}

/* Dòng bị lọc. ẨN chứ KHÔNG gỡ khỏi DOM — ô nhập của nó vẫn nằm trong form và
   vẫn được gửi lên, nên lọc không bao giờ làm mất số của người đang ẩn.
   Dùng class riêng chứ không dùng [hidden]: `display:table-row` của bảng thắng
   `display:none` mà bảng kiểu của trình duyệt đặt cho [hidden]. */
table.list tr.an-dong{display:none}

/* ĐIỆN THOẠI: chữ nhỏ một bậc, ô hẹp một bậc — để nhìn thấy NHIỀU CỘT hơn.
   Tấn Phú 16/09/2026: "chữ của table đang siêu to nên k thấy gì hết… size cột
   cũng co lại". Bảng ở đây cuộn ngang, nên mỗi pixel tiết kiệm được là thêm một
   phần cột lọt vào màn — quan trọng hơn hẳn việc chữ to thêm một bậc.
   Vẫn 12,5px chứ không nhỏ hơn: dưới mức đó thì đọc số tiền ngoài nắng là đoán.
   Áp cho MỌI bảng .list, không riêng màn lương — một kiểu bảng cho cả hệ thống,
   đúng như anh chốt. */
@media (max-width:560px){
  table.list{font-size:12.5px}
  table.list th,table.list td{padding:7px 8px}
  table.list th{font-size:10px; letter-spacing:.04em}
  table.list th.sap-duoc{padding-top:12px; padding-bottom:12px}
  table.list td.stt{font-size:10.5px}
  /* Cột tên: tên công nhân dài nhất trên bảng là "amí(chị) Thắm" — không cần
     rộng hơn thế. Trần này chặn một ô ghi chú dài kéo giãn cả cột. */
  table.list td .phu{font-size:11px}
}

.bang-cong-cu{display:flex; gap:6px 10px; align-items:center; flex-wrap:wrap; margin:6px 0 10px}
.bang-cong-cu input[type=search]{flex:1 1 220px; min-width:0; max-width:340px}
/* Câu đếm xuống HẲN một dòng riêng: nó dài ("hiện 3 / 26 dòng — mấy dòng đang
   ẩn VẪN được lưu bình thường") và chen cạnh ô lọc trên điện thoại thì bị cắt
   mất đúng vế trấn an người dùng. */
.bang-cong-cu .dem{flex:1 1 100%; font-size:12.5px; color:var(--muted)}
@media (min-width:861px){ .bang-cong-cu .dem{flex:0 1 auto} }

/* ── Huy hiệu TRẠNG THÁI ─────────────────────────────────────────────────────
   Năm mức, dùng chung cả hai hệ giao diện (fqfvn.css luôn được nạp, kể cả trên
   trang Nocturne). Màu tự mang theo nên không phụ thuộc token của hệ nào.

   Đừng lẫn với .tag: .tag là NHÃN DỮ LIỆU (loại hàng, sổ đích), còn .badge nói
   một việc đang ra sao. Dùng lẫn là người đọc mất manh mối màu. */
.badge{display:inline-block; font-size:11px; font-weight:600; line-height:1.5;
  padding:3px 9px; border-radius:6px; border:1px solid; white-space:nowrap}
/* Viền = chính màu nền, nên nhìn thì liền khối mà hình học không đổi — bỏ hẳn
   border sẽ làm mọi huy hiệu thấp đi 2px và lệch mấy hàng đang căn theo nó. */
.badge-ok  {color:var(--tt-ok);   background:var(--tt-ok-nen);   border-color:var(--tt-ok-nen)}
.badge-loi {color:var(--tt-loi);  background:var(--tt-loi-nen);  border-color:var(--tt-loi-nen)}
.badge-tin {color:var(--tt-tin);  background:var(--tt-tin-nen);  border-color:var(--tt-tin-nen)}
.badge-canh{color:var(--tt-canh); background:var(--tt-canh-nen); border-color:var(--tt-canh-nen)}
.badge-suco{color:var(--tt-suco); background:var(--tt-suco-nen); border-color:var(--tt-suco-nen)}

@media (max-width:560px){
  .commitbar-inner{gap:8px} .counter-rest{width:100%; order:5} .save-state{margin-left:0}
  .btn-commit,#commitForm{width:100%; order:6}
  .row-body > summary{grid-template-columns:auto 1fr; gap:6px}
  .row-money{grid-column:2}
  .field-grid{grid-template-columns:1fr} .field.field-span{grid-column:span 1}
}
@media (prefers-reduced-motion:reduce){*{transition:none!important; animation:none!important}}


/* Dashboard tồn kho dùng hệ thiết kế Nocturne ở css/nocturne.css, nạp riêng
   khi view đặt ViewData["BodyClass"] = "nocturne". Bộ lớp cũ (.stock-*,
   .table-wrap, .grid, .price-form…) đã xoá vì không còn view nào dùng. */

/* ── Dải "máy đọc tấm này thành loại gì" ───────────────────────────────────────
   Đứng đầu mọi màn duyệt đi từ màn Chụp giấy. Việc của nó là trả lời một câu
   trong nửa giây: MÁY ĐANG ĐỌC TỜ NÀY THÀNH CÁI GÌ — và cho đổi ngay tại chỗ
   nếu sai, chứ không bắt quay lại chụp lại. */
/* KHÔNG khai color:#fff ở đây nữa (18/09/2026). Một trong tám nền nay là VÀNG,
   mà chữ trắng trên vàng chỉ được 1,93:1. Mỗi loại lấy chữ từ --giay-*-chu của
   chính nó, nên thêm một loại nền sáng nữa cũng không phải sửa luật này. */
.dai-giay{border-radius:11px; padding:14px 16px; display:flex;
  flex-wrap:wrap; align-items:center; gap:10px 14px}
.dai-giay.g-sanluong {background:var(--giay-sanluong);  color:var(--giay-sanluong-chu)}
.dai-giay.g-dongthung{background:var(--giay-dongthung); color:var(--giay-dongthung-chu)}
.dai-giay.g-valot    {background:var(--giay-valot);     color:var(--giay-valot-chu)}
.dai-giay.g-chungtu  {background:var(--giay-chungtu);   color:var(--giay-chungtu-chu)}
.dai-giay.g-bangluong{background:var(--giay-bangluong); color:var(--giay-bangluong-chu)}
.dai-giay.g-phieuchi {background:var(--giay-phieuchi);  color:var(--giay-phieuchi-chu)}
.dai-giay.g-thumui   {background:var(--giay-thumui);    color:var(--giay-thumui-chu)}
.dai-giay.g-nhaphang {background:var(--giay-nhaphang);  color:var(--giay-nhaphang-chu)}
.dai-giay.g-quytamung{background:var(--giay-quytamung);color:var(--giay-quytamung-chu)}
.dai-giay .ten{font-weight:800; font-size:17px; letter-spacing:-.02em}
.dai-giay .vi-sao{font-size:12.5px; opacity:.9; flex-basis:100%; margin:0}
.dai-giay .doi{display:flex; flex-wrap:wrap; align-items:center; gap:7px; margin-left:auto}
.dai-giay .doi .dan{font-size:12px; opacity:.85}
/* Nút đổi loại: viền trắng trên nền màu. KHÔNG dùng .btn-primary — nút đỏ đặt
   trên dải màu là hai mảng màu lớn cãi nhau, và đỏ ở đây không có nghĩa gì. */
/* Viền và chữ theo CHỮ CỦA DẢI (currentColor), không phải trắng cứng: trên nền
   vàng thì chữ là mực, mà viền trắng lúc ấy gần như biến mất. */
.dai-giay .doi button{background:rgba(255,255,255,.14); color:inherit; font-size:13px;
  border:1px solid currentColor; border-radius:8px; padding:6px 11px}
.dai-giay .doi button:hover{background:rgba(255,255,255,.28)}
.dai-giay .doi form{display:inline}
/* Nền đen mờ nên chữ TRẮNG cố định, không ăn theo chữ của dải: trên dải vàng
   chữ là mực, mà mực trên mảng đen 28% là không đọc được. */
.dai-giay .ngo{background:rgba(0,0,0,.28); color:#fff; border-radius:6px; padding:3px 9px;
  font-size:11.5px; font-weight:600}
@media (max-width:560px){
  .dai-giay .doi{margin-left:0; flex-basis:100%}
  .dai-giay .doi .dan{flex-basis:100%}
  /* Ba nút chia nhau một hàng chứ đừng mỗi nút một hàng: nhãn đã ngắn, và
     chiều cao ở đây là chiều cao cướp của phần số cân bên dưới. */
  .dai-giay .doi form{flex:1 1 30%}
  .dai-giay .doi button{width:100%}
}

/* ── Ô chọn model máy đọc giấy, đặt ngay trên form gửi ──────────────────────
   Bày ra TÊN MODEL THẬT, không bày "tầng Nặng": người đứng ở kho tra được tên
   model, còn "tầng" thì không nói lên điều gì.

   Cố ý NHẸ HƠN cả form bên dưới — nó là mẩu chú thích kèm một ô chọn, không phải
   một khối nhập liệu nữa. Nặng bằng form chính là người gửi phải đọc hai lần mới
   biết chỗ nào để bấm gửi. */
/* HỘP TIẾN TRÌNH của lượt gửi giấy — Views/Shared/_HopGui.cshtml.
   Gom về đây từ hai khối <style> chép tay ở màn Sản lượng và màn Chụp giấy.
   Xem Models/HopGui.cs để biết vì sao gộp. */
dialog.tien-trinh{border:0; border-radius:14px; padding:0; max-width:min(92vw,420px); width:100%;
  box-shadow:var(--shadow); color:var(--ink); background:var(--surface)}
dialog.tien-trinh::backdrop{background:rgba(0,0,0,.45)}
/* Hộp được focus() chỉ để đỗ con trỏ ở chỗ vô hại, nó không phải nút bấm nên
   không đeo vòng focus. Nút bên trong vẫn giữ vòng như thường. */
dialog.tien-trinh:focus{outline:none}
.tt-than{padding:22px 24px 20px; display:flex; flex-direction:column; gap:14px}
.tt-buoc{list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:9px}
.tt-buoc li{display:flex; align-items:center; gap:9px; font-size:14px; color:var(--muted)}
.tt-buoc li .cham{flex:none; width:17px; height:17px; border-radius:50%; border:2px solid var(--line);
  display:inline-flex; align-items:center; justify-content:center; font-size:11px; line-height:1}
.tt-buoc li[data-trang-thai=dang]{color:var(--ink); font-weight:600}
.tt-buoc li[data-trang-thai=dang] .cham{border-color:var(--expense); border-right-color:transparent;
  animation:tt-quay .8s linear infinite}
.tt-buoc li[data-trang-thai=xong]{color:var(--ink)}
.tt-buoc li[data-trang-thai=xong] .cham{border-color:var(--expense); background:var(--expense); color:#fff}
.tt-buoc li[data-trang-thai=xong] .cham::before{content:"\2713"}
.tt-buoc li b{margin-left:auto; font-family:"JetBrains Mono",monospace; font-weight:500; font-size:12.5px}
@keyframes tt-quay{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){ .tt-buoc li[data-trang-thai=dang] .cham{animation:none} }
/* accent-color đổi được màu cả thanh xác định lẫn thanh chạy vô định, mà thanh
   vô định thì không vẽ tay được. (Khác chuyện input[type=range] ở CLAUDE.md:
   chỗ đó cấm accent-color vì nó không đặt được CỠ cục trượt.) */
dialog.tien-trinh progress{width:100%; height:6px; accent-color:var(--expense)}

/* ── Hộp XÁC NHẬN khi biên lai lệch (màn Duyệt chi phí kho) ───────────────────
   Lệch VẪN ghi được, nhưng bấm Duyệt lúc lệch thì làm một phép cộng để xác nhận
   (Tấn Phú 24/09/2026). Mượn khuôn của `dialog.tien-trinh`; con số chênh ở
   `.hop-lech-doi`. Thẻ so sánh to trên MÀN thì mượn `.khop` (`.bl-doi-chieu`). */
dialog.hop-lech{border:0; border-radius:14px; padding:0; max-width:min(92vw,420px); width:100%;
  box-shadow:var(--shadow); color:var(--ink); background:var(--surface)}
dialog.hop-lech::backdrop{background:rgba(0,0,0,.45)}
dialog.hop-lech:focus{outline:none}
.hop-lech-than{padding:20px 22px; display:flex; flex-direction:column; gap:12px}
.hop-lech-than h2{margin:0; font-size:17px}
.hop-lech-doi{margin:0; display:flex; flex-direction:column; gap:6px}
.hop-lech-doi > div{display:flex; justify-content:space-between; gap:12px; font-size:14px}
.hop-lech-doi dt{margin:0; color:var(--muted)}
.hop-lech-doi dd{margin:0; font-variant-numeric:tabular-nums; font-weight:600}
.hop-lech-doi .lech dt,.hop-lech-doi .lech dd{color:var(--danger)}
.hop-lech-toan{display:flex; align-items:center; gap:8px; flex-wrap:wrap; font-size:14px}
.hop-lech-toan input{width:5.5em; text-align:center}
.bl-doi-chieu{margin-top:12px}

/* Cụm gửi lương: HAI ô thả file cạnh nhau — chúng là một cặp, và người gửi
   phải thấy ngay mình đang thiếu tờ nào. Trên điện thoại xếp chồng, vẫn giữ
   thứ tự bảng trước phiếu sau.

   Để ở fqfvn.css chứ không ở nocturne.css vì cụm này dùng ở CẢ HAI hệ: màn Chi
   lương (bộ token cũ) và báo cáo Lương (Nocturne). File này luôn được nạp. */
.doi-o{display:grid; grid-template-columns:1fr 1fr; gap:14px}
.doi-o > div{min-width:0}
@media (max-width:860px){ .doi-o{grid-template-columns:1fr} }
/* Ô mà neo trỏ vào (`LoaiGiay.Neo`) — vào từ lưới Nhập liệu bằng AI thì ô của
   đúng tờ mình đang cầm có viền MÀU CỦA CHÍNH Ô VỪA BẤM ở lưới. `:target`,
   không JS, cùng nếp hộp xem ảnh. Viền là `outline` nên không xô bố cục của ô
   bên cạnh; lề cuộn chừa chỗ cho nó khỏi dính mép trên màn. */
.doi-o > :target{outline:2px solid var(--expense); outline-offset:8px; border-radius:6px;
  scroll-margin-top:18px}
.doi-o > .g-bangluong:target{outline-color:var(--giay-bangluong)}
.doi-o > .g-phieuchi:target {outline-color:var(--giay-phieuchi)}
.o-nhan{font-weight:600; margin-bottom:4px}
.o-phu{font-size:12.5px; color:var(--muted); margin:0 0 8px}

.ai-may{display:flex; flex-wrap:wrap; align-items:center; gap:6px 10px;
  margin:0 0 12px; padding:9px 13px; font-size:13px;
  background:var(--sunken); border:1px solid var(--line); border-radius:10px}
.ai-nhan{font-size:10.5px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--muted)}
.ai-model{font-family:"JetBrains Mono",monospace; font-weight:500; font-size:13px}
.ai-nha{font-size:11.5px; color:var(--muted)}
.ai-phu{font-size:12px; color:var(--muted)}
/* Ô chọn KHÔNG ăn width:100% của luật input chung — ở đây nó nằm trong một hàng
   chữ, kéo hết chiều ngang là đẩy mọi thứ khác xuống dòng. */
.ai-doi{display:flex; align-items:center; gap:7px; margin-left:auto}
.ai-doi select{width:auto; max-width:min(60vw,320px); padding:5px 9px; font-size:12.5px; border-radius:7px}
.ai-doi button{background:none; border:1px solid var(--line); border-radius:7px; padding:5px 12px;
  color:var(--muted); font-size:12.5px}
.ai-doi button:hover{color:var(--ink); border-color:var(--muted)}
.sr-only{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap; border:0}

@media (max-width:560px){
  /* Điện thoại: ô chọn xuống hàng riêng và ăn hết chiều ngang — ngón tay ướt
     lúc 6h sáng không bắt được một ô chọn rộng 120px nhét cuối hàng chữ. */
  .ai-doi{margin-left:0; width:100%}
  .ai-doi select{flex:1; max-width:none}
}

/* ── MÀN DUYỆT CHI LƯƠNG ─────────────────────────────────────────────────────
   Chuyển từ khối <style> inline trong Views/Payroll/Review.cshtml về đây
   (16/09/2026). cms/CLAUDE.md § "Giao diện" đã ghi HAI LẦN rằng nguồn kiểu thứ
   hai — file rời hay <style> inline đều tính — có thể không tới được máy người
   dùng; lần đầu là css/gia-mua.css, lần thứ hai là _VizStyles.cshtml. Mà đây là
   màn TIỀN LƯƠNG: mất `.o-cong`/`.o-tien` là mấy chục ô nhập ăn width:100% và
   bảng vỡ, mất `td .phu` là dòng chức danh to bằng tên.
   Đừng mở lại một khối <style> trong view đó. */
/* Ba con số của phép khớp, hàng ngang, cỡ to. Đây là thứ DUY NHẤT người
   duyệt phải nhìn trước khi nhìn bất cứ gì khác trên màn này. */
.khop{display:grid; grid-template-columns:repeat(3,1fr); gap:0; border:1px solid var(--line);
  border-radius:12px; overflow:hidden; background:var(--surface)}
.khop > div{padding:14px 16px; border-right:1px solid var(--line)}
.khop > div:last-child{border-right:0}
.khop .nhan{font-size:11px; letter-spacing:.12em; text-transform:uppercase; color:var(--muted)}
.khop .so{font-family:"JetBrains Mono",monospace; font-size:26px; font-weight:700; line-height:1.15}
.khop .phu{font-size:12.5px; color:var(--muted); margin-top:2px}
.khop .lech .so{color:var(--danger)}
@media (max-width:700px){
  .khop{grid-template-columns:1fr}
  .khop > div{border-right:0; border-bottom:1px solid var(--line)}
  .khop > div:last-child{border-bottom:0}
  .khop .so{font-size:22px}
}
.o-cong{width:4.4em}
.o-tien{width:7.4em}
.o-stk{width:9.5em}
td .phu{font-size:12px; color:var(--muted)}
td .ngo{font-size:12px; color:var(--danger); margin-top:3px}

/* ── BẢNG KIỂU SỔ — bảng "Từng người" của màn Duyệt chi lương ───────────────
   Tấn Phú chốt 23/09/2026: *"kẻ lại table giống kiểu của bên "sổ" cho dễ nhìn.
   (cũng giữ logic tự động lưu)"*.

   BẢN THỨ HAI của lưới ô màn Sổ (`Views/Ledgers/Sheet.cshtml` § LƯỚI Ô): kẻ đủ
   ô, đệm hẹp, chữ số đơn cách; hàng tiêu đề và hai cột đầu (số thứ tự · họ
   tên) DÍNH; ô NGƯỜI GÕ nền vàng như ô nhập của Lark, ô APP TÍNH nền trắng. Ghi
   chú chứ chưa gộp — luật đếm ở cms/CLAUDE.md § "Chống rác"; màn thứ ba cần thì
   gom cả hai về đây.

   Bảng vẫn mang `.list`: nó ăn luật sắp xếp + lọc (js/bang-sap-xep.js), trần bề
   ngang và vùng chạm của tiêu đề. Mấy luật dưới chỉ đè phần HÌNH.

   Bề rộng cột số thứ tự (64px) là con số DÙNG LẠI: cột họ tên dính đúng ở mép
   nó. Tính bằng px chứ không em — tiêu đề và ô mang hai cỡ chữ khác nhau, nên
   cùng một số em ra hai bề rộng và cột họ tên dính lệch. Rê chuột tô bằng
   `background-image` phủ lên màu nền: ô dính giữ nền ĐỤC của nó. */
/* Khung ÔM VỪA bảng (`fit-content`), bảng không kéo giãn cho đầy khung: kéo
   giãn là mấy ô số rộng ra cả gang tay, đúng thứ làm bảng cũ khó đọc. */
.bang-so-khung{width:fit-content; max-width:100%; overflow:auto; max-height:72vh;
  overscroll-behavior:contain; -webkit-overflow-scrolling:touch;
  border:1px solid var(--line); border-radius:10px; background:var(--surface)}
table.list.bang-so{width:max-content; border-collapse:separate; border-spacing:0;
  font-family:"JetBrains Mono",monospace; font-size:12.5px; font-variant-numeric:tabular-nums}
table.list.bang-so th,table.list.bang-so td{padding:0 7px; height:32px;
  border-right:1px solid var(--line); border-bottom:1px solid var(--line)}
table.list.bang-so thead th{position:sticky; top:0; z-index:3; height:40px; background:var(--sunken);
  color:var(--ink); border-bottom:2px solid var(--line)}
table.list.bang-so th:first-child,table.list.bang-so td.stt{position:sticky; left:0; z-index:2;
  box-sizing:border-box; width:64px; min-width:64px; max-width:64px;
  background:var(--sunken); text-align:right}
table.list.bang-so td.stt input[type=checkbox]{width:16px; height:16px; margin:0 5px 0 0; vertical-align:middle}
table.list.bang-so th:nth-child(2),table.list.bang-so td.ten{position:sticky; left:64px; z-index:2;
  box-shadow:1px 0 0 var(--line)}
table.list.bang-so thead th:first-child,table.list.bang-so thead th:nth-child(2){z-index:4}
table.list.bang-so td{background:var(--surface)}
table.list.bang-so td.nhap{background:var(--o-nhap)}
table.list.bang-so td.tinh{text-align:right}
table.list.bang-so td.chinh{font-weight:700}
table.list.bang-so tbody tr:hover td{background-image:linear-gradient(rgba(21,29,26,.05),rgba(21,29,26,.05))}
/* Ô nhập CHÌM vào ô bảng như một ô bảng tính: không viền, không nền riêng.
   Đang gõ thì nổi viền xanh "đang sửa" — đúng màu ô đang sửa của màn Sổ. */
/* Bề rộng từng ô đặt theo LỚP ô (dưới), không `width:100%`: ô trăm phần trăm
   trong một bảng tự canh cột thì cột lấy bề rộng mặc định của ô nhập — gần hai
   trăm điểm ảnh cho một con số hai chữ số. */
table.list.bang-so td.nhap input{height:30px; padding:0 2px; border:0; border-radius:0;
  background:transparent; font:inherit; color:inherit; box-shadow:none}
table.list.bang-so td.nhap input:focus{outline:2px solid var(--sua-vien); outline-offset:-2px;
  background:var(--sua-nen)}
table.list.bang-so td.nhap input[readonly]{background:transparent; color:var(--muted)}
table.list.bang-so td.nhap input.o-cong{width:5ch; text-align:right}
table.list.bang-so td.nhap input.o-tien{width:10ch; text-align:right}
table.list.bang-so td.nhap input.ten-nguoi{width:14ch; font-size:12.5px; font-weight:600}
table.list.bang-so td.nhap input.o-bo-phan{width:11ch}
table.list.bang-so td.nhap input.o-stk{width:15ch}
/* Ghi chú là câu chữ, không phải số: phông chữ thường và trần rộng hơn, để
   dòng có lời máy đọc không cao gấp bốn dòng khác. */
table.list.bang-so td.o-chu{padding-top:4px; padding-bottom:4px; max-width:22em;
  font-family:"Be Vietnam Pro",sans-serif; font-size:12px}

/* Khối người LƯƠNG 0 Đ — ngay dưới bảng đi làm, cùng hình, để soát. */
.khong-di-lam{margin-top:16px}
.khong-di-lam h3{font-size:14px; margin:0 0 4px}
.khong-di-lam > p{font-size:13px; margin:0 0 8px}

/* Khối giờ công theo bộ phận: cột số canh phải, dòng Tất cả đậm. */
table.list.gio-cong th:not(:first-child),table.list.gio-cong td:not(:first-child){text-align:right}
table.list.gio-cong tfoot td{font-weight:700; border-top:2px solid var(--line); border-bottom:0}

/* CHÚ THÍCH KIỂU BẢN ĐỒ cho màn bộ token cũ — công thức, không phải đoạn văn
   (../CLAUDE.md § Luật viết tài liệu, mục 12). Bản thứ hai của `pre.ct-ct` bên
   nocturne.css: màn Duyệt chi lương không nạp file đó. `pre` vì khoảng trắng
   canh cột LÀ nội dung; cuộn ngang chứ không bẻ dòng. */
pre.cong-thuc{margin:0 0 10px; padding:8px 12px; overflow-x:auto; border-left:3px solid var(--expense);
  border-radius:6px; background:var(--sunken); color:var(--muted);
  font:12px/1.6 "JetBrains Mono",monospace}
.bang-goc{max-height:340px; overflow:auto; background:var(--sunken); border:1px solid var(--line);
  border-radius:8px; padding:10px; font-family:"JetBrains Mono",monospace; font-size:11.5px;
  white-space:pre; line-height:1.5}


/* ── MẤY BƯỚC CỦA MỘT LƯỢT ĐỌC — màn phiên còn đang đọc ────────────────────
   Cùng ý nghĩa với mấy ô trên hộp tiến trình, nhưng đây là bản KHÔNG-JS: trang
   tự làm mới bằng <meta refresh> nên trạng thái phải nằm sẵn trong HTML máy chủ
   dựng ra, không phải do script tô. Khai ở đây chứ không ở nocturne.css vì màn
   duyệt Nhập hàng không dùng hệ Nocturne. */
.buoc-doc{list-style:none;margin:0 0 10px;padding:0;display:flex;flex-direction:column;gap:8px}
.buoc-doc li{display:flex;align-items:center;gap:10px;min-height:28px;color:var(--muted)}
/* Chấm tròn vẽ bằng ::before — một ký tự hình thì mỗi máy một nét, mà ba trạng
   thái này phải phân biệt được bằng HÌNH chứ không chỉ bằng màu. */
.buoc-doc li::before{content:"";flex:none;width:14px;height:14px;border-radius:50%;
  border:2px solid var(--line);background:#fff}
.buoc-doc li.xong{color:var(--ink)}
.buoc-doc li.xong::before{border-color:var(--expense);background:var(--expense)}
.buoc-doc li.chay{color:var(--ink);font-weight:600}
.buoc-doc li.chay::before{border-color:var(--expense);
  background:radial-gradient(circle,var(--expense) 0 3px,#fff 3px)}


/* ── NHÃN LOẠI GIẤY và HÀNG THẺ LỌC — tab Phiên duyệt ──────────────────────
   Khai Ở ĐÂY chứ không trong <style> của view: đây là VỐN TỪ dùng chung (nhãn
   trên từng dòng, và thẻ lọc ở đầu trang phải cùng một hình cùng một màu), mà
   một khối <style> trong view đã hai lần không tới được iPhone của Tấn Phú —
   xem § "Giao diện" ở cms/CLAUDE.md. */
/* Nhãn loại giấy: bo TRÒN HẾT (nó nói "cái này là gì"), nền màu đặc của
   chính loại đó, chữ trắng. Trắng trên cả bảy màu là thứ đã dùng sẵn ở dải
   `.dai-giay` đầu mỗi màn duyệt — cùng bộ màu, nên không phải đo lại.

   `justify-self:start` để nhãn ôm đúng bề ngang chữ. Phải là `justify-`, KHÔNG
   phải `align-`: `.phien > .mo` là lưới, mà trong lưới `align-self` canh theo
   trục DỌC còn bề ngang là `justify-self`. Viết nhầm trục thì ô lưới vẫn kéo
   giãn mặc định và nhãn thành một vệt màu chạy hết chiều ngang dòng — đo ra
   906px trên màn 1100px. Trông vẫn "có màu" nên nhìn lướt không thấy sai. */
.tag-giay{display:inline-block; justify-self:start; padding:2px 10px; border-radius:999px;
  font-size:12.5px; font-weight:700; letter-spacing:.01em;
  /* Không màu = controller lạ, chưa khai trong LoaiGiay. Xám trung tính chứ
     KHÔNG đoán bừa một màu: hai loại giấy chung màu còn khó hiểu hơn một
     nhãn xám, vì nhìn màu là người ta tin luôn. */
  background:var(--sunken); color:var(--muted)}
.tag-giay.g-sanluong {background:var(--giay-sanluong);  color:var(--giay-sanluong-chu)}
.tag-giay.g-dongthung{background:var(--giay-dongthung); color:var(--giay-dongthung-chu)}
.tag-giay.g-valot    {background:var(--giay-valot);     color:var(--giay-valot-chu)}
.tag-giay.g-chungtu  {background:var(--giay-chungtu);   color:var(--giay-chungtu-chu)}
.tag-giay.g-bangluong{background:var(--giay-bangluong); color:var(--giay-bangluong-chu)}
.tag-giay.g-phieuchi {background:var(--giay-phieuchi);  color:var(--giay-phieuchi-chu)}
.tag-giay.g-thumui   {background:var(--giay-thumui);    color:var(--giay-thumui-chu)}
.tag-giay.g-nhaphang {background:var(--giay-nhaphang);  color:var(--giay-nhaphang-chu)}
.tag-giay.g-quytamung{background:var(--giay-quytamung);color:var(--giay-quytamung-chu)}

/* CỤM NÚT LỌC THEO LOẠI — Tấn Phú chốt 17/09/2026: "Thêm filter theo loại dữ
   liệu nhập. Filter theo kiểu tag nha", và 22/09/2026: *"Đừng làm trượt ngang
   nữa. Thành cụm nút đi."*

   Là thẻ <a> chứ không phải nút JS: lọc ở MÁY CHỦ nên nó chạy khi không có JS,
   và đường dẫn kèm bộ lọc gửi cho người khác được.

   XUỐNG DÒNG, KHÔNG CUỘN. Đo ở khung 390px: chín thẻ trải 1273px, tức chỉ HAI
   thẻ lọt vào màn và bảy loại giấy còn lại nằm sau một quãng quẹt 907px mà trên
   iPhone thì không có thanh cuộn nào báo là chúng có ở đó. Một bộ lọc người ta
   không biết là có thì bằng không có. Cụm nút cao thêm vài dòng, nhưng cao là
   thứ cuộn dọc giải quyết được — còn khuất thì không. */
.loc-tag{display:flex; gap:8px; flex-wrap:wrap;
  padding:2px 0 8px; margin-bottom:6px;
  /* `min-width:0` là CHỐT CHẶN, và đo rồi: với vỏ trang hiện nay (`.page` là ô
     duy nhất bọc nó) thì bỏ đi cũng không đổi gì. Giữ vì nó chặn đúng một cú đã
     cắn repo này hai lần: một ô flex mặc định `min-width:auto` = bằng bề rộng
     NỘI DUNG, nên chỉ cần ai đó bọc cụm nút vào một lớp flex/grid nữa là nó
     thôi xuống dòng và đẩy CẢ TRANG rộng ra. Cùng họ với luật
     `white-space:nowrap` ở cms/CLAUDE.md § "Giao diện". */
  min-width:0; max-width:100%}
/* KHÔNG làm mờ thẻ thường (Tấn Phú chốt 18/09/2026: *"tôi cần kiểu sáng k âm"*).
   Luật cũ để `opacity:.55` cho thẻ chưa chọn, và CHÍNH NÓ là thứ làm cả hàng
   thẻ xỉn đi: nền màu pha 55% với nền trang trắng ra pastel xám — trích pixel
   ảnh anh gửi thì tím #7a3d8f hoá #9991bb, xanh #3d5a9e hoá #95a2c8. Đổi màu
   token cho tươi mà giữ lớp mờ này thì cũng ra đúng chỗ cũ.
   Nên bỏ hẳn độ mờ khỏi VIỆC phân biệt chọn/chưa chọn: nó đã là kênh yếu (ghi
   ngay ở luật cũ: "ra nắng thì chênh lệch độ mờ gần như không thấy"), lại còn
   ăn mất màu.
   40px vùng chạm — luật cứng cho mọi thứ ngón tay ướt lúc 6h sáng phải bấm. */
.loc-tag > a{flex:none; display:inline-flex; align-items:center; min-height:40px;
  text-decoration:none; white-space:nowrap; border:2px solid transparent}
/* Rê chuột thì SẪM đi chứ không sáng lên — nền nay sáng sẵn, sáng thêm là nhạt
   đi chứ không ai thấy nó phản hồi. Cùng nếp với nút vàng ở cms/CLAUDE.md. */
.loc-tag > a:hover{filter:brightness(.93)}
/* THẺ ĐANG CHỌN: viền mực + quầng. Vẫn phân biệt bằng HAI kênh như luật cũ đòi,
   nhưng cả hai đều là kênh HÌNH, không kênh nào ăn vào màu nền. */
.loc-tag > a.chon{border-color:var(--ink); box-shadow:0 0 0 3px rgba(21,29,26,.16)}
/* .9 chứ không .8: chữ trắng trên nền sáng vốn đã ít chênh, hạ thêm một nấc nữa
   là con số — thứ người ta liếc vào hàng thẻ để đọc — mờ trước cả tên loại. */
.loc-tag .so{font-weight:600; opacity:.9; margin-left:6px}

/* Ô TÌM + KHOẢNG NGÀY GỬI — Tấn Phú chốt 22/09/2026: *"cả ô tìm kiếm và lọc
   ngày"*. Một hàng ở khổ máy tính, xuống dòng khi chật; ô tìm ăn hết chỗ thừa
   còn hai ô ngày giữ bề ngang của chúng.

   `flex:1 1 200px` kèm `min-width:0` cho ô tìm: thiếu `min-width` thì một ô
   `input` trong flex lấy bề rộng mặc định của nó làm SÀN (khoảng 170px cộng
   đệm), nên ở khổ hẹp nó không co lại được và đẩy cả hàng rộng ra — cùng họ với
   cú đã ghi ở `.loc-tag`.

   Vùng chạm 40px cho cả ba ô và cái nút: đây là thứ ngón tay ướt lúc 6h sáng
   phải bấm, đúng luật của màn nhập liệu. */
.tim-phien{display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:2px}
.tim-phien .o-tim{flex:1 1 200px; min-width:0; display:flex}
.tim-phien input[type=search],.tim-phien input[type=date]{min-height:40px; min-width:0}
.tim-phien input[type=search]{width:100%}
.tim-phien label{display:flex; align-items:center; gap:6px; font-size:12.5px; color:var(--muted)}
.tim-phien button{min-height:40px; white-space:nowrap}
/* Hai ô ngày và nút Tìm đi CÙNG MỘT HÀNG: gói chúng vào một ô flex con thì cả
   cụm xuống dòng một lượt, thay vì mỗi ô rơi xuống một hàng. Đo ở 390px: ba
   hàng còn hai, dòng đầu của bảng lên được 63px. */
.tim-phien .cum-ngay{display:flex; gap:8px; align-items:center; flex:1 1 auto; min-width:0}
.tim-phien .cum-ngay label{flex:1 1 0; min-width:0}
.tim-phien .cum-ngay input[type=date]{flex:1 1 0; min-width:0; width:100%}

/* ĐOẠN MÔ TẢ GẤP LẠI TRÊN ĐIỆN THOẠI, bung hẳn ở khổ máy tính — Tấn Phú chốt
   22/09/2026, sau khi bỏ trượt ngang đẩy dòng đầu của bảng xuống 622px trên
   khung 390×844.

   Chữ KHÔNG mất đi đâu: `<details>` gấp chứ không giấu, và ở khổ máy tính hai
   luật dưới bung nó ra nên câu chữ y nguyên như trước.

   HAI luật bung, cố ý giữ cả hai vì chúng đánh vào HAI cơ chế khác nhau của hai
   đời trình duyệt: bản cũ che nội dung bằng `display:none` của trình duyệt (nên
   `display:block` thắng), bản mới (Chrome 131+) che bằng `::details-content` với
   `content-visibility`. Đo trong Chromium hôm nay thì MỖI luật một mình đã đủ —
   ghi ra đây để người sau đừng tưởng cái còn lại đang gánh việc gì.

   Ca xấu nhất nếu một trình duyệt không nhận cả hai: đoạn mô tả ở lại dạng gấp
   trên máy tính, bấm một cái là mở. Mất một cú bấm, không mất chữ nào. */
.mo-ta-gap > summary{cursor:pointer}
.mo-ta-gap > summary > strong{font-weight:700}
.mo-ta-gap[open] > summary{margin-bottom:6px}
@media (min-width:561px){
  /* `display:inline` + bỏ mũi tên: ở khổ máy tính nó phải đọc ra một đoạn văn
     bình thường, không phải một cái hộp gấp đang mở. */
  .mo-ta-gap > summary{display:inline; cursor:auto; list-style:none}
  .mo-ta-gap > summary::-webkit-details-marker{display:none}
  .mo-ta-gap > p{display:block}
  .mo-ta-gap::details-content{content-visibility:visible; block-size:auto}
}

/* CỤM NÚT THỨ HAI — LỌC THEO TRẠNG THÁI (Tấn Phú chốt 22/09/2026).
   Mượn cả máy móc của `.loc-tag` (xuống dòng, vùng chạm 40px, viền + quầng cho
   nút đang chọn) nhưng mang HÌNH của `.badge`: bo 6px chứ không phải viên thuốc
   bo tròn. Đó là luật ở cms/CLAUDE.md § "Màu" — phân biệt bằng HÌNH, không chỉ
   bằng màu; và hai cụm nút nằm chồng nhau mà cùng một hình thì mắt đọc ra MỘT
   cụm bị xuống dòng, tức là bấm nhầm cụm.
   `.badge` khai `font-size:11px`, nhỏ hơn hẳn nhãn loại giấy — nhấc lên 12,5px
   cho bằng cụm trên, vì đây là thứ phải BẤM chứ không phải thứ để liếc. */
.loc-tt > a{font-size:12.5px; padding:2px 10px}
/* Con số bám sát chữ của huy hiệu: `.badge` vốn là khối chữ đặc, nên khoảng hở
   6px của cụm trên ở đây trông như hai mẩu rời. */
.loc-tt .so{margin-left:5px; opacity:.85}
/* Nút "bỏ lọc" của cụm này: XÁM TRUNG TÍNH. `.badge` trần không khai màu nào nên
   nó trong suốt — phải cho nó nền, không thì nhìn ra một nút hỏng. Cùng vai với
   nút "Tất cả" của cụm trên, và cụm trên cũng xám vì đúng lý do ấy. */
.loc-tt > a.the-moi{background:var(--sunken); color:var(--muted); border-color:var(--line)}

/* ĐIỆN THOẠI: co cụm nút lại. Bỏ trượt ngang là đổi một quãng quẹt lấy mấy hàng
   nút, và mấy hàng ấy đẩy dòng đầu tiên của bảng xuống — đo ở 390×844 thì dòng
   đầu rơi xuống 690px, tức gần hết màn là nút. Co đệm và khoảng hở kéo được nó
   lên; KHÔNG đụng `min-height:40px`, vì vùng chạm là luật cứng cho ngón tay ướt
   lúc 6h sáng, không phải chỗ để tiết kiệm. */
@media (max-width:560px){
  .loc-tag{gap:6px; padding-bottom:6px; margin-bottom:2px}
  .loc-tag > a{padding-inline:9px}
  .loc-tt > a{padding-inline:8px}
}

/* ── GALLERY XẤP VỪA GỬI ────────────────────────────────────────────────────
   Màn chờ bày lại mấy tấm người ta vừa gửi (Tấn Phú chốt 18/09/2026). Khai ở
   ĐÂY chứ không trong <style> của view: hai màn chờ dùng chung, và § "Giao diện"
   đã ghi hai lần rằng một nguồn kiểu thứ hai có thể không tới được iPhone.

   `auto-fill` + `minmax` chứ không đếm cột cứng: xấp có khi một tấm, có khi tám.

   `min(140px,100%)` là CHỐT DỰ PHÒNG, không phải thuốc cho một lỗi đã thấy — nói
   rõ để người sau đừng tin nhầm: ĐO ở 320 · 360 · 390 · 1280px thì bản
   `minmax(140px,1fr)` trần cũng KHÔNG tràn, vì khung nội dung hẹp nhất (320px
   trừ lề) vẫn rộng hơn 140px nên lưới tự rút còn hai cột. Nó chỉ cứu ca khung
   hẹp hơn chính bề rộng tối thiểu của cột — chưa màn nào của app chạm tới. Giữ
   vì nó không tốn gì; đừng ghi công cho nó việc mà nó chưa từng làm. */

/* Ngày hàng về trên dòng phiên: đậm hơn giờ bấm gửi một bậc, và nhãn nguồn nhạt
   đi — người đọc liếc thấy NGÀY trước, còn "theo đâu" là thứ đọc khi cần. */
.ngay-nhap{display:block}
.ngay-nhap .nguon{opacity:.75}

.xap-gui{margin-top:18px}
.xap-gui h2{margin:0 0 4px; font-size:16px}
.xap-gui > .muted{margin:0 0 12px; font-size:13px}
/* THẺ NHỎ, KHÔNG GIÃN RA CHO ĐẦY HÀNG — Tấn Phú chốt 18/09/2026: *"Mấy gallery
   hình ntn width tối đa 250 thôi. Canh lại cho nó nhỏ. Cho giống gallery"*.

   Bản trước để track là `1fr` nên hai thẻ nuốt trọn bề ngang: ở màn 390px mỗi
   thẻ ra 190×253px — to gần bằng bộ soi ảnh ngay dưới, mà việc của nó chỉ là
   LIẾC. Nay track có TRẦN, và phần thừa dồn về bên trái (`justify-content:start`)
   chứ không chia đều cho mấy thẻ.

   Trần 250px là con số Tấn Phú đặt; track 115px là cái quyết hình thật, để ở màn
   390px xếp được BA thẻ một hàng thay vì hai. Giữ cả hai: trần kia còn chặn khi
   có người nới track lên sau này. */
.xap-luoi{display:grid; gap:10px; justify-content:start;
  grid-template-columns:repeat(auto-fill,minmax(min(96px,100%),115px))}
.xap-the{margin:0; min-width:0; max-width:250px}
.xap-the a{display:block; text-decoration:none}
.xap-the img{display:block; width:100%; aspect-ratio:3/4; object-fit:cover;
  border:1px solid var(--line); border-radius:10px; background:var(--surface)}
.xap-the figcaption{margin-top:5px; font-size:12px; color:var(--muted); text-align:center}

/* Thẻ CHỮ đứng cùng lưới với thẻ ảnh nên phải cao bằng: cùng `aspect-ratio`,
   và chữ dài thì tự cuộn TRONG thẻ chứ không kéo ô lưới cao ra. */
.xap-chu .chu{aspect-ratio:3/4; overflow:auto; padding:10px;
  border:1px solid var(--line); border-radius:10px; background:var(--surface);
  font-size:12.5px; line-height:1.45; white-space:pre-wrap; overflow-wrap:break-word}

/* ── LƯỚI CHỌN LOẠI GIẤY (màn Nhập liệu bằng AI) ────────────────────────────
   Tấn Phú chốt 18/09/2026: *"loại giấy tờ: hãy đổi thành icon menu. Chia làm
   grid. Và khi vào trang này. Hiển thị luôn grid icon. Vào chức năng nào thì
   mới hiện upload"*.

   Ẩn/hiện khối gửi bằng CHECKBOX + CSS, không bằng JS: màn này là cửa vào của
   mọi lượt nhập liệu, nên nó phải chạy cả khi JS chưa tới nơi — § "Giao diện"
   đã ghi hai lần một nguồn thứ hai không tới được iPhone của Tấn Phú.

   Ô checkbox phải ở NGOÀI luồng nhưng KHÔNG `display:none`: ẩn hẳn là bàn phím
   không tab tới được, mà `<label>` thì không tự nhận phím (cùng cái bẫy đã ghi
   ở § "Mở hộp chọn tệp thì dùng label"). Nên đẩy nó ra khỏi màn bằng vị trí. */
.mo-gui{position:absolute; width:1px; height:1px; opacity:0; pointer-events:none}
.khoi-gui{display:none}
.mo-gui:checked ~ .khoi-gui{display:block}
/* Ô đang mở đổi viền cho biết mình đang ở đâu — cùng nếp "thẻ đang chọn đổi
   VIỀN, không đổi màu ruột" của § "Bảng và thẻ". */
.mo-gui:checked ~ .luoi-giay .o-tunhan{border-color:var(--expense); box-shadow:0 0 0 1px var(--expense)}
.mo-gui:focus-visible ~ .luoi-giay .o-tunhan{outline:2px solid var(--expense); outline-offset:2px}

.luoi-giay{display:grid; gap:10px; margin:4px 0 18px;
  grid-template-columns:repeat(auto-fit,minmax(min(160px,100%),1fr))}
.luoi-giay > *{display:grid; grid-template-rows:auto auto 1fr; gap:2px;
  text-decoration:none; color:inherit; cursor:pointer; min-width:0;
  border:1px solid var(--line); border-top-width:4px; border-radius:11px;
  padding:12px 13px; background:var(--surface)}
.luoi-giay > *:hover{border-color:var(--expense)}
.luoi-giay .ic{width:26px; height:26px; margin-bottom:4px;
  fill:none; stroke:currentColor; stroke-width:1.6;
  stroke-linecap:round; stroke-linejoin:round}
.luoi-giay b{font-size:14px; line-height:1.3}
.luoi-giay span{font-size:12px; line-height:1.4; color:var(--muted)}
/* Vạch trên mang màu loại giấy — cùng bộ màu với nhãn ở tab Phiên duyệt, nên
   nhìn màu là nhận ra loại, không phải đọc chữ. */
.luoi-giay .g-sanluong {border-top-color:var(--giay-sanluong)}
.luoi-giay .g-dongthung{border-top-color:var(--giay-dongthung)}
.luoi-giay .g-valot    {border-top-color:var(--giay-valot)}
.luoi-giay .g-chungtu  {border-top-color:var(--giay-chungtu)}
.luoi-giay .g-thumui   {border-top-color:var(--giay-thumui)}
.luoi-giay .g-nhaphang {border-top-color:var(--giay-nhaphang)}
.luoi-giay .g-bangluong{border-top-color:var(--giay-bangluong)}
.luoi-giay .g-phieuchi {border-top-color:var(--giay-phieuchi)}
.luoi-giay .g-quytamung{border-top-color:var(--giay-quytamung)}
.luoi-giay .g-tunhan   {border-top-color:var(--expense)}

/* ── HỘP XEM ẢNH của gallery xấp vừa gửi ────────────────────────────────────
   Tấn Phú chốt 18/09/2026: *"Gallery nhấn vào modal image hiện lên, có dấu
   close"*.

   Mở bằng `:target`, KHÔNG bằng JS — màn chờ tồn tại đúng cho ca mạng chập.
   Được thêm ba thứ: nút Back của điện thoại đóng hộp, đường dẫn kèm `#anh-3`
   mở đúng tấm đó, và không dính cái bẫy `<dialog>` phải khai `display` dưới
   `[open]`.

   `z-index` 60 — trên `.combo-list` (30) và trên mọi thứ dính đỉnh, để hộp
   không bị một danh sách gợi ý hay một tấm ảnh dính đè lên nếu sau này có màn
   khác dùng lại nó. */
.hop-anh{display:none}
/* Ba HÀNG: thanh trên (có nút đóng) · ảnh · chân. Nút đóng nằm TRONG HÀNG, không
   `position:absolute` bám mép trên của khung nhìn — xem lý do ngay dưới. */
.hop-anh:target{display:grid; position:fixed; inset:0; z-index:60;
  grid-template-rows:auto 1fr auto; place-items:center; gap:10px; padding:14px;
  background:rgba(20,16,15,.92)}
/* MÉP TRÊN CỦA KHUNG NHÌN KHÔNG PHẢI MÉP TRÊN CỦA MÀN (18/09/2026).
   `position:fixed; inset:0` trên iOS phủ cả dải nằm SAU thanh địa chỉ Safari, nên
   nút đóng đặt `top:10px` rơi vào đúng dải đó: nhìn thì thấy nó, chạm thì trúng
   thanh địa chỉ. Tấn Phú khoanh đỏ đúng chỗ ấy — *"Nút close hỏng"*.
   `env(safe-area-inset-top)` KHÔNG cứu được: thanh địa chỉ Safari không phải
   safe-area inset, và trang này cũng không khai `viewport-fit=cover`.
   Nên chừa hẳn một dải: đệm trên 56px, đủ cho thanh địa chỉ ở mọi trạng thái. */
.hop-anh:target{padding-top:max(56px, env(safe-area-inset-top, 0px) + 14px)}
/* Nền phủ kín và nằm DƯỚI ảnh: chạm ra ngoài ảnh là đóng. */
.hop-anh > .hop-nen{position:absolute; inset:0; z-index:0}
.hop-anh > img{position:relative; z-index:1; min-height:0;
  max-width:100%; max-height:100%; object-fit:contain; border-radius:8px}
/* Thanh trên: nhãn tấm bên trái, nút đóng bên phải. Trải hết bề ngang để nút
   đóng về đúng mép phải mà không cần `position:absolute`. */
.hop-anh > .hop-dau{position:relative; z-index:2; justify-self:stretch;
  display:flex; align-items:center; justify-content:space-between; gap:10px}
.hop-anh > .hop-dau .ten{color:var(--trang); font-size:13.5px; font-weight:600;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* Nút đóng 44px — luật vùng chạm, và ở đây còn phải bấm trúng khi tay ướt. */
.hop-anh .hop-dong{flex:none; z-index:2;
  display:grid; place-items:center; width:44px; height:44px;
  font-size:26px; line-height:1; text-decoration:none;
  color:var(--ink); background:var(--trang); border:1px solid rgba(0,0,0,.15);
  border-radius:50%; box-shadow:0 2px 10px rgba(0,0,0,.45)}
.hop-anh > .hop-chan{position:relative; z-index:2; margin:0;
  font-size:13px; color:var(--trang); text-align:center}
.hop-anh > .hop-chan a{color:var(--trang); text-decoration:underline}

/* Nút "Xử lý lại" ngồi cùng hàng với huy hiệu máy đọc nên thấp hơn nút chính
   thường — nhưng KHÔNG dưới 34px: nó vẫn là nút ngón tay bấm. Form bọc nó phải
   `display:inline` để không xuống hàng, và `margin:0` để không đẩy hàng cao ra. */
.xu-ly-lai{display:inline; margin:0}
.btn-primary.nho{min-height:34px; padding:0 12px; font-size:12.5px}
/* Trên thẻ `<a>` (nút "Tính đơn giá" của màn Chi phí): đệm dọc bằng 0 nên chữ
   phải canh giữa bằng flex, không thì nó dính mép trên của cái nút cao 34px. */
a.btn-primary.nho{display:inline-flex; align-items:center}

/* ── HUY HIỆU "MÁY ĐỌC" + hộp kể máy nói gì ─────────────────────────────────
   Tấn Phú chốt 18/09/2026: *"Thiết kế 1 icon Xử lý AI … Khi nhấn vào hiển thị
   modal các thông tin này. Ở page k cần hiện. Rối layout."*

   Mở bằng `:target` như hộp xem ảnh — màn duyệt phải mở được cả khi một tệp JS
   không tới nơi. Khác hộp ảnh ở chỗ nó có PHẦN RUỘT CUỘN được: nguyên văn máy
   trả về có khi mấy nghìn ký tự. */
.may-doc-hang{display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:10px 0}
.may-doc-hang .muted{font-size:12.5px}
.may-doc{display:inline-flex; align-items:center; gap:7px; min-height:40px;
  padding:0 14px; border:1px solid var(--line); border-radius:999px;
  background:var(--surface); color:inherit; text-decoration:none;
  font-size:13.5px; font-weight:600}
.may-doc:hover{border-color:var(--expense)}
.may-doc .ic{width:19px; height:19px; flex:none;
  fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round}
/* Hai mức kêu lên. Dùng lại đúng hai màu của thang huy hiệu, đừng đẻ màu mới. */
.may-doc.muc-canh{border-color:var(--tt-canh-nen); background:var(--warn-soft)}
.may-doc.muc-loi {border-color:var(--danger)}

/* TỪNG NHÀ MỘT CHIP: đỏ nếu nó báo lỗi, xanh nếu nó đọc được (Tấn Phú chốt
   18/09/2026). Không đẻ màu mới — `--do` là đỏ báo lỗi đã dùng khắp app, còn
   `--tt-ok` là ô NGỌC của thang huy hiệu, thứ repo vẫn dùng cho "thành công".

   Nền TÔ ĐẶC chứ không chỉ đổi màu chữ: hai chip nằm sát nhau, mà phân biệt
   bằng riêng sắc chữ thì người phân biệt màu kém đọc ra hai cái giống hệt. Tô
   nền là còn thêm manh mối HÌNH.

   ĐỎ Ở ĐÂY LÀ `--danger`, KHÔNG PHẢI `--do`. Đã dính một lần trong chính lượt
   làm này: `--do` chỉ khai trong `nocturne.css`, mà màn duyệt chạy bộ token cũ
   nên KHÔNG nạp file đó — `var(--do)` thành giá trị không hợp lệ, và nền chip
   ra TRONG SUỐT. Không có gì báo: CSS sai kiểu đó không ném, không log, chỉ là
   một cái chip mất màu. Bài kiểm Chromium đo `backgroundColor` mới thấy.

   Quy tắc rút ra: luật nào nằm trong `fqfvn.css` thì CHỈ được dùng token khai
   trong `fqfvn.css` — file kia có thể không được nạp. */
.may-doc .nha{display:inline-flex; align-items:center; padding:2px 9px;
  border-radius:999px; font-size:12.5px; font-weight:600;
  background:var(--line); color:var(--ink)}
.may-doc .nha.ok {background:var(--tt-ok-nen); color:var(--tt-ok)}
.may-doc .nha.xau{background:var(--danger); color:var(--trang)}
/* Gạch ngang tên nhà đã hỏng — manh mối thứ hai, không chỉ có màu. */
.may-doc .nha.xau{text-decoration:line-through; text-decoration-thickness:1.5px}

.hop-may{display:none}
.hop-may:target{display:grid; position:fixed; inset:0; z-index:60;
  place-items:center; padding:12px; background:rgba(20,16,15,.92)}
.hop-may > .hop-nen{position:absolute; inset:0; z-index:0}
.hop-may > .than{position:relative; z-index:1; display:flex; flex-direction:column;
  width:min(720px,100%); max-height:100%;
  background:var(--surface); border-radius:12px; overflow:hidden}
.hop-may .dau{display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:10px 12px; border-bottom:1px solid var(--line)}
.hop-may .dau h2{margin:0; font-size:16px}
.hop-may .hop-dong{display:grid; place-items:center; width:44px; height:44px; flex:none;
  font-size:26px; line-height:1; text-decoration:none; color:inherit; border-radius:50%}
.hop-may .hop-dong:hover{background:var(--bg)}
/* Ruột cuộn, KHÔNG phải cả hộp: thanh tiêu đề và nút đóng phải luôn nhìn thấy —
   nguyên văn máy trả về dài mấy nghìn ký tự thì cuộn mãi mới thấy đường ra. */
.hop-may .ruot{overflow:auto; -webkit-overflow-scrolling:touch; padding:12px;
  display:grid; gap:10px}

.the-may{border:1px solid var(--line); border-left-width:4px; border-radius:10px;
  padding:10px 12px; min-width:0}
.the-may > b{display:block; font-size:13px; margin-bottom:5px}
.the-may > p{margin:0; font-size:13px; line-height:1.5}
.the-may > ul{margin:0; padding-left:18px; font-size:12.5px; line-height:1.5}
.the-may > pre{margin:0; max-height:40vh; overflow:auto; font-size:12px; line-height:1.45;
  white-space:pre-wrap; overflow-wrap:break-word}
.the-may.canh{border-left-color:var(--tt-canh-nen); background:var(--warn-soft)}
.the-may.xau {border-left-color:var(--danger)}

/* ── LƯỚI Ô TICK của màn Nhập hàng ──────────────────────────────────────────
   Tấn Phú chốt 18/09/2026: *"Nếu tờ giấy có danh sách khối lượng thành phần,
   thì hiển thị các số hạng và check giống sản lượng"*.

   Xếp theo CỘT như trên giấy, không chảy tràn theo bề rộng máy: người duyệt dò
   ngang từng cột với tấm ảnh, mà 50 con số chảy tràn thì phải đếm bằng ngón tay
   mới biết mình đang ở số nào. Cùng lý do với `row`/`col` của màn Sản lượng. */
.luoi-can{margin-top:8px; padding:8px 10px; border:1px dashed var(--line); border-radius:9px}
.luoi-can > .dau{display:flex; flex-wrap:wrap; align-items:center; gap:8px 12px; margin-bottom:6px;
  font-size:13px}
.luoi-can .dem{color:var(--muted); font-size:12.5px}
/* Nút "Tick cả dòng" ngồi trong dòng tiêu đề của lưới nên thấp hơn nút phụ
   thường — nhưng KHÔNG dưới 34px: nó là nút ngón tay ướt bấm lúc 6h sáng. */
.luoi-can .btn-phu.nho{min-height:34px; padding:0 10px; font-size:12.5px}
.luoi-can .cum{display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-bottom:6px}
.luoi-can .cum > .ten{flex:none; width:44px; font-size:11.5px; color:var(--muted)}
/* ĐIỆN THOẠI: TÊN CỘT XUỐNG DÒNG RIÊNG (Tấn Phú chốt 18/09/2026 — *"trên mobile:
   Cột 1 <xuống dòng> các số hạng"*).

   Nhãn ngồi cùng hàng thì chỉ hàng ĐẦU của cột bị thụt vào 50px, mấy hàng sau
   thì không — nên một cột năm ô bày ra hình răng cưa, và mắt mất luôn cái mốc
   để dò dọc xuống. Mà dò dọc chính là việc người duyệt đang làm: so từng cột
   trên màn với từng cột trên tấm ảnh.

   `width:100%` trong một flex `wrap` là ép mọi ô xuống hàng sau — cả cột nhận
   chung một mép trái. Khổ máy tính giữ nhãn inline: ở đó cả cột lọt MỘT hàng
   nên không có răng cưa nào, và cho nhãn một dòng riêng là phí một dòng cho
   mỗi cột. */
@media (max-width:560px){
  .luoi-can .cum > .ten{width:100%; margin-bottom:2px}
}
.luoi-can .o{display:inline-flex; align-items:center; gap:4px;
  border:1px solid var(--line); border-radius:8px; padding:2px 5px; background:var(--surface)}
/* Ô người duyệt đã sửa khác máy: viền đứt, nhìn là biết số nào từ tay người ra. */
.luoi-can .o.sua{border-style:dashed; border-color:var(--expense)}
.luoi-can .o .kg{width:4.2em; min-height:34px; padding:2px 4px; border:0; background:none;
  font-size:14px; text-align:right}
/* Ô số ĐANG KHOÁ: con trỏ vẫn là con trỏ bấm, vì cả cái `.o` là vùng bấm TICK —
   để nó ra con trỏ chữ I là mời người ta gõ vào chỗ không gõ được. */
.luoi-can .o .kg[readonly]{cursor:pointer}
/* Mở ra gõ thì phải NHÌN THẤY là đang gõ được, không thì nhấp đúp xong không ai
   biết mình đã ở trong ô. */
.luoi-can .o .kg:not([readonly]){background:var(--trang); outline:2px solid var(--expense);
  outline-offset:1px; border-radius:4px; cursor:text}
.luoi-can .o input[type=checkbox]{width:20px; height:20px; flex:none; margin:0}
.luoi-can .cong{font-size:12px; color:var(--muted)}
/* Cột cộng ra khác tổng con tờ giấy tự viết — chỗ đáng soi nhất của cả lưới. */
.luoi-can .cong.lech{color:var(--danger); font-weight:600}
.luoi-can > .muted{margin:4px 0 0; font-size:12px}

/* ── DÒNG TRẠNG THÁI TỰ LƯU ──────────────────────────────────────────────────
   Tấn Phú chốt 18/09/2026: *"mặc định hãy auto save khi có thay đổi dữ liệu hoặc
   sau 1 giây… k cần phải nhấn nút Lưu Sửa nữa"*.

   Bỏ cái nút đi thì dòng này là thứ DUY NHẤT nói phiên đã an toàn chưa — nên nó
   không còn là một câu "đã lưu" nháy lên 2,5 giây rồi tắt, mà là ba trạng thái
   ở nguyên đó. Ca HỎNG phải đọc được từ xa: người duyệt sắp đóng máy mà chưa
   lưu được thì mất cả buổi gõ.

   Khai ở `fqfvn.css` chứ không ở `<style>` của view — hai màn duyệt dùng nó, và
   § "KIỂU CỦA MỘT LOẠI NÚT THUỘC VỀ fqfvn.css" đã ghi đúng cú ấy một lần rồi. */
/* `--luu` chứ KHÔNG phải `--lam-500`: cái sau chỉ khai trong `nocturne.css`, mà
   màn duyệt chạy bộ token cũ nên không nạp file đó — `var(--lam-500)` thành giá
   trị không hợp lệ và dòng chữ rơi về màu thừa hưởng, không một tiếng báo. Đúng
   cú `var(--do)` và `var(--trang)` đã dính hai lần; `TokenCssTests` bắt ngay.
   Và `--luu` mới là màu ĐÚNG NGHĨA ở đây — cùng màu với việc lưu. */
.luu-xong{align-self:center; font-size:12.5px; font-weight:600; color:var(--luu)}
.luu-xong.dang{color:var(--muted)}
.luu-xong.hong{color:var(--danger)}

/* ── DÒNG BẢNG ĐANG RÊ TÔ KEM ────────────────────────────────────────────────
   Một trong bốn quy ước bảng-và-thẻ Tấn Phú chốt 14/09/2026, và tới 18/09 mới
   có chỗ khai: *"dòng bảng đang rê tô KEM, không tô đỏ nhạt"*. Đỏ nhạt ở đây đã
   đi làm màu BÁO LỖI (`--danger-soft`), nên rê chuột qua một dòng bình thường mà
   nó ửng đỏ là mắt đọc thành "dòng này có vấn đề". */
table.list tbody tr:hover{background:var(--warn-soft)}

/* ── TAB PHIÊN DUYỆT — BẢNG, không phải thẻ chồng ─────────────────────────────
   Tấn Phú chốt 18/09/2026: *"hãy chia nó làm thành table cho nó gọn. K phải card
   phân trang, nhìn kỳ lắm"*.

   Mấy luật này nằm ở ĐÂY chứ không trong một khối `<style>` của view — cms/CLAUDE.md
   § "MỘT file CSS cho cả hệ": một nguồn kiểu thứ hai đã HAI LẦN không được áp một
   dòng nào trên iPhone của Tấn Phú, và lần thứ hai chính là một khối `<style>`
   inline. Bảng này mà mất kiểu thì nó về bảng trần của trình duyệt — vẫn đọc
   được, nhưng nút Xoá ra mẩu xám mặc định và mất vùng chạm. */
.bang-phien a.mo{display:block; color:inherit; text-decoration:none}
.bang-phien a.mo:hover{text-decoration:underline}
/* TIÊU ĐỀ SẮP XẾP — Tấn Phú chốt 18/09/2026: *"Cho tôi bộ sort. Mặc định sort
   theo toa"*. Là <a> đi về MÁY CHỦ, vì bảng này phân trang ở máy chủ (xem
   `SapPhien`); còn `bang-sap-xep.js` sắp tại chỗ cho bảng KHÔNG phân trang.

   Hai đường khác nhau nhưng **nhìn phải y hệt nhau**: mũi tên ↕ / ↑ / ↓ và vùng
   chạm 44px đã khai ở `table.list th.sap-duoc` phía trên, nên ở đây chỉ còn đúng
   một việc — cho thẻ <a> ăn hết ô. Vùng bấm bằng cả tiêu đề chứ không bằng mấy
   chữ: ngón tay ướt lúc 6h sáng không bắt được một dòng chữ 10,5px. Đệm ÂM kéo
   ngược đúng bằng đệm của `th.sap-duoc` để chữ không nhích đi pixel nào. */
.bang-phien th a.sap{display:block; margin:-14px -10px; padding:14px 10px;
  color:inherit; text-decoration:none; white-space:nowrap}
.bang-phien th a.sap:hover{color:var(--ink)}
/* CỘT ĐANG SẮP nổi lên bằng CHỮ ĐẬM, không bằng nền tô: nền tô trong hàng tiêu
   đề xám nhìn ra một ô đang được chọn để sửa. Mũi tên thì `[aria-sort]` lo. */
.bang-phien th[aria-sort]:not([aria-sort=none]) a.sap{font-weight:700; color:var(--ink)}
/* Ô tóm tắt NỚI TRẦN RỘNG RA, vẫn là một cái trần.
   Luật cứng của repo là *"luôn luôn set max width cho mọi cột của mọi table"* —
   con số 16em là MẶC ĐỊNH DÙNG CHUNG cho những cột mà chữ dài chỉ là tình cờ
   (một câu ghi chú, một tên ngân hàng). Cột này thì ngược lại: câu máy đọc tóm
   lại tờ giấy CHÍNH LÀ thứ người ta liếc vào để nhận ra phiên nào là phiên nào.
   Để nguyên 16em thì ở khổ máy tính nó cắt ngang giữa một con số
   ("2.984 kg × 23.00…") trong khi bên phải còn thừa cả gang tay.
   Vẫn cắt đuôi bằng "…" chứ không xuống dòng: dòng cao đều nhau mới gọn, và
   `title` mang câu đầy đủ. Đừng gỡ hẳn trần — gỡ là một câu dài kéo cả cột. */
.bang-phien td.tom{font-family:"JetBrains Mono",monospace; font-size:12.5px}
/* NỚI CHỈ Ở KHỔ MÁY TÍNH. Trên điện thoại bảng cuộn ngang, nên mỗi em nới ra ở
   đây là thêm một quãng phải quẹt — giữ trần chung 16em cho khổ hẹp.
   24em là con số ĐO ĐƯỢC, không phải chọn cho đẹp: ở 1280px, 24em là mức rộng
   nhất mà cả bảy cột còn vừa trong lòng thẻ. 28em là bảng bắt đầu tràn 49px và
   cột Xoá rơi ra ngoài — tức ở khổ máy tính cũng phải quẹt mới xoá được. */
@media (min-width:861px){ .bang-phien td.tom{max-width:24em} }
.bang-phien td.khi,.bang-phien td.may{color:var(--muted); font-size:12.5px}
.bang-phien td.may{text-align:right; font-family:"JetBrains Mono",monospace}
.bang-phien .phu{color:var(--muted)}
/* Nguồn của ngày nhập: ngày từ tin nhắn Zalo và ngày suy từ toa viết tay có độ
   tin khác hẳn nhau mà in ra thì nhìn y hệt. Xem `NguonNgayNhap`. */
.bang-phien .nguon{font-size:11px; color:var(--muted)}
/* PHIÊN ĐÃ GHI SỔ tô VÀNG LỢT — nhìn lướt cả bảng là biết cái nào còn phải làm.
   Không làm mờ CHỮ, chỉ đổi nền: chữ mờ trên nền mờ là hai lần mờ.

   Vàng chứ không phải nền lún xám như trước: ở tab này gần như dòng nào cũng đã
   ghi sổ, nên cái nền ấy phủ kín bảng và thôi nói được điều gì — một màu chiếm
   chín phần mười bảng thì nó là nền, không phải dấu hiệu. Vàng thì vẫn là màu
   ĐỌC RA ĐƯỢC ở khoảng cách một cánh tay, chỗ người ta cầm điện thoại.

   Rê chuột vẫn là KEM (`--warn-soft`, luật ngay dưới): nó đặc hiệu hơn nên thắng,
   và ΔE 20,3 so với vàng này nên dòng đang rê vẫn nhận ra được. */
.bang-phien tr.xong td{background:var(--xong-vang)}
.bang-phien tbody tr:hover td{background:var(--warn-soft)}
/* Nút xoá: xám lúc thường, đỏ lúc rê. Để nó đỏ sẵn thì cả bảng trông như đang
   báo lỗi, mà xoá phiên nháp là việc bình thường. */
.bang-phien td.xoa{width:1%}
.bang-phien td.xoa button{min-width:34px; min-height:34px; padding:0 11px;
  border:1px solid var(--line); border-radius:8px; background:var(--surface);
  color:var(--muted); font-size:12.5px; line-height:1;
  -webkit-tap-highlight-color:transparent; touch-action:manipulation}
.bang-phien td.xoa button:hover{border-color:var(--danger); color:var(--danger); background:var(--danger-soft)}

/* ── ĐIỆN THOẠI: CỘT LOẠI GIẤY DÍNH MÉP TRÁI, HAI CỘT NGÀY XUỐNG HAI DÒNG ─────
   Đo trong Chromium ở khung 390×844: bảng rộng 1078px trong lòng thẻ 364px, tức
   phải QUẸT 714px mới tới cột cuối. Mà cột cuối là nút Xoá — việc không lùi được
   của cả màn này.

   Quẹt tới nơi rồi thì không còn gì nói đang đứng ở DÒNG NÀO: nhãn loại giấy,
   huy hiệu trạng thái và câu tóm tắt đều đã trôi khỏi mép trái, màn chỉ còn một
   cột "2 phút 5 giây" với một dải nút Xoá y hệt nhau. Hộp hỏi lại có đọc tên
   phiên ra (`data-mo-ta`) nên bấm nhầm chưa mất gì — nhưng NGẮM thì không ngắm
   được, và cái sai chỉ lộ ra ở bước cuối cùng.

   Dính khai ở `th`/`td`, KHÔNG ở `<thead>`/`<tr>`: Safari không nhận
   `position:sticky` trên hai thẻ nhóm ấy, nên khai ở đó là một luật nằm im đúng
   trên cái máy cần nó nhất — mà nhìn ở Chromium thì vẫn thấy chạy.

   NỀN PHẢI ĐỤC và phải đi theo trạng thái dòng, không thì mấy cột kia trôi XUYÊN
   QUA ô dính. `tr.xong td` cùng độ đặc hiệu với luật nền ngay trên nên phải khai
   lại đích danh; `tbody tr:hover td` đặc hiệu hơn sẵn nên không cần bản thứ hai.
   Vạch ngăn là `box-shadow` chứ không `border-right`: bảng `border-collapse:
   collapse` thì viền của ô dính không đi theo nó lúc cuộn.

   HAI CỘT NGÀY: nhãn nguồn ("theo tin nhắn Zalo") và tên người gửi ("· minhtuan")
   xuống dòng riêng. Đứng cùng dòng với con số thì hai cột ấy ngốn 200px + 192px
   = 392px — hơn cả một màn điện thoại — cho thứ vốn là hai mẩu ngắn. Cắt ở ĐÚNG
   chỗ nối chứ không thả `white-space:normal`: mỗi dòng vẫn có độ dài chặn trên
   nên cột không phình theo dữ liệu (cms/CLAUDE.md luật 34).

   ĐO HÔM NAY, sau cả luật gỡ đệm ngay dưới: hai cột ngày còn 117px + 128px, bảng
   rộng 892px, quẹt 528px thay vì 714px — mà dòng chỉ cao thêm 2px (49 → 51). */
/* `z-index:1` là CHỐT CHẶN, và đo rồi: bỏ đi HÔM NAY cũng không đổi gì — phần
   tử có `position` vốn được vẽ trên nội dung không định vị, nên ô dính đã nằm
   trên mấy cột trôi qua mà không cần khai. Thử phá đúng dòng này thì
   `kiem-bang-phien.mjs` vẫn xanh, nên đừng tin là nó đang gác gì.
   Giữ vì nó chặn một cú rẻ tiền: ngày nào có người đặt `position:relative` lên
   một ô khác của bảng — để treo một cái hộp, một mũi tên — thì ô ấy lên ngang
   hàng và trôi ĐÈ qua cái mốc. Cùng họ với `min-width:0` ở `.loc-tag`. */
@media (max-width:560px){
  .bang-phien th:first-child,.bang-phien td.loai{
    position:sticky; left:0; z-index:1;
    background:var(--surface); box-shadow:1px 0 0 var(--line)}
  .bang-phien tr.xong td.loai{background:var(--xong-vang)}
  /* Luật rê phải khai LẠI cho riêng ô dính. `.bang-phien tr.xong td.loai` có ba
     lớp, hơn `.bang-phien tbody tr:hover td` hai lớp — nên không có dòng này thì
     rê qua một dòng đã ghi sổ là sáu ô đổi sang kem còn ô dính ở lại vàng. */
  .bang-phien tbody tr:hover td.loai{background:var(--warn-soft)}
  .bang-phien td.khi .nguon,.bang-phien td.khi .phu{display:block}
  /* NHÃN NGUỒN KHÔNG ĐƯỢC KÉO CỘT. Tám loại giấy lấy ngày ở tám chỗ, và nhãn
     dài nhất ("ngày chuyển tiền (+2 ngày nữa)") đo ra 195px — tức một mình nó
     ăn hơn nửa khung 364px và nuốt đúng phần bề ngang mà luật xuống-hai-dòng
     ngay trên vừa tiết kiệm được.
     Cắt đuôi nhãn chứ không cắt NGÀY: con số là thứ người ta liếc vào, nhãn chỉ
     nói nó từ đâu ra — và câu đầy đủ vẫn còn trong `title` của ô. */
  .bang-phien td.khi{max-width:9em}
  .bang-phien td.khi .nguon{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
}

/* Ô CUỘN NGANG CỦA BẢNG NÀY KHÔNG CÓ ĐỆM NGANG — chỉ ở khổ điện thoại.
   `.card.table-wrap` vừa là mặt thẻ vừa là ô cuộn, mà `.card` có `padding:20px`.
   Trình duyệt cắt tràn ở hộp ĐỆM, nên hai dải 20px ấy KHÔNG phải đệm: chúng là
   chỗ nội dung đã cuộn qua vẫn hiện ra. Ảnh chụp lúc dựng cột dính cho thấy đúng
   thế — mẩu "…6" và "…a Za" của hai cột ngày nằm chình ình bên TRÁI nhãn loại
   giấy, tức cái mốc vừa dựng lên thì có thứ trôi qua trước mặt nó. Ô dính không
   lùi ra che được dải ấy: nó bị chặn bởi mép trái của chính cái bảng.

   Vì sao MỘT LỚP RIÊNG chứ không sửa thẳng `.card.table-wrap` cho cả hệ: dải rò
   ấy có ở cả 15 màn đang dùng khuôn này, nhưng chỉ bảng này có cột DÍNH — và chỉ
   khi có cột dính thì dải rò mới từ "một mẩu chữ bị cắt ở mép" thành "có thứ
   chạy qua trước mặt cái mốc". Khác nhau ở chức năng nên được phép khác nhau ở
   hình; gỡ đệm cho cả 15 màn là chuyện riêng, hỏi rồi hãy làm.

   Đệm DỌC giữ nguyên, và ô bảng vẫn có đệm riêng (`table.list th,td`) nên chữ
   không dán vào mép thẻ. Nó gỡ thêm 40px khỏi quãng phải quẹt.

   Viết `.card.khung-phien` chứ không `.khung-phien` trần: một lớp trần cùng độ
   đặc hiệu với `.card`, nên nó chỉ thắng NHỜ nằm sau trong file — tức là lành
   nhờ may. Hai lớp thì thắng vì đúng luật, dù ai có dồn lại thứ tự khai. */
@media (max-width:560px){
  .card.khung-phien{padding-inline:0}
}

/* Hộp hỏi lại trước khi xoá. MỘT hộp dùng chung cho cả bảng — JS chỉ đổi mấy ô
   ẩn theo dòng vừa bấm. Mỗi dòng một hộp thì mười phiên là mười cái <dialog> y
   hệt nhau nằm cuối trang.
   `display` khai DƯỚI [open]: luật dialog:not([open]){display:none} là của trình
   duyệt nên thua mọi luật của trang. */
dialog.hop-xoa{border:0; border-radius:14px; padding:0; max-width:min(92vw,420px); width:100%;
  box-shadow:var(--shadow); color:var(--ink); background:var(--surface)}
dialog.hop-xoa[open]{display:block}
dialog.hop-xoa::backdrop{background:rgba(0,0,0,.45)}
.hop-xoa .than{padding:22px 24px 20px; display:flex; flex-direction:column; gap:14px}
.hop-xoa h2{margin:0; font-size:17px}
.hop-xoa .cai-gi{font-family:"JetBrains Mono",monospace; font-size:13px; color:var(--muted)}
.hop-xoa .nut{display:flex; gap:10px; flex-wrap:wrap}
.hop-xoa .nut button{min-height:44px; padding:0 18px; border-radius:9px; border:1px solid var(--line);
  background:var(--surface); font-weight:600}
.hop-xoa .nut button.that{background:var(--danger); border-color:var(--danger); color:#fff}

/* ── NÚT GHI SỔ ĐANG CHẠY: XÁM và TẮT ────────────────────────────────────────
   Tấn Phú chốt 18/09/2026: *"Khi click vào. Gray button và disable button cho
   tới khi có kết quả (dù lỗi hay thành công)."*

   XÁM chứ không phải đỏ mờ. `.btn-primary:disabled` chỉ hạ `opacity` xuống .5,
   tức vẫn là cái nút ĐỎ, chỉ nhạt đi — đặt cạnh nút đỏ đậm thì mắt đọc thành
   "nút vẫn bấm được, chỉ hơi mờ", và người ta bấm lại. Mà bấm lại nút ghi sổ là
   GHI HAI LẦN lên sổ Lark, thứ không lùi được.

   Ba lớp (`.btn-primary.dang-ghi:disabled`) để chắc chắn thắng `:disabled` hai
   lớp ở trên, không phụ thuộc thứ tự dòng trong file.

   `cursor:progress` chứ không `not-allowed`: đây là *"đang chạy, đợi đi"*, khác
   hẳn *"không bấm được"* của nút chưa đủ điều kiện ghi. */
.btn-primary.dang-ghi:disabled,.btn-commit.dang-ghi:disabled,.btn-luu.dang-ghi:disabled{
  background:var(--sunken); color:var(--muted); border:1px solid var(--line);
  opacity:1; cursor:progress}

/* ── LƯỚI Ô TICK: ĐỐI CHIẾU ĐÃ TICK ↔ GIẤY GHI ───────────────────────────────
   Tấn Phú chốt 18/09/2026: *"hiển thị nổi chút chỗ: Đã tick và Giấy ghi (nếu có
   tổng)"*. Trước đó con số đã tick nằm lẫn trong một dòng chữ xám 12,5px, cạnh
   bộ đếm "18/18 đã soi" — thứ người soi ngồi chờ suốt mười tám cú tick lại là
   thứ mờ nhất trên cả cái lưới.

   Lớp `.doi-o` chứ KHÔNG dùng lại `.o`: `.o` là ô cân, và `_SoiChiaDoi` bắt mọi
   cú chạm bằng `closest('.o')` rồi tìm ô tick bên trong. Mượn tên lớp ấy là mời
   bộ chạm vơ luôn hai con số này vào — hôm nay nó thoát sớm vì không có ô tick,
   tức là đang lành NHỜ MAY, không nhờ thiết kế. */
.luoi-can .doi-tick{display:inline-flex; align-items:center; gap:6px; flex-wrap:wrap}
.luoi-can .doi-o{display:inline-flex; align-items:baseline; gap:5px; padding:2px 9px;
  border:1px solid var(--line); border-radius:8px; background:var(--surface)}
.luoi-can .doi-nhan{font-size:10px; font-weight:600; letter-spacing:.06em;
  text-transform:uppercase; color:var(--muted)}
/* Con số TO hơn hẳn chữ quanh nó — đó là cả điểm của mục này. Mono để hai con số
   xếp thẳng cột với nhau và với mấy ô cân ngay dưới. */
.luoi-can .doi-o b{font-family:"JetBrains Mono",monospace; font-size:15px; color:var(--ink)}
.luoi-can .doi-o{font-size:11.5px; color:var(--muted)}

/* ── MÀN CẤU HÌNH và màn sửa LUẬT XẾP chứng từ (hộp "Tính đơn giá" của màn Chi
   phí) — hai màn, một bộ kiểu: cụm từ khoá của màn sau là đúng cụm danh mục của
   màn trước. Chạy bộ token cũ, nên chỉ dùng token khai trong file này.
   `.field-grid` · `.field` · `.pick` · `.button-row` MƯỢN nguyên ở trên. */
.ch-nhom > h2{margin:0 0 10px; font-size:15px}
.ch-muc{background:var(--surface); border:1px solid var(--line); border-radius:11px;
  padding:13px 15px}
.ch-muc + .ch-muc{margin-top:10px}
.ch-muc > h3{margin:0 0 2px; font-size:14px}
.ch-khoa{font-family:"JetBrains Mono",monospace; font-size:11.5px; color:var(--muted)}
/* CON TRỰC TIẾP thôi: `.ch-muc p` trần thì nó với cả câu trong khối `.alert`
   nằm trong thẻ, và màu xám của nó THẮNG màu đỏ của `.alert` (chọn lọc cao
   hơn) — một lời cảnh báo mặc áo chú thích. */
.ch-muc > p{margin:6px 0 0; font-size:13px; color:var(--muted); max-width:66ch}
.ch-goc{margin-top:4px; font-size:12px; color:var(--muted)}
/* Ô nhập và nút NGANG HÀNG ở khoá kiểu số; khoá còn phải gõ JSON thì ô xuống
   dòng riêng vì nó là một khối nhiều dòng. */
.ch-sua{display:flex; flex-wrap:wrap; gap:10px; align-items:flex-end; margin-top:10px}
.ch-sua input[type=text]{width:16em}
.ch-sua textarea{width:100%; min-height:11em; font-family:"JetBrains Mono",monospace;
  font-size:12.5px; line-height:1.5}
.ch-sua > .ch-rong{flex:1 1 100%}
.ch-luc{margin-top:8px; font-size:12px; color:var(--muted)}

/* ── CỤM Ô NHẬP — mỗi trường một ô, thay cho khung JSON gõ tay ──────────── */
.ch-bang{margin-top:11px}
.ch-dongs{display:flex; flex-direction:column; gap:8px}
.ch-dong{display:flex; gap:10px; align-items:flex-start; background:var(--sunken);
  border:1px solid var(--line); border-radius:9px; padding:9px 11px}
.ch-dong > .field-grid{flex:1 1 auto; min-width:0}
/* Dòng đã tick Xoá: mờ đi VÀ đeo vạch đỏ ở mép. Chỉ mờ thôi thì trên điện
   thoại ngoài nắng không phân biệt được — phân biệt bằng HÌNH, không chỉ
   bằng màu, y luật §36 của cms/CLAUDE.md. */
.ch-dong.se-xoa{opacity:.55; box-shadow:inset 3px 0 0 var(--danger)}
/* Số thứ tự NẰM CẠNH ô tick, không nằm dưới: xếp dọc thì mỗi dòng gấp lại
   vẫn cao bằng hai dòng, và cả cụm mười bốn mục dài gấp đôi mà chẳng thêm
   chữ nào. Số thứ tự ở đây không phải để trang trí — câu lỗi lúc lưu gọi
   đúng "dòng 3", nên phải đếm được bằng mắt. */
.ch-bo{flex:none; display:flex; align-items:center; gap:6px; cursor:pointer; padding-top:1px}
/* `.pick` canh theo lưới của `.row`, ở đây không có lưới ấy. */
.ch-bo .pick{margin:0}
.ch-stt{flex:none; min-width:1.6em; text-align:right; font-family:"JetBrains Mono",monospace;
  font-size:11px; color:var(--muted); font-variant-numeric:tabular-nums}
.ch-them{margin-top:9px}
/* Dòng GẤP LẠI — mở ra mới thấy mấy ô. Danh mục vật tư có mười bốn dòng bảy
   ô, bày hết ra là một trang dài ba mươi nghìn pixel trên điện thoại, tức
   không tìm nổi mục cần sửa. `<details>` gấp được mà KHÔNG CẦN JS, và ô nằm
   trong phần đang gấp vẫn gửi lên như thường.
   Ô tick Xoá đứng NGOÀI `<details>`: nằm trong `<summary>` thì chạm vào nó
   là gấp/mở, chặn lại phải bằng JS — mà không JS thì ô tick hết ăn. */
.ch-than{flex:1 1 auto; min-width:0}
.ch-than > summary{display:flex; gap:7px; align-items:center; cursor:pointer;
  list-style:none; font-size:13.5px; padding:2px 0}
.ch-than > summary::-webkit-details-marker{display:none}
.ch-than > summary::before{content:"\25B8"; flex:none; color:var(--muted); font-size:11px}
.ch-than[open] > summary::before{content:"\25BE"}
.ch-than[open] > summary{padding-bottom:7px; margin-bottom:8px;
  border-bottom:1px dashed var(--line)}
/* `nowrap` ở đây có trần: `min-width:0` trên khối bao + cắt đuôi, nên câu dài
   không đẩy được trang ra — luật §34 của cms/CLAUDE.md. */
.ch-tom{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.ch-tom .ch-ma{font-family:"JetBrains Mono",monospace; font-size:12px; margin-right:6px}
  

/* ── Màn LUẬT XẾP chứng từ (hộp "Tính đơn giá" của màn Chi phí) ──────────────
   Mỗi nhóm một thẻ; mỗi điều kiện một hàng lưới: tick · dò ở · từ khoá · khớp
   bao nhiêu · xoá. Trong iframe lề trang chỉ 2px (`body.nhung .page`) — màn
   này tự chừa lề, và bộ chọn phải MẠNH hơn luật ấy, không thì nó thua lặng lẽ. */
body.nhung .page.lx{padding-inline:14px}
.lx-nhom{background:var(--surface); border:1px solid var(--line); border-radius:11px; padding:14px 16px}
.lx-nhom + .lx-nhom{margin-top:10px}
.lx-nhom-dau{display:flex; flex-wrap:wrap; align-items:baseline; gap:2px 10px; margin-bottom:12px}
.lx-nhom-dau h3{margin:0; font-size:15px; font-weight:800; color:var(--expense)}
.lx-di{font-size:12.5px; color:var(--muted)}
.lx-dk{display:grid; grid-template-columns:auto minmax(9em,15em) minmax(8em,1fr) auto auto;
  grid-template-areas:"bat cot tu khop xoa"; gap:8px 10px; align-items:center; position:relative;
  padding:8px 10px; background:var(--sunken); border:1px solid var(--line); border-radius:9px}
/* "Hoặc" giữa hai điều kiện — CHỮ, không chỉ khoảng trắng: nhìn vào là biết
   một dòng khớp là đủ, không phải phải khớp cả hai. */
/* `~` chứ không `+`: dòng vừa thêm nằm sau thẻ <noscript> của nhóm. */
.lx-dk ~ .lx-dk{margin-top:26px}
.lx-dk ~ .lx-dk::before{content:"Hoặc"; position:absolute; left:12px; top:-20px; font-size:11px;
  font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--muted)}
.lx-dk > [name=bat]{grid-area:bat; width:20px; height:20px; margin:0}
.lx-dk > select{grid-area:cot}
.lx-dk > .lx-tu{grid-area:tu}
.lx-dk > .lx-khop{grid-area:khop}
.lx-dk > .lx-xoa{grid-area:xoa}
.lx-dk select,.lx-dk input[type=text]{min-width:0; width:100%; font-size:13.5px; padding:6px 8px}
.lx-dk input.lx-tu{color:var(--expense); font-weight:700}
/* Bỏ tick = điều kiện TẮT: mờ đi VÀ gạch chữ — hình, không chỉ màu (§36). */
.lx-dk:has(> [name=bat]:not(:checked)){opacity:.55}
.lx-dk:has(> [name=bat]:not(:checked)) input.lx-tu{text-decoration:line-through}
.lx-khop{border:0; background:none; padding:2px 4px; font-size:12.5px; color:var(--muted);
  text-decoration:underline dotted; text-underline-offset:3px; cursor:pointer; white-space:nowrap}
.lx-khop:hover{color:var(--ink)}
.lx-xoa{width:34px; height:34px; border:1px solid var(--line); border-radius:8px; background:var(--surface);
  color:var(--muted); font-size:18px; line-height:1; cursor:pointer}
.lx-xoa:hover{color:var(--danger); border-color:var(--danger)}
.lx-them{margin-top:12px}
.lx-trang-thai{margin:0; min-height:1.2em}
@media (max-width:560px){
  .lx-dk{grid-template-columns:auto 1fr auto; grid-template-areas:"bat cot xoa" ". tu tu" ". khop khop"}
  .lx-dk > .lx-khop{justify-self:start}
}
.lx-ket-qua h3{margin:16px 0 6px; font-size:14px}
table.list th.so,table.list td.so{text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap}
/* Hàng có số ĐỔI: đậm và vạch đỏ ở mép — phân biệt bằng hình, không chỉ bằng màu (§36). */
table.list tr.lx-doi td{font-weight:650}
table.list tr.lx-doi td:first-child{box-shadow:inset 3px 0 0 var(--expense)}
/* Hộp xem trước: điều kiện hay nhóm kéo vào những dòng nào. `display` khai dưới
   [open] — khai trần là hộp hiện sẵn ở cuối trang (cùng bẫy với .hop-nhap). */
dialog.lx-hop{border:0; border-radius:12px; padding:0; width:min(96vw,900px); max-height:85vh;
  background:var(--surface); color:var(--ink); box-shadow:var(--shadow)}
dialog.lx-hop[open]{display:flex; flex-direction:column}
dialog.lx-hop::backdrop{background:rgba(36,31,30,.45)}
.lx-hop .dau{display:flex; align-items:center; gap:10px; padding:10px 14px; border-bottom:1px solid var(--line)}
.lx-hop .dau h3{flex:1; margin:0; font-size:14px}
.lx-hop .than{overflow:auto; padding:6px 14px 14px}

/* ── MÀN KIỂM KÊ — ô đếm theo chồng, hai thẻ lượt, biên bản ─────────────────
   Người đếm đứng ở kho đông, tay đeo găng: ô cao 44px, chữ 16px (thấp hơn thì
   Safari tự phóng cả trang khi chạm vào ô). Mã dòng to, tên loại nhỏ bên dưới —
   mắt tìm "B" chứ không đọc "Múi loại B". */
.o-dem{display:grid; grid-template-columns:5.5em 1fr; gap:2px 10px; align-items:center;
  padding:8px 0; border-bottom:1px solid var(--line)}
.o-dem label{display:flex; flex-direction:column; gap:2px}
.o-dem .ma-dem{font:700 18px/1.1 "JetBrains Mono",monospace}
.o-dem .ten-dem{font-size:11px; color:var(--muted)}
.o-dem input{width:100%; min-height:44px; font:16px "JetBrains Mono",monospace}
.o-dem .tong-dem{grid-column:2; min-height:1.2em; font-size:12.5px; color:var(--muted)}
.o-dem .tong-dem.loi{color:var(--danger)}
.tong-het{margin:10px 0 0; font-size:14px}
.hai-luot{display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:12px}
.the-luot{border:1px solid var(--line); border-radius:10px; padding:12px 14px; background:var(--surface)}
.the-luot h3{margin:0 0 6px; font-size:15px}
.tick-dong{display:flex; flex-wrap:wrap; gap:6px 14px; margin:8px 0}
.tick-dong label{display:inline-flex; align-items:center; gap:6px; min-height:40px; font-family:"JetBrains Mono",monospace}
.field-check{display:flex; align-items:flex-start; gap:8px; margin:10px 0; font-size:14px}
.field-check input{margin-top:3px}
/* Biên bản: dòng thừa vạch xanh, dòng thiếu vạch đỏ ở mép — hình, không chỉ màu (§36). */
table.list.bien-ban td:not(:first-child),table.list.bien-ban th:not(:first-child){text-align:right}
table.list.bien-ban tr.thua td:first-child{box-shadow:inset 3px 0 0 var(--luu)}
table.list.bien-ban tr.thieu td:first-child{box-shadow:inset 3px 0 0 var(--danger)}
table.list.bien-ban tr.thua td,table.list.bien-ban tr.thieu td{font-weight:650}
/* Số từng lượt: dòng hai lượt lệch nhau. */
table.list tr.lech td{background:var(--warn-soft)}

/* ── MÀN XUẤT NHẬP KHÁC — mỗi dòng một loại: tick, loại, thùng, kg ─────────────
   Bốn ô trên MỘT hàng ở điện thoại 390px: tick hẹp, ba ô chia đều phần còn lại;
   nút bỏ dòng xuống hàng riêng, dồn phải. Ô cao 44px, chữ 16px như ô đếm kiểm kê. */
.dong-xn{display:grid; grid-template-columns:2.4em repeat(3,minmax(0,1fr)); gap:4px 8px; align-items:end;
  padding:8px 0; border-bottom:1px solid var(--line)}
.dong-xn label{display:flex; flex-direction:column; gap:2px; font-size:11px; color:var(--muted)}
.dong-xn label.tick{align-items:center}
.dong-xn label.tick input{width:22px; height:22px; margin:11px 0}
.dong-xn select,.dong-xn input[type=text]{width:100%; min-height:44px; font:16px "JetBrains Mono",monospace}
.dong-xn .bo{grid-column:2 / -1; justify-self:end}

/* BẢN THỬ NGHIỆM — băng trên cùng mọi trang (Docs/15-ban-thu-nghiem.md). Nền hổ phách
   đặc, chữ đậm: phải khác bản thật ngay từ cái liếc đầu, kể cả ở trang đăng nhập.
   KHÔNG dính đỉnh, như mọi thanh trên (cms/CLAUDE.md luật 39). */
.thu-nghiem{background:var(--warn-manh); color:var(--ink); font-weight:800;
  text-align:center; padding:6px 16px; font-size:13px; letter-spacing:.02em}
