/* ═══════════════════════════════════════════════════════════════════════════
   STARVIEW CHAT (web) — BỐ CỤC RIÊNG CỦA TRANG CHAT
   ───────────────────────────────────────────────────────────────────────────
   Chỉ thêm bố cục; MÀU và nút lấy nguyên từ `hop.css` (cùng token, cùng nút,
   cùng ô nhập) — §2.1 quy ước icon: màu lấy từ token, cấm chép hex vào chỗ dùng.
   Điều bắt buộc giữ nguyên như trang họp: KHÔNG trượt ngang ở 360·768·1280·1920,
   dùng 100dvh + safe-area cho Safari/iPhone.
   ═══════════════════════════════════════════════════════════════════════════ */

#app { display: flex; flex-direction: column; height: 100dvh; overflow: hidden;
       padding-bottom: env(safe-area-inset-bottom); }

/* ⛔⛔ 03/09 — KHOANH VÙNG. `chat.css` nạp SAU `hop.css` trên CẢ HAI trang
   (hop.html:36 rồi :40), nên bốn luật khung trang dưới đây ĐANG ĐÈ lên thanh
   trên của TRANG HỌP: mất `padding` theo `var(--sat)`, mất `min-width:0` của
   `.mid`, và `flex-wrap:nowrap` + `.top > * {flex:none}` áp cho cả thanh họp.
   Ba luật thu hẹp theo bề ngang ở hop.css (998/1050/1143) cũng thua vì cùng
   độ đặc hiệu mà đứng TRƯỚC. ⇒ `.top`/`.mid` là thẻ TĨNH trong HTML (chat.html
   1 cái, hop.html 2 cái) nên khoanh theo TRANG là đúng và đủ. */
.trang-chat .top { display: flex; align-items: center; gap: 10px; flex: none;
       padding: 8px 12px; padding-top: calc(8px + env(safe-area-inset-top));
       border-bottom: 1px solid var(--line); background: var(--navy-dark); }
.trang-chat .top .sp { flex: 1; min-width: 0; }
.top .toi { font-size: 12px; color: var(--ink-2); overflow: hidden;
            text-overflow: ellipsis; white-space: nowrap; max-width: 42vw; }
/* ⛔ 🔴 TỰ ĐO 06/08 ở 360px — thanh trên VỠ: dòng phụ "Chat bảo mật đầu-cuối" xuống
   5 dòng, đèn khoá cũng gãy chữ, thanh cao hơn 200px và đẩy hết nội dung xuống.
   Luật đã rút ra ở trang họp: thanh trên KHÔNG được xuống dòng; chỗ hẹp thì
   BỎ CHỮ PHỤ, giữ đủ nút và đèn (không bỏ nút nào). */
.trang-chat .top { flex-wrap: nowrap; }
.trang-chat .top > * { flex: none; }
.top .pill { white-space: nowrap; }
@media (max-width: 900px) { .top .phu { display: none; } }
@media (max-width: 720px) { .top .pill .txt { display: none; } }
@media (max-width: 560px) { .top .toi { display: none; } }

/* ⛔ 🔴 TỰ ĐO 09/08/2026 — THANH TRÊN CHAT TRÀN NGANG **7 px** Ở 768 VÀ 1280.
   ĐO THẬT trong trình duyệt (không suy luận), `chat.html` ở 1280:
       #cTop  clientWidth = 1280   scrollWidth = 1287   ⇒ tràn 7
       #app   clientWidth = 1280   scrollWidth = 1287   ⇒ tràn theo

   NGUYÊN NHÂN **KHÔNG PHẢI** khe hở breakpoint của `.pill .txt` như đã đoán —
   tổng bề rộng các con vừa khít ô chứa (1186 + 7 khe × 10 = 1256 = 1280 − 24
   đệm). Thủ phạm là **mẹo chú giải `[data-tip]` của `hop.css`**:

       [data-tip]::after { content: attr(data-tip); position: absolute;
                           left: 50%; transform: translateX(-50%); opacity: 0 }

   Chú giải của nút CUỐI (`#bRa`, "Đăng xuất") rộng 57,7 px, canh giữa nút 40 px
   ⇒ nó thò sang phải nút **~19 px**. Đo được: `#bRa` right = 1268 (đúng mép
   trong), mà chú giải kéo vùng cuộn tới 1287.
   ⚠ `opacity: 0` **KHÔNG** gỡ phần tử khỏi bố cục, và phần tử định vị tuyệt đối
     **VẪN tính vào vùng tràn cuộn của tổ tiên**. Vì thế lỗi này vô hình khi nhìn
     bằng mắt — chỉ `scrollWidth` mới khai ra. Và nó có hậu quả THẬT: rê chuột
     vào nút cuối thì chú giải hiện **lòi ra ngoài mép phải cửa sổ**, đọc mất chữ.

   CÁCH VÁ: nút ĐẦU và nút CUỐI của thanh neo chú giải vào chính mép của mình
   thay vì canh giữa. Mũi tên `::before` GIỮ NGUYÊN canh giữa nút — nó nhỏ (5 px)
   nên không tràn, và phải chỉ đúng vào nút.

   ⚠ BỘ CHỌN CỐ Ý HẸP — bài học vòng trước: có BA thẻ `.top` khác nhau, và
     `chat.html` cũng nạp `hop.css`. Chỉ nhắm HAI chỗ gắn của bộ chat
     (`#cTop` của trang chat độc lập · `#scTop` của tab "Chat bảo mật" trong
     trang họp). Thanh trên của TRANG HỌP là `header.top` KHÔNG có id ⇒ luật này
     tuyệt đối không với tới nó. Đã đo trước/sau ở cả `hop.html`. */
#cTop > [data-tip]:last-child::after,
#scTop > [data-tip]:last-child::after {
  left: auto; right: 0; transform: none;
}
#cTop > [data-tip]:first-child::after,
#scTop > [data-tip]:first-child::after {
  left: 0; right: auto; transform: none;
}

/* CÙNG MỘT LỖI, CHỖ THỨ HAI — tự tìm ra khi quét MỌI `[data-tip]` chứ không chỉ
   nhìn `#cTop`. Đo ở 1280 trên `chat.html`:
       nút `bMoiNhom` ("Tạo nhóm (≤200)") — chú giải trải 210…308,
       cột `#cDs` cắt ở 300  ⇒ **mất 8 px chữ bên phải**.
   Khác lỗi trên ở chỗ nó KHÔNG làm tràn trang (cột tự cắt), nên `scrollWidth`
   của trang không hé ra — chỉ lộ khi so mép chú giải với mép tổ tiên CẮT.
   `.ds-hd` là lớp CHỈ CÓ trong bộ chat (chat.css 2 chỗ, chat-app.js 1 chỗ;
   `hop.css`/`hop-app.js`/`hop.html` = 0) nên bộ chọn này không với sang trang họp. */
.ds-hd > [data-tip]:last-child::after { left: auto; right: 0; transform: none; }
.ds-hd > [data-tip]:first-child::after { left: 0; right: auto; transform: none; }

/* CÙNG MỘT LỖI, CHỖ THỨ BA — KHUNG SOẠN TIN (tự đo 09/08/2026 ở bề ngang 1280).
       nút "Ghi tin thoại" (`#bThoai`): mép phải nút = 1268;
       chú giải rộng 86 px canh giữa ⇒ mép phải chú giải = 1291 > 1280
       ⇒ **tràn 11 px ra ngoài cửa sổ, mất chữ bên phải**.
   VÌ SAO HAI LUẬT TRÊN KHÔNG VỚI TỚI: chúng nhắm `#cTop`/`#scTop`/`.ds-hd`, còn
   nút này nằm trong `.soan`. Và ngay cả luật `:last-child` cũng trượt, vì thứ tự
   con thật của `.soan` là:
       input#cTep(1) · button#bTep(2) · textarea#cSoan(3) · button#bThoai(4) · button#bGui(5)
   ⇒ `#bThoai` là con ÁP CHÓT, không phải con cuối. `#bGui`/`#bThoai` thay nhau
   ẩn/hiện theo ô soạn có chữ hay không (`doiNutSoan`), nhưng **cả hai vẫn nằm
   trong DOM**, nên nút nào đang hiện cũng là nút ngoài cùng bên phải ⇒ phải neo
   mép phải cho CẢ HAI, không thể chỉ neo con cuối.
   ⚠ BỘ CHỌN CỐ Ý HẸP: `.soan` xuất hiện 0 lần trong `hop.css`/`hop-app.js`/
     `hop-core.js`/`mobile.html` (đã đếm), chỉ có trong bộ chat ⇒ luật này không
     đụng tới trang họp, đúng yêu cầu tránh `hop.css`.
   Mũi tên `::before` GIỮ NGUYÊN canh giữa nút — nó nhỏ nên không tràn. */
.soan > [data-tip]:last-child::after,
.soan > [data-tip]:nth-last-child(2)::after {
  left: auto; right: 0; transform: none;
}

.trang-chat .mid { flex: 1 1 auto; display: flex; min-height: 0; }

/* ─── cột danh sách hội thoại ─── */
.ds { width: 300px; flex: none; display: flex; flex-direction: column;
      border-right: 1px solid var(--line); background: var(--navy-dark);
      min-height: 0; overflow-y: auto; }
.ds-hd { display: flex; align-items: center; gap: 6px; padding: 8px 10px; flex: none;
         border-bottom: 1px solid var(--line); position: sticky; top: 0;
         background: var(--navy-dark); z-index: 2; }
.ds-hd .t { flex: 1; min-width: 0; font-size: 12px; font-weight: 700;
            letter-spacing: .04em; text-transform: uppercase; color: var(--ink-3); }
.nhomtieu { padding: 10px 12px 4px; font-size: 11px; font-weight: 700;
            letter-spacing: .05em; text-transform: uppercase; color: var(--ink-3); }
.hang { display: flex; align-items: center; gap: 10px; width: 100%;
        padding: 9px 12px; border: 0; background: transparent; color: var(--ink);
        text-align: left; cursor: pointer; font: inherit; }
.hang:hover { background: var(--surface-raised); }
.hang.on { background: color-mix(in srgb, var(--gold) 16%, var(--navy-dark));
           box-shadow: inset 3px 0 0 var(--gold); }
/* ⭐⭐ 04/09 — TÁCH TẦNG CHO `.av`: BẢN CHẤT ở lớp `.av`, KÍCH THƯỚC ở ngữ cảnh.
 ⛔ BỌ ĐÃ RA TỚI SẢN PHẨM, chủ dự án bắt được: *"đóng khung profile tròn thoi,
   sao để nó lệch thế kia"*. Luật cho thẻ `<img>` bên trong trước đây viết là
   `.hang .av img` — tức KHOÁ vào danh sách chat. Ảnh trong BONG BÓNG TIN
   (`.tin .ai-nut .av`, ⛔ KHÔNG nằm trong `.hang`) vì thế ⛔ không khớp luật
   nào ⇒ nở ra CỠ GỐC, tràn khỏi ô 22px và ĐÈ LÊN CHỮ của tin nhắn.
 ⭐ Chia đúng tầng: bo tròn · CẮT TRÀN · canh giữa · nền · và luật cho `<img>`
   bên trong lấp đầy ô — đó là BẢN CHẤT của một avatar, dùng ở đâu cũng đúng
   ⇒ thuộc về `.av`. Chỉ KÍCH THƯỚC mới khác theo ngữ cảnh.
 ⛔ TUYỆT ĐỐI ⛔ KHÔNG nâng `width/height/font-size` lên `.av`: 44px của danh
   sách sẽ RÒ sang cả năm ngữ cảnh còn lại (32 đầu phòng · 22 bong bóng ·
   88 hồ sơ · 36 kết quả tìm · 32 `.row` của hop.css).
 ⛔ `overflow:hidden` ⛔ KHÔNG cắt mất VÒNG AN TOÀN: `box-shadow`/`outline` của
   CHÍNH phần tử vẽ NGOÀI viền, còn `overflow` chỉ cắt phần tử CON. Đã ĐO bằng
   engine thật, ⛔ không tin lý thuyết suông — `bando/do_avatar_css.js`.
 ⛔ `hop.html` nạp CẢ `hop.css` lẫn tệp này, mà `hop.css` có `.row .av`
   (0,2,0 > 0,1,0 của `.av`) ⇒ mọi thuộc tính nó đã khai vẫn thắng; riêng
   `background` thì cả 7 chỗ dựng `.row .av` đều đặt NỘI TUYẾN nên luật dưới
   đây ⛔ không với tới. Đã đo, ⛔ không suy đoán. */
.av { border-radius: 50%; flex: none;
      position: relative; overflow: hidden;
      display: grid; place-items: center; font-weight: 700;
      background: var(--mau, var(--surface-raised)); color: #fff; }
/* ⭐ Ảnh LẤP ĐẦY ô rồi mới bị xén: `cover` giữ nguyên tỉ lệ nên ảnh NGANG hay
   DỌC đều ⛔ không méo, phần thừa để `overflow:hidden` của `.av` cắt theo
   đúng hình tròn. ⛔ Đừng đổi sang `contain`: sẽ hở nền hai bên. */
.av img { position: absolute; inset: 0; width: 100%; height: 100%;
          object-fit: cover; }
/* ⭐ M01 (07/08/2026) — HÀNG DANH SÁCH KIỂU WHATSAPP.
   Ảnh 44px (was 32) vì đây là thứ mắt bám vào đầu tiên; hàng cao hơn cho
   dòng trích tin cuối có chỗ thở.
   ⭐ 04/09 — nay CHỈ còn kích thước; phần bản chất đã lên `.av` ở trên. */
.hang .av { width: 44px; height: 44px; font-size: 16px; }

/* ⭐⭐ 04/09 — KHUNG TRÒN ĐỰNG GLYPH (nhóm 3 người · cá nhân 1 người).
   ⛔ Nền BẮT BUỘC `--surface-raised`, ⛔ KHÔNG dùng `--mau` theo tên: khe KHOÉT
     của glyph 3 người vẽ bằng chính màu nền — nền đổi màu là khe biến mất và
     ba chân dung dính liền thành hai (đúng bọ phiên Mobile đã trả giá).
   ⛔ Token `--surface-raised` khai ở CẢ SÁU chủ đề (office/light/game/relax/
     holiday/kitty) — đã đo, ⛔ không phải đoán.
   ⭐ Glyph chiếm 66% đường kính (Mobile dùng 62–70%): đủ lớn để đếm được ba
     đầu người ở 44px, vẫn còn lề để khung đọc ra là khung. */
.av.av-glyph { background: var(--surface-raised); color: inherit; }
/* Khung avatar ở ĐẦU HỘI THOẠI nhỏ hơn danh sách (32 vs 44). ⛔ Bọc bằng
   `.hd-av` chứ ⛔ không style nội tuyến: nội tuyến là bản thứ hai của một luật,
   và nó đã từng làm đầu phòng lệch hẳn với danh sách. */
.hd-av .av { width: 32px; height: 32px; font-size: 13px; }
.av.av-glyph svg { width: 66%; height: 66%; display: block; }

/* ⭐ ★B — VÒNG AN TOÀN. Bốn trạng thái, do DỮ LIỆU quyết định.
   ⛔ Không có lớp `vong-*` ⇒ KHÔNG vẽ vòng (chưa biết thì im lặng). */
.av.vong-xanh { box-shadow: 0 0 0 2px var(--success, #2E9E5B); }
.av.vong-vang { box-shadow: 0 0 0 2px var(--gold); }
.av.vong-do   { box-shadow: 0 0 0 2px var(--danger, #D9534F); }
/* Phòng KHÔNG mã hoá vẽ bằng NÉT ĐỨT chứ không chỉ đổi màu: khoảng 8% nam
   giới không phân biệt đỏ–xanh, mà đây là cảnh báo an toàn. Hình dạng đọc
   được kể cả khi màu không đọc được. */
.av.vong-xam { box-shadow: none; outline: 2px dashed var(--ink-3);
               outline-offset: 1px; }

.hang .n { flex: 1; min-width: 0; }
.hang .n b { display: block; font-size: 14px; font-weight: 600;
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hang .n span { display: block; font-size: 12px; color: var(--ink-3);
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Trích tin cuối — dòng MỚI HOÀN TOÀN. Thiếu nó thì phải mở từng phòng mới
   biết có gì mới. */
.hang .n .xt { margin-top: 1px; }
/* Cột phải: giờ trên, huy hiệu dưới — đúng khuôn WhatsApp/Zalo. */
.hang .phai { flex: none; display: flex; flex-direction: column;
              align-items: flex-end; gap: 4px; min-width: 42px; }
.hang .gio { font-size: 11px; color: var(--ink-3); white-space: nowrap; }
.hang .gio.moi { color: var(--gold); font-weight: 600; }
.hang .sl { flex: none; min-width: 20px; height: 20px; padding: 0 6px;
            border-radius: 10px; background: var(--gold); color: var(--on-accent);
            font-size: 11px; font-weight: 700; display: grid; place-items: center; }

/* ⭐ M01 — DẢI LỌC bốn viên.

   ⚠ ĐÃ SỬA HAI LẦN, ghi lại cả hai để đừng ai quay về cách cũ:

   ① Bản đầu dùng `overflow-x: auto` — dải cuộn ngang được. Đo bằng mắt ở cột
     300px (tab "Chat bảo mật" trong trang họp): viên "Lời mời" bị đẩy khuất,
     và KHÔNG có dấu hiệu nào cho biết là cuộn được ⇒ người dùng tưởng mất
     một bộ lọc.

   ② Bản vá thứ hai dùng `@media (max-width: 340px)` — SAI CHỖ, và tôi chỉ
     phát hiện vì chụp màn hình lại: media query đo VIEWPORT, không đo cột.
     Trong trang họp cửa sổ rộng 1280 mà cột chat chỉ 300 ⇒ luật không bao
     giờ ăn. Container query (`@container`) thì đúng nhưng phải khai
     `container-type` lên cây cha, tức đụng bố cục trang họp — không đáng.

   ⇒ Cách đúng và rẻ nhất: CHO XUỐNG DÒNG. Khung hẹp thì bốn viên thành hai
     hàng, thấy đủ cả bốn, không giấu gì, không phụ thuộc bề rộng cửa sổ. */
/* ⭐ 02/09 (anh Triệu): *"cân chỉnh và xếp 4 nút trên trên 1 dòng thôi"* —
   trước đây `flex-wrap: wrap` nên "Lời mời" rơi xuống dòng hai.
   ⛔ ĐỔI SANG MỘT DÒNG nhưng **GIỮ NGUYÊN nguyên tắc cũ: ⛔ không giấu nút nào**:
     · `nowrap` + `flex: 1 1 auto` ⇒ bốn viên **chia đều** bề ngang cột, cân đối
     · `min-width: 0` ⇒ viên được phép co (⛔ thiếu dòng này thì flex ⛔ KHÔNG co
       nhỏ hơn nội dung — đúng bẫy đã làm văng thanh nút màn gọi hôm qua)
     · `overflow-x: auto` là **lưới an toàn**: cột hẹp bất thường thì cuộn ngang,
       vẫn tới được cả bốn — ⛔ không cắt mất viên nào
     · padding co lại 5px→8px và `white-space: nowrap` để chữ ⛔ không gãy dòng */
.loc { display: flex; flex-wrap: nowrap; gap: 6px; padding: 8px 12px;
       overflow-x: auto; scrollbar-width: none; }
.loc::-webkit-scrollbar { display: none; }
.loc button { flex: 1 1 auto; min-width: 0; padding: 5px 8px; border-radius: 999px;
              border: 1px solid transparent; background: var(--surface-raised);
              color: var(--ink-3); font: inherit; font-size: 12px;
              white-space: nowrap; text-align: center;
              cursor: pointer; }
.loc button.on { background: color-mix(in srgb, var(--gold) 18%, transparent);
                 border-color: var(--gold); color: var(--gold);
                 font-weight: 700; }
.loc button .n { margin-left: 5px; padding: 0 5px; border-radius: 999px;
                 background: var(--gold); color: var(--on-accent);
                 font-size: 11.5px; font-weight: 700; }

/* ═══ ⭐⭐ 04/09/2026 — TAB TỰ TẠO: DẤU (+) GHIM NGOÀI VÙNG CUỘN ═══

   ⚠ ÁN 02/09 — huy hiệu E2EE từng TRÔI KHỎI MÀN vì nó nằm TRONG vùng cuộn
     ngang. Dải viên lọc nay có thể mang tới 4 + 10 = 14 viên, tức chắc chắn
     tràn. Nếu nút (+) nằm trong `.loc` thì đủ 10 tab là **đường tạo tab biến
     mất khỏi màn hình** — mà đó là đường DUY NHẤT tới hộp xoá tab, nên người
     dùng kẹt luôn: ⛔ không tạo được, ⛔ cũng không xoá được.
   ⇒ `.loc` (cuộn ngang, giữ NGUYÊN mọi kiểu cũ) bọc trong `.loc-dai`, còn nút
     (+) là anh em của nó với `flex: none` ⇒ LUÔN THẤY.
   ⛔ `.loc` ⛔ KHÔNG đổi một thuộc tính nào — bốn viên cũ giữ nguyên bề ngoài. */
.loc-dai { display: flex; align-items: center; }
.loc-dai > .loc { flex: 1 1 auto; min-width: 0; }
.ibtn.loc-them { flex: none; width: 32px; height: 32px; padding: 7px;
                 margin-right: 8px; background: var(--surface-raised); }
/* ⛔⛔ CHỈ KHI CÓ TAB TỰ TẠO. `.loc button` gốc mang `flex: 1 1 auto; min-width: 0`
   để BỐN viên chia đều bề ngang — nhưng đúng hai thuộc tính ấy cho viên CO NHỎ
   HƠN NỘI DUNG, nên với 14 viên thì dải ⛔ KHÔNG BAO GIỜ TRÀN ⇒ `overflow-x`
   ⛔ không có gì để cuộn và chữ bị CẮT CỤT trong im lặng. Thôi co ⇒ tràn thật
   ⇒ cuộn thật. ⛔ ⛔ KHÔNG có tab thì lớp này ⛔ không được gắn (xem chat-app.js)
   nên bốn viên cũ giữ NGUYÊN bố cục chia đều. */
.loc.co-tab button { flex: 0 0 auto; }
.loc button[data-loc^="tab:"] { max-width: 132px; overflow: hidden;
                                text-overflow: ellipsis; }

/* Hộp "Tab của tôi" — danh sách tab, mỗi hàng có tên · số hội thoại · 4 nút */
.tab-ql { display: flex; flex-direction: column; gap: 6px; max-height: 46vh;
          overflow-y: auto; margin-bottom: 12px; }
.tab-hang { display: flex; align-items: center; gap: 4px; padding: 4px 4px 4px 10px;
            border-radius: 9px; background: var(--surface-raised); }
.tab-hang .tn { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
                white-space: nowrap; font-size: 13px; color: var(--ink); }
.tab-hang .sl { flex: none; margin-right: 4px; padding: 0 6px; border-radius: 999px;
                background: var(--gold); color: var(--on-accent);
                font-size: 11.5px; font-weight: 700; }
.ibtn.sm { width: 30px; height: 30px; padding: 6px; }

/* Hộp "Tab của hội thoại này" — mỗi tab một hàng bật/tắt.
   ⛔ Trạng thái nói bằng CHỮ + HÌNH, ⛔ không chỉ bằng MÀU (án 04/09: phân biệt
     bằng màu ⇒ hai ảnh y hệt nhau với người ⛔ không phân biệt được màu). */
.tab-chon { display: flex; align-items: center; gap: 9px; width: 100%;
            padding: 9px 10px; border: 1px solid transparent; border-radius: 9px;
            background: var(--surface-raised); color: var(--ink-2);
            font: inherit; font-size: 13px; text-align: left; cursor: pointer; }
.tab-chon .ic { flex: none; width: 18px; height: 18px; }
.tab-chon .tn { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
                white-space: nowrap; }
.tab-chon .tt { flex: none; font-size: 11.5px; color: var(--ink-3); }
.tab-chon.on { border-color: var(--gold); color: var(--gold); font-weight: 700; }
.tab-chon.on .tt { color: var(--gold); }
.tab-chon:disabled { opacity: .55; cursor: default; }

/* ═══ ⭐ ④ (08/08/2026) — Ô TÌM DÍNH ĐỈNH + BA KHỐI KẾT QUẢ ═══ */
.tk-o { position: sticky; top: 40px; z-index: 2; display: flex; align-items: center;
        gap: 7px; margin: 8px 10px 2px; padding: 6px 9px; border-radius: 9px;
        background: var(--navy-deep); border: 1px solid var(--line); }
.tk-o .ic { flex: none; width: 15px; height: 15px; color: var(--ink-3); }
.tk-o input { flex: 1; min-width: 0; border: 0; background: none; color: var(--ink);
              font: inherit; font-size: 13px; padding: 2px 0; }
.tk-o input:focus { outline: none; }
.tk-o:focus-within { border-color: var(--gold); }
.tk-o .ibtn { flex: none; width: 24px; height: 24px; padding: 4px; }
/* trích đoạn: tô nền gold đúng chữ trúng */
.hang .xt mark { background: color-mix(in srgb, var(--gold) 34%, transparent);
                 color: var(--ink); border-radius: 3px; padding: 0 1px; }
/* ⚠ TỰ BẮT KHI CHỤP MÀN 08/08: hàng danh sách vốn `white-space: nowrap`, nên
   chữ trúng nằm hơi xa đầu câu là bị cắt mất — người dùng thấy kết quả mà
   KHÔNG thấy chỗ trúng. Riêng hàng kết quả tìm cho xuống 2 dòng. */
.tk-hang .xt, .tk-nguoi .hang .xt { white-space: normal; display: -webkit-box;
  -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; }
.tk-hang .av { background: var(--surface-raised); }
.tk-hang .av .ic { width: 20px; height: 20px; color: var(--gold-soft); }
.tk-nguoi:empty { display: none; }
/* ⛔ DÒNG NÓI THẬT VỀ GIỚI HẠN E2EE — luôn hiện dưới khối tin nhắn. */
.tk-that { display: flex; align-items: flex-start; gap: 7px; margin: 8px 12px 0;
           padding: 8px 10px; border-radius: 8px; font-size: 11.5px; line-height: 1.5;
           color: var(--ink-3); background: var(--surface-raised); }
.tk-that .ic { flex: none; width: 14px; height: 14px; margin-top: 1px; color: var(--gold-soft); }
.tk-that-nut { padding: 8px 12px 14px; }

/* Tin được cuộn tới từ kết quả tìm — NHÁY NỀN GOLD HAI NHỊP rồi tắt. */
@keyframes sv-nhay-tin {
  0%, 100% { background: var(--surface); }
  15%, 65% { background: color-mix(in srgb, var(--gold) 38%, var(--surface)); }
}
.tin.nhay { animation: sv-nhay-tin 1.1s ease-in-out 2; }
@media (prefers-reduced-motion: reduce) {
  .tin.nhay { animation: none; outline: 2px solid var(--gold); outline-offset: 2px; }
}

.hang.mo { cursor: default; }
.hang.mo:hover { background: transparent; }
.moi-nut { display: flex; gap: 6px; padding: 0 12px 10px 54px; }

/* ─── cột hội thoại ─── */
.hoi { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.hoi-hd { display: flex; align-items: center; gap: 8px; flex: none; padding: 8px 12px;
          border-bottom: 1px solid var(--line); background: var(--navy-dark); }
.hoi-hd .n { flex: 1; min-width: 0; }
.hoi-hd .n b { display: block; font-size: 15px; overflow: hidden;
               text-overflow: ellipsis; white-space: nowrap; }
/* ⛔ 🔴 TỰ BẮT 08/08/2026 — CA "360 ĐIỂM × CHỖ GẮN `bang`", đúng ca mà đặc tả
   M18 cảnh báo là hay bị bỏ nhất và dễ vỡ nhất.
   Dòng sống ("6 người · đã mã hoá đầu-cuối") KHÔNG có luật chống xuống dòng,
   mà ở bảng công cụ 360 điểm thì 4 nút icon + nút quay lại + ảnh đã ăn gần hết
   bề ngang ⇒ dòng đó gãy làm BỐN, đầu phòng cao hơn 250 điểm và đẩy hết dòng
   tin xuống. Phép đếm chuỗi không bắt được — chỉ chụp màn ở đúng bề rộng đó
   mới thấy. Nay cắt bằng dấu ba chấm, câu đầy đủ vẫn còn trong dòng sống khi
   khung rộng ra. */
.hoi-hd { flex-wrap: nowrap; }
.hoi-hd .n span { display: block; font-size: 12px; color: var(--ink-3);
                  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tin-vung { flex: 1 1 auto; overflow-y: auto; overflow-x: hidden; min-height: 0;
            padding: 14px 12px; display: flex; flex-direction: column; gap: 8px; }
/* ⭐ ③ (08/08/2026) — DẢI TRÊN CÙNG CỦA LỊCH SỬ: nút tải thêm · vòng xoay ·
   câu "đã tới đầu hội thoại". `flex: none` để nó không bị co khi danh sách dài. */
.tin-dinh { flex: none; align-self: center; display: flex; align-items: center; gap: 7px;
            padding: 2px 0 6px; font-size: 12px; color: var(--ink-3); }
.tin-dinh .ic { width: 14px; height: 14px; }
.tin-dinh.xong { opacity: .7; font-style: italic; }

/* ⭐ ⑤ (08/08/2026) — DẢI NGÀY DÍNH. Viên thuốc nhỏ căn giữa, dính đỉnh vùng
   cuộn khi kéo qua. `align-self: center` giữ nó ở giữa trong cột flex, còn
   `position: sticky; top: 0` là thứ làm nó dính — ⛔ phải dính vào chính phần
   tử CÓ overflow (`.tin-vung`), dính vào thẻ khác thì không bao giờ ăn. */
.daingay { position: sticky; top: 0; z-index: 3; flex: none; align-self: center;
           padding: 3px 0 5px; pointer-events: none; }
.daingay span { display: inline-block; padding: 3px 12px; border-radius: 999px;
                font-size: 11px; font-weight: 600; letter-spacing: .02em;
                color: var(--ink-2); background: var(--surface-raised);
                border: 1px solid var(--line); }

.tin { max-width: min(74ch, 78%); padding: 8px 11px; border-radius: 12px;
       background: var(--surface); align-self: flex-start; word-wrap: break-word;
       overflow-wrap: anywhere; }
/* ⭐ Đ4.4b (17/08/2026 — lệnh anh Triệu: bong bóng tin của mình "màu vàng
   thẫm quá, cần chuyển sang màu SÁNG và CHỮ ĐEN", nhấn "rất quan trọng
   trải nghiệm người dùng"). ĐỒNG BỘ với app mobile: cùng cặp màu
   #F3E5AB / #1B1B1B (SVColor.bongBongToi / chuBongBongToi) — hai bề mặt
   phải nhìn như một sản phẩm.
   ⛔ CHỈ đổi bong bóng CỦA MÌNH (.tin.toi). Bong bóng người khác giữ
   nguyên nền tối để hai bên còn tách bạch. */
.tin.toi { align-self: flex-end; background: #F3E5AB; color: #1B1B1B; }
.tin.toi .noi { color: #1B1B1B; }
.tin.toi .ai, .tin.toi .luc { color: #5A5340; }
.tin.toi .tuxoa { background: rgba(90,83,64,.14); color: #5A5340; }
.tin.toi .tl-trich { background: rgba(27,27,27,.07); }
.tin .ai { font-size: 11px; color: var(--ink-3); margin-bottom: 2px; }
.tin .noi { font-size: 14px; line-height: 1.5; white-space: pre-wrap; }
.tin .luc { font-size: 11px; color: var(--ink-3); margin-top: 3px; text-align: right; }
/* ⭐ Đ4.2c PARITY (17/08): khối BẢN DỊCH hiện dưới nội dung tin — tách bạch
   với bản gốc bằng viền trái, không lẫn vào nhau. */
.tin .dich-ra { margin-top: 6px; padding: 5px 8px; font-size: 13px;
                line-height: 1.45; border-left: 2px solid var(--gold-soft);
                background: rgba(232,182,76,.10); border-radius: 6px;
                white-space: pre-wrap; }
.tin.toi .dich-ra { background: rgba(27,27,27,.07); border-left-color: #5A5340;
                    color: #1B1B1B; }
/* VÁ 07/08/2026 — đồng hồ đếm ngược tự xoá, đứng cạnh giờ gửi.
   Vàng khi còn thời gian, đỏ khi sắp hết — đọc được cả khi không phân biệt màu
   nhờ có biểu tượng đồng hồ đi kèm. */
.tin .tuxoa { display: inline-flex; align-items: center; gap: 3px; margin-left: 6px;
              padding: 1px 6px; border-radius: 9px; font-size: 11.5px;
              background: rgba(232,182,76,.14); color: var(--gold-soft); }
.tin .tuxoa .ic { width: 11px; height: 11px; }
.tin .tuxoa.gap { background: rgba(226,87,76,.16); color: var(--danger); }
/* ═══ ⭐ ⑥ (08/08/2026) — CỬ CHỈ TRÊN TIN ═══
   ⛔ Web KHÔNG dùng vuốt-để-trả-lời: kéo chuột thì không ai đoán ra. Dùng nút
   hiện khi RÊ CHUỘT — và vẫn giữ đường bằng bàn phím (`:focus-within`) cho
   người không dùng chuột. */
.tin { position: relative; }
.tin-cuchi { position: absolute; top: -10px; display: flex; gap: 2px; padding: 2px;
             border-radius: 999px; background: var(--surface-raised);
             border: 1px solid var(--line); box-shadow: 0 4px 14px rgba(0,0,0,.35);
             opacity: 0; pointer-events: none; transition: opacity .12s ease; }
.tin:not(.toi) .tin-cuchi { right: -6px; }
.tin.toi .tin-cuchi { left: -6px; }
.tin:hover .tin-cuchi, .tin:focus-within .tin-cuchi { opacity: 1; pointer-events: auto; }
.tin-cuchi .ibtn { width: 26px; height: 26px; padding: 5px; }

/* ═══ ⭐ ④ 04/09 — CỤM BA BIỂU TƯỢNG TRONG BONG BÓNG (dịch · đọc · biểu cảm) ═══
   ⛔⛔ RÀNG BUỘC CỦA ANH TRIỆU: **nhỏ và mờ, ⛔ KHÔNG được lấn chữ**. Hôm nay
     anh vừa bắt một lỗi ẢNH ĐÈ LÊN CHỮ ⇒ ⛔ đừng tái diễn.
   ⇒ Cụm nằm TRONG hàng `.luc` (hàng giờ gửi) và **đẩy giờ sang phải bằng
     `float: left`** — ⛔ KHÔNG dùng `position: absolute`: tuyệt đối thì nó nằm
     NGOÀI luồng, ⛔ không ai giữ chỗ cho nó, và đó chính là cách chữ bị đè.
     Nổi trái thì trình duyệt PHẢI trừ đúng bề rộng cụm ra khỏi hàng giờ.
   ⭐ Hàng `.luc` vốn `text-align: right` và chỉ chứa mấy chữ số giờ ⇒ bên trái
     là chỗ TRỐNG SẴN, cụm này ⛔ không cướp chỗ của ai. */
/* ⛔⛔ 05/09 — ĐỘ RÕ LÚC THƯỜNG: .45 → .75. ⛔ ĐÂY LÀ SỐ ĐO, ⛔ đừng hạ lại.
   Ngưỡng WCAG cho THÀNH PHẦN GIAO DIỆN (nút/icon) là 3.0:1. Tính tương phản
   thật — ⚠ nhớ rằng `--ink-3` ĐÃ mang sẵn alpha .62 nên .45 thực ra là 0.279:
       bối cảnh                .45 (cũ)   .65    .75    .9 (rê chuột)
       tối · tin đến             2.30⛔  3.26✅ 3.84✅   4.84✅
       tối · tin của mình        1.99⛔  2.87⛔ 3.51✅   4.83✅
       sáng · tin đến            1.87⛔  2.59⛔ 3.10✅   4.15✅
       sáng · tin của mình       1.99⛔  2.87⛔ 3.51✅   4.83✅
   ⇒ .45 TRƯỢT cả bốn. .65 vẫn trượt 3/4. Chỉ .75 đạt cả bốn.
   ⭐ SAI LẦM CỦA BẢN CŨ: cho rằng "có hover là đủ". ⛔ Muốn RÊ CHUỘT vào một nút
     thì trước hết phải THẤY nó ở đó. Hover cứu được THAO TÁC, ⛔ không cứu được
     KHÁM PHÁ — đúng con bệnh "có mã ⛔ không phải có tính năng". */
.tin .luc .tin-tv { float: left; display: inline-flex; gap: 1px; align-items: center;
  margin: -2px 6px 0 -2px; opacity: .75; transition: opacity .15s; }
.tin:hover .luc .tin-tv, .tin:focus-within .luc .tin-tv { opacity: .9; }
.tin .luc .tin-tv .ibtn { width: 20px; height: 20px; padding: 3px; border: 0;
  background: transparent; color: inherit; border-radius: 5px; }
.tin .luc .tin-tv .ibtn:hover { background: rgba(127, 127, 127, .18); }
.tin .luc .tin-tv .ibtn svg { width: 100%; height: 100%; }
/* ⛔ 05/09 — ĐÃ GỠ nhánh `@media (hover: none)` ở đây: nó đặt `.75`, mà nay
   `.75` đã là giá trị LÚC THƯỜNG cho mọi nền nên nhánh này thành thừa.
   ⭐ Ghi lại để người sau ⛔ không tưởng mất tính năng: lý lẽ của nhánh cũ
   (*"màn cảm ứng ⛔ không có hover, nút mờ mãi là coi như ⛔ không có nút"*)
   VẪN ĐÚNG — nó đã được nâng lên áp cho CẢ máy tính, vì lý lẽ ấy đúng ở đó
   luôn: thấy nút rồi mới rê chuột vào được. */

/* Tấm chỉnh TỐC ĐỘ ĐỌC */
.doc-toc { display: flex; align-items: center; gap: 10px; margin: 10px 0 4px; }
.doc-toc input[type="range"] { flex: 1; min-width: 0; }
.doc-toc output { min-width: 52px; text-align: right; font-variant-numeric: tabular-nums;
  font-weight: 600; }
@media (hover: none) {
  /* Máy chạm: không có "rê chuột" ⇒ để nút hiện sẵn, mờ. Ẩn hẳn là xoá đường. */
  .tin-cuchi { opacity: .55; pointer-events: auto; }
}

/* KHỐI TRÍCH ở đầu bong bóng khi tin này trả lời một tin khác. */
.tl-trich { display: flex; flex-direction: column; gap: 1px; width: 100%;
            margin: 0 0 6px; padding: 5px 9px; border: 0;
            border-left: 3px solid var(--gold); border-radius: 6px;
            background: color-mix(in srgb, var(--gold) 9%, transparent);
            color: inherit; font: inherit; text-align: left; cursor: pointer; }
.tl-trich b { font-size: 11.5px; color: var(--gold-soft); }
.tl-trich span { font-size: 12px; color: var(--ink-3); overflow: hidden;
                 text-overflow: ellipsis; white-space: nowrap; }

/* DẢI "ĐANG TRẢ LỜI" trên thanh soạn tin. */
.tl-dai { flex: none; display: flex; align-items: center; gap: 8px;
          padding: 7px 12px; border-top: 1px solid var(--line);
          background: color-mix(in srgb, var(--gold) 8%, var(--navy-dark)); }
.tl-dai[hidden] { display: none; }
.tl-dai > .ic { flex: none; width: 15px; height: 15px; color: var(--gold); }
.tl-dai .tl-n { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.tl-dai .tl-n b { font-size: 12px; color: var(--gold-soft); }
.tl-dai .tl-n span { font-size: 12px; color: var(--ink-3); overflow: hidden;
                     text-overflow: ellipsis; white-space: nowrap; }

/* HÀNG CẢM XÚC dưới bong bóng — vẽ bằng NÉT, ⛔ không in emoji lên mặt sản phẩm. */
.cx-hang { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
.cx-vien { display: inline-flex; align-items: center; gap: 4px; padding: 1px 7px;
           border-radius: 999px; border: 1px solid var(--line);
           background: var(--surface-raised); color: var(--ink-2);
           font: inherit; font-size: 11px; cursor: pointer; }
.cx-vien.toi { border-color: var(--gold); color: var(--gold);
               background: color-mix(in srgb, var(--gold) 16%, transparent); }
.cx-vien .n { font-variant-numeric: tabular-nums; }
.cx-bang { display: flex; gap: 2px; min-width: 0; padding: 5px; }
.cx-chon { display: grid; place-items: center; width: 34px; height: 34px; padding: 6px;
           border: 0; border-radius: 8px; background: none; cursor: pointer; }
.cx-chon:hover { background: color-mix(in srgb, var(--gold) 18%, transparent); }
.cx-chon .cx { width: 20px; height: 20px; }

/* CHẾ ĐỘ CHỌN NHIỀU — thanh đầu phòng ĐỔI, thanh gốc chỉ tạm ẩn (không xoá). */
.chon-hd { background: color-mix(in srgb, var(--gold) 14%, var(--navy-dark)); }
.chon-hd .n b { font-size: 14px; }
.hoi-hd.an-khi-chon { display: none; }
.tin.chon-duoc { padding-left: 34px; cursor: pointer; }
.tin.da-chon { background: color-mix(in srgb, var(--gold) 14%, var(--surface)); }
.tin .tin-o { position: absolute; left: 9px; top: 10px; width: 17px; height: 17px;
              border-radius: 50%; border: 1.5px solid var(--ink-3);
              display: grid; place-items: center; }
.tin .tin-o.on { border-color: var(--gold); background: var(--gold); color: var(--on-accent); }
.tin .tin-o svg { width: 12px; height: 12px; }

.tin.chuagm { border: 1px dashed var(--warning); background: transparent; }
.tin.chuagm .noi { color: var(--warning); font-size: 13px; }
.tin .tep { display: flex; align-items: center; gap: 8px; }
.tin .tep .ic { color: var(--gold-soft); }

.soan { flex: none; display: flex; gap: 8px; align-items: flex-end;
        padding: 10px 12px; border-top: 1px solid var(--line); background: var(--navy-dark); }
.soan textarea { flex: 1; min-width: 0; resize: none; max-height: 40dvh;
                 font: inherit; font-size: 14px; line-height: 1.45; }

/* ─── 360 px: một cột, danh sách trượt lên trên ─── */
/* ⛔ 🔴 VÁ 08/08/2026 (⑨ M18) — HAI KHỐI DƯỚI ĐÂY TRƯỚC ĐÂY VIẾT TRẦN
   (`.mid` · `.ds` · `.tin`), mà `chat.css` được nạp trong CẢ `hop.html`
   ⇒ luật của cột chat ăn thẳng vào `#uiMid` của TRANG HỌP. Đúng loại "xô trang
   họp" mà luật §3 cấm. Nay bám vào `.sc-goc` — gốc chat do `datChoGan()` gắn —
   nên không rò ra ngoài chat.
   ⚠ Bề rộng cửa sổ vẫn ĐÚNG chỗ để đo cho `trang` và `toanman` (hai chỗ này
   chat chiếm trọn khung nhìn). Chỗ SAI là `bang`: bảng công cụ ~360 điểm nằm
   trong cửa sổ có thể rộng 1920 ⇒ `bang` KHÔNG đọc bề rộng cửa sổ nữa, xem
   khối "chỗ gắn `bang`" bên dưới.
   ⛔ Giữ nguyên `.top .toi` / `.chi-nho` viết trần: `.top` của chat là thẻ
   riêng (`#cTop` / `#scTop`), trang họp không có `.toi` lẫn `.chi-nho`. */
@media (max-width: 720px) {
  .sc-goc.sc-trang, .sc-goc.sc-toanman { flex-direction: column; }
  .sc-goc.sc-trang > .ds, .sc-goc.sc-toanman > .ds {
    width: 100%; max-height: 42dvh; border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  .sc-goc.sc-trang .hoi-hd .n b, .sc-goc.sc-toanman .hoi-hd .n b { font-size: 14px; }
  .sc-goc.sc-trang .tin, .sc-goc.sc-toanman .tin { max-width: 92%; }
  .top .toi { max-width: 32vw; }
}
/* khi đang mở một hội thoại trên máy nhỏ thì ẩn danh sách để không trượt hai tầng */
@media (max-width: 720px) {
  #app.dang-mo .ds { display: none; }
  .sc-goc.sc-trang.dang-mo > .ds, .sc-goc.sc-toanman.dang-mo > .ds { display: none; }
}

/* ─── ô nhập trong hộp thoại ───
   `hop.css` gói ô nhập trong `.field > label + input`; trang chat gói ngược lại
   (`<label class="fl">chữ<input></label>`) cho gọn khi dựng bằng chuỗi, nên phải
   tự khai kiểu — vẫn đúng token màu, không chép hex. */
.fl { display: block; margin-bottom: 12px; font-size: 12px; color: var(--ink-2); }
.fl input, .fl textarea, .fl select {
  display: block; width: 100%; margin-top: 5px; color: var(--ink);
  background: var(--navy-deep); border: 1px solid var(--line);
  border-radius: 9px; padding: 11px 12px; font-size: 15px;
}
.fl textarea { resize: vertical; line-height: 1.45; }
.fl input:focus, .fl textarea:focus, .soan textarea:focus {
  outline: 2px solid var(--gold); outline-offset: 1px;
}
.soan textarea { background: var(--navy-deep); border: 1px solid var(--line);
                 border-radius: 10px; padding: 10px 12px; color: var(--ink); }

/* chưa đăng nhập thì không có gì để liệt kê ⇒ bỏ cột trống cho khỏi trơ khung */
#app.chua-vao .ds { display: none; }
/* nút "về danh sách" chỉ có nghĩa khi hai cột chồng nhau (máy nhỏ) */
.chi-nho { display: none; }
@media (max-width: 720px) { .chi-nho { display: inline-grid; } }

/* khối khoá phục hồi trong hộp thoại — khoá phải đọc được, chép được */
.khoa-hien { font-family: ui-monospace, Menlo, monospace; font-size: 13px;
             line-height: 1.7; letter-spacing: .04em; word-spacing: .3em;
             background: var(--navy-deep); border: 1px solid var(--gold);
             border-radius: 10px; padding: 10px 12px; margin: 10px 0;
             user-select: all; overflow-wrap: anywhere; }

/* hàng tệp trong bong bóng tin: tên tệp co lại, nút Tải về không bị đẩy ra ngoài */
.tin .noi.tep { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tin .noi.tep .tt { flex: 1; min-width: 0; font-size: 13px; overflow-wrap: anywhere; }
.soan .ib { flex: none; }

/* thang thời hạn tự xoá — các mốc xếp thành lưới, mốc đang dùng sáng lên */
.tx-ds { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 4px; }
.tx-ds .tx.on { border-color: var(--gold); color: var(--gold-soft);
                background: color-mix(in srgb, var(--gold) 14%, transparent); }

/* ─── khi CHAT BẢO MẬT nằm trong bảng công cụ của TRANG HỌP ───
   Bảng công cụ hẹp (≈360px) nên hai cột phải xếp DỌC: danh sách trên, hội thoại
   dưới; và mọi thứ phải nằm trong chiều cao của bảng, không đẩy bảng dài ra. */
.sc-goi { display: flex; flex-direction: column; height: 100%; min-height: 0; gap: 0; }
.sc-goi .ds { width: auto; max-height: 40%; border-right: 0;
              border-bottom: 1px solid var(--line); }
.sc-goi .hoi { min-height: 0; }
.sc-goi .tin { max-width: 96%; }
.sc-goi .soan { padding: 8px; }

/* ═══ ⭐ ⑨ M18 — CHỖ GẮN `bang`: CHUYỂN MÀN, KHÔNG CHIA 40/60 ═══
   ⛔ KHÔNG dùng `@media`: bảng công cụ rộng ~360 điểm nhưng cửa sổ có thể 1920.
   Đặc tả M18: bỏ kiểu chia 40% danh sách / 60% hội thoại — bề ngang 360 không
   đủ cho hai tầng cuộn. Nay: chưa mở phòng thì danh sách chiếm khung; mở phòng
   rồi thì ẩn hẳn danh sách, nút "về danh sách" luôn hiện.
   ⚠ Cố ý KHÔNG ẩn cột hội thoại lúc chưa mở phòng: dải nhắc khoá phục hồi
   (`#bnKhoa`) nằm trong đó, mà ở chỗ gắn này KHÔNG có thanh trên nên đó là
   đường DUY NHẤT tới hộp khoá — ẩn đi là xoá một đường đang có (§1). */
.sc-goc.sc-bang { flex-direction: column; }
.sc-goc.sc-bang > .ds { width: auto; flex: 1 1 auto; max-height: none;
                        border-right: 0; border-bottom: 1px solid var(--line); }
.sc-goc.sc-bang > .hoi { flex: none; min-height: 0; }
.sc-goc.sc-bang.dang-mo > .ds { display: none; }
.sc-goc.sc-bang.dang-mo > .hoi { flex: 1 1 auto; }
.sc-goc.sc-bang .tin { max-width: 96%; }
.sc-goc.sc-bang .soan { padding: 8px; }
.sc-goc.sc-bang .hoi-hd .n b { font-size: 14px; }
/* nút "về danh sách" là thứ DUY NHẤT đưa người dùng trở ra ⇒ luôn hiện ở `bang` */
.sc-goc.sc-bang .chi-nho { display: inline-grid; }

/* ⛔ 🔴 VÁ 09/08/2026 — BẢNG HẸP (~250 px) VỠ BỐ CỤC. Số đo tay ở bề ngang 250:
       · dải nhắc khoá `#bnKhoa` cao **403,8 px ≈ 20 dòng** — nuốt gần trọn bảng;
       · cột danh sách hội thoại chỉ còn **47,3 px** (cột hội thoại 548,8 px)
         ⇒ đúng triệu chứng "không thấy cột danh sách", nên nhóm case vừa tạo
         KHÔNG hiện ra chỗ toast đã hứa.
   NGUYÊN NHÂN: `.sc-bang > .hoi` để `flex: none` ⇒ nó cao bằng NỘI DUNG, mà nội
   dung đang có dải nhắc khoá khổng lồ ⇒ ép `.ds` xuống còn vài chục điểm.
   ⛔ KHÔNG bỏ nút nào: dải nhắc khoá vẫn còn đủ chữ và đủ nút, chỉ gọn lại và
   tự cuộn khi quá cao. */

/* ① Dải nhắc khoá: xếp dọc, chữ nhỏ hơn, và KHÔNG được cao quá 1/3 bảng. */
.sc-goc.sc-bang #bnKhoa {
  flex-wrap: wrap; gap: 6px; padding: 8px;
  font-size: 12px; line-height: 1.35;
  max-height: 33%; overflow-y: auto;
}
.sc-goc.sc-bang #bnKhoa .txt { flex: 1 1 100%; min-width: 0; }

/* ② CHƯA mở hội thoại ⇒ DANH SÁCH là phần chính của bảng, không phải cột hội
      thoại rỗng. Mở rồi thì luật `.dang-mo` sẵn có tiếp quản (danh sách ẩn,
      hội thoại chiếm trọn) — không đụng tới. */
.sc-goc.sc-bang:not(.dang-mo) > .ds {
  flex: 1 1 auto; min-height: 40%; overflow-y: auto;
}
.sc-goc.sc-bang:not(.dang-mo) > .hoi {
  flex: 0 1 auto; min-height: 0; max-height: 55%; overflow-y: auto;
}

/* CHAT BẢO MẬT toàn màn hình (mở từ tiền sảnh trang họp) */
.sc-full { position: fixed; inset: 0; z-index: 70; display: flex; flex-direction: column;
           background: var(--navy-deep); }
.sc-full[hidden] { display: none; }
.sc-full .mid { flex: 1 1 auto; display: flex; min-height: 0; }

/* ═══ ⭐ ⑦ (08/08/2026) — MÀN GỌI TOÀN KHUNG + THANH GỌI THU NHỎ ═══
   ⛔ `.sc-goi-box` bên dưới GIỮ NGUYÊN, không xoá: đó là kiểu dáng thừa kế của
   khung gọi cũ (nằm trong hội thoại). Màn mới nối vào `.sc-full` đã có sẵn. */

/* z-index của lớp phủ gọi: trên lớp phủ chat toàn màn hình (`.sc-full` = 70),
   dưới hộp thoại (`hop.css .modal` = 80) — hộp thoại vẫn phải bấm được. */
.sc-goi-man { z-index: 72; }
.sc-goi-man .sc-goi-hinh { flex: 1 1 auto; max-height: none; aspect-ratio: auto; }
.sc-goi-man .sc-goi-thanh { flex: none; padding: 12px 14px;
  padding-bottom: calc(12px + env(safe-area-inset-bottom));
  background: var(--navy-dark); border-top: 1px solid var(--line); }
/* Hình của mình to hơn ở màn toàn khung, nhưng vẫn chừa mép an toàn. */
.sc-goi-man .sc-goi-hinh #scVidMinh { width: min(28%, 220px); right: 12px; bottom: 12px; }
/* Nút thu nhỏ dùng mũi tên chỉ XUỐNG — cùng ngôn ngữ hình với "thu về thanh". */
.sc-goi-man #scThuNho svg { transform: rotate(90deg); }
.sc-goi-man .sc-dh { flex: none; font-size: 13px; color: var(--gold-soft);
  font-variant-numeric: tabular-nums; letter-spacing: .04em; }

/* ⛔ Vỉ thông báo của trang họp ở z-index 60, tức DƯỚI lớp phủ gọi ⇒ trong lúc
   gọi thì mọi vỉ (tắt mic, lỗi…) sẽ vô hình. Nâng vỉ lên CHỈ trong lúc gọi,
   bằng cờ trên `<html>` do `veKhungGoi`/`dongKhungGoi` bật-tắt — không đụng
   `hop.css` và không ảnh hưởng trang họp lúc không gọi. */
html.sv-dang-goi .toast-wrap { z-index: 85; }

/* THANH GỌI THU NHỎ — cao 38 điểm, dính đỉnh, nền gold (M11).
   ⛔ Gắn vào `body` nên sống độc lập với cột hội thoại: đổi phòng bao nhiêu
   lần cũng không mất — đó chính là lỗi mà màn này vá. */
.sc-thanh-goi { position: fixed; z-index: 76; left: 0; right: 0;
  top: env(safe-area-inset-top); height: 38px;
  display: flex; align-items: center; gap: 8px; padding: 0 8px;
  background: var(--gold); color: var(--on-accent); }
.sc-thanh-goi .cham { flex: none; width: 8px; height: 8px; border-radius: 50%;
  background: var(--on-accent); animation: sv-cham 1.4s ease-in-out infinite; }
@keyframes sv-cham { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }
.sc-thanh-goi .sc-tg-mo { flex: 1; min-width: 0; display: flex; align-items: baseline;
  gap: 8px; border: 0; background: none; color: inherit; font: inherit;
  font-size: 13px; text-align: left; cursor: pointer; padding: 0;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.sc-thanh-goi .sc-tg-ten { font-weight: 700; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; max-width: 45%; }
.sc-thanh-goi #scTgChu { font-variant-numeric: tabular-nums; }
.sc-thanh-goi .ibtn { flex: none; color: var(--on-accent); }
.sc-thanh-goi .btn { flex: none; }
/* ⛔ Thanh KHÔNG được che thanh trên: ở `trang`/`toanman` thì đẩy nội dung
   xuống đúng 38 điểm. Che thanh trên là mất đường tới đèn khoá (Trung tâm an
   toàn) và nút đổi ngôn ngữ — hai thứ vẫn phải bấm được trong lúc gọi. */
html.sv-dang-goi.sv-cho-trang #app { padding-top: calc(38px + env(safe-area-inset-top)); }
html.sv-dang-goi.sv-cho-toanman .sc-full { top: calc(38px + env(safe-area-inset-top)); }
/* Ở `bang` thanh nằm TRONG gốc chat (xem `veThanhGoi`) nên không fixed, không
   vắt ngang trang họp. */
.sc-goc.sc-bang > .sc-thanh-goi { position: static; flex: none; }
@media (prefers-reduced-motion: reduce) { .sc-thanh-goi .cham { animation: none } }

/* BĂNG CUỘC GỌI ĐẾN — giữ nguyên hình dáng băng ngang (M11 bảo giữ), chỉ nổi
   lên đỉnh vì nay nó gắn vào `body` chứ không nằm trong cột hội thoại. */
.sc-goi-den-noi { position: fixed; z-index: 78;
  top: calc(8px + env(safe-area-inset-top)); left: 50%; transform: translateX(-50%);
  width: min(560px, calc(100vw - 16px)); box-shadow: 0 10px 30px rgba(0,0,0,.45); }

/* ─── khung CUỘC GỌI 1:1 nằm ngay trong hội thoại ─── */
.sc-goi-box { flex: none; border-bottom: 1px solid var(--line); background: var(--navy-dark); }
.sc-goi-hinh { position: relative; background: #000; aspect-ratio: 16/9; max-height: 46dvh; }
.sc-goi-hinh video { width: 100%; height: 100%; object-fit: cover; display: block; }
.sc-goi-hinh #scVidMinh { position: absolute; right: 8px; bottom: 8px; width: 26%;
  height: auto; border: 1px solid var(--line); border-radius: 8px; }
/* ⛔ 03/09 — GỠ luật `.sc-goi-thanh` MỘT HÀNG ở đây. Markup thật (chat-app.js
   :4382) là markup HAI HÀNG của hop.css:1407 (`.sc-goi-tt` trên, hàng nút
   dưới). Hai luật trộn nhau cho ra thứ ⛔ không khớp ý đồ của bên nào:
   `flex-direction:column` của hop.css sống sót, gap/padding lấy của đây. */
.sc-goi-thanh .txt { flex: 1; min-width: 0; font-size: 13px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

/* bảng lệnh nổi khi bấm vào tên người — làm việc tại chỗ, không rời trang */
.ai-nut { background: none; border: 0; padding: 0; margin: 0 0 2px; font: inherit;
  font-size: 11px; color: var(--gold-soft); cursor: pointer; text-align: left; }
.ai-nut:hover { text-decoration: underline; }

/* ═══ ⭐ 04/09 — KHUNG TÊN + ẢNH ĐẠI DIỆN TRONG BONG BÓNG TIN ═══
   Lệnh chủ dự án: *"kể cả trong tin nhắn cũng đóng khung tên người cùng ảnh
   profile nếu có"*. Khối này chỉ mọc ở tin ĐẦU mỗi cụm (xem `moCumTin`).
 ⛔ TUYỆT ĐỐI ⛔ KHÔNG thêm `.av.vong-*` vào đây bằng CSS: vòng an toàn nói về
   NGƯỜI, mà lớp giao diện hôm nay ⛔ không có đường hỏi mức của một người —
   xem chốt ① ở `veNguoiGui`. Vẽ vòng bằng CSS là lách qua chính rào ấy. */
.tin .ai-nut { display: flex; align-items: center; gap: 6px; width: 100%;
  min-width: 0; margin: 0 0 4px; padding: 2px 6px 2px 2px; border-radius: 999px;
  background: rgba(255, 255, 255, .05); }
.tin .ai-nut:hover { background: rgba(255, 255, 255, .09); text-decoration: none; }
.tin .ai-nut:hover .ai-ten { text-decoration: underline; }
/* Ảnh nhỏ hơn hẳn avatar cột trái (44px): đây là dấu nhận người, ⛔ không phải
   chủ thể của dòng. `flex:none` để tên dài ⛔ không bóp méo hình tròn. */
.tin .ai-nut .av { width: 22px; height: 22px; font-size: 10px; flex: none; }
.tin .ai-nut .ai-ten { min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-weight: 600; }
/* Bong bóng CỦA MÌNH nền sáng ⇒ khung trắng-mờ tàng hình. Ở đây chỉ là lớp
   phòng xa: `veNguoiGui` ⛔ không dựng khối này cho tin của mình. */
.tin.toi .ai-nut { background: rgba(27, 27, 27, .07); }
.tin.toi .ai-nut:hover { background: rgba(27, 27, 27, .12); }
.sv-menu { position: fixed; z-index: 90; min-width: 210px; padding: 6px;
  background: var(--surface-raised); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,.45); }
.sv-menu-h { font-size: 11.5px; color: var(--ink-3); padding: 4px 8px 6px;
  border-bottom: 1px solid var(--line); margin-bottom: 4px;
  overflow-wrap: anywhere; }
.sv-menu button { display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 8px 8px; border: 0; border-radius: 8px; background: none;
  color: var(--ink); font: inherit; font-size: 13px; text-align: left; cursor: pointer; }
.sv-menu button:hover { background: color-mix(in srgb, var(--gold) 16%, transparent); }
/* ⛔ 🔴 TỰ BẮT 08/08/2026 KHI CHỤP MÀN — TÊN PHÒNG VÀ DÒNG SỐNG DÍNH LIỀN NHAU.
   Đầu phòng 1:1 bọc tên trong `<button class="ten-nut">`; nút mặc định là
   `inline-block`, nên dù `<b>` bên trong đã `display:block` thì dòng sống
   ("6 người · đã mã hoá đầu-cuối") vẫn trôi lên NẰM CẠNH tên, đọc ra
   "Tổ Kỹ thuật LAB6 người · đã mã hoá…". Phép đếm chuỗi không bắt được lỗi
   này — chỉ nhìn ảnh chụp mới thấy. */
.ten-nut { display: block; background: none; border: 0; padding: 0; margin: 0; font: inherit;
  color: inherit; cursor: pointer; text-align: left; max-width: 100%; }
.ten-nut:hover b { text-decoration: underline; color: var(--gold-soft); }
.tin[data-tin] { cursor: context-menu; }
.sv-menu button:disabled { opacity: .5; cursor: not-allowed; }

/* ⭐ M05 (07/08/2026) — DẤU TÍCH trạng thái gửi trên bong bóng của mình. */
.tin .luc .tich { display: inline-flex; width: 14px; height: 14px;
                  margin-left: 4px; vertical-align: -2px; opacity: .75; }
.tin .luc .tich svg { width: 100%; height: 100%; }
/* Hai tích XANH = đã đọc. Xanh ở đây là quy ước toàn cầu, không phải màu
   trang trí — xem luật màu trong sv_avatar.dart (nhánh app điện thoại). */
.tin .luc .tich.daxem { color: var(--success, #3DBE7B); opacity: 1; }

/* ⭐ M07 — cảm xúc vẽ bằng NÉT, không in ký tự emoji lên mặt sản phẩm. */
.cx { display: inline-flex; width: 15px; height: 15px; vertical-align: -3px;
      color: var(--gold); }
.cx svg { width: 100%; height: 100%; }

/* ⭐ M12 (07/08/2026) — NHẬT KÝ CUỘC GỌI trong hộp thoại.
   Dùng lại chính lớp `.hang` của cột danh sách: một khuôn hàng cho cả app,
   người dùng không phải học hai kiểu danh sách. */
.nhatky { max-height: 52vh; overflow-y: auto; margin: 4px -4px; }
.nhatky .hang { padding: 8px 6px; }
/* Cuộc gọi NHỠ tô đỏ cả tên — thứ người dùng quét mắt tìm đầu tiên. */
.nhatky .n b.nho { color: var(--danger, #E05A4E); }
.nhatky .xt .ic { width: 12px; height: 12px; vertical-align: -2px; }

/* ⭐ M13 (07/08/2026) — NGĂN THÔNG TIN LIÊN HỆ. */
.tt-hs { text-align: center; padding: 4px 0 10px; }
.tt-hs .av { width: 88px; height: 88px; font-size: 32px; margin: 0 auto 10px; }
.tt-hs h2 { margin: 0 0 4px; font-size: 18px; }
/* TẦNG 2 — câu lời thường. Màu theo mức an toàn, nhưng chữ mới là thứ nói
   nghĩa: người mù màu vẫn đọc được đầy đủ. */
.tt-cau { margin: 0; font-size: 12px; color: var(--ink-3); }
.tt-cau.xanh { color: var(--success, #3DBE7B); }
.tt-cau.do   { color: var(--danger, #E05A4E); }
.tt-cau.xam  { color: var(--warning, #E8B64C); }

/* TẦNG 3 — chi tiết kỹ thuật, mặc định gấp lại. */
.lien-ky-thuat { display: block; width: 100%; margin-top: 10px; padding: 8px;
                 border: 0; background: transparent; color: var(--ink-3);
                 font: inherit; font-size: 12px; text-align: left;
                 cursor: pointer; }
.lien-ky-thuat:hover { color: var(--ink); }
.ky-thuat { padding: 6px 10px; border-radius: 8px;
            background: var(--surface-raised); }
.ky-thuat > div { display: flex; align-items: center; gap: 8px;
                  padding: 5px 0; font-size: 12px; }
.ky-thuat span { color: var(--ink-3); flex: none; }
.ky-thuat code { flex: 1; min-width: 0; overflow: hidden;
                 text-overflow: ellipsis; white-space: nowrap;
                 font-size: 11.5px; }

/* ⭐ M14 — MÀN XÁC MINH: ba cụm số cỡ lớn, giãn chữ để đọc qua điện thoại. */
.xm-so { display: flex; justify-content: center; gap: 12px; margin: 18px 0; }
.xm-so b { padding: 10px 16px; border-radius: 10px;
           background: var(--surface-raised);
           border: 1px solid color-mix(in srgb, var(--gold) 50%, transparent);
           color: var(--gold); font-size: 26px; letter-spacing: 3px;
           font-variant-numeric: tabular-nums; }
.xm-nut { display: flex; flex-direction: column; gap: 8px; }
.xm-xong { text-align: center; padding: 10px 0; color: var(--success, #3DBE7B); }
.hint.canh-bao { color: var(--danger, #E05A4E); font-weight: 600; }

/* ⭐ M15 (07/08/2026) — TRUNG TÂM AN TOÀN: danh sách VIỆC, không phải bảng số. */
.at-vong { display: flex; align-items: center; gap: 12px; margin: 6px 0 10px; }
.at-so { font-size: 22px; font-weight: 800; color: var(--gold);
         font-variant-numeric: tabular-nums; }
.at-thanh { flex: 1; height: 8px; border-radius: 999px;
            background: var(--surface-raised); overflow: hidden; }
.at-thanh i { display: block; height: 100%; background: var(--gold);
              transition: width .35s ease; }
.at-ds { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; }
.at-viec { display: flex; align-items: flex-start; gap: 10px; padding: 9px 4px;
           border-top: 1px solid var(--line); }
.at-viec .ic { flex: none; color: var(--ink-3); margin-top: 1px; }
/* Việc ĐÃ XONG dùng xanh — đúng luật màu: xanh chỉ dành cho tín hiệu an toàn. */
.at-viec.xong .ic { color: var(--success, #3DBE7B); }
.at-viec > span:last-child { display: flex; flex-direction: column; min-width: 0; }
.at-viec b { font-size: 13px; font-weight: 600; }
.at-viec span span { font-size: 12px; color: var(--ink-3); }
/* ⭐ ② (08/08/2026) — CÔNG TẮC trong Trung tâm an toàn.
   Hàng công tắc dùng lại khuôn hàng việc để người dùng không phải học kiểu
   danh sách thứ hai. */
.at-ct { margin-top: 10px; border-top: 1px solid var(--line); }
.ct-hang { display: flex; align-items: flex-start; gap: 10px; padding: 10px 4px;
           cursor: pointer; }
.ct-hang > span { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.ct-hang b { font-size: 13px; font-weight: 600; }
.ct-hang span span { font-size: 12px; color: var(--ink-3); line-height: 1.45; }
.ct-hang input[type="checkbox"] { flex: none; width: 18px; height: 18px;
                                  margin-top: 2px; accent-color: var(--gold); }
.at-bn-tat { margin: 0 4px 4px; font-size: 12px; color: var(--warning, #E8B64C); }

/* Đèn khoá trên thanh trên nay là NÚT — cho biết bấm được. */
button.pill { border: 0; font: inherit; cursor: pointer; }
button.pill:hover { filter: brightness(1.15); }

/* ⭐ M03 (07/08/2026) — kết quả tìm người trong hộp "Chat 1:1 mới". */
.tim-kq { max-height: 34vh; overflow-y: auto; margin: 2px -4px 8px; }
.tim-kq:empty { display: none; }
.tim-kq .hang { padding: 7px 6px; }
.tim-kq .av { width: 36px; height: 36px; font-size: 14px; }

/* ⭐ M09 (07/08/2026) — ẢNH TRONG BONG BÓNG.
   Khung có tỉ lệ cố định để dòng tin KHÔNG NHẢY khi ảnh giải mã xong — nhảy
   chỗ giữa lúc đang đọc là lỗi khó chịu hơn cả việc chờ ảnh. */
.noi.anh { display: flex; flex-direction: column; gap: 6px; }
.anh-khung { width: min(260px, 62vw); aspect-ratio: 4 / 3; border-radius: 10px;
             overflow: hidden; background: var(--surface-raised);
             display: grid; place-items: center; }
.anh-khung img { width: 100%; height: 100%; object-fit: cover; display: block; }
.anh-cho { width: 26px; height: 26px; color: var(--ink-3); opacity: .55; }
.noi.anh .tt { font-size: 11.5px; color: var(--ink-3); }

/* ⭐ M06 — nút mic hiện MỜ khi trình duyệt không ghi âm được, ⛔ không ẩn.
   ⛔ VÁ 09/08/2026: thêm `[aria-disabled]`. Nút mic ở ca "origin http" nay BỎ
   thuộc tính `disabled` (để bấm vào còn nói được lý do — `disabled` nuốt hết sự
   kiện chuột, mà trên máy cảm ứng thì tooltip không bao giờ hiện). Không có dòng
   này thì nút hết mờ và trông như bấm được bình thường — tệ hơn cả trước. */
.soan .ibtn[disabled],
.soan .ibtn[aria-disabled="true"] { opacity: .42; cursor: not-allowed; }

/* ═══ ⭐ TIN NHẮN THOẠI (08/08/2026) ═══ */
/* Dải ghi âm — thay chỗ ô soạn trong lúc ghi. */
.gt-dai { flex: none; display: flex; align-items: center; gap: 9px;
          padding: 9px 12px; border-top: 1px solid var(--line);
          background: var(--navy-dark); }
.gt-dai[hidden] { display: none; }
.gt-cham { flex: none; width: 9px; height: 9px; border-radius: 50%;
           background: var(--danger, #E05A4E);
           animation: sv-cham 1.2s ease-in-out infinite; }
.gt-dh { flex: none; font-size: 13px; color: var(--gold-soft);
         font-variant-numeric: tabular-nums; }
.gt-song, .th-song { flex: 1; min-width: 0; display: flex; align-items: center;
                     gap: 2px; height: 24px; overflow: hidden; }
.gt-song i, .th-song i { flex: none; width: 2px; border-radius: 1px;
                         background: var(--gold-soft); opacity: .8; }
@media (prefers-reduced-motion: reduce) { .gt-cham { animation: none } }

/* Bong bóng tin thoại — nút nghe + dạng sóng + đồng hồ + nút Tải về (giữ). */
.noi.thoai { display: flex; align-items: center; gap: 9px; min-width: min(230px, 64vw);
             cursor: pointer; }
.th-nut { flex: none; width: 34px; height: 34px; padding: 8px; border: 0;
          border-radius: 50%; background: var(--gold); color: var(--on-accent);
          cursor: pointer; display: grid; place-items: center; }
.th-nut svg { width: 100%; height: 100%; }
.th-nut.dang { animation: sv-cham 1s ease-in-out infinite; }
.noi.thoai .th-song { height: 22px; }
.noi.thoai.dang-phat .th-song i { opacity: 1; background: var(--gold); }
.th-dh { flex: none; font-size: 11.5px; color: var(--ink-3);
         font-variant-numeric: tabular-nums; }
.noi.thoai audio { display: none; }

/* ═══ ⭐ ① (08/08/2026) — THẺ CUỘC HỌP TRONG DÒNG TIN ═══
   Navy + gold, đúng luật màu; ⛔ không chép màu WhatsApp, ⛔ không emoji.
   Chip trạng thái đọc được cả khi không phân biệt màu nhờ CHỮ (Dự kiến / Đang
   họp / Đã xong / Nhỡ) — màu chỉ là lớp thứ hai. */
.noi.hop { display: flex; flex-direction: column; gap: 7px;
           min-width: min(240px, 68vw); padding: 2px 0; }
.noi.hop .hop-h { display: flex; align-items: center; gap: 7px; }
.noi.hop .hop-h .ic { flex: none; width: 17px; height: 17px; color: var(--gold); }
.noi.hop .hop-tieu { flex: 1; min-width: 0; font-size: 14px; font-weight: 700;
                     overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.noi.hop .hop-chip { flex: none; padding: 2px 8px; border-radius: 999px;
                     font-size: 11.5px; font-weight: 700; letter-spacing: .03em;
                     text-transform: uppercase;
                     background: var(--surface-raised); color: var(--ink-3); }
.noi.hop .hop-dong, .noi.hop .hop-ma {
  display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-3);
}
.noi.hop .hop-dong .ic, .noi.hop .hop-ma .ic { width: 13px; height: 13px; flex: none; }
/* Mã phòng luôn 5 chữ số trần — chữ đều nét để đọc/gõ lại bằng remote EP One. */
.noi.hop .hop-ma b { font-family: ui-monospace, Menlo, monospace; letter-spacing: .12em;
                     color: var(--gold-soft); font-size: 13px; }
.noi.hop .hop-thieu { color: var(--warning, #E8B64C); }
.noi.hop .hop-nut { align-self: flex-start; }

/* DỰ KIẾN — trung tính. ĐANG HỌP — nổi bật gold (nút chính đậm hơn). */
.noi.hop.hop-danghop .hop-chip { background: color-mix(in srgb, var(--gold) 24%, transparent);
                                 color: var(--gold); }
.noi.hop.hop-danghop { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold) 55%, transparent);
                       border-radius: 10px; padding: 8px; }
/* ⭐ 04/09 — `ketthuc` ("Đã kết thúc") dùng CHUNG chip trung tính với
   `daxong`. ⛔ TUYỆT ĐỐI ⛔ KHÔNG cho nó mượn màu đỏ của `.hop-nho`: màu đỏ ở
   thẻ này nghĩa là "⛔ không ai đến" — đúng lời cáo buộc mà bản vá đang gỡ.
   Trung tính là CỐ Ý, ⛔ không phải chưa kịp tô. */
.noi.hop.hop-daxong .hop-chip,
.noi.hop.hop-ketthuc .hop-chip { background: var(--surface-raised); color: var(--ink-3); }
.noi.hop.hop-nho .hop-chip { background: color-mix(in srgb, var(--danger, #E05A4E) 22%, transparent);
                             color: var(--danger, #E05A4E); }
.noi.hop.hop-nho .hop-tieu { color: var(--danger, #E05A4E); }

/* ⭐ ★B (07/08/2026) — THẺ "NHÀ MÌNH" trên màn đăng nhập.

   Đây là câu WhatsApp không bao giờ nói được, và là chỗ "LAN kín" chuyển từ
   hạn chế kỹ thuật thành lợi thế bán hàng. Đặt ở màn đăng nhập vì đó là lúc
   người dùng đang quyết định có tin sản phẩm này hay không. */
.nha-minh { margin-top: 16px; padding: 12px 14px; border-radius: 10px;
            background: color-mix(in srgb, var(--success, #3DBE7B) 8%, transparent);
            border: 1px solid color-mix(in srgb, var(--success, #3DBE7B) 26%, transparent); }
.nm-h { display: flex; align-items: center; gap: 7px; margin-bottom: 6px;
        color: var(--success, #3DBE7B); }
.nm-h b { font-size: 13px; }
.nha-minh p { margin: 0 0 8px; font-size: 12px; line-height: 1.5;
              color: var(--ink-2); }
.nha-minh ul { margin: 0; padding-left: 16px; }
.nha-minh li { font-size: 12px; color: var(--ink-3); line-height: 1.75; }

/* ═══════════ ⭐⭐ BỐ CỤC MÀN GỌI 65 / 35 — 01/09/2026 ═══════════
   CHỈ THỊ ANH TRIỆU: *"màn hình web khi nhận cuộc gọi từ mobile ko cần phải
   hiển thị full trang, nó sẽ mất diện tích để cho menu các nút ấn, hãy đóng
   khung 65% diện tích trang web cho hiển thị hình ảnh thôi, còn lại cho menu
   chức năng khác."*

   ⛔ VÌ SAO PHẢI CHIA CỨNG, ⛔ KHÔNG DÙNG `flex: 1 1 auto` NHƯ CŨ:
   ảnh anh gửi cho thấy khi cuộc gọi nối, hình phủ TRỌN trang và thanh nút biến
   mất hẳn — video mang kích thước tự nhiên 1080p, còn khung hình `flex:1 1 auto`
   thì **⛔ được phép nở theo nội dung**, nên nó đẩy thanh nút xuống ngoài tầm
   nhìn. Chia bằng `grid-template-rows` là **hợp đồng cứng**: hàng hình lấy đúng
   65%, hàng menu lấy đúng 35%, ⛔ nội dung to đến mấy cũng ⛔ KHÔNG giành thêm
   được một điểm ảnh nào. ⇒ Thanh nút ⛔ KHÔNG THỂ biến mất nữa.
   (`minmax(0, …)` là bắt buộc: thiếu nó thì hàng grid vẫn nở theo nội dung —
    đúng cái bẫy `min-height:auto` của flexbox, chỉ đổi tên.) */
.sc-goi-man {
  display: grid;
  /* ⭐ 02/09 (anh Triệu): *"điều chỉnh màn hình hội thoại rộng ra chiếm 85%
     diện tích webrtc windows"* — trước là 65/35.
     ⛔ NHƯNG hàng dưới KHÔNG dùng `minmax(0, 15fr)`: 15% của màn thấp là quá
     hẹp cho BA dòng (trạng thái · dải đo · hàng nút) ⇒ nút lại bị bóp/khuất,
     đúng lỗi "mất menu nút" đã chữa hôm qua.
     ⇒ Sàn của hàng dưới là `min-content` — nó **không bao giờ nhỏ hơn nội dung
       thật**, và chỉ khi màn đủ cao thì mới đúng 15%. Khung hình vẫn lấy phần
       còn lại, tức 85% ở mọi màn bình thường. */
  grid-template-rows: minmax(0, 85fr) minmax(min-content, 15fr);
}
.sc-goi-man .sc-goi-hinh {
  min-height: 0;          /* ⛔ thiếu dòng này thì hàng grid lại nở theo video */
  overflow: hidden;
  position: relative;     /* mốc neo cho ô hình nhỏ */
}
.sc-goi-man .sc-goi-thanh {
  min-height: 0;
  overflow-y: auto;       /* menu dài thì cuộn TRONG phần của nó */
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
}
/* Ô hình của chính mình: 35% dưới đã là nền tối nên ô nhỏ chỉ cần vừa phải. */
.sc-goi-man .sc-goi-hinh #scVidMinh { width: min(24%, 200px); right: 12px; bottom: 12px; }

/* ─── DẢI ĐO SỐNG (đặt trong 35% dưới) ───
   Trả lời tại chỗ câu "web có gửi hình đi không" — thứ đang phải tranh cãi
   giữa phiên Web và phiên Mobile. ⛔ Không bắt ai mở bàn điều khiển. */
.sc-goi-do {
  display: flex; align-items: center; justify-content: center;
  gap: 8px; flex-wrap: wrap; font-size: 12px;
}
.sc-goi-do .sc-do-o {
  padding: 3px 10px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted);
  font-variant-numeric: tabular-nums; letter-spacing: .02em;
}
.sc-goi-do .sc-do-o.ok { color: var(--gold-soft); border-color: var(--gold-soft); }
.sc-goi-do .sc-do-o.tat { color: #ff8a8a; border-color: #ff8a8a; }

/* Nút ĐANG TẮT phải đỏ — nhìn là biết, ⛔ không phải đoán qua biểu tượng. */
.sc-goi-nut .ib.dang-tat,
.sc-thanh-goi .ib.dang-tat { background: rgba(255,90,90,.22); color: #ff8a8a; }
.sc-goi-nut .ib:disabled { opacity: .5; cursor: progress; }

/* ⭐ 01/09 — ICON KHOÁ E2EE thay cho dòng chữ "Chat bảo mật đầu-cuối" trên
   thanh trên. Cỡ bằng các nút bên phải để thanh trên nhìn cân, ⛔ không để nó
   to lấn như một nhãn. */
.sc-e2ee-dau { display: inline-flex; align-items: center; flex: 0 0 auto; }
.sc-e2ee-dau svg { width: 22px; height: 22px; display: block; }

/* ⭐ 01/09 — THANH TRÊN CHỈ CÒN ICON (chỉ thị anh Triệu: *"xoá hết chữ trên
   thanh này đi, chỉ để icon gần nhau"*). Các nút đứng sát nhau thành một cụm. */
.sc-den-khoa.ok   { color: var(--ok, #6fdb9f); }
.sc-den-khoa.warn { color: var(--gold-soft, #fcc537); }
.sc-den-khoa svg  { width: 20px; height: 20px; display: block; }

/* Tên tài khoản thu thành MỘT viên chữ cái đầu — ⛔ không bỏ hẳn: người dùng
   phải biết mình đang đăng nhập bằng ai (án "bốn tài khoản lẫn nhau" 01/09).
   Rê chuột ra tên đầy đủ. */
.sc-toi-vien {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%; flex: 0 0 auto;
  background: var(--gold-soft, #fcc537); color: var(--navy-deep, #101a2b);
  font-size: 12px; font-weight: 700; line-height: 1; cursor: default;
}
.sc-ve-nha { display: inline-flex; align-items: center; justify-content: center; }

/* ⭐ 02/09 — Ô CHÚ THÍCH NỔI, gắn thẳng vào <body> nên ⛔ KHÔNG khung cha nào
   cắt được (chú thích cũ vẽ bằng ::after bên trong nút, bị `overflow:hidden`
   của thanh trên và `overflow:auto` của cột danh sách xén mất). */
.sv-chuthich {
  position: fixed; z-index: 9999; display: none;
  max-width: min(280px, 90vw);
  padding: 6px 10px; border-radius: 8px;
  background: var(--navy-dark, #16233c); color: var(--ink-1, #e8eef7);
  border: 1px solid var(--line, #2b3a55);
  font-size: 12px; line-height: 1.35; white-space: normal;
  box-shadow: 0 6px 20px rgba(0,0,0,.45);
  pointer-events: none;          /* ⛔ không chắn chuột khỏi nút bên dưới */
}

/* ⭐ 02/09 — MENU "+" Ở Ô SOẠN. Dùng chung khuôn `.sv-menu` sẵn có, chỉ thêm
   vị trí nổi (đặt trên nút, ⛔ không bị khung cha cắt vì gắn thẳng vào body). */
.sv-menu.sv-menu-them { position: fixed; z-index: 9998; min-width: 220px; }
.sv-menu.sv-menu-them button { display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left; }
.sv-menu.sv-menu-them button .ic { flex: 0 0 auto; display: inline-flex; }
.sv-menu.sv-menu-them button .ic svg { width: 20px; height: 20px; display: block; }

/* ⭐ 02/09 — BẢNG CẢM XÚC trong hộp thoại menu "+". */
.bang-cx { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px;
           padding: 10px 2px; }
.bang-cx .cx-o { font-size: 26px; line-height: 1; padding: 8px 0;
                 border: 1px solid transparent; border-radius: 10px;
                 background: var(--surface-raised); cursor: pointer; }
.bang-cx .cx-o:hover { border-color: var(--gold); background: color-mix(in srgb, var(--gold) 14%, transparent); }
@media (max-width: 420px) { .bang-cx { grid-template-columns: repeat(4, 1fr); } }

/* Tin chỉ có emoji thì hiện to — dấu hiệu do bên gửi đặt (`asia.starview.emoji_lon`). */
.tin .emoji-lon { font-size: 40px; line-height: 1.2; }

/* ⭐ 02/09 — BÌNH CHỌN & VỊ TRÍ trong bong bóng tin. */
.noi.poll { display: flex; flex-direction: column; gap: 6px; min-width: 240px; }
.noi.poll .poll-h { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }
.noi.poll .poll-h svg { width: 18px; height: 18px; }
.noi.poll .poll-o { position: relative; overflow: hidden; display: flex;
  align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 8px 10px; border-radius: 10px; border: 1px solid var(--line);
  background: var(--surface-raised); color: inherit; font: inherit; cursor: pointer; }
.noi.poll .poll-o.chon { border-color: var(--gold); }
.noi.poll .poll-thanh { position: absolute; left: 0; top: 0; bottom: 0;
  background: color-mix(in srgb, var(--gold) 22%, transparent); transition: width .25s; }
.noi.poll .poll-chu, .noi.poll .poll-so { position: relative; }
.noi.poll .poll-chu { flex: 1 1 auto; min-width: 0; }
.noi.poll .poll-so { font-variant-numeric: tabular-nums; opacity: .85; }
.noi.poll .poll-f { font-size: 11px; color: var(--ink-3); }
.noi.vitri { display: flex; align-items: center; gap: 8px; }
.noi.vitri svg { width: 18px; height: 18px; }

/* ⭐ 02/09 — ẢNH HIỆN NGAY TRONG KHUNG CHAT (trước chỉ có ô giữ chỗ). */
.noi.anh .anh-khung img { display: block; width: 100%; height: auto;
  max-height: 420px; object-fit: contain; border-radius: 10px; cursor: zoom-in;
  background: #0d1526; }
.noi.anh .anh-khung { min-height: 0; }

/* ⭐ 02/09 — hàng công tắc "Nhóm kiểm soát" trong hộp Tạo nhóm. */
.ks-hang { display: flex; align-items: flex-start; gap: 10px; margin: 12px 0 4px; }
.ks-hang input { margin-top: 3px; flex: 0 0 auto; }
.ks-hang .hint { font-size: 12px; line-height: 1.45; }


/* ═══════════════════════════════════════════════════════════════════════════
   ⭐ 02/09/2026 — HAI PHÉP XOÁ TIN (xem `SoAnTin` trong chat-app.js)
   ⛔ VÌ SAO CẦN CÂU PHỤ: hai mục menu cùng bắt đầu bằng chữ "Xoá" mà hậu quả
   khác hẳn nhau — một cái chỉ mất trên máy mình, một cái mất trên máy MỌI
   NGƯỜI. Nhãn suông là bẫy; câu phụ là thứ ngăn người dùng bấm nhầm.
   ═══════════════════════════════════════════════════════════════════════════ */
.sv-menu button > span { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.sv-menu .sub2 {
  font-size: 11px;
  line-height: 1.35;
  opacity: .68;
  font-weight: 400;
  white-space: normal;          /* ⛔ cho xuống dòng — câu phụ dài hơn nhãn */
  max-width: 236px;
}

/* Dải báo có nút HOÀN TÁC — lối thoát thứ nhất sau khi ẩn tin.
   ⛔ Nút phải nằm CÙNG HÀNG và nổi rõ: nó chỉ sống 6 giây, người dùng ⛔ không
   có thời gian đi tìm. */
.toast.has-undo { display: flex; align-items: center; gap: 10px; }
.toast.has-undo .undo {
  margin-left: auto;
  background: none;
  border: 1px solid currentColor;
  border-radius: 6px;
  padding: 3px 10px;
  font-size: 12px;
  font-weight: 600;
  color: inherit;
  cursor: pointer;
  flex: 0 0 auto;
}
.toast.has-undo .undo:hover { background: rgba(255,255,255,.12); }

/* Dải "Đang ẩn N tin — Hiện lại hết" — lối thoát thứ hai, sống mãi.
   ⛔ CHỈ hiện khi N > 0 (xem `daiDangAn`), nên ⛔ không cần lo nó thành rác. */
.tin-dinh.dang-an {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  opacity: .9;
}
.tin-dinh.dang-an .btn { margin-left: auto; flex: 0 0 auto; }

/* ═══════════ ⭐ 04/09/2026 — BẢNG EMOJI ĐẦY ĐỦ (sticker + biểu cảm) ═══════════

   Anh Triệu: *"sticker trên web cũng quá tệ quá ít tham khảo mobile"* + ảnh
   bảng emoji của app: *"lấy bộ này và nhiều hơn nếu có"*.

   ⛔⛔ BA LUẬT BỐ CỤC — hôm nay anh ấy ĐÃ bắt web một lỗi "ảnh đè lên chữ" mà
     bàn đo khi ấy xanh 15/15 vì nó kiểm CẤU TRÚC HTML chứ ⛔ không kiểm CSS
     thật sự tính ra. Nên ba luật dưới đây phải đo bằng `getComputedStyle` /
     `scrollWidth`, ⛔ không phải đọc chuỗi HTML:
     ① CUỘN DỌC nằm TRONG bảng (`.em-cuon`), ⛔ không đẩy bảng dài ra ngoài màn.
     ② ⛔ KHÔNG TRÀN NGANG ở mọi bề rộng — đo ở 520/360/320/280.
     ③ ⛔ KHÔNG GHIM SỐ CỘT. App đặt bề rộng TỐI ĐA mỗi ô (46px emoji / 60px
        sticker) rồi để số cột tự đổi; đếm "7 cột" trên ảnh chụp là đúng với
        RIÊNG màn ấy. `auto-fill + minmax` đo bề rộng THẬT CỦA KHỐI CHỨA nên
        cũng thoát bẫy `@media` đo VIEWPORT (bài học ⑨ M18: bảng này còn mở
        trong tab công cụ rộng ~360 điểm của một cửa sổ 1920).

   ⛔⛔ MỌI LUẬT DƯỚI ĐÂY PHẢI CÓ HAI LỚP CHỌN (`.em-bang .em-o`…): `.sv-menu
     button` là (0,1,1) — mạnh hơn `.em-o` (0,1,0) — nên viết một lớp là bị
     khuôn nút của menu đè `display/padding/width/font-size`, ⛔ không có lỗi
     nào báo, chỉ ra một bảng vỡ. */

/* Menu nổi khi mở bảng đầy đủ: `.cx-bang` gốc là `display:flex` cho hàng 6 cái —
   ⛔ giữ nguyên nó, chỉ chồng thêm biến thể LỚN. */
.sv-menu.cx-bang-lon { display: block; padding: 6px; overflow: hidden;
  width: min(336px, calc(100vw - 12px)); max-width: calc(100vw - 12px); }

.em-bang { display: flex; flex-direction: column; width: 100%;
  min-width: 0; max-width: 100%;
  max-height: min(420px, calc(100dvh - 28px));
  /* ⛔⛔ NỀN CỦA TIÊU ĐỀ DÍNH ⛔ KHÔNG ĐƯỢC GHI CỨNG. Bảng này sống ở HAI chỗ
     có nền KHÁC NHAU: menu nổi dùng `--surface-raised`, còn `.card` của hộp
     thoại dùng `--surface`. Ghi cứng một màu là tiêu đề dính lộ một DẢI MÀU
     LẠ trôi trên lưới — đúng lớp lỗi "ảnh đè lên chữ" anh Triệu bắt hôm nay,
     và đúng lý do phải đo `getComputedStyle` chứ ⛔ không đọc chuỗi HTML.
     ⭐ Đo được: nền tiêu đề rgb(41,64,95) vs nền thẻ rgb(31,48,80) — LỆCH. */
  --em-nen: var(--surface-raised); }
#uiModalCard .em-bang { max-height: min(56dvh, 430px); margin-top: 4px;
  --em-nen: var(--surface); }

/* HÀNG NHANH — sáu cảm xúc cũ, vẫn vẽ bằng nét. */
.em-bang .em-nhanh, .cx-bang-lon .em-nhanh { display: flex; flex-wrap: wrap; gap: 2px;
  padding-bottom: 5px; margin-bottom: 4px; border-bottom: 1px solid var(--line); }
/* ⛔⛔ 04/09 — LỖI NÀY CHỈ ẢNH CHỤP MỚI BẮT ĐƯỢC, ⛔ không phép đo cấu trúc nào
   thấy: sáu nút cảm xúc XẾP DỌC THÀNH CỘT. Gốc là ĐỘ ƯU TIÊN CSS —
   `.sv-menu button` là (0,1,1), MẠNH HƠN `.cx-chon` (0,1,0), nên nó áp
   `width:100%` lên từng nút. Bảng cũ thoát nạn chỉ vì `.cx-bang` là
   `display:flex` (nút thành mục co giãn); bảng mới đổi sang `display:block`
   nên luật cũ lộ nguyên hình.
   ⇒ Đây đúng lớp lỗi anh Triệu bắt hôm nay ("ảnh đè lên chữ") mà bàn đo khi ấy
     xanh 15/15 vì nó kiểm CẤU TRÚC HTML. Ba lớp chọn (0,3,0) mới đủ thắng. */
.sv-menu .em-nhanh .cx-chon, .em-bang .em-nhanh .cx-chon {
  display: grid; place-items: center; width: 34px; height: 34px; padding: 6px;
  margin: 0; border: 0; border-radius: 8px; background: none;
  font: inherit; text-align: center; cursor: pointer; }
.sv-menu .em-nhanh .cx-chon:hover, .em-bang .em-nhanh .cx-chon:hover {
  background: color-mix(in srgb, var(--gold) 18%, transparent); }
.sv-menu .em-nhanh .cx-chon .cx, .em-bang .em-nhanh .cx-chon .cx {
  width: 20px; height: 20px; }

/* TAB NHÓM — cuộn NGANG, ⛔ không xuống dòng (xuống dòng là bảng cao thêm và
   đẩy vùng cuộn ra ngoài màn ở máy thấp). */
.em-bang .em-tab { display: flex; gap: 2px; flex: 0 0 auto; min-width: 0;
  overflow-x: auto; overflow-y: hidden; scrollbar-width: thin;
  padding-bottom: 2px; border-bottom: 1px solid var(--line); }
.em-bang .em-tab-o { flex: 0 0 auto; display: grid; place-items: center;
  width: 34px; height: 30px; padding: 0; margin: 0;
  border: 0; border-bottom: 2px solid transparent; border-radius: 7px 7px 0 0;
  background: none; color: var(--ink-2); font: inherit; font-size: 17px;
  line-height: 1; text-align: center; cursor: pointer; }
.em-bang .em-tab-o:hover { background: color-mix(in srgb, var(--gold) 16%, transparent); }
.em-bang .em-tab-o.on { border-bottom-color: var(--gold); color: var(--ink); }
.em-bang .em-tab-o .ic { display: inline-flex; width: 17px; height: 17px; }
.em-bang .em-tab-o .ic svg { width: 100%; height: 100%; }

/* Ô TÌM — ⛔ bảng vài trăm emoji mà ⛔ không có ô tìm thì ⛔ không ai dùng nổi. */
.em-bang .em-tim { display: flex; align-items: center; gap: 6px; flex: 0 0 auto;
  min-width: 0; margin: 6px 0 4px; padding: 0 8px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--surface); }
.em-bang .em-tim .ic { flex: 0 0 auto; display: inline-flex; width: 15px; height: 15px;
  color: var(--ink-3); }
.em-bang .em-tim .ic svg { width: 100%; height: 100%; }
.em-bang .em-tim-o { flex: 1 1 auto; min-width: 0; width: 100%;
  border: 0; background: none; color: var(--ink); font: inherit; font-size: 13px;
  padding: 7px 0; outline: none; }

/* VÙNG CUỘN — luật ①. `min-height:0` là bắt buộc: thiếu nó thì khối co giãn
   trong flex ⛔ KHÔNG chịu nhỏ hơn nội dung và bảng tràn ra ngoài màn. */
/* ⛔⛔ `min-height` PHẢI CÓ SỐ. Để `auto` (mặc định của mục flex) thì khối
   ⛔ KHÔNG chịu co nhỏ hơn nội dung ⇒ bảng tràn khỏi màn. ⭐ Nhưng đặt `0` thì
   nó co được tới TẬN 0: đo được trên cửa sổ cao 560, hộp thoại chỉ còn ~35px
   lưới — người dùng thấy một khe hở chứ ⛔ không thấy bảng emoji nào.
   ⇒ Số dương là ĐÁY: co được (đủ để ⛔ không tràn) mà ⛔ không co tới mức vô
     nghĩa. ~96px ≈ hai hàng emoji. */
.em-bang .em-cuon { flex: 1 1 auto; min-height: 96px; min-width: 0;
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; }
.em-bang .em-nhom { min-width: 0; }
.em-bang .em-tieu { position: sticky; top: 0; z-index: 1; margin: 0;
  padding: 6px 4px 4px; font-size: 11.5px; font-weight: 600; color: var(--ink-3);
  background: var(--em-nen, var(--surface-raised));
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* LƯỚI — luật ③. ⛔ KHÔNG `repeat(7, …)`, ⛔ KHÔNG `@media`. */
.em-bang .em-luoi { display: grid; gap: 1px; min-width: 0; padding-bottom: 6px;
  grid-template-columns: repeat(auto-fill, minmax(46px, 1fr)); }
.em-bang[data-che="sticker"] .em-luoi {
  grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); }

.em-bang .em-o { display: grid; place-items: center; min-width: 0; width: 100%;
  aspect-ratio: 1 / 1; padding: 0; margin: 0; border: 0; border-radius: 8px;
  background: none; color: inherit; font: inherit; line-height: 1;
  font-size: 22px; text-align: center; cursor: pointer; overflow: hidden; }
.em-bang[data-che="sticker"] .em-o { font-size: 27px; }
.em-bang .em-o > span { pointer-events: none; }
.em-bang .em-o:hover,
.em-bang .em-o:focus-visible { background: color-mix(in srgb, var(--gold) 20%, transparent); }
.em-bang .em-khong { padding: 16px 6px; text-align: center; font-size: 12px;
  color: var(--ink-3); }
.em-bang .em-chan { flex: 0 0 auto; margin-top: 4px; padding: 5px 4px 1px;
  border-top: 1px solid var(--line); font-size: 11px; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Máy thấp (cửa sổ ngắn) — nhường chỗ cho hàng nhanh + tab + ô tìm.
   ⛔ TỪNG DÙNG `46dvh` Ở ĐÂY VÀ ĐÓ LÀ SỐ SAI: trên cửa sổ cao 560 nó ra 258px,
     trừ tab + ô tìm + chân bảng thì lưới chỉ còn ~35px. Trừ theo CHIỀU CAO
     CỐ ĐỊNH của thẻ (tiêu đề + nút + đệm ≈ 230px) mới là phép đúng — phần
     trăm ⛔ không biết mấy thứ kia chiếm bao nhiêu. */
@media (max-height: 560px) {
  .em-bang { max-height: calc(100dvh - 20px); }
  #uiModalCard .em-bang { max-height: calc(100dvh - 230px); }
}

/* ═══════ ⭐ 05/09 — BẢNG QUẢN TRỊ PHÒNG (khuôn WhatsApp) ═══════
   Lệnh anh Triệu: *"các tính năng whatsapp có mà starmeet chưa có thì cũng
   phải làm nút giả 'note' ra để cần nhắc code hay ko khi tôi xem trực tiếp"*.

   ⛔⛔ MỤC NOTE PHẢI NHÌN RA NGAY LÀ CHƯA CÓ. Đây là chỗ ranh giới giữa
   "bản kiểm kê" và "nút giả" — nếu nó trông giống nút thật thì ta vừa dựng
   đúng con bọ đang đi diệt cả ngày.
   ⇒ Ba dấu hiệu CÙNG LÚC, ⛔ đừng bỏ bớt cái nào:
     ① mờ hơn hẳn   ② con trỏ báo ⛔ không bấm được   ③ nhãn chữ "chưa làm"
   ⛔ Chỉ làm mờ là ⛔ KHÔNG ĐỦ — mờ đọc thành "đang tắt, bật lên là chạy". */
.sv-menu-phong { min-width: 268px; max-height: 78vh; overflow-y: auto; }
.sv-menu-phu { font-weight: 400; opacity: .85; border-bottom: 0;
  padding-bottom: 2px; }
/* Tiêu đề khu — ⛔ KHÔNG phải nút, ⛔ đừng cho nó nhận con trỏ bấm. */
.sv-menu-khu { font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); padding: 9px 8px 3px; margin-top: 2px;
  border-top: 1px solid var(--line); user-select: none; }
.sv-menu-phong .sv-menu-khu:first-of-type { border-top: 0; }

/* ⛔ `opacity: .62` chứ ⛔ KHÔNG phải .5 như nút disabled thường: đo tương phản
   05/09 cho thấy dưới ngưỡng WCAG 3:1 là người dùng ⛔ không đọc ra đây là một
   MỤC. Mục note phải ĐỌC ĐƯỢC — nó tồn tại để anh Triệu duyệt, ⛔ không phải để
   giấu. Mờ vừa đủ để phân biệt, ⛔ không mờ tới mức thành nhiễu. */
.sv-menu button.sv-note:disabled { opacity: .62; cursor: not-allowed; }
.sv-note-nhan { margin-left: auto; font-size: 9.5px; letter-spacing: .04em;
  text-transform: uppercase; padding: 2px 6px; border-radius: 999px;
  border: 1px dashed var(--ink-3); color: var(--ink-3); white-space: nowrap; }

/* Việc ⛔ KHÔNG LÙI ĐƯỢC — màu cảnh báo, và đặt cuối bảng. */
.sv-menu button.sv-nguyhiem { color: var(--danger, #E5534B); }
.sv-menu button.sv-nguyhiem:hover {
  background: color-mix(in srgb, var(--danger, #E5534B) 16%, transparent); }

/* ═══ Danh sách thành viên (05/09, đợt 1) ═══
   ⛔ Có `max-height` + cuộn: nhóm tới 1024 người (trần WhatsApp) — ⛔ không
   giới hạn chiều cao là hộp thoại dài quá màn và nút Đóng trôi khỏi tầm mắt. */
.tv-ds { max-height: 46vh; overflow-y: auto; margin: 6px 0;
  border: 1px solid var(--line); border-radius: 8px; }
.tv-hang { display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  border-bottom: 1px solid var(--line); }
.tv-hang:last-child { border-bottom: 0; }
.tv-ten { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.tv-id { font-size: 11px; color: var(--ink-3); overflow-wrap: anywhere; }
.tv-toi { font-size: 11px; color: var(--ink-3); }
.tv-chip { flex: 0 0 auto; font-size: 10px; letter-spacing: .04em;
  text-transform: uppercase; padding: 2px 7px; border-radius: 999px;
  border: 1px solid var(--gold); color: var(--gold); white-space: nowrap; }
.tv-chip.tv-moi { border-color: var(--ink-3); color: var(--ink-3); }

/* Nút phong/giáng trong danh sách thành viên (05/09, đợt 2) */
.tv-nut { flex: 0 0 auto; font-size: 11px; padding: 4px 9px; border-radius: 7px;
  border: 1px solid var(--line); background: none; color: var(--ink-2);
  cursor: pointer; white-space: nowrap; }
.tv-nut:hover { border-color: var(--gold); color: var(--gold); }
.tv-nut:disabled { opacity: .5; cursor: not-allowed; }
/* Hàng công tắc trong hộp Quyền nhóm — chữ bên trái, công tắc bên phải. */
.fl-cong { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; }
.fl-cong > span { display: flex; flex-direction: column; gap: 2px;
  align-items: flex-start; text-align: left; }
.fl-cong input[type="checkbox"] { flex: 0 0 auto; width: 18px; height: 18px; }

/* ═══ KÉO TỆP THẢ VÀO KHUNG CHAT (05/09, lệnh anh Triệu) ═══
   ⛔ Khung phủ dùng `::after` + `pointer-events: none` — ⛔ BẮT BUỘC:
   một lớp phủ NHẬN chuột sẽ nuốt luôn sự kiện `drop` của vùng bên dưới, tức
   khung báo "thả vào đây" rồi chính nó chặn việc thả. Nút tự phá chính mình. */
.hoi { position: relative; }
.hoi.dang-keo::after {
  content: attr(data-keo-chu);
  position: absolute; inset: 10px; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  text-align: center; padding: 20px;
  font-size: 15px; font-weight: 600; color: var(--gold);
  background: color-mix(in srgb, var(--surface-raised) 92%, transparent);
  border: 2px dashed var(--gold); border-radius: 14px;
  pointer-events: none;
}

/* ═══ ⭐ 06/09/2026 — HỒ SƠ NGƯỜI DÙNG (hộp "Xem hồ sơ" · "Hồ sơ của tôi") ═══
   ⛔ CHỈ dựng kiểu cho phần ĐỌC (hàng nhãn — giá trị). Phần NHẬP dùng lại
   `.fl` đã có ở trên (dòng 558) — ⛔ đừng dựng bộ ô nhập thứ hai: hai bộ là
   chắc chắn lệch nhau khi ai đó sửa một bên.
   ⛔ Câu VẮNG (`.hs-vang`) phải đọc ra là CHỮ GIẢI THÍCH chứ ⛔ không phải một
   giá trị: nghiêng + mờ. Bốn câu vắng khác nghĩa hẳn nhau, nên chúng KHÔNG
   được trông giống dữ liệu thật. */
.hs-khoi { display: flex; flex-direction: column; margin: 12px 0 4px; }
.hs-hang { display: flex; gap: 10px; align-items: baseline; padding: 8px 0;
  border-bottom: 1px solid var(--line); text-align: left; }
.hs-hang:last-child { border-bottom: 0; }
/* ⛔⛔ VÁ 09/09 — ĐƯỜNG KẺ BỊ ĐỨT QUÃNG. Khối hồ sơ nay có BA khối con nối
   nhau; luật `:last-child` ở trên bỏ đường kẻ cho hàng cuối của TỪNG khối con
   ⇒ giữa danh sách hiện hai chỗ khuyết, trông như bảng bị vỡ. Chỉ hàng CUỐI
   CÙNG THẬT SỰ mới được bỏ kẻ. */
.hs-khoi > .hs-hang,
.hs-khoi > div > .hs-hang { border-bottom: 1px solid var(--line); }
.hs-khoi > :last-child > .hs-hang:last-child,
.hs-khoi > .hs-hang:last-child { border-bottom: 0; }

/* ⭐ HÀNG NÚT CỦA HỘP HỒ SƠ (lệnh anh Triệu 09/09 — *"box này tôi thấy ko
   đẹp"*). Trước bản vá, ba nút của hộp có BA hình dạng vì mỗi cái ở một chỗ.
   ⛔ `#mBanBeNut` phải `display: contents` — nó chỉ là cái BỌC để mã có chỗ
     ghi vào, ⛔ KHÔNG được thành một ô riêng của hàng.
   ⛔ `flex: 1 1 0` chứ ⛔ KHÔNG `auto`: với `auto` bề rộng chạy theo ĐỘ DÀI
     CHỮ nên hàng nút trông xếp ngẫu nhiên. */
.hs-nut { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 2px; }
.hs-nut > span { display: contents; }
.hs-nut .btn { flex: 1 1 0; min-width: 118px; }
.hs-nut:empty { display: none; }

/* Mũi gấp của "Chi tiết kỹ thuật" — chữ xám lửng lơ ⛔ không cho biết đó là
   nút bấm được. */
.lien-ky-thuat { display: flex; align-items: center; gap: 7px; }
.lkt-mui { flex: none; width: 0; height: 0; border-left: 5px solid currentColor;
  border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform var(--fast, .15s); }
.lien-ky-thuat[aria-expanded="true"] .lkt-mui { transform: rotate(90deg); }
.hs-nhan { flex: 0 0 40%; max-width: 168px; font-size: 12.5px; color: var(--ink-3); }
.hs-gt { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; color: var(--ink); }
.hs-vang { flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-style: italic;
  color: var(--ink-3); }
/* Dòng phụ CHỈ đi kèm câu "Chưa lấy được" — nó nói về MÁY CHỦ, không về người
   kia, nên để chữ thẳng cho khỏi lẫn với ba câu còn lại. */
.hs-vang-gh { display: block; margin-top: 3px; font-style: normal; font-size: 11.5px;
  color: var(--ink-3); opacity: .85; }
.hs-cho { flex: 1 1 auto; font-size: 12.5px; color: var(--ink-3); opacity: .7; }
@media (max-width: 420px) {
  .hs-hang { flex-direction: column; gap: 2px; }
  .hs-nhan { flex: none; max-width: none; }
}
