/* EOS Studio — nền: bảng màu, chữ, và các phần tử cơ bản.
 *
 * ── MỘT KHỐI, HAI BẢNG MÀU ────────────────────────────────────
 * Mọi màu nằm trong khối `:root` dưới đây. Không một mã màu nào được viết
 * thẳng ở nơi khác — `npm run test-mau` đỏ nếu có.
 *
 * `light-dark(sáng, tối)` khai CẢ HAI bảng trên cùng một dòng, nên đọc bảng
 * màu là đọc một cột. Trình duyệt chọn vế nào theo `color-scheme`:
 *   - mặc định `light` → NỀN SÁNG, bất kể hệ điều hành đang để gì;
 *   - `<html data-nen="toi">` → nền tối, vì người dùng đã tự chọn.
 * Nút chuyển nằm ở dải trạng thái, mã trong `loi/nen.js`.
 *
 * ── VÌ SAO KHÔNG HỎI HỆ ĐIỀU HÀNH ─────────────────────────────
 * Bản trước để `light dark`, tức là theo cài đặt máy. Chủ dự án chốt: mặc
 * định là NỀN SÁNG, cố định. Nghĩa là mọi người mở app lần đầu đều thấy một
 * giao diện giống nhau — và ảnh chụp màn hình gửi cho nhau thì khớp. Máy để
 * nền tối cũng không đổi được điều đó; chỉ cú bấm của người dùng mới đổi, và
 * cú bấm ấy được nhớ lại.
 *
 * ── VÌ SAO CÓ CỘT SÁNG, KHÔNG PHẢI "ĐẢO NGƯỢC BẢNG TỐI" ───────
 * Đảo ngược là cái bẫy. Trong nền tối, mặt phẳng NỔI LÊN thì sáng hơn; trong
 * nền sáng nó cũng sáng hơn (ngà trên giấy ngà sẫm) — thứ tự KHÔNG lật. Và ba
 * màu trạng thái phải đổi hẳn: lục rêu `#a3bf7f` đọc tốt trên nền tối nhưng
 * chưa đạt 2:1 trên nền giấy sáng, tức là không đọc được. Mỗi vế được đo riêng.
 *
 * Con số trong chú thích là tỉ lệ tương phản WCAG, đo bằng `scripts/test-mau.mjs`.
 */

:root {
  /* `light`, không phải `light dark`: nền sáng là mặc định CỐ ĐỊNH, không
   * nhường cho cài đặt của máy. Nó cũng quyết luôn cách trình duyệt vẽ thanh
   * cuộn và các ô điều khiển gốc, nên hai thứ đó đi theo đúng bảng màu. */
  color-scheme: light;

  /* ── NHẬN DIỆN NOCTURNE (27/08/2026) ──────────────────────────
   * Bộ bàn giao «AI Control Layout» mang theo một design system tên Nocturne:
   * nền xanh xám gần trung tính, chữ Inter, bo 8px, và MỘT màu nhấn tím-lam
   * dùng làm ĐƯỜNG KẺ và ÁNH SÁNG chứ không phải mảng tô. Chủ dự án chốt
   * 27/08 lấy Nocturne cho CẢ HAI nền, thay nhận diện giấy ngà + mực sepia +
   * Palatino của bản 20/08.
   *
   * ── NỬA NỀN SÁNG LÀ SUY RA, KHÔNG PHẢI CHÉP ───────────────────
   * Bộ bàn giao chỉ vẽ nửa TỐI. Nửa sáng dưới đây suy từ chính thang màu của
   * Nocturne, theo đúng luật readme của nó ghi: trên nền tối lấy bậc 700–900
   * làm mảng tô và 100–300 làm chữ đặt trên mảng ấy; trên nền sáng thì đảo
   * vai — nhấn lùi về bậc 600 trở xuống. Không một bậc nào bịa thêm ngoài
   * thang, nên hai nền vẫn là một hệ chứ không phải hai bảng rời.
   *
   * ── VÌ SAO MÀU NHẤN LÀ BẬC 700, KHÔNG PHẢI #9184d9 ────────────
   * Nocturne khai nhấn là #9184d9 và dặn ĐỪNG tô mảng lớn bằng nó — nút chính
   * của nó là một cái viền, không phải một mảng nền. App này thì có
   * `button.chinh` tô đặc ở gần như mọi màn, mà chữ sáng trên #9184d9 chỉ đạt
   * 2,6:1 — không đọc được. Nên `--thuong` lấy bậc 700 (#5d5294): cùng sắc,
   * đủ sẫm để chở chữ ở 6,22:1, và bằng nhau ở cả hai nền. Bản #9184d9 nguyên
   * gốc vẫn còn, làm `--vien-chon` — nó là ĐƯỜNG KẺ, đúng vai readme giao.
   *
   * Con số trong chú thích là tỉ lệ tương phản WCAG, đo bằng `npm run test-mau`.
   */

  /* ── Mặt phẳng ─────────────  SÁNG       TỐI                  */
  --nen:          light-dark(#e4e7f5, #161826);  /* trang */
  --tam:          light-dark(#f3f5fe, #232532);  /* thẻ, nổi lên một bậc */
  --sau:          light-dark(#dfe3f3, #101220);  /* ô nhập, lõm xuống */

  /* ── Viền: HAI loại, khác chuẩn ───────────────────────────────
   * Viền trang trí chỉ cần thấy được. Viền ĐIỀU KHIỂN (ô nhập, nút, ô chọn)
   * là thứ nói cho người ta biết bấm vào đâu — WCAG 1.4.11 đòi ≥3:1. Gộp
   * chung thì hoặc ô nhập tàng hình, hoặc mọi cái thẻ đều kẻ khung chói. */
  --vien:         light-dark(#cfd3e5, #3f424d);  /* trang trí */
  --vien-nut:     light-dark(#75798c, #75798c);  /* điều khiển · 3.50 / 4.08 */
  --vien-sang:    light-dark(#9397ab, #9397ab);  /* điều khiển, đang trỏ vào */

  /* ── Chữ ──────────────────────────────────────────────────── */
  --chu:          light-dark(#23263a, #e9e9ed);  /* 12.09 / 14.54 */
  --chu-nhat:     light-dark(#4e5364, #b2b6ca);  /*  6.21 /  8.75 */
  --chu-mo:       light-dark(#565b6d, #b2b6ca);  /*  5.48 /  8.75 */
  --chu-code:     light-dark(#5d5294, #d2cefd);
  /* Chữ đặt TRÊN MẢNG MÀU (nút chính, nhãn trạng thái) — không phải "chữ
   * sáng". Hai nghĩa này trùng nhau ở bản tối và tách hẳn ở bản sáng: dùng
   * nhầm cái này cho chữ trên thẻ là chữ trắng trên thẻ trắng. */
  --chu-tren-mau: #f5f4ff;

  /* Chữ và nền đè LÊN ẢNH — cố ý KHÔNG đi qua `light-dark()`, vì tấm ảnh bên
   * dưới không đổi theo nền trang. Nhãn trên khung xem cắt, và ô xem mặt chữ
   * (nó mô phỏng chữ trắng trên ảnh thật) phải đọc được y hệt ở cả hai bản. */
  --chu-tren-anh: #fff;
  --nen-gia-anh:  #23303f;
  /* Ô đứng sau bản logo dùng cho nền tối (thẻ Cẩm nang, 24/08/2026). KHÔNG theo
     chủ đề: một logo vẽ cho nền tối phải luôn đứng trên nền tối, kể cả lúc app
     đang ở chế độ sáng — không thì nó thành một ô trống và người xem báo mất logo. */
  --nen-logo-toi: #1b1d29;

  /* ── Nhấn ─────────────────────────────────────────────────── */
  --thuong:       #5d5294;                       /* tím-lam Nocturne bậc 700 */
  --chon:         light-dark(#e7e5fe, #2b2741);  /* mảng "đang chọn" */
  /* Viền của thứ ĐANG CHỌN / ĐANG FOCUS — chính là màu nhấn nguyên bản của
   * Nocturne. Ở những chỗ này MÀU LÀ TÍN HIỆU DUY NHẤT nói cái nào đang được
   * chọn, nên nó phải thấy được chứ không chỉ "có màu". */
  --vien-chon:    light-dark(#796cbf, #9184d9);  /*  3.62 /  5.45 */
  --lien-ket:     light-dark(#5d5294, #d2cefd);  /*  5.50 / 11.73 */

  /* ── Trạng thái ───────────────────────────────────────────────
   * Nocturne là hệ MỘT màu nhấn, nhưng ba màu này không phải trang trí: chúng
   * là NGHĨA (được · coi chừng · hỏng), và nghĩa thì không được nói bằng cùng
   * một sắc với mọi thứ khác. Chúng giữ ba họ màu riêng, chỉ hạ độ bão hoà cho
   * hợp nền xanh xám. */
  --tot:          light-dark(#2f6b45, #8fd0a4);  /*  5.15 /  9.83 */
  --canh:         light-dark(#7a5312, #e0b070);  /*  5.55 /  8.90 */
  --xau:          light-dark(#a03a3a, #e59a92);  /*  5.39 /  7.85 */

  --nen-dung:     light-dark(#f6e0e0, #3a1f22);
  --vien-dung:    light-dark(#9b3535, #a04a4a);
  --nen-chan:     light-dark(#f2e8d8, #2b2416);
  --vien-chan:    light-dark(#7a5312, #7d5a1e);
  --vien-re:      light-dark(#2f6b45, #4f7d5f);

  /* ── Bề mặt phụ ───────────────────────────────────────────── */
  --nen-luot:     light-dark(#dcdfef, #2b2e3d);  /* đang trỏ vào */
  --vien-bang:    light-dark(#cfd3e5, #292b31);  /* kẻ dòng bảng */
  /* Nền sau ảnh/video khi ảnh chưa tải hoặc không kín khung. Đen tuyền ở bản
     sáng là một lỗ đen giữa trang, nên bản sáng dùng một bậc xám của thang. */
  --nen-anh:      light-dark(#cfd3e5, #0b0c14);

  /* Vạch chân DẢI ĐIỂM NHẤN — dải chở «cái gì đang chặn tôi» của mỗi tab, và
     nó phải đọc ra một tầng khác thẻ thường dù cùng dùng `--tam`. Nocturne kẻ
     vạch ấy bằng một bậc của màu nhấn, không bằng viền trung tính. */
  --vien-nhan:    light-dark(#b5abfc, #423a6a);

  /* ── Ô BỊ KHOÁ — 29/08/2026 ───────────────────────────────────
   * Trước hôm nay ô `disabled` KHÔNG có một dòng CSS nào: app đã đè cả nền
   * lẫn viền của ô nhập, nên phần làm xám sẵn có của trình duyệt không còn
   * chỗ bám, và một ô không gõ được trông y hệt một ô gõ được.
   *
   * Nền RIÊNG chứ không mượn `--sau`: `--sau` nghĩa là "chỗ bỏ gì đó vào",
   * và dùng nó cho một ô không bỏ được gì vào là nói dối bằng hình khối. */
  --nen-khoa:     light-dark(#e9ebf4, #1c1e2b);
  --vien-khoa:    light-dark(#c3c8dc, #34384a);

  /* `--nen-de` và `--vien-de` ĐI HẲN 29/08 (đợt 6): chúng chỉ tô cái hộp của
     `.de-so`, và cái hộp ấy đã bỏ — tiêu đề mục nay phân cấp bằng SỐ, không
     bằng khung, vì `.tam` đã là một cái khung rồi. Hai token không ai dùng
     là hai màu người sau sẽ tưởng còn vai; `test-kieu` mục 6b nay gác cả
     token chứ không chỉ lớp. */

  /* ── MÀU NHẤN KHI NÓ LÀM MỰC — 29/08/2026 ─────────────────────
   * `--thuong` là màu nhấn khi nó làm MẢNG TÔ, và nó cố định ở cả hai nền vì
   * mảng tô phải đủ sẫm để chở chữ sáng. Nhưng cùng màu ấy đem làm CHỮ hoặc
   * ĐƯỜNG KẺ trên nền tối thì chìm: đo được 2,60:1 ở nút phụ ngày 29/08, dưới
   * cả ngưỡng chữ 4,5 lẫn ngưỡng viền điều khiển 3,0. Một token không gánh
   * được hai vai qua hai nền — mảng tô cần SẪM, mực cần TƯƠNG PHẢN.
   *
   * Nên tách: `--thuong` là sơn, `--thuong-chu` là mực. Bậc 700 ở nền sáng,
   * bậc 400 ở nền tối, cùng một thang Nocturne. */
  --thuong-chu:   light-dark(#5d5294, #b5abfc);

  /* ── SƠN CỦA NÚT TIÊU TIỀN — 06/09/2026 ──────────────────────
   * Khối ⑤ có ba cột, và ĐÚNG MỘT cột tiêu tiền. Làm nổi nó bằng một mảng tô
   * quanh cả cột thì ba tiêu đề lệch nhau (đệm của hộp đẩy tiêu đề cột giữa
   * xuống) — chủ dự án chỉ ra bằng ảnh chụp. Nên nổi bằng MÀU của tiêu đề và
   * nút, không bằng một cái hộp.
   *
   * `--canh` là MỰC: nó đo tương phản với nền trang (5,55 / 8,90), và ở nền tối
   * nó sáng (#e0b070) — tô làm nền nút rồi đặt `--chu-tren-mau` lên là chữ sáng
   * trên nền sáng. Đúng cái bẫy mà khối «màu nhấn có hai vai» ngay trên đã ghi,
   * chỉ ngược chiều. Nên token này cố định SẪM ở cả hai nền, y như `--thuong`:
   * #f5f4ff trên #8a5a12 đo được 6,2:1.
   *
   * Tiêu đề cột vẫn dùng `--canh` — nó là mực, và nó đứng trên nền trang. */
  --tien:         #8a5a12;

  /* ── Họ chữ ───────────────────────────────────────────────────
   * Inter cho CẢ tiêu đề lẫn thân bài — Nocturne ghép Inter trên Inter, và
   * thứ bậc đến từ cỡ chữ với khoảng trắng chứ không từ hai họ chữ khác nhau.
   * Tệp nằm sẵn trong `assets/fonts/inter`, phục vụ qua `/font/inter/…`, nên
   * app vẫn chạy khi không có mạng — xem khối `@font-face` ngay dưới.
   * Đo cmap 27/08/2026: Inter đủ dấu chồng tiếng Việt, và có cả ký tự ₫. */
  --tieu-de:      "Inter", "Segoe UI", system-ui, sans-serif;

  /* Nền mờ sau hộp thoại. Tám ký tự — hai ký tự cuối là độ đục. Nó KHÔNG chở
   * chữ nào nên không có ngưỡng tương phản; việc của nó là làm trang phía sau
   * lùi lại đủ để hộp đọc ra một tầng riêng. */
  --nen-phu:      light-dark(#23263a66, #0000008c);

  /* Bo góc theo thang Nocturne: `--bo` là radius-md (nút, ô nhập, mục thanh
   * bên), `--bo-lon` là radius-lg (thẻ lớn, hộp thoại, fieldset). Bản 20/08
   * cố ý bo hẹp 4/7 để ra chất ấn loát cổ điển; nhận diện ấy đã thôi, và
   * Nocturne bo rộng hơn. */
  --bo-nho:       4px;
  --bo:           8px;
  --bo-lon:       14px;

  /* ── THANG CỠ CHỮ — 29/08/2026 ────────────────────────────────
   * Bảy bậc, thay cho HAI MƯƠI cỡ đo được hôm 29/08 (có cả 9,5 · 10,5 ·
   * 11,5 · 12,5 · 13,5px — dấu vân tay của việc chỉnh tay từng màn).
   *
   * Vì sao tầng này trôi mà bảng màu thì không: màu có token VÀ có
   * `test-mau` gác. Cỡ chữ trước hôm nay không có cái nào. Thang này chỉ
   * đứng được nếu `npm run test-kieu` gác nó — token một mình chưa từng
   * giữ được luật nào trong kho này.
   *
   * Không bậc nào giữa hai bậc. Cần nhỏ hơn 11 thì không phải cần chữ nhỏ
   * hơn — là đang nhồi quá nhiều vào một chỗ. */
  --cs-nho:       11px;  /* nhãn bảng, chú thích, huy hiệu, code */
  --cs-phu:       12px;  /* chữ phụ (.nhat), dòng meta */
  --cs-ban:       13px;  /* chữ trong bảng, danh sách dày, mục thanh bên */
  --cs-chinh:     14px;  /* MẶC ĐỊNH — thân bài, ô nhập, nút */
  --cs-de:        16px;  /* tiêu đề thẻ, tên app ở thanh bên */
  --cs-tab:       20px;  /* tiêu đề tab */
  --cs-so:        26px;  /* con số lớn — tiền, đếm */

  /* ── THANG KHOẢNG CÁCH — 29/08/2026 ───────────────────────────
   * Bảy bậc chia hết cho 4, thay cho 15 giá trị `gap` và hơn 20 tổ hợp
   * `padding` rời rạc. Chủ dự án chốt bản NGHIÊM: bỏ 6 và 10, dù chúng
   * đang dùng nhiều (6px ×13, 10px ×26 chỉ tính `gap`). Lý do chọn nghiêm:
   * một thang chín bậc thì thôi là thang — người viết vẫn phải chọn giữa
   * 8 và 10 mà không luật nào nói chọn cái nào, tức là vẫn trôi.
   *
   * Khoảng cách NÓI QUAN HỆ, không nói thẩm mỹ: hai thứ gần nhau hơn thì
   * người đọc hiểu chúng thuộc về nhau. Nên khoảng cách TRONG một nhóm
   * phải nhỏ hơn hẳn khoảng cách GIỮA hai nhóm — chênh một bậc chưa đủ. */
  --k-0:          2px;   /* trong một điều khiển — dấu tick với nhãn */
  --k-1:          4px;   /* hai thứ dính nhau về nghĩa — nhãn với đơn vị */
  --k-2:          8px;   /* MẶC ĐỊNH — giữa hai nút, giữa nhãn và ô */
  --k-3:          12px;  /* giữa hai trường trong một thẻ */
  --k-4:          16px;  /* đệm trong thẻ, giữa hai nhóm trường */
  --k-5:          24px;  /* giữa hai thẻ */
  --k-6:          32px;  /* giữa hai khối lớn trong một tab */

  /* Chiều cao dải công cụ của trang Hướng dẫn. Là TOKEN vì hai chỗ đọc nó:
     chính dải ấy, và `--cao-dinh` của cột mục lục dính — cột ấy phải bám DƯỚI
     dải, không thì phần đầu của nó nằm khuất sau dải. Hai con số gõ tay ở hai
     chỗ là hai con số sẽ lệch. */
  --cao-hd-cong-cu: 57px;

  /* MỘT chiều cao cho mọi điều khiển đứng cùng hàng. Trước hôm nay
   * `button` trần cao 31,7px còn `.chinh` · `.phu` · ô nhập đều 39,7px —
   * ba nút cạnh nhau thì một cái thụt chân. */
  /* 40, không phải 38: chiều cao TỰ NHIÊN của nút chính (đệm 9px trên dưới,
   * chữ 14px cao dòng 1,55) rơi vào 39,7px. Đặt trần thấp hơn con số ấy thì
   * `min-height` không với tới nó và ba hạng nút vẫn lệch nhau — đo được
   * 38 · 39,3 · 39,7 ngay lượt thử đầu. Trần phải CAO HƠN mọi chiều cao tự
   * nhiên thì nó mới là trần. 40 cũng chia hết cho 4, như thang khoảng cách. */
  --cao-dk:       40px;

  /* ── VÙNG CHẠM NHỎ NHẤT — 07/09/2026 ──────────────────────────
   *
   * `--cao-dk` là chiều cao của điều khiển đứng THÀNH HÀNG. Nút gỡ dấu «×» thì
   * không đứng hàng với ai — nó ngồi ở góc một tấm ảnh, nên nó có quyền nhỏ
   * hơn. Nhưng nhỏ tới đâu thì trước hôm nay không ai nói, và ba chủ tự đặt ba
   * con số: 18 · 20 · 21px.
   *
   * Đo trong trình duyệt 07/09: `.bd-anh .nut-bo` ra **18×18**. WCAG 2.5.8 (AA
   * của WCAG 2.2) đòi 24×24 — và đây là nút XOÁ một tấm ảnh khỏi khối, tức
   * loại nút mà bấm trượt tốn nhất. Một dấu «×» 18px cạnh một tấm ảnh là chỗ
   * ngón tay và con trỏ đều dễ trượt.
   *
   * 24 không chia hết cho 4 theo kiểu thang khoảng cách — nó KHÔNG phải một
   * bậc của thang ấy. Nó là một ngưỡng đến từ bên ngoài, như 3:1 của viền điều
   * khiển, nên nó đứng riêng và mang con số của chuẩn chứ không của app. */
  --cham:         24px;

  /* ── CAO DÒNG — 29/08/2026, đợt 6 ─────────────────────────────
   * Mục 04 sổ tay chốt HAI giá trị từ đợt 1: 1,55 cho chữ chạy, 1,2 cho tiêu
   * đề và chữ trong điều khiển. Nhưng chúng chưa bao giờ có token, nên chưa
   * bao giờ có lưới — và tầng này trôi đúng như cỡ chữ đã trôi: đo ra **16 giá
   * trị** (1,15 · 1,2 · 1,25 · 1,3 · 1,35 · 1,45 · 1,5 · 1,55 · 1,6 · 1,65 ·
   * 1,7 · 1,75 · 1,85 · 1,9 · 2). Cùng dấu vân tay của việc chỉnh tay từng màn.
   *
   * Số đo LÀ bằng chứng cho câu mở đầu của cả sổ tay: luật nào có phép thử thì
   * luật ấy còn, luật nào chỉ nằm trong đầu người viết thì luật ấy đã đi. Hai
   * con số này nằm trong sổ tay từ đợt 1 và vẫn đi mất. */
  --dong-chay:    1.55;  /* chữ chạy — đoạn văn, ô nhập nhiều dòng */
  --dong-chat:    1.2;   /* tiêu đề, và chữ bên trong một điều khiển */

  /* ── BẢN ĐỒ TẦNG — 29/08/2026, đợt 6 ──────────────────────────
   * App có 11 giá trị `z-index` rải ở ba tệp: 1 · 2 · 3 · 8 · 10 · 12 · 15 ·
   * 16 · 20 · 40 · 41. Từng con số đều đúng; cái thiếu là **một chỗ đọc được
   * cả chồng**. Không có nó thì mỗi lần thêm một thứ nổi lên, người viết chọn
   * số bằng cách đoán cho to hơn cái mình nhớ — và cái mình nhớ thì mỗi người
   * một khác.
   *
   * Khối này KHÔNG đổi một con số nào, cố ý: đổi thứ tự chồng lớp là đổi cái
   * gì che cái gì, và đó là hạng thay đổi phải nhìn thấy mới dám làm. Nó chỉ
   * đặt TÊN cho những gì đang có, và tên thì đọc được thành một câu:
   *
   *   trong thẻ → dính → dải tiêu đề → đáy → phủ → ngăn kéo → dừng khẩn → hộp
   *
   * Băng DỪNG KHẨN đứng trên mọi thứ của vỏ (20). Hộp chi tiết (40·41) cao hơn
   * nó vì nó là một lượt đối thoại người dùng vừa mở bằng tay — thứ họ đang
   * nhìn thì không được có gì chen lên trên. */
  --lop-trong-the:  1;   /* nhãn·nút nổi trên một tấm ảnh, bên trong một thẻ */
  --lop-dinh:       2;   /* dải hành động dính đáy vùng nội dung */
  --lop-dinh-tren:  3;   /* thanh dính ngay dưới dải tiêu đề màn */
  --lop-dau-man:    8;   /* dải tiêu đề màn */
  --lop-day:       10;   /* hàng nút cố định ở đáy một tab */
  --lop-day-vo:    12;   /* thanh đáy của VỎ, khổ điện thoại */
  --lop-phu:       15;   /* lớp phủ mờ sau ngăn kéo */
  --lop-ngan-keo:  16;   /* ngăn kéo thanh bên */
  --lop-khan:      20;   /* băng DỪNG KHẨN */
  --lop-hop-phu:   40;   /* nền mờ của hộp chi tiết */
  --lop-hop:       41;   /* hộp chi tiết */

  /* Bóng LÕM của ô gõ được — 29/08/2026. Chủ dự án chốt: ô thật phải có
   * chiều sâu lõm vào trong. Đây là nửa còn lại của cặp tín hiệu ở mục 06
   * sổ tay: ô gõ được là một CÁI LÕM, ô bị khoá là chữ nằm PHẲNG. Phân
   * biệt bằng hình khối thì còn đúng cả khi in đen trắng và với người mù
   * màu; phân biệt bằng sắc độ thì không.
   * Bản tối cần đậm hơn: bóng đen trên nền vốn đã tối thì mờ đi. */
  /* ── `light-dark()` LÀ HÀM MÀU, KHÔNG BỌC ĐƯỢC CẢ MỘT BÓNG — 01/09/2026 ──
   * Bản trước viết `light-dark(inset 0 1px 3px #23263a26, inset 0 1px 3px #00000073)`.
   * Đo trong trình duyệt: `CSS.supports('box-shadow', …)` trả **false**, và
   * `box-shadow` rơi về `none`. Tức là **bóng lõm chưa bao giờ vẽ ra** — kể từ
   * dòng nó được viết.
   *
   * Nó không phải trang trí. Sổ tay nhận diện mục 06 giao cho nó một VAI: bóng
   * lõm là thứ phân biệt ô «gõ được» với ô «bị khoá», và mục 09 ghi đúng câu
   * chủ dự án chốt — *«Ô thật → đổ bóng tạo chiều sâu lõm vào trong»*. Hỏng
   * lặng thì bảng bốn trạng thái mất một tín hiệu ở CẢ HAI vế: ô gõ được không
   * có bóng, và «không bóng» của ô khoá thôi nói được điều gì.
   *
   * Chữa: đưa `light-dark()` vào trong, bọc đúng cái MÀU. Đây cũng đúng họ lỗi
   * «token bịa ra không kêu» — chỉ khác chỗ tên biến thì có thật, giá trị của
   * nó mới là thứ không hợp lệ cho thuộc tính đang dùng. */
  --bong-lom:     inset 0 1px 3px light-dark(#23263a26, #00000073);

  /* ── Ô ĐIỀU KHIỂN GỐC CỦA TRÌNH DUYỆT — 23/08/2026 ───────────
   * Thanh trượt, ô tick và nút chọn được trình duyệt tô bằng màu nhấn của HỆ
   * ĐIỀU HÀNH — xanh dương `#0d6efd` trên Windows. `accent-color` chữa cả ba
   * loại ô bằng một dòng, và không phải dựng lại thanh trượt bằng tay — tức là
   * không mất phần bàn phím và trình đọc màn hình mà ô gốc cho sẵn. */
  accent-color: var(--thuong);
}

/* ── Inter, phục vụ từ chính máy chủ này ──────────────────────
 * KHÔNG `@import` Google Fonts. Bộ bàn giao Nocturne khai font bằng một dòng
 * `@import url('https://fonts.googleapis.com/…')`; ở đây thì không, vì app này
 * phải mở được khi máy không có mạng, và một `@import` hỏng làm cả trang đứng
 * chờ trước khi vẽ dòng đầu tiên.
 *
 * Khối này đứng SAU khối token, không đứng trước. `test-mau` đọc bảng màu
 * bằng cách cắt từ `:root {` tới dấu `}` đầu dòng đầu tiên — một khối
 * `@font-face` chen lên trước sẽ cắt mất cả bảng, và phép thử đỏ vì lý do
 * chẳng liên quan gì tới màu. (Lưới ấy nay đã neo hai đầu, nhưng thứ tự này
 * vẫn là thứ tự đúng.)
 *
 * Kho chỉ có hai nét: Regular 400 và Bold 700. Nocturne dặn "đừng đậm quá 500"
 * cho tiêu đề — nhưng với hai nét, 500 rơi về đúng Regular, tức là tiêu đề 15px
 * trông y hệt thân bài, và thứ bậc biến mất ở đúng cỡ chữ mà app này dùng nhiều
 * nhất. Nên tiêu đề ở đây là 600 (rơi về Bold). Lệch một bậc so với readme của
 * hệ, có chủ ý, và đây là lý do.
 */
@font-face {
  font-family: "Inter";
  src: url("/font/inter/Inter-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("/font/inter/Inter-Bold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
/* ── Nét NGHIÊNG THẬT — 29/08/2026 ────────────────────────────
 * Tải về vì ô bị khoá dùng chữ nghiêng. Không có tệp này thì trình duyệt
 * TỰ BỊA chữ nghiêng bằng cách xô nghiêng nét đứng — và với tiếng Việt dấu
 * chồng (ề · ộ · ữ) thì dấu trượt khỏi thân chữ nó thuộc về. Đúng loại lỗi
 * không kêu: chữ vẫn đọc được, chỉ là sai chính tả về mặt hình.
 * Qua đủ bốn cửa của `npm run test-font` ngày 29/08. */
@font-face {
  font-family: "Inter";
  src: url("/font/inter/Inter-Italic.ttf") format("truetype");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("/font/inter/Inter-BoldItalic.ttf") format("truetype");
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}

/* Lời chọn của người dùng — thứ DUY NHẤT đổi được nền. `="sang"` viết ra cho
 * đủ cặp: nó trùng mặc định, nhưng nhờ có nó mà một người đã từng chọn tối rồi
 * quay lại sáng vẫn được ghi một giá trị rõ ràng, thay vì phải xoá khoá lưu. */
:root[data-nen="sang"] { color-scheme: light }
:root[data-nen="toi"]  { color-scheme: dark }

* { box-sizing: border-box }

body {
  margin: 0;
  background: var(--nen);
  color: var(--chu);
  /* 1.55 thay vì 1.5: mực sepia trên giấy ngà cần thoáng dòng hơn một chút
   * mới ra vẻ nhàn nhã — chữ nêm chặt là chữ của bảng tính, không phải của
   * ấn phẩm. */
  font: 14px/1.55 var(--tieu-de);
}

/* TIÊU ĐỀ và THÂN BÀI cùng một họ chữ — Nocturne ghép Inter trên Inter. Thứ
   bậc đến từ CỠ và KHOẢNG TRẮNG, không từ hai họ chữ đối nhau. Giãn chữ âm nhẹ
   là chữ ký của hệ: Inter ở cỡ tiêu đề mà nêm chặt lại thì đọc ra một khối
   liền, không phải một dãy chữ rời. */
h1, h2, h3 { font-family: var(--tieu-de); font-weight: 600; letter-spacing: -.015em }

/* ── `h3` PHẢI TỰ KHAI CỠ — 07/09/2026 ────────────────────────
 * Tới hôm nay chỉ `.tam h3` khai `font-size`. Mọi `h3` ra ngoài một thẻ trôi
 * về mặc định trình duyệt — đo được **16,38px** (1.17em × 14px), tức một bậc
 * KHÔNG có trong thang bảy bậc ở đầu tệp này, và không lưới nào kêu vì nó
 * không phải một giá trị ai gõ ra.
 *
 * Đúng thứ ba tiêu đề đang treo ở mục N2 tiến độ (`h3.ng-nhom`, `h3.tr-nhom`,
 * «Dây thử») sẽ rơi vào. Khai ở luật GỐC thì chúng thôi phụ thuộc vào việc có
 * nằm trong `.tam` hay không. */
h1, h2 { font-size: var(--cs-tab) }
h3 { font-size: var(--cs-de) }

/* ── TIÊU ĐỀ MỤC CÓ SỐ — 29/08, dời khỏi `.de-so` ở đợt 6 ─────
 *
 * Chủ dự án chốt: tiêu đề mục trong một tab dùng CÙNG MỘT CỠ, phân cấp bằng SỐ
 * chứ không bằng cỡ chữ. Vì một tab ở đây thường có bốn tới bảy mục NGANG HÀNG
 * nhau, không phải một cây phân cấp ba tầng. Cỡ chữ giảm dần chỉ nói được «cái
 * này nhỏ hơn cái kia»; số thì nói được «cái này là mục 3 trong 6», tức trả lời
 * luôn câu «còn bao nhiêu nữa».
 *
 * ── VÌ SAO CÁI HỘP ĐI, CÒN CON SỐ Ở LẠI ──────────────────────
 * Đợt 1 gói chốt ấy vào một lớp riêng `.de-so`: cùng cỡ, có số, VÀ có khung
 * nền. Bốn phép lưới gác nó, cả bốn xanh suốt năm đợt — và đợt 6 đếm ra
 * **không tab nào dùng nó**. Không một chỗ.
 *
 * Lý do lộ ra khi đếm ba lối tiêu đề đang cùng làm một việc:
 *
 *   <h3> trong .tam   47 chỗ   mực mờ, không gạch
 *   .kf-buoc          34 chỗ   font tiêu đề, GẠCH CHÂN
 *   .de-so             0 chỗ   HỘP + nền + số
 *
 * `.tam` đã là một cái khung. Thêm một cái hộp có nền vào trong nó là khung
 * lồng khung — nên `.de-so` không dùng được ở đúng chỗ mà 81 tiêu đề kia đang
 * đứng, và nó chết vì thế chứ không vì ai quên.
 *
 * Cái nó mang lại mà hai lối kia KHÔNG có chỉ là con số. Nên đợt 6 giữ con số
 * và bỏ cái hộp: cơ chế đánh số về thẳng hai lối đang sống, và bất kỳ tiêu đề
 * mục nào cũng đeo được số chỉ bằng cách thêm `data-so`. Ba luật của mục 06b
 * sống nguyên vẹn; lối thứ tư thì đi.
 *
 *   <h3 data-so="03">Ảnh nhận diện</h3>
 *
 * Số nằm trong `::before` lấy từ thuộc tính, KHÔNG gõ vào chữ: chèn số vào
 * chính câu thì trình đọc màn hình đọc «Không ba Ảnh nhận diện», và tìm kiếm
 * trong trang cũng vấp phải nó.
 *
 * Chữ số đẳng chiều rộng, nếu không thì «11» hẹp hơn «03» và mép trái của các
 * tiêu đề so le nhau vài pixel.
 *
 * Nét 600 chứ không 700: kho font chỉ có 400 và 700, nên 600 và 700 vẽ ra
 * GIỐNG HỆT (đo 29/08). Khai 700 ở đây là hứa một bậc đậm mà font không giữ
 * được — sức nhấn thật đến từ màu `--thuong-chu`. Xem mục 02 sổ tay. */
/* ── `h3`, KHÔNG PHẢI `.tam h3` — đợt 7 ────────────────────────
 * Bản đợt 6b bó cơ chế vào `.tam h3`, vì lúc ấy 81 tiêu đề đều đứng trong một
 * `.tam`. Đo 31/08: **năm `<h3>` đứng NGOÀI** — `h3.ng-nhom` (tiêu đề nhóm trên
 * lưới ảnh Media nguồn), `h3.tr-nhom` (tiêu đề nhóm cột Trần chi tiêu), «Dây
 * thử» của Phòng thử, cộng hai tiêu đề hộp thoại. Ba cái đầu là MỤC thật.
 *
 * Với bộ chọn cũ, gắn `data-so` vào một trong ba chỗ ấy thì KHÔNG VẼ GÌ và
 * không lưới nào kêu: một đầu GHI thuộc tính, đầu kia ĐỌC bằng một bộ chọn hẹp
 * hơn, và chưa ai hỏi hai bên có khớp. Đúng chỗ hở mà đợt 6 đã gặp ở `/khoa-phong-cach`.
 *
 * Mở sang mọi `h3` là AN TOÀN, và an toàn nhờ chính luật 3 của mục 06b: quy tắc
 * `:not([data-so])` dưới đây tắt số cho mọi tiêu đề không mang thuộc tính. Nên
 * tiêu đề hộp thoại và tên tệp trong ngăn kéo vẫn không vẽ ô số rỗng — thay đổi
 * hôm nay là **0px**, và ba mục kia thì từ nay đeo được số. */
/* ── MỘT HÌNH SỐ CHO CẢ APP — chốt 02/09/2026 ─────────────────
 * Đợt chuẩn hoá 02/09 chia «hai tầng, hai hình»: BƯỚC dùng huy hiệu `.khoi-so`
 * (chuỗi có trạng thái xong/chưa), MỤC dùng chữ phẳng hai chữ số `01 02 03`
 * (phần trong một màn). Chủ dự án soi app thật và chỉ ra phân biệt ấy SAI:
 *
 *   «Đánh số của Kịch bản ngắn và 2 tab trong Chuẩn thương hiệu vẫn khác biệt
 *    so với các tab khác»
 *
 * Đúng — và đó là lỗi ở chính cái chuẩn, không phải chỗ bỏ sót. «Có trạng thái
 * xong/chưa hay không» là phân biệt KỸ THUẬT; người dùng không thấy nó. Với họ,
 * `01 → 02 → 03` rồi bấm «Cắt phân cảnh» CHÍNH LÀ làm tuần tự từng bước, y như
 * 1→8 của Kịch bản phim. Hai hình cho cùng một trải nghiệm là hai hình không
 * đồng bộ, bất kể lý lẽ đứng sau chúng gọn đến đâu.
 *
 * Nay MỘT hình: huy hiệu, giống `.khoi-so` tới từng thuộc tính (đo trong trình
 * duyệt: 11px · `--thuong` · bo 4px · đệm `1px 8px` · sàn 29px · tabular-nums).
 * Số một chữ số, không «0» đứng đầu — chuỗi dài nhất của hệ này là 3 mục, nên
 * `01` đọc như thể còn hàng chục mục nữa ở dưới.
 *
 * `top: -1px` vì đây là `::before` của một tiêu đề 16px, còn `.khoi-so` là con
 * của một dòng `.khoi-dau` căn theo `baseline` — cùng huy hiệu, hai ngữ cảnh
 * căn chỉnh khác nhau, và một pixel ấy là chỗ mắt thấy hai bên lệch nhau.
 *
 * Chữ cái khoanh tròn `Ⓐ Ⓑ Ⓒ Ⓓ` của tầng VÙNG (thẻ Cẩm nang) KHÔNG gộp vào
 * đây: nó là tầng TRÊN, và khác hệ ký hiệu nên không ai đọc lẫn một chữ cái
 * với một con số. */
h3::before {
  content: attr(data-so);
  flex: 0 0 auto;
  font-size: var(--cs-nho); font-weight: 600;
  color: var(--chu-tren-mau); background: var(--thuong);
  border-radius: var(--bo-nho); padding: 1px var(--k-2);
  min-width: calc(var(--cs-nho) + var(--k-2) * 2 + 2px);
  text-align: center; font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  margin-right: var(--k-2);
  position: relative; top: -1px;
}
/* Không có `data-so` thì không vẽ ô số rỗng. Một mục lẻ không đánh số vẫn phải
 * trông như người nhà — và đây là lý do cơ chế này dời được mà không đụng một
 * dòng markup nào: 81 tiêu đề đang có đều không mang `data-so`, nên hôm nay
 * chúng vẽ ra y như hôm qua. */
h3:not([data-so])::before { content: none }

.an { display: none }
.nhat { color: var(--chu-nhat); font-size: var(--cs-phu) }
.canh { color: var(--canh) }
.xau  { color: var(--xau) }
.tot  { color: var(--tot) }

code {
  font-family: Consolas, monospace;
  font-size: var(--cs-nho);
  color: var(--chu-code);
  background: var(--sau);
  padding: 1px var(--k-1);
  border-radius: var(--bo-nho);
}

/* ── Điều khiển ─────────────────────────────────────────── */

/* Nút KHÔNG khai lớp nào vẫn phải mang mặt của app — 23/08/2026.
 *
 * Ba hạng nút dưới đây (`chinh` · `phu` · `lienket`, cộng `mieng` ở `man.css`)
 * phủ gần hết, nhưng **13 nút trong `web/man/` quên khai lớp**, và chúng rơi về
 * kiểu mặc định của trình duyệt: nền xám lạnh `buttonface` giữa một trang nền
 * be. Chủ dự án chụp lại nút «Nạp từ địa chỉ» ngày 23/08.
 *
 * Đúng cùng một lỗi mà chú thích của khối ô nhập ngay dưới đã mô tả — *"ô vẫn
 * gõ được, chỉ là nhìn như của app khác"* — và cùng cách chữa: khai một nền
 * đúng cho chính thẻ, để quên lớp cũng không rơi ra ngoài bảng màu.
 *
 * Ba hạng kia đều đặt lại ĐỦ `background`, `border`, `padding`, nên chúng không
 * bị dòng này chạm tới. `mieng` cố ý KHÔNG đặt nền — nó nhận nền từ đây, và đó
 * là cái nó vẫn thiếu: viền của `mieng` là thứ duy nhất nói "bấm được", còn nền
 * xám của trình duyệt thì cãi lại điều đó. */
/* `inline-flex` + căn giữa cho MỌI nút: `min-height` một mình chỉ kéo cao cái
 * hộp, chữ vẫn nằm theo dòng cơ sở cũ và bị lệch lên trên. Đặt ở đây một lần
 * để ba hạng nút dưới không phải lặp lại.
 *
 * `justify-content: center` đúng cho một nút NHÃN. Nút nào là một HÀNG — ảnh
 * bên trái, chữ bên phải — phải tự khai `flex-start`; xem `.lua-chon` ở
 * `man.css` và `.ht-muc` bên dưới. */
button {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--k-2);
  background: var(--tam);
  color: var(--chu);
  border: 1px solid var(--vien-nut);
  border-radius: var(--bo);
  padding: var(--k-1) var(--k-4);
  min-height: var(--cao-dk);
  font: inherit;
  cursor: pointer;
}
button:hover:not(:disabled) { background: var(--nen-luot); color:var(--thuong); }

/* ── MẢNG BẤM ĐƯỢC: MỘT `<button>` KHÔNG TRÔNG NHƯ NÚT ────────
 *
 * Thẻ ảnh, ô đếm, vùng kéo–thả, tiêu đề nhóm thanh bên — bốn thứ người dùng
 * BẤM VÀO, nhưng không thứ nào là một cái nút để nhìn. Trước đợt 7 cả bốn dựng
 * bằng `<div data-hd="…">`: chuột bấm được, còn bàn phím nhảy qua và trình đọc
 * màn hình gọi chúng là chữ — đúng lỗi đợt 6d đã dọn cho 18 `<span class="mieng">`,
 * chỉ khác cái thẻ.
 *
 * Đổi sang `<button>` là đúng, nhưng nó kéo theo trọn quy tắc nền ở trên: nền
 * `--tam`, viền, đệm 14px, `justify-content: center`, và trần `--cao-dk` 40px.
 * Trần ấy là lần THỨ SÁU rơi xuống chỗ nó không thuộc về (`.tv-nhan` đợt 6d ·
 * ba nút × và chip số đợt 6g). Luật đã rút ra ở đợt 6g: một lớp đã tự khai kích
 * thước của mình thì trần của người khác chỉ phá nó.
 *
 * ── KHUÔN NÀY ĐÃ CÓ, CHỈ CHƯA CÓ TÊN ─────────────────────────
 * `.ng-anh` (`man.css`) đã tự reset đúng bộ này từ trước — `padding: 0`,
 * `border: 0`, `background: none`, `display: block`. Nó chỉ QUÊN hai thứ, và đo
 * trong trình duyệt 31/08 mới thấy: `min-height` vẫn 40px (chưa lộ ra vì ảnh
 * cao 80px, nhưng một ảnh thấp hơn 40 sẽ để lại khe trắng dưới nó), và
 * `text-align` vẫn `center` — chữ trong nút ảnh căn giữa thay vì căn trái.
 *
 * Nên đây không phải một lớp mới bịa ra: nó là cái khuôn ấy được đặt tên, vá
 * đủ, rồi gom cả bốn chỗ vào. Cùng cách đợt 6g khai một lần cho họ nút ×.
 *
 * ── VÌ SAO ĐỨNG NGAY ĐÂY, KHÔNG XUỐNG DƯỚI ───────────────────
 * Nó phải THẮNG `button` (0,0,1) và THUA lớp riêng của từng chỗ — `.tam`,
 * `.the`, `.bd-tha`, `.ng-anh` đều là (0,1,0) như nó, nên ai khai SAU thì
 * thắng. Đặt ở đây là lớp riêng vẫn giữ được nền, viền, đệm và bo góc của
 * mình; đặt xuống dưới `.tam` là ô đếm mất trắng cái thẻ của nó. */
/* `border-radius: inherit` chứ không `0`: một mảng bấm được nằm TRONG một khung
   (thẻ ảnh) phải lấy bo góc của khung ấy, còn khi chính nó là khung (`.tam`) thì
   lớp riêng khai sau và thắng. `0` sẽ cắt vuông góc ảnh trong thẻ bo tròn.

   `align-items` · `justify-content` · `gap` của `button` KHÔNG cần trung hoà:
   `display: block` ở đây làm cả ba vô hiệu. Lưới suy ra điều đó thay vì giữ một
   danh sách miễn gõ tay — xem `test-kieu` mục 3e. */
.mang-bam {
  display: block; width: 100%; text-align: left;
  min-height: 0; padding: 0; border: 0; border-radius: inherit;
  background: none; color: inherit; font: inherit; cursor: pointer;
}

button.chinh {
  background: var(--thuong); color: var(--chu-tren-mau); border: 0; border-radius: var(--bo);
  padding: var(--k-2) var(--k-5); font: inherit; font-weight: 600; cursor: pointer;
  /* Giãn chữ nhẹ trên mảng rượu vang — nút chính là con dấu của app, và con
   * dấu thì khắc chữ thưa. */
  letter-spacing: .02em;
  min-height: var(--cao-dk);
}

/* 21/08/2026 — hạng nút thứ ba, giữa `chinh` và `mieng`: hành động CÓ GỌI MÁY
 * nhưng không phải hành động chính của khối (Cho máy viết lại · Nạp từ Notion).
 * Bản trước dùng `mieng` cho chúng — cùng hạng với một ô chọn cách dựng, tức là
 * một nút gọi model trông y hệt một cái tick. Viền màu thương hiệu, không nền
 * đặc: đủ nặng để đọc ra "một hành động", đủ nhẹ để không tranh vai con dấu. */
button.phu {
  background: none; color: var(--thuong-chu); border: 1px solid var(--thuong-chu);
  border-radius: var(--bo); padding: var(--k-2) var(--k-4); font: inherit; font-weight: 600;
  cursor: pointer;
  min-height: var(--cao-dk);
}
/* Cỡ nhỏ cho nút phụ đứng TRONG một ô bảng (dòng cảnh) — cùng hạng, ít chỗ hơn. */
button.phu.nho { padding: var(--k-1) var(--k-3); font-size: var(--cs-phu); font-weight: 400 }

/* ── HẠNG NÚT THỨ BA, VÀ VÙNG CHẠM CỦA NÓ — 07/09/2026 ───────
 *
 * `padding: 0` là điều làm nó trông như một liên kết, và cũng là điều làm nó
 * hẹp: đo trên trang mẫu ra **113 nút** rộng 12–21px — «▲»/«▼» đổi thứ tự rộng
 * đúng 12px, «sửa» 21px, «cắt lại» 20px. Chiều cao thì đủ (40px, thừa hưởng từ
 * hàng), nên mắt thường không thấy gì lạ; chỉ ngón tay và con trỏ thấy.
 *
 * WCAG 2.5.8 miễn cho mục tiêu NẰM TRONG một câu — chữ quanh nó mới là thứ
 * quyết định chỗ nó đứng. Đo ra đúng **1** nút thuộc diện ấy; 113 nút kia đứng
 * thành cụm trong một hàng nút, nên chúng không được miễn.
 *
 * `min-width` chứ không phải `padding`: đệm đẩy chữ ra xa nhau và làm một hàng
 * năm nút trông rời rạc, còn bề rộng tối thiểu chỉ nới VÙNG BẤM quanh chữ đang
 * đứng yên. Nút nào đã rộng hơn 24px thì không đổi một pixel nào. */
button.lienket {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--lien-ket); text-decoration: underline; cursor: pointer;
  min-width: var(--cham);
}

/* ── HẠNG NÚT THỨ TƯ — dời từ `man.css` về 01/09/2026 ─────────
 *
 * Việc ② của `../../docs/kien-truc/chuan-cau-truc-giao-dien.md` (luật T2).
 * `mieng` là một ô chọn trong một hàng: cách dựng, hiệu ứng, tỉ lệ cắt. Nó
 * dùng ở **9 tab**, tức nó là từ vựng chung — nhưng nó khai ở `man.css`, tệp
 * tự nhận là *«kiểu riêng của từng màn»*.
 *
 * Chú thích của khối nút ngay trên đã biết điều đó và ghi thẳng: *«Ba hạng nút
 * dưới đây (chinh · phu · lienket, cộng `mieng` ở `man.css`)»*. Một hạng nút
 * phải chú thích rằng nó sống ở tệp khác là một hạng nút đặt nhầm chỗ — và câu
 * ấy đứng đó từ 23/08 mà không ai dời. Nay bốn hạng ở cùng một chỗ, và đọc bốn
 * hạng nút của app là đọc một khối liền. */
.mieng {
  display: inline-flex; align-items: center; gap: var(--k-1);
  /* `--vien-nut` chứ không phải `--vien`: viền ở đây KHÔNG trang trí, nó là
   * thứ duy nhất nói "cái này bấm được". Dùng viền trang trí thì nút chỉ còn
   * 1.18:1 trên nền sáng — nhìn ra một dòng chữ, không ra một cái nút. */
  border: 1px solid var(--vien-nut); border-radius: var(--bo);
  padding: var(--k-1) var(--k-2); margin: 0 var(--k-2) var(--k-2) 0; cursor: pointer; font-size: var(--cs-phu);
}
.mieng.chon { border-color: var(--vien-chon); background: var(--chon); color: var(--chu) }

/* Liệt kê ĐỦ các `type` đang dùng: bản cũ chỉ có `text` và `search`, nên ô
 * mật khẩu ở tab Tài khoản và ô email ở tab Đội nhóm rơi về kiểu
 * mặc định của trình duyệt — nền trắng, viền xám, giữa một trang nền be.
 * Lỗi này không kêu: ô vẫn gõ được, chỉ là nhìn như của app khác. */
/* `date` lọt khỏi danh sách này từ đầu, và nó là kiểu ô DUY NHẤT còn mang mặt
 * trình duyệt giữa một trang nền be — thấp hơn ô cạnh nó 6px, viền xám, nền
 * trắng. Lỗi đúng loại mà đoạn trên vừa mô tả. Thêm vào 23/08/2026. */
/* ── ĐẢO CHIỀU DANH SÁCH — 29/08/2026 ────────────────────────
 * Bản trước LIỆT KÊ những kiểu ô phải tô: text · search · email · password ·
 * number · url · tel · date. Một danh sách như thế phải nhớ thêm mãi, và
 * chú thích ngay trên đã kể nó lọt HAI lần: ô mật khẩu với ô email (23/08),
 * rồi ô ngày (cũng 23/08). Cả hai lần đều không kêu — ô vẫn gõ được, chỉ mang
 * mặt trình duyệt giữa một trang của app khác.
 *
 * Nay liệt kê chiều ngược: những kiểu KHÔNG phải ô chữ. Tập ấy ĐÓNG và ngắn
 * (mười kiểu, không ai thêm nữa), trong khi tập ô chữ thì nền web vẫn đang
 * nới ra — `datetime-local`, `time`, `month`, `week` đã có sẵn và sẽ đúng
 * ngay lần đầu ai đó dùng tới. `<input>` quên khai `type` cũng vào đây, vì
 * HTML mặc định nó là `text` còn bộ chọn cũ thì không.
 *
 * Lưới «ô nhập thiếu type» ở `test-web` vẫn giữ — nó vá triệu chứng, dòng này
 * vá nguyên nhân, và hai hàng rào không thừa. */
input:not([type=checkbox], [type=radio], [type=color], [type=range], [type=file],
          [type=button], [type=submit], [type=reset], [type=image], [type=hidden]),
textarea, select {
  background: var(--sau); color: var(--chu);
  border: 1px solid var(--vien-nut); border-radius: var(--bo);
  padding: var(--k-2); font: inherit;
  min-height: var(--cao-dk);
  box-shadow: var(--bong-lom);
}

/* ── BỐN TRẠNG THÁI CỦA MỘT Ô — 29/08/2026 ───────────────────
 * Sổ tay `docs/tra-cuu/nhan-dien-eos.md` mục 06. Trước hôm nay chỉ có trạng thái
 * ĐẦU TIÊN được vẽ, ba cái còn lại không có dòng nào.
 *
 *   gõ được   nền lõm  · viền điều khiển · chữ thường · CÓ bóng lõm
 *   bị khoá   nền khoá · viền đứt        · chữ NGHIÊNG mờ · KHÔNG bóng
 *   đang gõ   nền lõm  · viền đang chọn  · thêm vòng ngoài
 *   hỏng      nền lõm  · viền xấu        · kèm một dòng nói CÁCH SỬA
 *
 * Bốn tín hiệu độc lập cho ô bị khoá — không bóng, viền đứt, chữ mờ, chữ
 * nghiêng — chứ không chỉ một. Ai không phân biệt được màu vẫn còn ba cái. */
/* Cùng phép loại trừ với khối trên, và vì cùng một lý do: một ô tick bị khoá
 * mà nhận nền `--nen-khoa` + viền đứt + chữ nghiêng thì nó thôi trông như một
 * ô tick. `accent-color` với `opacity` của trình duyệt đã nói đúng chuyện ấy. */
input:not([type=checkbox], [type=radio], [type=color], [type=range], [type=file],
          [type=button], [type=submit], [type=reset], [type=image], [type=hidden]):disabled,
textarea:disabled, select:disabled,
input[readonly], textarea[readonly] {
  background: var(--nen-khoa);
  color: var(--chu-nhat);
  font-style: italic;
  border: 1px dashed var(--vien-khoa);
  box-shadow: none;
  cursor: not-allowed;
}
/* `readonly` KHÁC `disabled` ở một chỗ: nó vẫn nhận được con trỏ bàn phím,
 * nên người dùng bàn phím và trình đọc màn hình vẫn đọc được giá trị. Giữ
 * con trỏ chữ cho nó, đừng nói dối rằng không chạm vào được. */
input[readonly], textarea[readonly] { cursor: text }

/* ── ĐIỀU KHIỂN BỊ TẮT — cùng họ với ô nhập bị khoá ngay trên ─
 *
 * Tới 01/09/2026 ba loại nút và mục hộp thoại đều tắt bằng `opacity: .4–.5`.
 * Đo trên trình duyệt: nhãn nút CHÍNH ra **1,52:1** ở nền sáng — gần như
 * không đọc được — và không lưới màu nào kêu, vì `test-mau` gác CẶP TOKEN
 * còn `opacity` thì trộn ở lúc VẼ, sau khi token đã được đọc xong.
 *
 * Chỉnh số không cứu được: tính ngược ra phải để opacity 0,65–0,92 mới đạt
 * 4,5 — tức bỏ gần hết độ mờ. «Trông có vẻ tắt» và «đọc được» chống nhau
 * trực tiếp khi cơ chế là một lớp alpha phủ đều. Đó là SAI CÔNG CỤ.
 *
 * Nên dùng đúng lối của ô nhập bị khoá: cặp token đã đo (`--chu-nhat` trên
 * `--nen-khoa`, gác 4,5 ở `test-mau`), cộng ba tín hiệu KHÔNG MÀU — viền
 * đứt, bỏ bóng, con trỏ thường. Ai không phân biệt được màu vẫn còn ba cái.
 *
 * Chữ NGHIÊNG thì không lấy sang: trên một nhãn nút, chữ nghiêng đọc ra
 * "nhấn mạnh" chứ không đọc ra "đang tắt". Nên ở đây là ba tín hiệu, không
 * phải bốn — và nói thẳng ra thế còn hơn để người sau đếm nhầm. */
button:disabled,
button.chinh:disabled,
button.phu:disabled,
.ht-muc:disabled {
  background: var(--nen-khoa);
  color: var(--chu-nhat);
  border: 1px dashed var(--vien-khoa);
  box-shadow: none;
  cursor: default;
}
/* Ảnh KHÔNG chở chữ, nên nó là chỗ duy nhất `opacity` còn đúng việc. */
button:disabled img, .ht-muc:disabled img { opacity: .5 }

/* ── GIÁ TRỊ CHỈ ĐỂ ĐỌC — câu ③ của sổ tay, phần CẤU TRÚC ─────
 *
 * Khối `input:disabled` ở trên vá phần NHÌN: ô khoá thôi trông giống ô gõ được.
 * Nhưng một ô gõ-không-được vẫn còn là một Ô — bàn phím nhảy qua nó, trình đọc
 * màn hình gọi nó là "ô nhập, đã tắt", và một câu trả lời dài vẫn bị nhốt trong
 * một hộp cuộn ba dòng. Người dùng KHÔNG có quyền sửa thì thứ họ cần là đọc,
 * không phải một cái ô hỏng.
 *
 * Nên khi không sửa được, đừng vẽ ô: vẽ GIÁ TRỊ. Vẫn mang đủ tín hiệu của
 * trạng thái khoá (nền riêng, chữ nghiêng, viền đứt) để mắt xếp nó cùng họ với
 * ô khoá — chỉ khác ở chỗ nó là chữ thật, chọn được, xuống dòng hết ý.
 *
 * `white-space: pre-wrap` giữ xuống dòng người dùng đã gõ; không có nó thì một
 * bản khai nhiều đoạn dồn thành một khối liền. */
.gia-tri-doc {
  display: block; margin: 0;
  padding: var(--k-2) var(--k-3);
  background: var(--nen-khoa); color: var(--chu-nhat);
  font-style: italic; white-space: pre-wrap; word-break: break-word;
  border: 1px dashed var(--vien-khoa); border-radius: var(--bo);
}
/* Chưa trả lời thì nói ra là chưa, đừng để một hộp rỗng — một khoảng trống
 * không phân biệt được với "đã tải hỏng". */
.gia-tri-doc:empty::before { content: "chưa trả lời"; font-style: italic }

input:focus-visible, textarea:focus-visible, select:focus-visible {
  border-color: var(--vien-chon);
  outline: 2px solid var(--vien-chon);
  outline-offset: 1px;
}

/* Ô HỎNG. `aria-invalid` chứ không phải một lớp riêng: cùng một thuộc tính
 * vừa vẽ được viền đỏ vừa nói cho trình đọc màn hình biết ô này sai — một
 * nguồn sự thật, không hai. Viền đỏ MỘT MÌNH chỉ nói «sai», không nói «sai
 * gì», và người mù màu đỏ-lục không thấy nó đỏ: chỗ gọi phải kèm một dòng
 * nói CÁCH SỬA ngay dưới ô. */
input[aria-invalid="true"], textarea[aria-invalid="true"], select[aria-invalid="true"] {
  border-color: var(--xau);
  border-style: solid;
}
textarea { width: 100%; min-height: 90px; resize: vertical }

/* `<select>` là ô DUY NHẤT trong nhóm trên tự giãn theo OPTION dài nhất thay
 * vì theo khung chứa — input/textarea không có tật này. Một select đứng
 * ngoài `.hang` (nơi flex đã ép nó) tràn thẳng qua viền phải trên điện thoại
 * mà mọi khối cha vẫn đúng 100% — phát hiện 26/08/2026 ở select model logo,
 * dài tới mức lộ ra ngay cả khi không cố ý. `max-width` chứ không `width`:
 * nhiều chỗ đã tự đặt bề rộng riêng (`flex:1` trong `.hang`, `width:100%`
 * trong `.ap-o`), và max-width không giẫm lên none trong số đó. */
select { max-width: 100% }
/* Ô màu đứng CÙNG HÀNG với ô mã màu ở bảng chọn màu (`#st-ban .st-mau`), nên nó
 * theo chung một chiều cao điều khiển. Bản trước ghi 34px — lệch 6px so với ô
 * chữ bên cạnh, đúng loại lệch mà `--cao-dk` sinh ra để dẹp.
 * Mẩu màu 22px trong danh sách hạng mục (`.mau-o`) đè lại ở `man.css`: nó
 * không phải một điều khiển đứng hàng, nó là một chấm màu để xem. */
input[type=color] { height: var(--cao-dk); padding: var(--k-0); background: var(--sau);
                    border: 1px solid var(--vien-nut); border-radius: var(--bo) }

/* Vòng focus dùng chung: bàn phím phải thấy mình đang đứng ở đâu. Trước đây
 * chỉ có vòng mặc định của trình duyệt, và nó gần như tàng hình trên nền tối. */
:focus-visible {
  outline: 2px solid var(--lien-ket);
  outline-offset: 2px;
  border-radius: var(--bo);
}

label.o { display: inline-flex; align-items: center; gap: var(--k-2); margin: 0 var(--k-4) var(--k-2) 0; cursor: pointer }

/* ── `min-width: 0` KHÔNG phải trang trí — 28/08/2026 ─────────
 *
 * Bảng kiểu mặc định của trình duyệt cho `fieldset` một `min-inline-size:
 * min-content`, và đó là thứ DUY NHẤT trong app không co được theo chỗ chứa nó.
 * Đo trong tab Hậu kỳ, khổ 1536×864, thanh bên mở: fieldset «Công thức đã lưu»
 * đòi 489px trong cột phải 460px, thò ra 16px khỏi `#khu`, và kéo `scrollWidth`
 * của cả TRANG lên 1537 — tức là mở tab Hậu kỳ là cả app có thanh cuộn ngang.
 *
 * `#vo` đã khai `minmax(0, 1fr)` cho cột nội dung đúng vì lỗi cùng họ (26/08,
 * Kịch bản ngắn), nhưng luật ấy dừng ở cột — nó không đi xuyên vào một phần tử
 * tự khai min-content cho chính mình. Chỗ chữa là ở đây. */
fieldset {
  border: 1px solid var(--vien); border-radius: var(--bo-lon);
  padding: var(--k-4); margin: 0 0 var(--k-4); min-width: 0;
}
/* Nghiêng như dòng đề từ đầu chương — nhãn nhóm là lời chú, không phải nội dung. */
legend { color: var(--chu-nhat); font-size: var(--cs-phu); padding: 0 var(--k-2); font-style: italic }

/* ── Khối nội dung ──────────────────────────────────────── */

.tam {
  border: 1px solid var(--vien); border-radius: var(--bo-lon);
  padding: var(--k-4); background: var(--tam);
}
/* ── MỘT LỐI TIÊU ĐỀ MỤC — gộp ở đợt 7, 31/08/2026 ────────────
 *
 * Tới hết đợt 6 app có HAI lối tiêu đề mục cùng làm một việc:
 *
 *   `<h3>` trong `.tam`   61 chỗ   mực mờ, không gạch
 *   `<p class="kf-buoc">` 47 chỗ   font tiêu đề, mực đầy, GẠCH CHÂN
 *
 * (Đợt 6d còn đếm ra một lối thứ ba `.tam-dau` 5 chỗ. Đo lại ở đợt 7: sai số
 * của một regex — cả 5 chỗ là `<div class="tam-dau"><h3>…</h3></div>`, tức một
 * hàng flex BỌC quanh `<h3>` để đẩy một `<span>` sang phải, và 5 `<h3>` ấy đã
 * nằm trong con số 61. Không có lối thứ ba; có hai lối và một cái bọc.)
 *
 * ── CHỦ DỰ ÁN CHỐT 31/08, SAU KHI XEM CẢ HAI DỰNG BẰNG CSS THẬT ──
 * Gộp về hình của `.tam h3` — mực mờ, KHÔNG gạch chân — kèm một sửa: *«hai khối
 * cần có khoảng cách chứ không dính liền nhau»*. Bỏ gạch chân đi thì khoảng
 * trắng gánh một mình việc tách hai khối, nên tiêu đề nhận LỀ TRÊN.
 *
 * Bậc chọn bằng cách ĐO, không đoán: dựng bốn bậc cạnh nhau trên trình duyệt
 * (0 · `--k-3` · `--k-4` · `--k-5`) thì `--k-4` là bậc nhỏ nhất mà mắt đọc ra
 * HAI KHỐI, và nghĩa đã khai của nó — «giữa hai NHÓM trường» — đúng việc nó
 * đang làm. `--k-5` là «giữa hai THẺ»: dùng ở đây là nói sai một tầng, và ảnh
 * đo cho thấy nó bắt đầu trông như hai thẻ thật.
 *
 * ── VÀ GỘP VỀ `<h3>`, KHÔNG VỀ `.kf-buoc` ────────────────────
 * `.kf-buoc` là một `<p>`. 47 mục ấy vì thế KHÔNG lên cây tiêu đề: trình đọc
 * màn hình liệt kê tiêu đề của trang thì chúng không có mặt, và người dùng bàn
 * phím không nhảy tới được. Gộp theo chiều ngược — 61 `<h3>` thành `<p>` — là
 * đổi 61 tiêu đề thật thành đoạn văn. Nên `<h3>` thắng cái THẺ, còn `.tam h3`
 * thắng cái HÌNH.
 *
 * `display: flex` giữ đúng cái khả năng duy nhất mà `.kf-buoc` có và `<h3>`
 * chưa: nhận thêm một `.nhat` cuối dòng (nhãn nguồn, link Hướng dẫn) và đẩy nó
 * sang phải. Đó cũng là lý do `.tam-dau` từng phải tồn tại — nay nó dư.
 *
 * Giãn chữ theo quy tắc nền `-.015em`, không phải `.01em` của `.kf-buoc`: mục 04
 * sổ tay khai giãn âm cho tiêu đề từ `--cs-de` trở lên, và đây đúng là cỡ ấy. */
.tam h3 {
  display: flex; align-items: baseline; gap: var(--k-2); flex-wrap: wrap;
  margin: var(--k-4) 0 var(--k-3);
  font-size: var(--cs-de); color: var(--chu); font-weight: 600;
}
/* ── VÌ SAO `--chu` CHỨ KHÔNG `--chu-nhat` — 07/09/2026 ───────
 * Bản trước dùng `--chu-nhat`: 6,21:1 ở nền sáng. Đo cả tầng tiêu đề của thẻ
 * Cẩm nang thì ra một thứ tự lộn ngược — `.khoi-dau b` và `.kn-nhan b`, hai
 * hạng NẰM DƯỚI tiêu đề, đều `--chu` ở 12,09:1. Tức thứ neo mắt lại là thứ mờ
 * nhất trong thẻ, và người ta đọc tiêu đề khối ra một dòng chú thích.
 *
 * `test-mau` không bắt được: 6,21 vượt ngưỡng 4,5 nên nó xanh. Lưới màu đo
 * TỪNG cặp so với nền, không đo THỨ TỰ giữa các cặp — và thứ bậc là một quan
 * hệ, không phải một con số. */
/* Nhãn phụ cuối dòng tiêu đề — đẩy sang phải, và thôi mang nét tiêu đề. */
.tam h3 > .nhat { margin-left: auto; font-weight: 400 }
/* Tiêu đề là dòng ĐẦU của thẻ thì không cần lề trên: `.tam` đã có đệm `--k-4`,
 * và cộng thêm lề vào đó là 32px trống trên đầu mọi thẻ. Bản trước của
 * `.kf-buoc` để mỗi chỗ gọi tự huỷ bằng `style="margin-top:0"` — năm cái nạng
 * như thế đã gỡ trong cùng lượt gộp. */
.tam > h3:first-child { margin-top: 0 }

/* ── CỘT CỦA MỘT MÀN, VÀ NHỊP DỌC — dời về 01/09/2026 ─────────
 *
 * Việc ② (luật T2). `.tai-lieu` là **lớp dùng chung nhất của app**: cả 20 màn
 * đi qua nó, và nó khai ở `man.css` — tệp tự nhận là *«kiểu riêng của từng
 * màn»*. Không lớp nào trong app đặt sai chỗ rõ hơn thế.
 *
 * ── LUẬT `> * + *` THAY 25 LỀ ĐẶT TAY ───────────────────────
 * `.tam` không mang lề: từ đầu, mỗi chỗ gọi tự gắn `style="margin-bottom:14px"`.
 * Bỏ lề đặt tay mà không trả lại nhịp là năm thẻ DÍNH LIỀN nhau — đo được
 * 23/08/2026: cả năm `.tam` của tab đều `margin: 0`, và chồng thẻ ấy đọc ra một
 * khối liền mạch có mấy vạch kẻ ngang.
 *
 * Nhịp thuộc về CỘT, không thuộc về thẻ: `.tam` còn dùng ở lưới, ở hộp thoại,
 * ở cột hẹp — gắn lề vào chính nó là đẩy nhịp sang mọi chỗ đó. Nên luật đứng ở
 * khối chứa: **hai con cạnh nhau thì cách nhau một khe.** Thẻ đầu không lề, nên
 * không có khoảng trắng thừa dưới tiêu đề tab.
 *
 * ── DỜI LÊN ĐÂY LÀM NÓ YẾU ĐI MỘT BẬC, VÀ ĐÓ LÀ CÓ CHỦ Ý ────
 * Luật này (0,1,0) sinh ra để ĐÈ những lề đặt tay, và ở `man.css` nó đứng muộn
 * nên nó đè được cả thứ cùng hạng. Dời lên `nen.css` là bất kỳ lề cùng hạng nào
 * ở `man.css` cũng thắng nó.
 *
 * Đổi ấy được, vì 25 lề đặt tay kia ĐÃ GỠ — và điều đó không tin, mà đo: lượt
 * dời này chụp kiểu tính-toán của mọi phần tử trên trang mẫu trước và sau, so
 * từng thuộc tính một, và không phần tử nào đổi. Ngày nào phép so ấy đỏ lên ở
 * một lề, thì đó là một lề đặt tay vừa quay lại — chữa ở chỗ gọi, đừng dời luật
 * này xuống lại.
 *
 * ── HỌ `--khe-*` ĐÃ ĐI HẲN — 29/08/2026, đợt 6 ────────────────
 * Khối này từng khai `--khe-1..4` = 4·8·16·24px: một bộ tên THỨ HAI cho đúng
 * cái thang mà `:root` đã khai, và bộ ấy còn được dùng nhiều hơn — 91 chỗ so
 * với 78. Hai từ vựng cho một thang chính là cách một thang bắt đầu trôi lại:
 * người viết phải chọn tên, mà không luật nào nói chọn tên nào.
 *
 * Nguy hiểm thật không ở cái tên mà ở HAI NGUỒN cho một con số — ngày `--k-2`
 * dời mà `--khe-2` ở lại 8px thì app có hai khoảng cách 8px khác nhau, đúng lỗi
 * đợt 2b nhưng ở tầng token.
 *
 * Đợt 5 cho chúng suy ra từ thang (an toàn, vì một phiên khác đang viết dở mã
 * dùng tên cũ); đợt 6 dời nốt 91 chỗ gọi rồi gỡ hẳn khối khai. Số hiệu KHÔNG
 * khớp nhau khi dời — khe-3 là k-4, khe-4 là k-5 — nên bảng cặp phải viết tay,
 * và đó là lý do việc này không bao giờ là một lượt thay tên máy móc.
 *
 * `test-kieu` mục 0b nay gác chiều ngược: tên cũ KHÔNG được quay lại. */
.tai-lieu { width: 100% }
.tai-lieu > * + * { margin-top: var(--k-5) }

/* ── TRANG DẠNG TÀI LIỆU — đổi tên và dời về 01/09/2026 ───────
 *
 * Việc ④ của `../../docs/kien-truc/chuan-cau-truc-giao-dien.md`, chủ dự án chốt
 * tên. Trước đó lớp này tên `.huong-dan` và khai ở `man.css`.
 *
 * ── CÁI TÊN CŨ MANG HAI NGHĨA, VÀ ĐỔI TÊN GỠ LUÔN CÁI VA ────
 * `.huong-dan` chỉ HAI thứ không liên quan gì nhau trong cùng một app:
 *
 *   ① vỏ CHỮ của một trang tài liệu — khối này, 4 màn dùng
 *   ② mục thanh bên của tab Hướng dẫn — `#ben a.huong-dan` ở `vo.css`
 *
 * Hai nghĩa ấy chưa va nhau vì nghĩa ② luôn nằm dưới `#ben`, còn nghĩa ① nằm
 * dưới `#khu`. Nhưng đó là may, không phải luật: một lớp `.huong-dan p` không
 * hỏi ai là cha nó. Đây đúng điều kiện đã sinh ra tai nạn `.bd-vai` ⟷
 * `.khoi-vai` — và lượt đổi tên này gỡ nó, vì sau đây `.huong-dan` chỉ còn MỘT
 * nghĩa: mục thanh bên.
 *
 * ── VÀ CÁI TÊN CŨ NÓI SAI VỀ CHỦ ────────────────────────────
 * Bốn màn dùng vỏ này — Hướng dẫn, Đăng nhập, Trí nhớ AI, Ví credit — và chỉ
 * MỘT trong bốn là tab Hướng dẫn. Ba màn kia phải mượn một cái tên không phải
 * của mình; chú thích của chúng ghi ra điều đó bằng tay.
 *
 * ── NÓ KHÁC `.tai-lieu` Ở ĐÂU ───────────────────────────────
 * Hai lớp này thường lồng nhau (`.tai-lieu > .trang-tai-lieu`) nên phải nói rõ:
 *   `.tai-lieu`       — BỐ CỤC: bề rộng cột, và nhịp 24px giữa hai thẻ
 *   `.trang-tai-lieu` — CHỮ: cỡ tiêu đề, cao dòng đoạn văn, hộp mã
 * Một cái lo chỗ đứng của các khối, một cái lo chữ bên trong chúng. Tên gần
 * nhau vì hai thứ này thật sự cùng họ; nếu ngày nào phải phân biệt gấp thì đọc
 * đúng hai dòng trên. */
.trang-tai-lieu h2 {
  /* 19px thay vì 17: serif cần thêm cỡ mới giữ được sức nặng của tiêu đề mục. */
  font-size: var(--cs-tab); font-weight: 600; margin: var(--k-6) 0 var(--k-3);
  padding-bottom: var(--k-2); border-bottom: 1px solid var(--vien);
}
/* ── CON TRỰC TIẾP, KHÔNG PHẢI MỌI `h3` BÊN TRONG — 02/09/2026 ──
 *
 * Luật này dành cho tiêu đề mục TRẦN của một trang tài liệu — hai cái ở Trí nhớ
 * AI («Đang áp dụng ở đâu», «Hệ thống đo được gì»), con trực tiếp của
 * `.trang-tai-lieu`. Nhưng bộ chọn con-cháu với luôn vào mọi `<h3>` nằm trong
 * một `.tam` bên trong trang ấy, và nó CÙNG độ đặc hiệu 0,1,1 với `.tam h3` —
 * khai sau nên thắng.
 *
 * Đo trong trình duyệt 02/09: 13 tiêu đề mục của Ví credit và Trí nhớ AI vẽ ra
 * 14px mực `--chu` đầy, trong khi 19 tab kia vẽ 16px mực `--chu-nhat` mờ. Lệch
 * HAI CHIỀU NGƯỢC NHAU — nhỏ hơn nên nhẹ đi, mực đầy nên nặng lên — và không
 * lớp nào nói ra chuyện hai tab ấy cố ý khác cả app. Vì nó không cố ý.
 *
 * `>` trả lại đúng phạm vi luật này định phục vụ. */
.trang-tai-lieu > h3 { font-size: var(--cs-chinh); margin: 0 0 var(--k-2); color: var(--chu) }
.trang-tai-lieu p  { line-height: var(--dong-chay) }
.trang-tai-lieu td { line-height: var(--dong-chay) }
.trang-tai-lieu ol, .trang-tai-lieu ul { line-height: var(--dong-chay) }
.trang-tai-lieu .tam { margin: var(--k-4) 0 }
/* Hộp mã đi cùng bộ chữ chứ không ở lại `man.css`: nó là một thẻ HTML chung,
   không phải một khối của tab nào. (Ghi ra một chỗ hở gặp lúc dời: ba `<pre>`
   khác trong app — `hau-ky-prompt` và hai tệp `kich-ban-ngan-*` — nằm NGOÀI vỏ
   này nên chúng chưa từng nhận kiểu nào. Không sửa ở đợt này.) */
.trang-tai-lieu pre {
  background: var(--sau); border: 1px solid var(--vien);
  border-radius: var(--bo); padding: var(--k-3); overflow-x: auto;
}

/* ── MỘT TRƯỜNG TRONG FORM — đổi tên và dời về 01/09/2026 ─────
 *
 * Việc ④ (luật T2 · Đ4), chủ dự án chốt tên. Trước đó cặp này tên `.kf-o-chu` +
 * `.kf-nhan` và khai ở `man.css` — tức mang tiền tố của tab Kịch bản ngắn, dù
 * **bốn tab dùng nó** (Kịch bản ngắn · Kịch bản phim · Phòng thử · Media nguồn).
 *
 * ── VÌ SAO «TRƯỜNG», KHÔNG PHẢI «Ô CHỮ» ─────────────────────
 * Tên cũ hứa một cái ô. Khối này **không hứa thế**: một trong ba chỗ dùng thật —
 * «Hiệu ứng Ken Burns — $0» ở form sửa cảnh — không có ô nhập nào, dưới nhãn là
 * một hàng nút `.mieng`. Một cái tên hứa nhiều hơn thứ nó chở là cái tên sẽ dẫn
 * người sửa sau đi nhầm.
 *
 * ── BA TẦNG, VÀ TẦNG THỨ BA KHÔNG BẮT BUỘC ──────────────────
 *   ① `.truong-nhan`  nhãn — 14px nét 600, mực đầy
 *   ② `.nhat`         câu giải thích — 12px, mực mờ
 *   ③ ô gõ · hàng nút · hoặc không có gì
 *
 * Việc THẬT của lớp này nằm ở tầng ②: `.nhat` vốn là `display: inline`, và ở đây
 * nó thành `block` kèm lề dưới. Không có lớp thì nhãn và câu giải thích nằm cùng
 * một dòng — đúng chỗ chủ dự án chấm 7/10 ngày 17/08/2026: *«chưa thật sự sắc
 * nét, rõ ràng giữa tiêu đề và trường nhập liệu»*. Ba mức thông tin trông gần
 * bằng nhau thì mắt phải ĐỌC mới biết cái nào là cái nào.
 *
 * Sửa bằng KHOẢNG CÁCH và ĐỘ TƯƠNG PHẢN chứ không thêm đường kẻ: trong một nhóm
 * thì sát nhau, giữa hai nhóm thì xa hẳn; nhãn to hơn, đậm hơn, sáng hơn.
 *
 * Nó là một `<label>` thật, nên bấm vào nhãn là con trỏ nhảy vào ô.
 *
 * ── BIẾN THỂ NGANG: `.truong-ngang` ────────────────────────
 * Cùng ba tầng, nhưng nhãn và ô nằm MỘT dòng và ô rộng 64px — «Chương» · «Giây»
 * · «Mấy chương». Dùng ở 3 tab, nên nó thuộc về đây y như biến thể dọc.
 *
 * Vì sao là một lớp riêng chứ không phải `.truong.ngang`: nó đổi cả `display`
 * lẫn cách nhãn cư xử (`margin: 0`), tức nó thay khuôn chứ không thêm một nét.
 * Lớp ghép để dành cho biến thể chỉ đổi một hai nét — xem luật ③ của bộ chuẩn. */
.truong-ngang { display: flex; align-items: center; gap: var(--k-2) }
.truong-ngang .truong-nhan { margin: 0 }
/* Đệm và cao dòng gộp về đây ở đợt ④: chúng vốn nằm chung một luật với hai
   nhánh `.truong`, và hai nhánh ấy đã sang `nen.css`. `width` giữ 64px — ô này
   chở một hai chữ số, không phải một câu. */
.truong-ngang input { width: 64px; padding: var(--k-2) var(--k-3); line-height: var(--dong-chay) }
.truong { display: block; margin: var(--k-5) 0 0 }
.truong:first-of-type { margin-top: var(--k-3) }
.truong-nhan {
  display: block; margin-bottom: var(--k-1);
  font-size: var(--cs-chinh); font-weight: 600; color: var(--chu);
  letter-spacing: -.01em;
}
.truong .nhat { display: block; margin: 0 0 var(--k-2); font-size: var(--cs-phu); line-height: var(--dong-chay) }
/* `input[type=text]` có mặt vì một ô chữ MỘT dòng trong khối này phải trông y
   hệt ô nhiều dòng — bản trước chỉ nuôi `textarea`, nên ô «Tên phim» của tab
   Kịch bản phim thiếu cả bề rộng lẫn nền. */
.truong textarea,
.truong input[type=text] {
  width: 100%; padding: var(--k-2) var(--k-3); line-height: var(--dong-chay);
}

/* ── LƯỚI CHỌN — đổi tên và dời về 01/09/2026 ────────────────
 *
 * Việc Ⓐ. Trước đó tên `.khung-chon` + `.mot-bai` và khai ở `man.css`. Ba tab
 * dùng cặp này — Hậu kỳ · Phòng thử · Tổng quan — và «một bài» là **tên sai**:
 * khối ấy chở một lượt tạo, một lượt thử, một việc gần đây. Không cái nào là
 * một bài. Tên mới nói đúng việc: đây là MỘT LỰA CHỌN trong một lưới. */
.lua-chon-luoi {
  display: grid; gap: var(--k-2); margin-top: var(--k-3); max-height: 100%; overflow-y: auto;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}
.lua-chon {
  /* Nút kiểu HÀNG: nội dung dồn về trái, không nhận `center` của quy tắc nền
     `button` — nếu không, thẻ nhãn ngắn bị đẩy ảnh vào giữa và cột ảnh so le
     (đo ở lưới chọn ảnh Hậu kỳ: lề trái ba thẻ liền nhau ra 72 · 9 · 68px). */
  justify-content: flex-start;
  display: flex; gap: var(--k-3); align-items: center; text-align: left; cursor: pointer;
  background: var(--sau); border: 1px solid var(--vien); border-radius: var(--bo-lon);
  padding: var(--k-2); color: inherit; font: inherit; width: 100%;
}
.lua-chon:hover { border-color: var(--vien-sang) }
.lua-chon.dang { border-color: var(--vien-chon); background: var(--chon) }
.lua-chon img { width: 52px; height: 52px; object-fit: cover; border-radius: var(--bo-nho); background: var(--nen-anh); flex: 0 0 auto }
/* Cùng chỗ, cùng kích thước, nhưng KHÔNG phải thẻ ảnh: `<img src="">` cho ra ô
   vỡ, và một danh sách toàn ô vỡ đọc thành "app đang hỏng" — 25/08/2026. */
.lua-chon .khong-tep {
  width: 52px; height: 52px; border-radius: var(--bo-nho); flex: 0 0 auto;
  background: var(--sau); color: var(--chu-nhat);
  display: flex; align-items: center; justify-content: center; font-size: var(--cs-nho);
}
.lua-chon .chu { min-width: 0 }
.lua-chon .chu b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
/* Không dùng lại được. Nút này KHÔNG `disabled` — bấm vào vẫn mở ra xem —
   nên nó không lấy mặt của điều khiển bị tắt, chỉ hạ nhấn: chữ về token đã
   đo, viền đổi sang đứt, và ảnh thì mờ được vì ảnh không chở chữ. */
.lua-chon.khong { color: var(--chu-nhat); border-style: dashed }
.lua-chon.khong img { opacity: .5 }

/* ── DÒNG CHÚ THÍCH DƯỚI MỘT GIÁ TRỊ — dời về 01/09/2026 ─────
 *
 * Việc Ⓒ. Tên cũ `.bc-nguon` nghĩa là «nguồn của giá trị», và đo chỗ dùng thật
 * thì nó sai: Chuẩn thương hiệu viết `<span class="chu-thich canh">máy đã chạy ·
 * chưa mở bán</span>` — đó là TRẠNG THÁI, không phải nguồn.
 *
 * Thứ nó thật sự là: một dòng nhỏ, mực mờ, gắn dưới một giá trị, tô được ba màu
 * nghĩa. Tên mới nói vai ấy, nên nó đúng cho cả bốn tab đang dùng. */
.chu-thich { display: block; font-size: var(--cs-nho); font-weight: 400; color: var(--chu-mo); margin-top: 1px }
.chu-thich.canh { color: var(--canh) }
.chu-thich.tot { color: var(--tot) }
/* Cùng nhãn ấy khi nó đứng GIỮA một câu, không phải dưới một nhãn — thẻ nhấn
   của ba khối nền tảng dùng biến thể này. `display: block` mặc định là đúng ở
   `.bc-dong` (nó là chú thích của nhãn, nằm dưới nhãn) và sai ở đây. */
.chu-thich.trong-dong { display: inline; margin-top: 0; margin-right: var(--k-1) }


/* ── TÊN DƯỚI MỘT Ô ẢNH — đổi tên và dời về 01/09/2026 ───────
 * Việc Ⓖ. Tên cũ `.kf-ten` mang viết tắt của một tab, nhưng bốn tab dùng nó. */
.ten-o {
  font-size: var(--cs-nho); line-height: var(--dong-chat); word-break: break-word;
  color: var(--chu-nhat);
}

/* Dòng một vế thì không phải «giá trị để tra» — trả nó về màu chữ thường. Luật
   bắc cầu giữa hai lớp đều ở tệp này, nên nó về đây (việc Ⓑ, 01/09/2026); việc
   cho nó CO ĐƯỢC thì `.doi > :last-child:not(:only-child)` ở trên đã lo. */
.tam .doi > span:only-child { color: var(--chu) }

/* ── THANH CÔNG CỤ ĐẦU TRANG — đổi tên và dời về 01/09/2026 ───
 *
 * Trước đó tên `.hd-thanh` và khai ở `man.css`. Năm màn dùng nó, và chú thích
 * của bốn trong năm màn ấy đều phải ghi đúng một câu — «theo khuôn hd-thanh» —
 * để giải thích vì sao mình mượn một lớp mang tên tab khác. **Một lớp mà bốn
 * màn phải viết chú thích để biện minh cho việc dùng nó thì cái tên đã sai.**
 *
 * Nó chở: tiêu đề hoặc lời dặn ở trái, nút hành động ở phải, một đường kẻ dưới.
 * `align-items: flex-end` để hai vế lệch cỡ chữ vẫn thẳng chân. */
.thanh-cong-cu {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: var(--k-4); flex-wrap: wrap; margin-bottom: var(--k-4);
  padding-bottom: var(--k-3); border-bottom: 1px solid var(--vien);
}
/* `h2` trong thanh này bỏ viền và đệm của quy tắc `.trang-tai-lieu h2` — thanh
   đã có đường kẻ của chính nó, hai đường kẻ chồng nhau là một vạch dày. */
.thanh-cong-cu h2 { border: 0; padding: 0 }

/* ── THANH HÀNH ĐỘNG — dời về 01/09/2026 (đợt ⑫, luật Đ4) ────
 *
 * Cặp của `.thanh-cong-cu` ngay trên: thanh TRÊN chở công cụ của cả trang,
 * thanh DƯỚI chở hành động của một thẻ. Trước đó nó tên `.ns-hanh-dong` và ở
 * `man.css` — một lớp hai tab dùng mà đeo tên viết tắt của một tab.
 *
 * Nó KHÔNG phải "một hàng nút". Bảng sửa cao ~840px trên một khung nhìn 720px,
 * nên nút Lưu đặt ở cuối luồng văn bản là nằm ngoài tầm mắt đúng lúc người ta
 * vừa chỉnh xong và muốn lưu. Dính đáy thì nó luôn ở đó, và không phải đoán
 * người dùng sẽ cuộn tới đâu. Lề âm để nó ăn hết bề ngang thẻ chở nó. */
.thanh-hanh-dong {
  position: sticky; bottom: 0; z-index: var(--lop-dinh);
  display: flex; align-items: center; gap: var(--k-3); flex-wrap: wrap;
  margin: var(--k-4) -var(--k-4) -var(--k-4); padding: var(--k-3) var(--k-4);
  background: var(--tam);
  border-top: 1px solid var(--vien);
  border-radius: 0 0 var(--bo-lon) var(--bo-lon);
}

/* ── LƯỚI HAI CỘT — bố cục Nocturne, 28/08/2026 ──────────────
 *
 * Cả 17 tab trong bộ bàn giao dùng CÙNG một hình: cột trái rộng là thứ người
 * dùng SOẠN, cột phải hẹp là thứ đang CHẶN họ — giá của cú bấm sắp tới, cái
 * còn thiếu, prompt sẽ gửi đi, kết quả vừa về. Bề rộng cột phải khác nhau theo
 * tab (380px→700px), nên nó là một biến chứ không phải một con số ở đây.
 *
 * ── VÌ SAO CHIỀU NÀY, KHÔNG PHẢI CHIỀU NGƯỢC ─────────────────
 * `.ban-lam-viec` (bản cũ) chia `340px + 1fr`: hẹp trái, rộng phải. Ba tệp
 * dùng nó, và cả ba đều đặt FORM ở cột hẹp — tức là thứ người ta gõ vào bị
 * nhét chỗ chật nhất màn hình. Bộ bàn giao lật lại: soạn ở chỗ rộng, còn thứ
 * chỉ để ĐỌC thì cột hẹp là đủ. Lớp cũ **đi hẳn 28/08/2026** (đợt 2b); xem đầu
 * `man.css` về chỗ ba tệp ấy đã chuyển tới.
 *
 * Gập về một cột dưới 1180px: cột phải 480px cộng cột trái tối thiểu ~600px là
 * đã 1100px trước khi tính thanh bên 272px. Dưới ngưỡng ấy hai cột thành hai
 * dải chữ hẹp, và không dải nào đọc được. */
.hai-cot {
  /* Mặc định khai NGAY TẠI ĐÂY chứ không ở `:root`: nó là một núm của chính
     lưới này, không phải một token của bảng màu. Tab nào cần khác thì đặt đè
     `style="--cot-phai:620px"` trên phần tử lưới. */
  --cot-phai: 480px;
  display: grid; gap: var(--k-4); align-items: start;
  /* `min(…, 46%)` là cái chặn thứ hai, và nó chặn một ca mà ngưỡng gập bên
     dưới không với tới: cửa sổ vẫn rộng nhưng cột phải khai 620px thì cột trái
     bị bóp còn một dải hẹp. Cột phải không bao giờ được chiếm quá nửa. */
  grid-template-columns: minmax(0, 1fr) min(var(--cot-phai), 46%);
}
.hai-cot > .trai, .hai-cot > .phai {
  min-width: 0; display: flex; flex-direction: column; gap: var(--k-4);
}

/* ── HAI CỘT ĐỀU NHAU — đợt 2t, bản vẽ N3, 29/08/2026 ─────────
 *
 * `.hai-cot` mặc định là «soạn rộng + rail hẹp», và cái hẹp ấy đúng khi cột
 * phải chỉ để ĐỌC. Trần chi tiêu không phải hình đó: cả hai cột chở cùng một
 * loại thẻ — một ô trần có chỗ gõ số — chỉ khác nhau ở BÁN KÍNH của cái phanh.
 * Hai thứ ngang hàng mà một bên hẹp hơn là nói rằng bên ấy phụ, và ở đây thì
 * «trần cả tháng» không hề phụ so với «trần một cú bấm».
 *
 * Khác `.st-cot` (`1fr : 0.92fr`) ở lý do, không ở con số: bên ấy nhường vài
 * điểm ảnh cho cột chở ẢNH; ở đây không cột nào cần hơn cột nào.
 *
 * LUẬT GẬP KHAI LẠI, KHÔNG «MƯỢN» — cùng cái bẫy đã cắn ba lần: luật gập của
 * `.hai-cot` nằm trong `@container` ở hạng (0,1,0), còn lớp này (0,2,0) thắng
 * nó kể cả khi điều kiện container đã khớp. Một lớp biến thể ghi đè
 * `grid-template-columns` thì phải chở theo CẢ ngưỡng gập của chính nó. */
.hai-cot.cot-deu { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) }
@container khu (max-width: 1040px) {
  .hai-cot.cot-deu { grid-template-columns: minmax(0, 1fr) }
}

/* ── CỘT PHẢI ĐI THEO KHI CUỘN — XIN, KHÔNG MẶC ĐỊNH ─────────
 *
 * Đo trong app 28/08 ở Media nguồn: cột phải cao 403px cạnh một cột trái cao
 * 3182px — 88% chiều cao của nó là khoảng trống, và ba khối nó chở (tệp cần
 * đặt tên · dung lượng · đi tiếp) biến mất khỏi màn ngay màn hình thứ hai.
 * Bản vẽ không gặp chuyện này vì vùng nội dung của nó tự cuộn và cột phải luôn
 * trong khung; app này cuộn cả trang, nên `sticky` là bản dịch đúng của ý ấy.
 *
 * ── VÌ SAO PHẢI XIN TỪNG TAB ────────────────────────────────
 * Cột phải KHÔNG phải lúc nào cũng thấp hơn màn hình. Kịch bản phim chở năm
 * khối ④⑤⑥⑦⑧ ở cột ấy; ghim đỉnh một cột cao hơn khung nhìn là ghim luôn phần
 * đáy của nó ra ngoài tầm với, và không có cách nào cuộn tới. Nên đây là một
 * lớp phải KHAI, và tab nào khai thì tab ấy đã đo cột của mình.
 *
 * `max-height` + `overflow-y` là cái chặn thứ hai, cho ngày một khối mới rơi
 * vào cột: cột dài ra thì nó tự cuộn trong chính nó, không thò ra ngoài màn.
 *
 * ── `--cao-dinh`: THỨ KHÁC ĐANG DÍNH PHÍA TRÊN ──────────────
 * Dải tiêu đề không phải thứ duy nhất dính đỉnh. Media nguồn còn một thanh lọc
 * dính, cao 61px và tô nền đặc — đo được 28/08: cột phải bám lên 82px thì 47px
 * đầu của nó nằm khuất dưới thanh ấy, tức là tiêu đề khối đầu tiên biến mất.
 * Tab nào chồng thêm một tầng dính thì khai chiều cao tầng ấy vào `--cao-dinh`;
 * mặc định 0 cho sáu tab còn lại, vốn không có tầng nào. */
.hai-cot > .phai.bam {
  position: sticky; top: calc(var(--cao-dau-man) + var(--cao-dinh, 0px) + 14px);
  max-height: calc(100vh - var(--cao-dau-man) - var(--cao-dinh, 0px) - 28px);
  overflow-y: auto; scrollbar-width: thin;
}
/* Gập một cột thì thôi bám: cột phải nay là một dải nằm DƯỚI cột trái, và một
   dải cuối trang ghim đỉnh chỉ tự cắt cụt chính nó. */
@container khu (max-width: 1040px) {
  .hai-cot > .phai.bam { position: static; max-height: none; overflow-y: visible }
}

/* ── LƯỚI HAI Ô ĐỀU NHAU — dời từ `man.css` về 01/09/2026 ─────
 *
 * Việc ② (luật T2). Lưới bố cục thứ hai của app, dùng ở **5 tab** — nó thuộc
 * cùng họ với `.hai-cot` ngay trên, và nay đứng cùng chỗ.
 *
 * ── DỜI LÊN ĐÂY KHÔNG LÀM VỠ HAI BIẾN THỂ CỦA NÓ ────────────
 * `man.css` có `.kn-hinh` và `.kn-giao` — hai lớp ghi đè `grid-template-columns`
 * của lớp này ở CÙNG hạng riêng (0,1,0), nên chỉ thứ tự nguồn quyết ai được
 * dùng. Chú thích ở đó viết *«đặt NGAY SAU nó»*, và đo 29/08 đã cho thấy đặt
 * ngược lại thì vùng Ⓓ vẫn ra hai cột dù đã khai ba.
 *
 * Lượt dời này đẩy lớp gốc lên SỚM HƠN, tức khoảng cách còn nới ra: hai biến
 * thể vẫn đứng sau, và nay chúng đứng sau ở một thứ tự do `index.html` khoá
 * (nen → vo → man) thay vì do vị trí dòng trong một tệp 2.700 dòng. Quan hệ ấy
 * chắc hơn chứ không lỏng đi — nhưng chú thích bên kia phải sửa theo, vì
 * «ngay sau» thôi đúng khi hai thứ ở hai tệp. */
.canh-nhau {
  /* 16px, cùng khe với `.hai-cot`: hai lưới này nằm cạnh nhau trong cùng một
     màn, và hai khe khác nhau đọc ra hai hệ lưới khác nhau. */
  display: grid; gap: var(--k-4); align-items: start;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
/* Gập rồi thì hai khối chồng lên nhau, và lúc ấy khe giữa chúng là khe giữa HAI
   THẺ — nhịp dọc của tài liệu đòi 24px ở chỗ đó, không phải 16px. */
@container khu (max-width: 900px) {
  .canh-nhau { grid-template-columns: minmax(0, 1fr); gap: var(--k-5) }
}
/* Một ô của cặp có thể chở HAI thẻ, không phải một: khối «Bản khai» trả về cả
   thẻ khai lẫn thẻ bản hệ thống viết lại. `.tam` không mang lề (xem `.tai-lieu >
   * + *`), và nhịp dọc ấy chỉ với tới CON TRỰC TIẾP của `.tai-lieu` — nên hai
   thẻ nằm trong một ô dính liền nhau nếu không có dòng này. */
.canh-nhau > * > .tam + .tam { margin-top: var(--k-5) }

/* ── ĐO CỘT NỘI DUNG, KHÔNG ĐO CỬA SỔ ────────────────────────
   Đây là lỗi đo được 28/08 chứ không phải đề phòng: một `@media (max-width:
   1180px)` nhìn CỬA SỔ, mà thứ chứa lưới này là `#khu` — vốn đã bị thanh bên
   272px ăn mất. Ở cửa sổ 1280px (trên ngưỡng, nên không gập) cột trái chỉ còn
   316px, và mọi khối trong đó vỡ.

   `@container` đo đúng thứ cần đo, và nó còn tự đúng khi người dùng gập thanh
   bên: cột nội dung rộng thêm 208px thì lưới hai cột mở lại, không phải đợi ai
   kéo cửa sổ. Không một ngưỡng @media nào làm được điều đó. */
@container khu (max-width: 1040px) {
  .hai-cot { grid-template-columns: minmax(0, 1fr) }
}

/* ── KHỐI ĐÁNH SỐ ────────────────────────────────────────────
 *
 * Vạch màu 4px bên trái + một số + tên + nhãn vai căn phải. Nó là một BIẾN THỂ
 * của `.tam`, không phải một thẻ thứ hai — khai lại nền, viền và đệm ở đây là
 * dựng hai chỗ để lệch nhau đúng lúc ai đó sửa `.tam`.
 *
 * Khác `.tam` ở đúng một điều, và điều ấy là cả lý do nó tồn tại: `.tam` là một
 * thẻ nội dung ngang hàng với mọi thẻ khác, còn `.khoi` là một BƯỚC trong một
 * thứ tự — số của nó nói ra thứ tự ấy, và vạch màu nối các bước thành một cột
 * đọc được từ trên xuống mà không phải đọc chữ.
 *
 * Bo góc chỉ ba phía: vạch trái là một cạnh phẳng, bo nó lại thì vạch mảnh dần
 * ở hai đầu và thôi ra một vạch.
 *
 * ── GỐC: `.bd-*` CỦA TAB GHÉP ẢNH THẬT (v9) ──────────────────
 * Bộ này đã có từ v9, nhưng mang tiền tố `bd-` nên khoá vào một tab. `.bd-dau`
 * thì lại đã lan ra 17 tệp mà vẫn đội tên ấy — đúng họ lỗi "tên của MÃ lọt ra
 * chỗ dùng chung". Bố cục Nocturne cần đúng bộ này ở 17 tab, nên nó bỏ tiền tố
 * và về đây, cạnh `.tam`. */
.tam.khoi { display: flex; flex-direction: column; gap: var(--k-3); margin: 0 }

/* ── VẠCH TRÁI = «KHỐI NÀY BẮT BUỘC» — chốt 01/09/2026 ──────
 *
 * Vạch từng dính vào `.khoi`, nên MỌI bước đều có vạch: mười ba khối trên hai
 * tab Kịch bản. Chủ dự án: *«Chỉ block nào thực sự quan trọng mới cần phải tô
 * viền.»* Chú thích `kich-ban-the.js` đã tự nêu đúng bệnh từ trước — *«Khách
 * hàng (bắt buộc, chặn cắt) và Ảnh phong cách (tuỳ chọn, không đi vào lượt
 * cắt) trông giống hệt nhau»*.
 *
 * Nên vạch thành một lớp RIÊNG, và nó nói đúng một câu: thiếu khối này thì
 * không ra kết quả. Một khối có số mà bỏ qua được thì có huy hiệu, không vạch.
 *
 * Góc trái vuông — xem mục 4c của `test-kieu`. */
.tam.can {
  border-left: 4px solid var(--thuong);
  border-radius: 0 var(--bo-lon) var(--bo-lon) 0;
}
/* Bước chỉ mượn MỘT PHẦN của thứ bạn đưa vào — khối «Phong cách» của tab Ghép
 * ảnh thật bỏ qua người, vật và bối cảnh, chỉ học cách nhìn. Nét đứt cộng màu
 * CẢNH nói ra điều đó mà không tốn một dòng chữ, và nó là màu NGHĨA chứ không
 * phải sắc nhấn: bản vẽ Nocturne phải dịch bốn màu nghĩa sang bốn bậc sáng vì
 * hệ ấy không có token nghĩa nào — app này thì có, nên giữ. */
.tam.can.nhe { border-left: 4px dashed var(--canh) }

/* `flex-wrap` từ 28/08: tab Kịch bản phim đưa vào đây một dòng đầu bốn phần —
   số, tên, câu tóm tắt, rồi nút «xem / sửa» đẩy sang phải. Ở cột 700px nó vừa,
   ở cột đã gập thì không, và một dòng đầu không xuống hàng được là một dòng đầu
   tràn khỏi thẻ. Bộ ba phần của tab Ghép ảnh thật không đổi gì vì nó chưa bao
   giờ chạm mép. */
.khoi-dau { display: flex; align-items: baseline; gap: var(--k-2); margin: 0 0 var(--k-2); flex-wrap: wrap }

/* `.khoi-tao` ĐI HẲN ngày 01/09/2026 (đợt ⑭). Nó sống được hai đợt và chết vì
   luật mới: nền của nó là `--tam` — đúng nền `.tam` đã cho — còn vạch thì nay
   thuộc về `.can`. Một lớp không còn khai gì riêng là một lối thứ hai để làm
   cùng một việc, và người sửa sau sẽ phải đoán chọn cái nào. */
.khoi-so {
  flex: 0 0 auto; font-size: var(--cs-nho); font-weight: 600;
  /* `--chu-tren-mau`, không phải `--nen`: `--thuong` là màu ĐẬM cố định ở cả
   * hai nền, nên mượn `--nen` (cũng đậm ở nền tối) là số chìm vào huy hiệu.
   * Khối «nhẹ» thì NGƯỢC LẠI phải giữ `--nen`: `--canh` đổi độ sáng theo nền,
   * nên nền trang luôn ngược sáng với nó. */
  color: var(--chu-tren-mau); background: var(--thuong);
  border-radius: var(--bo-nho); padding: 1px var(--k-2);
  /* ── BỀ RỘNG TỐI THIỂU, VÀ VÌ SAO — 02/09/2026 ─────────────
   * Đo trong trình duyệt: cùng lớp này, hộp ra 23,2px với «8» nhưng 31,3px với
   * «②» và 28,2px với «10» — huy hiệu co theo BỀ RỘNG KÝ TỰ, nên mép trái các
   * tiêu đề trong một chuỗi so le nhau tới 8px.
   *
   * Đây đúng cái bẫy mà luật 2 của mục 06b đã lường trước cho `data-so`
   * (`tabular-nums`, để «11» không hẹp hơn «03») — chỉ là `.khoi-so` chưa được
   * hưởng nó. Nay có cả hai: chữ số đẳng chiều rộng, và một cái sàn.
   *
   * Sàn SUY từ ba token dựng nên nó — cỡ chữ, hai lần đệm, hai lần viền — chứ
   * không gõ `29px`: đổi cỡ chữ thì nó vẫn vuông vắn, gõ con số thì không.
   * Cùng cách `.the-xoa` chặn bề rộng ở đợt 6g.
   *
   * Chuỗi tới hai chữ số vẫn rộng hơn sàn một chút (đo 30,2 so với 29,0) — đó
   * là đúng: `tabular-nums` giữ MỖI CHỮ SỐ bằng nhau, không ép hai chữ số bằng
   * một. Ép nữa thì mọi chuỗi 1–9, tức gần hết app, phải đội một huy hiệu rộng
   * thừa cho một ca hiếm. */
  min-width: calc(var(--cs-nho) + var(--k-2) * 2 + 2px);
  text-align: center; font-variant-numeric: tabular-nums;
}
.khoi.nhe .khoi-so { background: var(--canh); color: var(--nen) }
.khoi-ten { font-size: var(--cs-chinh); font-weight: 600; line-height: var(--dong-chat) }
/* Nhãn vai — «giữ đúng người» · «chỉ lấy cái nhìn». Nó nói KHỐI NÀY LÀM GÌ VỚI
 * ẢNH BẠN ĐƯA VÀO, và đó là câu mà bảng vai bên trong đang phải đoán. In hoa
 * cỡ nhỏ giãn rộng: đọc ra một nhãn phân loại, không ra một câu. */
/* `0 1 auto` chứ không `0 0 auto`: nhãn dài hơn chỗ còn lại thì phải XUỐNG
 * DÒNG TRONG CHÍNH NÓ, không thò ra khỏi thẻ. Đo ở 420px (đợt 2t): một nhãn
 * 50 ký tự rộng 446px trong cột 356px, thò 119px, và không cách nào đọc nốt.
 * Nhãn ngắn — mọi nhãn hôm nay — không đổi một điểm ảnh nào: chúng đã hẹp hơn
 * chỗ trống nên `flex-shrink` không có gì để co. Cùng chữa với lỗi
 * `.doi > :last-child` của đợt 2b, và cùng lý do: một phần tử không co được
 * nằm trong lưới co được thì lưới thua. */
.khoi-vai {
  margin-left: auto; flex: 0 1 auto; min-width: 0;
  font-size: var(--cs-nho); font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--thuong);
}
.khoi.nhe .khoi-vai { color: var(--canh) }
/* Ghi chú lồng trong khối — câu giải thích một hành vi không đoán được. Nền
 * nhạt + vạch 2px, để nó đọc ra "lời của app" chứ không phải một dòng nữa của
 * dữ liệu. */
.khoi-ghi {
  margin: var(--k-3) 0 0; padding: var(--k-2) var(--k-3);
  background: var(--chon); border-left: 2px solid var(--thuong);
  border-radius: 0 var(--bo-nho) var(--bo-nho) 0;
  font-size: var(--cs-phu); line-height: var(--dong-chay);
}
/* Câu dặn KHÔNG có nền — nó chỉ là chú thích, không phải một tầng riêng. */
.khoi-dan { margin: var(--k-2) 0 0; font-size: var(--cs-phu); line-height: var(--dong-chay); color: var(--chu-nhat) }

/* Thẻ cột phải mang tin ĐÁNG ĐỌC TRƯỚC KHI BẤM (prompt sẽ gửi đi, kết quả vừa
 * về). Viền nhấn thay viền trung tính — cùng cách Nocturne dùng nhấn: một
 * đường kẻ, không phải một mảng tô.
 *
 * ── ĐỔI TÊN TỪ `.tam.nhan` — 28/08/2026 ──────────────────────
 * Tên cũ đụng `.nhan`, lớp HUY HIỆU ở dưới tệp này (cỡ chữ 10px, viền, đệm
 * 1px 5px, và `white-space: nowrap`). Dòng cũ chỉ đè `border-color`, nên bốn
 * thuộc tính kia vẫn rơi xuống — một thẻ khai `class="tam nhan"` nhận nguyên
 * `nowrap`, và mọi đoạn chữ trong nó thành MỘT DÒNG dài tràn khỏi cột.
 *
 * Đo được lúc dựng cột phải của Media nguồn: một đoạn 444px rộng thành 762px.
 * Lỗi này nằm im từ đợt 2a vì lớp ấy khai ra mà chưa tab nào dùng — đúng kiểu
 * bẫy chờ người đầu tiên đọc CSS rồi tin vào cái tên. */
.tam.diem { border-color: var(--vien-chon) }
/* `.tam-dau` ĐÃ GỠ ở đợt 7 — nó là một hàng flex bọc quanh `<h3>` chỉ để đẩy một
   `<span>` sang phải, và `.tam h3` nay tự làm được điều đó (`display: flex` cộng
   `> .nhat { margin-left: auto }`). Năm chỗ dùng nó đã phẳng hoá: cái `<span>`
   vào thẳng trong `<h3>`.

   Ghi lại vì sao nó từng cần: `<h3>` là block, nên trước đợt 7 không có cách nào
   cho một nhãn phụ đứng cuối dòng tiêu đề — phải bọc thêm một tầng. `.kf-buoc`
   thì có, và đó chính là khả năng lượt gộp phải giữ lại. Một cái bọc sinh ra để
   bù cho một hình thiếu thì nó chết đúng lúc cái hình ấy đủ. */

/* ── DÒNG HAI VẾ — bố cục Nocturne đợt 2b, 28/08/2026 ─────────
 *
 * «nhãn ……… giá trị», ngăn nhau bằng một kẻ ngang mảnh. Bộ bàn giao dùng đúng
 * hình này ở SÁU chỗ thuộc bốn tab khác nhau — «Kho nguồn đang chiếm», «4 tệp
 * cần bạn đặt tên», «Các bản đã lưu», tám hạng mục của Cẩm nang — nên nó về
 * đây thay vì mọc ra sáu bản sao, đúng bài học của bộ `.khoi` đợt 2a.
 *
 * Kẻ ở MÉP TRÊN, không mép dưới: dòng cuối của một danh sách nằm ngay trên câu
 * dặn hoặc mép thẻ, và một kẻ ngang ở đó đọc ra một dòng rỗng thứ hai.
 *
 * `align-items: baseline` chứ không `center` — hai vế thường lệch cỡ chữ, và
 * căn giữa theo hộp thì chân chữ so le nhau. */

/* ── HÀNG NGANG DÙNG CHUNG — dời từ `man.css` về 01/09/2026 ───
 *
 * Việc ② (luật T2). `.hang` dùng ở **15 tab** và là lớp bố cục dùng nhiều thứ
 * hai của app sau `.tam` — nó khai ở `man.css` chỉ vì nó ra đời trong một màn.
 *
 * `min-width: 0` là dòng quan trọng, và nó đi theo: mặc định một ô flex KHÔNG
 * co nhỏ hơn nội dung của nó, nên `<input style="flex:1">` cạnh một nút sẽ đẩy
 * nút ra ngoài mép màn hình thay vì tự hẹp lại. Chủ dự án gặp đúng thế ở Kho
 * nguồn 17/08/2026: nút «Nạp từ địa chỉ» bị cắt mất một nửa.
 *
 * `.hang-nut` KHÔNG đi theo — nó dùng ở 2 tab, và chú thích của nó ở `man.css`
 * kể một cái bẫy của riêng chỗ ấy. Dời một lớp vì nó DÙNG CHUNG, không dời vì
 * nó trông giống lớp bên cạnh. */
.hang { display: flex; gap: var(--k-2); align-items: center; flex-wrap: wrap }
.hang > input, .hang > textarea, .hang > select { min-width: 0 }

/* ── Ô MÀU VÀ Ô LOGO CỦA BỘ NHẬN DIỆN — dời về 01/09/2026 ────
 *
 * ── VÌ SAO ĐỨNG NGAY SAU `.hang`, KHÔNG Ở TRÊN ──────────────
 * `.hang-mau` và `.hang-logo` là BIẾN THỂ của `.hang`: markup viết
 * `class="hang hang-mau"`, và cả hai lớp cùng hạng riêng (0,1,0). Chỉ thứ tự
 * nguồn quyết cái nào được dùng.
 *
 * Lượt dời đầu của đợt ⑤ đặt chúng ở TRÊN `.hang`, và phép so trước/sau bắt
 * ngay: khe của hàng ô màu rơi 16px → 8px, hàng ô logo 24px → 8px, ở cả sáu
 * khổ. Trước đó chúng ở `man.css` nên luôn đứng sau; dời lên `nen.css` là mất
 * chỗ đứng ấy nếu không đặt lại cho đúng.
 *
 * Cùng cái bẫy `.canh-nhau` ⟷ `.kn-hinh` của đợt ②, chỉ lần này nó nằm TRONG
 * một tệp thay vì bắc qua hai. Lần thứ tư của cùng một họ lỗi.
 *
 * Việc Ⓔ. Ấn phẩm **tiêu thụ** bộ nhận diện mà Chuẩn thương hiệu chốt — đó là
 * quan hệ đúng, nên hai họ này là từ vựng chung chứ không phải kiểu của một tab.
 *
 * `.kn-logo-toi` thành `.logo-o.toi` — biến thể là lớp GHÉP, không phải hậu tố.
 *
 * ── MỘT VA TÊN ĐÃ TRÁNH ĐƯỢC ────────────────────────────────
 * `.kn-mau-o` KHÔNG đổi thành `.o-mau`: lớp ấy **đã tồn tại** — chấm màu 12px ở
 * Hậu kỳ — và nó khác hẳn ô `input[type=color]` 28×22 kèm nhãn ở đây. Đặt trùng
 * là dựng lại đúng tai nạn `.bd-vai` ⟷ `.khoi-vai`. Nên đảo thứ tự: `.mau-o`. */
.hang-mau { margin-top: var(--k-2); gap: var(--k-4) }
.mau-o { display: inline-flex; align-items: center; gap: var(--k-1); font-size: var(--cs-phu) }
.mau-o input[type=color] { width: 28px; height: 22px; padding: 1px; cursor: default }

.hang-logo { gap: var(--k-5); margin: var(--k-2) 0 }
.logo-o {
  display: inline-flex; flex-direction: column; align-items: center; gap: var(--k-1);
  padding: var(--k-2) var(--k-4);
  background: var(--sau); border-radius: var(--bo);
}
.logo-o img { max-width: 140px; max-height: 56px; object-fit: contain }

/* Bản logo cho nền tối PHẢI đứng trên nền tối. Một logo trắng trên giấy trắng
 * là một ô trống, và người xem sẽ báo là logo bị mất. Hai màu lấy từ token —
 * `man.css` không được giữ mã màu nào, và lưới `test-mau` đếm. */
.logo-o.toi { background: var(--nen-logo-toi) }
.logo-o.toi .nhat { color: var(--chu-tren-mau) }

.doi {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--k-3); padding: var(--k-2) 0; border-top: 1px solid var(--vien);
  font-size: var(--cs-ban);
}
/* Vế TRÁI phải co được, và phải bẻ được giữa từ.
 *
 * Đo được 28/08 trên trang mẫu: vế trái của khối «tệp cần đặt tên» là một TÊN
 * TỆP — `zalo-nhan-2026-08-27-hoi-cho-vietbuild.jpg` — và tên tệp không có dấu
 * cách nào để xuống dòng. Mặc định một ô flex không co nhỏ hơn nội dung của nó,
 * nên cả dòng đẩy ra ngoài mép thẻ 460px và cột phải tràn ngang.
 *
 * Cùng họ với `min-width: 0` của `.hang` ở `man.css`, chỉ khác thủ phạm: ở đó
 * là một `<input>`, ở đây là một chuỗi không chỗ bẻ. Nên cần cả hai dòng —
 * `min-width` cho phép co, `overflow-wrap` cho phép bẻ. */
.doi > :first-child { min-width: 0; overflow-wrap: anywhere }
/* Vế phải là thứ để TRA, không phải để đọc thành câu: chữ nhạt và số cùng bề
   rộng, để mắt dóng được theo cột dọc mà không cần một cái bảng.
 *
 * `:not(:only-child)` là bắt buộc, không phải cho gọn. Một dòng chỉ có MỘT vế
 * (bảng so hai tầng xoá, danh sách bốn việc) thì vế ấy vừa là `:first-child`
 * vừa là `:last-child` — và `flex: 0 0 auto` ở đây sẽ ĐÈ luôn `min-width: 0` ở
 * trên, nên dòng thôi co được. Đo được ở bề rộng điện thoại 28/08: ba thẻ tràn
 * 25–40px, trong khi ở màn rộng không gì báo. */
.doi > :last-child:not(:only-child) {
  flex: 0 0 auto; color: var(--chu-nhat); font-variant-numeric: tabular-nums;
}
/* Vế phải là một đường đi thì nó thôi là chữ nhạt — một liên kết mờ hơn nhãn
   bên cạnh nó là một liên kết không ai thấy. */
.doi > a:last-child, .doi > button:last-child { color: var(--lien-ket) }

/* ── «ĐI TIẾP» ────────────────────────────────────────────────
 *
 * Ba đường đi ra khỏi tab, xếp dọc thành hộp bấm được cả ô. Bộ bàn giao đặt
 * khối này ở cuối cột phải của Media nguồn và Ấn phẩm, và nó trả lời câu «xong
 * ở đây rồi thì đi đâu» — câu mà thanh bên trả lời được, nhưng chỉ khi người
 * dùng đã biết mình cần tab nào.
 *
 * `min-height` chứ không `height`: tên tab dài gặp cột hẹp thì xuống hai dòng,
 * và một chiều cao cứng sẽ cắt mất dòng thứ hai. */
.di-tiep { display: flex; flex-direction: column; gap: var(--k-2) }
.di-tiep > a {
  display: flex; align-items: center; justify-content: space-between; gap: var(--k-2);
  min-height: 42px; padding: var(--k-2) var(--k-3);
  border: 1px solid var(--vien-nhan); border-radius: var(--bo-nho);
  font-size: var(--cs-ban); color: var(--chu); text-decoration: none;
}
.di-tiep > a:hover { border-color: var(--vien-chon); background: var(--chon) }
/* Mũi tên là dấu, không phải chữ — nó không được đọc to bằng tên tab cạnh nó. */
.di-tiep > a::after { content: '→'; color: var(--chu-nhat) }

.chan {
  border-left: 4px solid var(--vien-dung); background: var(--nen-chan);
  padding: var(--k-3); white-space: pre-wrap;
}

/* ── KHI NỘI DUNG LÀ THẺ, KHÔNG PHẢI CHỮ THÔ — 23/08/2026 ─────
 *
 * `pre-wrap` ở trên có lý do: `.chan` ra đời để in NGUYÊN VĂN câu từ chối của
 * máy chủ, và câu ấy mang `\n` thật cần giữ.
 *
 * Nhưng cùng class đó nhận cả HTML dựng bằng template literal nhiều dòng — và
 * lúc ấy `pre-wrap` giữ luôn **thụt lề của mã nguồn**: một câu bị bẻ đôi giữa
 * chừng rồi dòng sau thụt vào sáu dấu cách, đúng như nó nằm trong tệp `.js`.
 * Đã thấy thật ở dải cảnh báo «ảnh mẫu đã đổi» của tab Chuẩn thương hiệu.
 *
 * Nên khối nào chở THẺ thì khai thêm class này. Không đổi mặc định: 62 chỗ
 * đang dùng `.chan` cho chữ thô, và chúng đúng. */
.chan.dung-the { white-space: normal }

/* ── HAI HẠNG CẢNH BÁO, HAI MÀU — 28/08/2026 ─────────────────
 *
 * `.chan` mặc định là hổ phách: «để mắt tới». Đúng cho phần lớn — một trần sắp
 * chạm, một lượt sẽ tốn thêm.
 *
 * Nhưng có loại khác: thứ SẼ HỎNG nếu bấm tiếp mà không sửa. Lời thoại dài hơn
 * độ dài cảnh thì phần thừa bị CẮT MẤT lúc ghép — tiền đã trả cho giọng đọc, và
 * người dùng chỉ biết khi nghe bản nháp. Một hộp hổ phách nói câu ấy đứng lẫn
 * giữa những hộp hổ phách nói «để mắt tới», nên nó đọc ra cùng một hạng.
 *
 * Đỏ không phải để to tiếng: nó là hạng thứ hai, và app đã có sẵn cặp màu ấy ở
 * băng dừng khẩn. Dùng lại, không bịa màu mới. */
.chan.hong { border-color: var(--vien-dung); background: var(--nen-dung) }
.chan.hong b { color: var(--xau) }

/* `min(300px, 100%)` chứ không `300px` trần: `minmax(300px, …)` là một sàn
   CỨNG, nên trong một hộp hẹp hơn 300px thì lưới không co mà THÒ RA. Đo 28/08
   ở tab Hậu kỳ: lưới «Đang xuất 3 tỉ lệ» nằm trong cột đọc-thôi 320px, tức
   thân thẻ 290px, và mỗi ô rộng 300px — tràn 10px, không có đường cuộn nào.
   `.luoi` dùng ở tám tab, nên sàn phải biết co ở mọi chỗ chứ không riêng đây. */
.luoi { display: grid; gap: var(--k-4); grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)) }

/* ── Phòng thử — LƯỚI KẾT QUẢ TRONG CỘT PHẢI, 28/08/2026 ─────
   Đợt A dời lưới kết quả từ giữa trang vào cột phải, và sàn 300px của `.luoi`
   không sống nổi ở đó. Đo ở khổ thật (1536×864, thanh bên mở): cột phải được
   530px — `min(560px, 46%)` của một `#khu` 1208px — mà hai ô 300px cộng khe
   14px cần 614px. Lưới tụt về MỘT cột, bốn bản xếp dọc, và tab mất đúng phép
   so mà cả nó sinh ra để làm: «cùng một đầu vào, nhiều model, ĐẶT CẠNH NHAU».

   Sàn 250px cho hai ô 258px vừa khít. Con số ấy không bịa: bàn giọng của chính
   tab này đã chốt 260px là bề rộng đủ cho một thẻ kết quả kèm hàng nút, và ở
   đây thẻ chở đúng bộ nút ấy. Tấm nhỏ là để SO; bấm vào mở bản đầy đủ. */
#ar-ra .luoi { grid-template-columns: repeat(auto-fill, minmax(min(250px, 100%), 1fr)) }

/* Phòng thử — khuôn NGHE. Cột hẹp hơn `.luoi` vì một đoạn tiếng không cần bề
   rộng của một tấm ảnh, và mọi nút chạy phải nằm trong MỘT tầm mắt: tai không
   có bộ nhớ như mắt, nên cuộn trang để tìm nút phát là mất luôn phép so. */
.ar-tieng { display: grid; gap: var(--k-3); grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)) }
.ar-tieng audio { width: 100%; display: block; margin: var(--k-3) var(--k-3) 0; max-width: calc(100% - 22px) }

/* ── Phòng thử — DÂY THỬ, đợt C 28/08/2026 ───────────────────
 *
 * Năm mắt trên một hàng, và `auto-fit` chứ không `repeat(5, 1fr)`: ở cột nội
 * dung 1160px thì 5 × 210px cộng bốn khe vừa khít, còn ở bề rộng điện thoại
 * năm dải 60px là năm dải không đọc được. Sàn 210px cho nó tự về 4 → 3 → 2 → 1.
 *
 * Mắt là `<button>` chứ không `<div>`: bấm vào là ĐỔI BÀN, và một thứ bấm được
 * mà không phải nút thì bàn phím không tới được. Nên phải khai lại font, canh
 * chữ và con trỏ — mặc định của `<button>` căn giữa và thu nhỏ chữ. */
.ar-day { margin-bottom: var(--k-5) }
.ar-day-tren { display: flex; align-items: baseline; gap: var(--k-4); flex-wrap: wrap; margin-bottom: var(--k-3) }
.ar-day-tren h3 { margin: 0; font-size: var(--cs-de); font-weight: 600; color: var(--chu-nhat) }
.ar-day-tren p { margin: 0; flex: 1; min-width: 260px }
.ar-day-luoi { display: grid; gap: var(--k-3); grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)) }

.ar-mat {
  display: block; width: 100%; text-align: left; cursor: pointer;
  font: inherit; color: var(--chu);
  background: var(--tam); border: 1px solid var(--vien); border-radius: var(--bo);
  padding: var(--k-3) var(--k-3);
}
.ar-mat:hover { border-color: var(--vien-sang) }
/* Mắt đang mở: viền nhấn, KHÔNG tô nền đặc. Tô đặc thì năm thẻ thành một dãy
   trong đó một thẻ hét lên, và mắt thôi đọc được ba dòng chữ bên trong nó. */
.ar-mat.chon { border-color: var(--vien-chon); box-shadow: inset 0 0 0 1px var(--vien-chon) }
/* Mắt chưa mở vẫn bấm được — nó dẫn tới đúng câu lý do, dài hơn, ở khối đầu
   vào. Mờ đi thì đủ nói «chỗ này chưa chạy được»; khoá hẳn thì người dùng
   không có đường nào đọc vì sao. */
.ar-mat.dong { background: none; border-style: dashed; color: var(--chu-nhat) }

.ar-day-dau { display: flex; align-items: baseline; gap: var(--k-2); flex-wrap: wrap; margin-bottom: var(--k-1) }
/* ── HỆ SỐ THỨ TƯ, GỘP VỀ .khoi-so — 02/09/2026 ────────────────
 * Sáu mắt của Dây thử là chuỗi phụ thuộc nhau có trạng thái mở/đóng — đúng
 * tầng BƯỚC như Kịch bản phim, Ghép ảnh thật. Nhưng `phong-thu-day.js` dựng
 * trước khi hai tầng BƯỚC/MỤC được đặt tên (28/08), nên số của nó đi một lối
 * riêng: `.ar-day-so` chỉ là chữ mờ `--chu-mo`, không huy hiệu — hệ số THỨ TƯ,
 * tách khỏi cả `data-so` lẫn `.khoi-so`, và không giống bất kỳ tab nào khác.
 *
 * Markup nay mang CẢ HAI lớp — `class="ar-day-so khoi-so"` — nên `.khoi-so` lo
 * hình (huy hiệu, bề rộng, `tabular-nums`), còn `.ar-day-so` chỉ còn giữ MỘT
 * việc: dịu màu khi mắt CHƯA MỞ. Toàn thẻ `.ar-mat.dong` đã mờ theo
 * (`color: var(--chu-nhat)`, viền đứt) — huy hiệu tô đặc `--thuong` không đổi
 * theo sẽ hét lên giữa một thẻ đang cố ý im, đúng cách `.khoi.nhe .khoi-so` đã
 * đổi màu cho khối «nhẹ»: nền huy hiệu phải đổi theo NGỮ CẢNH, không phải theo
 * việc nó đứng ở khối nào. */
.ar-mat.dong .ar-day-so { background: var(--vien); color: var(--chu-nhat) }
.ar-day-dau b { font-size: var(--cs-ban); font-weight: 600 }
.ar-day-luot { font-size: var(--cs-phu); line-height: var(--dong-chay) }
.ar-day-can {
  font-size: var(--cs-nho); color: var(--chu-mo); margin-top: var(--k-1);
  padding-top: var(--k-1); border-top: 1px dotted var(--vien);
}

/* Phòng thử — khối CHẤM ĐIỂM. Gập lại theo mặc định: bốn tiêu chí × năm nút là
   hai chục ô cho MỖI tấm, và lưới bốn tấm thành tám chục ô — người dùng thôi
   nhìn ảnh. Nên nó chỉ mở khi có người bấm. */
.ar-cham { border-top: 1px solid var(--vien); margin-top: var(--k-2); padding-top: var(--k-2) }
.ar-cham-hang { display: flex; gap: var(--k-2); align-items: center;
  justify-content: space-between; margin-bottom: var(--k-2); flex-wrap: wrap }
.ar-cham-ten { flex: 1; min-width: 130px; font-size: var(--cs-phu); line-height: var(--dong-chat) }
.ar-cham .mieng { padding: var(--k-1) var(--k-2); min-width: 30px }

/* ── LƯỚI CHỌN MEDIA — dời về 01/09/2026 (đợt ⑫, luật Đ4) ────
 *
 * Ô 84px chở một ảnh HOẶC một video, kèm nhãn dưới và số `@image1` trên góc.
 * Dùng ở 5 tab — nhiều nhất trong cả bảng mượn của luật Đ4 — nên nó là từ vựng
 * dùng chung, không phải tài sản của tab nào. Trước đó tên `.kf-anh-hang` ·
 * `.kf-o` · `.kf-so` ở `man.css`.
 *
 * Tên `.luoi-chon-media` nói vai: đây là lưới ĐỂ CHỌN, không phải lưới để xem.
 * Hai thứ ấy khác nhau ở chỗ ô nào cũng bấm được và có trạng thái `.chon`.
 *
 * ── LUẬT ẢNH PHẢI GỌI TÊN CẢ `video` ────────────────────────
 * Bản trước chỉ gọi `img`. `veMedia()` vẽ `<video>` cho mọi media `video/*`,
 * và một `<video>` không ai đặt bề rộng thì lấy bề rộng THẬT của tệp; ô flex
 * mặc định `min-width: auto` nên không co xuống dưới nội dung. Một clip 1920px
 * kéo cái nút 84px thành 1920px và cả hàng tràn ngang qua bảng bên cạnh.
 *
 * Lỗi ấy KHÔNG kêu ở đâu: không lỗi console, không phép thử đỏ, và ô ảnh ngay
 * cạnh vẫn đúng. Nó chỉ lộ ra khi Media nguồn của khách có một clip. Hai luật
 * HẸP mượn khuôn này (`.bc-pc-hang` · `.ng-soat`, cả hai ở `man.css`) cũng phải
 * gọi tên cả hai thẻ. */
.luoi-chon-media { display: flex; flex-wrap: wrap; gap: var(--k-2) }
.media-o {
  position: relative; width: 84px; padding: 0;
  display: flex; flex-direction: column; gap: var(--k-0); text-align: center;
}
.media-o img, .media-o video {
  width: 100%; height: auto; display: block;
  border-radius: var(--bo); border: 2px solid transparent;
}
.media-o.chon img, .media-o.chon video { border-color: var(--vien-chon) }
/* Số thứ tự in ĐÈ lên ảnh đã chọn — `@image1` là thứ prompt gọi tới, nên nó
   phải đọc được ngay trên tấm ảnh chứ không nằm dưới nhãn. */
.media-so {
  position: absolute; top: 3px; left: 3px; z-index: var(--lop-trong-the);
  background: var(--thuong); color: var(--chu-tren-mau);
  font-size: var(--cs-nho); font-weight: 600; padding: 1px var(--k-1); border-radius: var(--bo);
}

.the { border: 1px solid var(--vien); border-radius: var(--bo-lon); overflow: hidden; background: var(--tam); position: relative }
.the img, .the video { width: 100%; display: block; background: var(--nen-anh) }
.the .mo-ta { padding: var(--k-2) var(--k-3); font-size: var(--cs-phu) }
.the .mo-ta b { color: var(--chu) }

/* Ô đang bị ẩn tay: vẫn đọc được, nhưng nhìn là biết nó không nằm trong lưới
   mặc định — người dùng chỉ thấy nó khi đã tự bật «lượt đã ẩn tay». */
.the-mo { color: var(--chu-nhat) }
/* `.the .mo-ta b` tự đặt `--chu` nên nó KHÔNG thừa kế; phải nói riêng, không
   thì một nửa dòng mô tả sáng nguyên trong một thẻ đang mờ. */
.the-mo .mo-ta b { color: var(--chu-nhat) }
.the-mo img, .the-mo video { opacity: .55 }

/* KHÔNG CÓ TỆP THÌ KHÔNG VẼ THẺ ẢNH. `<img src="">` cho ra ô vỡ kèm chữ `alt`,
   và một lưới đầy ô vỡ làm cả thư viện trông như đang hỏng — 25/08/2026. */
.the-trong {
  aspect-ratio: 4 / 3; display: flex; align-items: center; justify-content: center;
  background: var(--sau); color: var(--chu-nhat); font-size: var(--cs-phu);
}

/* Nút xoá ở góc ô. Chìm cho tới khi trỏ vào ô: lưới này để DUYỆT BẰNG MẮT, và
   một dấu × đậm trên mỗi ô làm hỏng đúng việc ấy. */
/* ── NÚT BIỂU TƯỢNG: TRẦN CHIỀU CAO KHÔNG ÁP ─────────────────
 *
 * `button` nền khai `min-height: var(--cao-dk)` (40px) để mọi ĐIỀU KHIỂN đứng
 * cùng một hàng cao bằng nhau. Nút biểu tượng thì không đứng hàng nào: nó là
 * một dấu × 20px nằm đè lên góc một tấm ảnh. Trần ấy kéo nó cao 40px trong khi
 * bề ngang vẫn 20 — thành một viên nén dọc, và chủ dự án chấm đúng chỗ ấy
 * 29/08: «nhìn mất thẩm mỹ».
 *
 * Đây là lần THỨ TƯ cùng một lỗi (`.tv-nhan` hôm qua, rồi ba nút × này), nên
 * khai một lần cho cả họ thay vì vá lẻ từng lớp. Ba lớp dưới đây đều tự khai
 * kích thước của mình — và một lớp đã tự khai kích thước thì trần của người
 * khác chỉ phá nó.
 *
 * `test-web` gác chiều ngược: lớp nút nào tự khai `height`/`width` mà quên
 * `min-height` sẽ bị bắt, thay vì đợi ai đó nhìn thấy.
 *
 * Đệm BỐN CHIỀU BẰNG NHAU — chốt của chủ dự án. Một dấu × là một hình vuông;
 * đệm ngang rộng hơn đệm dọc làm nó ra hình chữ nhật, và mắt đọc hình chữ nhật
 * ấy thành một cái nhãn chứ không thành một cái nút bỏ. */
.nut-bo { min-height: auto }

/* ── MỘT NÚT BỎ, KHÔNG BA — gộp 01/09/2026 ───────────────────
 *
 * Việc Ⓕ của `../../docs/kien-truc/chuan-cau-truc-giao-dien.md`. Trước đó app có
 * BA cái tên cho cùng một vật: `.the-xoa` (× trên một thẻ ảnh), `.kpc-go` (× trên
 * ô ảnh Chuẩn thương hiệu), `.bd-bo` (× trên ô ảnh Ghép ảnh thật).
 *
 * Chính dòng ngay trên đã tự tố cáo điều đó: nó phải liệt kê cả ba để cho chúng
 * chung một luật `min-height: auto`. **Một luật phải gọi tên ba lớp là một luật
 * đang nói rằng ba lớp ấy là một.**
 *
 * Chúng khác nhau đúng một thứ — CHỖ ĐỨNG và cỡ — mà chỗ đứng là việc của khối
 * CHỨA, không của cái nút. Nên hình ở đây, vị trí ở `man.css` theo từng chủ:
 * `.the .nut-bo` · `.kpc-o .nut-bo` · `.bd-anh .nut-bo`. Đúng khuôn `.tam h3`
 * của đợt 7: hình ở tầng nền, chỗ đứng ở tầng màn.
 *
 * Ẩn-hiện theo hover thì KHÔNG gộp: chỉ `.the` giấu nút cho tới lúc trỏ vào, hai
 * chủ kia luôn hiện. Đó là hành vi của chủ, nên nó ở lại với chủ. */
.nut-bo {
  position: absolute; z-index: var(--lop-trong-the);
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--vien-nut); border-radius: var(--bo);
  background: var(--tam); color: var(--chu-nhat);
  font-size: var(--cs-nho); line-height: 1; padding: var(--k-1); cursor: pointer;
  /* Đệm đều bốn chiều vẫn chưa cho ra hình vuông: dấu × hẹp hơn chiều cao một
     dòng chữ, nên hộp ra 18×21.
     `aspect-ratio: 1` KHÔNG chữa được — nó chỉ lái một chiều khi chiều kia đã
     xác định, mà ở đây cả hai đều co theo nội dung. Đã thử và đo: vẫn 18×21.

     Bản trước chặn bề rộng bằng chính chiều cao, SUY từ ba token dựng nên nó
     (`--cs-nho` + hai đệm + hai viền = 21px). Phép suy ấy đúng về HÌNH nhưng
     sai về NGƯỠNG: nó cho ra bất cứ con số nào ba token kia cộng lại, và 21
     nằm dưới vùng chạm nhỏ nhất mà WCAG 2.5.8 đòi. Một cái nút vuông vẫn là
     một cái nút bấm trượt.

     Nên nay cả hai chiều neo vào `--cham` — vuông vì hai chiều cùng một con
     số, và đủ lớn vì con số ấy là ngưỡng chứ không phải tổng của ba token. */
  min-width: var(--cham); min-height: var(--cham);
}
.nut-bo:hover { color: var(--xau); border-color: var(--xau) }

/* Chỗ đứng theo từng chủ. `.the` còn giấu nút cho tới lúc trỏ vào — một dải thẻ
   ảnh mà mỗi thẻ đeo sẵn một dấu × thì đọc ra một danh sách đang chờ bị xoá. */
.the .nut-bo { top: 6px; right: 6px; opacity: 0 }
.the:hover .nut-bo, .the .nut-bo:focus-visible, .the-mo .nut-bo { opacity: 1 }

/* SỬA HỘ 30/08/2026 — sổ vướng #24. Đợt chuẩn hoá token viết lại luật này và
 * đánh rơi hết thân của nó, chỉ còn `font-weight`. Hệ quả: 28 tệp trong
 * `web/man` dùng `class="nhan"` mất viền, mất đệm, mất cỡ chữ nhỏ, mất
 * `nowrap` — huy hiệu thôi trông ra huy hiệu; và hai biến thể ngay dưới đặt
 * `border-color` lên một phần tử KHÔNG CÒN VIỀN, tức chúng không làm gì cả.
 * Không lưới nào bắt được: `test-mau` chỉ soi màu, `test-kieu` soi cỡ chữ và
 * khoảng cách theo thang chứ không soi việc một lớp MẤT thuộc tính.
 * Trả lại thân theo HEAD, GIỮ `font-weight` mà đợt ấy thêm vào. */
.nhan {
  font-size: var(--cs-nho); border: 1px solid var(--vien); border-radius: var(--bo-nho);
  padding: var(--k-0) var(--k-1); margin-right: var(--k-1);
  white-space: nowrap; font-weight: 600;
}
.nhan.re  { border-color: var(--vien-re); color: var(--tot) }
.nhan.dat { border-color: var(--vien-chan); color: var(--canh) }

/* ── Bảng ───────────────────────────────────────────────── */

table { border-collapse: collapse; width: 100% }
th, td { text-align: left; padding: var(--k-2) var(--k-2); border-bottom: 1px solid var(--vien-bang); vertical-align: top }
/* Đầu bảng dính dưới DẢI TIÊU ĐỀ, không dưới một con số gõ tay. Bản trước ghi
 * `top: 34px` — chiều cao dải trạng thái CŨ, thứ đợt 1 đã bỏ hẳn — nên từ
 * 27/08 mọi đầu bảng trong app dính vào một dải cao 68px và nằm khuất hẳn
 * dưới nó. Vẫn «dính», chỉ là dính vào chỗ không ai nhìn thấy. */
/* KHÔNG viết hoa cả cụm: sổ tay bản đầu định lấy nguyên lối
 * `text-transform: uppercase` của Nocturne, nhưng Nocturne đặt cho chữ Anh.
 * Tiếng Việt viết hoa hết thì dấu chồng (Ả · Ộ · Ữ) đội lên khỏi dòng và va vào
 * dòng trên — chỗ này hệ ngoại nhập không giúp được.
 *
 * ── HAI LẦN ĐỔI CỠ, VÀ VÌ SAO LẦN NÀY KHÁC ───────────────────
 * Trước 29/08 có `thead th { font-size:16px; font-weight:bold }` ĐÈ lên quy tắc
 * này. Đợt 1 gỡ nó vì nó làm nhãn bảng TO HƠN CẢ TIÊU ĐỀ THẺ (`--cs-de` = 16) —
 * một đầu bảng to bằng tên của cái thẻ chứa nó thì thứ bậc đảo ngược.
 *
 * Rồi bậc nhỏ nhất (`--cs-nho` = 11) cộng mực mờ (`--chu-nhat`) lại đi quá xa
 * về phía kia: đầu bảng NHẠT HƠN chính những dòng nó gán nhãn, nên mắt quét
 * bảng không có chỗ bám. Chủ dự án chấm 29/08.
 *
 * `--cs-chinh` (14) là bậc ở GIỮA hai lần trượt ấy. Bản đầu của lượt sửa này
 * chọn `--cs-ban` (13) và đo ra một chỗ hỏng nữa: nhãn bảng vẫn NHỎ HƠN chính
 * dòng dưới nó (`td` thừa hưởng 14 của `body`), tức vẫn chưa nổi lên. Bằng cỡ
 * thân bài là ngưỡng thấp nhất còn nói được «đây là đầu bảng».
 *
 * Trên nữa thì đụng `--cs-de` (16) của tiêu đề thẻ — đúng chỗ đợt 1 đã trượt.
 * Nên 14 là bậc duy nhất còn lại, và nó đủ: cùng cỡ với thân nhưng NÉT 600 (vẽ
 * bằng Bold) và mực `--chu` đầy, trong khi thân là nét 400.
 *
 * Mực về `--chu` đầy — đầu bảng phải đậm hơn thân, không nhạt hơn.
 *
 * Nét giữ 600, KHÔNG lên 700: kho font chỉ có mặt 400 và 700, nên 600 đã được
 * vẽ bằng Bold rồi (đo 29/08, mục 04 sổ tay). Khai 700 ở đây là hứa thêm một
 * bậc đậm mà font không giữ được — sức nổi thật đến từ CỠ và MỰC. */
th { color: var(--chu); font-weight: 600; font-size: var(--cs-chinh);
     position: sticky; top: var(--cao-dau-man); background: var(--nen) }

/* ── ĐẦU BẢNG THÔI DÍNH KHI BẢNG NẰM TRONG KHUNG CUỘN — 28/08/2026 ──
 *
 * `overflow-x: auto` biến khối bọc thành một KHUNG CUỘN, và CSS ép `overflow-y`
 * theo về `auto` luôn — không có cách nào cuộn một trục mà để trục kia `visible`.
 * Từ lúc ấy `position: sticky` của `th` neo vào chính khung bọc chứ không vào
 * trang, nên `top: var(--cao-dau-man)` — 68px, chiều cao dải tiêu đề — đẩy hàng
 * tiêu đề xuống 68px BÊN TRONG hộp và nó nằm đè lên hàng đầu tiên.
 *
 * Đo được 28/08 trong app: đè đúng **37px** — che mất nửa trên tấm ảnh nền và
 * dòng mô tả của cảnh 1.
 *
 * Chú thích của `.bang-cuon` bên dưới đã cảnh báo đúng điều này, nhưng cho ca
 * điện thoại; khi đợt 2a đưa bảng vào một CỘT và 14h mở cuộn ngang ở MỌI bề
 * rộng, cảnh báo ấy thành đúng ở khắp nơi mà không ai đọc lại.
 *
 * Dính không cứu được ở đây: hộp không cuộn dọc, nên chẳng có gì để dính theo.
 * Trả nó về dòng chảy bình thường là hết đè, và không mất gì. */
.bang-cuon th { position: static }
td.so, th.so { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap }
tr.thu-hoi { color: var(--chu-nhat); text-decoration: line-through }
tr.thu-hoi img { opacity: .45 }
img.nho { width: 54px; height: 54px; object-fit: cover; border-radius: var(--bo-nho); background: var(--nen-anh) }

/* Bảng nhiều cột (ảnh + chữ dài + nút) không co vừa điện thoại — khai class
 * này ở khối BỌC bảng để nó cuộn ngang thay vì đẩy tràn cả trang. Không áp
 * mặc định cho mọi `table`: đa số bảng trong app chỉ vài cột ngắn, tự co vừa
 * bằng cách xuống dòng trong ô — bọc thêm cho chúng là thêm một khung cuộn
 * không ai cần.
 *
 * ĐÁNH ĐỔI ĐÃ CHỐT: `th{position:sticky;top:34px}` ở trên hết dính bên
 * trong khung này — dính-khi-cuộn-dọc cần đúng khung cuộn của TRANG, không
 * phải khung cuộn ngang riêng của bảng. Tràn cả trang tệ hơn mất dính tiêu
 * đề, nên chỉ áp ở bề rộng điện thoại — desktop đủ chỗ, không đánh đổi gì. */
/* ── VÀ TRONG MỘT CỘT THÌ DESKTOP CŨNG KHÔNG ĐỦ CHỖ — 28/08/2026 ──
 *
 * Câu «desktop đủ chỗ, không đánh đổi gì» ở trên đúng khi bảng nằm trên CẢ bề
 * ngang trang. Đợt 2a đưa bảng phân cảnh vào cột trái của `.hai-cot`, và tiền
 * đề ấy hết đúng ngay hôm đó — chỉ không ai đo lại.
 *
 * Đo trong app thật 28/08, cửa sổ 1280 với thanh bên đã gập: cột trái 602px,
 * bảng đòi 619px, `overflow-x` là `visible` — nên ô cuối của mỗi hàng, tức
 * đúng cái nút «sửa», thò ra **17px** ngoài mép thẻ và không có thanh cuộn nào
 * để với tới. Hỏng im lặng: bảng vẫn đọc được, chỉ cột hành động là mất.
 *
 * Đánh đổi `th` dính vẫn y như luật dưới đây đã chốt, và ở đây nó còn rẻ hơn:
 * bảng trong một cột hẹp thì phần lớn màn hình đã là chỗ khác rồi. */
/* ── VÀ CUỐI CÙNG: VÔ ĐIỀU KIỆN — 01/09/2026 khuya ───────────
 *
 * Hai luật ở trên cho `overflow-x: auto` đúng HAI ca: bảng trong một cột của
 * `.hai-cot`, và cửa sổ dưới 640px. Ở mọi ca khác `overflow-x` là `visible`.
 *
 * ĐO ĐƯỢC, KHÔNG SUY: khối «Khách hàng» của khu Quản trị là một bảng chín cột
 * ở TẦNG ĐẦU của `#khu` — không trong `.hai-cot`. Cửa sổ 1500px (nên `@media`
 * không bắt), cột nội dung bị bó còn 460px: bảng đòi **696px**, `overflow-x` là
 * `visible`, và ba cột cuối thò ra ngoài mép thẻ **không có thanh cuộn nào để
 * với tới**.
 *
 * Đây là lần thứ BA cùng một lớp lỗi trong đúng khối chú thích này, và hai lần
 * trước đã ghi chính xác nguyên nhân mà vẫn chữa theo CA: `@media` đo **CỬA
 * SỔ**, còn thứ bó cái bảng là **CỘT NỘI DUNG** — và cột ấy hẹp đi vì thanh bên
 * 272px, vì `.hai-cot`, vì bất cứ khối bọc nào ngày mai. Sửa theo ca thì mỗi ca
 * mới là một lượt hỏng im lặng nữa. `.hai-cot` là ca thứ hai; đây là ca thứ ba.
 *
 * VÀ ĐÁNH ĐỔI ĐÃ TRẢ RỒI: `.bang-cuon th { position: static }` ở trên **không
 * có điều kiện**. Nghĩa là hôm nay `.bang-cuon` ở tầng đầu, cửa sổ trên 640px,
 * đang mất đầu bảng dính MÀ KHÔNG được cuộn — trả giá mà không nhận hàng. Gỡ
 * điều kiện là nhận nốt phần hàng đã trả tiền.
 *
 * Không có ngưỡng nào phải đoán, vì câu hỏi chỉ có một câu trả lời đúng: một
 * bảng rộng hơn khối bọc nó thì phải CUỘN. Bảng nào vừa thì `auto` không dựng
 * thanh cuộn nào — nó vô hại ở đúng những chỗ nó không cần. */
.bang-cuon, .hai-cot .bang-cuon { overflow-x: auto }

@media (max-width: 640px) {
  /* Cột ghi chú/lỗi tự do dài (Lịch sử tạo media) ép cả hàng cao vọt khi bị
   * bó hẹp cùng những cột số ngắn bên cạnh — một hàng 250px cao xen giữa
   * những hàng 70px đọc như dữ liệu hỏng. `nowrap` giữ mỗi hàng một dòng;
   * cuộn ngang (đã có từ `.bang-cuon`) là cách đọc nốt phần chữ dài đó. */
  .cot-ghi-chu { white-space: nowrap }
}

/* ── Bảng liệt kê "đã có" → thẻ danh sách trên điện thoại ────
 *
 * Khác `.bang-cuon`: bảng ở đây có MỘT cột chở tên dài (tiêu đề kịch bản,
 * tên phim), còn lại là số liệu ngắn. Cuộn ngang ép cột tên hẹp tới mức chữ
 * rớt xuống một từ một dòng — "Nhật ký một mình mở Xưởng bánh" thành bảy
 * dòng, cả hàng cao gấp năm hàng thường. Xấu hơn cả tràn trang.
 *
 * Ở điện thoại xếp mỗi hàng thành MỘT THẺ: tên đứng riêng dòng đầu (cỡ chữ
 * to hơn, giống một tiêu đề), số liệu còn lại gộp thành một dòng phụ nhỏ bên
 * dưới, ngăn nhau bằng dấu «·» — cùng ngôn ngữ với `.lua-chon` ở Tổng quan.
 * Không dựng markup mới: vẫn NGUYÊN bảng `<table>`, chỉ đổi `display` bằng
 * CSS (`table-row` → `block`), nên desktop không đổi gì. */
@media (max-width: 640px) {
  .bang-the table, .bang-the tbody, .bang-the tr { display: block; width: auto }
  .bang-the thead { display: none }
  .bang-the tr { border-bottom: 1px solid var(--vien); padding: var(--k-3) 0 }
  .bang-the tr:first-child { border-top: 1px solid var(--vien) }
  .bang-the td { display: inline; padding: 0; border: 0 }
  .bang-the td:first-child {
    display: block; margin-bottom: var(--k-1); font-size: var(--cs-chinh); color: var(--chu);
    text-align: left;
  }
  /* `<button>` mặc định `text-align: center` của trình duyệt — một dòng
   * không lộ ra, nhưng tiêu đề dài xuống hai dòng thì dòng hai bị đẩy vào
   * giữa thay vì thẳng lề trái với dòng một. Cùng lỗi `.ng-the-ten` đã sửa
   * cho Media nguồn (man.css), lặp lại ở đây vì `.lienket` gốc không set
   * `text-align`. */
  .bang-the td:first-child button { text-align: left }
  .bang-the td:not(:first-child) {
    font-size: var(--cs-phu); color: var(--chu-nhat); white-space: nowrap;
  }
  /* Dấu «·» đứng TRƯỚC mọi ô số liệu trừ ô đầu tiên sau tên — `+` chỉ khớp
   * ô liền sau một ô KHÔNG phải ô đầu, nên đúng dòng thứ hai không có dấu
   * mở đầu thừa. */
  .bang-the td:not(:first-child) + td::before { content: ' · '; color: var(--chu-nhat) }
}

/* ── Hộp thoại của app (loi/hop-thoai.js) ────────────────────
 *
 * Thay cho `prompt()`/`confirm()` của trình duyệt. Lý do đầy đủ ở đầu tệp
 * `loi/hop-thoai.js`; ở đây chỉ nói phần nhìn.
 *
 * `<dialog>` mặc định có viền, đệm và `max-width` riêng của trình duyệt — phải
 * đặt lại cả ba, không thì hộp mang mặt của trình duyệt chứ không của app. */
dialog.ht {
  border: 1px solid var(--vien); border-radius: var(--bo-lon);
  background: var(--tam); color: var(--chu);
  padding: var(--k-4); margin: auto;
  width: min(520px, calc(100vw - 32px));
  max-height: calc(100vh - 64px); overflow-y: auto;
  font: 14px/1.55 var(--tieu-de);
}
dialog.ht::backdrop { background: var(--nen-phu) }

/* Hộp tự vẽ (`hopTuy`) rộng hơn ba hộp kia: nó chở một LƯỚI ẢNH, và 520px là
   hai cột — đủ để thấy có ảnh, không đủ để chọn giữa chúng. Bề rộng đi qua biến
   để chỗ gọi đặt được, nhưng vẫn kẹp trong bề ngang cửa sổ. */
dialog.ht.ht-rong {
  --ht-rong: 760px;                 /* chỗ gọi đặt đè bằng style thuộc tính */
  width: min(var(--ht-rong), calc(100vw - 32px));
}

.ht-dau { display: flex; gap: var(--k-3); align-items: flex-start; margin-bottom: var(--k-3) }
.ht-dau h3 { margin: 0; font-size: var(--cs-de); font-family: var(--tieu-de) }
.ht-dau p { margin: var(--k-1) 0 0 }
.ht-dau-chu { min-width: 0; flex: 1 }
/* Ảnh nhỏ trong đầu hộp: gỡ nhầm và đổi vai nhầm đều xảy ra vì người ta đọc
 * tên chứ không nhìn ảnh — nên hộp nào nói về một tấm ảnh thì bày tấm ấy ra. */
.ht-anh {
  width: 64px; height: 64px; object-fit: cover; flex: 0 0 64px;
  border-radius: var(--bo); background: var(--nen-anh);
}

/* Câu hệ quả in NGUYÊN VĂN, kể cả xuống dòng: câu từ chối của máy chủ mang con
 * số ("còn 3 cảnh đang dùng"), và bẻ dòng lại là làm mất nhịp của nó. */
.ht-than {
  white-space: pre-wrap; margin: 0 0 var(--k-4); line-height: var(--dong-chay);
  background: var(--sau); border: 1px solid var(--vien);
  border-radius: var(--bo); padding: var(--k-3);
}

.ht-chon { display: flex; flex-direction: column; gap: var(--k-2); margin-bottom: var(--k-4) }
.ht-nhom {
  margin: var(--k-2) 0 var(--k-0); font-size: var(--cs-nho); color: var(--chu-nhat);
  font-style: italic; letter-spacing: .02em;
}
.ht-nhom:first-child { margin-top: 0 }
.ht-muc {
  /* Nút kiểu HÀNG: nội dung dồn về trái, không nhận `center` của quy tắc nền. */
  justify-content: flex-start;
  display: flex; gap: var(--k-3); align-items: center; text-align: left; width: 100%;
  border: 1px solid var(--vien-nut); border-radius: var(--bo);
  background: var(--tam); color: var(--chu); padding: var(--k-2) var(--k-3);
  cursor: pointer; font: inherit; font-size: var(--cs-ban);
}
.ht-muc:hover:not(:disabled) { background: var(--nen-luot); border-color: var(--vien-chon) }
.ht-muc img { width: 38px; height: 38px; object-fit: cover; border-radius: var(--bo-nho); background: var(--nen-anh) }
.ht-muc > span { min-width: 0 }

.ht-form { display: flex; flex-wrap: wrap; gap: var(--k-3); margin-bottom: var(--k-3) }
.ht-o { display: flex; flex-direction: column; gap: var(--k-1); flex: 1 1 100%; font-size: var(--cs-phu) }
.ht-o-hang { flex: 1 1 45% }
.ht-o input, .ht-o textarea { width: 100% }
.ht-loi:empty { display: none }
.ht-loi { margin: 0 0 var(--k-3) }

/* Nút đứng bên PHẢI, nút thuận ngoài cùng — cùng thứ tự với thanh hành động
 * của bảng sửa ảnh, để tay không phải học lại chỗ bấm ở mỗi hộp. */
.ht-nut { display: flex; gap: var(--k-2); justify-content: flex-end; align-items: center; flex-wrap: wrap }
.ht-nut .chinh.ht-nguy { background: var(--vien-dung); border-color: var(--vien-dung) }


/* ── Xem ảnh phóng to (loi/xem-anh.js) ────────────────────────
 *
 * Cùng cái vỏ `<dialog class="ht">` ở trên, nhưng bốn thứ phải khai lại vì hộp
 * này chở một TẤM ẢNH chứ không chở một phiếu:
 *
 *   · Không có mặt bàn. Nền và viền của `.ht` bỏ đi, để phần trống quanh tấm
 *     ảnh là nền mờ chứ không phải một tấm bảng rỗng — một tấm ảnh dọc đứng
 *     giữa một tấm bảng 1600px đọc ra «hộp bị lỗi», không đọc ra «ảnh đang mở».
 *   · Cao gần hết màn hình, và KHÔNG tự cuộn: khung ảnh bên trong cuộn.
 *   · Xếp dọc hai tầng — dải chữ và nút ở trên, phần còn lại là ảnh.
 *   · Dải chữ tự mang nền, vì nó không còn mặt bàn nào đỡ nữa.
 */
dialog.ht.xa-hop {
  background: none; border: 0; overflow: hidden;
  max-height: calc(100vh - 24px);
  padding: var(--k-3);
}
/* ── `display` KHAI RIÊNG, VÀ CHỈ KHI HỘP ĐANG MỞ ────────────
 *
 * Trình duyệt giấu một `<dialog>` chưa mở bằng đúng một luật:
 * `dialog:not([open]) { display: none }`. Bộ chọn `dialog.ht.xa-hop` dài hơn
 * luật ấy, nên khai `display: flex` ở khối trên là dựng một hộp KHÔNG BAO GIỜ
 * TẮT — đóng rồi mà vẫn nằm nguyên trên màn hình, mất cả nền mờ lẫn bẫy tiêu
 * điểm, và không một dòng lỗi nào.
 *
 * Soi 29/08 trên trình duyệt thật: hộp đã `close()` vẫn đo ra 1366×461. Nó
 * KHÔNG lộ ra khi dùng bình thường, vì `hopTuy()` gỡ hộp khỏi cây ngay trong
 * sự kiện `close` — tức là luật hỏng này đang được một dòng ở tệp KHÁC che cho.
 * Ba hộp còn lại của app không dính, vì chúng không đụng tới `display` bao giờ.
 */
dialog.ht.xa-hop[open] { display: flex; flex-direction: column; gap: var(--k-3) }
.xa-dau {
  display: flex; gap: var(--k-3); align-items: flex-start; justify-content: space-between;
  flex-wrap: wrap;
  background: var(--tam); border: 1px solid var(--vien); border-radius: var(--bo);
  padding: var(--k-2) var(--k-3);
}
.xa-dau h3 { margin: 0; font-size: var(--cs-de); font-family: var(--tieu-de) }
.xa-dau p { margin: var(--k-1) 0 0 }
.xa-chu { min-width: 0 }
.xa-nut { display: flex; gap: var(--k-3); align-items: center; flex-wrap: wrap }

/* Khung ảnh: ăn hết chỗ còn lại, canh giữa, và là chỗ DUY NHẤT cuộn được.
 *
 * `min-height: 0` bắt buộc — một ô flex mặc định không co nhỏ hơn nội dung của
 * nó, nên thiếu dòng này thì tấm ảnh đẩy cả hộp dài quá màn hình thay vì tự
 * cuộn. Cùng cái bẫy `.hang > input` đã ghi ở `man.css`, ở trục dọc.
 *
 * `safe` trong hai dòng canh giữa cũng bắt buộc, và nó chỉ lộ ra ở lối xem thứ
 * hai: một tấm ảnh to hơn khung mà canh giữa thì phần thò ra ở mép TRÊN và mép
 * TRÁI không cuộn tới được — mất hẳn, không phải khuất tạm. `safe` bảo trình
 * duyệt bỏ canh giữa đúng lúc ấy và dồn về đầu. */
.xa-o {
  flex: 1; min-height: 0; overflow: auto;
  display: grid; align-content: safe center; justify-content: safe center;
}
.xa-anh, .xa-cat {
  display: block; max-width: 100%; max-height: 78vh; width: auto; height: auto;
  border-radius: var(--bo); background: var(--nen-anh); cursor: zoom-in;
}
/* Khung cắt — cùng ngữ nghĩa `cover` mà máy chủ dùng, nên khung này LÀ khung
 * sẽ ra (lý lẽ đầy đủ ở `man/hau-ky-ban-xem.js`). Hai biến do `loi/xem-anh.js`
 * đặt lúc mở hộp; bỏ trống thì về khung vuông cắt giữa. */
.xa-cat { aspect-ratio: var(--xa-ti-le, 1); object-fit: cover; object-position: var(--xa-vi-tri, 50% 50%) }

/* Lối xem thứ hai. Với ảnh thường là ĐÚNG CỠ TỆP — bỏ mọi cái trần, khung
 * ngoài cuộn. Với khung cắt là CẢ TẤM CHƯA CẮT — bỏ tỉ lệ ép và thôi cắt, để
 * thấy đúng phần mà khung kia đang bỏ đi. */
.xa-o.mo-rong .xa-anh { max-width: none; max-height: none }
.xa-o.mo-rong .xa-cat { aspect-ratio: auto; object-fit: contain }
.xa-o.mo-rong img { cursor: zoom-out }

/* Con trỏ là thứ DUY NHẤT báo trước rằng bấm được — một tấm ảnh không có viền
 * nút nào, và một cái nút không ai biết là nút thì bằng không có. */
[data-hd="phong-to"] { cursor: zoom-in }
