:root{
  --bg:#F3F3F1; --surf:#FFFFFF; --surf2:#FAFAF9;
  --ink:#17191B; --ink2:#5C6266; --ink3:#93999D; --line:#E3E3DF; --line2:#D2D3CE;
  --go:#146450; --go-bg:#E4F0EB; --go-line:#B6D6C9;
  --due:#A8500E; --due-bg:#FAEDE0; --due-line:#E8CBAA;
  /* Màu NHẬN DIỆN riêng của zone Lễ tốt nghiệp (19/9). Ngô Phú Cường xin cho
     zone này "khác với giao diện hàng ngày một chút".

     Chàm chứ không phải một sắc xanh lá hay cam đậm hơn, và lý do là ngữ
     nghĩa chứ không phải thẩm mỹ: trong sản phẩm này --go (xanh) có ĐÚNG một
     nghĩa (xong / người thu đã nhận) và --due (cam) có đúng một nghĩa (còn
     phải làm gì đó). Mượn một trong hai làm màu nhận diện là làm hỏng cả hai.
     Chàm là hướng duy nhất còn trống, và nó tách xa cả hai trên trục XANH
     LAM — trục mà người mù màu đỏ-lục vẫn phân biệt được, khác hẳn cặp
     xanh-lá ↔ cam vốn đã sát nhau (ΔE 7.7 protan, xem khối biểu đồ bên dưới).

     LUẬT: --le CHỈ dùng cho khung sườn (băng đầu trang, huy hiệu bước, viền
     khối đang mở). TUYỆT ĐỐI không dùng nó để nói một trạng thái — trạng thái
     vẫn chỉ có ba màu cũ: xanh xong, cam còn nợ, xám chưa tới. */
  --le:#403A72; --le2:#57508F; --le-bg:#EEEDF6; --le-line:#CBC8E4;
  --r:14px; --r2:10px; --wrap:520px;
  --sh:0 1px 2px rgba(23,25,27,.05), 0 4px 14px rgba(23,25,27,.045);
  --sh2:0 2px 6px rgba(23,25,27,.07), 0 12px 32px rgba(23,25,27,.1);
  --num:'Space Grotesk',ui-monospace,monospace;
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font-family:'Be Vietnam Pro',system-ui,sans-serif;
  font-size:15px;line-height:1.5;-webkit-font-smoothing:antialiased;
  padding-bottom:calc(84px + env(safe-area-inset-bottom))}
body.noapp{padding-bottom:0}

/* ─── CẢM GIÁC ỨNG DỤNG, KHÔNG PHẢI TRANG WEB ───────────────────────────────
   Bốn thứ dưới đây là bốn dấu hiệu "đây là trình duyệt" mà người dùng nhận ra
   ngay cả khi không gọi tên được. Sửa 5/9 sau khi Ngô Phú Cường chụp màn hình
   đầu trang chồng lên đồng hồ hệ thống.

   1. CHẠM HAI LẦN KHÔNG PHÓNG NỮA. `touch-action:manipulation` bỏ cử chỉ
      chạm-hai-lần-để-phóng, và bỏ luôn 300ms trình duyệt phải CHỜ xem có cú
      chạm thứ hai không — nút bấm nhạy hẳn lên, đây là thứ cảm nhận được rõ
      nhất. Chạy trên CẢ iOS, khác `user-scalable=no` chỉ chạy trên Android.
   2. KHÔNG KÉO CĂNG MÉP MÀN HÌNH. `overscroll-behavior-y:none` bỏ cú nảy cao
      su ở đầu/cuối trang và bỏ "kéo xuống để tải lại" của Chrome Android —
      thứ mà kéo nhầm một cái là mất nguyên trạng thái màn đang mở.
   3. CHẠM GIỮ KHÔNG BÔI ĐEN CHỮ. Bôi đen nhãn nút rồi hiện bảng "Sao chép /
      Tra cứu" là dấu hiệu trang web rõ nhất. CHỈ tắt ở phần khung sườn
      (đầu trang, thanh dưới, nút) — nội dung như số điện thoại, email, ghi
      chú tư liệu PHẢI copy được, nên tuyệt đối không đặt lên `body`.
   4. KHÔNG TRƯỢT NGANG. Một phần tử lỡ rộng quá là cả trang lắc ngang được,
      cảm giác long lẻo. */
html{touch-action:manipulation;overscroll-behavior-y:none}
body{overscroll-behavior-y:none;overflow-x:hidden}
header,nav,button,label.f,.eb,.raillb,.grab,.seg{
  -webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
.av,.nb .g{-webkit-user-drag:none;user-drag:none}
.wrap{max-width:var(--wrap);margin:0 auto;padding:0 16px}
button{font:inherit;color:inherit;cursor:pointer;border:0;background:none;padding:0}
.num{font-family:var(--num);font-variant-numeric:tabular-nums;letter-spacing:-.01em}
:focus-visible{outline:2px solid var(--go);outline-offset:2px}
::selection{background:var(--go-bg)}

/* ─── ĐẦU TRANG ─── */
/* `padding-top:env(safe-area-inset-top)` là thứ CHỪA CHỖ cho đồng hồ, cột sóng
   và pin của hệ điều hành. Thiếu đúng một dòng này thì khi cài lên màn hình
   chính, đầu trang vẽ đè lên thanh trạng thái — Ngô Phú Cường chụp được ngày
   5/9: "Nhóm 6" nằm chồng lên "16:57", "21 ngày đến bảo vệ" chồng lên cột sóng.
   Lề DƯỚI đã chừa từ đầu (thanh điều hướng, sheet, toast đều có
   safe-area-inset-bottom) — chỉ riêng lề TRÊN bị bỏ quên.
   Đặt trên `header` chứ không phải `body`: header dán dính (sticky) nên phần
   đệm này phải đi theo nó, để lúc cuộn thì nội dung trôi qua BÊN DƯỚI vùng mờ
   chứ không lòi lên trên thanh trạng thái. */
header{position:sticky;top:0;z-index:40;background:rgba(243,243,241,.88);
  backdrop-filter:saturate(1.6) blur(14px);border-bottom:1px solid var(--line);
  padding-top:env(safe-area-inset-top)}
.hd{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:13px 0 11px}
.hl{display:flex;align-items:baseline;gap:9px;min-width:0}
.hl .g{font-family:var(--num);font-weight:700;font-size:17px;letter-spacing:-.02em}
.hl .s{font-size:11.5px;color:var(--ink3);white-space:nowrap}
.cd{display:flex;align-items:baseline;gap:4px;flex:0 0 auto}
.cd .n{font-family:var(--num);font-weight:700;font-size:19px;letter-spacing:-.03em;color:var(--due)}
.cd .u{font-size:10.5px;color:var(--ink3)}
.av{width:34px;height:34px;border-radius:50%;flex:0 0 auto;display:grid;place-items:center;
  font-family:var(--num);font-weight:600;font-size:12px;color:#fff;letter-spacing:.02em;
  box-shadow:inset 0 -1px 2px rgba(0,0,0,.12)}
.avbtn{transition:transform .16s}
.avbtn:active{transform:scale(.93)}

/* thanh 8 đoạn */
.rail{display:flex;gap:3px;padding:0 0 4px}
.seg{flex:1;height:6px;border-radius:99px;background:var(--line2);overflow:hidden;position:relative;
  transition:height .18s}
.seg i{position:absolute;inset:0 auto 0 0;background:var(--go);border-radius:99px;
  transition:width .7s cubic-bezier(.22,1,.36,1)}
.seg.zero{background:var(--due-line)}
.seg:active{height:9px}
.raillb{display:flex;justify-content:space-between;padding:0 0 12px;font-size:10.5px;color:var(--ink3)}
.raillb b{font-family:var(--num);color:var(--ink);font-weight:600}

/* ─── KHUNG ─── */
main{padding-top:18px}
.view{display:none}
.view.on{display:block;animation:in .34s cubic-bezier(.22,1,.36,1)}
@keyframes in{from{opacity:0;transform:translateY(9px)}to{opacity:1;transform:none}}
.eb{display:flex;align-items:center;gap:9px;margin:0 0 11px;font-size:11px;font-weight:600;
  letter-spacing:.09em;text-transform:uppercase;color:var(--ink3)}
.eb .c{font-family:var(--num);background:var(--line);color:var(--ink);padding:1px 6px;
  border-radius:99px;font-size:10.5px;letter-spacing:0}
.eb::after{content:'';flex:1;height:1px;background:var(--line)}
.sect{margin-bottom:30px}
.card{background:var(--surf);border-radius:var(--r);box-shadow:var(--sh);margin-bottom:10px;overflow:hidden}
.cb{padding:15px 16px}
.mut{color:var(--ink2);font-size:13.5px;line-height:1.5}
.foot{font-size:11.5px;color:var(--ink3);line-height:1.65;padding:14px 2px 36px}

/* ─── VIỆC CỦA BẠN ─── */
.hero{background:var(--surf);border-radius:18px;box-shadow:var(--sh2);padding:18px;margin-bottom:22px;
  position:relative;overflow:hidden}
.hero::before{content:'';position:absolute;inset:0 0 auto 0;height:3px;
  background:linear-gradient(90deg,var(--due),var(--go))}
.hero .lb{font-size:11px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--ink3);margin-bottom:9px}
.hero h2{font-size:19px;font-weight:700;line-height:1.32;letter-spacing:-.015em;margin:0 0 7px}
.hero p{margin:0 0 15px;color:var(--ink2);font-size:14px;line-height:1.5}
.cta{display:inline-flex;align-items:center;gap:8px;background:var(--ink);color:#fff;
  padding:11px 18px;border-radius:99px;font-size:14px;font-weight:600;transition:transform .16s,opacity .16s}
.cta:active{transform:scale(.97)}
.cta.done{background:var(--go-bg);color:var(--go);box-shadow:inset 0 0 0 1px var(--go-line)}

/* ─── DÒNG HOẠT ĐỘNG ─── */
.fd{display:flex;gap:12px;padding:11px 0;border-bottom:1px solid var(--line)}
.fd:last-child{border-bottom:0}
.fd .av{width:28px;height:28px;font-size:10.5px}
.fd .x{flex:1;font-size:13.5px;color:var(--ink2);line-height:1.45;padding-top:3px}
.fd .x b{color:var(--ink);font-weight:600}
.fd .t{font-family:var(--num);font-size:11px;color:var(--ink3);flex:0 0 auto;padding-top:5px}

/* ─── CƠ CẤU ─── */
.of{display:flex;align-items:center;gap:13px;padding:13px 16px;border-bottom:1px solid var(--line)}
.of:last-child{border-bottom:0}
.of .rl{font-size:10.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink3);flex:0 0 74px}
.of .nm{font-weight:600;font-size:15px}
.of .co{font-size:12.5px;color:var(--ink2);margin-top:1px;line-height:1.35}
.of .src{font-size:11px;color:var(--ink3);margin-top:4px}
.of.void .nm{color:var(--due);font-weight:500}
.ico{width:26px;height:26px;border-radius:8px;display:grid;place-items:center;flex:0 0 auto;
  background:var(--line);color:var(--ink2);font-size:14px;transition:.16s}
.ico:hover{background:var(--ink);color:#fff}

/* ─── THÀNH VIÊN ─── */
.mrow{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--line);width:100%;text-align:left}
.mrow:last-of-type{border-bottom:0}
.mrow .b{flex:1;min-width:0}
.mrow .nm{font-weight:600;font-size:14.5px;display:flex;align-items:center;gap:6px}
.mrow .co{font-size:12.5px;color:var(--ink2);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ring{width:26px;height:26px;flex:0 0 auto;border-radius:50%;display:grid;place-items:center;
  font-family:var(--num);font-size:9.5px;font-weight:600}
.ring.full{background:var(--go-bg);color:var(--go)}
.ring.part{background:var(--due-bg);color:var(--due)}
.pan{background:var(--surf2);border-top:1px solid var(--line);padding:0 16px;
  max-height:0;overflow:hidden;transition:max-height .38s cubic-bezier(.22,1,.36,1)}
.pan.on{max-height:620px;padding:15px 16px}
.fi{margin-bottom:12px}
.fi .k{font-size:10.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink3);margin-bottom:3px}
.fi .v{font-size:14px;line-height:1.45}
.fi .v.blank{color:var(--ink3);font-style:italic}
.fi .was{font-size:11.5px;color:var(--ink3);margin-top:2px}
.fi .was s{opacity:.7}
.tagrow{display:flex;gap:6px;flex-wrap:wrap;margin-top:2px}
.tg{font-size:11.5px;padding:3px 9px;border-radius:99px;background:var(--line);color:var(--ink2);white-space:nowrap}
.tg.go{background:var(--go-bg);color:var(--go)}
.tg.due{background:var(--due-bg);color:var(--due)}

/* ─── KHO ─── */
.fl{display:flex;gap:7px;overflow-x:auto;padding-bottom:14px;scrollbar-width:none;
  margin:0 -16px;padding-left:16px;padding-right:16px}
.fl::-webkit-scrollbar{display:none}
/* Trong bảng trượt lên thì hàng chip KHÔNG cuộn ngang được: lề âm -16px của
   .fl bị mép bảng cắt mất, chip cuối cùng lòi ra ngoài và không ai kéo tới
   được. Cho xuống dòng thay vì cuộn — bốn con số phải nhìn thấy cùng lúc thì
   người thu mới biết còn bao nhiêu người chưa khai. */
.fl.cuon{flex-wrap:wrap;overflow:visible;margin:0;padding-left:0;padding-right:0;row-gap:7px}
/* Và chip trong hàng XUỐNG DÒNG được phép tự xuống dòng BÊN TRONG nó.
   .fc có white-space:nowrap, đúng cho hàng CUỘN NGANG — ở đó chip dài thì kéo
   tới được. Hàng .cuon thì không cuộn, nên nowrap chỉ đẩy chữ ra ngoài mép
   màn hình: nhãn lĩnh vực KHKD dài nhất ("Nông nghiệp - Lâm nghiệp - Thuỷ sản
   (trồng trọt & chăn nuôi)") đo được 416px ở khổ 390px, tức TRÀN NGANG cả
   trang và kéo theo mọi thứ khác lệch đi 42px.
   Không phép kiểm chuỗi nào thấy, và ngay cả phép đo tràn ngang cũng không
   thấy nếu nó đo lúc khối chứa hàng chip đang GẬP — đúng chỗ đã sót. */
.fl.cuon .fc{white-space:normal;max-width:100%;text-align:left;line-height:1.35}

/* ─── Biểu đồ tiến độ thu ───
   Cột chồng ba trạng thái. Ba điều KHÔNG phải trang trí:
   1. Vân chéo trên ô cam. Đo bằng trình kiểm màu: xanh --go và cam --due cách
      nhau ΔE 7,7 với người mù màu đỏ-lục ở nền sáng — nằm trong dải sàn 6–8,
      chỉ hợp lệ khi có mã hoá thứ hai. Vân chéo là mã hoá ấy; bỏ nó đi là hai
      ô nhìn như một với một phần người đọc.
   2. Khe 2px giữa các ô, để mắt tách được ranh giới mà không cần màu.
   3. Số in thẳng dưới mỗi cột. Ô xám tương phản 1,47 với nền trắng, dưới 3:1
      nên bắt buộc phải có nhãn chữ đi kèm. */
.tk{display:grid;gap:14px}
.tk-r{display:grid;gap:6px}
.tk-nh{display:flex;align-items:baseline;gap:8px;font-size:13.5px;font-weight:600}
.tk-nh .p{margin-left:auto;font-family:var(--num);font-size:12.5px;color:var(--ink3);font-weight:600}
.tk-t{display:flex;gap:2px;height:14px;border-radius:7px;overflow:hidden;background:var(--surf);
  box-shadow:inset 0 0 0 1px var(--line)}
.tk-t i{display:block;min-width:0}
.tk-a{background:var(--go)}
.tk-b{background:var(--due);
  background-image:repeating-linear-gradient(45deg,transparent 0 3px,rgba(255,255,255,.5) 3px 6px)}
.tk-c{background:var(--line2)}
.tk-s{font-size:11.5px;color:var(--ink2);line-height:1.5}
.tk-s b{font-family:var(--num);color:var(--ink)}
.tk-con{padding-left:14px;border-left:2px solid var(--line);display:grid;gap:11px;margin-top:2px}
/* Chú giải: ô màu + CHỮ, không bao giờ chỉ có màu. */
.tk-cg{display:flex;flex-wrap:wrap;gap:12px;font-size:11.5px;color:var(--ink2);margin-bottom:4px}
.tk-cg span{display:inline-flex;align-items:center;gap:6px}
.tk-cg i{width:11px;height:11px;border-radius:3px;display:block;flex:0 0 auto}
.fc{font-size:12.5px;white-space:nowrap;border-radius:99px;padding:7px 14px;background:var(--surf);
  color:var(--ink2);box-shadow:var(--sh);transition:.16s}
.fc.on{background:var(--ink);color:#fff}
.rs{display:flex;gap:12px;padding:13px 16px;border-bottom:1px solid var(--line);align-items:center;
  text-decoration:none;color:inherit;width:100%;text-align:left}
.rs:last-child{border-bottom:0}
/* Nút ✎ nằm cạnh dòng tư liệu, KHÔNG nằm trong thẻ <a> — nhét button vào
   trong <a> là HTML sai và bấm vào nút sẽ mở luôn đường dẫn. */
.rsw{display:flex;align-items:stretch;border-bottom:1px solid var(--line)}
.rsw:last-child{border-bottom:0}
.rsw .rs{flex:1;min-width:0;border-bottom:0}
.rsw .ico{flex:0 0 auto;display:flex;align-items:center;padding:0 14px;color:var(--ink3)}
.rsw .ico:hover{color:var(--ink)}
.ext{font-family:var(--num);font-size:9px;font-weight:600;letter-spacing:.06em;padding:5px 6px;
  border-radius:6px;background:var(--line);color:var(--ink2);flex:0 0 auto}
.rs .b{flex:1;min-width:0}
.rs .t{font-weight:600;font-size:14px;line-height:1.35}
.rs .m{font-size:11.5px;color:var(--ink3);margin-top:3px}

/* ─── PHẦN BÀI ─── */
.pt{display:flex;gap:13px;padding:14px 16px;border-bottom:1px solid var(--line);width:100%;text-align:left;
  transition:background .16s}
.pt:last-child{border-bottom:0}
.pt:active{background:var(--surf2)}
.pnum{font-family:var(--num);font-weight:700;font-size:13px;color:var(--ink3);flex:0 0 20px;padding-top:2px}
.pt.zero .pnum{color:var(--due)}
.pbd{flex:1;min-width:0}
.pbd h4{margin:0 0 4px;font-size:15px;font-weight:600;line-height:1.3;letter-spacing:-.005em}
.pbd .rq{font-size:12.5px;color:var(--ink2);line-height:1.45;margin-bottom:8px}
.own{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--ink2)}
.own .av{width:20px;height:20px;font-size:8.5px}
.hint{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--go);
  background:var(--go-bg);padding:4px 10px;border-radius:99px;box-shadow:inset 0 0 0 1px var(--go-line)}
.pbar{height:4px;background:var(--line);border-radius:99px;margin-top:9px;overflow:hidden}
.pbar i{display:block;height:100%;background:var(--go);border-radius:99px;
  transition:width .7s cubic-bezier(.22,1,.36,1)}
.pt.zero .pbar i{background:var(--due)}
.pct{font-family:var(--num);font-size:12px;font-weight:600;color:var(--ink3);flex:0 0 auto;padding-top:2px}
/* Xong thì nói bằng hình chứ đừng bắt đọc con số: "100%" và "80%" trông na ná
   nhau khi lướt, còn dấu ✓ trên nền xanh thì nhận ra ngay không cần đọc.
   Dùng đúng cặp màu --go/--go-bg của vòng tròn hồ sơ đã điền đủ, để "xong" ở
   chỗ nào trong ứng dụng cũng là một màu ấy. */
.pct.xong{font-size:13px;color:var(--go);background:var(--go-bg);
  border-radius:999px;width:22px;height:22px;display:grid;place-items:center;padding:0}
.eb .c.xong{background:var(--go-bg);color:var(--go)}

/* Buổi HÔM NAY tách thành khối riêng ở đầu tab Hôm nay.
   Viền xanh chứ không chỉ đổi nền — nền nhạt lướt qua là không thấy, đúng bài
   học đã ghi trong public/lich/lich.css. Cùng cặp màu --go/--go-bg với mọi chỗ
   khác nói "xong / đang là", để "hôm nay" ở màn nào cũng một màu. */
.eb .c.nay{background:var(--go-bg);color:var(--go);text-transform:none;letter-spacing:.01em}
.card.naycard{box-shadow:0 0 0 1.5px var(--go),var(--sh)}
.fd.buoinay .x b{color:var(--go)}

/* Đầu mục buổi học trong tab Tư liệu — cấp dưới của .eb, nên nhỏ và nhạt hơn,
   và cần lề trên vì nó đứng sau một .card chứ không phải đầu mục lớn. */
.eb.ebbuoi{margin-top:18px;font-size:10.5px;color:var(--ink3);text-transform:none;letter-spacing:.02em}
.eb.ebbuoi:first-of-type{margin-top:0}

/* Danh bạ lớp.
   Nhãn Ban cán sự lớp dùng cặp màu --go giống mọi chỗ khác nói "có vai, đang
   giữ việc". KHÔNG dùng --due: cam trong sản phẩm này luôn nghĩa là "còn phải
   làm gì đó", mà giữ chức thì không phải việc còn nợ. N7: chuỗi hiển thị viết
   đủ "Ban cán sự lớp", không viết tắt — nhãn lấy thẳng từ máy chủ.

   Thông tin đã che in bằng phông số cho các dấu * thẳng hàng, và nhạt hơn để
   nhìn là biết ngay đây không phải số bấm gọi được. */
.tg.bcs{background:var(--go-bg);color:var(--go);font-size:10px;padding:1px 7px}
.v .che{font-family:var(--num);color:var(--ink3);letter-spacing:.02em}
#dbTim{margin-top:2px}

/* Trong danh bạ lớp, dòng chức vụ/đơn vị được xuống HAI dòng thay vì cắt cụt.
   Ở thẻ Nhóm thì cắt một dòng là đủ — mười bốn người quen mặt nhau, tên là đã
   nhận ra. Ở thẻ Cả lớp thì không: lớp này có hàng chục người cùng làm ở
   "Công ty Cổ phần Hữu Nghị…", nên cắt tại chỗ ấy là năm dòng liền giống hệt
   nhau và danh bạ mất đúng cái việc nó sinh ra để làm. Đo bằng ảnh chụp, không
   phải đoán. */
.dblop .mrow .co{white-space:normal;overflow:visible;text-overflow:clip;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;line-height:1.35}
/* Nhãn "đã thu đủ" trên đầu thẻ đợt thu — cùng cặp màu với mọi dấu ✓ khác. */
.xongchip{background:var(--go-bg);color:var(--go);border-radius:999px;
  padding:2px 8px;margin-left:6px;letter-spacing:.04em;white-space:nowrap}
/* "Cả nhóm đã khai" — mốc thật nhưng CHƯA phải quỹ xong. Màu cam của "đã tự
   khai", không bao giờ xanh: xanh trong ứng dụng này chỉ có một nghĩa duy
   nhất là người thu đã soi sao kê và nhận được tiền. */
.khaichip{background:var(--due-bg);color:var(--due);border-radius:999px;
  padding:2px 8px;margin-left:6px;letter-spacing:.04em;white-space:nowrap}

/* Báo có bản mới. Nổi trên thanh điều hướng, không che nút nào, và không tự
   tải lại — người ta có thể đang gõ dở một ô. */
.banmoi{position:fixed;left:50%;transform:translateX(-50%);
  bottom:calc(96px + env(safe-area-inset-bottom));z-index:60;
  background:var(--ink);color:#fff;border-radius:999px;
  padding:11px 20px;font-size:13.5px;font-weight:600;box-shadow:var(--sh2)}
@media (prefers-reduced-motion:no-preference){
  .banmoi{animation:banlen .22s ease-out}
  @keyframes banlen{from{opacity:0;transform:translate(-50%,10px)}to{opacity:1;transform:translate(-50%,0)}}
}

/* ─── RUBRIC ─── */
.rub{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:20px}
.rc{background:var(--surf);border-radius:var(--r);box-shadow:var(--sh);padding:14px 15px}
.rc .p{font-family:var(--num);font-weight:700;font-size:26px;letter-spacing:-.03em;line-height:1}
.rc .l{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink3);margin-top:4px}
.rc ul{margin:9px 0 0;padding-left:15px;font-size:12.5px;color:var(--ink2);line-height:1.5}

/* ─── TÂM ĐẮC ─── */
.q{background:var(--surf);border-radius:var(--r);box-shadow:var(--sh);padding:15px 16px;margin-bottom:10px}
.q blockquote{margin:0 0 11px;font-size:15px;line-height:1.52}
.qf{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px;color:var(--ink3)}
.qf .w{color:var(--ink);font-weight:600}
.lnk{font-size:12px;color:var(--due);text-decoration:underline;margin-left:auto}

/* chỉ đọc (không có quyền sửa) */
.ro{background:var(--surf);box-shadow:inset 0 0 0 1px var(--line2);border-radius:10px;
  padding:12px 13px;font-size:14px;margin-bottom:14px}

/* nút phụ, viền mảnh */
.wide.ghost{background:transparent;color:var(--ink2);box-shadow:inset 0 0 0 1px var(--line2)}

/* ─── QUỸ ─── */
.amt{font-family:var(--num);font-weight:700;font-size:34px;letter-spacing:-.035em;line-height:1}
.amt small{font-family:'Be Vietnam Pro';font-size:14px;font-weight:500;color:var(--ink3);letter-spacing:0}
.qrw{padding:20px 16px;text-align:center;background:var(--surf2);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.qrw img,.qrw .ph{width:196px;max-width:100%;border-radius:12px;background:#fff;margin:0 auto;display:block}
.qrw .ph{height:auto;min-height:120px;display:grid;place-items:center;box-shadow:inset 0 0 0 1px var(--line2);
  font-size:12px;color:var(--ink3);padding:22px;line-height:1.55}
.qrw .cap{font-size:12.5px;color:var(--ink2);margin-top:13px;line-height:1.6}
.copy{display:inline-flex;align-items:center;gap:7px;font-family:var(--num);font-size:13px;font-weight:600;
  background:var(--surf);box-shadow:var(--sh);padding:8px 13px;border-radius:99px;margin-top:9px;transition:.16s;
  max-width:100%;overflow-wrap:anywhere}
.copy:active{transform:scale(.96)}
/* Hai nút chép xếp dọc, canh giữa trong .qrw. Số tài khoản có nút RIÊNG từ
   19/9: nhánh dự phòng lúc mã QR hỏng bảo người ta "chuyển khoản tay theo số
   tài khoản bên dưới", mà số ấy trước nay là chữ thường 12.5px không chép
   được — tức đường lui chính thức bắt người 50 tuổi đọc tay một dãy 10-14
   chữ số rồi gõ lại vào app ngân hàng. Gõ sai một số là tiền đi nhầm người. */
.qrw .chep{display:flex;flex-direction:column;align-items:center;gap:7px;margin-top:11px}
.qrw .chep .copy{margin-top:0}
/* Nhãn phụ phải rơi về phông CHỮ, không dùng var(--num): Space Grotesk thiếu
   glyph tiếng Việt nên "chép" rụng dấu và đứng lệch hẳn — đúng lỗi đã trả giá
   với tiêu đề trang /giao-thuong. */
.copy .nh{font-family:'Be Vietnam Pro',system-ui,sans-serif;font-size:11px;
  font-weight:500;color:var(--ink3);letter-spacing:0}
.dots{display:flex;gap:5px;flex-wrap:wrap;margin-top:12px}
.dots i{width:12px;height:12px;border-radius:4px;background:var(--line2);transition:background .4s}
.dots i.d{background:var(--due)}
.dots i[data-v]{background:var(--go)}
.wide.ok{background:var(--go-bg);color:var(--go);box-shadow:inset 0 0 0 1px var(--go-line);
  display:block;text-align:center;padding:14px;border-radius:12px;font-weight:600;font-size:15px}
.wide:disabled{opacity:.5}
/* .wide vốn viết cho <button>; dùng cho <a> thì phải tự căn giữa và bỏ gạch chân */
a.wide{display:block;text-align:center;text-decoration:none}

/* lời nhắc bắt buộc đọc trước khi mở đợt thu (mục 6.2 SRS) */
.warn{background:var(--due-bg);box-shadow:inset 0 0 0 1px var(--due-line);border-radius:12px;
  padding:13px 15px;font-size:13.5px;line-height:1.55;color:var(--ink)}
.warn b{color:var(--due)}

/* trạng thái trong sổ thu — màu đi kèm chữ, không dùng màu làm tín hiệu duy nhất */
.st-ok{color:var(--go);font-weight:600}
.st-mid{color:var(--due)}
.st-no{color:var(--ink3)}

/* ─── BẢNG PHÂN CÔNG THUYẾT TRÌNH ─── */
.prow{display:flex;align-items:center;gap:13px;padding:11px 16px;border-bottom:1px solid var(--line)}
.prow:last-child{border-bottom:0}
.prow .pnum{padding-top:0}
.prow .pbd{flex:1;min-width:0}

/* ─── THẺ WIZARD (rộng hơn thẻ đăng nhập) ─── */
.claimcard.wide-card{max-width:460px}
.claimcard .opt{margin-bottom:8px}
.claimcard .fd input[type=checkbox]{accent-color:var(--go);width:18px;height:18px;flex:0 0 auto}

/* ─── SẮP CÓ ─── */
.soon{background:var(--surf);border-radius:var(--r);box-shadow:var(--sh);padding:26px 20px;text-align:center}
.soon .ic{font-size:26px;margin-bottom:10px}
.soon h3{font-size:16px;font-weight:700;margin:0 0 6px}
.soon p{color:var(--ink2);font-size:13.5px;line-height:1.55;margin:0 auto;max-width:32ch}

/* ─── THANH DƯỚI ─── */
nav{position:fixed;left:0;right:0;bottom:0;z-index:50;background:rgba(243,243,241,.9);
  backdrop-filter:saturate(1.6) blur(16px);border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom)}
/* Sáu tab, không còn năm — và nhãn dài nhất nay là "Giao thương", mười một ký
   tự. Ngô Phú Cường quyết 5/9: giữ nguyên chữ, chật thì BỎ CHỮ chứ không đổi
   tên tab cho vừa.

   Số ĐO ĐƯỢC (scripts/kiem/pw-nav.mjs), không phải ước lượng:

     khổ máy   nút rộng   "Giao thương" cần
      320px      49px          75px   ✗
      390px      61px          75px   ✗   ← iPhone 12–15
      412px      64px          75px   ✗
      430px      69px          75px   ✗   ← kể cả iPhone Pro Max
      520px      82px          75px   ✓   ← chỉ máy tính bảng trở lên

   Tức nhãn đầy đủ KHÔNG vừa trên bất kỳ điện thoại nào — 520px cũng chính là
   --wrap, bề rộng lớn nhất thanh nav từng đạt, nên trên nữa cũng không rộng
   thêm. Nhưng rụng chữ cả sáu tab thì lộ ra chuyện khác: icon "Bài" và icon
   "Tư liệu" đều là hình trang giấy, bỏ chữ đi là hai tab ấy gần như không
   phân biệt được.

   Nên dưới 520px: TAB ĐANG MỞ giữ chữ, năm tab kia còn icon. Người dùng luôn
   đọc được mình đang ở đâu, và bấm sang tab khác là chữ hiện ra ngay tại đó.
   Tab đang mở lấy flex 1.8 để có chỗ cho chữ — ở 320px nó được 81px, vừa đủ
   cho 75px.

   MỘT CẢNH BÁO cho ai đo lại: `white-space:nowrap` khiến nhãn quá dài TRÀN ra
   ngoài nút chứ không xuống dòng, nên phép kiểm dựa vào chiều cao nút hay vào
   scrollWidth của thanh nav đều KHÔNG bắt được — cả hai đã báo xanh trên đúng
   cái màn hình đang hỏng. Phải so scrollWidth của chính .lb với clientWidth
   của .nb.

   Và con số trên vẫn lệch với máy thật: sandbox không ra được internet nên
   Google Fonts không tải, phép đo chạy trên font dự phòng của hệ thống chứ
   không phải Be Vietnam Pro. Font dự phòng rộng hơn, nên đây là phép đo bi
   quan — máy thật chỉ có thể rộng rãi hơn, không chật hơn. */
.navin{max-width:var(--wrap);margin:0 auto;display:flex;padding:7px 6px 9px;gap:3px}
.nb{flex:1;min-width:0;padding:8px 1px;border-radius:10px;font-size:11px;font-weight:500;color:var(--ink3);
  display:flex;flex-direction:column;align-items:center;gap:5px;transition:.18s}
.nb .lb{white-space:nowrap;letter-spacing:-.01em}
@media (max-width:519px){
  .nb .lb{display:none}
  .nb.on .lb{display:block}
  /* Tab đang mở nở ra lấy chỗ cho chữ; năm tab kia co lại quanh icon. */
  .nb.on{flex:1.8}
  /* Nút chỉ còn icon phải giữ nguyên chiều cao của nút có chữ, nếu không thì
     nền trắng của tab đang mở nhô lên và thanh nav trông như gãy. stretch của
     .navin lo phần chiều cao; justify-content căn icon vào giữa khoảng ấy. */
  .nb{justify-content:center}
}
/* Icon là SVG nét (xem bảng ICON trong app.js), không còn là khối bo góc bằng
   box-shadow nữa. Chỉ cần định kích thước; màu theo currentColor của nút. */
.nb .g{width:20px;height:20px;display:block;transition:.18s}
.nb.on{color:var(--ink);font-weight:600;background:var(--surf);box-shadow:var(--sh)}

/* ─── SHEET ─── */
.veil{position:fixed;inset:0;z-index:60;background:rgba(23,25,27,.4);display:none;
  align-items:flex-end;justify-content:center;backdrop-filter:blur(2px)}
.veil.on{display:flex;animation:fade .2s ease}
@keyframes fade{from{opacity:0}to{opacity:1}}
/* `dvh` chứ không phải `vh`: trên di động `vh` tính theo màn hình lúc thanh
   địa chỉ ĐANG ẨN, nên sheet cao 90vh thực tế tràn khỏi màn khi thanh hiện ra
   — nút Lưu ở đáy bị đẩy ra ngoài, không bấm được. `vh` giữ làm đường lui cho
   trình duyệt cũ (khai trước, cái sau đè lên nếu hiểu được).
   `overscroll-behavior:contain` để cuộn hết sheet thì DỪNG, không kéo lây
   trang nền phía sau trôi theo — trang nền trôi trong khi sheet đứng yên là
   dấu hiệu "web" rõ nhất còn sót lại. */
.sheet{background:var(--bg);width:100%;max-width:var(--wrap);border-radius:20px 20px 0 0;
  padding:8px 16px calc(22px + env(safe-area-inset-bottom));max-height:90vh;max-height:90dvh;
  overflow-y:auto;overscroll-behavior:contain;
  animation:up .34s cubic-bezier(.22,1,.36,1)}
@keyframes up{from{transform:translateY(28px)}to{transform:none}}
.grab{width:38px;height:4px;border-radius:99px;background:var(--line2);margin:0 auto 15px}
.sheet h3{font-size:19px;font-weight:700;letter-spacing:-.015em;margin:0 0 5px}
.sheet .sub{font-size:13.5px;color:var(--ink2);margin:0 0 18px;line-height:1.5}
label.f{display:block;font-size:10.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink3);margin:0 0 6px}
/* CỠ CHỮ Ô NHẬP PHẢI ≥ 16px — đây là con số CỦA iOS, không phải thẩm mỹ. Ô nhỏ
   hơn 16px thì Safari tự phóng to cả trang lúc chạm vào ô, rồi KHÔNG tự thu
   lại: gõ xong một ô là mọi thứ to đùng, phải chụm hai ngón thu về tay. Đây
   chính là kiểu "phóng nhầm" khó chịu nhất, và là lý do 15px cũ phải đổi.
   Bịt được ở đây thì khoá zoom hay không cũng không còn quan trọng mấy. */
input,textarea,select{width:100%;background:var(--surf);border:0;box-shadow:inset 0 0 0 1px var(--line2);
  border-radius:10px;padding:12px 13px;font-family:'Be Vietnam Pro',sans-serif;font-size:16px;
  color:var(--ink);margin-bottom:14px;transition:box-shadow .16s}
textarea{resize:vertical;min-height:74px;line-height:1.5}
input:focus,textarea:focus,select:focus{outline:none;box-shadow:inset 0 0 0 2px var(--ink)}
.hintline{font-size:11.5px;color:var(--ink3);margin:-10px 0 14px}
.errline{font-size:11.5px;color:var(--due);margin:-10px 0 14px}
.sa{display:flex;gap:10px;margin-top:6px}
.big{flex:1;padding:14px;border-radius:12px;font-size:14.5px;font-weight:600;text-align:center;transition:transform .16s}
.big:active{transform:scale(.98)}
.big.go{background:var(--ink);color:#fff}
.big.go:disabled{opacity:.5}
.big.c{background:transparent;box-shadow:inset 0 0 0 1px var(--line2);color:var(--ink2)}
.wide{width:100%;padding:14px;border-radius:12px;background:var(--ink);color:#fff;font-weight:600;
  font-size:15px;transition:transform .16s}
.wide:active{transform:scale(.985)}

/* ─── TOAST ─── */
#toast{position:fixed;left:50%;bottom:calc(96px + env(safe-area-inset-bottom));transform:translate(-50%,14px);
  background:var(--ink);color:#fff;padding:11px 18px;border-radius:99px;font-size:13.5px;font-weight:500;
  z-index:70;opacity:0;pointer-events:none;transition:.28s cubic-bezier(.22,1,.36,1);white-space:nowrap;
  box-shadow:0 8px 28px rgba(0,0,0,.24)}
#toast.on{opacity:1;transform:translate(-50%,0)}

/* ─── MÀN NHẬN LINK MỜI / CHƯA ĐĂNG NHẬP ─── */
/* Màn nhận link mời / đăng nhập chạy với body.noapp (không có header, không có
   thanh dưới) nên phải TỰ chừa cả hai lề an toàn — không có ai chừa hộ. */
.claimwrap{min-height:100vh;min-height:100dvh;display:flex;align-items:center;justify-content:center;
  padding:calc(24px + env(safe-area-inset-top)) 16px calc(24px + env(safe-area-inset-bottom))}
.claimcard{max-width:400px;width:100%;background:var(--surf);border-radius:20px;box-shadow:var(--sh2);
  padding:24px 22px}
.claimcard .lb{font-size:11px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--ink3);margin-bottom:8px}
.claimcard h1{font-size:21px;font-weight:700;letter-spacing:-.015em;margin:0 0 8px}
.claimcard p.sub{font-size:13.5px;color:var(--ink2);margin:0 0 20px;line-height:1.5}
.claimcard .err{background:var(--due-bg);color:var(--due);border-radius:12px;padding:14px 16px;font-size:13.5px;line-height:1.5}

/* ─── MÀN VÀO: bốn chỗ sửa ngày 19/9 ────────────────────────────────────────
   Ba bước của /vao và hai màn của /dangnhap là ấn tượng đầu tiên của 77 người
   chưa vào được ứng dụng. Khảo sát ngày 19/9 chỉ ra bốn chỗ, mỗi chỗ một lý
   do cụ thể chứ không phải cho đẹp. */

/* 1. KHỐI BÁO LỖI THẬT, thay cho .hintline.
   Trước nay cả ba màn báo lỗi bằng `.hintline` — 11,5px màu --ink3, tức
   2,7:1 trên nền trắng — rồi chỉ đổi MÀU CHỮ sang --due khi hỏng. Đó là
   thông điệp QUYẾT ĐỊNH BỎ CUỘC, đặt ở đúng cỡ chữ nhỏ nhất và màu nhạt nhất
   của cả ứng dụng, cho một lớp 35–55 tuổi đang đứng ở cửa.
   Nay dùng lại đúng cặp màu của `.claimcard .err` (--due trên --due-bg =
   4,78:1, đạt AA), 13,5px. KHÔNG thêm màu mới, và KHÔNG có nhánh xanh:
   --go/--go-bg trong sản phẩm này có đúng một nghĩa (người thu đã nhận
   tiền), mượn nó cho "đã gửi mã mới" là làm hỏng nghĩa ấy. Tin báo bình
   thường dùng nền xám trung tính. */
.vmsg{display:none;font-size:13.5px;line-height:1.5;border-radius:12px;
  padding:12px 14px;margin:0 0 14px;background:var(--due-bg);color:var(--due)}
.vmsg.hien{display:block}
.vmsg.tin{background:var(--bg);color:var(--ink2)}
.vmsg .lam{display:block;margin-top:7px;font-size:12.5px;opacity:.88}

/* 2. CẢ DÒNG TÊN BẤM ĐƯỢC.
   Trước nay chỉ nút "là tôi" bấm được: chữ 12px trong một ô cao 18px, nằm
   sát mép phải. Đó là cú chạm QUAN TRỌNG NHẤT của cả sản phẩm — bước đầu
   tiên của mọi người chưa vào — và nó nhỏ hơn mọi nút khác trên màn.
   Nay cả dòng là một <button>, cao tối thiểu 56px. Vẫn giữ chữ "là tôi" làm
   dấu hiệu bấm được: bỏ đi thì dòng trông như một dòng chữ để đọc. */
.fdpick{display:flex;align-items:center;gap:12px;width:100%;min-height:56px;
  padding:11px 2px;background:none;border:0;border-bottom:1px solid var(--line);
  text-align:left;transition:background .14s}
.fdpick:last-child{border-bottom:0}
.fdpick:active{background:var(--bg)}
.fdpick .x{flex:1;min-width:0;font-size:13.5px;color:var(--ink2);line-height:1.45}
.fdpick .x b{display:block;color:var(--ink);font-weight:600;font-size:15px;line-height:1.35}
.fdpick .x .d{font-size:12.5px;color:var(--ink2);margin-top:2px}
.fdpick .ch{flex:0 0 auto;font-size:12.5px;font-weight:600;color:var(--due);
  text-decoration:underline}

/* 3. CHỈ DẤU BƯỚC. Ba bước của /vao trước nay không có dấu hiệu nào, nên
   bước nào cũng trông như bước cuối và không ai biết còn bao xa. --ink2 chứ
   không --ink3: nó là thông tin, không phải trang trí. */
.claimcard .lb{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.claimcard .lb .bw{color:var(--ink2);flex:0 0 auto}

/* 4. ĐƯỜNG CHUYỂN LUỒNG là một NÚT, không phải chữ trong chân thẻ.
   "Đăng nhập bằng email" (ở /vao) và "tự nhận diện" (ở /dangnhap) là hai
   đường ra duy nhất khi đi nhầm luồng — mà chúng đang là liên kết 11,5px
   nằm trong .foot màu --ink3. Ai đi nhầm luồng thì đúng là người ít kiên
   nhẫn nhất còn lại. */
/* Viền NHẠT hơn `.wide.ghost` và cách xa hơn: nó là lối RẼ SANG LUỒNG KHÁC,
   không phải một lựa chọn thứ ba ngang hàng với hai nút trên. Cùng độ đậm thì
   ba nút xếp chồng đọc thành ba việc như nhau. */
.vlink{display:block;width:100%;min-height:46px;margin-top:20px;padding:12px;
  border-radius:12px;background:transparent;color:var(--ink2);font-size:13.5px;
  box-shadow:inset 0 0 0 1px var(--line);text-align:center;line-height:1.4}
.vlink b{color:var(--ink);font-weight:600}
.vlink:active{background:var(--bg)}

/* Chỗ giữ chỗ lúc đang hỏi máy chủ tên. Debounce 350ms cộng một lượt gọi
   trên mạng hội trường là vài giây màn hình đứng im — mà đứng im là thứ làm
   người ta gõ lại từ đầu hoặc bỏ cuộc. */
.vdang{font-size:13px;color:var(--ink2);padding:10px 2px}

/* Ô CHỈ ĐỌC ở màn nhận link mời. Trước nay là `<input disabled>`, và một ô
   nhập mờ đi đọc lên như một ô HỎNG — chạm vào không được, không ai nói vì
   sao. Họ tên ở đó là thứ để ĐỐI CHIẾU chứ không phải để sửa, nên vẽ nó như
   một dòng chữ. */
.vdoc{font-size:15px;font-weight:600;color:var(--ink);padding:12px 2px 14px}

/* Dòng gợi ý dưới ô nhập, CHỈ trong ba màn vào. Toàn ứng dụng `.hintline` là
   11,5px màu --ink3 (2,7:1) — đó là quyết định áp cho mọi màn và chưa ai xin
   đổi. Nhưng ở ĐÂY nó đang giải thích những ô mà gõ sai là mất đường vào lại
   khi đổi máy, cho người chưa dùng ứng dụng lần nào, nên nâng riêng trong
   phạm vi `.claimcard` chứ không đổi biến chung. */
.claimcard .hintline{font-size:12.5px;color:var(--ink2)}

/* 5. BĂNG "máy này đang đăng nhập rồi" (19/9).
   Người đã ở trong ứng dụng mở /dangnhap ra thì gặp một màn "Bạn là ai?" rồi
   bị đòi số điện thoại — số phải khớp bản Ban tổ chức nạp 15/8 chứ không phải
   số họ tự khai, nên với 38 người thiếu số đó là ngõ cụt. Băng này là lối ra.

   Nền xám trung tính chứ KHÔNG --go-bg: cặp màu xanh trong sản phẩm này có
   đúng một nghĩa (người thu đã nhận tiền). Đây không phải một trạng thái
   "xong", chỉ là một lời chỉ đường.
   Viền trên liền `--line` để nó tách khỏi tiêu đề mà không thành một khối
   cảnh báo: người đang muốn đăng nhập bằng tài khoản khác KHÔNG làm gì sai. */
.vphien{background:var(--bg);border-radius:12px;padding:13px 14px 12px;
  margin:12px 0 4px;box-shadow:inset 0 0 0 1px var(--line)}
.vphien .t{font-size:13.5px;line-height:1.5;color:var(--ink2);margin-bottom:11px}
.vphien .t b{color:var(--ink);font-weight:600}
.vphien .n{font-size:12px;line-height:1.5;color:var(--ink2);margin-top:9px}

@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* Chấm đỏ báo có thông báo chưa xem. Đặt ở góc trên phải của biểu tượng tab,
   không đè lên chữ. Có viền cùng màu nền để tách khỏi nét biểu tượng. */
.nb{position:relative}
.nb.cham::after{
  content:'';position:absolute;top:6px;left:calc(50% + 7px);
  width:8px;height:8px;border-radius:50%;
  background:var(--due,#E5484D);box-shadow:0 0 0 2px var(--bg,#14161A);
}

/* ─── Tư liệu gắn vào buổi học (tab Hôm nay) ───
   Thụt vào và có gạch dọc để đọc ra ngay là "thuộc về buổi ở trên", không phải
   một mục ngang hàng. Cùng một dòng dữ liệu với tab Tư liệu, không phải bản sao. */
.tlb{margin-top:8px;padding-left:10px;border-left:2px solid var(--line);
  display:flex;flex-direction:column;gap:5px}
.tlb a,.tlb .trong,.tlb .xem{display:flex;align-items:center;gap:7px;font-size:12.5px;line-height:1.4;
  text-decoration:none;color:var(--go);font-weight:500}
.tlb .trong{color:var(--ink3);font-weight:400}
.tlb a:hover,.tlb .xem:hover{text-decoration:underline}
.tlb .k{flex:0 0 auto;font-family:var(--num);font-size:9.5px;font-weight:600;letter-spacing:.04em;
  padding:1px 5px;border-radius:4px;background:var(--line);color:var(--ink2)}
.tlb .mt{color:var(--ink3);font-size:11px}

/* Nội dung Text (mdSafe() trong app.js) hiện trong sheet xem ghi chú — nén
   margin mặc định của trình duyệt lại cho vừa khung sheet chật. */
.mdview{font-size:14.5px;line-height:1.6;color:var(--ink)}
.mdview h4,.mdview h5,.mdview h6{margin:14px 0 6px;line-height:1.35}
.mdview h4{font-size:17px}.mdview h5{font-size:15.5px}.mdview h6{font-size:14.5px}
.mdview p{margin:0 0 10px}
.mdview ul,.mdview ol{margin:0 0 10px;padding-left:20px}
.mdview li{margin:0 0 4px}
.mdview a{color:var(--go)}
.mdview > :first-child{margin-top:0}
/* URL dán thẳng dài 60-70 ký tự là chuyện thường trong thông báo. Không có
   dòng này thì nó đẩy toạc thẻ ra và cả trang trượt ngang. */
.mdview a{overflow-wrap:anywhere}

/* Bản gọn cho thông báo ở tab Hôm nay — cùng một hàm dựng (mdSafe), chỉ khác
   cỡ chữ và lề, không viết bộ quy tắc thứ hai. */
.mdview.nho{font-size:14px;line-height:1.55}
.mdview.nho p,.mdview.nho ul,.mdview.nho ol{margin:0 0 6px}
.mdview.nho h4,.mdview.nho h5,.mdview.nho h6{margin:10px 0 4px;font-size:14.5px}
.mdview.nho > :last-child{margin-bottom:0}

/* Thanh B / I / gạch đầu dòng / liên kết của ô soạn thông báo. */
.mdbar{display:flex;gap:6px;margin:0 0 7px}
.mdbar button{min-width:36px;height:32px;border-radius:8px;font-size:13.5px;color:var(--ink2);
  background:var(--surf);box-shadow:inset 0 0 0 1px var(--line2);transition:transform .14s}
.mdbar button:active{transform:scale(.93)}
.mdxem{background:var(--surf2);border-radius:10px;padding:11px 13px;margin:0 0 14px;
  box-shadow:inset 0 0 0 1px var(--line);min-height:44px}

/* Nhãn buổi trên dòng ở tab Tư liệu — nửa còn lại của cùng một lối tắt. */
.nb-buoi{display:inline-block;font-size:10.5px;font-weight:600;padding:1px 6px;
  border-radius:99px;background:var(--go-bg);color:var(--go);margin-right:6px}

/* ─── GIAO THƯƠNG (tab Giao thương) ───────────────────────────────────────────
   Danh mục "bán gì, bán cho ai" của cả lớp. Thẻ ở đây rộng hơn thẻ thành viên
   ở tab Nhóm vì nó phải chứa bốn dòng chữ chứ không phải một dòng tên — nhưng
   vẫn dùng đúng --r, --sh và bảng màu cũ, không dựng phong cách thứ hai. */

/* Thẻ "Gian hàng của bạn" — luôn đứng đầu tab. Người ta phải thấy TÌNH TRẠNG
   của mình trước khi xem của người khác, nếu không thì xem xong rồi quên mất
   là mình chưa điền. */
.gtme{padding:14px 16px}
.gtmeh{display:flex;align-items:center;justify-content:space-between;gap:12px}
.pill{flex:0 0 auto;border:1px solid var(--line2);background:var(--bg);color:var(--ink);
  padding:8px 16px;border-radius:99px;font:inherit;font-size:13.5px;font-weight:600;cursor:pointer}
.pill:active{filter:brightness(.96)}

/* Gợi ý ghép. Nút bấm được, dẫn xuống đúng thẻ người ấy trong danh mục. */
.gtgs{display:flex;flex-direction:column;gap:7px}
.gtg{display:flex;align-items:center;gap:11px;width:100%;text-align:left;
  background:var(--surf);border:1px solid var(--line);border-radius:var(--r2);
  padding:11px 13px;font:inherit;cursor:pointer}
.gtg:active{filter:brightness(.98)}
.gtg .nm{font-weight:600;font-size:14.5px;line-height:1.3}
.gtg .vs{font-size:12px;color:var(--ink2);margin-top:2px;line-height:1.4}

.gttim{width:100%;padding:11px 13px;border:1px solid var(--line2);border-radius:var(--r2);
  background:var(--surf);color:var(--ink);font:inherit;font-size:15px;-webkit-appearance:none}
.gttim::placeholder{color:var(--ink3)}
.gttim:focus{outline:none;border-color:var(--go);box-shadow:0 0 0 1px var(--go)}

.gtc{background:var(--surf);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--sh);padding:15px;margin-bottom:10px}
.gth{display:flex;gap:11px;align-items:flex-start}
.gtc .nm{font-weight:600;font-size:15.5px;line-height:1.3}
.gtc .co{font-size:12.5px;color:var(--ink2);margin-top:2px;line-height:1.4}
.gtn{flex:0 0 auto;font-size:11px;font-weight:600;color:var(--ink3);
  border:1px solid var(--line);border-radius:99px;padding:2px 8px}
.gtng{display:flex;flex-wrap:wrap;gap:5px;margin-top:9px}

/* "Bán gì" là dòng người ta tới đây để đọc — cỡ chữ của một câu trả lời, không
   phải cỡ của một nhãn phụ. */
.gtb{font-size:14.5px;line-height:1.45;margin-top:11px}
.gtp{font-size:13px;color:var(--ink2);line-height:1.45;margin-top:8px}
.gtm{font-size:13px;color:var(--ink2);line-height:1.55;margin-top:9px}
.gtc .k{font-size:10.5px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink3);display:block;margin-bottom:1px}

/* "Đang cần" tô cam: với người đọc thì đây là chỗ HỌ bán được hàng, tức dòng
   có giá trị nhất trên thẻ. Cam đã mang nghĩa "còn việc phải làm" ở khắp ứng
   dụng nên nó đọc đúng mà không cần chú giải. Xanh --go thì KHÔNG dùng ở đây:
   xanh trong sản phẩm này có đúng một nghĩa là "xong". */
.gtcan{margin-top:10px;padding:9px 11px;border-radius:var(--r2);
  background:var(--due-bg);border:1px solid var(--due-line);font-size:13px;line-height:1.45}
.gtcan .k{color:var(--due)}

.gtlh{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.gtlh a{text-decoration:none;border:1px solid var(--line2);border-radius:var(--r2);
  padding:7px 14px;font-size:13px;font-weight:600;color:var(--ink);background:var(--bg)}
.gtlh a:active{filter:brightness(.96)}

/* Hai công tắc mức lộ, trong sheet sửa gian hàng. Chữ giải thích để NGAY dưới
   nhãn chứ không nhét vào chú thích cuối bảng: đây là chỗ người ta quyết định
   có đưa tên và số điện thoại của mình ra internet hay không, và câu giải
   thích phải nằm trong tầm mắt lúc ngón tay đang ở trên công tắc. */
.gtck{display:flex;flex-direction:column;gap:12px;margin-top:16px;
  padding:14px;border-radius:var(--r2);background:var(--bg);border:1px solid var(--line)}
.gtsw{display:flex;gap:11px;align-items:flex-start;cursor:pointer}
.gtsw input{flex:0 0 auto;width:20px;height:20px;margin:1px 0 0;accent-color:var(--go)}
.gtsw b{display:block;font-size:14px;font-weight:600;line-height:1.35}
.gtsw i{display:block;font-style:normal;font-size:12px;color:var(--ink2);
  line-height:1.5;margin-top:3px}
/* Làm mờ chứ không giấu: giấu đi thì người ta không biết là CÓ lựa chọn ấy,
   rồi bật công khai xong lại tưởng số điện thoại mặc nhiên ra theo. */
.gtsw.mo{opacity:.45}

/* ─── TRỢ LÝ KHKD (sheet hội thoại, veHoiThoai() trong app.js) ───────────────
   Ba điều cố ý ở khối này:

   1. `.tlbox` PHẢI có thanh cuộn RIÊNG, không để sheet cuộn hộ. `veHoiThoai()`
      đẩy màn xuống cuối bằng `box.scrollTop = box.scrollHeight` sau mỗi lượt —
      không có `overflow-y` ở đây thì câu vừa gửi nằm ngoài tầm nhìn và học
      viên tưởng gửi hỏng, trong khi thật ra chỉ là chưa cuộn tới. Kèm
      `overscroll-behavior:contain` để cuộn hết hội thoại thì DỪNG, không kéo
      lây cả sheet trôi theo (cùng lý lẽ đã ghi cho chính `.sheet`).
   2. Bong bóng của người dùng KHÔNG dùng cặp `--go-bg`/`--go`. Cặp màu ấy
      trong sản phẩm này có đúng một nghĩa — người thu đã nhận tiền — nên đem
      tô lời nhắn là phá quy ước màu. Dùng `--ink` đảo nền, đúng kiểu nút
      `.big.go` đã dùng cho "đây là thứ bạn vừa làm".
   3. `max-width:84%` cho cả hai phía: bong bóng chạm hai mép màn là mất đường
      viền thị giác giữa "trợ lý hỏi" và "tôi trả lời", mà đó là thứ duy nhất
      phân biệt hai vai trong một khối chữ dài. */
.tlbox{display:flex;flex-direction:column;gap:9px;max-height:46vh;max-height:46dvh;
  overflow-y:auto;overscroll-behavior:contain;padding:2px 1px 4px;margin:0 0 14px;
  -webkit-overflow-scrolling:touch}
.tlbb{max-width:84%;border-radius:14px;padding:10px 13px;font-size:14px;line-height:1.55;
  overflow-wrap:anywhere}
/* Người dùng: nền đậm, nằm bên phải. */
.tlbb.toi{align-self:flex-end;background:var(--ink);color:#fff;white-space:pre-wrap;
  border-bottom-right-radius:5px}
/* Trợ lý: nền trắng viền nhạt, nằm bên trái. Chữ do mô hình sinh ra đi qua
   mdSafe() nên bên trong là `.mdview.nho`, không phải văn bản thuần. */
.tlbb.tl{align-self:flex-start;background:var(--surf);
  box-shadow:inset 0 0 0 1px var(--line);border-bottom-left-radius:5px}
.tlbb.tl .mdview a{overflow-wrap:anywhere}
/* Chỗ giữ chỗ trong lúc chờ máy chủ. Một lượt gọi mất hàng chục giây — không
   có gì nhấp nháy ở đây thì màn hình đứng im và người ta bấm Gửi lần nữa. */
.tlbb.dangcho{color:var(--ink3);font-style:italic;animation:tlnhay 1.5s ease-in-out infinite}
@keyframes tlnhay{0%,100%{opacity:1}50%{opacity:.45}}

/* ═══════════ LỄ TỐT NGHIỆP 26/9 — /totnghiep ═══════════
   Màn riêng trong chính ứng dụng (không phải thư mục rời như /lich hay
   /giao-thuong — lý do ghi ở đầu khối renderTotNghiep trong app.js).

   .claimwrap có `align-items:center`, đúng cho một thẻ đăng nhập cao vài trăm
   pixel và SAI hẳn cho một biểu mẫu dài: canh giữa theo chiều dọc thì đầu
   trang bị đẩy lên khỏi màn hình và không cuộn ngược lên được. Vì vậy .tnwrap
   canh flex-start và tự cuộn. */
.tnwrap{min-height:100vh;min-height:100dvh;padding:calc(env(safe-area-inset-top) + 22px) 16px
  calc(env(safe-area-inset-bottom) + 40px);background:var(--bg);
  background-image:radial-gradient(130% 320px at 50% 0,rgba(64,58,114,.11),rgba(64,58,114,0) 72%)}
.tncard{max-width:var(--wrap);margin:0 auto}
/* Băng đầu trang — thứ nói "bạn đang ở một chỗ KHÁC" trong đúng nửa giây đầu.
   Nền đặc chàm chứ không phải một thẻ trắng nữa: trên điện thoại người ta
   nhận ra một màn hình bằng MẢNG MÀU trước khi kịp đọc chữ, và cả ứng dụng
   còn lại là thẻ trắng trên nền xám ấm. */
.tnhead{position:relative;overflow:hidden;border-radius:18px;padding:19px 17px 17px;
  margin-bottom:15px;color:#fff;
  background:linear-gradient(135deg,var(--le) 0%,var(--le2) 58%,#6E62AD 100%);
  box-shadow:0 12px 28px -16px rgba(64,58,114,.85)}
/* Mũ tốt nghiệp chìm ở góc: đủ để nhận ra chủ đề, đủ mờ để không giành chỗ
   đọc của tiêu đề. pointer-events:none vì nó nằm đè lên vùng chạm. */
.tnhead::after{content:'🎓';position:absolute;right:-8px;top:-14px;font-size:96px;
  line-height:1;opacity:.14;pointer-events:none}
.tnhead .lb{display:flex;align-items:center;justify-content:space-between;gap:10px;
  font-size:10.5px;font-weight:600;letter-spacing:.11em;text-transform:uppercase;
  color:rgba(255,255,255,.72);margin-bottom:7px}
/* Lối về ở ĐẦU trang (19/9). Biểu mẫu dài hơn ba màn điện thoại và `noapp` đã
   bỏ thanh sáu tab, nên trước đó đường ra duy nhất nằm tận chân trang.
   Nền trắng mờ chứ không phải nút đặc: nó là lối RA, không được giành sự chú
   ý với ba khối cần khai. Vùng chạm 44px giữ bằng padding dọc cộng
   `min-height` — chữ chỉ 10,5px nên tự nó cao chưa tới 16px.
   `position:relative` + `z-index` để nằm TRÊN mũ 🎓 của ::after, thứ đang phủ
   đúng góc phải này. */
.tnhead .tnve{position:relative;z-index:1;flex:0 0 auto;display:inline-flex;
  align-items:center;gap:6px;min-height:44px;padding:0 15px;border-radius:999px;
  background:rgba(255,255,255,.16);color:#fff;text-decoration:none;
  letter-spacing:.04em;box-shadow:inset 0 0 0 1px rgba(255,255,255,.22)}
.tnhead .tnve:active{background:rgba(255,255,255,.28)}
.tnhead h1{font-size:25px;font-weight:700;letter-spacing:-.02em;margin:0 0 7px;
  line-height:1.15;color:#fff}
.tnhead p{margin:0;font-size:12.5px;line-height:1.5;color:rgba(255,255,255,.84)}

.tncard>p.sub{font-size:13.5px;color:var(--ink2);margin:0 0 16px;line-height:1.55}

/* BA Ô TIẾN ĐỘ — LUÔN MỘT HÀNG.
   Bản đầu dùng flex-wrap và ở khổ 390px ô thứ ba rơi xuống dòng hai (Ngô Phú
   Cường chụp màn hình 19/9). Rơi dòng là mất đúng công dụng của dải này: ba
   mốc phải nhìn thấy CÙNG LÚC thì mới biết còn thiếu phần nào. Nay là lưới
   ba cột bằng nhau nên không có cách nào rơi; chữ trạng thái xuống dòng
   BÊN TRONG ô, không đẩy ô xuống. */
.tnprog{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin:0 0 18px}
/* NÚT THẬT từ 19/9, không còn là <span> — chạm một ô là mở đúng khối của nó.
   Phải khai lại `border`/`font`/`color` vì thẻ button mang mặc định riêng của
   trình duyệt. `min-height:48px` là phần thêm: padding cũ cho khoảng 40px,
   dưới ngưỡng 44px mà phần còn lại của ứng dụng đang giữ; ô rộng ~114px ở khổ
   390px nên chỉ thiếu chiều cao, không thiếu bề ngang. */
.tnprog>button{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:2px;min-width:0;min-height:48px;text-align:center;background:var(--surf);
  border:0;border-radius:12px;padding:9px 5px;font:inherit;color:inherit;
  box-shadow:var(--sh);cursor:pointer;transition:transform .16s}
.tnprog>button:active{transform:scale(.97)}
.tnprog>button b{font-size:12.5px;font-weight:600;line-height:1.2}
.tnprog>button i{font-style:normal;font-size:10.5px;color:var(--ink3);line-height:1.2}
/* Xong thì đổi CẢ Ô sang xanh, không chỉ thêm một chip con: liếc một cái là
   đếm được còn mấy ô trắng. Vẫn đúng luật màu — xanh ở đây là "xong", nghĩa
   duy nhất nó được phép mang. */
.tnprog>button.xong{background:var(--go-bg);box-shadow:inset 0 0 0 1px var(--go-line)}
.tnprog>button.xong b,.tnprog>button.xong i{color:var(--go)}
/* Ô của khối ĐANG MỞ. Chàm `--le` là nối tiếp ngôn ngữ đã có chứ không phải
   màu mới: `.tnsec[open]` ngay dưới cũng dùng đúng chàm ấy cho dải trái, viền
   và nền đầu khối — ô và khối nói cùng một chuyện ("bạn đang ở đây") nên phải
   cùng một màu. Đúng vế "viền khối đang mở" mà luật --le cho phép, và KHÔNG
   phải một trạng thái: xong/còn nợ vẫn chỉ xanh và cam.

   `outline` chứ không `box-shadow`: ô đã xong đang dùng box-shadow cho viền
   xanh --go-line, ghi đè bằng box-shadow là mất viền ấy. Outline chồng lên
   được, và `outline-offset:-2px` giữ nó nằm trong ô nên không đội lưới. */
.tnprog>button.dangmo{outline:2px solid var(--le);outline-offset:-2px}

/* <details> thay vì tự viết accordion bằng JS: mở/đóng chạy sẵn, bàn phím và
   trình đọc màn hình hiểu sẵn, và không có trạng thái nào để lệch. */
.tnsec{position:relative;background:var(--surf);border-radius:var(--r);box-shadow:var(--sh);
  margin-bottom:11px;overflow:hidden}
/* Khối ĐANG MỞ phải nhận ra được từ xa — Ngô Phú Cường 19/9: "3 phần cần khai
   chưa nổi bật". Ba khối giống hệt nhau thì không biết mình đang đứng ở đâu,
   nhất là sau khi cuộn xuống giữa một biểu mẫu dài. Dải chàm bên trái cộng
   nền đầu khối: hai dấu hiệu, không chỉ một. */
.tnsec[open]{box-shadow:0 0 0 1.5px var(--le-line),var(--sh)}
.tnsec[open]::before{content:'';position:absolute;left:0;top:0;bottom:0;width:4px;
  background:var(--le);z-index:1}
.tnsec>summary{display:flex;align-items:center;gap:12px;padding:14px 14px 14px 16px;
  cursor:pointer;list-style:none;user-select:none}
.tnsec[open]>summary{background:var(--le-bg)}
.tnsec>summary::-webkit-details-marker{display:none}
/* Huy hiệu bước: SỐ khi chưa khai, ✓ khi xong. Số thứ tự nói được "còn mấy
   việc nữa" — thứ một dấu chấm tròn không nói được. Vòng tròn 30px cũng là
   mỏ neo cho mắt: ba khối nay bắt đầu bằng ba hình tròn thẳng hàng. */
.tnsec>summary .n{flex:0 0 auto;width:30px;height:30px;border-radius:50%;display:grid;
  place-items:center;font-family:var(--num);font-size:13px;font-weight:600;
  color:var(--le);background:var(--le-bg);box-shadow:inset 0 0 0 1.5px var(--le-line)}
.tnsec>summary .n.xong{background:var(--go-bg);color:var(--go);font-family:inherit;
  font-size:15px;box-shadow:inset 0 0 0 1.5px var(--go-line)}
.tnsec>summary .t{flex:1;min-width:0}
.tnsec>summary .t b{display:block;font-size:15.5px;font-weight:600;line-height:1.3}
/* Dòng hạn đi XUỐNG DÒNG RIÊNG chứ không nằm cạnh tiêu đề. Nằm cạnh thì ở khổ
   390px nó đẩy tiêu đề vỡ ra và cả hàng thành ba dòng so le — đúng cảnh trong
   ảnh chụp ngày 19/9. */
.tnsec>summary .t i{display:block;font-style:normal;font-size:11.5px;color:var(--ink3);
  margin-top:2px;line-height:1.35}
/* Còn phải chuyển tiền thì dòng ấy là CAM — màu của việc còn nợ, nghĩa duy
   nhất cam được mang. Không dùng chip: chip ✓ xanh có nghĩa khác hẳn. */
.tnsec>summary .t i.con{color:var(--due);font-weight:600}
.tnsec>summary .ch{flex:0 0 auto;width:26px;height:26px;border-radius:50%;display:grid;
  place-items:center;background:var(--surf2);color:var(--ink3);font-size:11px;
  transition:transform .18s}
.tnsec[open]>summary .ch{transform:rotate(180deg);background:#fff}
.tnbody{padding:14px 16px 18px;border-top:1px solid var(--le-line)}
.tnbody>.wide{margin-top:4px}

/* Công tắc gian hàng / văn nghệ — cùng khuôn .gtsw của Giao thương. Không
   dùng lại chính .gtsw vì lề dọc ở đây khác (nằm giữa các ô nhập, không nằm
   trong một khối riêng cuối sheet). */
.tnsw{display:flex;gap:11px;align-items:flex-start;cursor:pointer;margin:2px 0 14px}
.tnsw input{flex:0 0 auto;width:20px;height:20px;margin:1px 0 0;accent-color:var(--go)}
.tnsw b{display:block;font-size:14px;font-weight:600;line-height:1.35}
.tnsw i{display:block;font-style:normal;font-size:12px;color:var(--ink2);
  line-height:1.5;margin-top:3px}

/* Khối phí: .qrw vốn dựng cho thẻ đợt thu ở tab Quỹ (tràn hết bề ngang thẻ),
   ở đây nằm giữa một biểu mẫu nên phải bo góc lại cho khỏi cắt ngang. */
.tnphi{margin:2px 0 20px}
.tnphi .qrw{border-radius:12px;border:1px solid var(--line);margin-bottom:12px}
.tnphi .eb{margin-bottom:10px}

.tnsoon{font-size:12px;color:var(--ink2);line-height:1.55;background:var(--surf2);
  border-radius:12px;padding:12px 14px;margin:4px 0 16px}
.tnback{display:block;text-align:center;margin-top:22px;font-size:13px;color:var(--ink2);
  text-decoration:none;padding:10px}

/* ══ CÙNG LÀM ĐỀ TÀI (migration 0045) ═══════════════════════════════════════
   Ba danh sách cùng một khuôn thẻ, phân biệt bằng viền chứ không bằng màu nền
   đặc: khối Đề tài có thể có cả ba cùng lúc, mà ba mảng màu xếp chồng đọc
   thành ba mức quan trọng khác nhau trong khi chúng ngang nhau.

   `.tncl.cho` — lời rủ đang chờ TÔI — là khối DUY NHẤT được tô, và tô CAM
   (`--due`, màu của việc còn nợ). KHÔNG dùng `--go`/`--go-bg`: cặp ấy trong
   sản phẩm này có đúng MỘT nghĩa, người thu đã nhận tiền. Và không dùng chàm
   `--le`: luật của nó là CHỈ cho khung sườn của zone (băng đầu trang, huy
   hiệu bước, viền khối đang mở), không bao giờ để nói một trạng thái. */
.tncl{border:1px solid var(--line);border-radius:12px;padding:2px 13px 11px;margin:14px 0 0}
.tncl.cho{border-color:var(--due);background:var(--due-bg);margin:0 0 18px}
.tncl>.h{font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink2);padding:11px 0 3px}
.tncl.cho>.h{color:var(--due)}
.tncl>.ft{font-size:11.5px;color:var(--ink2);line-height:1.55;padding:9px 0 2px;
  border-top:1px solid var(--line);margin-top:10px}
.tncl .it{padding:8px 0;border-top:1px solid var(--line)}
.tncl.cho .it{border-top-color:var(--due-line)}
.tncl .it:first-of-type{border-top:0}
.tncl .it b{font-size:14.5px;font-weight:600}
.tncl .it i{font-style:normal;font-size:12px;color:var(--ink2)}
.tncl .it p{margin:3px 0 0;font-size:13px;color:var(--ink2);line-height:1.5}
.tncl .it q{display:block;margin:6px 0 0;font-size:13px;color:var(--ink);line-height:1.5;
  padding-left:10px;border-left:2px solid var(--line2);font-style:italic}
.tncl .it a{display:inline-block;margin-top:5px;font-size:12.5px;color:var(--due)}
/* Dòng gọn: tên + nhãn nhóm + một nút chữ đẩy sang phải. Vùng chạm của .lnk
   phải đủ 44px cao — nó là nút HUỶ một lời rủ, không phải một chú thích. */
.tncl .it.gon{display:flex;align-items:center;gap:7px;flex-wrap:wrap;min-height:44px}
.tncl .it.gon .mut{font-size:12px}
.tncl .it.gon .lnk{padding:12px 2px 12px 10px}
.tncl .btns{display:flex;gap:9px;margin-top:10px;flex-wrap:wrap}
/* 44px là vùng chạm tối thiểu của cả ứng dụng. Hai nút này quyết định một
   thoả thuận giữa hai người — không phải chỗ để tiết kiệm chiều cao. */
.tncl .btns button{min-height:44px;padding:0 16px;border:0;border-radius:11px;
  font:inherit;font-size:13.5px;font-weight:600;background:var(--ink);color:#fff}
.tncl .btns button.ghost{background:transparent;color:var(--ink2);
  box-shadow:inset 0 0 0 1px var(--line2)}
.tncl .btns button:disabled{opacity:.5}

/* Sheet "Rủ người cùng làm". Danh sách cuộn riêng, trần theo chiều cao màn —
   146 người là ~9.000px, để nó đẩy nút Gửi ra khỏi sheet thì không ai gửi
   được. dvh chứ không vh: vh tính theo màn lúc thanh địa chỉ ĐANG ẨN. */
.ruds{max-height:46dvh;max-height:46vh;overflow-y:auto;overscroll-behavior:contain;
  margin:10px -2px 0;border-top:1px solid var(--line)}
/* Người đã có quan hệ: mờ đi, KHÔNG biến mất khỏi danh sách. Bỏ họ ra là
   người tìm không thấy tên sẽ tưởng ứng dụng bỏ sót — đúng lỗi đã chữa ở màn
   tìm tên 19/9. */
.ruds .fdpick.mo{opacity:.45}
.ruds .fdpick.mo .ch{color:var(--ink2);text-decoration:none}
.rupick{display:flex;align-items:center;gap:10px;padding:12px 0;margin-bottom:4px;
  border-bottom:1px solid var(--line);font-size:15px}
.rupick .lnk{margin-left:auto;padding:10px 2px 10px 10px}

/* Thẻ dẫn sang zone, đặt đầu tab Hôm nay. Không thêm tab thứ bảy: pw-nav.mjs
   đã đo rằng nhãn "Giao thương" cần 75px mà nút rộng nhất chỉ 69px ở khổ
   430px, nên thêm một tab nữa là mọi tab hẹp thêm — cho một việc hết hạn 26/9. */
.tnthe{display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  background:var(--surf);border-radius:var(--r);box-shadow:var(--sh);padding:14px 16px;
  margin-bottom:12px;text-decoration:none;color:inherit}
.tnthe .ic{flex:0 0 auto;width:38px;height:38px;border-radius:11px;display:grid;
  place-items:center;background:var(--go-bg);font-size:19px}
.tnthe .tx{flex:1;min-width:0}
.tnthe .tx b{display:block;font-size:14.5px;font-weight:600;line-height:1.3}
.tnthe .tx span{display:block;font-size:12px;color:var(--ink2);margin-top:2px;line-height:1.45}
.tnthe .go{flex:0 0 auto;color:var(--ink3);font-size:15px}

/* ── Ban cán sự lớp xem theo LĨNH VỰC — thứ thay cho bình chọn Zalo ────────
   Lượt bình chọn Zalo vẽ một thanh nền sau mỗi dòng để so sánh bằng mắt; giữ
   đúng cách đọc ấy vì cả lớp đã quen nó. Khác ở chỗ bấm vào được: mở ra thì
   thấy TÊN từng người, đề tài của họ và link bài — thứ Zalo không cho. */
.dstnbox{display:flex;flex-direction:column;gap:6px;max-height:50vh;max-height:50dvh;
  overflow-y:auto;overscroll-behavior:contain;padding:1px}
/* flex-shrink:0 LÀ BẮT BUỘC, không phải cho đẹp. .dstnbox là flex column có
   max-height, nên mặc định mọi mục con CO LẠI dưới chiều cao nội dung khi
   danh sách dài hơn khung — và tên lĩnh vực hai dòng bị cắt mất dòng dưới
   ("…linh kiện ô tô" còn "…linh kiện ô"). Không lỗi JS, không phép kiểm chuỗi
   nào thấy; chỉ ảnh chụp mới thấy. */
.dstnlv{position:relative;display:block;width:100%;flex:0 0 auto;text-align:left;
  overflow:hidden;background:var(--surf);border-radius:10px;padding:10px 13px;
  box-shadow:var(--sh)}
/* Thanh nền nằm DƯỚI chữ: z-index chứ không phải opacity, để chữ giữ nguyên
   độ tương phản — dòng dài nhất cũng phải đọc được trên nền đậm nhất. */
.dstnlv .thanh{position:absolute;inset:0 auto 0 0;background:var(--go-bg);z-index:0}
.dstnlv .tx{position:relative;z-index:1;display:block}
.dstnlv b{display:block;font-size:13.5px;font-weight:600;line-height:1.35}
.dstnlv i{display:block;font-style:normal;font-size:11.5px;color:var(--ink2);margin-top:2px}
/* Lĩnh vực chưa ai chọn vẫn HIỆN, chỉ mờ đi: biến mất thì Ban cán sự lớp
   không phân biệt được "chưa ai chọn" với "mình quên mất nó". */
.dstnlv.trong{opacity:.5;box-shadow:inset 0 0 0 1px var(--line)}
.dstnai{flex:0 0 auto;background:var(--surf2);border-radius:10px;padding:2px 13px;margin:-2px 0 4px}
/* Người cùng làm, THỤT VÀO dưới chủ bài — nhìn lướt phải đếm được ra số BÀI,
   mà xếp ngang hàng thì màn hình đọc thành một danh sách người, đúng cái vừa
   sửa. Vạch trái chứ không dấu đầu dòng: nó nói "thuộc về khối trên". */
.dstncl{margin:4px 0 2px;padding-left:10px;border-left:2px solid var(--line2);
  font-size:12.5px;color:var(--ink2);line-height:1.6}

/* ── Thẻ TỔNG QUAN của màn thống kê (19/9) ─────────────────────────────────
   Ba ô số một hàng, LƯỚI chứ không flex-wrap — cùng lý do `.tnprog`: ba con
   số của một câu hỏi phải nhìn thấy CÙNG LÚC thì mới so được với nhau, rơi
   dòng là mất đúng công dụng ấy. Nhãn xuống dòng BÊN TRONG ô.

   MÀU: `.go` chỉ dùng cho "người thu đã nhận" và `.due` chỉ cho việc còn phải
   làm — đúng hai nghĩa duy nhất của hai màu ấy trong sản phẩm này. Ô không
   mang nghĩa nào thì để trung tính, KHÔNG tô cho đẹp: tô xanh ô "có dự" là
   biến một câu trả lời thành một lời khen. */
.dstnso{display:grid;grid-template-columns:repeat(3,1fr);gap:7px}
.dstno{display:flex;flex-direction:column;align-items:center;justify-content:flex-start;
  gap:3px;min-width:0;text-align:center;background:var(--surf2);border-radius:10px;
  padding:10px 5px}
.dstno b{font-size:21px;font-weight:700;line-height:1.05;letter-spacing:-.02em}
.dstno i{font-style:normal;font-size:11px;line-height:1.35;color:var(--ink2)}
.dstno.go{background:var(--go-bg)}
.dstno.go b{color:var(--go)}
.dstno.due{background:var(--due-bg)}
.dstno.due b{color:var(--due)}

/* ── Ảnh chân dung / logo cho chứng chỉ (zone Lễ tốt nghiệp) ────────────────
   Ô xem trước có kích thước CỐ ĐỊNH và khai sẵn tỉ lệ: ảnh về sau không đẩy
   nội dung nhảy xuống, đúng bài học đã ghi cho sổ tay ("ảnh phải khai
   width/height hoặc aspect-ratio, không thì mỗi ảnh hiện ra lại đẩy nội dung
   nhảy và đọc trên điện thoại giật liên tục"). */
.tnanh{display:flex;gap:10px;margin:2px 0 4px}
.tnanh1{flex:1 1 0;min-width:0;background:var(--surf);border:1px solid var(--line);
        border-radius:12px;padding:10px;display:flex;flex-direction:column;gap:8px}
.tnanh-xem{aspect-ratio:1/1;border-radius:9px;background:var(--bg);
           border:1px dashed var(--line2);display:flex;align-items:center;
           justify-content:center;overflow:hidden}
.tnanh-xem img{width:100%;height:100%;object-fit:cover;display:block}
.tnanh-trong{color:var(--ink3);font-size:12px}
.tnanh-ok{color:var(--go);font-size:12px;font-weight:600}
.tnanh-cho{color:var(--ink3);font-size:12px;animation:tlnhay 1.1s ease-in-out infinite}
.tnanh-nut{display:flex;flex-direction:column;gap:4px;min-width:0}
.tnanh-nut>b{font-size:12.5px;line-height:1.3}
/* <label> bọc một <input type=file> ẩn: bấm vào nhãn là mở trình chọn tệp,
   không cần một dòng JS nào để nối hai thứ lại. */
.tnanh-chon{display:inline-block;text-align:center;cursor:pointer;
            font-size:12.5px;font-weight:600;padding:7px 9px;border-radius:9px;
            background:var(--ink);color:#fff;touch-action:manipulation}
.tnanh-chon:active{transform:scale(.985)}
.tnanh-mo{font-size:11.5px;color:var(--ink3);text-decoration:none}
.tnanh-loi{font-size:11.5px;color:var(--due);line-height:1.45}

/* ═══ Gala tối 26/9 (/totnghiep từ ngày Lễ) ═══
   Không dùng var(--num) cho "Bàn VIP3" / "Lượt 5": Space Grotesk thiếu glyph
   tiếng Việt ("à", "ư") nên chữ rơi về monospace dự phòng — bài học tiêu đề
   /giao-thuong. Chỉ con số thuần mới được dùng phông ấy. */
.gl-head::after{content:'🥂'}
.gl-toi{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:0 0 18px}
.gl-toi>div{background:var(--surf);border-radius:var(--r);box-shadow:var(--sh);padding:13px 14px}
.gl-toi span{display:block;font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink3);margin-bottom:4px}
.gl-toi b{display:block;font-size:26px;font-weight:700;letter-spacing:-.02em;line-height:1.1;color:var(--le)}
.gl-toi b small{font-size:14px;font-weight:600;color:var(--ink3)}
.gl-toi b.nho{font-size:16px;color:var(--ink2)}
.gl-toi .gl-mc,.gl-toi .gl-da{grid-column:1/-1}
.gl-toi .gl-mc{background:var(--le-bg);color:var(--le);font-weight:600;font-size:14.5px}
.gl-toi .gl-da{font-size:14.5px;font-weight:600;line-height:1.4}
.gl-khong{background:var(--due-bg);color:var(--due);border-radius:var(--r);padding:13px 14px;
  font-size:13.5px;line-height:1.5;margin:0 0 18px}
.gl-list .gl-d{display:flex;align-items:flex-start;gap:10px;padding:10px 14px;border-top:1px solid var(--line)}
.gl-list .gl-d:first-child{border-top:0}
.gl-d.toi{background:var(--le-bg)}
.gl-t{flex:1;min-width:0}
.gl-t b{display:block;font-size:14.5px;font-weight:600;line-height:1.35}
.gl-t b i{font-style:normal;font-weight:500;color:var(--le)}
.gl-t span{display:block;font-size:12px;color:var(--ink3);line-height:1.4;margin-top:1px}
.gl-n{flex:0 0 auto;max-width:42%;text-align:right;font-size:12px;font-weight:600;color:var(--le);
  line-height:1.4;padding-top:2px}
#glTim{margin:0 0 10px}
.gl-rong{padding:4px 2px 12px}
.gl-bansec .tnbody.gl-list{padding:0}
.tncard>.eb{margin-top:20px}
/* Khối "Bảo vệ đề tài chiều nay" (veBaoVe, migrations/0051) mượn khuôn .gl-toi:
   ô số đề tài trải cả hàng (không có ô bàn đứng cạnh), và tên "bạn" trong dòng
   người cùng bảo vệ phải là chữ thường — .gl-toi b vốn là con số 26px. */
.gl-bv .gl-luot{grid-column:1/-1}
.gl-bv .gl-da b,.gl-t span b{display:inline;font-size:inherit;letter-spacing:0;line-height:inherit;
  font-weight:700;color:var(--le)}
.gl-bv .gl-da:last-child{font-weight:500}
/* Ngày Lễ 26/9: thanh "Tiến độ bài" và con số đếm ngược rút khỏi đầu trang
   (laNgayLe() trong app.js). Chữ "Hôm nay bế giảng" thay chỗ "ngày đến bảo vệ". */
body.ngayle .rail,body.ngayle .raillb,body.ngayle .cd .n{display:none}
body.ngayle .hd{padding-bottom:10px}
body.ngayle .cd .u{font-size:12px;font-weight:600;color:var(--le)}
