/* BTS Dev — Control Tower · dark ops theme, responsive web + Android PWA */
:root{
  --bg:#0b1520; --panel:#10202e; --panel2:#152a3b; --line:#1f3a4f;
  --txt:#d7e3ec; --dim:#7d93a5; --teal:#17a2b8; --navy:#0e4f63;
  --red:#e05252; --orange:#e8973a; --green:#37b06f; --yellow:#d8c53a; --gray:#5a6f80;
}
*{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--txt);font:14px/1.45 'Segoe UI',system-ui,sans-serif;-webkit-font-smoothing:antialiased}
a{color:var(--teal);text-decoration:none}
.loading{padding:40px;text-align:center;color:var(--dim)}

/* login */
.login-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:16px}
.login-box{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:28px;width:100%;max-width:380px}
.login-box h1{font-size:19px;margin-bottom:4px}
/* LOGO THẬT TRÊN MÀN ĐĂNG NHẬP (#199, 17/08/2026). Trước đây chỗ này là chữ emoji 📡 — biểu tượng
   ngoài màn hình điện thoại đã là logo CTO gửi từ đợt #177, nhưng vào trong phần mềm lại quay về
   một ký tự chung chung. CTO mở APK 0.7.7 và nói «chưa thấy đổi logo mới»; ông nhìn vào ĐÂY.
   Dùng thẳng /icon/icon-192.png — cùng một tệp với biểu tượng cài đặt, nên không có đường nào để
   hai chỗ lệch nhau. */
.login-logo{display:block;width:96px;height:96px;margin:0 auto 12px;border-radius:20px}
.login-box .sub{color:var(--dim);font-size:12px;margin-bottom:18px}
.login-box .err{color:var(--red);font-size:12px;min-height:16px;margin-top:8px}

/* layout */
.shell{display:flex;min-height:100vh}
/* Thanh ☰ và nền mờ CHỈ tồn tại trên màn hẹp — ẩn mặc định, media query dưới mới bật lên.
   Khai ở đây chứ không chỉ trong media query: thẻ không có luật nào thì hiện ra như khối trống
   trên màn rộng, và lỗi kiểu ấy chỉ lộ khi ai đó mở trên máy tính. */
.mbar,.mbd{display:none}
.side{width:212px;background:var(--panel);border-right:1px solid var(--line);padding:14px 0;flex-shrink:0;
  display:flex;flex-direction:column;position:sticky;top:0;height:100vh;overflow-y:auto}
.side .brand{padding:0 16px 14px;font-weight:700;font-size:15px;border-bottom:1px solid var(--line)}
.side .brand small{display:block;color:var(--dim);font-weight:400;font-size:10.5px}
.side nav{flex:1;padding-top:8px}
.side nav a{display:block;padding:9px 16px;color:var(--txt);font-size:13.5px;border-left:3px solid transparent}
.side nav a.on{background:var(--panel2);border-left-color:var(--teal);color:#fff}
.side .foot{padding:10px 16px;border-top:1px solid var(--line);font-size:11.5px;color:var(--dim)}
/* Liên kết chân menu không gãy giữa chữ — hết chỗ thì cả «Cerrar sesión» xuống dòng, không còn «Cerrar»
   một dòng «sesión» một dòng ở khổ điện thoại 375 px (bấm thử góp ý b6030, 03-10-2026). */
.side .foot a{white-space:nowrap}
.main{flex:1;min-width:0;--main-dem:22px;padding:18px var(--main-dem)}
.topbar{display:flex;align-items:center;gap:10px;margin-bottom:16px;flex-wrap:wrap}
.topbar h2{font-size:17px;flex:1}
.userchip{background:var(--panel);border:1px solid var(--line);border-radius:20px;padding:5px 12px;font-size:12px;color:var(--dim)}
.userchip b{color:var(--txt)}

/* HÀNG NGANG DÙNG CHUNG — LUẬT NÀY TRƯỚC 22/08/2026 KHÔNG TỒN TẠI.

   app.js đặt class="row" ở 76 chỗ. Trong tệp này có 615 bộ chọn, 22 cái mang chữ «row»
   (.ms-row, .qc-row, .dlg-row, .bc-row, tr.row-red…) — và KHÔNG cái nào là `.row`. Tức là suốt
   từ đầu, 76 thẻ ấy chỉ là <div> khối bình thường; đo trên trình duyệt: getComputedStyle trả
   display:block.

   VÌ SAO KHÔNG AI THẤY. 36/76 chỗ tự ghi display:flex nội dòng nên vẫn đúng. Trong 40 chỗ còn
   lại, phần lớn có con là <button>/<span> — hai thứ vốn đã tự nằm ngang — nên nhìn qua vẫn ổn.
   Cái mất là mọi thuộc tính CHỈ CÓ NGHĨA VỚI HỘP FLEX mà tác giả đã viết ra và tin là có tác
   dụng: đo được 32 chỗ ghi gap:, 19 chỗ flex-wrap:, 11 chỗ align-items:, 3 chỗ justify-content:,
   cùng mấy con <span style="flex:1"> vốn định làm thanh đẩy. Tất cả rơi vào hư không.

   HỎNG THẤY ĐƯỢC BẰNG MẮT là 6 chỗ có con là <div> khối — thanh lọc xếp DỌC thay vì ngang:
   #/qc (app.js:12989), #/mailadmin (15912, 15935), #/quyen-thu (16298), KPI tỉnh (16632),
   #/danhba (23032). Đo trên b618, bề rộng 1053px: hàng lọc màn #/qc cao 222px thay vì 70px.

   BA THUỘC TÍNH, KHÔNG PHẢI MỘT:
     · display:flex     — việc chính.
     · align-items:center — KHÔNG để mặc định (stretch). Với stretch, một <button> cao 19px đứng
       cạnh ô nhập cao 35px bị KÉO GIÃN thành 35px; đo được đúng thế ở 8 hàng. center cũng chính
       là thứ 34/36 chỗ đã-có-flex tự ghi bằng tay.
     · flex-wrap:wrap   — bắt buộc. Thiếu nó, hàng nút màn #/mailadmin TRÀN NGANG ở bề rộng 375px
       (đo được scrollWidth > clientWidth).

   ĐO TRƯỚC/SAU TRÊN CẢ 76 CHỖ, hai bề rộng 1053px và 375px: không hàng nào tràn ngang thêm,
   không hàng nào cao lên quá 16px (ba chỗ +6..16px là do gap: bắt đầu có tác dụng — đúng ý người
   viết), 9 hàng gọn lại. 65-67 hàng đứng yên hoàn toàn.

   tools/soat-lop-css-mo-coi.py canh để loại hỏng này không tái diễn: nó kêu khi app.js dùng một
   lớp mà tệp này không có luật nào, và kêu khi một thẻ mang thuộc tính flex mà không phải hộp
   flex. Chính bộ ấy bắt được ca này — cùng 17 lớp mồ côi khác. */
.row{display:flex;align-items:center;flex-wrap:wrap}

/* controls */
input,select,textarea,button{font:inherit;color:var(--txt)}
input,select,textarea{background:var(--panel2);border:1px solid var(--line);border-radius:7px;padding:8px 10px;width:100%}
input:focus,select:focus,textarea:focus{outline:1px solid var(--teal)}
label{display:block;font-size:11.5px;color:var(--dim);margin:10px 0 4px}
button{cursor:pointer;border:none;border-radius:7px;padding:8px 14px;background:var(--navy);color:#fff;font-size:13px}
button:hover{filter:brightness(1.15)}
button.pri{background:var(--teal);color:#04222a;font-weight:600}
button.ghost{background:transparent;border:1px solid var(--line);color:var(--dim)}
button.danger{background:var(--red)}
button.small{padding:4px 9px;font-size:11.5px}
button:disabled{opacity:.4;cursor:not-allowed}

/* cards + tables */
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;margin-bottom:16px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:12px 14px}
.card .v{font-size:24px;font-weight:700}
.card .k{font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:.4px}
.card.click{cursor:pointer}
.card.click:hover{border-color:var(--teal)}
.card.red .v{color:var(--red)} .card.orange .v{color:var(--orange)} .card.green .v{color:var(--green)}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:14px;margin-bottom:16px}
.panel h3{font-size:13px;color:var(--dim);text-transform:uppercase;letter-spacing:.5px;margin-bottom:10px}
.tbl-wrap{overflow-x:auto}
table{width:100%;border-collapse:collapse;font-size:13px;white-space:nowrap}
th{color:var(--dim);font-size:11px;text-transform:uppercase;letter-spacing:.4px;text-align:left;padding:6px 10px;border-bottom:1px solid var(--line)}
td{padding:7px 10px;border-bottom:1px solid rgba(31,58,79,.5)}
tr.click{cursor:pointer} tr.click:hover td{background:var(--panel2)}
/* Ô BẤM ĐƯỢC cũng phải nói rằng nó bấm được. Chỉ có tr.click mang bàn tay, nên 20 ô số bấm được
   của menu Điều hành dự án bày mũi tên như một ô chữ chết — mà chính chúng là đường vào danh
   sách chi tiết và tệp Excel. Đặt sau tr.click để hàng bấm được không bị ô bên trong đè ngược. */
td.click, th.click{cursor:pointer}
/* Hàng TỔNG cuối bảng — phải tách khỏi các dòng dữ liệu bằng mắt, nếu không người đọc lướt qua và
   cộng lại bằng tay. Đường kẻ trên đậm hơn kẻ dòng thường, nền hơi nổi, và dính đáy khi cuộn dọc. */
tr.tong td{background:var(--panel2);border-top:2px solid var(--line);font-weight:600}
tfoot tr.tong td{position:sticky;bottom:0;z-index:1}
.num{text-align:right;font-variant-numeric:tabular-nums}

/* ══ FREEZE PANES KIỂU EXCEL — ÁP CHO MỌI BẢNG SỐ LIỆU ══════════════════════
   CTO 04/08/2026: «mọi bảng số liệu đều phải cố định tiêu đề cột và tiêu đề dòng,
   lăn sang phải/trái thì số liệu trượt còn tiêu đề đứng yên; lăn hết rồi mới
   trượt cả trang sang bảng tiếp theo».

   TRƯỚC ĐÂY chỉ 15/77 bảng có class .freeze, và không bảng nào giữ được HÀNG
   tiêu đề. Đọc một bảng rộng thành ra phải kéo qua kéo lại mới biết con số đang
   nằm ở vùng nào, mốc nào — đúng cái CTO mô tả.

   NAY BỎ HẲN VIỆC PHẢI GẮN CLASS: quy tắc bám vào .tbl-wrap, tức là bám vào
   "bảng nào nằm trong khung cuộn". Bảng viết sau này tự có, không ai phải nhớ.

   BA LỚP CHỒNG NHAU, thứ tự z-index không được đổi:
     ô góc trên-trái  z4  — phải nằm trên cả hai dải, nếu không nó bị dải kia đè
     hàng tiêu đề     z3
     cột đầu          z2
     ô dữ liệu        (mặc định)

   VÌ SAO DÙNG box-shadow inset THAY CHO border: bảng để border-collapse:collapse,
   mà đường viền của ô dính (sticky) trong bảng collapse thuộc về "lưới bảng" chứ
   không thuộc ô — nên nó trôi theo khi cuộn và để lại vệt hở. Bóng inset vẽ bên
   trong ô nên đi cùng ô. */
.tbl-wrap table th:first-child,
.tbl-wrap table td:first-child{
  position:sticky;left:0;z-index:2;background:var(--panel);
  box-shadow:6px 0 8px -4px rgba(0,0,0,.55);
}
.tbl-wrap table th{
  position:sticky;top:0;z-index:3;background:var(--panel);
  box-shadow:inset 0 -1px 0 var(--line);
}
/* ── TIÊU ĐỀ HAI DÒNG: PHẢI CHO CẢ <thead> DÍNH, KHÔNG CHỈ TỪNG Ô ────────────
   Luật `th{top:0}` ở trên đúng cho bảng có MỘT dòng tiêu đề. Bảng có HAI dòng
   (nhóm cột ở trên, cột con ở dưới) thì CẢ HAI dòng cùng đòi top:0 — dòng dưới
   trèo lên đè mất dòng trên. CTO báo 15/08/2026 ở «Bảng điều hành»: cuộn xuống
   là hai tên nhóm biến mất, còn mấy ô rowspan trải qua hai dòng thì lệch dọc,
   đọc rất dễ nhầm cột.

   ĐO TRÊN TRÌNH DUYỆT THẬT, không đoán. Trước khi sửa: dòng 1 và dòng 2 đều nằm
   ở y=60 — tức là chồng khít lên nhau. Sau khi thêm luật này: dòng 1 ở y=60,
   dòng 2 ở y=88, cách nhau đúng chiều cao dòng 1. Kéo ngang 400px thì cột trạm
   vẫn đóng băng ở x=31 như cũ.

   VÌ SAO CHỈ THÊM, KHÔNG SỬA LUẬT CŨ: <thead> dính thì nó thành khối chứa của
   các <th> bên trong, nên ô dòng 2 không trèo lên trên dòng 1 được nữa — luật cũ
   giữ nguyên và bảng một dòng tiêu đề không đổi một pixel nào (đã đo). Trình
   duyệt quá cũ không hiểu dính-trên-thead thì rơi về đúng hành vi hôm nay, chứ
   không mất tính năng.

   TOÀN HỆ CHỈ CÓ HAI BẢNG tiêu đề hai dòng — «Bảng điều hành» và «Trạm × 12 mốc»,
   cả hai đều trong màn Kế hoạch tháng — nhưng luật đặt ở đây chứ không đặt riêng
   cho hai bảng ấy, để bảng thứ ba viết sau này không vấp lại. */
.tbl-wrap table thead{position:sticky;top:0;z-index:3}
.tbl-wrap table th:first-child{
  z-index:4;box-shadow:6px 0 8px -4px rgba(0,0,0,.55), inset 0 -1px 0 var(--line);
}
.tbl-wrap table tr.click:hover td:first-child{background:var(--panel2)}

/* Ô dính phải có NỀN ĐẶC, nếu không dữ liệu bên dưới hiện xuyên qua khi trượt.
   Bảng nằm trong .panel2 thì lấy nền của panel2, không thì lấy nền panel. */
.panel2 .tbl-wrap table th, .panel2 .tbl-wrap table td:first-child{background:var(--panel2)}

/* ── VÌ SAO KHUNG PHẢI CÓ TRẦN CHIỀU CAO ──────────────────────────────────
   Đây là chỗ dễ làm sai nhất, và tôi đã đo trên trình duyệt thật chứ không đoán.

   position:sticky bám vào VÙNG CUỘN GẦN NHẤT. Khung .tbl-wrap khai overflow-x:auto,
   mà theo chuẩn CSS thì overflow-y "visible" bị ép thành "auto" khi trục kia không
   phải visible — nên khung ĐÃ LÀ vùng cuộn dọc rồi. Chỉ có điều nó không bị giới
   hạn chiều cao nên chẳng bao giờ cuộn: hàng tiêu đề bám vào một vùng đứng yên,
   còn TRANG mới là thứ cuộn — kết quả là tiêu đề trôi đi mất y như cũ.

   Đo được: cuộn trang 500px thì hàng tiêu đề đi từ y=152 xuống y=-348, tức là ra
   khỏi màn hình. Thử overflow-y:clip để khung thôi làm vùng cuộn — engine ở đây
   quy về "hidden", vẫn là vùng cuộn, không ăn thua.

   Cho khung một trần chiều cao là biến nó thành vùng cuộn dọc THẬT. Đo lại: cuộn
   500px trong khung thì tiêu đề đứng nguyên y=152 còn các dòng trượt lên dưới nó —
   đúng freeze panes của Excel.

   72vh: đủ cao để thấy nhiều dòng, vẫn chừa chỗ cho người dùng biết còn nội dung
   phía dưới trang. Bảng ngắn hơn trần thì max-height không chạm tới, không sinh
   thanh cuộn thừa.

   KHÔNG đặt overscroll-behavior:none. Lăn hết bảng rồi thì phải trôi tiếp sang
   bảng sau — chính là điều CTO dặn; chặn chaining là chặn mất bước đó. */
.tbl-wrap{max-height:72vh;overscroll-behavior:auto}

/* badges */
.b{display:inline-block;border-radius:5px;padding:2px 7px;font-size:11px;font-weight:600}
.b.red{background:rgba(224,82,82,.16);color:var(--red)}
.b.orange{background:rgba(232,151,58,.16);color:var(--orange)}
.b.green{background:rgba(55,176,111,.16);color:var(--green)}
.b.gray{background:rgba(90,111,128,.2);color:var(--dim)}
.b.teal{background:rgba(23,162,184,.14);color:var(--teal)}
.b.yellow{background:rgba(216,197,58,.14);color:var(--yellow)}

/* ══ LỚP MỒ CÔI ĐÃ VÁ (22/08/2026, cùng đợt với .row) ═══════════════════════════════════════
   tools/soat-lop-css-mo-coi.py tìm ra 14 lớp nữa được app.js dùng mà tệp này không có luật nào.
   Chúng im lặng y như .row: trình duyệt bỏ qua một lớp không tồn tại, không cảnh báo gì cả.
   Bốn lớp đã xử bằng cách SỬA app.js (đổi về tên sẵn có) nên không xuất hiện dưới đây:
     .r → .num   ·   .btn/.sm/.primary → button.small / button.pri   ·   .grey → .gray
     .wrap → bỏ (thừa, vì .row đã có flex-wrap)   ·   .amber → .orange (bảng màu không có --amber)
   Riêng năm thẻ ở màn Đội thi công đặt SỐ vào .k và NHÃN vào .l — ngược với 106 chỗ khác trong
   app.js — nên `.card.red .v` không bao giờ khớp và con số hiện ra bé tí, màu xám. Đã sửa ở
   app.js chứ không thêm luật cho .l, vì cái sai nằm ở chỗ gọi. */

/* MÀU CHỮ TRẦN. 35 chỗ trong app.js viết class="red" / "green" / "yellow"… không kèm .b hay
   .card — mà mọi luật màu đang có đều là bộ chọn GHÉP (.b.red, .chip.on.green, .vg-nhan.orange,
   .m12-dot.green…). Nên chữ đáng lẽ đỏ vẫn hiện màu thường, và không ai thấy vì nội dung vẫn
   đúng, chỉ mất màu. Đặt độ đặc hiệu (0,1,0) — thấp hơn mọi luật ghép sẵn có, nên không luật nào
   bị cướp mất. */
.red{color:var(--red)} .green{color:var(--green)} .orange{color:var(--orange)}
.yellow{color:var(--yellow)} .teal{color:var(--teal)} .gray{color:var(--dim)}

/* .dim — CHỈ đổi màu. Khác .muted ở chỗ không ép font-size:12px: .dim dùng trên <summary> và các
   khối chữ cỡ thường ở màn Khảo sát đất, ép cỡ vào là thu nhỏ chữ đang đúng. */
.dim{color:var(--dim)}

/* .ph — tiêu đề nhỏ mở đầu một .panel (màn Đội thi công, Sổ dữ liệu). */
.ph{font-weight:600;margin-bottom:10px}

/* .big — con số lớn đứng dưới một nhãn .muted.sub2 (màn Ảnh vật tư). Màu lấy từ nhóm màu chữ
   trần ở trên: class="big green" / "big orange" / "big red". */
.big{font-size:22px;font-weight:700;line-height:1.2}

/* .clk — hàng bảng bấm được. Không có luật nghĩa là con trỏ vẫn là mũi tên, người dùng không có
   dấu hiệu nào cho biết bấm được vào hàng. */
.clk{cursor:pointer}
.clk:hover>td{background:var(--panel2)}

/* .fld — ô nhập có nhãn xếp TRÊN (hộp thoại Nhà thầu). Thiếu luật thì <label> nằm ngang, nhãn và
   ô nhập dính vào nhau thành một dòng chữ chạy. */
.fld{display:flex;flex-direction:column;gap:4px}

/* .dot — chấm trạng thái đứng trước tên cảnh báo. Là <span> RỖNG, nên không luật thì nó không có
   kích thước và biến mất hoàn toàn — mất luôn cột màu phân biệt đỏ/cam/xám. */
.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px;
  vertical-align:middle;background:var(--gray)}
.dot.red{background:var(--red)} .dot.orange{background:var(--orange)}
.dot.gray{background:var(--gray)} .dot.green{background:var(--green)}

/* checklist */
.ck-sec{margin:14px 0 6px;font-weight:700;font-size:13px;color:var(--teal)}
.ck-item{display:flex;gap:10px;align-items:flex-start;padding:9px 10px;border:1px solid var(--line);border-radius:8px;margin-bottom:6px;background:var(--panel2)}
.ck-item.hidden-ok{display:none}
.ck-main{flex:1;min-width:0}
.ck-main .cp{font-size:13.5px}
.ck-main .std{font-size:11.5px;color:var(--dim);margin-top:2px}
.ck-meta{font-size:10.5px;color:var(--dim);margin-top:3px;display:flex;gap:8px;flex-wrap:wrap}
.tri{display:flex;gap:4px;flex-shrink:0}
.tri button{padding:6px 10px;font-size:12px;background:var(--panel);border:1px solid var(--line);color:var(--dim)}
.tri button.ok.sel{background:var(--green);color:#062;border-color:var(--green);color:#02180c}
.tri button.nok.sel{background:var(--red);border-color:var(--red);color:#fff}
.tri button.ny.sel{background:var(--gray);border-color:var(--gray);color:#fff}
/* NA = không áp dụng. Màu nhạt hơn NY để phân biệt "chưa làm" với "không có ở trạm này". */
.tri button.na.sel{background:var(--dim);border-color:var(--dim);color:#fff}
.ck-note input{margin-top:6px;font-size:12px;padding:5px 8px}

/* milestones timeline */
.ms-row{display:flex;align-items:center;gap:10px;padding:7px 0;border-bottom:1px solid rgba(31,58,79,.5)}
.ms-dot{width:12px;height:12px;border-radius:50%;background:var(--gray);flex-shrink:0}
.ms-dot.done{background:var(--green)} .ms-dot.late{background:var(--red)} .ms-dot.next{background:var(--teal);box-shadow:0 0 6px var(--teal)}
.ms-label{flex:1;min-width:0;font-size:13px}
.ms-dates{font-size:11.5px;color:var(--dim);text-align:right;flex-shrink:0}

/* text dài: cắt gọn + tooltip thay vì bóp cột rơi chữ */
.ellip{display:inline-block;max-width:340px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}
.sub2{font-size:11px;line-height:1.3}

/* kỳ điều hành: chip chọn tháng/quý + phễu tiến độ */
.chips{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.chip{padding:5px 13px;border-radius:16px;background:var(--panel);border:1px solid var(--line);cursor:pointer;font-size:12.5px;color:var(--dim);user-select:none}
.chip:hover{border-color:var(--teal)}
.chip.on{background:var(--teal);color:#04222a;font-weight:700;border-color:var(--teal)}
/* NUT DAT / KHONG DAT cua dot kiem an toan (viec #142).
   `.chip.on` mac dinh to XANH NGOC — dung cho «khong ap dung», nhung to mot muc KHONG DAT bang
   mau ay thi ca bang doc luot qua deu ra ve on. Mau phai noi dung thu no dang noi. */
/* MUC DOI ANH MA CHUA CO ANH (viec #143). Vien cam ben trai, KHONG to ca dong:
   380/507 muc trong 11 luot dang chay dang thieu anh — to nen ca 380 dong thi ca man do rực
   va nguoi doc thoi nhin. Vien mong thi luot mat van bat duoc ma khong at het moi thu khac. */
.qc-row.thieu-anh{border-left:3px solid var(--orange)}
.qc-row.thieu-anh.chan-cung{border-left-color:var(--red)}   /* b821: NGHIÊM TRỌNG thiếu ảnh — chặn nộp */
.qc-anh{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.chip.on.green{background:var(--green);color:#04240f;border-color:var(--green)}
.chip.on.red{background:var(--red);color:#2b0606;border-color:var(--red)}
.chip:disabled{cursor:default;opacity:.55}
.chip:disabled:hover{border-color:var(--line)}
.chip.on:disabled{opacity:.8}
/* Huy hieu «da doi N lan» tren o dang ky — nho, khong bam duoc, chi de mat lia thay ngay. */
.pill{display:inline-block;padding:1px 7px;border-radius:10px;background:var(--panel2);border:1px solid var(--orange);color:var(--orange);font-size:11px;white-space:nowrap}
/* O dang ky cua ky DA CHOT: van go duoc (V277) nhung phai ghi ly do — vien cam de nhac mat. */
input.dk-khoa{border-color:var(--orange)}
/* O GOI Y THEO SLA (#4): dien san ngay suy ra nhung CHUA phai cam ket — vien dut net + chu mo de
   phan biet voi ngay tinh da chot. Bam «Ap SLA» hoac Nop moi thanh cam ket. */
input.dk-goiY{border-style:dashed;border-color:var(--dim);color:var(--dim);font-style:italic;opacity:.85}
.feed-row{display:flex;gap:10px;padding:5px 0;border-bottom:1px solid rgba(31,58,79,.4);font-size:12.5px;align-items:baseline}
.feed-row.click{cursor:pointer} .feed-row.click:hover{background:var(--panel2)}
.box-gps{border:1px solid var(--line);border-left:4px solid var(--orange);background:var(--panel2);padding:7px 10px;border-radius:6px;font-size:12.5px}
/* ── BẢN ĐỒ: NHUỘM LẠI ĐỒ CỦA LEAFLET CHO KHỚP NỀN TỐI (việc #179) ─────────────────────────────
   HAI LUẬT NGẦM phải nhớ khi sửa khối này, vì cả hai đều làm mã trông đúng mà chạy sai:
   (1) THỨ TỰ NẠP — leaflet.css KHÔNG nằm trong index.html, napLeaflet() chèn nó lúc mở bản đồ đầu
       tiên. Nếu chèn kiểu appendChild thì nó đứng SAU style.css và thắng mọi luật cùng hạng của
       mình. Ba dòng dưới đây (nền bản đồ, hộp thoại) viết từ lâu nhưng CHƯA BAO GIỜ có hiệu lực vì
       lý do đó — nền vẫn #ddd, hộp thoại vẫn trắng. napLeaflet() nay chèn leaflet.css TRƯỚC
       style.css; đừng đổi lại thành appendChild.
   (2) HẠNG CHỌN — leaflet.css có sẵn một loạt luật hai lớp «.leaflet-touch …», «.leaflet-retina …»,
       «.leaflet-container .leaflet-control-attribution». Máy Android đều dính .leaflet-touch, tức
       là đúng máy của anh em ngoài trạm. Nên mọi luật dưới đây phải mang tiền tố .leaflet-container
       cho đủ hai lớp, bằng hạng với chúng, rồi thắng nhờ thứ tự ở (1). Bỏ tiền tố đi là hỏng đúng
       trên điện thoại và vẫn đẹp trên máy tính — kiểu lỗi khó thấy nhất. */
/* KHUNG BẢN ĐỒ PHẢI LÀ MỘT TẦNG RIÊNG — nếu không, chấm trạm đè lên menu ☰ (việc #191, mục 5).
   CTO báo tối 17/08: «trong APK, mở menu ra thì các chấm trạm vẫn nằm đè lên menu».
   Leaflet tự đặt z-index cho từng khay của nó: khay chấm 400, khay nhãn 650, khay hộp thoại 700,
   khay nút điều khiển 1000. Menu trượt .side chỉ có z-index 60, và giữa hai bên KHÔNG có tầng
   ngăn nào — .main lẫn .panel đều không mở tầng riêng — nên bốn con số kia đấu thẳng với 60 và
   thắng sạch. Không phải lỗi của Leaflet: nó đánh số trong phạm vi bản đồ, mình mới là bên để
   phạm vi ấy hở ra ngoài.
   Cách chữa KHÔNG phải là nâng .side lên 1001 — làm thế là chạy đua với một thư viện mình không
   sở hữu, và lần sau nó thêm khay mới thì lỗi quay lại. Cách chữa là ĐÓNG phạm vi: mở một tầng
   riêng ngay ở khung bản đồ, mọi con số bên trong ở lại bên trong. Sau đó bản đồ nằm đúng mức
   thường của dòng chảy, thua mọi thứ có z-index — menu 60, hộp thoại 900, thanh khẩn 9999.
   Khai CẢ HAI cách mở tầng: `isolation` gọn và đúng ý nhất, còn `position:relative;z-index:0` thì
   máy Android đời cũ nào cũng hiểu. Máy hiểu cả hai thì kết quả y hệt, không cái nào hại cái nào. */
.leaflet-container{background:#0b1520;isolation:isolate;position:relative;z-index:0}
.leaflet-popup-content-wrapper,.leaflet-popup-tip{background:var(--panel);color:var(--txt)}
.leaflet-popup-content a{color:var(--teal)}

/* Hộp chọn nền bản đồ. Leaflet dựng mỗi dòng bằng <label><span><input><span>tên</span></span>, và
   ba luật chung ở đầu tệp này rơi thẳng vào đó: input{width:100%;padding:8px 10px;border;
   background} biến ô tròn thành khối xám chiếm hết dòng, label{display:block;margin:10px 0 4px}
   đẩy chữ xuống dưới ô. Kết quả là mấy khối xám xếp dọc — CTO xem ảnh và nói thẳng là xấu. Nên
   phải GỠ đúng ba luật đó rồi dựng lại, chứ không phủ màu lên trên. */
.leaflet-container .leaflet-control-layers{
  background:rgba(16,32,46,.95);border:1px solid var(--line);border-radius:10px;
  box-shadow:0 8px 24px rgba(0,0,0,.5);backdrop-filter:blur(6px);color:var(--txt)}
.leaflet-container .leaflet-control-layers-expanded{
  padding:9px 10px 8px;min-width:180px;background:rgba(16,32,46,.95);color:var(--txt)}
.leaflet-container .leaflet-control-layers-toggle{
  width:34px;height:34px;border-radius:9px;background-size:20px 20px;
  background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2317a2b8' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3 3 8l9 5 9-5-9-5z'/%3E%3Cpath d='M3 13l9 5 9-5'/%3E%3C/svg%3E")}
.leaflet-container .bd-tieu{font-size:10px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--dim);margin:1px 0 7px;padding:0 2px 7px;border-bottom:1px solid var(--line)}
.leaflet-container .leaflet-control-layers label{
  display:block;margin:0;padding:0;font-size:13px;line-height:1.3;color:var(--txt);
  border-radius:7px;cursor:pointer;transition:background .12s}
.leaflet-container .leaflet-control-layers label>span{
  display:flex;align-items:flex-start;gap:9px;padding:6px 8px;border-radius:7px}
.leaflet-container .leaflet-control-layers label:hover{background:var(--panel2)}
/* Dòng đang chọn phải tự sáng lên: Leaflet không gắn lớp nào cho nó, chỉ có thể bắt qua ô đã tích.
   :has() thiếu thì cùng lắm là mất vệt sáng, ô tròn teal vẫn chỉ đúng dòng đang dùng. */
.leaflet-container .leaflet-control-layers label:has(input:checked){background:rgba(23,162,184,.14)}
.leaflet-container .leaflet-control-layers label:has(input:checked)>span{
  box-shadow:inset 2px 0 0 var(--teal)}
.leaflet-container .leaflet-control-layers label input{
  appearance:none;-webkit-appearance:none;flex:0 0 auto;
  width:14px;height:14px;margin:2px 0 0;padding:0;border-radius:50%;
  border:1.5px solid var(--dim);background:transparent;position:relative;box-sizing:border-box}
.leaflet-container .leaflet-control-layers label input[type=checkbox]{border-radius:4px}
.leaflet-container .leaflet-control-layers label input:checked{
  border-color:var(--teal);background:var(--teal);box-shadow:0 0 0 3px rgba(23,162,184,.18)}
.leaflet-container .leaflet-control-layers label input[type=radio]:checked::after{
  content:'';position:absolute;inset:3px;border-radius:50%;background:#0b1520}
.leaflet-container .leaflet-control-layers label input[type=checkbox]:checked::after{
  content:'';position:absolute;left:3.5px;top:.5px;width:3.5px;height:7.5px;
  border:solid #0b1520;border-width:0 2px 2px 0;transform:rotate(45deg)}
.leaflet-container .leaflet-control-layers label input:focus-visible{
  outline:2px solid var(--teal);outline-offset:2px}
/* Tên hãng ảnh — thứ CTO cần để chọn «chỗ này hãng nào nét hơn», nên nó phải đọc được. */
.leaflet-container .bd-hang{display:block;font-size:10.5px;color:var(--dim);margin-top:2px;
  letter-spacing:.01em}
.leaflet-container .leaflet-control-layers-separator{border-top:1px solid var(--line);margin:7px 2px}
/* Dải cảnh báo «đã vượt mức bản đồ gốc»: ảnh đang phóng to nên mờ, vẽ ranh có sai số vị trí. */
.leaflet-container .bd-vuot{max-width:min(78vw,340px);background:rgba(180,83,9,.94);color:#fff;
  font-size:12px;line-height:1.35;padding:6px 9px;border-radius:8px;box-shadow:0 6px 18px rgba(0,0,0,.5);
  border:1px solid rgba(255,255,255,.25)}
/* Nút thu phóng và dòng ghi công: để nguyên là hai mảng trắng chói giữa nền tối. */
.leaflet-container .leaflet-bar{border:1px solid var(--line);border-radius:9px;overflow:hidden;
  box-shadow:0 8px 24px rgba(0,0,0,.5)}
.leaflet-container .leaflet-bar a,.leaflet-container .leaflet-bar a:focus{
  background:rgba(16,32,46,.95);color:var(--txt);border-bottom-color:var(--line)}
.leaflet-container .leaflet-bar a:hover{background:var(--panel2);color:var(--teal)}
.leaflet-container .leaflet-bar a.leaflet-disabled{background:rgba(16,32,46,.8);color:var(--gray)}
/* NÚT ◎ «VỊ TRÍ CỦA TÔI» + CHẤM XANH NHẤP NHÁY (b870). Nút cùng bộ với nút thu phóng (nền tối, biểu
   tượng teal); đang bắt GPS thì biểu tượng quay và chữ tiến độ hiện bên trái; đang bám theo thì sáng hẳn
   lên. Nút là <button> nên phải đè luật button chung ở đầu tệp (padding, nền navy, bo góc). Chấm là
   divIcon HTML chứ không vẽ canvas — màn Bản đồ trạm vẽ canvas, mà canvas không chạy hoạt hoạ CSS. Chấm và
   vòng sóng KHÔNG nhận cú chạm: trên màn khảo sát mọi cú chạm là cắm tâm / cắm đỉnh. */
.leaflet-container .dv-hop{display:flex;align-items:center;gap:6px}
/* Nút «về trạm / về các trạm» (b880) đứng trên ◎: cùng khuôn nút, hộp không có chữ tiến độ bên trái. */
.leaflet-container .dv-ve-hop{margin-bottom:8px}
.leaflet-container .dv-nut{display:flex;align-items:center;justify-content:center;width:34px;height:34px;
  margin:0;padding:0;border:0;border-radius:0;background:rgba(16,32,46,.95);color:var(--teal);cursor:pointer}
.leaflet-container .dv-nut:hover{background:var(--panel2)}
.leaflet-container .dv-nut:focus-visible{outline:2px solid var(--teal);outline-offset:-2px}
.leaflet-container .dv-nut.dv-theo{background:var(--teal);color:#0b1520}
.leaflet-container .dv-nut.dv-dang svg{animation:dv-quay 1.1s linear infinite}
.leaflet-container .dv-chu{max-width:min(60vw,260px);background:rgba(16,32,46,.95);color:var(--txt);
  font-size:12px;line-height:1.3;padding:5px 9px;border-radius:8px;border:1px solid var(--line);
  box-shadow:0 6px 18px rgba(0,0,0,.5)}
.leaflet-container .dv-cham{pointer-events:none}
.leaflet-container .dv-cham i{position:absolute;border-radius:50%;pointer-events:none}
.leaflet-container .dv-cham .dv-loi{left:4px;top:4px;width:14px;height:14px;box-sizing:border-box;
  background:#1a73e8;border:2px solid #fff;box-shadow:0 0 4px rgba(0,0,0,.55)}
.leaflet-container .dv-cham .dv-song{left:-9px;top:-9px;width:40px;height:40px;
  background:rgba(26,115,232,.45);animation:dv-nhip 1.6s ease-out infinite}
.leaflet-container .dv-cham.dv-cu .dv-loi{background:#9aa0a6}
.leaflet-container .dv-cham.dv-cu .dv-song{animation:none;opacity:0}
@keyframes dv-nhip{0%{transform:scale(.35);opacity:.9}80%,100%{transform:scale(1);opacity:0}}
@keyframes dv-quay{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){
  .leaflet-container .dv-cham .dv-song{animation:none;opacity:.3;transform:scale(.6)}
  .leaflet-container .dv-nut.dv-dang svg{animation:none}}
.leaflet-container .leaflet-control-attribution{
  background:rgba(11,21,32,.8);color:var(--dim);font-size:10.5px;border-radius:7px 0 0 0;
  padding:2px 7px}
.leaflet-container .leaflet-control-attribution a{color:var(--teal)}

/* ── TAY CẦM CỦA BÚT VẼ RANH ĐẤT (việc #180) ───────────────────────────────────────────────────
   Đỉnh phải ĐỦ TO ĐỂ CHẠM TRÚNG bằng ngón tay đeo găng giữa nắng Tanzania, nhưng đủ nhỏ để không
   che mất ranh đất đang vẽ. Nên: chấm nhìn thấy 12px, còn vùng chạm nới ra 28px bằng một lớp giả
   trong suốt — mắt thấy nhỏ, ngón tay thấy to. Vẽ 6 đỉnh sát nhau mà mỗi đỉnh chiếm 28px thật thì
   chúng dính vào nhau thành một mảng, không còn nắn được đỉnh nào. */
.ksd-dinh, .ksd-tam{background:none;border:0}
.ksd-dinh::before{content:'';position:absolute;left:2px;top:2px;width:12px;height:12px;
  border-radius:50%;background:var(--orange);border:2px solid #0b1520;
  box-shadow:0 0 0 1px var(--orange)}
.ksd-dinh::after,.ksd-tam::after{content:'';position:absolute;left:-6px;top:-6px;
  width:28px;height:28px;border-radius:50%}
.ksd-tam::before{content:'';position:absolute;left:2px;top:2px;width:18px;height:18px;
  border-radius:50%;background:var(--teal);border:3px solid #0b1520;
  box-shadow:0 0 0 2px var(--teal)}
.ksd-tam::after{left:-4px;top:-4px;width:30px;height:30px}
/* Tay cầm dời cả hình: to hơn mọi thứ khác trên bản đồ vì nó là thứ ngón tay phải bắt trúng, và
   khác màu đỉnh/tâm để không ai tưởng kéo nó là kéo một đỉnh. */
.ksd-tay{background:#2f7ed8;border:2px solid #fff;border-radius:50%;color:#fff;
  font-size:17px;line-height:26px;text-align:center;cursor:move;
  box-shadow:0 1px 5px rgba(0,0,0,.55)}
/* Số thứ tự góc của hình dựng từ số đo (việc #183) — chỉ để ĐỌC, không chạm: người cầm máy phải
   biết «góc 3 trên màn» là góc nào ngoài ruộng thì mới bấm GPS đúng chỗ. */
/* Tên góc A,B,C… và tên cạnh a,b,c… hiện sau khi CHỐT HÌNH (việc #267). Màu cam theo đúng màu
   ranh đất, để không lẫn với vòng số XANH của hình đang ướm từ số đo thước (việc #183).
   .ksd-ten-goc KHÔNG được đặt pointer-events:none — chấm ấy phải kéo được để chỉnh góc. */
.ksd-ten-goc{background:#e8973a;color:#0b1520;border-radius:50%;border:2px solid #0b1520;
  font-size:12px;font-weight:800;line-height:16px;text-align:center}
/* Nhãn nay mang cả số mét (việc #267 đợt 2) nên dài ngắn tuỳ cạnh: cấm xuống dòng, và bề
   ngang do iconSize bên app.js tính theo số ký tự. */
.ksd-ten-canh{background:rgba(11,21,32,.82);color:#e8973a;border:1px solid #e8973a;
  border-radius:4px;font-size:11px;font-weight:700;line-height:16px;text-align:center;
  white-space:nowrap;padding:0 3px;pointer-events:none}

/* MỘT CẠNH MỘT THẺ trong bảng số đo thước — không phải bảng, nên không dùng .the-nho.
   Bảng bốn cột cắt cụt cột «Thước đo» trên điện thoại 375px (ảnh CTO gửi 22/08). */
.ksd-canh-the{border:1px solid var(--line);border-radius:10px;padding:8px 10px;
  background:var(--panel2)}

/* Thanh trượt số đo: ngón tay chứ không phải con trỏ chuột. Đầu kéo 26px là ngưỡng chạm tối
   thiểu quen dùng; nấc 0,5 m khai ở thuộc tính step của thẻ input, không khai ở đây. */
input[type=range].ksd-thanh{-webkit-appearance:none;appearance:none;width:100%;height:26px;
  margin:6px 0 2px;background:transparent;cursor:pointer}
input[type=range].ksd-thanh::-webkit-slider-runnable-track{height:6px;border-radius:3px;
  background:var(--bg);border:1px solid var(--line)}
input[type=range].ksd-thanh::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
  width:26px;height:26px;margin-top:-11px;border-radius:50%;background:var(--orange);
  border:2px solid var(--bg)}
input[type=range].ksd-thanh::-moz-range-track{height:6px;border-radius:3px;
  background:var(--bg);border:1px solid var(--line)}
input[type=range].ksd-thanh::-moz-range-thumb{width:24px;height:24px;border-radius:50%;
  background:var(--orange);border:2px solid var(--bg)}
.ksd-so-goc{background:#5b8def;color:#fff;border-radius:50%;border:2px solid #0b1520;
  font-size:11px;font-weight:700;line-height:14px;text-align:center;pointer-events:none}
.fun-row{margin-bottom:10px}
.fun-top{display:flex;justify-content:space-between;font-size:13px;margin-bottom:4px;gap:8px}
.pbar{height:8px;background:var(--panel2);border-radius:4px;overflow:hidden}
.pbar>i{display:block;height:100%;background:var(--green);border-radius:4px}
.pbar>i.warn{background:var(--orange)}
.pbar>i.bad{background:var(--red)}
/* KS-5 — thẻ tiến độ khảo sát (#/khao-sat-tien-do) */
.kstd-luc{margin:-4px 0 10px}
.kstd-luoi{display:grid;grid-template-columns:repeat(auto-fill,minmax(290px,1fr));gap:10px;margin-bottom:16px}
.kstd-the{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:12px 14px}
.kstd-the h3{font-size:14px;margin-bottom:4px}
.kstd-the .pbar{margin:6px 0}
.kstd-so{font-size:22px;font-weight:700}
.kstd-pct{margin-left:6px}
.kstd-green{color:var(--green)} .kstd-orange{color:var(--orange)} .kstd-red{color:var(--red)}
.kstd-bd{display:block;width:100%;height:90px;margin-top:8px;background:var(--panel2);border-radius:6px}
.kstd-thuc{fill:none;stroke:var(--teal);stroke-width:2}
.kstd-kh{fill:none;stroke:var(--dim);stroke-width:1.5;stroke-dasharray:5 4}
.kstd-nay{stroke:var(--orange);stroke-width:1;opacity:.6}
.kstd-truc{display:flex;justify-content:space-between;gap:6px;font-size:11px;color:var(--dim);margin-bottom:4px}
.kstd-o{width:9.5em;max-width:100%}
.kstd-o[type=number]{width:6em}

/* cảnh báo dạng thẻ */
.al-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(290px,1fr));gap:10px}
.al-card{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:11px 13px;cursor:pointer}
.al-card:hover{background:var(--panel2)}
.al-top{display:flex;align-items:center;gap:8px}
.al-site{font-size:14.5px}
.al-days{margin-left:auto;color:var(--red);font-weight:700;font-size:13px;white-space:nowrap}
.al-label{font-size:12.5px;margin-top:5px;line-height:1.4}
.al-sub{font-size:11px;color:var(--dim);margin-top:3px}
.al-sub-row{display:flex;gap:8px;align-items:center;padding:4px 0;border-top:1px solid rgba(31,58,79,.4);font-size:12px}
.al-sub-row .al-label{margin-top:0}

.grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.grid2>*{min-width:0}   /* chống grid blowout: bảng rộng phải cuộn TRONG khung, không đẩy cả trang */
/* Màn «Gán người»: đội · người · trạm đứng cạnh nhau để giám đốc tỉnh gán liên tiếp hàng chục trạm; nhưng
   200+230+300 px là 746 px, mà tới 860 px thanh bên 212 px vẫn còn — dưới 1000 px xếp dọc ba panel (b854:
   ở 375 px lưới cũ làm CẢ TRANG rộng 756 px, người dùng kéo ngang mới thấy cột trạm). */
.gn-ba-cot{display:grid;grid-template-columns:minmax(200px,1fr) minmax(230px,1.2fr) minmax(300px,2fr);gap:8px;align-items:start}
.gn-ba-cot>*{min-width:0}
@media (max-width:1000px){.gn-ba-cot{grid-template-columns:1fr}}
.filters{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}
.filters>*{width:auto;min-width:130px}
.filters input[type=text]{flex:1;min-width:200px}
.chat-box{max-height:280px;overflow-y:auto;margin-bottom:10px}
.chat-msg{background:var(--panel2);border-radius:8px;padding:7px 10px;margin-bottom:6px;font-size:12.5px}
.chat-msg .who{color:var(--teal);font-weight:600;font-size:11px}
.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:0 14px}
.muted{color:var(--dim);font-size:12px}
.mt{margin-top:12px}

/* ══ MENU TRÊN ĐIỆN THOẠI: NGĂN KÉO, KHÔNG PHẢI DẢI NGANG ══
 *
 * Khối cũ ép .side thành «flex-direction:row + overflow-x:auto» — một dải nút cuộn ngang. Cách ấy
 * đúng vào thời menu còn là danh sách phẳng. Nhưng menu sau này đổi sang NHÓM XỔ DỌC (thẻ <details
 * class="navg">), và một thẻ <details> mở ra thì cao chứ không dài: nhét vào hộp flex ngang, nó
 * bung xuống và chiếm gần nửa màn hình, đứng lì ở đó vì .side là sticky.
 * Đo trên máy CTO ngày 08/08/2026: menu ăn ~540px trên màn 1280px cao, mọi nội dung bị đẩy xuống.
 *
 * Nay là ngăn kéo đúng nghĩa: bình thường trượt ra ngoài màn, bấm nút ☰ mới đẩy vào, chọn xong
 * tự đóng. Dùng transform chứ không dùng display để còn trượt mượt và giữ được trạng thái cuộn.
 */
@media (max-width:860px){
  .shell{flex-direction:column}

  /* Thanh trên cùng chứa nút ☰ — chỉ có trên màn hẹp. */
  .mbar{display:flex;align-items:center;gap:10px;padding:8px 10px;background:var(--panel);
        border-bottom:1px solid var(--line);position:sticky;top:0;z-index:40}
  .mbar .mbtn{font-size:20px;line-height:1;background:none;border:1px solid var(--line);
              color:var(--txt);border-radius:6px;padding:5px 10px;cursor:pointer}
  .mbar .mttl{font-weight:700;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

  .side{position:fixed;top:0;left:0;bottom:0;width:262px;max-width:84vw;height:100dvh;
        z-index:60;overflow-y:auto;padding:14px 0;flex-direction:column;
        transform:translateX(-102%);transition:transform .18s ease-out;
        box-shadow:2px 0 14px rgba(0,0,0,.45)}
  .side.mo{transform:translateX(0)}
  .side .brand small{display:block}

  /* Nền mờ phía sau: bấm ra ngoài là đóng. Không có nó thì người dùng bị kẹt trong menu.
     PHẢI khai display ở đây: luật gốc «.mbar,.mbd{display:none}» vẫn đè xuống nếu không ghi lại,
     và khi ấy nền mờ không bao giờ hiện — bấm bằng mã thì vẫn chạy (click() không cần thấy),
     nhưng ngón tay người dùng thì chạm vào khoảng không. Lỗi đúng kiểu «thử thì được, dùng thì
     không», bắt được lúc đo display trên trình duyệt chứ không phải lúc đọc mã. */
  .mbd{display:block;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:55;opacity:0;
       pointer-events:none;transition:opacity .18s}
  .mbd.mo{opacity:1;pointer-events:auto}

  .main{--main-dem:10px;padding:12px var(--main-dem)}
  .grid2{grid-template-columns:1fr}
  .ck-item{flex-direction:column}
  .userchip{display:none}                       /* chip user chiếm chỗ — ẩn trên mobile */
  .topbar h2{font-size:16px}
  .cards{grid-template-columns:repeat(3,1fr);gap:8px}
  .card{padding:9px 10px}
  .card .v{font-size:19px}
  .card .k{font-size:9.5px}
  /* bảng: KHÔNG bóp cột — giữ độ rộng tối thiểu và cuộn ngang trong khung */
  table.wide{min-width:640px}
  .ellip{max-width:200px}
  table.freeze td:first-child .ellip{max-width:84px}   /* cột đóng băng gọn trên màn hẹp */
  .filters>*{min-width:110px}
  .al-grid{grid-template-columns:1fr}
}

/* 3B — chi tiết trạm theo NHÓM LUỒNG CÔNG VIỆC */
.panel.wf{padding:0}
.panel.wf>summary{padding:12px 14px;cursor:pointer;font-size:14px;list-style:none;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.panel.wf>summary::-webkit-details-marker{display:none}
.panel.wf>summary>b{flex:1;min-width:0}
.mb{margin-bottom:8px}
.panel.wf>summary::before{content:'▸';color:var(--teal);font-size:12px}
.panel.wf[open]>summary::before{content:'▾'}
.panel.wf[open]>summary{border-bottom:1px solid var(--line)}
.panel.wf>*:not(summary){padding-left:14px;padding-right:14px}
.panel.wf>*:last-child{padding-bottom:12px}
.wf-line{display:flex;align-items:center;gap:9px;padding:6px 0;border-bottom:1px solid rgba(31,58,79,.4);font-size:13px}
.wf-lbl{flex:1;min-width:0}
.wf-dates{font-size:11.5px;color:var(--dim);text-align:right;white-space:nowrap}
.wf-badge{border-radius:5px;padding:1px 7px;font-size:11px;font-weight:700}
.wf-badge.green{background:rgba(55,176,111,.18);color:var(--green)}
.wf-badge.teal{background:rgba(23,162,184,.16);color:var(--teal)}
.wf-badge.gray{background:rgba(90,111,128,.2);color:var(--dim)}
.wf-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:4px 14px;padding:8px 0}
.wf-info{display:flex;justify-content:space-between;gap:8px;font-size:12px;border-bottom:1px dotted rgba(31,58,79,.6);padding:3px 0}
.wf-info span{color:var(--dim)}
@media (max-width:860px){ .wf-line{flex-wrap:wrap} .wf-dates{text-align:left;width:100%} }
/* AD-0009 — lý do bác số khai: một dòng chữ riêng dưới dòng mốc (trước chỉ ở chú thích rê chuột) */
.wf-line:has(.ms-ly-do),.wf-moc-in:has(.ms-ly-do){flex-wrap:wrap}
.ms-ly-do{flex-basis:100%;font-size:12px;color:var(--red);white-space:normal;padding-left:17px}
/* AD-0009 — thẻ hàng đợi số khai */
.sk-the{display:flex;flex-direction:column;gap:6px;padding:10px 12px}
.sk-dau{font-size:13.5px}
.sk-nut{display:flex;gap:8px;flex-wrap:wrap}

/* 3C — cảnh báo theo vấn đề gốc (NOC) */
.root-card{padding:0;margin-bottom:10px}
.root-head{display:flex;gap:10px;align-items:center;padding:11px 14px;cursor:pointer;flex-wrap:wrap;font-size:13.5px}
.root-head:hover{background:var(--panel2)}
.root-meta{display:flex;gap:16px;flex-wrap:wrap;padding:0 14px 10px;font-size:11.5px;color:var(--dim);border-bottom:1px solid var(--line)}
.root-sites{padding:0 14px}
.root-sites:not(:empty){padding:10px 14px}

/* 3D — nhãn trên bản đồ */
.map-lbl{background:rgba(11,21,32,.82);border:none;color:#d7e3ec;font-size:10.5px;padding:1px 4px;box-shadow:none}
.map-lbl::before{display:none}


/* ---------- Popup dùng chung ---------- */
.ovl { position: fixed; inset: 0; background: rgba(2,8,16,.72); display: flex;
       align-items: center; justify-content: center; z-index: 900; padding: 12px; }
.ovl-box { background: var(--panel, #101c28); border: 1px solid var(--line, #1e2d3d); border-radius: 12px;
           width: min(720px, 100%); max-height: 88vh; display: flex; flex-direction: column; }
.ovl-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
            padding: 12px 14px; border-bottom: 1px solid var(--line, #1e2d3d); }
.ovl-x { background: none; border: 0; color: var(--dim); font-size: 17px; cursor: pointer; padding: 2px 6px; }
.ovl-x:hover { color: var(--txt); }
/* 💬 góp ý ngay trong đầu hộp thoại — nền mờ của hộp che nút 💬 trên thanh tiêu đề (bản nâng cấp góp ý). */
.ovl-nut { display: flex; align-items: center; gap: 2px; flex: none; }
.ovl-gx { background: none; border: 0; cursor: pointer; font-size: 15px; padding: 2px 6px; opacity: .75; }
.ovl-gx:hover, .ovl-gx:focus-visible { opacity: 1; }
.ovl-body { padding: 14px; overflow-y: auto; }

/* ---------- Sổ ghi chú theo trạm ---------- */
.note-new { display: flex; gap: 8px; align-items: flex-start; }
.note-new textarea { flex: 1; min-width: 0; resize: vertical; font: inherit;
  background: var(--panel2); color: var(--txt);
  border: 1px solid var(--line, #1e2d3d); border-radius: 8px; padding: 8px 10px; }
.note-new button { white-space: nowrap; }
.note { border-left: 3px solid var(--teal, #22d3ee); padding: 7px 0 7px 10px; margin-top: 10px; }
.note-rep { border-left-color: var(--line, #334155); margin-left: 16px; }
.note-rep-box { margin: 8px 0 4px 16px; }
.note-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; }
.note-time { margin-left: auto; }
.note-body { margin-top: 3px; white-space: normal; word-break: break-word; }
.note-act { margin-top: 4px; display: flex; gap: 12px; }
.note-act a { font-size: 12px; color: var(--dim); text-decoration: none; }
.note-act a:hover { color: var(--teal, #22d3ee); }
@media (max-width: 560px) {
  .note-new { flex-direction: column; }
  .note-new button { width: 100%; }
  .note-time { margin-left: 0; width: 100%; }
}


/* ---------- Tag @user / @nhóm khi báo vướng ---------- */
.mention-box { position: relative; }
.mention-box input { width: 100%; }
.mention-list { position: absolute; z-index: 60; left: 0; right: 0; top: 100%;
  background: var(--panel, #101c28); border: 1px solid var(--line, #1e2d3d);
  border-radius: 8px; max-height: 240px; overflow-y: auto; }
.mention-list:empty { display: none; }
.mention-item { padding: 7px 10px; cursor: pointer; border-bottom: 1px solid var(--line, #1e2d3d); }
.mention-item:last-child { border-bottom: 0; }
.mention-item:hover { background: rgba(34,211,238,.10); }
/* Dòng đang chọn bằng phím mũi tên. Không có lớp này thì bấm mũi tên xuống rồi Enter là chọn một
   dòng mà mắt không thấy dòng nào sáng lên — người dùng bấm Enter trong lúc không biết mình đang
   chọn gì. */
.mention-item.on { background: rgba(34,211,238,.18); }

/* ---------- Ô chọn NHIỀU bên bằng @ (kết luận họp) ---------- */
/* Thẻ nằm TRÊN ô gõ chứ không nằm trong: ô gõ giữ nguyên chiều cao dù đã chọn mười bên, nên hàng
   biểu mẫu bên cạnh không bị đẩy lệch mỗi lần thêm một thẻ. */
.at-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.at-chips:not(:empty) { margin-bottom: 6px; }
.at-chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 8px;
  border-radius: 14px; font-size: 12.5px; line-height: 1.6;
  background: rgba(34,211,238,.12); border: 1px solid var(--teal, #22d3ee); }
/* Mã @ gộp mang màu khác vì nó KHÔNG phải một bên — nó nở ra thành nhiều bên lúc lưu, và người
   gõ phải thấy sự khác nhau ấy trước khi bấm Lưu chứ không phải sau. */
.at-chip.gop { background: rgba(245,158,11,.14); border-color: var(--orange, #f59e0b); }
.at-chip b { cursor: pointer; opacity: .65; font-size: 14px; }
.at-chip b:hover { opacity: 1; color: var(--red, #ef4444); }

/* ---------- Việc gán cho tôi (ưu tiên cao nhất trên bảng điều hành) ---------- */
.panel.forme { border: 1px solid var(--teal, #22d3ee); border-left-width: 4px;
  background: linear-gradient(180deg, rgba(34,211,238,.07), transparent 60%); }
.b.teal { background: rgba(34,211,238,.18); color: var(--teal, #22d3ee); }


/* ---------- Hộp thoại chọn ngày ---------- */
.dlg-row { margin-bottom: 14px; }
.dlg-row label { display: block; margin-bottom: 5px; font-weight: 600; }
.dlg-row input { width: 100%; font: inherit; padding: 9px 10px; border-radius: 8px;
  background: var(--panel2); color: var(--txt); border: 1px solid var(--line, #1e2d3d); }
/* input type=date trên điện thoại: vùng bấm phải đủ lớn để mở lịch bằng 1 chạm */
.dlg-row input[type="date"] { min-height: 44px; }
.dlg-date { display: flex; gap: 6px; align-items: center; }
.dlg-date input { flex: 1; min-width: 0; }
.dlg-act { display: flex; gap: 8px; margin-top: 18px; }
.dlg-act button { flex: 1; min-height: 42px; }
@media (max-width: 560px) {
  .dlg-date { flex-wrap: wrap; }
  .dlg-date input { flex: 1 1 100%; }
  .dlg-date button { flex: 1; }
}
/* Ô tích trong hộp thoại (12/08/2026, màn Yêu cầu phối hợp).
   width:auto phải ghi đè .dlg-row input{width:100%} ở trên, nếu không ô tích kéo dài hết dòng và
   dòng chữ bị đẩy xuống dưới — trông như một ô nhập bị hỏng. */
.dlg-cks { display: flex; flex-direction: column; gap: 8px; }
.dlg-ck { display: flex; align-items: flex-start; gap: 8px; font-weight: 400;
  padding: 7px 9px; border: 1px solid var(--line, #1e2d3d); border-radius: 8px; cursor: pointer; }
.dlg-row .dlg-ck input[type="checkbox"] { width: auto; min-height: 0; flex: none;
  margin-top: 3px; accent-color: var(--teal, #16a3a3); }
.dlg-ck .dlg-ck-t { flex: 1; }
/* Việc không chọn được: mờ đi nhưng VẪN HIỆN, kèm câu vì sao ngay bên cạnh. Ẩn hẳn thì người dùng
   tưởng hệ thống quên mất việc ấy và đi hỏi vòng quanh. */
.dlg-ck.off { opacity: .55; cursor: not-allowed; }

/* ---------- Khối toạ độ luồng trên màn trạm ---------- */
.ych-td { border: 1px solid var(--line, #1e2d3d); border-radius: 8px; padding: 8px 10px;
  margin-bottom: 8px; }
.ych-td-h { display: flex; align-items: baseline; gap: 8px; }
.ych-td-v { margin: 4px 0; }
.ych-td-v code { background: var(--panel2); padding: 3px 7px; border-radius: 6px;
  word-break: break-word; }
.ych-td-a { display: flex; flex-wrap: wrap; gap: 6px; }
/* Dòng phiếu khẩn cấp: một vệt đỏ bên trái, KHÔNG tô nền cả dòng — nền đỏ đè lên các nhãn màu
   trong chính dòng ấy và làm chúng đọc không ra. */
tr.ych-khan td:first-child { box-shadow: inset 3px 0 0 0 var(--red, #e5484d); }


/* ---------- Thanh chuyển chế độ xem: Của tôi / Toàn bộ ---------- */
.scope-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 12px; }
.scope-bar .seg { display: inline-flex; border: 1px solid var(--line, #1e2d3d); border-radius: 999px; overflow: hidden; }
.scope-bar .seg button { border: 0; background: transparent; color: var(--dim);
  padding: 7px 15px; cursor: pointer; font: inherit; font-size: 13px; min-height: 38px; }
.scope-bar .seg button.on { background: var(--teal, #22d3ee); color: #04202b; font-weight: 700; }
.scope-bar .seg button:not(.on):hover { color: var(--txt); }
@media (max-width: 560px) {
  .scope-bar .seg { width: 100%; }
  .scope-bar .seg button { flex: 1; }
}

/* ── nút gạt kết quả checklist ────────────────────────────────────────────
   Dùng lại .tri của bộ checklist cũ, chỉ nới vùng bấm. Người dùng là anh em
   thi công đứng chân cột, đeo găng, màn hình dính bụi — nút 12px cao 26px là
   không bấm trúng. 44px là ngưỡng tối thiểu cả Apple lẫn Google đều khuyến
   nghị, và dự án đã dùng đúng con số đó cho ô ngày ở .dlg-row. */
.qc-tri{display:flex;gap:6px;flex-shrink:0}
.qc-tri button{flex:1;min-width:78px;min-height:44px;font-size:13px;font-weight:600;
  background:var(--panel);border:1px solid var(--line);color:var(--dim);border-radius:8px}
.qc-tri button.ok.sel{background:var(--green);border-color:var(--green);color:#02180c}
.qc-tri button.nok.sel{background:var(--red);border-color:var(--red);color:#fff}
.qc-tri button.na.sel{background:var(--gray);border-color:var(--gray);color:#fff}
.qc-row{border:1px solid var(--line);border-radius:8px;padding:10px;margin-bottom:8px;background:var(--panel2)}
.qc-row.mo{opacity:.62}
.qc-row.cham{border-color:var(--teal)}
.qc-row .qc-note{width:100%;min-height:40px;margin-top:8px}
.qc-truoc{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;font-size:11.5px}
@media (max-width:860px){ .qc-tri{width:100%} .qc-tri button{min-width:0} }

/* vị trí NY của nút gạt — «chưa làm được», khác hẳn «không đạt» nên phải khác màu */
.qc-tri button.ny.sel{background:var(--yellow);border-color:var(--yellow);color:#2a2400}

/* ── dải 11 ô mốc chính trên tháp điều hành ──────────────────────────────
   Mỗi mốc một ô nhỏ, xếp tự xuống dòng. Trên điện thoại thành 2 cột, trên
   màn hình rộng thành 11 ô một hàng — không dùng số cột cố định vì 11 là số
   lẻ, chia cứng kiểu nào cũng thừa một ô lệch hàng. */
.moc-dai{display:flex;flex-wrap:wrap;gap:8px}
.moc-o{flex:1 1 128px;min-width:128px;border:1px solid var(--line);border-radius:8px;
  padding:8px 10px;background:var(--panel2)}
.moc-o.mo{opacity:.5}
.moc-o .moc-so{font-size:11px;color:var(--dim);font-weight:700}
.moc-o .moc-ten{font-size:11.5px;line-height:1.25;height:30px;overflow:hidden;color:var(--dim)}
.moc-o .moc-v{font-size:19px;margin-top:4px}
.moc-o .moc-v span{font-size:13px}
.moc-o .moc-p{font-size:14px;font-weight:700}

/* Ô mốc BẤM ĐƯỢC (CTO 10/08/2026: "bấm vào 1 trong 12 mốc không được, cần phải bấm được để xem
   theo công đoạn"). Phải TRÔNG như bấm được, không chỉ bấm được: viền sáng khi rê chuột là tín
   hiệu duy nhất người dùng có, vì ô này nằm trong một dải toàn ô giống nhau.
   Ô đang soi giữ viền teal đặc để biết mình đang đứng ở mốc nào — bảng bên dưới đã đổi theo nó. */
.moc-o.click{cursor:pointer}
.moc-o.click:hover{border-color:var(--teal)}
.moc-o.moc-o-soi{border-color:var(--teal);box-shadow:inset 0 0 0 1px var(--teal)}

/* ── khối vướng ưu tiên trên tháp điều hành ──────────────────────────────── */
.vg-panel{border-left:3px solid var(--red)}
.vg-nhom{margin-bottom:10px}
.vg-nhan{font-size:12px;font-weight:700;color:var(--dim);margin:6px 0 4px;text-transform:uppercase}
.vg-nhan.red{color:var(--red)}
.vg-nhan.orange{color:var(--orange)}
.vg-row{border:1px solid var(--line);border-radius:8px;padding:8px 10px;margin-bottom:6px;
  background:var(--panel2);cursor:pointer}
.vg-row:hover{border-color:var(--teal)}
.vg-top{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
@media (max-width:860px){ .vg-top{gap:4px} }

/* ── dải vướng khẩn cấp: bám đỉnh màn hình, không cuộn mất ─────────────────
   Đặt position:sticky ở đầu body để nó nằm trên MỌI màn hình, không chỉ tháp
   điều hành — vướng cháy nhà thì người đang xem màn vật tư cũng phải thấy. */
.emg-bar{position:sticky;top:0;z-index:9999;background:var(--red);color:#fff;
  padding:8px 12px;box-shadow:0 2px 10px rgba(0,0,0,.4);animation:emgNhap 1.4s infinite}
@keyframes emgNhap{0%,100%{filter:brightness(1)}50%{filter:brightness(1.35)}}
.emg-bar .emg-t{font-weight:800;font-size:14px;letter-spacing:.3px}
.emg-bar .emg-row{background:rgba(0,0,0,.18);border-radius:6px;padding:6px 8px;margin:6px 0;cursor:pointer}
.emg-bar .emg-row:hover{background:rgba(0,0,0,.3)}
.emg-bar button{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.4);color:#fff}
@media (prefers-reduced-motion:reduce){ .emg-bar{animation:none} }

/* ── đầu cột 11 mốc: con số phải đọc được từ xa ──────────────────────────────
   CTO: «tiêu đề bảng này là các con số thể hiện 11 mốc quan trọng, cần phóng to
   lên cho nổi bật». Kiểu th chung (11px, hoa, xám mờ) đúng cho cột chữ nhưng
   nuốt mất con số — mà con số ở đây CHÍNH LÀ tên cột, không phải nhãn phụ.
   Không dùng text-transform/letter-spacing: chữ hoa không có nghĩa với chữ số,
   còn giãn chữ làm số hai chữ số (10, 11) lệch khỏi tâm cột. */
th.moc-h{font-size:20px;font-weight:800;color:var(--txt);text-transform:none;
  letter-spacing:0;line-height:1.1;padding-top:2px;padding-bottom:4px;
  border-bottom:2px solid var(--teal)}
@media (max-width:860px){ th.moc-h{font-size:17px} }

/* Nhãn rút gọn dưới số thứ tự mốc. Số 1..12 to đậm để đọc từ xa (CTO duyệt), nhưng chỉ có số thì
   người đọc phải nhớ mốc nào ứng số nào — mà bộ mốc VỪA đổi thứ tự ngày 04/08/2026 (chèn HD_VE_CNT
   vào vị trí 2), nên trí nhớ cũ của mọi người đang sai. Nhãn nhỏ dưới số là chỗ chữa. */
.moc-h-lbl{font-size:10px;font-weight:400;color:var(--dim);text-transform:none;
  letter-spacing:0;line-height:1.2;margin-top:1px}
.moc-h-o{font-size:11px;color:var(--dim);margin-left:2px;vertical-align:super}
@media (max-width:860px){ .moc-h-lbl{font-size:9px} }

/* Hàng đầu cột toàn mốc (bảng «Trạm × 12 mốc»). `th.moc-h` tự kẻ vạch teal dưới chân, nhưng
   hàng ấy còn hai ô KHÔNG phải mốc ở hai đầu — cột trạm và cột «đã khai». Không kẻ cho chúng
   thì vạch teal đứt ở hai mép, trông như bảng vẽ hỏng chứ không như một đường phân cách. */
.moc-hang th{border-bottom:2px solid var(--teal)}



/* MENU GOM NHÓM — thẻ <details> trong thanh bên.
   Mũi tên mặc định của trình duyệt xấu và lệch, nên tắt đi rồi tự vẽ bằng ký tự ▸/▾. */
.side .navg { border-bottom: 1px solid rgba(255,255,255,.06); }
.side .navg > summary {
  list-style: none; cursor: pointer; padding: 10px 16px;
  font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: #7d97b5; user-select: none; display: flex; align-items: center; gap: 6px;
}
.side .navg > summary::-webkit-details-marker { display: none; }
.side .navg > summary::before { content: "▸"; font-size: 9px; transition: transform .12s; }
.side .navg[open] > summary::before { transform: rotate(90deg); }
.side .navg > summary:hover { color: #cfe0f0; }
.side .navg > a { padding-left: 30px; }

/* ── BỘ CHỌN TRƯỜNG (màn Dữ liệu) ─────────────────────────────────────────────
   328 ô tích trên một mặt phẳng là thứ không ai đọc hết. Gom theo nhóm nghiệp vụ,
   mặc định chỉ hiện trường của đơn vị mình; trường của đơn vị khác vào chế độ mở
   rộng thì mờ đi và đeo phù hiệu chủ trường, để tích nhầm cũng nhìn ra ngay. */
.fhead { display:flex; flex-wrap:wrap; align-items:center; gap:14px; padding:8px 10px;
  background:var(--panel2); border:1px solid var(--line); border-radius:8px; font-size:12.5px; }
.fhead .fexp { margin-left:auto; display:inline-flex; align-items:center; gap:6px;
  cursor:pointer; color:var(--dim); }
.fhead .fexp input { width:auto; }
.fgrp { border:1px solid var(--line); border-radius:8px; margin-top:8px; background:var(--panel); }
.fgrp > summary { cursor:pointer; list-style:none; padding:7px 10px; display:flex; align-items:center;
  gap:10px; font-size:12.5px; user-select:none; }
.fgrp > summary::-webkit-details-marker { display:none; }
.fgrp > summary::before { content:"▸"; color:var(--dim); font-size:10px; transition:transform .12s; }
.fgrp[open] > summary::before { transform:rotate(90deg); }
.fgrp > summary:hover { background:var(--panel2); }
.fgrp .fgn { font-weight:600; }
.fgrp .fgc { color:var(--dim); font-size:11.5px; font-variant-numeric:tabular-nums; }
.fgrp .fgall { margin-left:auto; color:var(--dim); font-size:11.5px; display:inline-flex;
  align-items:center; gap:5px; }
.fgrp .fgall input { width:auto; }
.fgl { display:grid; grid-template-columns:repeat(auto-fill,minmax(230px,1fr)); gap:2px 10px;
  padding:2px 10px 10px 24px; border-top:1px solid var(--line); }
.fitem { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--txt);
  padding:3px 4px; border-radius:4px; cursor:pointer; }
.fitem:hover { background:var(--panel2); }
.fitem input { width:auto; flex-shrink:0; }
.fitem span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.fitem.khac { color:var(--dim); }
.fitem .own { margin-left:auto; font-style:normal; font-size:10px; padding:1px 5px; border-radius:3px;
  background:var(--panel2); border:1px solid var(--line); color:var(--dim); flex-shrink:0; }
.fbar { position:sticky; bottom:0; display:flex; flex-wrap:wrap; align-items:center; gap:8px;
  margin-top:10px; padding:9px 10px; background:var(--panel2); border:1px solid var(--line);
  border-radius:8px; font-size:12.5px; }
.fbar > span:first-child { margin-right:auto; }

/* ── KHỐI THEO DÕI TICKET VƯỚNG (V116) ────────────────────────────────────────
   Bốn câu người mở vướng hỏi mỗi ngày, xếp thành bốn dòng nhãn-giá trị để mắt
   quét dọc một lượt là xong, không phải đọc thành câu. */
.vg-td { border:1px solid var(--line); border-radius:8px; background:var(--panel2); overflow:hidden; }
.vg-td-h { padding:6px 10px; background:var(--navy); color:#fff; font-size:12px; font-weight:600; }
.vg-td-r { display:flex; flex-wrap:wrap; align-items:baseline; gap:8px; padding:7px 10px;
  border-top:1px solid var(--line); font-size:12.5px; }
.vg-td-r > span:first-child { min-width:118px; color:var(--dim); font-size:11.5px; flex-shrink:0; }
.vg-td-r .ok { color:var(--green, #3fb950); }
.vg-chain { display:flex; flex-direction:column; gap:5px; }
.vg-step { border-left:2px solid var(--orange); padding-left:8px; }

/* Bằng chứng đính theo mốc (V119) */
.bc-row { display:flex; flex-wrap:wrap; align-items:center; gap:8px; padding:6px 8px;
  border:1px solid var(--line); border-radius:6px; margin-bottom:5px; font-size:12.5px; }
.bc-row > span { margin-right:auto; }

/* ── MÀN TRẠM: BA TẦNG NỔI BẬT (V129) ──────────────────────────────────────
   CTO: "12 mốc chính hiện nổi bật hơn những thứ còn lại". Tầng 1 dải 12 mốc chữ đậm nhất,
   tầng 2 mốc chính lặp lại trong từng mục, tầng 3 trường chi tiết chữ nhỏ nhãn xám.
   Nén được nhờ gộp chuỗi: 65 cột truyền dẫn ra 17 dòng mà không giấu trường nào. */
.m12-band{padding:13px 15px 5px}
.m12-cap{font-size:11px;letter-spacing:.09em;text-transform:uppercase;color:var(--teal);
  margin-bottom:11px;display:flex;align-items:center;gap:10px}
.m12-cap span{color:var(--dim);letter-spacing:0;text-transform:none;font-size:12px}
.m12-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(172px,1fr));gap:0 18px}
.m12{display:flex;align-items:flex-start;gap:8px;padding:6px 0;border-bottom:1px solid #172d40}
.m12-n{font-size:11px;color:#54697c;min-width:13px;text-align:right;padding-top:3px;
  font-variant-numeric:tabular-nums}
.m12-dot{width:8px;height:8px;border-radius:50%;flex:none;margin-top:6px;background:#2a4358}
.m12-dot.green{background:var(--green)} .m12-dot.red{background:var(--red)}
.m12-dot.orange{background:var(--orange)} .m12-dot.gray{background:#2a4358}
/* ĐÈN 2 «đúng hẹn & chất lượng» — VÒNG VIỀN quanh chấm đèn 1, không phải chấm thứ hai (#256).
   Dải 12 mốc không có chỗ cho 24 chấm, và hai chấm cạnh nhau thì người xem phải học xem cái nào
   là cái nào. box-shadow KHÔNG chiếm chỗ trong bố cục: chấm vẫn đúng 8px nên tên mốc không bị
   đẩy sang phải. Ring 2px ăn vào khoảng gap 8px của .m12, còn dư 6px nên không đè lên số thứ tự. */
.m12-dot.rred{box-shadow:0 0 0 2px var(--red)}
.m12-dot.rorange{box-shadow:0 0 0 2px var(--orange)}
.m12-dot.rgreen{box-shadow:0 0 0 2px var(--green)}
/* KÝ HIỆU đi kèm màu, và CHỈ khi có chuyện (✕ hoặc !). Đèn xanh im lặng — màn hình chỉ nói khi
   cần nói. Đặt ở CUỐI DÒNG NGÀY chứ không chen vào hàng ngang giữa chấm và tên mốc: ô lưới chỉ
   rộng 172px, thêm một thẻ vào hàng ấy là bóp mất chỗ của tên mốc (đúng lỗi đã sửa ở #240). */
.m12-k{font-weight:700;font-size:12px}
.m12-k.red{color:var(--red)} .m12-k.orange{color:var(--orange)}
/* DÒNG CHÚ THÍCH CỦA ĐÈN (#188, CTO 17/08/2026: «một dòng chú thích nhỏ»).
   Đẩy sang phải cùng hàng với tên mốc để mắt quét dọc một cột duy nhất; màn hẹp thì xuống dòng
   chứ không cắt chữ — «quá hạn 12 ngày» mà cụt thành «quá hạn 1» là sai số liệu, không phải xấu.

   Chọn con TRỰC TIẾP của summary chứ không `.wf .m12-dot`: trong THÂN khối cũng có một .m12-dot
   (dòng «Mốc N · …»), và một luật lỏng ở đây sẽ lặng lẽ dịch cái chấm ấy đi vài điểm ảnh.

   Ô CHÚ THÍCH NÀY TỪNG ĐÓNG `*​/` SỚM MỘT DÒNG (sửa 19/08/2026). Hai dòng chữ còn lại rơi ra
   ngoài chú thích, trình duyệt đọc chúng thành SELECTOR rồi nuốt luôn luật ngay bên dưới. Đo
   trên trình duyệt thật: CSSOM chỉ còn MỘT luật cho .panel.wf>summary>.wf-den — bản trong
   media 560px. Luật nền biến mất, nên trên màn rộng «quá hạn N ngày» không được đẩy sang phải
   và ngắt dòng giữa cụm — đúng cái điều dòng ngay trên đây dặn phải tránh. Hỏng lặng: không
   trang nào lỗi, chỉ là một luật không tồn tại. */
.panel.wf>summary>.wf-den{margin-left:auto;padding-left:10px;white-space:nowrap}
.panel.wf>summary>.m12-dot{margin-top:0;align-self:center}
/* Cụm số liệu của dòng tiêu đề: huy hiệu + thanh tiến độ + «chưa xong» + nút Hướng dẫn.
   Gom vào MỘT thẻ để màn hẹp đẩy cả cụm xuống dòng dưới bằng đúng một luật, thay vì bốn thẻ
   rời tự tìm chỗ. */
.panel.wf>summary>.wf-meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0}
/* Tên mốc dài không ngắt được thì XUỐNG DÒNG, không tràn ra ngoài ô.
   Ảnh CTO gửi 19/08: «Control Number» tràn khỏi ô tiêu đề đã bị bóp còn ~40px và in đè lên huy
   hiệu 0/1 — hai chữ chồng nhau, đọc ra số khác. Ô bị bóp là do min-width:0 ngay dưới đây; luật
   này là lưới an toàn cho cả những chỗ khác chưa lường tới. */
.panel.wf>summary>b{overflow-wrap:anywhere}
@media (max-width:560px){
  .panel.wf>summary>.wf-den{margin-left:0;flex-basis:100%;white-space:normal;padding-left:0}
  /* MÀN HẸP: TIÊU ĐỀ ĐƯỢC TRỌN MỘT DÒNG.
     `b` mang flex:1 + min-width:0, tức nó được phép co NHỎ HƠN chữ bên trong. Trên máy 360px,
     thanh tiến độ 70px + huy hiệu + «chưa xong» + nút Hướng dẫn đều không co được, nên tiêu đề
     còn khoảng 40px và mỗi từ rơi xuống một dòng: «2 ·» / «Trình» / «ký» / «hợp» / «đồng».
     Đẩy cả cụm meta xuống dòng dưới thì tiêu đề lấy lại nguyên chiều ngang. */
  .panel.wf>summary>.wf-meta{flex-basis:100%}
  .panel.wf>summary{gap:5px 8px;padding:10px 12px}
}
.m12-b{flex:1;min-width:0}
.m12-l{font-size:13.5px;font-weight:600;line-height:1.3}
.m12-d{font-size:11.5px;color:var(--dim);font-variant-numeric:tabular-nums;margin-top:1px}
.wf-moc-in{display:flex;align-items:center;gap:9px;padding:9px 11px;margin:7px 0 3px;
  background:var(--panel2);border-radius:6px}
.wf-moc-l{font-size:13.5px;font-weight:600;flex:1}
.wf-moc-d{font-size:12.5px;font-variant-numeric:tabular-nums}

.wf-br{display:flex;align-items:center;gap:9px;margin:15px 0 1px}
.wf-br-t{font-size:12.5px;font-weight:600;color:var(--teal)}
.wf-pill{font-size:10.5px;padding:1px 8px;border-radius:11px;background:#1b3446;color:#7d93a5}
.wf-pill.on{background:#14342a;color:var(--green)}
.wf-br-off{display:flex;align-items:center;gap:8px;padding:8px 11px;margin-top:11px;
  background:#0e1c29;border:1px solid var(--line);border-radius:6px;color:#54697c;font-size:12px}
.wf-lech{margin:8px 0;padding:8px 11px;background:#241a17;border:1px solid #4a3226;
  border-radius:6px;color:var(--orange);font-size:12px}

.wf-grp{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:#54697c;
  margin:12px 0 2px;display:flex;align-items:center;gap:7px}
.wf-grp-n{font-size:10px;color:#3d5468;font-variant-numeric:tabular-nums}
.wf-r{display:flex;align-items:baseline;gap:10px;padding:4px 0;border-bottom:1px solid #16293a}
.wf-k{flex:1;font-size:12.5px;color:var(--dim);min-width:0}
.wf-vv{font-size:12.5px;font-variant-numeric:tabular-nums;text-align:right}
.wf-ar{color:#2d4c63;padding:0 2px}
.wf-v{white-space:nowrap}
.wf-empty{color:#54697c}
/* Ô đã ẩn (truong_nhan.an_di) — gập cuối mỗi nhóm. Mờ hơn hẳn để mắt lướt qua được, nhưng vẫn mở
   ra sửa được: nhiều ô trong số này đang giữ dữ liệu thật của hàng trăm trạm. */
.wf-an{margin:2px 0 0 8px}
.wf-an>summary{font-size:11px;color:#54697c;cursor:pointer;list-style:none;padding:3px 0}
.wf-an>summary::-webkit-details-marker{display:none}
.wf-an>summary:hover{color:var(--teal)}
.wf-an[open]>summary{color:#7d94a8}
.wf-an .wf-r{opacity:.62}
.wf-an .wf-r:hover{opacity:1}
.wf-edit{border-bottom:1px dashed #2d4c63;cursor:text;padding-bottom:1px}
.wf-edit:hover{border-bottom-color:var(--teal);color:var(--teal)}
/* Ô ĐÃ CHỐT: nét gạch LIỀN, không phải nét đứt.
   Nét đứt trong màn này có nghĩa «bấm vào sửa được». Ô chốt vẫn từng sửa được nên phải khác hẳn
   ô của phòng khác (không gạch gì) — người dùng cần đọc ra ngay đây là «hết hạn sửa», không phải
   «không có quyền». Con trỏ để help chứ không phải text: rê vào là hiện câu giải thích. */
.wf-chot{border-bottom:1px solid #3a3a2a;cursor:help;padding-bottom:1px;opacity:.85}
.wf-khoa{font-size:.85em;opacity:.7;margin-left:2px}
/* Nút gọi đứng cạnh ô số máy (#133). Vùng bấm 24px để ngón tay ngoài hiện trường bấm trúng —
   biểu tượng chỉ .9em thì vùng bấm cũng bé theo, mà màn này chủ yếu dùng trên điện thoại. */
.wf-goi{margin-left:6px;font-size:.9em;text-decoration:none;opacity:.75;
        display:inline-block;min-width:24px;text-align:center;line-height:1.4}
.wf-goi:hover{opacity:1}
.wf-just{background:#14342a;border-radius:3px;padding:0 4px}
.wf-inp{font:inherit;background:var(--panel2);color:var(--txt);border:1px solid var(--teal);
  border-radius:4px;padding:1px 5px;min-width:104px;font-variant-numeric:tabular-nums}
@media(max-width:640px){.m12-grid{grid-template-columns:1fr}.wf-vv{white-space:normal}}

/* MA TRAN QUYEN (V163) — 34 nhom x 12 don vi, moi o bon chu cai X S N D.
   Bon chu thay cho bon o tich: 408 o x 4 tich la 1.632 o tich tren mot man, nhin vao chi thay mot
   bien vuong. Bon chu thi doc duoc ca dong trong mot cai liec. */
table.qnt th { position: sticky; top: 0; background: var(--panel2); z-index: 1 }
table.qnt td:first-child { min-width: 190px }
.qnt-o { white-space: nowrap; text-align: center; padding: 3px 5px }
.qnt-o.qnt-day   { background: rgba(55,176,111,.08) }
.qnt-q { display: inline-block; width: 17px; text-align: center; border-radius: 3px;
         font-size: .74rem; font-weight: 700; color: var(--dim); background: transparent }
.qnt-q.on { color: #04222a; background: var(--teal) }
.qnt-q[onclick] { cursor: pointer }
.qnt-q[onclick]:hover { outline: 1px solid var(--teal) }
/* Dong ma KHONG DON VI NAO sua duoc — hoac la o may tu tinh (dung), hoac la nhom chua ai
   nhan (sai). Do de nguoi soat dung lai nhin, khong phai de bao loi. */
tr.qnt-khong-ai td:first-child { border-left: 3px solid var(--red) }
tr.qnt-khong-ai { background: rgba(224,82,82,.05) }

/* Ô quyền cấp TẠM — gạch chéo nhạt để phân biệt với ô quyền thường mà không cần đọc chú thích. */
.qnt-o.qnt-tam{position:relative}
.qnt-o.qnt-tam::after{content:'✻';position:absolute;top:1px;right:3px;font-size:9px;
  color:var(--orange);opacity:.85;pointer-events:none}


/* ══════════════════════════════════════════════════════════════════════════════════════════
   GIA PHẢ DÍNH TRÊN MÀN CHI TIẾT TRẠM (CTO chốt 07/08/2026)
   ══════════════════════════════════════════════════════════════════════════════════════════
   Vấn đề: màn một trạm có hàng trăm dòng chia bốn cấp. Cuộn xuống thì mọi tiêu đề bay mất, người
   xem không còn biết ô mình đang đọc là con của ai. CTO: «phải thấy rõ gia phả nhà mình, tránh bị
   quên mất cội nguồn».

   Cách làm: xếp chồng position:sticky theo ba tầng, mỗi tầng bám dưới tầng trên.

       ┌─ tầng 1  tên trạm                    top: var(--dinh-1)
       │  ┌─ tầng 2  nhóm nghiệp vụ           top: var(--dinh-2)
       │  │  ┌─ tầng 3  nhóm trường           top: var(--dinh-3)
       │  │  │   dòng dữ liệu đang đọc
       │  │  └─ (cuộn hết nhóm này thì tầng 3 tự nhường chỗ cho nhóm kế)

   VÌ SAO KHÔNG VIẾT JAVASCRIPT NGHE SỰ KIỆN CUỘN. Trình duyệt làm sẵn việc này ở tầng dựng hình,
   mượt và không tốn gì. Tự nghe scroll rồi đo vị trí là chạy mã ở mỗi khung hình, giật trên điện
   thoại, và sai mỗi khi có khối gập/mở đổi chiều cao.

   ĐIỀU KIỆN ĐỂ ĐÚNG: mỗi tiêu đề phải có KHỐI CHA RIÊNG bao đúng phần nội dung của nó — xem
   .wf-sec dựng ở app.js (veTruong). Nếu để chung một khối, các tiêu đề cùng bám một chỗ và đè lên
   nhau thay vì nhường chỗ cho nhau.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
:root{
  /* Dải vướng khẩn (.emg-bar) chèn vào đầu body và CAO THAY ĐỔI, nên mốc tầng 1 phải là biến —
     app.js cập nhật lại mỗi khi dải ấy hiện hoặc mất. Không có dải thì bằng 0. */
  --emg-cao: 0px;
  --dinh-1: var(--emg-cao);                     /* tên trạm  */
  --dinh-2: calc(var(--emg-cao) + 44px);        /* nhóm nghiệp vụ, dưới thanh tên trạm */
  --dinh-3: calc(var(--emg-cao) + 89px);        /* nhóm trường, dưới nhóm nghiệp vụ */
}

/* ── TẦNG 1: thanh tên trạm ───────────────────────────────────────────────────────────────── */
.main .topbar{
  position:sticky; top:var(--dinh-1); z-index:30;
  /* Nền đặc là bắt buộc. Thiếu nền thì nội dung bên dưới hiện xuyên qua lúc trượt — lỗi trông
     như màn hình nhoè, và người dùng tưởng phần mềm vẽ hỏng. */
  background:var(--bg);
  /* Bù đệm của .main để nền chạy hết chiều ngang. ĐỌC TỪ BIẾN, không viết số: bản trước
     ghim -22px ở đây trong khi media query đổi đệm .main xuống 10px, nên trên điện thoại
     375px thanh này rộng 399px và trang phải cuộn ngang đúng 12px (việc #258). */
  margin-left:calc(-1 * var(--main-dem)); margin-right:calc(-1 * var(--main-dem));
  padding:10px var(--main-dem) 8px;
  margin-bottom:8px; border-bottom:1px solid var(--line);
}

/* ── TẦNG 2: nhóm nghiệp vụ ───────────────────────────────────────────────────────────────── */
.main .panel.wf>summary{
  position:sticky; top:var(--dinh-2); z-index:20;
  background:var(--panel);
  /* Bo góc trên để lúc dính không lộ mép panel phía sau. */
  border-radius:10px 10px 0 0;
}
/* Tiêu đề khối tĩnh (.panel>h3) cùng tầng 2. Không gộp chung selector với summary vì hai thứ có
   padding khác nhau, gộp lại sẽ phải viết đè và khó lần. */
.main .panel>h3{
  position:sticky; top:var(--dinh-2); z-index:20;
  background:var(--panel);
  margin-left:-14px; margin-right:-14px; padding:2px 14px 8px;    /* bù padding của .panel */
}

/* ── TẦNG 3: nhóm trường ──────────────────────────────────────────────────────────────────── */
/* .wf-sec là khối cha riêng của từng nhóm — dựng ở app.js. Nhờ nó, cuộn hết nhóm nào thì tiêu đề
   nhóm ấy trôi đi, nhóm kế lên thế chỗ. */
.main .wf-sec>.wf-grp,
.main .ck-sec,
.main .wf-br{
  position:sticky; top:var(--dinh-3); z-index:10;
  background:var(--panel);
}
.main .wf-sec>.wf-grp{ padding:4px 0 3px; margin:10px 0 0; }
.main .ck-sec{ padding:5px 0 4px; }
.main .wf-br{ padding:5px 0 3px; }

/* ── THỤT LỀ THEO CẤP ─────────────────────────────────────────────────────────────────────── */
/* CTO: «mỗi cấp cha con thì nên thụt lùi đầu dòng một chút để thấy rõ gia phả».
   Thụt bằng padding-left tăng dần, kèm một vạch dọc mờ để mắt lần được nhánh. */
.main .wf-sec{ padding-left:10px; border-left:1px solid var(--line); margin-left:2px; }
.main .wf-sec>.wf-grp{ margin-left:-10px; padding-left:10px; }
.main .wf-sec .wf-r{ padding-left:8px; }

/* Trên màn hẹp, ba tầng tiêu đề ăn quá nhiều chiều cao. Bỏ tầng 1 (tên trạm đã có ở thanh trên
   cùng của điện thoại) và kéo hai tầng còn lại lên. */
@media (max-width: 860px){
  :root{
    --dinh-2: var(--emg-cao);
    --dinh-3: calc(var(--emg-cao) + 40px);
  }
  .main .topbar{ position:static; }
  .main .wf-sec{ padding-left:7px; }
}

/* Ai bật «giảm chuyển động» thì sticky vẫn giữ — nó không phải hiệu ứng, nó là thông tin. */

/* Kho đã ngưng dùng — mờ đi nhưng KHÔNG ẩn: phiếu điều chuyển cũ vẫn trỏ vào nó, giấu đi là người
   tra lịch sử không hiểu mã kho ấy là gì. */
.kho-ngung td{opacity:.5}


/* ══════════════════════════════════════════════════════════════════════════════════════════
   TIMELINE VẬT TƯ — HAI ĐƯỜNG SÁT NHAU, KHÔNG CHỒNG LÊN NHAU (CTO chốt 07/08/2026)
   ══════════════════════════════════════════════════════════════════════════════════════════
   Đường trên: KẾ HOẠCH, nét đứt, màu trung tính — nó là cái nền để so, không phải thứ cần nhìn.
   Đường dưới: THỰC TẾ, nét liền dày, xanh nếu đạt kế hoạch, đỏ nếu trượt.
   Hai đường cách nhau 5px: đủ để mắt đọc là hai thứ khác nhau, đủ gần để so được độ dài.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
.vt-luong{margin:10px 0 14px}
.vt-luong-trong{opacity:.45}
.vt-ten{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;
  color:var(--dim);margin-bottom:5px}
.vt-hang{margin-bottom:12px}
.vt-dau{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12px;margin-bottom:5px}

/* Đường ray: hai vạch chồng theo trục dọc nhưng tách nhau, cộng hàng chấm mốc. */
.vt-ray{position:relative;height:24px}
.vt-duong{position:absolute;left:0;right:0;display:grid;grid-template-columns:repeat(3,1fr);gap:0}
.vt-duong-kh  {top:4px}
.vt-duong-that{top:13px}
.vt-duong-kh .vt-doan{border-top:2px dashed var(--dim);opacity:.75}
.vt-duong-that .vt-doan{border-top:3px solid transparent}
.vt-doan-that.vt-doan-dat  {border-top-color:var(--green)}
.vt-doan-that.vt-doan-truot{border-top-color:var(--red)}

/* Chấm mốc nằm giữa hai đường, đặc = ngày thật, rỗng = mới là kế hoạch. */
.vt-cham{position:absolute;left:0;right:0;top:4px;display:grid;
  grid-template-columns:repeat(4,1fr);justify-items:start}
.vt-cham span:last-child{justify-self:end}
.vt-diem{display:block;width:11px;height:11px;border-radius:50%;margin-top:3px}
.vt-dac.vt-dat  {background:var(--green);box-shadow:0 0 0 2px var(--bg)}
.vt-dac.vt-truot{background:var(--red);box-shadow:0 0 0 2px var(--bg)}
.vt-rong        {background:var(--bg);border:2px solid var(--dim)}
.vt-rong.vt-truot{border-color:var(--red)}

.vt-ngay{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-top:2px}
.vt-o{display:flex;flex-direction:column;gap:1px;font-size:11px;line-height:1.35}
.vt-o:last-child{align-items:flex-end;text-align:right}
.vt-nhan{color:var(--dim);text-transform:uppercase;font-size:9.5px;letter-spacing:.3px}
.vt-kh{color:var(--dim)}
.vt-that{font-weight:700}
.vt-that.vt-dat{color:var(--green)}
.vt-that.vt-truot{color:var(--red)}
.vt-trong{color:var(--dim);opacity:.6;font-style:italic}
/* Ô chặng cuối «Tới trạm» khi chi nhánh được ghi ngày: là <button> nhưng phải trông y hệt .vt-o,
   chỉ thêm viền nét đứt gợi ý bấm được và cây bút. Giữ cả hai lớp .vt-o (bố cục cột) + .vt-o-ghi. */
button.vt-o-ghi{background:none;border:1px dashed var(--line);border-radius:6px;cursor:pointer;
  color:inherit;font:inherit;padding:3px 5px;margin:-3px -5px;width:calc(100% + 10px)}
button.vt-o-ghi:hover{border-color:var(--teal);background:var(--panel2)}
button.vt-o-ghi:focus-visible{outline:2px solid var(--teal);outline-offset:1px}
.vt-but{color:var(--teal);font-size:10.5px;opacity:.75;margin-top:1px}
button.vt-o-ghi:hover .vt-but{opacity:1}
/* Danh sách tệp chi tiết trong cửa sửa dòng kết luận: mỗi tệp một hàng, nút gỡ nằm phải. */
.cs-tep{display:flex;gap:8px;align-items:center;justify-content:space-between;padding:3px 0;
  border-bottom:1px solid var(--line)}
.cs-tep:last-child{border-bottom:none}

.vt-tom{padding:8px 11px;border-radius:8px;background:var(--panel2);
  border-left:3px solid var(--teal);font-size:13px;margin-bottom:10px}
.vt-chuthich{display:flex;gap:14px;flex-wrap:wrap;font-size:10.5px;color:var(--dim);margin-bottom:10px}
.vt-chuthich i{display:inline-block;width:16px;height:0;vertical-align:middle;margin-right:4px}
.vt-mau-kh   {border-top:2px dashed var(--dim)}
.vt-mau-dat  {border-top:3px solid var(--green)}
.vt-mau-truot{border-top:3px solid var(--red)}
.vt-mau-cho  {border-top:3px dotted var(--dim);opacity:.5}

/* Bấm vào số quyết định thì sổ ra chi tiết — CTO giao 07/08/2026.
   Số quyết định là nút chứ không phải chữ đậm: phải nhìn ra bấm được, nếu không thì không ai bấm. */
.vt-so{background:none;border:0;padding:0;font:inherit;font-weight:700;color:var(--teal);
  cursor:pointer;display:inline-flex;align-items:center;gap:3px}
.vt-so:hover{text-decoration:underline}
.vt-so:focus-visible{outline:2px solid var(--teal);outline-offset:2px;border-radius:3px}
.vt-mui{font-size:9px;opacity:.7}

.vt-chitiet{margin:8px 0 4px;padding-left:11px;border-left:2px solid var(--teal);
  display:flex;flex-direction:column;gap:8px}
.vt-ct-khoi{background:var(--panel2);border-radius:8px;padding:9px 11px}
.vt-ct-ten{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.3px;
  color:var(--dim);margin-bottom:7px;display:flex;gap:7px;align-items:center;flex-wrap:wrap}
.vt-ct-luoi{display:grid;grid-template-columns:repeat(4,1fr);gap:7px 12px}
.vt-ct-o{display:flex;flex-direction:column;gap:1px;font-size:11.5px;line-height:1.35;
  min-width:0;overflow-wrap:anywhere}
.vt-ct-mo{font-size:12px;margin-top:6px}
.vt-tep-hang{display:flex;gap:9px;flex-wrap:wrap;margin-top:8px}
.vt-tep{font-size:12px;padding:4px 9px;border-radius:6px;background:var(--panel);
  border:1px solid var(--line);text-decoration:none}
.vt-tep:hover{border-color:var(--teal)}
.vt-tep-khong{color:var(--dim);opacity:.6;font-style:italic;border-style:dashed}

@media (max-width:860px){
  .vt-ngay{grid-template-columns:repeat(2,1fr);gap:4px}
  .vt-o:last-child{align-items:flex-start;text-align:left}
  .vt-ct-luoi{grid-template-columns:repeat(2,1fr)}
}

/* Khiếu nại hàng hoá — CTO giao 07/08/2026.
   Phiếu nhận là bảng nhập nhanh: ô số phải hẹp và thẳng cột để mắt quét được một lượt, vì người
   dùng đang đứng ở chân cột với cái điện thoại chứ không ngồi trước màn hình rộng. */
.kn-phieu input{padding:4px 6px;border-radius:5px;border:1px solid var(--line);
  background:var(--panel);color:inherit;font:inherit}
.kn-phieu input[type=number]{text-align:right;font-variant-numeric:tabular-nums}
.kn-phieu .kn-hong,.kn-phieu .kn-sai{border-color:var(--red);opacity:.85}
.kn-tom{padding:8px 11px;border-radius:8px;background:var(--panel2);
  border-left:3px solid var(--red);font-size:13px;margin-bottom:10px}
.kn-xong td{opacity:.5}
.kn-xin{margin-top:10px;padding:8px 11px;border-radius:8px;background:var(--panel2);
  border-left:3px solid var(--dim);font-size:12.5px}
.kn-ct .vt-ct-luoi{gap:8px 14px}

/* Call-off: thiết kế trên, thực tế dưới, trong cùng một ô — CTO giao 08/08/2026.
   Xếp chồng chứ không tách hai bảng: người lắp cần so hai con số bằng một cái liếc, tách bảng là
   bắt mắt phải nhảy qua lại và tự nhớ. */
.co-tk{display:block;color:var(--dim);font-size:11.5px}
.co-tt{display:block;font-weight:600;font-variant-numeric:tabular-nums}
td.co-lech{background:rgba(224,82,82,.12)}
td.co-lech .co-tt{color:var(--red)}
.co-chu{margin-top:8px;font-size:11px;color:var(--dim)}

/* ══ Ô MỐC Ở DẢI TÓM TẮT LÀ NÚT BẤM ĐƯỢC (CTO giao 08/08/2026) ══
   .m12 đổi từ div sang button để bàn phím và trình đọc màn hình dùng được. Nhưng button có kiểu
   chung của cả phần mềm (nền navy, chữ trắng, bo góc, padding) — không trung hoà thì mười hai ô
   mốc biến thành mười hai cái nút xanh. Trung hoà đúng những thuộc tính ấy, giữ nguyên phần còn
   lại của .m12 để dải mốc trông y như cũ, chỉ khác là bấm được. */
button.m12{width:100%;background:none;border:none;border-bottom:1px solid #172d40;border-radius:0;
  padding:6px 0;color:inherit;font:inherit;text-align:left;cursor:pointer}
button.m12:hover{background:var(--panel2);filter:none}
button.m12:focus-visible{outline:2px solid var(--teal);outline-offset:-2px}

/* Nháy sáng mục vừa nhảy tới. Trang dài và mục nào cũng giống mục nào — không nháy thì mắt vẫn
   phải đi tìm xem mình vừa được đưa tới đâu. Tôn trọng người tắt hiệu ứng chuyển động. */
@keyframes wf-nhay-kf{0%{background:rgba(23,162,184,.28)}100%{background:var(--panel)}}
.wf-nhay{animation:wf-nhay-kf 1.6s ease-out}
.wf-nhay > summary{color:var(--teal)}
@media (prefers-reduced-motion:reduce){
  .wf-nhay{animation:none;box-shadow:0 0 0 2px var(--teal)}
}

/* Ô nạp quyết định bị làm mờ khi không có quyền — CTO 08/08/2026: «các user khác thì ẩn hoặc làm
   mờ luôn». Chọn làm mờ CÓ GIẢI THÍCH thay vì ẩn hẳn: người đang đi tìm chức năng ấy mà không
   thấy gì sẽ tưởng phần mềm hỏng, còn thấy một dòng nói rõ ai làm được thì họ biết đi hỏi ai. */
.panel.qd-mo{opacity:.55;border-style:dashed}

/* ── DẢI THẺ SỐ CHECKLIST TRÊN MÀN TRẠM ────────────────────────────────────────────────────
   Không dùng .cards: .card và .panel dùng CÙNG một màu nền, lồng vào nhau thì thẻ chìm hẳn,
   chỉ còn viền 1px phân biệt. Dải 6 thẻ đầu màn trạm nổi được là vì nó nằm trên nền --bg.
   Ở đây dùng --panel2 để thẻ bật lên khỏi nền panel.
   Trên điện thoại 375px, grid auto-fill 150px cho 2 cột = 3 hàng — vẫn ngắn hơn hẳn bảng 15
   dòng, nhưng dùng flex cuộn ngang thì 5 thẻ nằm gọn MỘT hàng, đúng ý «chỉ tóm tắt mấy thông
   tin» chứ không đẩy màn hình dài thêm. */
.qc-dai { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; }
.qc-the {
  flex: 1 0 auto; min-width: 92px; text-align: left; cursor: pointer;
  background: var(--panel2); border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 10px; color: var(--txt); font: inherit;
}
.qc-the:hover:not(:disabled) { border-color: var(--teal); }
.qc-the:disabled { opacity: .45; cursor: default; }
.qc-the:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.qc-the .qc-so { display: block; font-size: 1.45rem; font-weight: 700; line-height: 1.1;
                 font-variant-numeric: tabular-nums; }
.qc-the .qc-nhan { display: block; font-size: .74rem; color: var(--dim); margin-top: 2px; }
.qc-the.cam .qc-so { color: var(--orange); }
.qc-the.do  .qc-so { color: var(--red); }

/* Dấu * đỏ: hồ sơ BẮT BUỘC mọi trạm phải có. Chỉ tô màu, không đổi luồng chữ — đặt trong
   <span> cạnh nhãn nên không được chiếm dòng. */
.req{color:var(--red);font-weight:700}

/* ── THƯ VIỆN ẢNH VẬT TƯ (b413) ────────────────────────────────────────────────────────────────
   Nút 📷 nằm CẠNH mã vật tư trong ô của bảng, và bảng vật tư thì đã chật. Nên nó phải nhỏ, không
   được đẩy dòng cao thêm, và vẫn phải bấm trúng bằng ngón tay trên điện thoại — vùng bấm 24px là
   mức tối thiểu còn giữ được cả hai. Không đặt nền: một chấm màu nữa trong bảng đã nhiều màu là
   thêm nhiễu, còn con mắt vốn đã tìm thấy biểu tượng máy ảnh rất nhanh. */
.anhvt {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 24px; height: 24px; margin-left: 2px; vertical-align: middle;
  border-radius: 6px; cursor: pointer; font-size: .95rem; line-height: 1;
  opacity: .78; user-select: none;
}
.anhvt:hover { opacity: 1; background: var(--panel2); }
.anhvt:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; opacity: 1; }

/* Hộp xem ảnh: lưới tự giãn, ảnh không tràn ngang trên điện thoại. */
.anhvt-ls { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
.anhvt-ls figure { margin: 0; }
.anhvt-ls img {
  width: 100%; height: auto; max-height: 60vh; object-fit: contain;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel2);
}
.anhvt-ls figcaption { font-size: .74rem; color: var(--dim); margin-top: 4px; }

/* Mốc không thuộc đơn vị mình: hiện ổ khoá đúng chỗ cây bút chì, cùng kích thước nên dòng không
   xô lệch giữa mốc gõ được và mốc chỉ xem. Không ẩn hẳn — ẩn thì người dùng tưởng phần mềm quên
   vẽ nút, còn ổ khoá kèm chú giải nói rõ phải báo ai. */
.ms-khoa { display: inline-flex; align-items: center; justify-content: center;
           opacity: .45; cursor: not-allowed; }

/* ---------- Nút Gỡ trên thẻ người giám sát (CTO 12/08/2026) ----------
   Trước hôm nay đây là một dấu ✕ trần, đặt ngay sau đoạn chữ nhỏ «· INFRA · 7 trạm» — mắt đọc nó
   thành một ký tự của dòng chữ ấy chứ không thành một nút bấm được. Có chữ «Gỡ» đi kèm, có nền
   riêng và có viền, thì nó tự nói mình là nút. Kế thừa màu chữ của thẻ để đọc được trên cả thẻ
   sáng lẫn thẻ nền teal. */
/* Nút gỡ trên chip — dùng chung cho vai giám sát (.gs-go) và vai người quản lý trạm (.pt-go,
   việc #131). Một luật trình bày, hai chỗ dùng: tách ra hai bộ quy tắc thì ngày ai đó chỉnh một
   bộ là hai nút cùng nghĩa trông khác nhau trên cùng một màn hình. */
.gs-go, .pt-go { margin-left: 8px; padding: 1px 8px; border-radius: 12px; font: inherit; font-size: 11.5px;
         font-weight: 700; line-height: 1.6; cursor: pointer; color: inherit;
         background: rgba(0,0,0,.16); border: 1px solid rgba(0,0,0,.28); }
.gs-go:hover, .pt-go:hover { background: var(--red, #ef4444); color: #fff; border-color: var(--red, #ef4444); }
.gs-go:disabled, .pt-go:disabled { opacity: .5; cursor: progress; }

/* ---------- Dòng đếm cuối danh sách gợi ý (CTO 12/08/2026) ----------
   Bốn ô gợi ý đều có trần ở máy chủ và tới hôm nay không ô nào nói mình vừa cắt. Dòng này luôn
   hiện số dòng đang thấy; khi chạm trần thì đổi câu thành «còn nữa, gõ thêm để lọc». Không mang
   data-i nên bấm vào không chọn nhầm ai — nó là chú thích, không phải một lựa chọn. */
.mention-dem { padding: 5px 10px; font-size: 11.5px; color: var(--dim, #8aa0b4);
               border-top: 1px solid var(--line, #1e2d3d); background: var(--panel2, #0d1826);
               position: sticky; bottom: 0; }

/* ---------- Ba nút trạng thái của bảng «Ai được nhận thư» (CTO 17/08/2026, việc #185) ----------
   BA NÚT CHỨ KHÔNG PHẢI MỘT Ô TICK, vì trạng thái ở đây có ba: cho nhận · cấm · không đặt luật.
   Ô tick chỉ diễn được hai, mà «không đặt» khác «cấm» thật sự — gỡ luật cá nhân là trả người ấy
   về theo nhóm, còn đặt cấm là giữ im dù nhóm có bật. Mất trạng thái thứ ba thì không có đường
   lùi sau khi lỡ tay.
   Nút đang có hiệu lực tô đặc theo nghĩa của nó (xanh = cho, đỏ = cấm) để nhìn lướt cả bảng là
   thấy ngay dòng nào đang bật; nút không hiệu lực để chìm, đủ đọc mà không tranh mắt. */
.qt-seg { display: inline-flex; border: 1px solid var(--line, #1e2d3d); border-radius: 999px;
          overflow: hidden; }
.qt-n { border: 0; background: transparent; color: var(--dim, #8aa0b4); cursor: pointer;
        font: inherit; font-size: 11.5px; font-weight: 600; padding: 3px 11px; white-space: nowrap; }
.qt-n + .qt-n { border-left: 1px solid var(--line, #1e2d3d); }
.qt-n:not(.on):hover { color: var(--txt, #e6edf3); background: var(--panel2, #0d1826); }
.qt-n.cho.on { background: var(--green, #37b06f); color: #04220f; }
.qt-n.cam.on { background: var(--red, #e05252); color: #2a0606; }
.qt-n.bo.on  { background: var(--panel2, #0d1826); color: var(--txt, #e6edf3); }
/* Cấp 4 xem được nhưng không sửa được (CTO chốt). Nút mờ và con trỏ báo cấm, kèm câu giải thích
   ở đầu màn — nút biến mất thì người xem tưởng màn hình hỏng, không tưởng mình thiếu quyền. */
.qt-n:disabled { opacity: .45; cursor: not-allowed; }


/* ═══════════════════════════════════════════════════════════════════════════════════════════
   «THỰC HIỆN KẾT LUẬN» — BẢNG PHẢI VỪA KHUNG, VÀ TRÊN ĐIỆN THOẠI THÌ THÀNH THẺ  (việc #200)
   ═══════════════════════════════════════════════════════════════════════════════════════════
   ĐO ĐƯỢC 18/08/2026, và đây là lý do 11 kết luận của cuộc họp 13/08 không có LẤY MỘT dòng báo
   tiến độ nào sau 5 ngày:

     · máy tính 1280px : khung .tbl-wrap rộng 964, bảng rộng 1214 → thừa 250px;
     · điện thoại 387px: khung rộng 326, bảng vẫn 1214          → thừa 888px;
     · nút «Ghi tiến độ» — cửa DUY NHẤT để báo tiến độ — nằm ở cột thứ tư, mốc x=989 trên điện
       thoại, tức là phải kéo ngang gần một màn hình mới thấy.

   Người dùng không lười; cái nút không có ở chỗ họ nhìn. Bảng rộng 1214 vì MỘT cột: «Nội dung»
   chiếm 744px (61%) do chuỗi 110 ký tự chạy một dòng không xuống hàng, và nó đẩy hai cột cuối
   ra ngoài mép phải.

   Hai lớp vá, theo đúng thứ tự ấy:
     1. chặn bề ngang cột nội dung → bảng vừa khung trên máy tính, không cần kéo ngang;
     2. dưới 860px thì hàng thành THẺ xếp dọc → trên điện thoại không còn khái niệm «ngoài mép».

   Phạm vi bám vào #klBody nên không màn nào khác bị đụng. */

/* `white-space:nowrap` thừa hưởng từ luật bảng chung là thứ THẬT SỰ thổi cột nội dung lên 744px:
   chuỗi 110 ký tự không được phép xuống hàng nên nó đẩy bảng rộng ra bằng đúng chiều dài câu.
   Chặn bề ngang mà không mở xuống hàng thì chữ chỉ tràn ra ngoài ô — phải làm cả hai. */
#klBody td.kl-nd{max-width:460px;white-space:normal}
#klBody td.kl-nd a,
#klBody td.kl-nd b,
#klBody td.kl-nd div{white-space:normal;overflow-wrap:anywhere}

@media (max-width:860px){
  /* Bỏ hàng tiêu đề: mỗi ô tự mang nhãn của mình qua data-nhan, nên hàng th thành thừa. Ẩn từng
     ô th (không ẩn cả tr) để không phải gắn thêm lớp vào bảy chỗ dựng bảng khác nhau. */
  #klBody .tbl-wrap table th{display:none}
  #klBody .tbl-wrap{overflow-x:visible}
  #klBody .tbl-wrap table,
  #klBody .tbl-wrap tbody,
  #klBody .tbl-wrap tr,
  #klBody .tbl-wrap td{display:block;width:auto;max-width:none}
  /* `table.wide{min-width:640px}` (dòng 411) là thứ giữ bảng rộng 640px NGAY CẢ khi hàng đã
     thành thẻ — đo được: máy 375px mà thẻ vẫn 640px, tức vẫn phải kéo ngang. Ràng buộc ấy sinh
     ra cho bảng THẬT, thẻ thì không cần. */
  #klBody .tbl-wrap table.wide{min-width:0;width:100%}
  #klBody .tbl-wrap tr{
    border:1px solid var(--line,#1e2d3d); border-radius:10px;
    padding:10px 12px; margin-bottom:10px; background:var(--panel,#101c28);
  }
  #klBody .tbl-wrap td{border:0;padding:3px 0;white-space:normal}
  #klBody .tbl-wrap td *{white-space:normal;overflow-wrap:anywhere}
  /* Nhãn cột đi kèm ô — không có nó thì ba dòng chữ rời nhau đọc ra vô nghĩa. */
  #klBody .tbl-wrap td[data-nhan]::before{
    content:attr(data-nhan); display:block;
    font-size:11px; letter-spacing:.4px; text-transform:uppercase;
    color:var(--dim,#8aa0b4); margin-bottom:2px;
  }
  /* Nút hành động chiếm cả bề ngang thẻ: đây là thứ người ta mở màn hình này để bấm. */
  #klBody .tbl-wrap td button{width:100%;margin-top:6px}
  /* Cột freeze dán trái không còn nghĩa khi hàng đã là thẻ. */
  #klBody .tbl-wrap table td:first-child{position:static;background:none}
}


/* ═══════════════════════════════════════════════════════════════════════════════════════════
   «QUẢN LÝ TÀI KHOẢN» — NĂM NÚT HÀNH ĐỘNG NẰM NGOÀI MÉP MÀN HÌNH  (việc #202)
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Tìm ra bằng cách quét 40 màn hình bằng trình duyệt thật, 18/08/2026, sau khi vá cùng bệnh ở
   màn «Thực hiện Kết luận» (#200). Trong 40 màn chỉ MỘT màn có nút hành động rơi ra ngoài mép,
   và đúng là màn dùng nhiều nhất trong đợt go-live này:

     · máy tính 1280px : khung .tbl-wrap rộng 964, bảng rộng 1.345 → thừa 381px;
     · điện thoại 387px: khung rộng 326, bảng vẫn 1.345;
     · cả năm nút — «Reset mật khẩu · Khoá/Mở · Sửa · Quyền · Xoá» — nằm ở mốc x=1.384…1.583
       trên máy tính (mép phải màn hình là 1.280) và x=1.160…1.351 trên điện thoại.

   Nghĩa là: KHÔNG một nút quản trị tài khoản nào hiện ra ở lần nhìn đầu tiên. Phải biết rằng
   bảng kéo ngang được thì mới với tới. Đây là màn mà từ nay tới 01/09 sẽ có sóng cấp lại mật
   khẩu (mọi người bị buộc đổi mật khẩu lần đăng nhập đầu), và b430 đã mở quyền ấy cho Giám đốc
   Vùng với Trưởng phòng — mở cửa xong mà tay nắm cửa lại nằm ngoài tường.

   BẢNG RỘNG 1.345px VÌ HAI CHỖ, không phải vì nhiều cột:

   (1) `.ellip` khai `display:inline-block` (dòng 200) vì nó sinh ra cho <span>. Cột «Đơn vị»
       lại dùng HAI <div class="ellip"> chồng lên nhau — inline-block biến hai div ấy thành hai
       khối NẰM CẠNH NHAU: đo được chức danh ở x=442 còn đơn vị ở x=559, cùng một dòng y. Vừa
       sai bố cục (dòng phụ đáng lẽ nằm dưới, hoá ra dính bên phải, đọc thành một câu liền), vừa
       làm cột rộng 424px thay vì 220px. Cả hệ chỉ có ĐÚNG hai chỗ viết <div class="ellip">, và
       cả hai đều ở bảng này, đều muốn xếp dọc — nên sửa thẳng ở `div.ellip` là đủ và không đụng
       chỗ nào khác.

   (2) Ô năm nút thừa hưởng `white-space:nowrap` của luật bảng chung → năm nút xếp một hàng
       ngang 317px. Cho xuống hàng thì còn 140px.

   ĐO LẠI SAU KHI VÁ, cùng trình duyệt cùng dữ liệu: bảng 1.345 → 964, đúng bằng bề ngang khung.
   Số nút nằm ngoài mép: 99 → 0. */

/* <div class="ellip"> phải xếp dọc như mọi <div> khác. .ellip đặt inline-block cho <span> —
   không được để luật ấy đổi nghĩa của thẻ div. */
div.ellip{display:block}

/* ── LUẬT CHUNG: BẢNG THÀNH THẺ TRÊN MÀN NHỎ  (lớp .the-nho) ─────────────────────────────
   Đây là lần thứ BA phải viết bộ luật này (Kết luận b530, Tài khoản b531, hai màn vật tư b532),
   nên nó thành một lớp dùng chung thay vì chép thêm lần nữa. Bảng nào muốn thành thẻ dưới 860px
   thì gắn `class="the-nho"`, và mỗi ô gắn `data-nhan="tên cột"` — không phải viết lại CSS.

   CÒN NỢ: khối `#klBody` (việc #200) vẫn giữ bản chép riêng của nó. Gộp nốt vào đây thì phải đo
   lại màn Kết luận bằng cú bấm, mà tối nay không còn chỗ cho việc ấy — để đợt sau, đừng quên. */
.the-nho td.o-nut{white-space:normal;max-width:190px}
.the-nho td.o-nut button{margin:2px 4px 0 0}

@media (max-width:860px){
  /* Ẩn từng ô th chứ không ẩn cả tr hay thead: có bảng khai <thead>, có bảng để <tr><th> trần. */
  .the-nho th{display:none}
  /* …nhưng hàng CHỈ có th thì ẩn cả hàng: luật `.the-nho tr{border;padding}` bên dưới vẽ nó thành một
     viên thuốc rỗng đứng trên mỗi bảng (đo b830 trên khung 652px, có từ b530 mà chưa ai nhìn ra). */
  .the-nho tr:not(:has(td)){display:none}
  .the-nho, .the-nho tbody, .the-nho tr, .the-nho td{display:block;width:auto;max-width:none}
  /* `table.wide{min-width:640px}` (dòng 411) giữ bảng rộng 640px NGAY CẢ khi hàng đã thành thẻ —
     đo được: máy 375px mà thẻ vẫn 640px. Ràng buộc ấy sinh ra cho bảng THẬT, thẻ thì không cần. */
  .tbl-wrap .the-nho.wide{min-width:0;width:100%}
  .the-nho tr{
    border:1px solid var(--line,#1e2d3d); border-radius:10px;
    padding:10px 12px; margin-bottom:10px; background:var(--panel,#101c28);
  }
  .the-nho td{border:0;padding:3px 0;white-space:normal}
  /* Số canh phải chỉ có nghĩa khi còn cột để so hàng trên hàng dưới. Trong thẻ, nhãn (::before) đứng
     trên và canh trái — số lệch sang mép phải là hai thứ của một ô rời nhau (b853, sáu bảng cl-qc). */
  .the-nho td.num{text-align:left}
  .the-nho td *{white-space:normal;overflow-wrap:anywhere}
  /* .ellip cắt đuôi bằng dấu … chỉ có nghĩa khi bề ngang bị bó. Trong thẻ thì không bó nữa. !important vì hai
     thứ nặng hơn luật này đang cắt chữ trong thẻ (b854): `table.freeze td:first-child .ellip{max-width:84px}`
     (dòng ~537, 13 bảng freeze sang thẻ — mã trạm ô đầu còn 84 px) và bảy chỗ app.js ghim nội tuyến
     `style="max-width:130px"` cho tên người/đơn vị — trên bàn tên ngắn chưa lộ, tên Tanzania ba chữ là cắt. */
  .the-nho td .ellip{max-width:none!important;overflow:visible;text-overflow:clip}
  /* Nhãn cột đi kèm ô — không có nó thì mấy dòng chữ rời nhau đọc ra vô nghĩa. */
  .the-nho td[data-nhan]::before{
    content:attr(data-nhan); display:block;
    font-size:11px; letter-spacing:.4px; text-transform:uppercase;
    color:var(--dim,#8aa0b4); margin-bottom:2px;
  }
  /* Ô rỗng (cột không có gì cho dòng này) thì đừng để lại một cái nhãn trơ. */
  .the-nho td:empty{display:none}
  .the-nho td.o-nut{max-width:none;margin-top:6px}
  .the-nho td.o-nut button{margin:0 6px 6px 0}
  /* Cột dán trái hết nghĩa khi hàng đã là thẻ. */
  .tbl-wrap .the-nho td:first-child{position:static;background:none}
}


/* Việc #221 — một NGÀY hỏng, không phải một ô hỏng: tô cả dòng thì mắt bắt được ngay khi
   lướt dải nhịp, chứ một con số 0 nằm lẫn trong cột thì trôi qua. */
tr.row-red > td{background:rgba(224,82,82,.10)}

/* ══ MÀN HÌNH TỰ GIẢI THÍCH — nút (?) và hộp nội dung (việc #227) ══════════════════════════
   Không có luật nào cho `.card` ở đây, và đó là chủ ý: bản dựng đầu gắn dấu ⓘ lên cả 113 thẻ
   số, CTO bác vì cồng kềnh. Nay giao diện KHÔNG đổi một chút nào ở thẻ — thẻ chỉ mang thêm
   thuộc tính data-ct vô hình để hộp giải thích đọc được giá trị và đường đi của chính nó. */
.ct-nut{margin-left:2px;padding:2px 9px;font-weight:700;line-height:1.5;flex-shrink:0}
/* ── HƯỚNG DẪN GẬP SAU DẤU ❗ (góp ý GY-0003, 25/08/2026) ─────────────────────────────────────
   Đo được: 116 đoạn hướng dẫn dài hơn 120 ký tự vẽ thường trực ra màn, đoạn dài nhất 591 ký tự.
   Trên điện thoại mỗi đoạn như thế đẩy nội dung thật xuống gần một màn hình.

   Dấu ❗ phải NHỎ mà vẫn BẤM TRÚNG bằng ngón tay: chữ 11px cho nó không tranh chỗ với nội dung.

   ĐO THẬT TRÊN MÁY 25/08/2026, KHÔNG TIN CON SỐ TRONG ĐẦU. Bản đầu nới vùng chạm bằng padding và
   chú thích ở đây ghi «24px». Mở trình duyệt bấm rồi đo `getBoundingClientRect()`: ô bấm thật ra
   **31 × 23 px** — non nửa mức 44 px mà cả Apple lẫn Android đều lấy làm sàn. Tính năng này sinh
   ra CHO ĐIỆN THOẠI, nên một nút khó bấm là tính năng chết.

   ĐÃ THỬ NỚI BẰNG VÙNG PHỦ VÔ HÌNH (`::before` position:absolute, inset âm) VÀ ĐÃ BỎ. Nó nới được
   thật — đo ra 43 × 43 — nhưng vùng ấy trùm sang dòng trên và dòng dưới, mà nó là phần tử có định
   vị nên SƠN ĐÈ lên hàng xóm và nhận luôn cú bấm của họ. Đo trên đúng màn Bảng điều hành: 2 trong
   3 dấu ❗ có phần tử bấm được nằm trong vùng nới, và `elementFromPoint` tại điểm chồng lấn trả về
   chính cái nút ❗ — tức hai ô bảng bên cạnh bị cướp cú bấm. Một nút dễ bấm mà làm hỏng nút khác
   thì không phải là sửa, chỉ là dời chỗ hỏng.

   Bài học chung: MUỐN NHẬN CÚ BẤM Ở ĐÂU THÌ PHẢI CHIẾM CHỖ Ở ĐẤY. Padding to cộng margin âm cũng
   dính đúng cái bẫy này — margin âm chỉ bỏ phần chỗ ĐẶT TRƯỚC, cái hộp vẫn phủ ra ngoài và vẫn
   cướp bấm.

   Nên nút phóng to THẬT và chiếm chỗ thật: 14px chữ + 10/11px padding ≈ 36 × 34. Chưa tới sàn 44,
   và đó là trần lương thiện của một con dấu nằm GIỮA DÒNG CHỮ: muốn 44 thì dòng chứa nó phải cao
   44, tức đội bố cục của cả 53 màn lên. Giá phải trả ở đây là dòng có ❗ cao thêm chừng chục pixel,
   chỉ một dòng, và hướng dẫn thì mặc định vẫn gập.

   MARGIN PHẢI KHÔNG ĐƯỢC ÂM. Bản đầu để `-2px` và đo ra đúng 2px: mép phải của nút nằm ở 51,8 còn
   chữ «S» đầu câu bắt đầu ở 49,8 — nút có nền riêng nên nó SƠN ĐÈ lên chữ. Nhìn ảnh chụp mới thấy,
   `soat-lop-css-mo-coi` không bắt được loại này.

   `display:inline` cho `.meo` là CÓ Ý: nó thay chỗ của một đoạn chữ nằm sẵn trong vỏ chữ mờ của
   53 màn khác nhau. Đổi sang block là tự ý chen thêm một dòng vào bố cục của cả 53 chỗ ấy. */
.meo{display:inline}
.meo-nut{border:0;background:none;cursor:pointer;font-size:14px;line-height:1;
  padding:10px 11px;margin:0 3px 0 0;color:var(--dim);border-radius:6px;vertical-align:middle}
.meo-nut:hover{color:var(--teal);background:var(--panel2)}
.meo-nut[aria-expanded="true"]{color:var(--teal)}
.meo-chu{display:inline}
/* Gập rồi thì KHÔNG chiếm chỗ nào — `hidden` mặc định là display:none, nhưng .meo-chu vừa khai
   display:inline nên nó thắng `hidden` nếu không nói rõ. Đây là cái bẫy im lặng của cặp
   [hidden] + display: chữ vẫn hiện nguyên mà thuộc tính thì bảo đã ẩn. */
.meo-chu[hidden]{display:none}

.ct-nut:hover{border-color:var(--teal);color:var(--teal)}
.ct-gt{white-space:pre-wrap;color:var(--txt);font-size:13px;line-height:1.6;
  background:var(--panel2);border:1px solid var(--line);border-radius:8px;padding:12px 14px;margin-bottom:14px}
/* Mỗi con số một khối, ngăn nhau bằng đường kẻ chứ không bằng khoảng trắng: hộp này thường dài
   hơn một màn hình, và khi cuộn thì khoảng trắng không nói được chỗ nào hết một mục. */
.ct-so{border-top:1px solid var(--line);padding:12px 0}
.ct-so:first-child{border-top:0;padding-top:0}
.ct-ten{font-weight:700;font-size:14px;margin-bottom:8px}
.ct-tri{font-weight:400;font-size:12px;color:var(--teal);background:var(--panel2);
  border:1px solid var(--line);border-radius:12px;padding:2px 9px;margin-left:6px;
  font-variant-numeric:tabular-nums;white-space:nowrap}
.ct-h{font-size:10.5px;color:var(--dim);text-transform:uppercase;letter-spacing:.5px;margin:10px 0 3px}
.ct-so>div:not(.ct-h):not(.ct-ten){font-size:13px;line-height:1.6}
/* CÁCH TÍNH đọc khác NGHĨA bằng mắt, vì nó là thứ khác: một bên là lời, một bên là vị ngữ đang
   chạy có tên bảng và tên cột. Nền riêng + chữ hơi nhỏ để người không cần kỹ thuật lướt qua
   được, mà người cần đối chiếu vẫn thấy đủ chữ. */
.ct-so .ct-ct{background:var(--panel2);border-left:3px solid var(--navy);border-radius:0 6px 6px 0;
  padding:8px 11px;font-size:12.5px;color:var(--txt)}
.ct-so button{margin-top:10px}
.ct-chan{margin-top:14px;padding-top:10px;border-top:1px solid var(--line);
  font-size:11px;color:var(--dim)}
@media(max-width:767px){
  .ct-tri{display:inline-block;margin:4px 0 0}
  .ct-gt{font-size:12.5px;padding:10px 11px}
}

/* ═══ LƯỚI ẢNH BẰNG CHỨNG CHECKLIST (việc #277) ═══════════════════════════════════════════════
   Ô vuông cố định chứ không theo tỉ lệ ảnh: ảnh hiện trường có cả dọc lẫn ngang, để tự do thì
   lưới răng cưa và mắt phải dò từng hàng. object-fit: cover cắt mép chứ không bóp méo — người
   xem cần nhận ra CẢNH trong ảnh, mà cảnh bị bóp thì khó nhận hơn cảnh bị cắt mép. */
.cl-anh { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.cl-anh-o {
  position: relative; width: 92px; height: 92px; padding: 0; overflow: hidden;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel2);
  cursor: zoom-in; line-height: 0;
}
.cl-anh-o img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cl-anh-o:hover { border-color: var(--teal); }
.cl-anh-o:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.cl-anh-vid { display: flex; align-items: center; justify-content: center;
              font-size: 1.6rem; color: var(--teal); cursor: pointer; }
/* Huy hiệu khoảng cách: nằm đè mép dưới, nền tối để đọc được trên cả ảnh sáng lẫn ảnh tối. */
.cl-anh-gps {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 2px 4px;
  font-size: .62rem; line-height: 1.3; text-align: center;
  background: rgba(0,0,0,.62); color: #ffd479;
}
.cl-anh-gps.xa { color: #ff9a8b; }

/* ═══ BỘ XEM ẢNH HIỆN TRƯỜNG (b826, nối tiếp đèn chiếu việc #277) ═══════════════════════════════
   Nền tối phủ kín, hộp ảnh xếp dọc: đầu (đang xem gì) · sân (ảnh, mũi tên, huy hiệu phóng) ·
   chân (lật, phóng, chú thích, mở gốc). Sân co giãn còn đầu và chân giữ nguyên, nên ảnh luôn
   lấy hết chỗ thừa mà hai dòng chữ không bao giờ bị đẩy ra ngoài màn.
   z-index 9500: trên hộp modal (900) và trên hộp góp ý (9000) — ảnh mở từ trong hộp nào cũng
   phải nằm trên hộp ấy. Chỉ thanh khẩn (9999) đứng trên nó, và đó là cố ý. */
.anh-lop {
  position: fixed; inset: 0; z-index: 9500; background: rgba(8,10,14,.965);
  display: flex; align-items: center; justify-content: center;
  padding: 10px;
  padding-top: max(10px, env(safe-area-inset-top));
  padding-bottom: max(10px, env(safe-area-inset-bottom));
  overscroll-behavior: contain;
}
body.anh-mo { overflow: hidden; }        /* đừng để trang dưới cuộn theo ngón tay lật ảnh */
.anh-hop {
  display: flex; flex-direction: column; gap: 8px;
  width: min(1200px, 100%); height: min(96vh, 100%); cursor: default;
}
.anh-dau { display: flex; align-items: flex-start; gap: 10px; color: #e9edf4; }
.anh-ten { flex: 1; font-size: .86rem; line-height: 1.45; overflow-wrap: anywhere; }
/* SÂN: một mặt phẳng nhận mọi cử chỉ. touch-action:none — trình duyệt không được cuộn hay phóng
   TRANG khi ngón tay đang ở đây; user-select:none — kéo ảnh không bôi đen chữ xung quanh. */
.anh-than {
  flex: 1; min-height: 0; position: relative; overflow: hidden; border-radius: 8px;
  touch-action: none; user-select: none; -webkit-user-select: none;
}
.anh-truot {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  will-change: transform;
}
/* Ảnh vừa khung theo max-width/max-height; phóng bằng transform quanh TÂM nên cỡ bố cục
   (clientWidth/Height) không đổi — chính cỡ ấy dùng để kẹp ảnh trong mép khi dời. */
.anh-img {
  max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 8px;
  transform-origin: center center; -webkit-user-drag: none; cursor: zoom-in;
}
.anh-img.keo { cursor: grab; }
.anh-img.keo:active { cursor: grabbing; }
.anh-video { font-size: 1rem; padding: 10px 16px; }
.anh-cho {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  max-width: 90%; padding: 8px 12px; border-radius: 8px; text-align: center;
  background: rgba(0,0,0,.55); color: #cfd6e2; font-size: .85rem; line-height: 1.4;
}
.anh-cho.loi { color: #ff9a8b; }
/* Mũi tên hai bên cho CHUỘT; màn chạm vuốt là lật nên giấu đi cho khỏi che ảnh. */
.anh-mui {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 44px; height: 72px; border: 0; border-radius: 8px; padding: 0;
  background: rgba(0,0,0,.4); color: #fff; font-size: 2rem; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: opacity .18s;
}
.anh-mui-trai { left: 8px; }
.anh-mui-phai { right: 8px; }
.anh-mui:hover { background: rgba(0,0,0,.72); }
.anh-mui[disabled] { opacity: .25; cursor: default; }
.anh-zoom {
  position: absolute; top: 8px; right: 8px; padding: 2px 8px; border-radius: 10px;
  background: rgba(0,0,0,.55); color: #fff; font-size: .72rem; pointer-events: none;
}
.anh-zoom-nut { display: inline-flex; gap: 2px; align-items: center; }
.anh-zoom-nut button { min-width: 30px; }
.anh-chan {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  color: #cfd6e2; font-size: .78rem;
}
.anh-meo { flex-basis: 100%; }
/* CHẠM MỘT LẦN LÊN ẢNH: ẩn hết chữ, nền đen tuyền, ảnh lấy trọn màn — đúng nếp Photos. */
.anh-lop.anh-tron { background: #000; padding: 0; }
.anh-lop.anh-tron .anh-hop { width: 100%; height: 100%; gap: 0; }
.anh-lop.anh-tron .anh-dau, .anh-lop.anh-tron .anh-chan, .anh-lop.anh-tron .anh-mui { display: none; }
.anh-lop.anh-tron .anh-than, .anh-lop.anh-tron .anh-img { border-radius: 0; }
@media (hover: none) and (pointer: coarse) {
  .anh-mui { display: none; }
}
@media (max-width: 767px) {
  .cl-anh-o { width: 78px; height: 78px; }
  .anh-hop { height: 100%; }
  .anh-chan { gap: 6px; }
}
/* Ô ảnh 64 điểm trong bảng và trong dòng ảnh (sổ VCC, checklist từng mục, bằng chứng) — cùng
   luật cắt mép với lưới 92 điểm, chỉ nhỏ hơn để một hàng bảng không cao gấp ba. */
.cl-anh-o.nho { width: 64px; height: 64px; }
.vn-anh .cl-anh-o { flex: 0 0 auto; }
.bc-row .cl-anh-o { flex: 0 0 auto; }
/* Ảnh vật tư: nút bọc ảnh trong lưới, không viền, không nền — chỉ đổi con trỏ để báo bấm được. */
.anhvt-o { display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in; line-height: 0; }

/* ── #279 Dòng chảy công đoạn ────────────────────────────────────────────────────────────────
   Dải CUỘN NGANG trong hộp riêng (bài học #258: tràn ngang phải nằm trong container của nó,
   không bao giờ đẩy cả trang). Ô tái dùng .moc-o của tháp điều hành cho quen mắt.
   Cụm viền đứt = các mốc chạy SONG SONG (4a-C) — viền nói thay lời chú thích.
   .dc-vd = quán quân theo thước đang chọn (1a), viền cam để mắt rơi vào đúng chỗ tệ nhất. */
.dc-dai{display:flex;gap:8px;overflow-x:auto;padding:10px 2px 4px}
.dc-dai .moc-o{flex:0 0 172px;min-width:172px}
.dc-cum{display:flex;gap:8px;flex:0 0 auto;border:1px dashed var(--teal);border-radius:10px;
  padding:8px 6px 6px;position:relative;margin-top:2px}
.dc-cum .dc-nhan{position:absolute;top:-9px;left:8px;font-size:10px;color:var(--teal);
  background:var(--panel2);padding:0 5px;border-radius:4px}
.dc-vd{box-shadow:inset 0 0 0 2px var(--orange)}

/* ═══ Vị trí thửa đất — ba hộp chọn ĐVHC (việc #278) ═══
   flex-wrap để ba hộp tự xuống dòng trên điện thoại hẹp; min-width:0 cho phép hộp co thay vì
   đẩy tràn ngang (bài học #258 — hai con số không có dây nối nhau). */
.dvh-hang { display: flex; gap: 6px; flex-wrap: wrap; }
.dvh-hang select { flex: 1 1 140px; min-width: 0; }

/* ── TOOLTIP TIÊU CHÍ CON SỐ (CTO 25/08/2026) ────────────────────────────────────────────────
   Neo theo toạ độ MÀN HÌNH (position: fixed) chứ không theo thẻ cha: bốn thẻ mức khẩn nằm trong
   .cards, mà .tbl-wrap quanh bảng có overflow riêng — hộp neo theo cha sẽ bị chính khung cuộn ấy
   cắt mất một nửa. */
#ct-tip {
  position: fixed;
  z-index: 3000;
  max-width: 380px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  color: var(--txt);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .45);
  font-size: 12.5px;
  line-height: 1.45;
  pointer-events: none;   /* hộp không được chắn chính con số nằm dưới nó */
}
#ct-tip .ct-tip-ten { font-weight: 700; margin-bottom: 6px; }
#ct-tip .ct-tip-h {
  margin-top: 8px;
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--dim);
}
/* Con trỏ nói rằng có gì để đọc. Thẻ bấm được giữ nguyên bàn tay — đích đến của cú bấm quan
   trọng hơn lời giải thích. */
[data-ct]:not(.click):not(.card), [data-ct-ref],
[data-ct-tho]:not(.click) { cursor: help; }

/* Dải 30 ô của dashboard đội VCC (b723): mỗi ô một ngày, xanh là có báo cáo — lời giải nằm ở
   title từng ô, không cần trục hay chú giải riêng cho một dải nhỏ. */
.vcc-o {
  display: inline-block;
  width: 15px; height: 15px;
  border-radius: 3px;
  margin: 1px;
  background: var(--line);
  cursor: help;
}
.vcc-o.on { background: var(--green); }

/* ── Bức tranh điều hành (b738) ── */
.bt-day{display:flex;gap:6px;overflow-x:auto;padding:4px 0 8px;align-items:stretch}
.bt-the{flex:0 0 auto;min-width:132px;max-width:150px;border:1px solid var(--line);border-left:4px solid var(--line);border-radius:8px;padding:8px;background:var(--panel)}
.bt-the.do{border-left-color:var(--red)} .bt-the.cam{border-left-color:var(--orange)}
.bt-the .bt-ten{font-size:11.5px;font-weight:700;min-height:2.6em}
.bt-the .bt-ton b{font-size:20px}
.bt-nhom{flex:0 0 auto;writing-mode:vertical-rl;transform:rotate(180deg);font-size:10.5px;color:var(--dim);text-transform:uppercase;letter-spacing:.4px;padding:4px 0;border-right:1px solid var(--line)}
.bt-nhanh{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;margin-bottom:16px}
.bt-nhanh .panel{margin-bottom:0}
.bt-cd{padding-left:18px} .bt-cd li{margin:6px 0;line-height:1.45}
/* minmax(420px) trên khung 355 px là ép cột 420 → cả trang tràn ngang (đo b854: docSW 430 ở 375 px, hai
   panel «vận tốc» và «trễ KH»); min(420px,100%) giữ nguyên máy tính mà không bao giờ rộng hơn khung. */
.bt-mach{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(420px,100%),1fr));gap:12px;margin-bottom:16px}
.bt-mach .panel{margin-bottom:0;min-width:0}
.bt-nho td,.bt-nho th{padding:4px 6px;font-size:12.5px}
.bt-cot-bar{width:36%} .bt-bar{height:8px;background:var(--orange);border-radius:4px;min-width:2px}
.bt-im td{background:rgba(255,80,80,.07)}
.bt-thanh{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-bottom:8px}
.bt-thanh .bt-cach{margin-left:12px}
.bt-mt th{vertical-align:top} .bt-mt .bt-cot-ten{font-weight:400;font-size:10px;color:var(--dim);white-space:nowrap;max-width:88px;overflow:hidden;text-overflow:ellipsis}
.bt-mt tr.bt-tong td{font-weight:700;border-bottom:2px solid var(--line)}
.bt-tim{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.bt-tim input{min-width:260px}
.bt-tim-kq{display:flex;gap:6px;flex-wrap:wrap}
tr.bt-ke td{background:rgba(0,180,200,.08)} tr.bt-lo td{background:rgba(255,160,0,.08)}
.teal{color:var(--teal)}
/* ── Kế hoạch tháng b782: ô số bấm được, lưới trạm × mốc, cờ SLA ───────────────────── */
.kh-so{background:none;border:0;padding:0 2px;font:inherit;font-weight:700;color:var(--teal);cursor:pointer;text-decoration:underline dotted}
.kh-so.muted{color:var(--dim);font-weight:400}
.kh-grid td{vertical-align:top;white-space:nowrap}
.kh-grid input[type=date],.kh-tram input[type=date]{width:138px}
.kh-o{display:inline-flex;gap:3px;align-items:center}
.kh-phu{font-size:11px;color:var(--dim);margin-top:2px;display:flex;gap:6px;align-items:center;min-height:14px}
.kh-canh{color:var(--orange);font-weight:700;cursor:help}
/* Dòng hiện trường dưới mã trạm ở bảng tỉnh (b798): nguồn điện, khoảng cách tới đường điện,
   ngày khảo sát, có phác thảo hay chưa. Nhạt hơn tên trạm ngay trên nó — nó là NỀN để đọc 12 ô
   ngày bên phải, không phải thứ mắt phải dừng lại. */
.kh-ht{color:var(--dim);margin-top:2px}

/* Nút trong ô kế hoạch: ✓ nhận gợi ý máy, ✕ xoá riêng ô này — hai nút thay phiên nhau đứng trong
   .kh-o nên cùng cỡ, và ✕ nhạt hơn ô ngày để không tranh mắt người đang gõ. */
.kh-o .kh-nhan,.kh-o .kh-xoa{padding:1px 5px;line-height:1.2;font-size:12px}
.kh-o .kh-xoa{color:var(--dim);border-color:var(--dim)}
.kh-o .kh-xoa:hover{color:var(--red);border-color:var(--red)}
.kh-trong-ky{background:rgba(31,163,152,.08)}
.kh-qua-han td{background:rgba(224,82,82,.08)}
.kh-doi td{background:rgba(245,166,35,.12)}
.kh-doi-sau td,td.kh-doi-sau{outline:1px dashed var(--orange)}
.kh-xong{white-space:nowrap}

/* ── TÌM NHANH + MỨC DÙNG + CHỌN NGÀNH (b785) ─────────────────────────── */
.side .tim-nut{display:flex;align-items:center;gap:6px;margin:8px 12px 0;padding:7px 10px;width:calc(100% - 24px);
  background:var(--panel2);border:1px solid var(--line);border-radius:8px;color:var(--dim);font-size:12.5px;cursor:pointer;text-align:left}
.side .tim-nut:hover{color:var(--txt);border-color:var(--teal)}
.side .tim-nut kbd{margin-left:auto;font-size:10px;border:1px solid var(--line);border-radius:4px;padding:1px 5px;color:var(--dim);font-family:inherit}
.tn-o{width:100%;box-sizing:border-box;font-size:15px;padding:10px 12px;margin-bottom:6px}
.tn-kq{max-height:60vh;overflow:auto;margin-top:8px}
.tn-dong{padding:8px 10px;border-radius:6px;cursor:pointer;display:flex;gap:8px;align-items:baseline}
.tn-dong.on,.tn-dong:hover{background:var(--panel2)}
.tn-loai{font-size:10.5px;text-transform:uppercase;color:var(--dim);min-width:52px;letter-spacing:.03em}
.nd-chon{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px}
.nd-chon a{display:block;padding:14px;background:var(--panel);border:1px solid var(--line);border-radius:10px;color:var(--txt)}
.nd-chon a:hover{border-color:var(--teal)}

/* ── DẢI TAB CỤM (b786) — nằm trong .topbar nên dính theo thanh tiêu đề khi cuộn ───── */
.topbar .cum-tab{flex-basis:100%;display:flex;gap:2px;flex-wrap:wrap;margin-top:2px}
.cum-tab a{padding:6px 12px;color:var(--dim);font-size:13px;border-bottom:2px solid transparent;white-space:nowrap;border-radius:6px 6px 0 0}
.cum-tab a:hover{color:var(--txt);background:var(--panel2)}
.cum-tab a.on{color:#fff;border-bottom-color:var(--teal);background:var(--panel2)}
@media (max-width:860px){.topbar .cum-tab{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:thin}}
/* b789 — ô lọc theo mã trạm gắn tự động trên mọi bảng có cột trạm */
.tim-ma{display:flex;align-items:center;gap:8px;margin:6px 0 4px}
.tim-ma input{max-width:260px;padding:6px 10px}

/* b793: hộp gán người giám sát — ba nhóm địa bàn gập/mở + ô tìm. `.bc-row` là flex nên thuộc tính
   hidden của HTML không tự giấu được dòng — phải nói rõ ở đây. */
.gsm-nhom summary { cursor:pointer; font-weight:600; padding:4px 0; }
.gsm-nhom summary:hover { color:var(--orange); }
.gsm-uv[hidden], .gsm-nhom[hidden] { display:none; }

/* Ô TRẠNG THÁI TỜ KẾ HOẠCH — nhãn chữ, KHÔNG phải nút (b816).
   Lớp .chip cũ khiến ô này trông y hệt nút chuyển tab; người dùng bấm vào và không có gì
   xảy ra. Viền mảnh + chữ nhỏ + con trỏ mặc định: nhìn là biết đây là chữ để đọc. */
.kh-tt { display:inline-block; padding:1px 7px; border:1px solid var(--dim);
  border-radius:4px; font-size:.82em; line-height:1.5; white-space:nowrap; cursor:default; }

/* ═══ ẢNH CHỜ GỬI CỦA SỔ BÁO CÁO NGÀY (b818) ══════════════════════════════════════════════════
   Người khai chụp tới năm tấm rồi mới bấm Gửi, nên phải NHÌN THẤY mình đang cầm những tấm nào:
   một danh sách tên tệp không cho biết tấm nào là tấm chụp nhầm cần bỏ. Ô ảnh vuông cố định
   (cùng cỡ và cùng luật cắt mép với lưới ảnh nghiệm thu), chữ nằm bên phải, nút bỏ ở cuối hàng.
   align-items: flex-start vì khối chữ có thể cao ba dòng — căn giữa thì ảnh trôi xuống. */
.vn-anh { display:flex; gap:10px; align-items:flex-start; padding:6px 0;
  border-bottom:1px solid rgba(31,58,79,.4); }
.vn-anh img { width:64px; height:64px; object-fit:cover; border-radius:8px; flex:0 0 auto;
  border:1px solid var(--line); background:var(--panel2); }
.vn-anh-chu { flex:1; min-width:0; display:flex; flex-direction:column; gap:3px; font-size:12.5px; }

/* ---------- ỨNG DỤNG ANDROID (b828): dải nhắc bản mới · nút tải · số bản · liên kết màn đăng nhập ---------- */
.uda-dai{display:flex;flex-wrap:wrap;gap:8px 12px;align-items:center;margin:0 0 12px;padding:8px 12px;border-radius:8px;
  background:rgba(232,151,58,.14);border:1px solid var(--orange);font-size:13px}
.uda-dai a{font-weight:700}
a.uda-nut{display:inline-block;background:var(--teal);color:#04222a;font-weight:700;padding:10px 16px;border-radius:8px;text-decoration:none}
a.uda-nut:hover{filter:brightness(1.08)}
.uda-ban{font-size:2rem;font-weight:800;letter-spacing:.5px;line-height:1.1}
.uda-buoc{padding-left:20px;line-height:1.5}
.uda-buoc li{margin:6px 0}
.uda-login{margin-top:12px;text-align:center;font-size:13px}
/* ── Bàn đối tác năm khối b830: hạng A–D và thanh điểm 0–100 có vạch mặt bằng (trung vị) ───── */
.dtb-hang{font-size:16px;font-weight:800;padding:0 8px;border-radius:8px;display:inline-block;vertical-align:middle}
.dtb-hang.green{background:rgba(52,199,89,.18);color:var(--green)} .dtb-hang.teal{background:rgba(23,162,184,.16);color:var(--teal)}
.dtb-hang.orange{background:rgba(255,160,0,.16);color:var(--orange)} .dtb-hang.red{background:rgba(255,69,58,.18);color:var(--red)}
.dtb-thanh{position:relative;height:6px;background:var(--panel2);border-radius:3px;margin-top:6px}
.dtb-thanh i{position:absolute;left:0;top:0;bottom:0;border-radius:3px;background:var(--teal)}
.dtb-thanh b{position:absolute;top:-3px;width:2px;height:12px;background:var(--orange);cursor:help}
/* ── Thẻ dòng công đoạn (b851): danh mục checklist · danh mục kiểm · khối trạm · modal «Xem tất cả» ──
   CTO 16-09-2026 chụp hai màn từ điện thoại: bảng 5–6 cột nowrap rộng 1.191 px trong khung 325 px, phải
   kéo ngang ba màn hình mới tới nút «Mở làm». Thẻ dòng flex: chủ đề đã dịch đậm và xuống dòng, mã tệp
   mono mờ ở dòng phụ, cột phải (huy hiệu + nút) rơi xuống dưới thân khi máy hẹp. Không .tbl-wrap,
   không sticky, không cuộn ngang — chữ tự xuống dòng nên khổ nào cũng vừa. */
.qc-ds{display:flex;flex-direction:column;gap:6px}
.qc-cd{display:flex;gap:10px;align-items:flex-start;padding:9px 10px;border:1px solid var(--line);border-radius:8px;background:var(--panel2)}
.qc-cd-mo{opacity:.5}
.qc-cd-so{flex:0 0 auto;min-width:26px;font-weight:700;font-size:13px;color:var(--dim);padding-top:1px}
.qc-cd-than{flex:1 1 0;min-width:0}
.qc-cd-ten{font-weight:700;font-size:14px;line-height:1.3;white-space:normal;overflow-wrap:anywhere}
.qc-cd-phu{font-size:11px;color:var(--dim);margin-top:2px;white-space:normal}
.qc-ma{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10.5px;color:var(--dim);background:rgba(125,147,165,.14);padding:0 5px;border-radius:4px;font-weight:400;white-space:nowrap}
.qc-cd-meta{font-size:11.5px;margin-top:4px;line-height:1.5;white-space:normal}
.qc-cd-canh{font-size:11px;color:var(--orange);margin-top:3px;white-space:normal}
.qc-cd-gap{margin-top:4px}
.qc-cd-gap summary{cursor:pointer;font-size:11px;color:var(--dim);user-select:none;list-style-position:inside}
.qc-cd-tyle{display:flex;flex-wrap:wrap;gap:4px 6px;margin-top:4px}
.qc-tl{font-size:10.5px;color:var(--dim);background:rgba(125,147,165,.12);border-radius:4px;padding:1px 6px;white-space:normal}
.qc-tl b{color:var(--txt)}
.qc-cd-phai{flex:0 0 auto;display:flex;flex-direction:column;align-items:flex-end;gap:6px;max-width:42%}
.qc-cd-phai .b{white-space:nowrap}
a.qc-cd-nut{text-decoration:none}
@media (max-width:640px){
  .qc-cd{flex-wrap:wrap}
  .qc-cd-phai{flex:1 0 100%;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-start;max-width:none;padding-left:36px}
}
/* b859 — ô của màn #/sms đang bày giá trị Viettel điền sẵn mà CHƯA lưu: viền đứt để không lẫn với ô đã ghi */
input.dien-san{border-style:dashed;border-color:var(--orange)}
/* ══ b867 — MA TRẬN KẾ HOẠCH (thẻ Tổng hợp của Kế hoạch tháng) ══════════════════════════════════
   Lăng kính khối lượng dùng MỘT sắc xanh năm bậc: nền tối nên bậc 1 tối nhất (gần nền), bậc 5 sáng nhất. Đã soát
   bằng bộ kiểm bảng màu (validate_palette --ordinal --mode dark --surface #10202e = --panel, 25-09-2026): độ sáng
   tăng đều, các bậc kề cách nhau đủ, bậc 1 vẫn tách khỏi nền 2,04:1, một sắc (lệch 3°). Chữ trắng trên hai bậc tối,
   chữ màu nền trên ba bậc sáng. Chỉ dòng TỈNH được tô — dòng vùng và toàn mạng là tổng. Tiến độ dùng ba màu trạng
   thái sẵn có (xanh lá · cam · đỏ) và LUÔN đi kèm con số, không bao giờ chỉ có màu. */
:root{--mt1:#184f95;--mt2:#256abf;--mt3:#3987e5;--mt4:#6da7ec;--mt5:#9ec5f4}
.kh-mt{width:auto;min-width:100%}
.kh-mt th{text-transform:none;letter-spacing:0;text-align:center;white-space:normal;padding:5px 3px;vertical-align:bottom}
.kh-mt th.kh-mt-khoi{font-size:10.5px;text-transform:uppercase;letter-spacing:.3px;vertical-align:middle}
.kh-mt th.kh-mt-khoi small{display:block;text-transform:none;letter-spacing:0;font-weight:400;font-size:10.5px;opacity:.85}
.kh-mt th.kh-mt-moc{font-size:11px;font-weight:600;color:var(--txt);min-width:64px;max-width:92px;line-height:1.2}
.kh-mt th.kh-mt-moc b{display:block;color:var(--dim);font-size:10.5px}
.kh-mt th.le,.kh-mt td.le{background-image:linear-gradient(rgba(125,147,165,.07),rgba(125,147,165,.07))}
.kh-mt th.mo{opacity:.35}
.kh-mt td{padding:2px;text-align:center;height:38px;position:relative}
.kh-mt td:first-child,.kh-mt th:first-child{text-align:left;padding:0 10px;min-width:118px;white-space:nowrap}
.kh-mt td.kh-mt-cong,.kh-mt th.kh-mt-cong{text-align:right;padding:0 10px;min-width:96px;white-space:nowrap;box-shadow:inset 1px 0 0 var(--line)}
.kh-mt tr.kh-mt-toan td{font-weight:700;border-bottom:2px solid var(--line)}
.tbl-wrap table.kh-mt tr.kh-mt-vung td{background-color:var(--panel2);font-weight:600}
.kh-mt tr.kh-mt-tinh td:first-child{padding-left:26px}
.kh-mt-vung{background:none;border:0;padding:0;border-radius:0;color:var(--txt);font:inherit;font-size:13px;cursor:pointer}
.kh-mt-vung:hover{filter:none;color:var(--teal)}
.kh-mt-bam{display:block;width:100%;padding:0;background:none;border:0;border-radius:4px;color:inherit;font:inherit;cursor:pointer}
.kh-mt-bam:hover,.kh-mt-bam:focus-visible{filter:none;outline:2px solid var(--teal);outline-offset:-1px}
.kh-mt-o{display:block;height:34px;line-height:34px;border-radius:4px;font-size:13px;font-weight:600;color:var(--txt)}
.kh-mt-o.rong{color:var(--dim);font-weight:400}
.kh-mt-o.b1{background:var(--mt1);color:#fff}
.kh-mt-o.b2{background:var(--mt2);color:#fff}
.kh-mt-o.b3{background:var(--mt3);color:var(--bg)}
.kh-mt-o.b4{background:var(--mt4);color:var(--bg)}
.kh-mt-o.b5{background:var(--mt5);color:var(--bg)}
.kh-mt-o.td{height:auto;min-height:34px;line-height:1.1;padding:3px 0 4px}
.kh-mt-o.td b{display:block;font-size:13px}
.kh-mt-o.td small{display:block;font-size:10px;color:var(--dim);font-weight:400;margin-top:1px}
.kh-mt-o.td .thanh{display:block;height:3px;margin:3px 7px 0;border-radius:2px;overflow:hidden}
.kh-mt-o.td .thanh span{display:block;height:100%;border-radius:2px}
.kh-mt-o.td.green{background:rgba(55,176,111,.16)}
.kh-mt-o.td.green .thanh{background:rgba(55,176,111,.3)}
.kh-mt-o.td.green .thanh span{background:var(--green)}
.kh-mt-o.td.orange{background:rgba(232,151,58,.16)}
.kh-mt-o.td.orange .thanh{background:rgba(232,151,58,.3)}
.kh-mt-o.td.orange .thanh span{background:var(--orange)}
.kh-mt-o.td.red{background:rgba(224,82,82,.16)}
.kh-mt-o.td.red .thanh{background:rgba(224,82,82,.3)}
.kh-mt-o.td.red .thanh span{background:var(--red)}
.kh-mt-o.td.it b{color:var(--dim);font-weight:400}
.kh-mt-o.td.it .thanh{background:rgba(90,111,128,.3)}
.kh-mt-o.td.it .thanh span{background:var(--gray)}
.kh-mt td.mo .kh-mt-o,.kh-mt td.mo .kh-mt-doi{opacity:.22}
.kh-mt-doi{position:absolute;top:1px;right:1px;padding:0 3px;border:0;border-radius:3px;font-size:10px;line-height:14px;font-weight:700;background:var(--orange);color:var(--bg)}
.kh-mt-nhap{color:var(--orange);cursor:help}
.kh-mt-lk .chip{font-weight:600}
.kh-mt-lk .chip.tat{opacity:.45}
.kh-mt-hoi{margin:10px 0 2px;font-size:14px}
.kh-mt-doc{max-width:980px;margin-bottom:4px}
.kh-mt-the{margin:12px 0 10px;grid-template-columns:repeat(auto-fill,minmax(170px,1fr))}
.kh-mt-the .card .v{font-size:22px}
.kh-mt-the .card .k{text-transform:none;letter-spacing:0;font-size:12px;line-height:1.35;margin-top:2px}
.kh-mt-dk{margin-top:6px}
.kh-mt-dk .sub2{margin-left:8px}
.kh-mt-giai{display:flex;flex-wrap:wrap;gap:6px 14px;align-items:center;margin-top:8px;font-size:11.5px;color:var(--dim)}
.kh-mt-mau{display:inline-block;width:20px;height:12px;border-radius:3px;vertical-align:-1px;margin-right:4px}
.kh-mt-mau.b1{background:var(--mt1)}
.kh-mt-mau.b2{background:var(--mt2)}
.kh-mt-mau.b3{background:var(--mt3)}
.kh-mt-mau.b4{background:var(--mt4)}
.kh-mt-mau.b5{background:var(--mt5)}
/* Điện thoại: thẻ hai cột; cột tên chỉ giữ MÃ vùng (chuỗi tỉnh đã có ở ô chọn Vùng và ở dòng tỉnh khi mở) để
   nhường chỗ cho cột số — đo 375 px: cột tên 175 → ~80 px, từ 2 lên ~3,5 cột mốc trong khung. */
@media (max-width:640px){
  .kh-mt-the{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
  .kh-mt-the .card{padding:8px 10px}
  .kh-mt-the .card .v{font-size:19px}
  .kh-mt-vung .muted{display:none}
  .kh-mt td:first-child,.kh-mt th:first-child{min-width:0;padding:0 6px}
  .kh-mt tr.kh-mt-tinh td:first-child{padding-left:14px}
  .kh-mt td.kh-mt-cong,.kh-mt th.kh-mt-cong{min-width:0;padding:0 6px}
}

/* ── GẠT MÉP ĐỂ LÙI / TIẾN (b871) — mũi tên đi theo ngón tay từ mép vào; sáng hẳn lên khi đã kéo đủ xa để nhả.
   Mép phải khi không còn bước tiến: mũi tên mờ, nhả ra không có gì xảy ra. Không nhận chạm (pointer-events:none). */
.ls-mui{position:fixed;top:0;width:44px;height:44px;margin-top:-22px;border-radius:50%;display:flex;
  align-items:center;justify-content:center;font:700 28px/1 'Segoe UI',system-ui,sans-serif;padding-bottom:3px;
  background:var(--panel2);color:var(--teal);border:1px solid var(--line);box-shadow:0 4px 14px rgba(0,0,0,.45);
  z-index:10001;pointer-events:none;opacity:0;transition:background .12s,color .12s,opacity .12s}
.ls-mui.trai{left:-46px}
.ls-mui.phai{right:-46px}
.ls-mui.san{background:var(--teal);color:#fff;border-color:var(--teal)}
.ls-mui.het{color:var(--dim)}
@media (prefers-reduced-motion:reduce){.ls-mui{transition:none}}

/* ── KHẢO SÁT HIỆN TRƯỜNG (V611, KS-1) — màn #/khao-sat ─────────────────────────────────────────
   Thiết kế cho ngón cái ngoài trời nắng: ô chạm ≥ 44 px, chữ ô nhập 16 px (iPhone không tự phóng to
   khi chạm vào ô dưới 16 px), thanh mục dính trên, thanh Lùi / Tới / Nộp dính dưới. */
.ksh-dau .chips{margin-bottom:10px}
.ksh-tim{display:flex;gap:6px;margin-bottom:6px}
.ksh-tim input{flex:1;font-size:16px}
.ksh-the{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:12px 14px;margin-bottom:8px}
.ksh-the:active{border-color:var(--teal)}
.ksh-the-dau{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.ksh-the-dau b{font-size:15px}
.ksh-cach{margin-left:auto;font-weight:700;color:var(--teal)}
.ksh-dem{display:flex;gap:5px;flex-wrap:wrap;margin-top:6px}
.ksh-yk{margin-top:6px;padding:6px 9px;border-left:3px solid var(--red);background:rgba(224,82,82,.08);font-size:12.5px}
.ksh-the .err,.ksh-phieu .err,.ksh-truong .err{color:var(--red)}
.ksh-cand{border-top:1px solid var(--line);padding:10px 0}
.ksh-cand:first-of-type{border-top:none}
.ksh-dong{padding:9px 4px;border-radius:7px;font-size:13px}
.ksh-dong:active{background:var(--panel2)}
.ksh-to{width:100%;min-height:46px;margin-top:6px;font-size:15px}

.ksh-phieu{padding-bottom:76px}
.ksh-dau-phieu{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-bottom:8px}
.ksh-tien{height:6px;background:var(--panel2);border-radius:3px;margin:8px 0 4px;overflow:hidden}
.ksh-tien div{height:100%;background:var(--green);transition:width .25s}
.ksh-cb{color:var(--orange);font-weight:600}
.ksh-bao{padding:8px 12px;border-radius:8px;margin-bottom:8px;font-size:13px;background:var(--panel2)}
.ksh-bao.orange{color:var(--orange)}
.ksh-bao.green{color:var(--green)}
.ksh-bao.red{color:var(--red)}
.ksh-muc-tab{display:flex;gap:6px;overflow-x:auto;padding:6px 0 8px;position:sticky;top:0;z-index:5;background:var(--bg);
  scrollbar-width:none}
.ksh-muc-tab::-webkit-scrollbar{display:none}
.ksh-muc-tab .chip{white-space:nowrap;padding:8px 14px;font-size:13px}
.ksh-so-do{display:inline-block;min-width:18px;padding:0 5px;margin-left:3px;border-radius:9px;background:var(--red);color:#fff;
  font-size:11px;text-align:center}
.ksh-chan{position:fixed;left:0;right:0;bottom:0;z-index:20;display:flex;gap:8px;padding:10px 12px;
  padding-bottom:calc(10px + env(safe-area-inset-bottom,0px));background:var(--panel);border-top:1px solid var(--line)}
.ksh-chan button{min-height:46px;font-size:15px}
.ksh-chan button.pri{flex:1}
@media (min-width:861px){.ksh-chan{left:212px}}

.ksh-form{display:flex;flex-direction:column;gap:6px}
.ksh-truong{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:11px 12px;scroll-margin-top:60px}
.ksh-truong.ksh-nhay{border-color:var(--orange);box-shadow:0 0 0 2px rgba(232,151,58,.35)}
.ksh-nhan{font-weight:600;font-size:14px;margin-bottom:6px;display:flex;gap:5px;align-items:center;flex-wrap:wrap}
.ksh-sao{color:var(--red)}
.ksh-cham{color:var(--orange)}
.ksh-hs{display:inline-block;font-size:10px;font-weight:700;padding:1px 5px;border-radius:4px;background:var(--panel2);color:var(--dim)}
.ksh-hs.teal{color:var(--teal)}
.ksh-hs.green{color:var(--green)}
.ksh-hs.yellow{color:var(--yellow)}
.ksh-hs.orange{color:var(--orange)}
.ksh-hs.gray{color:var(--dim)}
.ksh-truong .chips{gap:8px;margin-top:4px}
.ksh-chip{min-height:42px;padding:9px 15px;font-size:14px;border-radius:21px;background:var(--panel2)}
.ksh-o{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:4px}
.ksh-o input{flex:1;min-width:120px}
.ksh-vao{font-size:16px;min-height:44px}
textarea.ksh-vao{min-height:80px}
.ksh-dv{color:var(--dim);font-weight:600;min-width:20px}
.ksh-loi{margin-top:6px;font-size:12.5px;font-weight:600}
.ksh-loi.chan{color:var(--red)}
.ksh-loi.cb{color:var(--orange)}
.ksh-dvhc{display:flex;flex-direction:column;gap:6px}
.ksh-dv1,.ksh-dv2,.ksh-dv3{font-size:16px;min-height:44px}
.ksh-td{padding:6px 0}
.ksh-td b{font-size:15px}
.ksh-do{margin-top:6px}
.ksh-dang-do{padding:10px;border:1px dashed var(--teal);border-radius:8px;color:var(--teal)}
.ksh-tinh{font-size:15px;font-weight:700;color:var(--teal)}
.ksh-lap{border-left:3px solid var(--teal);padding-left:8px;margin:4px 0;display:flex;flex-direction:column;gap:6px}
.ksh-lap-dau{font-weight:700;color:var(--teal);padding:4px 0}

.ksh-anh{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px}
.ksh-anh-o{position:relative;width:84px;height:84px;border-radius:8px;overflow:hidden;background:var(--panel2);border:1px solid var(--line)}
.ksh-anh-o img{width:100%;height:100%;object-fit:cover;display:block}
.ksh-anh-o span{position:absolute;left:0;right:0;bottom:0;font-size:11px;padding:1px 4px;background:rgba(0,0,0,.55);color:#fff}
.ksh-anh-o.da_gui{border-color:var(--green)}
.ksh-xoa{position:absolute;top:2px;right:2px;width:26px;height:26px;padding:0;border-radius:13px;background:rgba(0,0,0,.6);font-size:12px}
.ksh-anh-moi{width:84px;height:84px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  font-size:26px;background:var(--panel2);border:1px dashed var(--teal);color:var(--teal)}
.ksh-anh-moi span{font-size:11px}
.ksh-pano{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:4px}
.ksh-pano-o{position:relative;aspect-ratio:1;border-radius:8px;overflow:hidden;background:var(--panel2);border:1px dashed var(--line);
  display:flex;align-items:center;justify-content:center;font-size:24px;cursor:pointer}
.ksh-pano-o.co{border:1px solid var(--green)}
.ksh-pano-o img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.ksh-pano-h{position:absolute;left:4px;top:4px;font-size:12px;font-weight:700;padding:1px 6px;border-radius:5px;background:rgba(0,0,0,.6);color:#fff}
.ksh-pano-xem{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:8px}

.ksh-lb-nen{position:fixed;inset:0;z-index:1000;background:rgba(4,10,16,.82);display:flex;align-items:center;justify-content:center;padding:16px}
.ksh-lb,.ksh-tam{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:18px;width:100%;max-width:420px;
  max-height:88vh;overflow-y:auto;text-align:center}
.ksh-tam{text-align:left}
.ksh-tam h3{margin-bottom:10px}
.ksh-lb .ksh-o{justify-content:center;margin-top:14px}
.ksh-lb .ksh-o button{min-height:48px;min-width:120px;font-size:15px}
.ksh-lb-dia{width:180px;height:180px;margin:4px auto 10px;border-radius:50%;border:3px solid var(--line);display:flex;align-items:center;
  justify-content:center;background:radial-gradient(circle,var(--panel2) 60%,var(--panel) 100%)}
.ksh-lb-kim{font-size:64px;line-height:1;color:var(--teal);transition:transform .15s linear}
.ksh-lb-so{font-size:30px;font-weight:800}
.ksh-lb button.ksh-lb-dung{background:var(--green);color:#04240f}
.ksh-tam-dong{display:flex;gap:8px;align-items:center;justify-content:space-between;padding:9px 0;border-top:1px solid var(--line)}
.ksh-tam-dong.cot{flex-direction:column;align-items:stretch}
.ksh-ld{font-size:16px;min-height:42px}
.ksh-cb-qua{margin-top:8px;padding:6px 9px;border-left:3px solid var(--orange);background:rgba(232,151,58,.08)}
/* Bảng đọc phiếu: chữ phải XUỐNG DÒNG (luật gốc table{white-space:nowrap} cắt cột giá trị ở khổ 375 px). */
.ksh-bang{width:100%;border-collapse:collapse;white-space:normal;table-layout:fixed}
.ksh-bang td{word-break:break-word}
.ksh-bang td{padding:7px 6px;border-top:1px solid var(--line);vertical-align:top;font-size:13px}
.ksh-bang td:first-child{color:var(--dim);width:42%}
.ksh-bang th.ksh-lap-dau{text-align:left;padding-top:10px}

/* ── KS-2 ĐI TUYẾN: thẻ «Đi tuyến» (bản đồ nhỏ + nút một chạm + danh sách điểm) và tờ một điểm ── */
.kst-khung{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:9px 10px}
.kst-tt{gap:5px;margin-top:0}
.kst-bd-o{position:relative;margin-top:6px}
.kst-bd{display:block;width:100%;height:230px;border-radius:8px;border:1px solid var(--line);background:var(--panel2);touch-action:manipulation}
.kst-bd-nut{position:absolute;top:6px;right:6px;display:flex;gap:4px}
.kst-bd-nut button{background:rgba(11,21,32,.78)}
/* Nút đi lại (danh sách, ‹ ›, khung bản đồ): vẫn bấm được khi phiếu chỉ xem — kstVeDiTuyen chừa lớp này lúc khoá nút. */
.kst-nav{white-space:nowrap}
.kst-nhanh{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:8px 0}
.kst-them{min-height:52px;font-size:15px}
.kst-luat{background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--red);border-radius:10px;padding:6px 12px 8px}
.kst-ds-dau{margin:10px 2px 6px;font-size:14px}
.kst-the{padding:10px 12px}
.kst-to{display:flex;flex-direction:column;gap:6px}
.kst-to-dau{display:flex;gap:6px;align-items:center}
.kst-to-dau button{min-height:40px;min-width:44px}
.kst-to-cuoi{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:4px}
.kst-to-cuoi .kst-go{grid-column:1 / -1;min-height:42px}
.kst-luoi{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:6px 0 12px}
.kst-luoi button{min-height:48px;font-size:14px}
.kst-xem-diem{border-top:1px solid var(--line);padding:8px 0}
.kst-xem-diem summary{cursor:pointer;font-size:13.5px;line-height:1.5}
.kst-xem-diem .ksh-bang{margin-top:6px}
/* KS-3 viba · VSAT: mặt cắt tuyến, nút gợi ý theo toạ độ, bảng thiết kế hai đầu. */
.ksv-mc{display:block;width:100%;height:210px;margin:6px 0 4px;border-radius:8px;border:1px solid var(--line);background:var(--panel2)}
.ksv-gy{white-space:nowrap}
.ksv-gy .sub2{opacity:.8;margin-left:2px}
.ksv-tk th{font-size:12px;text-align:left;padding:6px;color:var(--dim)}
.ksv-tk td:first-child{color:var(--dim)}
/* ── HỌC & THI (V615, HT-1): dải số, module, thẻ bài học, phương án bấm to cho điện thoại, đồng hồ bài ── */
.hth-dai{display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:8px}
.hth-so{background:var(--panel2);border-radius:8px;padding:8px 10px;display:flex;flex-direction:column;gap:2px}
.hth-so b{font-size:20px}
.hth-so span{font-size:11.5px;color:var(--dim)}
.hth-nut{gap:8px;margin-top:10px}
.hth-tab{gap:6px;margin:0 0 12px}
.hth-loc{gap:8px;margin-bottom:8px}
.hth-loc select,.hth-loc input[type=search]{width:auto;flex:1 1 150px}
.hth-o{display:inline-flex;align-items:center;gap:6px;font-size:13px;cursor:pointer;margin:6px 0}
.hth-o input,.tbl-wrap td input[type=checkbox],.tbl-wrap th input[type=checkbox],.hth-sua-pa input[type=radio]{width:auto}
.hth-mod{padding:9px 4px;border-top:1px solid var(--line)}
.hth-mod:first-of-type{border-top:0}
.hth-mod:hover,.hth-the-dong:hover{background:var(--panel2)}
.hth-tien{height:4px;background:var(--panel2);border-radius:2px;margin-top:5px;overflow:hidden}
.hth-tien i{display:block;height:100%;background:var(--teal)}
.hth-the-dong{display:flex;gap:10px;align-items:flex-start;padding:9px 4px;border-top:1px solid var(--line)}
.hth-the-dong:first-of-type{border-top:0}
.hth-dau{width:22px;text-align:center;color:var(--dim);font-size:15px}
.hth-dau.xong{color:var(--green)}
.hth-muc{padding:10px 4px;border-top:1px solid var(--line);line-height:1.45}
.hth-muc:first-child{border-top:0}
.hth-muc.stop{border-left:3px solid var(--red);padding-left:9px}
.hth-chuan{margin-top:3px;color:var(--dim);font-size:13px}
.hth-cau.dung{border-left:3px solid var(--green)}
.hth-cau.sai{border-left:3px solid var(--red)}
.hth-de{font-size:15px;line-height:1.5;margin:6px 0 10px;white-space:pre-line}
.hth-anh{background:var(--panel2);border:1px dashed var(--line);border-radius:8px;padding:8px 10px;margin-bottom:10px;font-size:13px}
.hth-pa{display:flex;gap:10px;align-items:flex-start;width:100%;text-align:left;margin:6px 0;padding:10px 12px;min-height:44px;
  background:var(--panel2);border:1px solid var(--line);border-radius:9px;color:inherit;font-size:14px;line-height:1.4}
button.hth-pa{cursor:pointer}
button.hth-pa:disabled{cursor:default;opacity:1}
.hth-pa.chon{border-color:var(--teal);box-shadow:inset 0 0 0 1px var(--teal)}
.hth-pa.dung{border-color:var(--green);background:rgba(55,176,111,.14)}
.hth-pa.sai{border-color:var(--red);background:rgba(224,82,82,.12)}
.hth-pa-chu{flex:none;width:22px;height:22px;border-radius:50%;background:var(--line);display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:700}
.hth-kq-dong{font-weight:700;margin:10px 0 4px;font-size:15px}
.hth-kq-dong.dung{color:var(--green)}
.hth-kq-dong.sai{color:var(--red)}
.hth-chan-doan{color:var(--orange);font-size:13px;margin:4px 0}
.hth-giai{background:var(--panel2);border-radius:8px;padding:8px 10px;margin:8px 0;font-size:13px;line-height:1.5;white-space:pre-line}
.hth-kq{text-align:center}
.hth-kq .hth-nut{justify-content:center}
.hth-kq-diem{font-size:38px;font-weight:700;margin:4px 0}
.hth-canh{color:var(--red);font-weight:600}
.hth-loi{color:var(--red);font-size:12.5px;margin-top:6px}
.hth-dongho{position:sticky;top:env(safe-area-inset-top,0px);z-index:5;background:var(--panel);border:1px solid var(--line);border-radius:9px;
  padding:8px 12px;margin-bottom:10px;font-size:18px;font-weight:700;text-align:center}
.hth-dongho.gap{color:var(--red);border-color:var(--red)}
.hth-lv td{background:var(--panel2)}
.hth-ngu{border-top:1px solid var(--line);padding:8px 0}
.hth-co{background:rgba(232,151,58,.14);color:var(--orange);border-radius:8px;padding:8px 10px;margin:6px 0;font-size:13px}
.hth-ghi{white-space:pre-line;font-size:13px;background:var(--panel2);border-radius:8px;padding:8px 10px;margin:4px 0}
.hth-bao{border-top:1px solid var(--line);padding:6px 0}
.hth-sua-pa{display:flex;gap:8px;align-items:flex-start;margin-bottom:8px}
.hth-sua-pa input[type=text]{margin-bottom:4px}
