/* gemcare dashboard — LAYOUT and the few pieces the GemBoss kit has no primitive for.
 *
 * Colours, type, radii, shadows and the shared primitives (.ui-btn, .ui-card, .ui-tabs, .ui-table,
 * .ui-empty, .ui-textarea) come from gemboss-ui.css, which inbox.html loads FIRST. That
 * file is generated by gemboss and vendored (scripts/gemboss-ui.ts); never edit it, and never restyle
 * a kit class here — one owner per component. Every value below is a --gemboss-* / --ui-* token or on
 * the gemboss spacing/radius scale; `npm run ui:gates` holds both.
 *
 * @gemboss/ui 0.5.0 (03/10/2026, Chris: "giống hệt admin"): chữ nền 13px/20px sans, điều khiển 32px dưới chuột và 44px
 * ở ≤767px / dưới ngón tay (kể cả `ui-btn--sm`), focus iris, nút đỏ, badge có tông, popup và confirm đều do gói vẽ.
 * File này từng đặt `--ui-control-h: 44px` cho MỌI con trỏ, body serif 14px/1.45, một ô nhập `.field` riêng và chừng
 * chục luật `min-height: 44px` lên `.ui-btn`: đã gỡ, vì luật của trang thắng luật nền của gói và giữ gemcare khác admin.
 */
html, body { height: 100%; margin: 0; }
body { background: var(--gemboss-canvas); }
[hidden] { display: none !important; }
button, input, select, textarea { font: inherit; color: inherit; }
/* Not textarea: the kit's .ui-textarea owns its focus state (border in --ui-accent); a ring on top drew a double border. */
button:focus-visible, select:focus-visible, input:focus-visible, summary:focus-visible {
  outline: 2px solid var(--ui-focus); outline-offset: 2px;
}

/* Ô nhập là `gemboss-field` (chữ), `ui-select` (chọn), `ui-textarea` (nhiều dòng) của gói: cùng viền, hover, focus iris và
   chiều cao với admin. `.field` còn lại chỉ là móc BỐ CỤC (rộng hết cột ở vài chỗ dưới đây), không vẽ gì. Tới 03/10/2026
   nó là một ô riêng 44px, viền #e5e7ea, focus bằng box-shadow: 42 chỗ, 18 là <select>. */
.field { max-width: 100%; }
.link {
  background: none; border: 0; padding: 0; min-height: 24px; cursor: pointer;
  color: var(--ui-text-muted); text-decoration: underline;
}
.link:hover { color: var(--ui-text); }
.muted { color: var(--ui-text-muted); margin: 4px 0 0; font-size: var(--gemboss-fs-caption); }

.brand { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: var(--gemboss-fs-body-lg); letter-spacing: -.01em; color: var(--gemboss-text-strong); }
/* Dấu hiệu GemBoss thật (SVG vendored). `display: block` chứ không để mặc định inline: một <img>
   inline ngồi trên đường cơ sở chữ và kéo theo 4px khoảng trắng dưới đáy — ở rail 64px, 4px đó
   đủ để hàng icon đầu tiên lệch khỏi lưới. Bo góc do CHÍNH file SVG vẽ (rect rx=7), không bo
   lại ở đây: bo hai lần thì góc gặm vào nét vẽ. */
.brand-mark { display: block; width: 28px; height: 28px; }
/* ── Login ──
   Màn đăng nhập là `.gb-auth` của gemboss-ui.css (auth.css): khung, thẻ, bậc co theo chiều cao, cột
   thương hiệu từ 900×520, lớp tối. Ở đây CHỈ còn phần của gemcare, không đè luật nào của gói:
   (1) cột XANH LÁ bằng token `--gb-panel-*`. `#login` (1,1,0) thắng cả khối tối của auth.css
       (`:root:not([data-theme="light"]) .gb-auth`), nên cột giữ màu xanh ở cả hai chế độ.
       Màu từ `--gemboss-success` #007f5f, đậm dần theo đúng sắc đó để chữ trắng qua AA mọi chỗ.
   (2) dấu ngoặc kép lớn sau câu trích, vẽ bằng ::before để trình đọc màn hình không đọc dấu câu;
   (3) nút sang Help centre.
   Viên gem trong thẻ là `.gb-auth-mark img` của gói (gemboss #3497), không cỡ riêng ở đây: `.brand-mark`
   bên trên vẫn chỉ là 28px của thanh rail. */
#login.gb-auth {
  --gb-panel-bg:
    radial-gradient(880px 620px at 22% 8%, rgba(255, 255, 255, .16), transparent 62%),
    linear-gradient(152deg, #00795a 0%, #045c46 52%, #063f32 100%);
  --gb-panel-ink: #ffffff;
  --gb-panel-muted: rgba(255, 255, 255, .78);
  --gb-panel-eyebrow: rgba(255, 255, 255, .8);
}
.care-quote { position: relative; }
.care-quote::before { content: '\201C'; position: absolute; top: -.36em; left: -.34em;
  font-size: 3.2em; line-height: 1; color: rgba(255, 255, 255, .13); pointer-events: none; }
.care-panel-cta { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; margin-top: 32px;
  padding: 0 20px; border-radius: var(--gemboss-radius-sm); background: #ffffff; color: #063f32;
  font-size: var(--gemboss-fs-body); font-weight: 600; text-decoration: none;
  transition: transform .15s ease; }
.care-panel-cta:hover { transform: translateY(-1px); }
.care-panel-cta:focus-visible { outline: 2px solid #ffffff; outline-offset: 3px; }

/* ── Layout ── */
/* Cột 1 là một RAIL BIỂU TƯỢNG 64px, kiểu Intercom: các màn hình nằm đó, còn bộ lọc của hộp thư nằm ở
   đầu danh sách. Cột phải rộng hơn trước (320px) vì nay nó mang hai tab. */
/* Bề rộng hai cột là BIẾN: người trực kéo được (thanh `.ui-resizer` ở hai mép cột giữa — inbox.js "Kéo giãn cột").
   `--*-w-user` là thứ JS đặt (đã kẹp theo khung); `--details-w` vẫn là biến luật "chưa mở hội thoại ⇒ 0" bên dưới
   ghi đè — JS không bao giờ đặt nó trực tiếp, vì một style inline sẽ thắng luật đó và rãnh 320px trắng quay lại. */
.app {
  --list-w: var(--list-w-user, 340px); --details-w: var(--details-w-user, 320px);
  height: 100%; display: grid; grid-template-columns: 64px var(--list-w) minmax(0, 1fr) var(--details-w);
}
/* Chưa chọn hội thoại ⇒ rãnh cột phải phải bằng 0, chứ không phải "ẩn phần tử đi".
 *
 * `#details` vào trang với `hidden`, tức `display: none` — nhưng rãnh 320px của nó VẪN nằm đó. Ở 1280
 * lúc vừa đăng nhập đó là 320px trắng trơn ở mép phải, và "Select a conversation" thì căn giữa một
 * khung lệch hẳn sang trái. Mọi phép đo bằng số vẫn xanh (không tràn ngang, target 44px, 0 pageerror);
 * chỉ một tấm ảnh chụp production mới lộ. Đây đúng là cái bẫy đã ghi cho khổ ≤900 ở cuối file — tôi vá
 * nó ở đó rồi bỏ sót chính nó ở đây.
 *
 * Sửa bằng BIẾN, không bằng một luật `grid-template-columns` thứ hai: một luật thứ hai sẽ phải được
 * ghi đè lại trong cả hai media query bên dưới (chúng đặt `.app`, độ đặc hiệu thấp hơn `.app:not(…)`,
 * nên sẽ thua và cột phải quay lại ở khổ hẹp — đúng lỗi vừa sửa xong). Biến thì không dự vào cuộc
 * tranh độ đặc hiệu nào cả. */
.app:not([data-open]) { --details-w: 0px; }
.rail {
  background: var(--gemboss-rail-bg); border-right: 1px solid var(--gemboss-divider);
  display: flex; flex-direction: column; align-items: center; padding: 12px 0; gap: 12px; min-height: 0;
}
.rail .brand { padding: 0; }
.rail-nav { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }

/* Mỗi nút là một ô 44px — ngưỡng điều khiển của §9.1 — và mang nhãn chữ hiện khi rê chuột/focus.
   Nhãn là một phần tử thật, không phải `title`: `title` không hiện khi đi bằng bàn phím. */
.rail-btn {
  position: relative; width: 44px; height: 44px; display: grid; place-items: center;
  border: 0; border-radius: var(--gemboss-radius-md); background: none; color: var(--gemboss-text-muted); cursor: pointer;
}
.rail-btn:hover { background: var(--gemboss-hover); color: var(--gemboss-text); }
.rail-btn[aria-current="true"] { background: var(--gemboss-active-bg); color: var(--gemboss-active-fg); }
/* `display: none`, KHÔNG phải `opacity: 0`.
 *
 * Một nhãn trong suốt vẫn CHIẾM CHỖ: nó nằm ở `left: calc(100% + 8px)` nên thò ra ngoài mép phải và
 * làm cả trang cuộn ngang ở khổ hẹp — đo 23/09, `#duty-tip` đẩy `scrollWidth` lên 351 ở viewport 320.
 * Mất hiệu ứng mờ dần là cái giá rẻ hơn hẳn một trang cuộn ngang trên điện thoại. */
.rail-tip {
  display: none; position: absolute; left: calc(100% + 8px); top: 50%; transform: translateY(-50%);
  white-space: nowrap; padding: 4px 8px; border-radius: var(--gemboss-radius-sm);
  background: var(--gemboss-text); color: var(--gemboss-surface); font-size: var(--gemboss-fs-caption);
  pointer-events: none; z-index: 40;
}
.rail-btn:hover .rail-tip, .rail-btn:focus-visible .rail-tip,
.rail-duty:hover .rail-tip, .rail-duty:focus-visible .rail-tip,
.rail-me:hover .rail-tip, .rail-me:focus-visible .rail-tip { display: block; }
.rail-count { position: absolute; top: 2px; right: 2px; }
.rail-duty, .rail-me {
  position: relative; width: 44px; height: 44px; display: grid; place-items: center;
  border: 0; border-radius: 50%; background: var(--gemboss-hover); color: var(--gemboss-text);
  font-size: var(--gemboss-fs-caption); font-weight: 600; cursor: pointer;
}
.rail-duty__dot { width: 10px; height: 10px; border-radius: 50%; background: var(--gemboss-text-muted); }
.rail-duty[data-status="on_duty"] .rail-duty__dot { background: var(--gemboss-success); }
.rail-duty[data-status="away"] .rail-duty__dot { background: var(--gemboss-warning); }
/* Bảng trực mở ra từ rail — nó không vừa trong 64px. */
.rail-pop {
  position: fixed; left: 72px; bottom: 12px; z-index: 50; width: min(280px, calc(100vw - 84px));
  background: var(--gemboss-surface); border: 1px solid var(--gemboss-divider);
  border-radius: var(--gemboss-radius-lg); box-shadow: 0 8px 24px rgb(0 0 0 / .14); padding: 12px;
  display: grid; gap: 12px;
}
.rail-pop__foot { margin: 0; }
.list-view { display: block; }
.list-view .field { width: 100%; }
/* Hàng nút của luồng hội thoại phải XUỐNG DÒNG: ở 320px "Customer & AI" thò ra 66px ngoài mép. */
.thread-actions { flex-wrap: wrap; }

/* Hai tab của cột phải là `ui-tabs` của gói (`role=tab`, đang chọn = `aria-selected`). Ở đây chỉ khoảng cách với nội dung. */
.d-tabs { margin: -4px 0 12px; }
.d-panel { min-width: 0; }

/* Trợ lý. Câu hỏi nhanh trước ô nhập: người trực ca đêm không nghĩ ra câu hỏi, họ cần chỗ để bấm. */
/* Tab trợ lý AI là MỘT CỘT CHAT: luồng cuộn riêng, câu hỏi nhanh + ô hỏi đứng yên ở đáy, như mọi khung chat của fleet.
   HÌNH của từng mảnh (bong bóng, câu hỏi gợi ý, "đang làm", ô hỏi) là của @gemboss/ui chat.css + chat-composer.css; ở
   đây chỉ có bố cục cột, lề, và hai thứ chỉ gemcare có (dòng ghi model, nút "Insert into reply"). */
/* Flex CHỈ ở nơi cột đang hiện: ≥1100 là một cột của lưới, dưới 1100 là ngăn kéo khi đang mở. Một luật không giới hạn
   (1,2,0) từng thắng `.details { display: none }` của ngăn kéo ĐÃ ĐÓNG và nhét cột vào lưới, bóp cả hộp thư (review #235,
   đo ở 1024 và 390). Không `overflow: hidden`: khi dock chat đội nằm trong cột (`data-dock="column"`, cột ngắn đi), luồng
   co về 0 và phần còn lại phải CUỘN tới được — `.details` vẫn là khung cuộn như trước. */
@media (min-width: 1100px) {
  .details:has(> #d-panel-ai:not([hidden])) { display: flex; flex-direction: column; }
}
@media (max-width: 1099px) {
  .app[data-details="open"] .details:has(> #d-panel-ai:not([hidden])) { display: flex; flex-direction: column; }
}
#d-panel-ai:not([hidden]) { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
#ai-body:not([hidden]) { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
/* Luồng tràn ra sát hai mép cột (padding 16 của .details): khung cuộn tự có padding 16 của nó. */
.ai-thread { list-style: none; margin: 0 -16px; }
.ai-quick { margin: 8px 0 0; }
.ai-private { margin: 0; font-size: var(--gemboss-fs-caption); }
#ai-form { margin: 8px 0 0; }
.ai-turn__who { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.count {
  font-size: var(--gemboss-fs-caption); font-weight: 600; min-width: 20px; text-align: center; font-variant-numeric: tabular-nums;
  border-radius: var(--gemboss-radius-full); padding: 0 6px; background: var(--gemboss-hover); color: var(--gemboss-text-muted);
}
.count:empty { display: none; }
/* Bị gọi tên là việc gửi tới MỘT NGƯỜI, không tới hàng đợi: mượn màu của huy hiệu quá hạn. */
.mention-pill { display: flex; align-items: center; width: 100%; min-height: 44px; padding: 0 12px; border-radius: var(--gemboss-radius-md);
  border: 1px solid var(--gemboss-danger); background: var(--gemboss-surface); color: var(--gemboss-danger);
  font: inherit; font-weight: 600; text-align: left; cursor: pointer; }
.mention-pill:hover { background: var(--gemboss-hover); }
/* Vòng focus là MỘT màu ở mọi điều khiển, như admin: iris của gói (`--ui-focus`), không đỏ / đen theo từng nút. */
.mention-pill:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; }
/* Việc của mình, không phải lời gọi tên: màu chính, không màu báo động. */
.mine-pill { border-color: var(--gemboss-primary); color: var(--gemboss-primary); }
/* One rule (28/09/2026): it was declared twice, the second overriding most of the first. These are the
   values that were winning; `flex-direction` went with the flex it belonged to. */
.rail-foot { margin-top: auto; display: grid; align-items: center; gap: 12px; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.team { display: flex; flex-wrap: wrap; gap: 6px; }
/* `> .team-chip`, KHÔNG `span`. Luật cũ `.team span` khớp MỌI span con cháu — vô hại khi chip chỉ
   chứa chữ, nhưng chip nay có ảnh đại diện, và `.av` lẫn `.av__initials` đều là span. Để nguyên thì
   mỗi ô ảnh tự mọc một viên thuốc có viền và một chấm online riêng: ba chấm cho một người. Đúng họ
   lỗi `element-css-collides-across-sections` — một luật con cháu với vào phần tử nó không sở hữu. */
.team > .team-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px 2px 2px;
  border-radius: var(--gemboss-radius-full); border: 1px solid var(--gemboss-divider); color: var(--gemboss-text);
}
.team > .team-chip::before { content: ""; order: 3; width: 8px; height: 8px; border-radius: 50%; background: var(--gemboss-divider); }
.team > .team-chip[data-online="true"]::before { background: var(--gemboss-success); }
.me { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 0 4px; color: var(--gemboss-text); }
/* Công tắc trực. Ba nút là một nhóm: trạng thái đang chọn phải NHÌN ra ngay, vì nó quyết định câu
   khách nghe lúc 2 giờ sáng. Nút cao 44px theo §9.1 (điều khiển, không phải link trong danh sách). */
.duty { display: grid; gap: 6px; padding: 0 4px; }
.duty-buttons { display: flex; gap: 6px; }
.duty-buttons .ui-btn { flex: 1 1 0; padding: 0 8px; font-size: var(--gemboss-fs-caption); }
.duty-buttons .ui-btn[aria-pressed="true"] {
  border-color: var(--gemboss-primary); color: var(--gemboss-primary); font-weight: 600;
}
.duty[data-status="on_duty"] .ui-btn[aria-pressed="true"] { border-color: var(--gemboss-success); color: var(--gemboss-success); }
.duty #duty-note { margin: 0; }

.conn { display: flex; align-items: center; gap: 8px; padding: 0 4px; }
.conn-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gemboss-warning-border); }
.conn[data-state="live"] .conn-dot { background: var(--gemboss-success); }
.conn[data-state="offline"] .conn-dot { background: var(--gemboss-danger); }
/* The EMAIL channel, not the socket. `no_mx` is red rather than amber on purpose: a configured
   channel that silently bounces is worse than one that is plainly switched off, because the team
   believes it works. */
.intake[data-state="off"] .conn-dot { background: var(--gemboss-text-muted); }
.intake[data-state="no_mx"] .conn-dot { background: var(--gemboss-danger); }
.intake[data-state="no_mx"] { color: var(--gemboss-danger); font-weight: 600; }
/* `unknown` stays amber and stays quiet. It is not a refusal — it is our own resolver failing — and
   painting it red would teach the team that red means nothing. */
.intake[data-state="unknown"] .conn-dot { background: var(--gemboss-warning-border); }
/* `elsewhere` is red and bold like `no_mx`, and it is the worse of the two: a refused mail at least
   bounces back to the customer, while mail handed to the wrong host disappears with everyone — sender
   and team — believing it arrived. */
.intake[data-state="elsewhere"] .conn-dot { background: var(--gemboss-danger); }
.intake[data-state="elsewhere"] { color: var(--gemboss-danger); font-weight: 600; }
/* `unverified` is amber: nothing is known to be broken, and nothing is known to work either. */
/* `swapped`: model VẪN trả lời, nhưng không phải model được xin. Hổ phách như `unknown` vì tính
   năng chưa chết, nhưng ĐẬM — `unknown` cố ý "im lặng, không phải một lời từ chối", và câu to nhất
   của hàng model không được mặc áo của trạng thái không-cần-làm-gì. Ở đây CÓ việc phải làm: biết
   rằng chất lượng nháp đã đổi. */
.intake[data-state="swapped"] .conn-dot { background: var(--gemboss-warning-border); }
.intake[data-state="swapped"] { font-weight: 600; }
.intake[data-state="unverified"] .conn-dot { background: var(--gemboss-warning-border); }

.list { background: var(--gemboss-surface); border-right: 1px solid var(--gemboss-divider); display: flex; flex-direction: column; min-height: 0; }
.list-head { padding: 16px; border-bottom: 1px solid var(--gemboss-divider); }
.list-head h2 { margin: 0; font-size: var(--gemboss-fs-h3); }
.conversations { list-style: none; margin: 0; padding: 0; overflow-y: auto; flex: 1; }
.conversations button {
  width: 100%; min-height: 64px; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 8px; text-align: left;
  padding: 12px 16px; border: 0; border-bottom: 1px solid var(--gemboss-divider-soft); background: transparent; cursor: pointer;
  transition: background-color var(--gemboss-transition-fast) ease;
}
.conversations button:hover { background: var(--gemboss-hover); }
/* "Load more" cuối danh sách: một hàng đủ 44px, căn giữa, không trông như một hội thoại. */
.conversations .load-more-row { padding: 8px 12px; }
.conversations .load-more { width: 100%; justify-content: center; color: var(--gemboss-accent); }
.conversations button[aria-current="true"] { background: var(--gemboss-active-bg); box-shadow: inset 2px 0 0 var(--gemboss-primary); }
.c-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.c-time { color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); }
.c-preview { color: var(--gemboss-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.c-meta { display: flex; gap: 6px; align-items: center; justify-self: end; }
.c-unread {
  background: var(--gemboss-accent); color: var(--ui-accent-fg); font-size: var(--gemboss-fs-micro); font-weight: 700;
  border-radius: var(--gemboss-radius-full); padding: 0 6px; min-width: 20px; text-align: center;
}
.c-assignee { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
/* A GemFeedback request on a mixed list ("All open", "Mine"): replying posts publicly, so the row says where it came from. */
.c-channel { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-full, 999px); padding: 0 6px; white-space: nowrap; }
.conversations button[data-unread="true"] .c-preview { color: var(--gemboss-text); font-weight: 500; }

.thread { display: flex; flex-direction: column; min-width: 0; min-height: 0; background: var(--gemboss-surface); }
.thread-empty { flex: 1; }
.thread-body { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.thread-head {
  display: flex; align-items: center; gap: 12px; padding: 8px 16px; background: var(--gemboss-surface);
  border-bottom: 1px solid var(--gemboss-divider); flex-wrap: wrap;
}
.back { display: none; }
/* `min-width` có SÀN, không phải 0.
 *
 * `flex: 1` + `min-width: 0` cho khối tên co xuống tới KHÔNG khi hàng nút không đủ chỗ. Đo 23/09 ở
 * 1280 (khổ laptop của Chris, ngăn chi tiết đang mở): hộp tên còn **24px**, tên khách hiện ra là
 * "H…" và email là "ho…". Lỗi xuất hiện từ lúc thêm nút "Escalate to tech" ở lát 5 ① — năm nút cộng
 * một ô chọn không vừa 556px.
 *
 * Sàn 140px: hàng nút xuống dòng (header vốn `flex-wrap: wrap`) thay vì nuốt mất thứ quan trọng
 * nhất trên màn — tên người đang nói chuyện với mình. */
.who { display: grid; min-width: 140px; flex: 1; }
.who strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who span { color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thread-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
/* Nhãn ĐỦ khi rộng, nhãn NGẮN khi chật — cho "New request" và "Escalate to tech".
 *
 * Đo 26/09 ở 1280 — khổ laptop phổ biến nhất, lý do §9.1 thêm khổ này: thanh hành động rộng 524px mà
 * cần 591px (assignee 149 · New request 113 · Escalate to tech 134 · Resolve 83 · Snooze 80 · 4 khe
 * 8px), nên Snooze rơi xuống dòng riêng và đầu khung chiếm BA dòng. Ngưỡng 1440 là chỗ thanh có dư thật
 * cho nhãn đủ (thread ~716px).
 * Ngưỡng theo KHUNG NHÌN, không theo bề rộng của thanh: thanh vẫn gãy ở vài dải hẹp (901–982 khi ngăn
 * "Customer & AI" cũng ở trên hàng, 1100–1218) — review #143 tính ra. Truy vấn theo container là việc
 * sau; PR này nhắm 1280 và 1366.
 * Hai nút không có aria-label: tên truy cập là chữ đang hiện (xem inbox.html). */
.lbl-short { display: none; }
@media (max-width: 1439px) {
  .thread-actions .lbl-long { display: none; }
  .thread-actions .lbl-short { display: inline; }
  /* Đệm ngang 12px thay 16px — chỉ trong thanh này, không đụng `.ui-btn` của hệ thiết kế (vendored,
     có khoá sha256). Nút vẫn cao 44px: đệm dọc không đổi. */
  .thread-actions > .ui-btn, .thread-actions .snooze > summary { padding-left: 12px; padding-right: 12px; }
}
/* Ô chọn người phụ trách rộng bằng NGƯỜI ĐANG ĐƯỢC CHỌN, không bằng tên dài nhất trong danh sách (đo:
   149px với 31 agent). `field-sizing: content` làm việc ấy ở Chromium; trình duyệt chưa hiểu nó thì giữ
   hành vi cũ (tên dài nhất, trần 180px dưới đây) — không ai mất gì.
 * Bản trước ép cứng 128px ở 901–1439: "Chris Koh (you)" (146px) bị cắt thành "Chris Koh (yo" — đúng dấu
   "đây là của tôi" bị cắt trước tiên (review #143, có ảnh). Cắt chữ là đỏ ở §9.1; một hàng xuống dòng
   thì không. */
#t-assignee { field-sizing: content; }
/* Gợi ý của model: trải hết hàng (header là flex-wrap), chữ nhỏ, màu phụ. Nó KHÔNG được trông như
   một trạng thái của hội thoại — nó là một câu ai đó đoán. */
.tech-hint {
  flex: 1 0 100%; margin: 0; padding: 4px 0 0;
  font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted);
}
.assign .field { max-width: 180px; }

.messages { list-style: none; margin: 0; padding: 20px 16px; overflow-y: auto; flex: 1; display: flex; flex-direction: column; gap: 12px; }
/* Nền khung chat (Chris 05/10/2026). Hai yêu cầu, hai lượt:
   1. "toàn bộ ui là trắng nên dễ bị lẫn", thích nền WhatsApp ⇒ vùng tin có nền be ấm, tách khỏi cột trắng; rồi "background
      không có hoa văn như whatsapp" ⇒ thêm hoạ tiết nét mảnh (chat-pattern.svg: bong bóng, gem, túi, thẻ giá, sao…, độ đậm 7%).
   2. "màu tin nhắn cần đồng nhất ngoài website và bên trong… để agent và khách có cảm giác như nhau nhiều nhất" ⇒ màu đi theo
      NGƯỜI NÓI, giống hệt ở ba nơi: widget trên website (widget.js), panel hỗ trợ trong gemboss admin, và hộp thư này.
      Khách = gần đen #1b1c1e chữ trắng (đã là màu "tin của tôi" ở widget và panel gemboss, = --gemboss-primary);
      đội = trắng; GemCare AI = tím nhạt (như Fin của Intercom: không được trông như một người); ghi chú nội bộ = vàng viền đứt
      (chỉ đội thấy). Test the-chat-looks-the-same-on-both-sides giữ hai file cùng giá trị.
   Biến đặt TRONG `.messages`, không phải token của gói: gói không có màu chat, và hộp thư chỉ có giao diện sáng. */
.messages {
  --care-chat-bg: #efeae2; --care-chat-customer: #1b1c1e; --care-chat-on-customer: #ffffff; --care-chat-team: #ffffff;
  --care-chat-ink: #111b21; --care-chat-bot: #ece8ff; --care-chat-shadow: 0 1px .5px rgba(11, 20, 26, .13); --care-chat-meta: #54656f;
  background: var(--care-chat-bg) url("/assets/chat-pattern.svg") 0 0 / 240px 240px repeat;
}
/* Chữ phụ (giờ, người gửi, "Translate") trên nền be: màu phụ của gói chỉ đạt 4,46:1 ở đây (đo 05/10, ngưỡng 4,5). */
.messages .msg-meta, .messages > .day { color: var(--care-chat-meta); }
.msg { max-width: min(640px, 85%); display: grid; gap: 4px; }
.msg-bubble { padding: 8px 12px; border-radius: var(--gemboss-radius-xl); white-space: pre-wrap; overflow-wrap: anywhere; }
.msg-bubble a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.msg-bubble a:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; border-radius: 2px; }
.msg-meta { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.msg[data-sender="contact"] { align-self: flex-start; }
.msg[data-sender="contact"] .msg-bubble { background: var(--care-chat-customer); color: var(--care-chat-on-customer); box-shadow: var(--care-chat-shadow); border-bottom-left-radius: var(--gemboss-radius-xs); }
.msg[data-sender="agent"], .msg[data-sender="bot"] { align-self: flex-end; text-align: right; }
.msg[data-sender="agent"] .msg-bubble { background: var(--care-chat-team); color: var(--care-chat-ink); box-shadow: var(--care-chat-shadow); border-bottom-right-radius: var(--gemboss-radius-xs); text-align: left; }
.msg[data-sender="bot"] .msg-bubble { background: var(--care-chat-bot); color: var(--care-chat-ink); box-shadow: var(--care-chat-shadow); border-bottom-right-radius: var(--gemboss-radius-xs); text-align: left; }
.msg[data-sender="system"] { align-self: center; max-width: min(520px, 92%); text-align: center; }
.msg[data-sender="system"] .msg-bubble { background: var(--care-chat-team); color: var(--gemboss-text-muted); box-shadow: var(--care-chat-shadow); border-radius: var(--gemboss-radius-full); padding: 4px 12px; font-size: var(--gemboss-fs-caption); }
.msg[data-private="true"] .msg-bubble { background: var(--gemboss-warning-soft); color: var(--gemboss-text); border: 1px dashed var(--gemboss-warning-border); box-shadow: none; }
.msg[data-pending="true"] { opacity: .6; }
.msg[data-failed="true"] .msg-meta { color: var(--gemboss-danger); }
.day { align-self: center; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); padding: 4px 0; }
.messages > .day { background: var(--care-chat-team); box-shadow: var(--care-chat-shadow); border-radius: var(--gemboss-radius-sm); padding: 4px 12px; }
/* "Earlier with this customer": chuyện trước đó, MỜ và chỉ đọc — không lẫn với tin của hội thoại đang mở. */
.earlier { display: flex; flex-direction: column; gap: 8px; padding: 12px; border: 1px dashed var(--gemboss-divider); border-radius: 12px; color: var(--gemboss-text-muted); }
.earlier__title { margin: 0; font-size: var(--gemboss-fs-caption); font-weight: 600; text-transform: uppercase; letter-spacing: .04em; }
.earlier__conv { display: flex; flex-direction: column; gap: 4px; }
.earlier__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: var(--gemboss-fs-caption); }
.earlier__msg { display: flex; flex-direction: column; font-size: var(--gemboss-fs-body); overflow-wrap: anywhere; }
.earlier__who { font-size: var(--gemboss-fs-caption); }
.earlier__gap { font-size: var(--gemboss-fs-caption); }
.typing { margin: 0; padding: 0 16px 6px; color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); }

.composer { background: var(--gemboss-surface); border-top: 1px solid var(--gemboss-divider); padding: 0 16px 12px; display: grid; gap: 8px; }
.composer .ui-textarea { min-height: 72px; max-height: 40vh; }
.composer[data-mode="note"] .ui-textarea { background: var(--gemboss-warning-soft); border-color: var(--gemboss-warning-border); }
/* `flex-wrap` ở MỌI khổ: bốn nút không vừa 264px, và câu báo lỗi (#composer-error) lấy riêng một dòng. */
.composer-foot { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* ── Attachments: files waiting in the composer ── */
.files { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.file {
  display: flex; align-items: center; gap: 8px; padding: 4px 4px 4px 4px; min-width: 0; width: min(280px, 100%);
  border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md); background: var(--gemboss-surface);
}
.file[data-status="error"] { border-color: var(--gemboss-danger); background: var(--gemboss-danger-soft); }
.file-preview { width: 44px; height: 44px; flex: none; object-fit: cover; border-radius: var(--gemboss-radius-sm); background: var(--gemboss-hover); }
.file-preview--doc { display: grid; place-items: center; font-size: var(--gemboss-fs-micro); font-weight: 700; color: var(--gemboss-text-muted); }
.file-body { display: grid; gap: 2px; min-width: 0; flex: 1; }
.file-name { font-size: var(--gemboss-fs-caption); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-state { font-size: var(--gemboss-fs-micro); color: var(--gemboss-text-muted); overflow-wrap: anywhere; }
.file[data-status="error"] .file-state { color: var(--gemboss-danger); }
.file-bar { height: 4px; border-radius: var(--gemboss-radius-full); background: var(--gemboss-divider-soft); overflow: hidden; }
.file-bar-fill { display: block; height: 100%; width: 0; background: var(--gemboss-accent); transition: width var(--gemboss-transition-fast) linear; }
.file[data-status="ready"] .file-bar, .file[data-status="error"] .file-bar { visibility: hidden; }
.file[data-status="finalizing"] .file-bar-fill, .file[data-status="preparing"] .file-bar-fill { width: 100%; opacity: .35; }
.file-remove { flex: none; }
#composer-send:disabled { cursor: progress; }

/* ── Attachments in the thread ── */
.msg-files { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; } /* con của grid .msg: không có min-width 0 thì video 360px ép nó rộng hơn bong bóng ở 320/390 */
.msg[data-sender="agent"] .msg-files, .msg[data-sender="bot"] .msg-files { justify-content: flex-end; }
.file-thumb {
  padding: 0; border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-lg); overflow: hidden; cursor: zoom-in;
  background: var(--gemboss-hover); line-height: 0; max-width: 100%;
}
.file-thumb img { display: block; max-width: 100%; height: auto; object-fit: cover; }
.file-thumb:hover { box-shadow: var(--gemboss-shadow-resting); }
.msg[data-private="true"] .file-thumb, .msg[data-private="true"] .file-chip { border: 1px dashed var(--gemboss-warning-border); }
.file-chip {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 4px 12px 4px 4px; max-width: 100%;
  border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md); background: var(--gemboss-surface);
  color: var(--gemboss-text); text-decoration: none; text-align: left;
}
.file-chip:hover { background: var(--gemboss-hover); }
.file-chip:focus-visible, .file-thumb:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; }
.file-chip-icon {
  width: 36px; height: 36px; flex: none; display: grid; place-items: center; border-radius: var(--gemboss-radius-sm);
  background: var(--gemboss-danger-soft); color: var(--gemboss-danger); font-size: var(--gemboss-fs-micro); font-weight: 700;
}
.file-chip-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.file-chip-size { color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); flex: none; }
/* Ảnh thu nhỏ không tải được: về dạng chip (xem `attachmentNodes`), vẫn là <button> mở lightbox. Ô biểu tượng
   trung tính: màu đỏ của chip PDF ở đây sẽ đọc thành "lỗi nghiêm trọng" cho một ảnh chỉ mất bản xem trước. */
.file-chip--broken { font: inherit; cursor: zoom-in; }
.file-chip--broken .file-chip-icon { background: var(--gemboss-hover); color: var(--gemboss-text-muted); }
/* Video trong luồng tin: khung phát + dòng tên/cỡ bấm tải được. Độ rộng nằm trên chính <video> (phần tử replaced co được
   trong cột grid của .msg); đặt `width: 360px` lên khối bọc thì cột bị ép rộng ra và tràn bong bóng ở 320/390 (đo 03/10). */
.file-video { display: flex; flex-direction: column; gap: 4px; max-width: min(360px, 100%); min-width: 0; }
.file-video video {
  display: block; width: 360px; max-width: 100%; height: auto; max-height: 360px; border-radius: var(--gemboss-radius-lg); background: #000;
  border: 1px solid var(--gemboss-divider);
}
.msg[data-private="true"] .file-video video { border: 1px dashed var(--gemboss-warning-border); }
.file-video-name {
  display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 4px; color: var(--gemboss-text); text-decoration: none;
}
.file-video-name:hover .file-chip-name { text-decoration: underline; }
.file-video-name:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; }
.file-video-failed { color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); }

.thread { position: relative; }
/* Vị trí hai thanh kéo: dải 6px nằm ĐÈ lên mép cột giữa (nửa trong, nửa ngoài), không phải một rãnh lưới — thêm con
   vào lưới `.app` là xáo thứ tự tự xếp cột. Trong `.thread` vì ngăn chi tiết cuộn (`overflow-y: auto`): đặt ở đó thì
   thanh bị cắt và trôi theo khi cuộn. `.thread >` cho độ đặc hiệu (0,2,0): thắng `position: relative` của `.ui-resizer`
   mà không viết lại class của gói (cổng gemboss-ui.test). */
.thread > .inbox-seam { position: absolute; top: 0; bottom: 0; width: 6px; z-index: 6; }
.thread > .inbox-seam--list { left: -3px; }
.thread > .inbox-seam--details { right: -3px; }
/* Trên màn chạm, dải chạm 24px+ của gói nằm ĐỐI XỨNG quanh mép — tức 14px đè lên mép phải mọi dòng hội thoại và mép
   trái cột phải: chạm vào đó là kéo (không mở hội thoại), vuốt từ đó không cuộn (review #232). Dời dải sang phía CỘT GIỮA
   (nơi chỉ có lề 16px của luồng tin): cùng 27px, nhưng chỉ 3px đè lên danh sách / cột phải — y như với chuột. */
@media (pointer: coarse) {
  .thread > .inbox-seam--list::after { left: 0; right: -21px; }
  .thread > .inbox-seam--details::after { left: -21px; right: 0; }
}
/* Chưa mở hội thoại thì không có cột phải để kéo. */
.app:not([data-open]) .thread > .inbox-seam--details { display: none; }
.drop-hint {
  position: absolute; inset: 8px; z-index: 5; display: grid; place-items: center; pointer-events: none;
  border: 2px dashed var(--gemboss-accent); border-radius: var(--gemboss-radius-xl); background: var(--gemboss-primary-soft);
  color: var(--gemboss-text-strong); font-weight: 600;
}
dialog.lightbox {
  border: 0; padding: 0; background: transparent; max-width: calc(100vw - 32px); max-height: calc(100vh - 32px);
  display: none; flex-direction: column; gap: 8px; align-items: flex-end;
}
dialog.lightbox[open] { display: flex; }
dialog.lightbox::backdrop { background: rgba(20, 19, 18, .72); }
.lightbox-bar { display: flex; gap: 8px; }
.lightbox-bar .ui-btn { background: var(--gemboss-surface); }
#lightbox-img { display: block; max-width: calc(100vw - 32px); max-height: calc(100vh - 96px); object-fit: contain; border-radius: var(--gemboss-radius-md); background: var(--gemboss-surface); }
#lightbox-img[hidden] { display: none; }
.lightbox-error {
  margin: 0; max-width: min(420px, calc(100vw - 32px)); padding: 16px; border-radius: var(--gemboss-radius-md);
  background: var(--gemboss-surface); color: var(--gemboss-text);
}
.composer-hint { color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); flex: 1; }
/* Lỗi của ô soạn lấy HẲN một dòng, phía trên các nút, ở mọi khổ. Để nó chia khe với dòng gợi ý thì
   một trong hai bị bóp thành cột: dòng gợi ý bốn dòng ở 1280, câu lỗi sáu dòng trong 69px ở 390 (đo
   27/09/2026). Và KHÔNG giấu dòng gợi ý: nó là chỗ duy nhất nói câu trả lời sẽ đi đâu (Telegram, email,
   hay trợ lý Gem). Chỉ nhắm đúng #composer-error — `.ui-error` trong ngăn Request cũng nằm trong một
   `.composer-foot`. `margin: 0` vì `.ui-error` của gói có lề trên. */
#composer-error { order: -1; flex-basis: 100%; margin: 0; }
@media (max-width: 480px) {
  /* Ở khổ điện thoại, dòng gợi ý lấy một dòng riêng DƯỚI hàng nút. Để nó chen giữa "/ Replies" và Send
     thì nó thành một cột bốn dòng và đẩy Send xuống một hàng riêng; thêm dòng câu báo nữa là Send lòi
     khỏi đáy màn 320×640 (đo 27/09/2026). */
  .composer-hint { order: 1; flex-basis: 100%; }
}

.details { background: var(--gemboss-surface); border-left: 1px solid var(--gemboss-divider); padding: 16px; overflow-y: auto; }
.details h3 { font-size: var(--gemboss-fs-caption); text-transform: uppercase; letter-spacing: .06em; color: var(--gemboss-text-muted); margin: 20px 0 8px; }
.details h3:first-child { margin-top: 0; }
.details dl { margin: 0; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 6px 12px; }
.details dt { color: var(--gemboss-text-muted); }
.details dd { margin: 0; overflow-wrap: anywhere; }
/* Field về khách (lát 6). Mỗi dòng là MỘT nút: nhãn, giá trị, rồi ai đặt + lúc nào. Ba dòng chữ nên luôn cao hơn 44px,
   kể cả dưới chuột. Link (Store URL) có nút mở riêng bên phải: một <a> lồng trong <button> là HTML hỏng. */
.d-fields { display: grid; gap: 4px; margin: 0 -10px; }
.d-fields .d-field-add { width: calc(100% - 20px); margin: 4px 10px 0; }
.d-field { display: flex; align-items: stretch; gap: 4px; }
.d-field-btn {
  flex: 1 1 auto; min-width: 0; min-height: 44px; display: grid; gap: 1px; text-align: left; cursor: pointer; padding: 6px 10px;
  border: 1px solid transparent; border-radius: var(--gemboss-radius-md); background: transparent; color: inherit; font: inherit;
}
.d-field-btn:hover { background: var(--gemboss-hover); border-color: var(--gemboss-divider); }
.d-field--archived .d-field-btn { cursor: default; opacity: .75; }
.d-field--archived .d-field-btn:hover { background: transparent; border-color: transparent; }
.d-field-btn:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 1px; }
.d-field-lbl { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.d-field-val { overflow-wrap: anywhere; }
.d-field-src { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.d-field-open {
  flex: none; width: 44px; display: grid; place-items: center; border-radius: var(--gemboss-radius-md); color: inherit; text-decoration: none;
}
.d-field-open:hover { background: var(--gemboss-hover); }
.d-field-edit {
  display: grid; gap: 6px; padding: 10px; border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md);
  background: var(--gemboss-surface);
}
.d-field-edit .field { width: 100%; }
.d-field-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.d-field-checks { display: grid; gap: 2px; }
.d-field-check { display: flex; align-items: center; gap: 8px; min-height: 32px; }
@media (pointer: coarse), (max-width: 767px) { .d-field-check { min-height: 44px; } }
.d-field-history { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 2px; font-size: var(--gemboss-fs-caption); }
.d-field-history:empty { display: none; }


/* Ghi chú về khách + thẻ đề xuất (lát 7). */
.d-note-form { display: grid; gap: 6px; }
.d-note-form .ui-textarea, .d-proposal .ui-textarea { width: 100%; }
.d-proposal { display: grid; gap: 8px; margin-top: 8px; padding: 10px; border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md); }
.d-prop-title { margin: 0; font-weight: 600; }
.d-prop-row { display: flex; align-items: flex-start; gap: 8px; min-height: 32px; cursor: pointer; }
.d-prop-row input { margin-top: 3px; flex: none; }
@media (pointer: coarse), (max-width: 767px) { .d-prop-row { min-height: 44px; } .d-prop-orig summary { padding-block: 12px; } }
.d-prop-row > span { display: grid; gap: 1px; min-width: 0; overflow-wrap: anywhere; }
.d-prop-old { color: var(--gemboss-text-muted); text-decoration: line-through; }
.d-prop-empty { color: var(--gemboss-text-muted); font-style: italic; }
.d-prop-quote { display: block; margin-top: 2px; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); overflow-wrap: anywhere; }
.d-prop-orig summary { cursor: pointer; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.d-prop-orig p { margin: 4px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.d-note-result { margin: 0; }
.d-notes { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; }
.d-note { display: grid; gap: 2px; }
.d-note-body { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.d-note-meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; }

.previous { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.previous button {
  min-height: 32px; width: 100%; text-align: left; cursor: pointer; padding: 4px 12px;
  border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md); background: transparent;
}
.previous button:hover { background: var(--gemboss-hover); }

/* Nhóm nút (khoảng thời gian, ai làm, loại việc, cách xem) là `ui-seg` của gói: 32px dưới chuột, 44px ở ≤767px và dưới
   ngón tay, nút chữ không bẻ dòng, nhóm rộng hơn chỗ trống thì cuộn trong chính nó. `.range` chỉ còn là móc cho JS. Tới
   03/10/2026 đây là một nhóm tự vẽ (nền xám, ô chọn nổi, 44px dưới mọi con trỏ). */

/* ── Full-width screens: requests, reports, team settings ──
   By the ATTRIBUTE, not by a list of its values. The list version was three rules that each had to be
   found and extended; the third screen was added without them and rendered inside the 220px rail column
   with the conversation list still beside it. A layout rule that has to be edited for every new screen
   is a layout rule that will be missed. */
.app[data-screen] { grid-template-columns: 64px minmax(0, 1fr); }
.app[data-screen] .list, .app[data-screen] .thread, .app[data-screen] .details { display: none !important; }
.board-screen { overflow: auto; padding: 24px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; align-content: start; background: var(--gemboss-app-bg); min-width: 0; }
.board { display: grid; grid-template-columns: repeat(4, minmax(220px, 1fr)); gap: 12px; align-items: start; overflow-x: auto; padding-bottom: 8px; }
.column { background: var(--gemboss-primary-soft); border-radius: var(--gemboss-radius-lg); padding: 12px; display: grid; gap: 8px; min-height: 160px; align-content: start; }
.column[data-drop="true"] { outline: 2px dashed var(--gemboss-accent); outline-offset: -2px; }
.column-head { display: flex; justify-content: space-between; align-items: center; font-weight: 600; padding: 2px 4px; }
.column-head .count { background: var(--gemboss-surface); }
.card-rq {
  width: 100%; min-height: 44px; text-align: left; cursor: pointer; display: grid; gap: 4px; padding: 12px;
  background: var(--gemboss-surface); border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md);
  transition: box-shadow var(--gemboss-transition-fast) ease;
}
.card-rq:hover { box-shadow: var(--gemboss-shadow-resting); }
.card-rq[aria-grabbed="true"] { opacity: .5; }
.card-rq .rq-num { color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); }
.card-rq .rq-name { font-weight: 600; overflow-wrap: anywhere; }
.card-rq .rq-foot { display: flex; justify-content: space-between; gap: 8px; color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); }
.empty-col { color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); padding: 4px; margin: 0; }
/* Popup: `<dialog class="ui-modal">` của @gemboss/ui 0.5.0 (đầu có tiêu đề + nút ×, thân cuộn, chân đứng yên, nền của
   gói). Tới 03/10/2026 gemcare có bốn hình popup riêng: ngăn 560/440 với nút ✕ trôi ở góc, hai hộp thoại 480/420 KHÔNG có
   nút ×, mỗi cái một màu nền phía sau. Ở đây chỉ còn BỀ RỘNG của hai cỡ ngăn; 460px mặc định của gói cho hộp thoại. */
.care-drawer { width: min(560px, 100%); }
.care-drawer--small { width: min(440px, 100%); }
#rq-meta { margin-top: 0; }
.rq-desc { white-space: pre-wrap; margin: 0 0 12px; overflow-wrap: anywhere; }
.rq-controls { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.rq-controls label, .rq-new label { display: grid; gap: 4px; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.rq-new { display: grid; gap: 12px; }
/* A grid item's default minimum is its MIN-CONTENT, and a <select>'s min-content is its longest option —
   so "Published — merchants can read it" pushed the article editor 6px wider than the dialog and gave it a
   horizontal scrollbar at 320px. The visual gate cannot see this (it measures the page, not the dialog);
   it was measured with boundingBox(). `min-width: 0` lets the column shrink; the select then fills it. */
.rq-new label { min-width: 0; }
.rq-new select.field { width: 100%; box-sizing: border-box; }
.rq-new h3 { margin: 0; font-size: var(--gemboss-fs-h3); }
.rq-events { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 8px; max-height: 40vh; overflow-y: auto; }
.rq-events li { font-size: var(--gemboss-fs-caption); border-left: 2px solid var(--gemboss-divider); padding: 2px 0 2px 12px; }
.rq-events li[data-private="true"] { border-left-color: var(--gemboss-warning-border); background: var(--gemboss-warning-soft); }
.rq-events .ev-body { white-space: pre-wrap; margin-top: 2px; overflow-wrap: anywhere; font-size: var(--gemboss-fs-body); }
.rq-events .ev-meta { color: var(--gemboss-text-muted); }
.rq-comment { display: grid; gap: 8px; }
/* Ô tích là `ui-check` của gói (hàng 44px, hộp 18px, focus iris); ở đây chỉ phần nó chiếm trong hàng nút. */
.rq-private { flex: 1; }

/* ── Reports ── */
/* minmax(0, 1fr): an implicit grid column is `auto` and grows to the widest table — measured 5px of
   page scroll in Reports at 390 before this. */
.reports { overflow-y: auto; padding: 24px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; align-content: start; background: var(--gemboss-app-bg); min-width: 0; }
.reports-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; flex-wrap: wrap; }
.reports-head h2 { margin: 0; font-size: var(--gemboss-fs-h1); }
/* Bọc các thẻ toàn đội để ẩn cả khối theo quyền — `contents` để chúng VẪN là con của lưới
   `.reports`, nếu không thì sáu thẻ dính liền nhau không còn khe 16px nào. Lỗi này tôi bỏ sót vì
   chỉ thử vai cs_agent, mà với vai đó khối này ĐANG ẨN (§9.2: ma trận trạng thái, không chỉ mặc định). */
#r-team { display: contents; }
/* Gói ép lưới ô số thành 2 cột ở khổ ≤520px theo VIEWPORT. Ô nằm trong thẻ "Your numbers" hay trong popup hồ sơ khách thì
   thùng chứa hẹp hơn nhiều: ở 320px mỗi ô còn 93px và nhãn thành một cột chữ (visual-gate `text_squeezed`, 03/10/2026).
   Ở hai chỗ đó: một cột khi THÙNG CHỨA dưới 400px (container query, không đoán theo khổ màn). */
#r-mine, #profile > .ui-modal__body { container-type: inline-size; }
@container (max-width: 400px) { #r-mine-tiles, #p-tiles { grid-template-columns: minmax(0, 1fr); } }
/* Ô số là `ui-metric ui-metric--lg` trong `ui-metrics ui-metrics--grid` của gói (nhãn, số lớn, dòng chân); so với kỳ trước
   là `ui-delta`. Tới 03/10/2026 là `.tiles` / `.tile` / `.tile-delta` tự vẽ. Còn lại một dòng riêng: mục tiêu (`tile-goal`). */

/* Mục tiêu và khoảng cách. Màu KHÔNG phải tín hiệu duy nhất: câu "đạt" / "chưa đạt" / "chưa có số để
   so" nằm ngay trong chữ, nên một người không phân biệt được màu vẫn đọc được kết luận (§9.2). */
.tile-goal { font-size: var(--gemboss-fs-caption); font-weight: 600; }
.tile-goal[data-state="met"] { color: var(--gemboss-success); }
.tile-goal[data-state="missed"] { color: var(--gemboss-danger); }
/* Chưa có số để so là TRUNG TÍNH, không phải cảnh báo — nó không nói gì về người này cả. */
.tile-goal[data-state="unmeasured"] { color: var(--gemboss-text-muted); font-weight: 400; }
/* Biểu đồ hội thoại mỗi ngày là `ui-chart` của gói (renderDailyChart). Tới 03/10/2026 là một SVG tự vẽ kèm tooltip riêng. */
.table-view { margin-top: 12px; }
/* 44px, not 24: a disclosure is a control (WCAG 2.5.5), and `min-height` on an inline box does nothing —
   these four "Show as table" rows measured 24px at 320/390/768 from the day they shipped. */
.table-view summary { display: inline-flex; align-items: center; cursor: pointer; color: var(--gemboss-text-muted); min-height: 44px; margin-bottom: 8px; }
#r-agents > p { margin: 0; padding: 12px; }

/* Ở khổ rộng ngăn chi tiết luôn hiện, nên nút mở nó là một nút không làm gì.
   Rule này phải đứng TRƯỚC media query: cùng độ ưu tiên (#id) thì rule đứng sau thắng, và đặt nó ở
   dưới làm nút biến mất ở MỌI khổ — đo trên trình duyệt 23/09, nút có trong DOM mà không bao giờ bấm được. */
#t-details { display: none; }
/* Cùng lý do thứ tự như trên: rule nền đứng TRƯỚC media query. */
.details-close { display: none; }

/* ── Hàng đợi tech ── */
/* Tiêu đề ngăn giữa dãy số và danh sách việc: không có nó thì hai khối dính vào nhau và người đọc
   không biết con số bên trên nói về cửa sổ 7 ngày còn danh sách bên dưới là NGAY BÂY GIỜ. */
.tech-h3 { margin: 20px 0 8px; font-size: var(--gemboss-fs-h3); }
#tech-list { display: grid; gap: 12px; }
.tech-card {
  display: grid; gap: 8px; padding: 12px;
  background: var(--gemboss-surface); border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-lg);
}
/* Quá hẹn: một VẠCH bên trái, không phải nền đỏ cả thẻ. Nền màu trên cả khối làm chữ khó đọc và
   tranh chấp với mọi trạng thái khác của thẻ — cùng lý do với vạch "gọi tên mình" bên chat. */
.tech-card.is-breached { border-left: 2px solid var(--gemboss-danger); }
.tech-card__head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.tech-card__sev { flex: none; }
.tech-card__who { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Số đồng hồ dùng chữ số ĐỀU BỀ RỘNG: một cột con số nhảy qua nhảy lại giữa các hàng là cột không
   liếc qua được. */
.tech-card__clock { flex: none; font-variant-numeric: tabular-nums; color: var(--gemboss-text-muted); }
.tech-card__clock.is-breached { color: var(--gemboss-danger); font-weight: 700; }
.tech-card__reason { margin: 0; overflow-wrap: anywhere; }
.tech-card__meta { margin: 0; font-size: var(--gemboss-fs-caption); }
.tech-card__foot { display: flex; flex-wrap: wrap; gap: 8px; }

/* Thân của hai popup chuyển tech / trả về CS: chỉ bố cục ô trong thân, hình popup là của gói. */
.esc-form { display: grid; gap: 12px; }
.esc-form label { display: grid; gap: 6px; font-weight: 500; }
.esc-form .muted { margin: 0; font-size: var(--gemboss-fs-caption); }

/* ── Narrower screens ── */
/* Ngưỡng hạ từ 1365 xuống 1100.
 *
 * Rail cũ rộng 220px nên ở 1280 không còn chỗ cho cột phải, và nó bị ẩn hẳn — mà 1280 là khổ laptop
 * phổ biến nhất, tức thông tin khách biến mất đúng ở chỗ nhiều người làm việc nhất. Rail nay 64px:
 * 64 + 300 + 320 = 684, còn 596px cho luồng hội thoại ở 1280. Vừa. */
@media (max-width: 1099px) {
  .app { grid-template-columns: 64px 300px minmax(0, 1fr); }
  /* Dưới 1100 các cột đã gập (cột phải trượt vào, rồi danh sách và luồng thay nhau): không còn mép nào để kéo. */
  .thread > .inbox-seam { display: none; }
  .app[data-screen] { grid-template-columns: 64px minmax(0, 1fr); }
  /* Dưới ngưỡng này cột phải không còn chỗ trong lưới — nhưng "không có chỗ" không được phép thành
     "không tới được". Nó trượt vào từ mép phải khi bấm nút Customer. */
  .details { display: none; }
  .app[data-details="open"] .details {
    display: block; position: fixed; inset: 0 0 0 auto; width: min(360px, 100vw);
    z-index: 30; box-shadow: -8px 0 24px rgb(0 0 0 / .18);
  }
  #t-details { display: inline-flex; }
  /* ≥44px theo ngưỡng điều khiển của §9.1; nổi ở góc phải trên, không đẩy nội dung xuống. */
  .details-close { display: inline-flex; position: absolute; top: 8px; right: 8px; min-width: 44px; min-height: 44px; }
  .app[data-details="open"] .details { padding-top: 60px; }
}
@media (max-width: 900px) {
  .board-screen { padding: 16px; }
  .board { grid-template-columns: repeat(4, minmax(240px, 80vw)); }
  .reports { padding: 16px; }
  /* Rail GIỮ NGUYÊN chiều dọc ở điện thoại, chỉ hẹp lại 56px.
   *
   * Luật cũ biến nó thành một hàng ngang trên đỉnh — hợp với rail CHỮ rộng 220px, vô nghĩa với rail
   * biểu tượng: 8 icon xếp ngang chiếm nguyên một dải màn hình để nói đúng thứ một cột 56px nói được.
   * Và `minmax(0, …)` chứ không phải `1fr`: một rãnh `1fr` nở ra bằng đứa con rộng nhất của nó, và cả
   * trang cuộn ngang ở 390px (đo được 8px ở hộp thư, 4px ở báo cáo).
   *
   * QUAN TRỌNG: rãnh của cột danh sách phải BIẾN MẤT, không chỉ ẩn phần tử. `display: none` giấu đứa
   * con nhưng `grid-template-columns` vẫn giữ rãnh 300px — ở viewport 320 thì 64 + 300 = 364 và trang
   * cuộn ngang 76px. Đo 23/09 ngay sau khi ghép main. */
  .app { grid-template-columns: 56px minmax(0, 1fr); }
  .app[data-screen] { grid-template-columns: 56px minmax(0, 1fr); }
  /* Một phần tử lưới mặc định `min-width: auto`, tức nó KHÔNG co được dưới bề rộng nội dung: rãnh
     tính ra 264px mà ô soạn vẫn 380px và cả trang cuộn ngang. Luật cũ vá chỗ này bằng
     `.rail > * { min-width: 0 }`; nay cần cho ba cột nội dung. */
  .list, .thread, .details { min-width: 0; }
  /* Hàng tab của ô soạn xuống dòng ở 264px. (Hàng nút dưới ô soạn xuống dòng ở MỌI khổ — xem
     `.composer-foot` ở trên: câu báo lỗi của nó lấy riêng một dòng.) */
  .composer-tabs { flex-wrap: wrap; }
  .rail { padding: 8px 0; gap: 8px; }
  /* Rail hẹp lại 56px ở điện thoại, nhưng KÍCH THƯỚC một điều khiển thì không co theo bề rộng rail:
     44px là ngưỡng §9.1 (WCAG 2.5.5) và nó áp cho cả ba loại nút, không riêng .rail-btn. Luật cũ chỉ
     nâng .rail-btn, nên công tắc trực và nút tài khoản đứng ở 40px từ #82 tới giờ (đo ở 320/390/768).

     CẢ HAI chiều, không riêng chiều cao: `.rail-duty`/`.rail-me` mang `border-radius: 50%`, nên
     44×40 không phải một nút nhỏ — nó là một hình BẦU DỤC. Nâng mỗi `height` vừa không đạt ngưỡng
     (2.5.5 đo cả bề ngang) vừa làm hỏng hình. 44 vẫn lọt rail 56px. */
  .rail-btn, .rail-duty, .rail-me { width: 44px; height: 44px; min-height: 44px; }
  .app[data-pane="thread"] .list { display: none; }
  .app:not([data-pane="thread"]) .thread { display: none; }
  .back { display: inline-flex; }
  .thread-actions { width: 100%; }
  /* `flex: 1` nghĩa là `flex-basis: 0` — ô chỉ nhận PHẦN THỪA sau khi ba nút cạnh nó lấy chỗ. Ở 390px
     chẳng còn gì, và ô Assignee co xuống **39×44**: dưới sàn chạm 44px của §9.1, đo trên prod 24/09.
     `.thread-actions` vốn đã `flex-wrap: wrap`, nên cho nó một BASIS đủ lớn thì nó xuống dòng riêng
     thay vì bị nghiền — rộng hơn hẳn 44px, và dễ bấm hơn cho ngón tay. */
  .thread-actions .assign { flex: 1 1 160px; min-width: 160px; }
  .assign .field { max-width: none; width: 100%; }
  .msg { max-width: 92%; }
  /* §9.1 measures tap targets at 320/390/768: a CONTROL is 44px there, however link-like it looks.
     Both of these read as text and are buttons — the gate counts what they are, and so do thumbs. */
  .link { min-height: 44px; }
}
@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto !important; transition: none !important; } }

/* "Load earlier messages", above the first page of a long thread. */
.older { display: flex; justify-content: center; padding: 8px 0 4px; }

/* When customers write, on their own clock. One hue for "how many"; an outline for "we were closed". */
.heat { overflow-x: auto; }
.heat-grid {
  display: grid;
  grid-template-columns: 32px repeat(24, minmax(12px, 1fr));
  gap: 2px;
  min-width: 420px;
  align-items: center;
}
.heat-corner { height: 16px; }
.heat-hour { font-size: var(--gemboss-fs-micro); color: var(--gemboss-text-muted); text-align: center; }
.heat-day { font-size: var(--gemboss-fs-micro); color: var(--gemboss-text-muted); }
.heat-cell {
  height: 16px;
  border-radius: 2px;
  /* The same iris the daily chart uses, light to dark: the two cards are one system, and a sequential ramp
     is one hue — a second colour here would read as a second variable. */
  background: color-mix(in srgb, var(--gemboss-accent) calc(var(--fill, 0) * 100%), var(--gemboss-hover));
}
/* Hours nobody is on duty: outlined rather than recoloured, so the count ramp stays readable. */
.heat-cell[data-covered="no"] { box-shadow: inset 0 0 0 1px var(--gemboss-warning); }
/* Ô chưa có dữ liệu (trước lần check-in đầu tiên, hoặc lúc đội tắt check-in): khác với "0 khách". */
.heat-cell[data-nodata] { opacity: 0.35; }
/* Dải ngày của thẻ khối lượng việc: mỗi ngày một cột, cao theo số trả lời (bậc 0–8, không style inline vì CSP). */
.wl-strip { display: flex; align-items: flex-end; gap: 1px; height: 28px; min-width: 120px; }
.wl-day { flex: 1 1 0; min-width: 2px; height: 2px; border-radius: 1px; background: color-mix(in srgb, var(--gemboss-accent) 70%, var(--gemboss-hover)); }
.wl-day[data-level="0"] { background: var(--gemboss-hover); }
.wl-day[data-level="1"] { height: 12.5%; }
.wl-day[data-level="2"] { height: 25%; }
.wl-day[data-level="3"] { height: 37.5%; }
.wl-day[data-level="4"] { height: 50%; }
.wl-day[data-level="5"] { height: 62.5%; }
.wl-day[data-level="6"] { height: 75%; }
.wl-day[data-level="7"] { height: 87.5%; }
.wl-day[data-level="8"] { height: 100%; }
/* Backlog: cùng cột bậc 0–8 với dải khối lượng việc, cao hơn; ngày chưa biết vẽ mờ, không vẽ thành 0. */
.bl-chart .wl-strip { height: 72px; min-width: 0; }
/* Thấp và phẳng: một cột cao mờ đọc như một đỉnh (review #245), còn đây là "chưa có số". */
.wl-day[data-nodata] { height: 3px; opacity: 0.5; background: var(--gemboss-text-muted); }

/* How customers rated us: `ui-barlist` of the kit (renderCsat), one row per score, longest bar = most common. */

/* Search sits in the list header, where the list it filters is. */
.list-head { display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
.search { display: flex; }
.search .field { width: 100%; }

/* Saved replies: a list above the composer, driven by `/` in the box. */
.replies { display: flex; flex-direction: column; gap: 2px; max-height: 240px; overflow-y: auto; margin-bottom: 4px; }
/* Bảng chọn tên ở chat đội: cùng khối `.replies`/`.reply` với ô ghi chú (một hình dạng, §9.3). Ở màn Chat nó trải hết
   ba cột của lưới ô nhập; ở cửa sổ nổi nó nằm ngay trên ô nhập. */
.chat-mention { grid-column: 1 / -1; }
.dock-win__mention { margin: 0 8px; }
.reply {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "shortcut title" "shortcut body";
  gap: 0 8px;
  min-height: 44px;
  padding: 6px 8px;
  border: 1px solid var(--gemboss-divider);
  border-radius: 12px;
  background: var(--gemboss-surface);
  text-align: left;
  cursor: pointer;
}
.reply[aria-selected="true"] { border-color: var(--gemboss-accent); background: var(--gemboss-hover); }
.reply-shortcut { grid-area: shortcut; align-self: center; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--gemboss-fs-micro); color: var(--gemboss-accent); }
.reply-title { grid-area: title; font-size: var(--gemboss-fs-body); color: var(--gemboss-text-strong); }
.reply-body { grid-area: body; font-size: var(--gemboss-fs-micro); color: var(--gemboss-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.replies-open { flex: 0 0 auto; }

/* The customer's name is a way in, so it looks like one. */
.t-name-btn {
  padding: 0;
  border: 0;
  background: none;
  color: var(--gemboss-text-strong);
  font: inherit;
  cursor: pointer;
  text-align: left;
  /* Ngưỡng ĐIỀU KHIỂN của §9.1, không phải ngưỡng 24px của link trong danh sách: đây là một nút mở
     một màn khác. `align-content` để hai dòng vẫn dính nhau khi khối cao hơn nội dung. */
  min-height: 44px;
  align-content: center;
}
.t-name-btn strong { font-weight: 600; }
/* Email nằm TRONG một <button>, nên mặc định nó không bôi đen được. Chrome nghe `user-select`;
   Firefox/Safari thì không cho chọn chữ trong nút dù khai gì đi nữa. Đường chắc chắn để sao chép
   email vẫn là ngăn Customer bên phải (mục Email ở `#d-contact`) — ở đó nó là văn bản thường. */
.t-name-btn #t-sub { -webkit-user-select: text; user-select: text; cursor: text; }
.t-name-btn:hover { color: var(--gemboss-accent); text-decoration: underline; }
.t-name-btn:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; }

/* Everything this customer has said to us. */
.profile h4 { margin: 16px 0 6px; font-size: var(--gemboss-fs-body); color: var(--gemboss-text-strong); }
.profile .previous button { display: grid; gap: 2px; width: 100%; min-height: 44px; padding: 8px; border: 0; border-radius: 12px; background: none; text-align: left; cursor: pointer; }
.profile .previous button:hover { background: var(--gemboss-hover); }
.profile .previous button[aria-current="true"] { background: var(--gemboss-hover); }
.profile .p-title { font-size: var(--gemboss-fs-body); color: var(--gemboss-text-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.profile .p-meta { font-size: var(--gemboss-fs-micro); color: var(--gemboss-text-muted); }

/* How long this customer has been waiting, in working time. Red once it is past the line. */
.c-wait { font-size: var(--gemboss-fs-micro); color: var(--gemboss-text-muted); white-space: nowrap; }
.c-wait[data-overdue="true"] { color: var(--gemboss-danger); font-weight: 600; }
/* Dấu ĐANG HOÃN trên hàng: cùng cỡ chữ với đồng hồ chờ, nhạt hơn — nó là trạng thái, không phải báo động. */
.c-snoozed { font-size: var(--gemboss-fs-micro); color: var(--gemboss-text-muted); white-space: nowrap; }
.count[data-overdue="true"] { background: var(--gemboss-danger); color: var(--gemboss-surface); }

/* What a conversation is about: fixed topics as toggles, free tags as removable chips. */
/* Chip chủ đề (bật / tắt) và chip tag (gỡ). Cùng số đo với `.ui-chips > a.ui-tag--chip` của gói (28px dưới chuột, 44px ở
   ≤767px và dưới ngón tay, bo 8, chữ 12px, ô chọn nền accent-soft viền accent). Gói chỉ vẽ chip là LINK (`a`, chọn =
   `aria-current`); chip là NÚT bật tắt (`aria-pressed`) chưa có, nên đây vẫn là một bản của gemcare. Khi gói có, gỡ. */
.labels { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.chip {
  min-height: 28px;
  padding: 4px 12px;
  border: 1px solid var(--ui-border);
  border-radius: var(--ui-radius-md);
  background: var(--gemboss-surface);
  color: var(--ui-text);
  font: inherit;
  font-size: var(--gemboss-fs-caption);
  cursor: pointer;
}
.chip:hover { background: var(--ui-surface-2); border-color: var(--ui-text-muted); }
.chip[aria-pressed="true"] { border-color: var(--ui-accent); background: var(--ui-accent-soft); color: var(--ui-accent-strong); font-weight: 600; }
.chip--tag { border-style: dashed; }
.chip:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 1px; }
@media (pointer: coarse), (max-width: 47.9975em) { .chip { min-height: 44px; } }
.tag-add .field { width: 100%; }

/* Team settings: closed days and refused mail. */
.closure-add,
.kpi-add,
.sr-add { display: flex; flex-wrap: wrap; gap: 12px; align-items: end; margin-top: 12px; }
.closure-field,
.kpi-field,
.sr-field { display: grid; gap: 4px; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.closure-field .field,
.kpi-field .field,
.sr-field .field { width: 100%; }
/* 160px, not 220: at 320px the name field then shares its row with Add instead of pushing the button
   below the fold — measured 578px against a 568px fold before this. */
.closure-field.grow,
.kpi-field.grow,
.sr-field.grow { flex: 1 1 160px; }

/* Nội dung câu mẫu là một đoạn văn, không phải một ô chữ: nó chiếm trọn một hàng và tự xuống dòng
   cùng với hai ô trên nhờ `flex-wrap` của `.sr-add`. `flex-basis: 100%` chứ không phải `width`:
   trong một flex container, `width` vẫn để ô co lại chen cạnh ô khác. */
.sr-field.wide { flex: 1 1 100%; }
.sr-field .ui-textarea { width: 100%; }
/* Hai nút đi cùng nhau xuống hàng cuối, và giữ khe 8px cho §9.1 khi chúng nằm cạnh nhau. */
.sr-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: end; }

/* Chủ đề đã bỏ khỏi danh sách nhưng hội thoại này vẫn mang: nhạt đi để nó không mời bấm, nhưng KHÔNG
   ẩn — ẩn đi là cách một cái nhãn còn trong báo cáo mà không ai gỡ được. Chữ "(đã bỏ)" mới là thứ
   nói ra trạng thái; màu chỉ đi kèm. */
/* `.chip[aria-pressed="true"]` là 0-2-0, còn `.chip--retired` một mình chỉ 0-1-0 — mà chip đã bỏ
   CHỈ hiện ra khi đang được chọn, nên màu ở đây không bao giờ thắng: một khai báo chết. Ghép cả hai
   để nó thật sự đổi hình. Chữ "(đã bỏ)" vẫn là thứ nói ra trạng thái; nét đứt chỉ đi kèm. */
.chip--retired,
.chip--retired[aria-pressed="true"] { border-style: dashed; color: var(--gemboss-text-muted); }
/* A day already past is history, not a setting — legible, but it stops competing with the ones ahead. */
.ui-table tr.is-past td { color: var(--gemboss-text-muted); }

.junk { display: grid; gap: 12px; margin-top: 12px; }
.junk-item {
  display: grid; gap: 8px; justify-items: start; min-width: 0; max-width: 100%;
  padding: 12px; border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md);
  background: var(--gemboss-hover);
}
/* An email address is ONE unbroken token, and a cold pitch is where the 90-character ones live. Without
   `anywhere` the header refuses to shrink and drags the card to 567px inside a 320px panel — which the
   visual gate does NOT report, because `.reports` scrolls it as an ancestor and a clipped overflow is
   legitimate for a carousel. Measured with `boundingBox()`, not with the gate. */
.junk-item header { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; min-width: 0; max-width: 100%; font-size: var(--gemboss-fs-caption); }
.junk-item header strong, .junk-subject { overflow-wrap: anywhere; min-width: 0; }
.junk-rule {
  border-radius: var(--gemboss-radius-full); padding: 2px 8px;
  background: var(--gemboss-surface); border: 1px solid var(--gemboss-divider); color: var(--gemboss-text-muted);
}
.junk-subject { margin: 0; font-weight: 600; max-width: 100%; }
/* A disclosure is a control, not a link in prose — 44px (WCAG 2.5.5). Declared at full strength here rather
   than in the ≤900px block: this file's junk rules sit AFTER that block, so an override there loses the
   cascade at equal specificity and the gate keeps measuring 24px. */
.junk-item summary { display: inline-flex; align-items: center; min-height: 44px; cursor: pointer; color: var(--gemboss-text-muted); }
.junk-body {
  margin: 8px 0 0; max-height: 240px; overflow: auto; white-space: pre-wrap; word-break: break-word;
  font: inherit; color: var(--gemboss-text-muted);
}

/* Help centre: a status chip in the list, and a title that reads as a link because it opens the editor. */
/* Cùng lý do. Đây là một `<button>` nằm trong ô bảng, và cổng đếm nó là ĐIỀU KHIỂN chứ không phải
   link — "however link-like it looks", đúng câu chú thích đã có sẵn trong file này. Đo trên prod ở
   390px với một nhãn ngắn: 29×44. */
.link-strong {
  border: 0; padding: 0; min-height: 44px; min-width: 44px; background: none; cursor: pointer; text-align: left;
  font-weight: 600; color: var(--gemboss-text-strong); text-decoration: underline;
}

/* Mentions and the shortcut sheet. */
.mention-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; }
.mention-list button {
  display: grid; gap: 2px; width: 100%; min-height: 44px; padding: 12px; text-align: left; cursor: pointer;
  border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md);
  background: var(--gemboss-surface); color: var(--gemboss-text);
}
.mention-list button:hover { background: var(--gemboss-hover); }
.mention-list button:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; }
.mention-who { font-weight: 600; overflow-wrap: anywhere; }
.mention-text { color: var(--gemboss-text-muted); overflow-wrap: anywhere; }

/* Two columns that stay readable at 320: the keys are narrow, the meaning takes the rest. */
.keys { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 8px 12px; margin: 12px 0 0; align-items: baseline; }
.keys dt { white-space: nowrap; }
.keys dd { margin: 0; color: var(--gemboss-text-muted); }
kbd {
  display: inline-block; min-width: 20px; padding: 2px 6px; text-align: center;
  font: inherit; font-size: var(--gemboss-fs-caption); font-variant-numeric: tabular-nums;
  border: 1px solid var(--gemboss-divider); border-bottom-width: 2px; border-radius: var(--gemboss-radius-md);
  background: var(--gemboss-hover); color: var(--gemboss-text);
}

/* Lengths and shades as ATTRIBUTES: the page runs under style-src 'self', so an inline style is ignored
   silently — the bar would render at zero with nothing to say why. */
[data-level="0"].file-bar-fill { width: 0%; }
[data-level="1"].file-bar-fill { width: 5%; }
[data-level="2"].file-bar-fill { width: 10%; }
[data-level="3"].file-bar-fill { width: 15%; }
[data-level="4"].file-bar-fill { width: 20%; }
[data-level="5"].file-bar-fill { width: 25%; }
[data-level="6"].file-bar-fill { width: 30%; }
[data-level="7"].file-bar-fill { width: 35%; }
[data-level="8"].file-bar-fill { width: 40%; }
[data-level="9"].file-bar-fill { width: 45%; }
[data-level="10"].file-bar-fill { width: 50%; }
[data-level="11"].file-bar-fill { width: 55%; }
[data-level="12"].file-bar-fill { width: 60%; }
[data-level="13"].file-bar-fill { width: 65%; }
[data-level="14"].file-bar-fill { width: 70%; }
[data-level="15"].file-bar-fill { width: 75%; }
[data-level="16"].file-bar-fill { width: 80%; }
[data-level="17"].file-bar-fill { width: 85%; }
[data-level="18"].file-bar-fill { width: 90%; }
[data-level="19"].file-bar-fill { width: 95%; }
[data-level="20"].file-bar-fill { width: 100%; }
.heat-cell[data-level="1"] { background: color-mix(in srgb, var(--gemboss-accent) 23%, var(--gemboss-hover)); }
.heat-cell[data-level="2"] { background: color-mix(in srgb, var(--gemboss-accent) 34%, var(--gemboss-hover)); }
.heat-cell[data-level="3"] { background: color-mix(in srgb, var(--gemboss-accent) 45%, var(--gemboss-hover)); }
.heat-cell[data-level="4"] { background: color-mix(in srgb, var(--gemboss-accent) 56%, var(--gemboss-hover)); }
.heat-cell[data-level="5"] { background: color-mix(in srgb, var(--gemboss-accent) 67%, var(--gemboss-hover)); }
.heat-cell[data-level="6"] { background: color-mix(in srgb, var(--gemboss-accent) 78%, var(--gemboss-hover)); }
.heat-cell[data-level="7"] { background: color-mix(in srgb, var(--gemboss-accent) 89%, var(--gemboss-hover)); }
.heat-cell[data-level="8"] { background: color-mix(in srgb, var(--gemboss-accent) 100%, var(--gemboss-hover)); }
.heat-cell[data-level="0"] { background: var(--gemboss-hover); }

/* The question a merchant typed, quoted back to whoever is about to answer it. Quiet, but not muted
   into invisibility: it is the reason the article exists. */
.asked { margin: 0; padding: 8px 12px; border-left: 2px solid var(--gemboss-divider);
  background: var(--gemboss-canvas); color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); }
.asked b { color: var(--gemboss-text-strong); font-weight: 600; }

/* The attachment bucket refusing an origin the chat runs on: red, because every screenshot sent from
   there is lost in the customer's browser and we never learn it existed. */
.intake[data-state="blocked"] .conn-dot { background: var(--gemboss-danger); }
.intake[data-state="blocked"] { color: var(--gemboss-danger); font-weight: 600; }

/* An origin that produced a SIGNED customer token is not a stranger — it is our own product being
   refused. It gets the danger colour because that is a customer who cannot reach us at all. */
/* Class RIÊNG `is-ours` trên hàng, không chỉ thuộc tính: `.ui-table tr[data-ours] td` không mang class nào của gemcare, nên
   nó là một luật vẽ lại bên trong bảng của gói (cổng gemboss-ui.test bắt từ 03/10/2026). */
.ui-table tr.is-ours td { color: var(--gemboss-danger); font-weight: 600; }

/* The schedule itself — read at a glance, so it is the biggest thing on the card. */
.hours-line { margin: 0 0 4px; font-size: var(--gemboss-fs-body-lg); color: var(--gemboss-text-strong); font-weight: 600; }

/* The hours form. Wraps to one column on a phone, like every other form on this screen. */
.hours-form { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin-top: 12px; }
.hours-form .hours-field { display: flex; flex-direction: column; gap: 4px; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.hours-form .hours-field.grow { flex: 1 1 220px; }
/* Ô múi giờ: một <select> rộng bằng tên múi giờ dài nhất, nên ở 320/390 nó đẩy thẻ rộng 341px trong khung 264/334 (đo
   03/10/2026). Cùng cách chữa với `.rq-new label`: ô co được, select lấp đầy ô. */
.hours-form .hours-field { min-width: 0; max-width: 100%; }
.hours-form .hours-field .field { width: 100%; }
.hours-days { border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md); padding: 8px 12px 12px; margin: 0; flex: 1 1 100%; }
.hours-days legend { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); padding: 0 4px; }
.hours-days div { display: flex; flex-wrap: wrap; gap: 8px; }
.hours-days label { padding: 0 8px; }   /* ô tích là `ui-check` của gói */
.hours-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* Chat nội bộ: cột kênh + cuộc trò chuyện, cùng hình dạng với hộp thư nên không ai phải học một cách
   đọc thứ hai. Ở khổ hẹp cột kênh nằm TRÊN, cuộn ngang, thay vì biến mất. */
.chat-screen { display: grid; grid-template-columns: 220px minmax(0, 1fr); min-height: 0; overflow: hidden; }
.chat-rail { border-right: 1px solid var(--gemboss-divider); padding: 16px; overflow-y: auto; }
.chat-rail__title { font-size: var(--gemboss-fs-caption); text-transform: uppercase; letter-spacing: .06em;
  color: var(--gemboss-text-muted); margin: 0 0 8px; }
.chat-rail ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.chat-channel { width: 100%; text-align: left; min-height: 44px; padding: 0 12px; border: 0; border-radius: var(--gemboss-radius-md);
  background: none; font: inherit; color: var(--gemboss-text); cursor: pointer; }
.chat-channel:hover { background: var(--gemboss-hover); }
.chat-channel[aria-current="true"] { background: var(--gemboss-hover); font-weight: 600; }
/* Hàng kênh: tên co lại, số chưa đọc KHÔNG co. Không có `flex: none` thì một tên dài đẩy con số ra
   khỏi hàng và huy hiệu biến mất đúng ở kênh đang ồn nhất. */
.chat-channel { display: flex; align-items: center; gap: 8px; }
.chat-channel__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-channel__unread { flex: none; background: var(--gemboss-accent); color: var(--ui-accent-fg); }
/* Lời gọi tên dùng màu CẢNH BÁO, không phải màu nhấn của số chưa đọc: hai ý nghĩa khác nhau thì
   không được trông giống nhau (§9.3). Và nó có chữ "@" chứ không chỉ màu — màu một mình không đọc
   được với người mù màu, và cũng không đọc thành lời được. */
.chat-channel__calls { flex: none; background: var(--gemboss-warning); color: var(--gemboss-text-strong); font-weight: 700; }

/* Tin gọi đích danh mình: một vạch bên trái, KHÔNG phải nền màu. Nền màu trên cả khối tin làm chữ
   khó đọc và tranh chấp với mọi trạng thái khác của hàng. */
.chat-msg--calls-me { border-left: 2px solid var(--gemboss-warning); padding-left: 8px; }

.chat-quote {
  display: flex; align-items: baseline; gap: 8px; width: 100%; text-align: left; cursor: pointer;
  /* 44px, không phải 24px. Đây là một <button> thật — bấm vào là xem tin gốc, nhảy tới nó nếu nó
     đang trong danh sách, mở luồng nếu không — nên nó thuộc tầng ĐIỀU KHIỂN của §9.1, không phải
     tầng "link trong danh sách". Giá phải trả là mỗi tin trả lời cao thêm ~20px; đổi lại nó bấm
     trúng được bằng ngón tay. */
  min-height: 44px; padding: 2px 8px; margin-bottom: 2px; border: 0; border-left: 2px solid var(--gemboss-divider);
  background: none; font: inherit; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted);
}
.chat-quote:hover { background: var(--gemboss-hover); }
/* Đang tra luồng. Nạp xong mới mở hộp thoại (xem `openChatThread`), nên chỗ báo "đang chạy"
   phải nằm ở chính cái nút vừa bấm — nếu không, cú bấm trông như không ăn. */
.chat-quote[aria-busy="true"] { opacity: .55; cursor: progress; }
.chat-quote__who { flex: none; font-weight: 600; }
/* Đoạn trích PHẢI cắt bằng ellipsis: một tin gốc dài 120 ký tự mà xuống dòng thì dòng trích cao hơn
   chính tin trả lời nó. */
.chat-quote__text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Hộp thoại luồng: tin gốc rồi tới các trả lời. Không dùng lại `.chat-msg` — hàng ở đây không có
   nút Reply, không có thẻ link, không có trạng thái "gọi tên tôi", nên vay nét của nó chỉ tạo ra một
   chủ sở hữu thị giác thứ hai cho cùng một hình dạng (§9.3). */
.thread-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.thread-msg { border-left: 2px solid var(--gemboss-divider); padding-left: 8px; }
.thread-msg--root { border-left-color: var(--gemboss-accent); }
.thread-msg__who {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
  font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted);
}
.thread-msg__tag { font-weight: 600; }
/* Tin trong luồng KHÔNG cắt bằng ellipsis — người mở hộp này mở nó để đọc hết câu. */
.thread-msg__body { white-space: pre-wrap; overflow-wrap: anywhere; }

/* Ô "bỏ khỏi báo cáo" trong ngăn chi tiết. Xếp dọc và cho khoảng thở: ô chọn cạnh nút trên một hàng
   ngang sẽ tràn ở khổ 320, và ngăn này vốn đã hẹp. */
.d-exclusion { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
/* Trạng thái "đã bỏ" phải ĐỌC RA ĐƯỢC, không chỉ đổi màu: một hội thoại đã bỏ trông y hệt hội thoại
   bình thường là cách người thứ hai tưởng số liệu sai. */
.d-exclusion .d-excluded { margin: 0; font-weight: 600; }
.d-exclusion .ui-select, .d-exclusion .ui-btn { width: 100%; }

/* Menu Hoãn là `details.ui-menu.ui-menu--end` của gói (khung, mục 44px, bấm ra ngoài / Escape qua `initMenus`). Ở đây chỉ
   nhãn của nút khi đang hoãn và chỗ đặt tấm menu ở khổ hẹp. */
/* Đang hoãn: "☾ Until Mon 09:03 PM" (hay "☾ No reminder") ở MỌI khổ — cùng dấu ☾ với chip ở hàng danh
   sách, nên nút tự nói "hoãn" bằng mắt; trình đọc màn hình nghe chữ "Snoozed," ẩn đi trước nó. */
.snooze > summary.is-snoozed { white-space: normal; text-align: left; }
.snooze-moon { margin-right: 4px; }
/* Trên 900px: neo vào MÉP PHẢI của nút, mở sang trái (`ui-menu--end` của gói). Neo `left: 0` thì ở 901px menu chạy tới
   x 971 trên khung 901: trang CUỘN NGANG. Mép phải nút Snooze luôn ở x ≥ 452, nên mở sang trái không chạm mép trái. */
/* Ở khổ hẹp menu GHIM VÀO KHUNG NHÌN, không neo vào nút.
 *
 * Hai lần thử neo vào nút đều hỏng, và hỏng ở hai phía khác nhau — vì vị trí cái nút ĐỔI theo khổ:
 *   `right: 0`  → ở 320px menu nằm ở left -48 (48px ngoài mép TRÁI, bị cắt, trang không cuộn nên
 *                 phép đo "tràn ngang" một mình không thấy gì)
 *   `left: 0`   → ở 768px menu chạy tới 872 (104px ngoài mép PHẢI, và trang CUỘN NGANG)
 * Chọn phía nào cũng sai ở phía kia. Cái sai là neo vào một mốc di động.
 *
 * `position: fixed` + bốn mép của khung nhìn thì không phụ thuộc nút nằm đâu. Dựng thành một tấm
 * dưới đáy — mẫu quen trên điện thoại, và ngón tay với tới dễ hơn một menu bám mép trên. */
/* `.snooze > .snooze-menu` (0,2,0), đứng sau gói: thắng `.ui-menu--end .ui-menu__list` (0,2,0) của gói nhờ thứ tự. */
@media (max-width: 900px) {
  .snooze > .snooze-menu {
    position: fixed; top: auto; left: 16px; right: 16px; bottom: 16px; width: auto; min-width: 0;
  }
}

/* ── Ảnh đại diện ───────────────────────────────────────────────────────────────────────────────
   Một khối, một biến `--av` cho kích thước, dùng ở hàng tin (36), rail (32) và panel You (72).
   Ô chữ cái đầu là một ô ĐẦY ĐỦ chứ không phải nền trống: hàng có ảnh và hàng không có phải cùng
   một mép chữ, nếu không thì cả danh sách đọc ra như hai danh sách chồng lên nhau. */
.av {
  --av: 36px;
  width: var(--av); height: var(--av); flex: none;
  display: inline-flex; border-radius: 50%; overflow: hidden;
  /* `overflow: hidden` cắt ảnh theo hình tròn, nhưng Safari vẫn vẽ góc vuông chồng lên khi phần tử
     nằm trong một stacking context đang transform. `isolation` dựng một context riêng, và đó là
     cách rẻ nhất để hình tròn là hình tròn ở mọi trình duyệt. */
  isolation: isolate;
}
.av__img { width: 100%; height: 100%; object-fit: cover; display: block; }
.av__initials {
  width: 100%; height: 100%; display: inline-flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: calc(var(--av) * .38); line-height: 1; letter-spacing: .02em;
  /* Màu suy từ tên (xem `initialsNode`). Sắc ĐỘ cố định, chỉ đổi sắc MÀU qua 12 dải `.av--hN`.
     ĐO trong Chromium 26/09/2026, tỉ lệ tương phản WCAG của chữ trên nền, cả 12 dải:
       h0 8,78 · h1 6,86 · h2 4,75 · h3 5,21 · h4 5,43 · h5 5,35
       h6 5,18 · h7 7,93 · h8 10,87 · h9 9,75 · h10 8,01 · h11 8,52
     Thấp nhất 4,75:1 (h2, 60° vàng), trên ngưỡng AA 4,5:1 cho chữ thường. Tôi từng viết ở đây là
     "~7,9:1 ở mọi góc màu" — tính trên giấy, và sai: độ sáng HSL không phải độ sáng mắt người thấy,
     nên dải vàng–lục thấp hơn dải lam–tím gấp đôi. Ai đổi hai con số 92%/28% này thì ĐO LẠI cả 12,
     h2 là dải sát ngưỡng nhất. */
  background: hsl(var(--av-hue, 220) 45% 92%);
  color: hsl(var(--av-hue, 220) 55% 28%);
}
/* Kích thước: MỘT tập class cố định, vì CSP `style-src 'self'` chặn `style` inline (xem avatarNode). */
.av--s20 { --av: 20px; }
.av--s32 { --av: 32px; }
.av--s36 { --av: 36px; }
.av--s72 { --av: 72px; }
/* Mười hai dải màu. Sắc ĐỘ cố định ở .av__initials, chỉ đổi sắc MÀU ở đây — nên tương phản của cả
   mười hai là cùng một phép tính (xem chú thích ở .av__initials) và đã được đo trong trình duyệt. */
.av--h0 { --av-hue: 0; }   .av--h1 { --av-hue: 30; }  .av--h2 { --av-hue: 60; }   .av--h3 { --av-hue: 90; }
.av--h4 { --av-hue: 120; } .av--h5 { --av-hue: 150; } .av--h6 { --av-hue: 180; }  .av--h7 { --av-hue: 210; }
.av--h8 { --av-hue: 240; } .av--h9 { --av-hue: 270; } .av--h10 { --av-hue: 300; } .av--h11 { --av-hue: 330; }
/* Chỗ giữ cho tin được gộp: giữ CỘT, không giữ CHIỀU CAO.
   Bản đầu là `visibility: hidden` với nguyên 36×36, và đo được hệ quả: hàng gộp chỉ có một dòng chữ
   20px nhưng cao 36px, nên câu thứ ba của một mạch cách câu thứ hai 40px trong khi câu thứ hai cách
   câu đầu 24px — một mạch ba câu mà nhịp gãy ở giữa. Cột đã được `grid-template-columns: 36px` giữ
   sẵn, nên ô này chỉ cần chiếm chỗ trong cột, không cần cao. */
.av--spacer { visibility: hidden; height: 0; }

/* Hàng tin có HAI cột: ảnh, rồi mọi thứ còn lại. `minmax(0, 1fr)` chứ không `1fr` — một grid item
   mặc định không co dưới bề rộng nội dung, nên một URL dài không ngắt sẽ đẩy cả cột ra ngoài. */
.chat-msg { position: relative; display: grid; grid-template-columns: 36px minmax(0, 1fr); gap: 8px; align-items: start; }
/* ĐẶT CHỖ TƯỜNG MINH, không để lưới tự xếp.
 *
 * Ở màn rộng `.chat-acts` là `absolute` nên nó ra khỏi lưới, và tự xếp cho ra đúng: ảnh cột 1, thân
 * cột 2. Ở khổ hẹp và màn cảm ứng nó thôi `absolute` (để không đè chữ) — tức nó thành một Ô LƯỚI,
 * chiếm cột 2 ngay sau ảnh, và đẩy `.chat-msg__main` xuống dòng dưới, vào CỘT 1 rộng 36px. Ảnh chụp
 * 390px cho thấy đúng thế: "Mình vừa đặt ảnh đại diện" mỗi dòng một chữ, và "người" bị bẻ thành
 * "ngườ / i". Mọi check của cổng thị giác đều xanh cho bản đó.
 * (Từ 28/09/2026 thanh nút nằm TRONG `.chat-msg__main` nên không còn thành ô lưới được nữa; đặt chỗ tường minh vẫn
 * giữ, để con thứ ba nào sau này cũng không đẩy thân tin vào cột ảnh.) */
.chat-msg > .av { grid-column: 1; grid-row: 1; }
.chat-msg > .chat-msg__main { grid-column: 2; grid-row: 1; min-width: 0; }
.chat-msg__main { min-width: 0; }
/* Tin được gộp nằm sát tin trên: chúng là MỘT lượt nói. `.chat-messages` đặt gap 12px cho mọi hàng,
   nên phải kéo ngược lại ở đây. */
.chat-msg--grouped { margin-top: -8px; }
.chat-msg__who { display: flex; align-items: center; gap: 8px; }
/* Thanh nút nổi của một tin — kiểu Lark / Slack (chatActs trong inbox.js).
 *
 * Bản trước neo thanh vào góc phải của HÀNG, mà hàng rộng bằng cả cột: rê chuột lên tin "ok" thì thanh hiện cách chữ
 * 789px ở khổ 1280 và 1429px ở 1920, và hàng không sáng nền — không gì nối thanh với tin (đo 28/09/2026). Giờ:
 *   · hàng sáng nền khi rê chuột / focus bàn phím — mắt biết đang đứng ở tin nào;
 *   · khối nội dung co theo chữ (`fit-content`) và chừa sẵn chỗ bên phải (`--chat-acts-w`), thanh đứng NGAY sau chữ,
 *     nhô lên mép trên một chút như Slack; tin dài chạm trần bề rộng thì thanh vẫn có chỗ, không đè lên chữ;
 *   · khung hẹp (cửa sổ chat nổi 340px, màn nhỏ) thì container query đặt thanh ở góc trên phải của HÀNG.
 * Nút vẫn 44px (§9.1 — điều khiển); thanh `absolute` nên không cộng chiều cao nào vào hàng. `pointer-events: none`
 * lúc ẩn: một nút trong suốt vẫn nhận cú bấm là nút bẫy (#82). `:focus-within` để đi Tab qua các nút giữ thanh hiện. */
.chat-messages { container-type: inline-size; --chat-acts-w: 272px; }
.chat-msg { padding: 4px 12px; margin-inline: -12px; border-radius: var(--gemboss-radius-md); }
.chat-msg:hover, .chat-msg:focus-within, .chat-msg--picking { background: var(--gemboss-hover); }
.chat-msg > .chat-msg__main { position: relative; justify-self: start; width: fit-content; max-width: calc(100% - var(--chat-acts-w)); }
.chat-acts {
  position: absolute; top: -8px; left: calc(100% + 8px); z-index: 2; opacity: 0; pointer-events: none;
  display: flex; align-items: center; gap: 2px; padding: 2px;
  border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md);
  background: var(--gemboss-surface); box-shadow: var(--gemboss-shadow-resting); white-space: nowrap;
}
.chat-act {
  min-width: 44px; min-height: 44px; padding: 0 10px;
  border: 0; border-radius: var(--gemboss-radius-sm); background: transparent; cursor: pointer; font: inherit;
  font-size: var(--gemboss-fs-caption); color: var(--gemboss-accent);
}
.chat-act--emoji { padding: 0; font-size: 18px; }
.chat-act:hover { background: var(--gemboss-hover); }
.chat-act[aria-pressed="true"] { background: var(--gemboss-primary-soft); }
.chat-msg > .chat-msg__hover-time {
  grid-column: 1; grid-row: 1; align-self: center; justify-self: end; opacity: 0;
  font-size: 11px; line-height: 1; color: var(--gemboss-text-muted); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.chat-msg:hover > .chat-msg__hover-time, .chat-msg:focus-within > .chat-msg__hover-time { opacity: 1; }
.chat-msg:hover .chat-acts,
.chat-acts:focus-within,
.chat-msg--picking .chat-acts { opacity: 1; pointer-events: auto; }
@container (max-width: 640px) {
  .chat-msg > .chat-msg__main { position: static; max-width: 100%; }
  .chat-acts { left: auto; right: 4px; top: -14px; }
}
/* Thiết bị KHÔNG có hover, ở MỌI khổ — không chỉ khổ hẹp.
 *
 * Luật cũ chỉ cứu thanh nút dưới 900px, nên một iPad ngang (1024, cảm ứng) vẫn rơi vào nhánh
 * "hiện khi rê chuột" mà ở đó không có con trỏ nào để rê. Bề rộng là một phép đoán về thiết bị;
 * `hover: none` là chính câu hỏi cần hỏi. Để SAU luật base nên nó thắng mà không cần `!important`. */
@media (hover: none) {
  /* Thôi nổi ⇒ nằm trong luồng, DƯỚI nội dung tin, cùng cột với chữ. */
  .chat-msg > .chat-msg__main { max-width: 100%; }
  .chat-acts { position: static; opacity: 1; pointer-events: auto; margin-top: 4px; width: fit-content; }
  /* Luôn hiện dưới MỌI tin thì chỉ giữ ☺＋ và Reply: năm nút ở mỗi tin là một hàng nút nặng hơn cả chữ, và bảng
     chọn vẫn có đủ tám biểu tượng. */
  .chat-act--emoji { display: none; }
  .chat-msg__who { flex-wrap: wrap; }
}

/* Bảng chọn biểu tượng. Nằm TRONG luồng (không `absolute`): một lớp nổi trong một cột cuộn phải tự
   lo chuyện bị mép dưới cắt, còn một hàng trong luồng thì trình duyệt tự cuộn tới. */
.chat-picker {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0;
  padding: 4px; border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md);
  background: var(--gemboss-surface); box-shadow: var(--gemboss-shadow-resting); width: fit-content; max-width: 100%;
}
.chat-picker__btn {
  min-width: 44px; min-height: 44px; padding: 0;
  border: 1px solid transparent; border-radius: var(--gemboss-radius-sm);
  background: none; cursor: pointer; font-size: 20px; line-height: 1;
}
.chat-picker__btn:hover { background: var(--gemboss-hover); }
.chat-picker__btn[aria-pressed="true"] { border-color: var(--gemboss-accent); background: var(--gemboss-primary-soft); }

/* Chip đã thả.
 *
 * Hộp bấm cao 44px theo ngưỡng ĐIỀU KHIỂN của §9.1, nhưng viên thuốc NHÌN THẤY chỉ 26px: phần đệm
 * trong suốt gánh phần còn lại. Làm cả viên thuốc cao 44px thì mỗi tin có cảm xúc dày thêm gần một
 * dòng tin, và số tin đọc được trong một màn tụt xuống — cùng phép đánh đổi mà `.chat-reply` đã cân
 * một lần ở #87, chỉ khác là ở đây giải pháp là đệm chứ không phải cho nút nổi lên. */
.chat-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 2px; }
.chat-chip {
  display: inline-flex; align-items: center; gap: 4px;
  min-height: 44px; min-width: 44px; padding: 9px 0; border: 0; background: none; cursor: pointer; font: inherit;
}
.chat-chip__emoji, .chat-chip__n {
  display: inline-flex; align-items: center; height: 26px;
  border: 1px solid var(--gemboss-divider); background: var(--gemboss-surface);
}
.chat-chip__emoji { padding: 0 4px 0 6px; border-right: 0; border-radius: 999px 0 0 999px; font-size: 14px; }
.chat-chip__n { padding: 0 8px 0 4px; border-left: 0; border-radius: 0 999px 999px 0;
  font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); font-variant-numeric: tabular-nums; }
.chat-chip:hover .chat-chip__emoji, .chat-chip:hover .chat-chip__n { background: var(--gemboss-hover); }
/* Cảm xúc CỦA MÌNH phải đọc ra được mà không cần so sánh với cái bên cạnh — viền và nền, không chỉ
   màu chữ: một người mù màu nhìn hai chip chỉ khác nhau ở sắc độ thì thấy hai chip giống hệt nhau. */
.chat-chip.is-mine .chat-chip__emoji, .chat-chip.is-mine .chat-chip__n {
  border-color: var(--gemboss-accent); background: var(--gemboss-primary-soft); color: var(--gemboss-accent);
}
/* Thẻ tóm tắt của một link dán trong chat. Cả thẻ là một điều khiển (bấm vào là mở hội thoại/task),
   nên 44px theo §9.1 — và `flex-wrap` để ở 320px nó xuống dòng thay vì bóp chữ. */
.link-card {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  /* Có TRẦN chiều ngang: kéo hết cột thì nó đọc ra như một dải phân cách, không ra một tấm thẻ —
     và ở 1920 cái "dải" đó dài 1,5m chữ với khoảng trống ở giữa. */
  min-height: 44px; max-width: 560px; margin-top: 4px; padding: 4px 12px;
  border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md);
  background: var(--gemboss-surface); color: var(--gemboss-text); text-decoration: none;
  font-size: var(--gemboss-fs-caption);
}
.link-card:hover { background: var(--gemboss-hover); }
.link-card__kind { flex: none; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--gemboss-text-muted); }
/* Tiêu đề co được và cắt bằng ellipsis; `min-width: 0` vì một flex item mặc định không co dưới bề
   rộng nội dung, và không có nó thì một tiêu đề dài đẩy trạng thái ra khỏi thẻ. */
.link-card__title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; color: var(--gemboss-text-strong); }
.link-card__sub { flex: none; color: var(--gemboss-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; }
.link-card__status { flex: none; }
.chat-msg__body[data-mid].is-flash { background: var(--gemboss-primary-soft); border-radius: var(--gemboss-radius-sm); }

.chat-replying {
  display: flex; align-items: center; gap: 8px; padding: 4px 8px;
  border-left: 2px solid var(--gemboss-accent); background: var(--gemboss-primary-soft);
  border-radius: var(--gemboss-radius-sm);
}
.chat-replying__text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.chat-replying button { flex: none; }
.chat-rail__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
/* Ngưỡng ĐIỀU KHIỂN §9.1: 44px cả hai chiều. Một nút "+" 24px là nút chỉ chuột mới bấm trúng. */
.chat-new { font-size: var(--gemboss-fs-h3); line-height: 1; }

.chat-new-form { display: grid; gap: 12px; }
.chat-new-form label { display: grid; gap: 6px; font-weight: 500; }
/* Danh sách người phải CUỘN, không đẩy hai nút cuối hộp thoại ra khỏi màn: một đội 30 người thì
   nút Start rơi khỏi màn hình đầu, và đó là §9.1 đỏ (CTA rơi khỏi màn đầu). */
.chat-people { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; max-height: 40vh; overflow-y: auto; }
.chat-person { width: 100%; min-height: 44px; display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 0 12px; border: 0; border-radius: var(--gemboss-radius-md); background: none; cursor: pointer;
  font: inherit; color: var(--gemboss-text); text-align: left; }
.chat-person:hover { background: var(--gemboss-hover); }
.chat-person[aria-pressed="true"] { background: var(--gemboss-primary-soft); font-weight: 600; }
.chat-person__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-person__tick { flex: none; color: var(--gemboss-accent); font-weight: 700; }
.chat-main { display: grid; grid-template-rows: auto minmax(0, 1fr) auto auto; min-height: 0; }
.chat-head { padding: 16px; border-bottom: 1px solid var(--gemboss-divider); }
.chat-head h3 { margin: 0; }
.chat-head p { margin: 4px 0 0; }
.chat-messages { list-style: none; margin: 0; padding: 16px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; }
/* Vạch "New messages": cùng màu nhấn với số chưa đọc ở rail (cùng một ý nghĩa), chữ thật ở giữa. */
/* KHÔNG đặt tên `.chat-new`: đó là nút "+" tạo kênh — dùng chung tên là nút ấy mang vạch và màu này (§9.3). */
.chat-unread-line { display: flex; align-items: center; gap: 8px; color: var(--gemboss-accent); font-size: 12px; font-weight: 600; }
.chat-unread-line::before, .chat-unread-line::after { content: ""; flex: 1; height: 1px; background: currentColor; }
.chat-msg__who { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.chat-msg__body { white-space: pre-wrap; overflow-wrap: anywhere; }
.chat-msg__edited { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
/* Tin đang sửa: khối nội dung trải hết hàng. Bình thường nó `fit-content` (để thanh nút đứng sát tin), nên ô sửa của
   một câu ngắn chỉ rộng bằng câu ấy — đo được 188px ở khổ 1280. Lưới: `width: auto` với `justify-self: start` vẫn co
   theo nội dung, nên phải `stretch`. */
.chat-msg--editing > .chat-msg__main { justify-self: stretch; width: auto; max-width: none; }
.chat-edit { display: flex; flex-direction: column; gap: 6px; }
.chat-edit__row { display: flex; gap: 8px; justify-content: flex-end; }
.chat-confirm { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 4px; font-size: var(--gemboss-fs-caption); }
/* Lưới HAI HÀNG, không phải một hàng flex.
 *
 * `.chat-replying` là con thứ ba của form. Để form là `display: flex` thì nó thành một cột thứ ba
 * NẰM CẠNH ô nhập và nút Send — ở 390px, ô nhập bị bóp còn ~20px, chữ "Message" xuống dòng từng chữ
 * cái, và danh sách tin trượt ngang. Ảnh chụp 390 lộ ra; phép đo tràn-ngang của TÀI LIỆU vẫn 0 vì
 * cái trượt nằm trong một khối có `overflow` riêng.
 *
 * Dòng "đang trả lời" trải hết hàng trên; ô nhập và Send ở hàng dưới. */
.chat-composer {
  /* Ba cột: 📎 · ô nhập · Send. Khay ảnh, dòng "đang trả lời" và nhãn ẩn trải hết cả ba (1 / -1). */
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 8px; align-items: end;
  padding: 16px; border-top: 1px solid var(--gemboss-divider);
}
.chat-files { grid-column: 1 / -1; margin: 0; }
.chat-attach { font-size: 18px; }
/* Khổ HẸP: ô nhập một hàng riêng, 📎 và Send xuống hàng dưới.
 *
 * Ba cột cạnh nhau ở 320px cho ô nhập ~105px — đo trên ảnh: chữ gợi ý gãy thành bốn dòng, và gõ một
 * câu trong một ô hẹp chừng ấy là đọc lại chính chữ mình qua một khe. Không check nào của cổng thị
 * giác đỏ cho nó (không tràn, không cắt — chỉ là chật), nên ngưỡng ở đây đặt theo ảnh chụp, không
 * theo một phép đo. Hai nút vẫn 44px và cách nhau hơn 8px. */
@media (max-width: 480px) {
  /* `order`, không `grid-template-areas`: vùng đặt tên thì hàng của khay ảnh và dòng "đang trả lời"
     VẪN tồn tại khi chúng ẩn, và mỗi hàng rỗng vẫn ăn một `gap` 8px — ô soạn phình ra 16px khoảng
     trắng ở trên mà không có gì trong đó. `order` chỉ đổi thứ tự tự xếp, không đẻ hàng. */
  .chat-composer .ui-textarea { grid-column: 1 / -1; order: 1; }
  .chat-attach { grid-column: 1; order: 2; }
  #chat-send { grid-column: 3; order: 2; }
}
/* Đang kéo ảnh vào khung tin: nói ra là thả được ở đây. Viền NÉT ĐỨT chứ không chỉ đổi màu nền —
   một nền nhạt hơn một chút là thứ mắt không bắt được giữa lúc tay đang kéo. */
.chat-main[data-dropping] { outline: 2px dashed var(--gemboss-accent); outline-offset: -8px; }
/* Ảnh trong hàng tin: dùng lại `.msg-files`/`.file-thumb` của tin khách (xem `attachmentNodes`). Tin
   chat luôn căn TRÁI — không có "tin của mình nằm bên phải" như khung khách — nên ghi đè đúng một
   thuộc tính, không định nghĩa lại khối. */
.chat-msg .msg-files { justify-content: flex-start; margin-top: 4px; }
.chat-composer .ui-textarea { min-width: 0; }
.chat-replying { grid-column: 1 / -1; }
.chat-composer .ui-sr-only { grid-column: 1 / -1; }
#chat-error { margin: 0 16px 12px; }

@media (max-width: 900px) {
  /* Cột kênh thành một hàng cuộn ngang — ẩn hẳn nó là cách một màn hình mất lối vào ở điện thoại. */
  .chat-screen { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
  .chat-rail { border-right: 0; border-bottom: 1px solid var(--gemboss-divider); padding: 8px 16px; }
  .chat-rail ul { flex-direction: row; overflow-x: auto; gap: 8px; }
  .chat-channel { width: auto; white-space: nowrap; padding: 0 16px; }

  /* Thanh nút KHÔNG còn thôi nổi theo BỀ RỘNG ở đây (28/09/2026). Hai lý do cũ đã có chỗ đúng hơn: khung hẹp thì
   container query đặt thanh ở góc trên phải của HÀNG (không đè chữ); màn cảm ứng thì `@media (hover: none)`. Luật
   cũ ở đây ép thanh 5 nút thành một hàng tĩnh 242px dưới mỗi tin, và ở 320px nó tràn khỏi cột tin (cuộn ngang). */
  .chat-msg__who { flex-wrap: wrap; }
}

/* Danh sách việc: cả hàng bấm được, nên nó phải TRÔNG bấm được — con trỏ và nền khi rê chuột.
   Nút sắp xếp ở tiêu đề cao 44px theo cùng ngưỡng điều khiển của §9.1. */
/* Cả hàng là một điều khiển (`role="button"`), nên nó theo ngưỡng 44px của §9.1 chứ không phải ngưỡng
   24px của "link trong danh sách". Đo trước khi sửa: 37px ở ≥768. */
#board-list .ui-table tr[role="button"] { cursor: pointer; }
#board-list .ui-table tbody td { padding-block: 12px; }
#board-list .ui-table tbody tr:hover td,
#board-list .ui-table tbody tr:focus-visible td { background: var(--gemboss-hover); }
/* Nút sắp xếp của BẤT KỲ bảng nào, không chỉ `#board-list`. Bảng theo người ở màn Reports cũng
   dùng `.th-sort`, và khi luật còn scope theo id thì nó mất cả kiểu chữ lẫn SÀN 44px — mã có, đúng,
   và không áp vào đâu cả. */
.ui-table .th-sort { min-height: 44px; min-width: 44px; padding: 0 8px; font: inherit; font-weight: 600; }
/* Việc nội bộ đọc khác việc của khách ngay ở cột Kind — chữ, không chỉ màu. */
/* Chọn theo TÊN CỘT, không theo `nth-child`: tôi vừa đếm sai một lần khi chèn hai cột vào giữa, và
   một chỉ số cứng sẽ hỏng lặng lẽ mỗi lần ai đó đổi thứ tự cột. */
#board-list .ui-table tr[data-kind="internal"] td.col-kind { color: var(--gemboss-text-muted); }
/* Hàng trễ hạn: chỉ tô Ô HẠN, không tô cả hàng — tô cả hàng làm mọi cột trông như đang báo động, và
   người ta học cách bỏ qua màu đỏ. Chữ "· 3h late" trong ô mới là thứ nói ra sự thật. */
#board-list .ui-table tr[data-late="true"] td.col-due { color: var(--gemboss-danger); font-weight: 600; }

/* Ưu tiên và trễ hạn trên thẻ kanban. Chữ trước, màu sau: `Urgent` và `Overdue` đọc được kể cả khi
   không phân biệt được màu — màu chỉ để mắt bắt nhanh hơn. */
.rq-prio { font-weight: 600; }
.rq-prio[data-p="urgent"] { color: var(--gemboss-danger); }
.rq-prio[data-p="high"] { color: var(--gemboss-text); }
.rq-prio[data-p="low"] { color: var(--gemboss-text-muted); font-weight: 400; }
.rq-late { color: var(--gemboss-danger); font-weight: 600; }
#rq-due-note[data-late="true"] { color: var(--gemboss-danger); }

/* "Right now" trên màn Shifts: mỗi hàng là một người ĐÁNG LẼ đang làm, và cái nhãn nói họ đã vào chưa.
   Màu chỉ là lớp thứ hai — chữ "not checked in" phải đọc được kể cả khi không phân biệt được màu. */
.shift-now { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.shift-now li { display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 12px;
  border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-md); }
.shift-now li[data-in="false"] { border-color: var(--gemboss-danger); }
.shift-flag { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.shift-now li[data-in="false"] .shift-flag { color: var(--gemboss-danger); }

/* Sổ sự kiện trong ngăn chi tiết: mốc thời gian cùng cỡ nhưng nhạt hơn câu chữ, để mắt đi theo VIỆC
   trước rồi mới tới GIỜ — người trực cần biết chuyện gì xảy ra, giờ chỉ là để đối chiếu. */
.events { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.events li { display: flex; flex-direction: column; gap: 2px; font-size: var(--gemboss-fs-caption); }
.events-when { color: var(--gemboss-text-muted); font-variant-numeric: tabular-nums; }

/* Công tắc chính sách, dưới cùng thẻ giờ làm việc: một đường kẻ tách nó khỏi form giờ vì nó lưu NGAY
   khi bấm, không chờ nút Save ở trên. Hàng ≥44px theo cùng ngưỡng với .hours-days label. */
.policy { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--gemboss-divider); }
/* `ui-check` của gói, nhưng nhãn hai dòng (tiêu đề + giải thích) nên hộp bám dòng đầu chứ không căn giữa. */
.policy-row { align-items: flex-start; gap: 12px; }
.policy-row input { margin: 12px 0 0; }
.policy-row > span { display: flex; flex-direction: column; gap: 2px; padding: 12px 0; }
.policy-help { display: block; max-width: 62ch; }
#duty-policy-state { margin: 8px 0 0; }


/* ── Panel "You" ────────────────────────────────────────────────────────────────────────────── */
.me-card { display: flex; align-items: center; gap: 12px; margin: 12px 0; }
.me-card__who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.me-card__who strong { color: var(--gemboss-text-strong); }
.me-card__who span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.me-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
/* Đổi mật khẩu: một cột, mỗi ô chiếm trọn chiều ngang panel — panel hẹp (drawer small), không chia hàng. */
.me-alerts { display: flex; flex-direction: column; gap: 8px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--gemboss-divider); }
.me-alerts .me-actions { margin-top: 0; }
#me-notify[aria-pressed="true"], #me-sound[aria-pressed="true"] { border-color: var(--gemboss-accent); color: var(--gemboss-accent); }
.me-password { display: flex; flex-direction: column; gap: 10px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--gemboss-divider); }
.me-password__title { margin: 0; font-size: 14px; color: var(--gemboss-text-strong); }
.me-password__hint { margin: 0; }
.me-password .closure-field { width: 100%; }
.me-password .ui-btn { align-self: flex-start; }
.staff-actions { display: flex; flex-wrap: wrap; gap: 8px; }
/* `<label for>` làm nút, vì `<input type=file>` trần không đạt 44px và không nhận được kiểu của hệ
   thiết kế. Nó phải TRÔNG và CHẠM như một nút: con trỏ, chiều cao, và một vòng focus thật — vòng
   focus đi theo cái input đang ẩn, nên phải vẽ lại ở đây hoặc người dùng bàn phím không thấy mình
   đang đứng ở đâu. */
.me-upload { cursor: pointer; }
#me-avatar-file:focus-visible + .me-upload,
.me-upload:has(+ #me-avatar-file:focus-visible) { outline: 2px solid var(--gemboss-accent); outline-offset: 2px; }

/* ── Dock chat đội (cửa sổ nổi kiểu LinkedIn) ─────────────────────────────────────────────────
   Neo ở góc dưới phải, `fixed` nên ra khỏi lưới của #app. Thùng chứa `pointer-events: none` để
   khoảng trống GIỮA các cửa sổ không nuốt cú bấm vào hộp thư phía sau — chỉ bản thân thanh và cửa
   sổ nhận bấm. Không có dòng đó thì một dải 8px vô hình phía trên hộp thư sẽ chặn mọi cú bấm. */
.dock {
  position: fixed; right: 16px; bottom: 0; z-index: 40;
  display: flex; align-items: flex-end; gap: 8px; pointer-events: none;
}
.dock[hidden] { display: none; }
.dock-windows { display: flex; align-items: flex-end; gap: 8px; }
.dock-panel, .dock-win {
  pointer-events: auto; background: var(--gemboss-surface);
  border: 1px solid var(--gemboss-divider); border-bottom: 0;
  border-radius: var(--gemboss-radius-md) var(--gemboss-radius-md) 0 0;
  box-shadow: 0 -2px 16px rgba(0, 0, 0, .08);
}
.dock-panel { width: 280px; }
.dock-bar {
  width: 100%; min-height: 44px; display: flex; align-items: center; gap: 8px; padding: 0 12px;
  border: 0; background: none; cursor: pointer; font: inherit; font-weight: 600; color: var(--gemboss-text-strong);
}
.dock-bar__title { flex: 1; text-align: left; }
.dock-bar__chev { color: var(--gemboss-text-muted); transition: transform .14s ease; }
.dock[data-open="1"] .dock-bar__chev { transform: rotate(180deg); }
.dock-list { list-style: none; margin: 0; padding: 4px 0; max-height: 360px; overflow-y: auto; border-top: 1px solid var(--gemboss-divider); }
.dock-channel {
  width: 100%; min-height: 44px; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 0 12px; border: 0; background: none; cursor: pointer; font: inherit; color: var(--gemboss-text); text-align: left;
}
.dock-channel:hover { background: var(--gemboss-hover); }
.dock-channel[aria-current="true"] { font-weight: 600; }
.dock-channel__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dock-empty { padding: 12px; }

/* Một cửa sổ: đầu · khung tin · lỗi · ô soạn. `minmax(0, 1fr)` cho khung tin — không có nó thì một
   kênh dài đẩy ô soạn ra khỏi đáy màn hình thay vì cuộn bên trong. */
.dock-win {
  width: 340px; height: min(460px, calc(100vh - 96px));
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto auto;
}
.dock-win[data-minimized="1"] { height: auto; }
.dock-win[data-minimized="1"] > :not(.dock-win__head) { display: none; }
.dock-win__head { display: flex; align-items: center; border-bottom: 1px solid var(--gemboss-divider); }
.dock-win[data-minimized="1"] .dock-win__head { border-bottom: 0; }
.dock-win__toggle {
  flex: 1; min-width: 0; min-height: 44px; display: flex; align-items: center; gap: 8px; padding: 0 4px 0 12px;
  border: 0; background: none; cursor: pointer; font: inherit; font-weight: 600; color: var(--gemboss-text-strong); text-align: left;
}
.dock-win__title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dock-win__btn {
  flex: none; min-width: 44px; min-height: 44px; border: 0; background: none; cursor: pointer;
  font: inherit; color: var(--gemboss-text-muted); border-radius: var(--gemboss-radius-sm);
}
.dock-win__btn:hover, .dock-win__toggle:hover { background: var(--gemboss-hover); }
/* Khung tin DÙNG LẠI `.chat-messages` (cùng hàm vẽ, cùng hàng tin) — chỉ đệm nhỏ lại cho ô 340px. */
.dock-win__msgs { padding: 12px; }
.dock-win__error { margin: 0 12px 8px; }
.dock-win__composer {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: end;
  padding: 8px; border-top: 1px solid var(--gemboss-divider);
}
.dock-win__input { min-height: 44px; max-height: 120px; resize: none; }

/* Chế độ CỘT: đang mở một hội thoại khách ở màn ≥ 1100 — dock nằm GỌN trong cột phải (Customer / AI),
   rộng đúng bằng cột, xếp dọc. Nó che thông tin khách tạm thời, và KHÔNG BAO GIỜ che ô trả lời hay
   tin mới nhất của khách ở cột giữa. Xem `dockMode()` cho phép đo đã dẫn tới luật này.
   `right: 0` và bề rộng lấy từ CHÍNH biến của cột (`--details-w`), không gõ lại 320: cột đổi rộng
   thì dock đổi theo, không có con số thứ hai để trôi. */
.dock[data-mode="column"] {
  right: 0; width: var(--details-w, 320px);
  flex-direction: column; align-items: stretch; gap: 0;
  max-height: 100vh;
}
.dock[data-mode="column"] .dock-windows { flex-direction: column; align-items: stretch; gap: 0; }
.dock[data-mode="column"] .dock-win,
.dock[data-mode="column"] .dock-panel {
  width: 100%; border-radius: 0; border-left: 0; border-right: 0; box-shadow: none;
}
/* Một cửa sổ mở rộng mỗi lúc (xem `collapseOthers`), nên nó được chiều cao cố định: cao tối đa mà
   vẫn chừa ~40% cột cho thông tin khách phía trên — thông tin ấy bị che chứ không bị mất. */
.dock[data-mode="column"] .dock-win:not([data-minimized="1"]) { height: min(440px, 58vh); }
.dock[data-mode="column"] .dock-list { max-height: 30vh; }

/* Cột phải CHỪA đúng chiều cao dock ở đáy khi dock nằm trong nó (chế độ cột), để phần tử cuối — ô
   "Add a tag…" — cuộn lên được TRÊN dock thay vì nằm vĩnh viễn dưới nó. `--dock-h` là chiều cao ĐO
   bằng ResizeObserver (xem inbox.js), không phải một hằng số: dock cao 44px khi thu gọn, ~500px khi
   mở một cửa sổ. */
.app[data-dock="column"] .details {
  /* THU NGẮN khung cuộn, không ĐỆM nó.
   *
   * Bản trước đệm `padding-bottom` + `scroll-padding-bottom` bằng chiều cao dock. Nó làm phần cuối
   * cuộn TỚI ĐƯỢC — nhưng ở vị trí cuộn 0, thứ nằm dưới thanh dock vẫn bị đè: cổng thị giác báo
   * `content_covered` ở 1366 (một chip tag) và 1920 (một tiêu đề). Khung cuộn kết thúc NGAY TRÊN dock
   * thì dock không đè lên thứ gì bên trong nó, ở bất kỳ vị trí cuộn nào — và Tab tự cuộn đúng mà
   * không cần `scroll-padding`. */
  align-self: start;
  height: calc(100% - var(--dock-h, 0px));
}

/* Tin kênh đang gửi (vẽ ngay lúc bấm Enter — xem `guiTinKenh`): mờ đi một chút tới khi server nhận. Không làm mờ quá
   để còn đọc được câu vừa gõ; chữ "Sending…" nói lý do bằng lời, không chỉ bằng màu. */
.chat-msg--sending .chat-msg__main { opacity: .6; }
.chat-msg__sending { color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-micro); }

/* Lỗi của "Draft help article": cả một hàng dưới các nút, như .tech-hint. */
.t-help-draft-error { flex: 1 0 100%; margin: 0; }
.ai-draft { margin-left: 8px; }

/* "Questions answered by hand" (Reports). */
.r-repeat-row { display: flex; gap: 8px; align-items: center; margin-block: 8px; }
.r-repeat-ex { display: flex; flex-wrap: wrap; gap: 8px; }

/* AI review of closed conversations: ngăn hội thoại + Reports. */
.d-checks, .check-queue { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.check-item { display: grid; gap: 8px; padding-block: 12px; border-top: 1px solid var(--gemboss-divider); min-width: 0; }
.check-item:first-child { border-top: 0; padding-top: 0; }
.check-item > * { margin: 0; overflow-wrap: anywhere; }
.check-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.check-quote { border-left: 3px solid var(--gemboss-divider); padding-left: 8px; color: var(--gemboss-text-muted); }
.check-verdict { display: flex; flex-wrap: wrap; gap: 8px; }
.check-verdict .ui-btn[aria-pressed="true"] { font-weight: 600; box-shadow: inset 0 0 0 2px currentColor; }

/* Coaching signals (Reports). */
/* Câu hỏi kiểm tra (bước 6). Lựa chọn là nút cao 44px, chữ trái, xuống dòng được; kết quả nói bằng chữ VÀ bằng màu viền. */
.quiz { display: grid; gap: 12px; margin-top: 12px; }
.quiz-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.quiz-question { margin: 0; font-weight: 600; }
.quiz-progress { margin: 0; }
.quiz-options { display: grid; gap: 8px; }
.quiz-option { min-height: 44px; justify-content: flex-start; text-align: left; white-space: normal; height: auto; padding-block: 8px; }
/* Đã trả lời thì nút khoá, nhưng CHỮ không được mờ đi: người ta đang đọc lại chính các lựa chọn để hiểu vì sao sai. */
.quiz-option:disabled { opacity: 1; color: var(--gemboss-text); cursor: default; }
.quiz-option[data-result="right"] { border-color: var(--gemboss-success); box-shadow: inset 0 0 0 1px var(--gemboss-success); }
.quiz-option[data-result="wrong"] { border-color: var(--gemboss-danger); box-shadow: inset 0 0 0 1px var(--gemboss-danger); }
.quiz-feedback { display: grid; gap: 8px; }
.quiz-verdict { margin: 0; font-weight: 600; }
.quiz-verdict[data-result="right"] { color: var(--gemboss-success-strong); }
.quiz-verdict[data-result="wrong"] { color: var(--gemboss-danger-strong); }
.quiz-quote { margin: 0; padding-left: 12px; border-left: 3px solid var(--gemboss-divider); color: var(--gemboss-text-muted); overflow-wrap: anywhere; }
.coach-person { display: grid; gap: 8px; padding-block: 12px; border-top: 1px solid var(--gemboss-divider); }
.coach-person:first-child { border-top: 0; }
.coach-signal { margin: 0; }
.coach-ev { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.coach-quote { margin: 0; color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); flex: 1 1 200px; min-width: 0; overflow-wrap: anywhere; }

/* Bản dịch tin của khách (inbox.js `dichNode`). Bản gốc ở trên, bản dịch là khối phụ bên dưới: viền trái + nền nhạt,
   không phải một bong bóng thứ hai (nó không phải lời ai nói thêm). Nút cao 44px theo §9.1. */
.msg-dich { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; max-width: 100%; min-width: 0; }
.msg-dich-nhan { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.msg-dich-chu {
  padding: 6px 10px; border-left: 3px solid var(--gemboss-divider); background: var(--gemboss-hover);
  border-radius: var(--gemboss-radius-sm); white-space: pre-wrap; overflow-wrap: anywhere; color: var(--gemboss-text);
}
.msg-dich-nut {
  min-height: 44px; padding: 0 4px; border: 0; background: none; font: inherit; font-size: var(--gemboss-fs-caption);
  color: var(--gemboss-accent); cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
}
.msg-dich-nut:hover { color: var(--gemboss-text-strong); }
.msg-dich-nut:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; border-radius: var(--gemboss-radius-xs); }
.msg-dich-nut:disabled { color: var(--gemboss-text-muted); cursor: progress; text-decoration: none; }
.msg-dich-loi { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }

/* Dòng "đã dịch câu trả lời" trên ô soạn (inbox.js `dichTraLoi`): nói đã đổi chữ trong ô, và cho Undo lấy lại câu tiếng Anh. */
.composer-translated {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0;
  font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted);
}
.composer-translated-undo {
  min-height: 44px; padding: 0 4px; border: 0; background: none; font: inherit; color: var(--gemboss-accent);
  cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
}
.composer-translated-undo:hover { color: var(--gemboss-text-strong); }
.composer-translated-undo:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; border-radius: var(--gemboss-radius-xs); }

/* Link trong câu trả lời của trợ lý (lát 8a): khách / hội thoại mở ngay trong hộp thư. Nút trông như link, cỡ chữ của bong bóng. */
.ai-link { display: inline; padding: 0; border: 0; background: none; font: inherit; color: inherit; text-decoration: underline; cursor: pointer; }
.ai-link:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 1px; }
.ai-warn { margin: 4px 0 0; font-size: var(--gemboss-fs-caption); }
.ai-review { margin-top: 6px; align-self: flex-start; }

/* @GemCare trong chat đội (lát 9): ảnh đại diện "AI", nhãn AI cạnh tên, dòng "đang tìm". */
.av--ai { display: inline-grid; place-items: center; border-radius: 50%; background: var(--gemboss-text-strong, #1a1a1a); color: var(--gemboss-surface, #fff);
  font-size: 12px; font-weight: 700; letter-spacing: .04em; }
.chat-ai-badge { margin-left: 6px; padding: 0 5px; border-radius: 4px; font-size: 10px; font-weight: 700; letter-spacing: .04em;
  background: var(--gemboss-hover); color: var(--gemboss-text-strong, inherit); }
.chat-gemcare-looking { list-style: none; padding: 4px 0 4px 44px; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
.chat-ai-asked { margin-left: 6px; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }

/* Hộp thư là một khung cao đúng màn hình: TRANG không bao giờ cuộn, chỉ các ngăn bên trong cuộn.
   `.ui-sr-only` của gói là `position: absolute` không neo. Nằm trong một ngăn cuộn mà không tổ tiên nào được định vị thì nó
   thoát khỏi ngăn, và vị trí tĩnh của nó kéo dài cả trang: nhãn ẩn của ô "Add tag" ở đáy cột Customer nằm ở 967px trên màn
   832px, nên care.gemboss.ai/inbox/unassigned/24 cuộn cả trang (Chris, 05/10/2026; cùng lỗi bảng ở memory
   sr-only-in-table-escapes-scroller). Neo nó về góc khối chứa: thoát ra thì cũng nằm ở đầu trang, không đẩy đáy trang.
   Trong `.app` (khung hộp thư), không định nghĩa lại class của gói ở phạm vi toàn cục (test/gemboss-ui.test.ts). */
.app .ui-sr-only { top: 0; left: 0; }
/* ── Contacts › People (src/contact-search.ts) ─────────────────────────────────────────────────── */
.ct-toolbar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.ct-search { flex: 1 1 240px; min-width: 0; max-width: 420px; min-height: 44px; }
/* §9.1: điều khiển ≥44px ở MỌI khổ. Nhóm `ui-seg` của gói chỉ nới lên 44px ở khổ hẹp (Reports đang 30px ở desktop); màn
   mới không thừa hưởng chỗ hụt đó. */
#ct-type button { min-height: 44px; }
.ct-count { margin: 0; font-weight: 600; color: var(--gemboss-text-strong); }
.ct-table th { padding: 0; }
.ct-sort { all: unset; box-sizing: border-box; display: block; width: 100%; min-height: 44px; padding: 0 12px; cursor: pointer;
  font: inherit; font-weight: 600; color: var(--gemboss-text-muted); white-space: nowrap; }
.ct-sort:hover { color: var(--gemboss-text); }
.ct-sort.is-on { color: var(--gemboss-text-strong); }
.ct-sort:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: -2px; }
.ct-row td { white-space: nowrap; }
.ct-row.is-link { cursor: pointer; }
.ct-row.is-link:hover td { background: var(--gemboss-hover); }
.ct-who { display: flex; align-items: center; gap: 10px; min-width: 220px; }
.ct-avatar { flex: none; width: 28px; height: 28px; border-radius: var(--gemboss-radius-full); display: grid; place-items: center;
  background: var(--gemboss-primary-soft); color: var(--gemboss-text-strong); font-size: var(--gemboss-fs-caption); font-weight: 600; }
.ct-who__text { display: flex; flex-direction: column; min-width: 0; }
.ct-who__btn { all: unset; box-sizing: border-box; display: flex; flex-direction: column; min-width: 0; min-height: 44px; justify-content: center; cursor: pointer; }
.ct-who__btn:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; border-radius: var(--gemboss-radius-sm); }
.ct-who__name { color: var(--gemboss-text-strong); font-weight: 500; overflow: hidden; text-overflow: ellipsis; max-width: 280px; }
.ct-who__sub { font-size: var(--gemboss-fs-caption); overflow: hidden; text-overflow: ellipsis; max-width: 280px; }
.ct-badge { flex: none; font-size: var(--gemboss-fs-micro); padding: 1px 6px; border-radius: var(--gemboss-radius-full);
  border: 1px solid var(--gemboss-divider); color: var(--gemboss-text-muted); }
.ct-num { font-variant-numeric: tabular-nums; }
.ct-shop { color: var(--gemboss-text-muted); }
.ct-pager { display: flex; align-items: center; gap: 12px; justify-content: flex-end; }
.ct-pager .ui-btn { min-height: 44px; }

/* ── Contacts › bộ lọc nhóm and/or ─────────────────────────────────────────────────────────────── */
/* Khung nổi định vị theo màn Contacts (nó cuộn cùng danh sách, không trôi khỏi chip khi cuộn). */
.contacts { position: relative; }
.ct-filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.ct-group { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: 3px; max-width: 100%;
  border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-lg); background: var(--gemboss-surface); }
.ct-chip { display: inline-flex; align-items: center; max-width: 100%; border-radius: var(--gemboss-radius-md); background: var(--gemboss-primary-soft); }
.ct-chip__text, .ct-chip__x, .ct-andor, .ct-plus, .ct-add, .ct-clear, .ct-pop__item {
  all: unset; box-sizing: border-box; min-height: 44px; cursor: pointer; display: inline-flex; align-items: center; }
.ct-chip__text { padding: 0 4px 0 10px; color: var(--gemboss-text-strong); font-weight: 500; overflow-wrap: anywhere; min-width: 0; }
.ct-chip__x, .ct-plus { min-width: 44px; justify-content: center; color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-body); }
.ct-andor { padding: 0 6px; font-weight: 600; color: var(--gemboss-text-muted); }
.ct-andor:hover, .ct-chip__x:hover, .ct-plus:hover { color: var(--gemboss-text-strong); }
.ct-join { font-weight: 600; color: var(--gemboss-text-muted); }
.ct-add { padding: 0 8px; color: var(--gemboss-accent); font-weight: 600; }
.ct-clear { padding: 0 8px; color: var(--gemboss-text-muted); text-decoration: underline; text-underline-offset: 2px; }
.ct-chip__text:focus-visible, .ct-chip__x:focus-visible, .ct-andor:focus-visible, .ct-plus:focus-visible, .ct-add:focus-visible,
.ct-clear:focus-visible, .ct-pop__item:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: -2px; border-radius: var(--gemboss-radius-sm); }
.ct-pop { position: absolute; z-index: 20; width: min(340px, calc(100% - 32px)); max-height: 440px; overflow-y: auto; padding: 8px;
  box-shadow: var(--gemboss-shadow-overlay); display: grid; gap: 4px; }
.ct-pop__search { min-height: 44px; }
.ct-pop__list { display: grid; }
.ct-pop__head { margin: 8px 8px 2px; font-size: var(--gemboss-fs-caption); font-weight: 600; color: var(--gemboss-text-muted); text-transform: uppercase; letter-spacing: .04em; }
.ct-pop__item { padding: 0 10px; border-radius: var(--gemboss-radius-sm); color: var(--gemboss-text); }
.ct-pop__item:hover { background: var(--gemboss-hover); }
.ct-pop__empty { margin: 8px; }
.ct-pop__title { margin: 4px 8px 8px; font-weight: 600; color: var(--gemboss-text-strong); }
.ct-pop__opt { display: grid; gap: 4px; padding: 0 4px; }
/* Cả nhãn là vùng bấm (44px), không phải cái chấm tròn 18px. */
.ct-pop__radio { display: flex; align-items: center; gap: 10px; min-height: 44px; cursor: pointer; }
.ct-pop__radio input { margin: 0; width: 18px; height: 18px; flex: none; }
.ct-pop__val { margin: 0 0 6px 28px; min-height: 44px; width: calc(100% - 28px); box-sizing: border-box; }
.ct-pop__days { display: flex; align-items: center; gap: 8px; }
.ct-pop__days input { width: 96px; min-height: 44px; }
.ct-pop__error { margin: 0 8px; }
.ct-pop__foot { display: flex; justify-content: flex-end; padding: 8px 4px 4px; }
.ct-pop__foot .ui-btn { min-height: 44px; }

/* ── Contacts › segment (cột trái, như Intercom) ─────────────────────────────────────────────────── */
.ct-body { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 24px; align-items: start; }
.ct-main { display: grid; gap: 16px; min-width: 0; }
.ct-segs { display: grid; gap: 2px; position: sticky; top: 0; }
.ct-segs__head { margin: 12px 10px 4px; font-size: var(--gemboss-fs-caption); font-weight: 600; color: var(--gemboss-text-muted); text-transform: uppercase; letter-spacing: .04em; }
.ct-seg { all: unset; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 44px;
  padding: 0 10px; border-radius: var(--gemboss-radius-md); cursor: pointer; color: var(--gemboss-text); }
.ct-seg:hover { background: var(--gemboss-hover); }
.ct-seg[aria-current="true"] { background: var(--gemboss-primary-soft); color: var(--gemboss-text-strong); font-weight: 600; }
.ct-seg:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: -2px; }
.ct-seg__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ct-seg__count { flex: none; font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); font-variant-numeric: tabular-nums; }
.ct-segbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.ct-segbar__title { margin: 0; font-size: var(--gemboss-fs-h3); color: var(--gemboss-text-strong); }
.ct-segbar__actions { display: flex; gap: 8px; flex-wrap: wrap; }
.ct-segbar__actions .ui-btn { min-height: 44px; }
.ct-pop__name { margin: 0; width: 100%; }
.ct-pop__note { margin: 0 4px; font-size: var(--gemboss-fs-caption); }
/* Khổ hẹp: cột segment thành một hàng cuộn ngang trên đầu (như thanh tab), không chiếm nửa màn 320px. */
@media (max-width: 900px) {
  .ct-body { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .ct-segs { display: flex; gap: 6px; overflow-x: auto; position: static; padding-bottom: 4px; }
  .ct-seg { flex: none; border: 1px solid var(--gemboss-divider); }
  .ct-segs__head { display: none; }
}

/* ── Contacts › bản đồ (lát 5, Leaflet + markercluster tự host) ─────────────────────────────────── */
.ct-countbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
#ct-view button { min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; }
/* Icon nét: gói tô `fill` cho nút đang chọn, làm quả địa cầu thành một chấm đen (ảnh 1366, 05/10). */
#ct-view svg { fill: none; }
.ct-map { height: clamp(360px, calc(100vh - 340px), 760px); border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-lg);
  overflow: hidden; background: var(--gemboss-primary-soft); }
.ct-map-note { margin: 0; }
/* Người lẻ: chấm avatar 32px có chữ cái đầu (như Intercom), không phải ghim xanh mặc định của Leaflet. */
.ct-pin { background: none; border: 0; }
.ct-pin__dot { display: grid; place-items: center; width: 32px; height: 32px; border-radius: var(--gemboss-radius-full);
  background: var(--gemboss-surface); color: var(--gemboss-text-strong); font-weight: 600; font-size: var(--gemboss-fs-caption);
  border: 2px solid var(--gemboss-accent); box-shadow: var(--gemboss-shadow-raised); box-sizing: border-box; }
.ct-popup { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px 10px; align-items: center; min-width: 200px; }
.ct-popup__text { display: grid; gap: 2px; min-width: 0; overflow-wrap: anywhere; }
.ct-popup__open { grid-column: 1 / -1; min-height: 44px; }

/* ── Contacts › feed hội thoại (lát 7, như Intercom) ──────────────────────────────────────────────── */
.ct-feed { display: grid; gap: 12px; min-width: 0; max-width: 900px; }
.ct-feed__status { min-height: 44px; width: auto; }
.ct-feed__new { all: unset; box-sizing: border-box; min-height: 44px; display: flex; align-items: center; justify-content: center; cursor: pointer;
  border-radius: var(--gemboss-radius-md); background: var(--gemboss-primary-soft); color: var(--gemboss-accent); font-weight: 600; }
.ct-feed__new:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; }
.ct-feed__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.ct-feed__day { margin-top: 8px; font-size: var(--gemboss-fs-caption); font-weight: 600; color: var(--gemboss-text-muted); text-transform: uppercase; letter-spacing: .04em; }
.ct-feed__card { display: grid; gap: 8px; padding: 14px 16px; border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-lg); background: var(--gemboss-surface); }
.ct-feed__head { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 10px; align-items: start; }
.ct-feed__what { margin: 4px 0 0; color: var(--gemboss-text); overflow-wrap: anywhere; }
.ct-feed__what strong { color: var(--gemboss-text-strong); }
.ct-feed__when { font-size: var(--gemboss-fs-caption); white-space: nowrap; padding-top: 6px; }
.ct-feed__title { margin: 0; font-weight: 600; color: var(--gemboss-text-strong); overflow-wrap: anywhere; }
.ct-feed__body { display: flex; align-items: center; gap: 10px; justify-content: space-between; min-width: 0; }
.ct-feed__preview { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--gemboss-text-muted); }
.ct-feed__preview.is-note { padding: 2px 8px; border-left: 3px solid var(--gemboss-warning-border); background: var(--gemboss-warning-soft); color: var(--gemboss-text); }
.ct-feed__count { flex: none; min-width: 24px; padding: 1px 6px; text-align: center; border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-sm);
  font-size: var(--gemboss-fs-caption); font-variant-numeric: tabular-nums; color: var(--gemboss-text-muted); }
.ct-feed__foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ct-feed__chip { font-size: var(--gemboss-fs-caption); padding: 2px 8px; border-radius: var(--gemboss-radius-full); background: var(--gemboss-primary-soft); }
.ct-feed__open { margin-left: auto; min-height: 44px; }
.ct-feed__more { justify-self: center; min-height: 44px; }
/* Khách đang gõ: ba chấm nhảy như Intercom; tắt chuyển động khi người dùng xin giảm chuyển động. */
.ct-feed__typing { display: inline-flex; gap: 4px; padding: 8px 12px; border-radius: var(--gemboss-radius-full); background: var(--gemboss-primary-soft); }
.ct-feed__typing span { width: 6px; height: 6px; border-radius: 50%; background: var(--gemboss-accent); animation: ct-go 1.2s infinite ease-in-out; }
.ct-feed__typing span:nth-child(2) { animation-delay: .15s; }
.ct-feed__typing span:nth-child(3) { animation-delay: .3s; }
@keyframes ct-go { 0%, 60%, 100% { opacity: .3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) { .ct-feed__typing span { animation: none; opacity: .7; } }
@media (max-width: 600px) {
  .ct-feed__head { grid-template-columns: auto minmax(0, 1fr); }
  .ct-feed__when { grid-column: 2; padding-top: 0; }
}

/* ── Contacts › hồ sơ một khách (lát 6, như trang user của Intercom) ───────────────────────────────── */
.ct-chat { margin-left: auto; min-height: 44px; flex: none; }
.ct-profile { display: grid; gap: 16px; min-width: 0; max-width: 1100px; }
.ct-back { all: unset; box-sizing: border-box; min-height: 44px; display: inline-flex; align-items: center; justify-self: start; cursor: pointer;
  color: var(--gemboss-accent); font-weight: 600; }
.ct-back:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; border-radius: var(--gemboss-radius-sm); }
.cp-head { display: grid; border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-lg); overflow: hidden; background: var(--gemboss-surface); }
/* Bản đồ nhỏ làm nền đầu trang (Intercom đặt nơi khách ở sau tên). Không có toạ độ ⇒ dải màu trơn cùng chiều cao. */
.cp-map { height: 140px; background: var(--gemboss-primary-soft); }
.cp-map .leaflet-control-attribution { font-size: 10px; }
.cp-who { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 0 20px 16px; margin-top: -28px; position: relative; z-index: 500; }
.cp-avatar { width: 64px; height: 64px; font-size: var(--gemboss-fs-h1); border: 3px solid var(--gemboss-surface); box-sizing: border-box; flex: none; }
.cp-who__text { min-width: 0; flex: 1 1 240px; padding-top: 30px; }
.cp-name { margin: 0; font-size: var(--gemboss-fs-h2); color: var(--gemboss-text-strong); overflow-wrap: anywhere; }
.cp-meta { margin: 4px 0 0; display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: center; }
.cp-open { min-height: 44px; margin-top: 30px; }
.cp-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: 16px; align-items: start; }
.cp-col { display: grid; gap: 16px; min-width: 0; }
.cp-card { min-width: 0; }
.cp-h3 { margin: 0 0 12px; font-size: var(--gemboss-fs-body); font-weight: 600; color: var(--gemboss-text-strong); }
.cp-dl { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 8px 16px; margin: 0; }
.cp-dl dt { color: var(--gemboss-text-muted); }
.cp-dl dd { margin: 0; overflow-wrap: anywhere; color: var(--gemboss-text); }
.cp-segs { display: flex; flex-wrap: wrap; gap: 0 8px; }
.cp-seg { all: unset; box-sizing: border-box; min-height: 44px; display: inline-flex; align-items: center; cursor: pointer; }
.cp-seg:hover .ct-feed__chip { background: var(--gemboss-hover); }
.cp-seg:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; border-radius: var(--gemboss-radius-full); }
@media (max-width: 900px) {
  .cp-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 600px) {
  .cp-who { padding: 0 12px 12px; }
  .cp-open { margin-top: 0; width: 100%; }
}
/* Khối Details + Notes mượn từ cột Customer: ở hồ sơ có chỗ, nên mọi điều khiển của nó đủ 44px ở mọi khổ (§9.1). */
#cp-kit-slot :is(button, select, input, .field) { min-height: 44px; }
#cp-kit-slot { display: grid; gap: 8px; }
#cp-kit-slot h3 { margin: 0; }

/* ── Contacts › cột, chọn hàng loạt, xuất (lát 8) ─────────────────────────────────────────────────── */
.ct-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.ct-tools > .ui-btn, .ct-bulk > .ui-btn { min-height: 44px; }
.ct-bulk { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.ct-bulk__n { font-weight: 600; color: var(--gemboss-text-strong); }
.ct-bulk__mode { display: flex; gap: 16px; padding: 0 4px; }
.ct-bulk__val { padding: 0 4px 6px; }
.ct-bulk__val :is(select, input) { width: 100%; min-height: 44px; box-sizing: border-box; }
#ct-pop #ct-bulk-field { width: calc(100% - 8px); margin: 0 4px 4px; min-height: 44px; box-sizing: border-box; }
/* Ô tích: vùng bấm phủ cả ô 44px, không chỉ cái hộp 16px. Cột hẹp, không giãn theo bảng. */
.ct-table .ct-check-cell { width: 44px; padding: 0; }
.ct-check-hit { display: grid; place-items: center; width: 44px; min-height: 44px; cursor: pointer; }
.ct-check { width: 16px; height: 16px; margin: 0; cursor: pointer; }
.ct-text { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ct-table .ct-th-text { padding: 0 12px; font-weight: 600; white-space: nowrap; text-align: left; }

/* ── Team chat theo Lark (05/10/2026, đo ở `_local/gemcare/lark-research/REPORT.md`) ─────────────────────────────────────
   Bong bóng: tin của MÌNH xanh nhạt #d1e3ff, người khác xám #ebeced, GemCare AI tím nhạt (cùng màu AI ở khung chat khách),
   bo 8px, góc dưới trái 2px ở tin của mình (như Lark), đệm 10px 12px, chữ 14/20. Mọi tin vẫn canh trái như nhóm chat Lark:
   tên + ảnh nói ai, màu nói "của tôi hay không". Thân tin chỉ-có-ảnh không có bong bóng (ảnh tự là một khối). */
.chat-msg__body { display: inline-block; max-width: 100%; box-sizing: border-box; padding: 10px 12px; border-radius: 8px;
  background: #ebeced; color: #1f2329; font-size: 14px; line-height: 20px; }
.chat-msg--mine .chat-msg__body { background: #d1e3ff; border-bottom-left-radius: 2px; }
.chat-msg--ai .chat-msg__body { background: #ece8ff; }
/* Nhảy tới tin được trích: viền thay cho đổi nền (nền đã là màu bong bóng). */
.chat-msg__body[data-mid].is-flash { background: #ebeced; outline: 2px solid var(--gemboss-accent); outline-offset: 2px; border-radius: 8px; }
.chat-msg--mine .chat-msg__body[data-mid].is-flash { background: #d1e3ff; }
.chat-msg--grouped { margin-top: -10px; }
/* Hàng sáng nhẹ khi rê (Lark: transition nền .2s), thanh nổi hiện dần .1s. */
.chat-msg { transition: background-color .2s linear; }
.chat-acts { transition: opacity .1s linear; gap: 0; padding: 0; border-color: #dee0e3; border-radius: 6px; box-shadow: 0 2px 8px rgba(31, 35, 41, .08); }
.chat-act { min-width: 44px; padding: 0; display: inline-grid; place-items: center; color: #646a73; }
.chat-act svg { display: block; }
.chat-act:hover { color: var(--gemboss-text-strong); }
.chat-messages { --chat-acts-w: 236px; }
/* ⋯: menu nằm TRONG luồng dưới tin, như bảng chọn biểu tượng (một lớp nổi trong cột cuộn tự bị mép cắt). */
.chat-menu { display: grid; min-width: 180px; width: fit-content; margin-top: 6px; padding: 4px; border: 1px solid #dee0e3; border-radius: 6px;
  background: var(--gemboss-surface); box-shadow: 0 6px 18px rgba(31, 35, 41, .08); }
.chat-menu__item { all: unset; box-sizing: border-box; min-height: 44px; padding: 0 12px; display: flex; align-items: center; border-radius: 4px;
  cursor: pointer; color: var(--gemboss-text-strong); font-size: var(--gemboss-fs-body); }
.chat-menu__item:hover, .chat-menu__item:focus-visible { background: var(--gemboss-hover); }
.chat-menu__item:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: -2px; }
.chat-menu__item--danger { color: var(--gemboss-danger); }
/* Nền hàng khi rê chuột NHẠT hơn bong bóng người khác: --gemboss-hover (#eceef2) gần như trùng #ebeced, và bong bóng của
   người khác chìm vào hàng đúng lúc người ta đang nhìn nó (ảnh 1366, 05/10). Lark làm hàng sáng rất nhẹ. */
.chat-msg:hover, .chat-msg:focus-within, .chat-msg--picking { background: #f7f8f9; }
/* ── Bảng chọn biểu tượng + viên phản ứng theo Lark (lát L3, 05/10/2026) ─────────────────────────────────────────────
   Lark: ô tìm, "Frequently used", rồi các nhóm; lưới 9 cột ô ~48px, panel ~440px. Ở đây ô 44px (§9.1), 9 cột = 396px +
   đệm; cửa sổ nổi 340px thì lưới tự co còn 7 cột. Thân cuộn riêng, cao tối đa 300px, để bảng không đẩy cả kênh. */
/* Bề rộng CỐ ĐỊNH 420px (9 cột × 44 + đệm): `%` ở đây là % của khối tin co theo chữ (fit-content), nên bảng co theo tin
   ngắn còn 7 cột (ảnh 1366, 05/10). Trần là bề rộng vùng tin (đơn vị container cqi của .chat-messages) trừ cột ảnh. */
.chat-picker { display: grid; gap: 6px; width: 420px; max-width: calc(100cqi - 64px); box-sizing: border-box; padding: 8px; flex-wrap: nowrap; }
.chat-picker__search { min-height: 44px; width: 100%; box-sizing: border-box; }
.chat-picker__body { max-height: 300px; overflow-y: auto; overscroll-behavior: contain; display: grid; gap: 2px; }
.chat-picker__head { margin: 6px 4px 2px; font-size: var(--gemboss-fs-caption); color: #8f959e; }
.chat-picker__grid { display: grid; grid-template-columns: repeat(auto-fill, 44px); justify-content: start; }
.chat-picker__btn { border: 0; border-radius: 6px; background: transparent; font-size: 22px; line-height: 1; }
.chat-picker__empty { margin: 8px 4px; }
/* Viên mang tên (Lark: "👍 | Felixpham"): nền xanh nhạt khi có mình, xám khi không; tên dài thì cắt "…" (title có đủ). */
.chat-chip__n { max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: normal; }
/* ── Luồng như Lark (lát L4, 05/10/2026) ─────────────────────────────────────────────────────────────────────────────
   Thanh nổi có thêm "Reply in thread": 6 nút × 44px. Dưới tin gốc: hai câu trả lời mới nhất (ảnh nhỏ thì không cần — tên đậm
   12px + trích xám như Lark) và "N replies" màu nhấn. Ngăn luồng là cột thứ ba của màn Chat; hẹp hơn 1100px thì nó PHỦ lên
   cuộc trò chuyện (một cột 360px cạnh một cột chat 300px thì cả hai đều không đọc được). */
.chat-messages { --chat-acts-w: 276px; }
.chat-threadprev { display: grid; gap: 0; margin-top: 4px; justify-items: start; }
.chat-threadprev__row, .chat-threadprev__more { all: unset; box-sizing: border-box; min-height: 28px; display: flex; align-items: center; gap: 6px;
  max-width: 100%; cursor: pointer; font-size: var(--gemboss-fs-caption); border-radius: 4px; padding: 0 4px; }
.chat-threadprev__row strong { color: var(--gemboss-text-strong); font-weight: 600; flex: none; }
.chat-threadprev__text { color: #646a73; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-threadprev__more { min-height: 44px; color: #1456f0; font-weight: 500; }
.chat-threadprev__row:hover, .chat-threadprev__more:hover { background: #eef1f6; }
.chat-threadprev__row:focus-visible, .chat-threadprev__more:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 1px; }
@media (min-width: 1101px) { .chat-screen.has-thread { grid-template-columns: 220px minmax(0, 1fr) 380px; } }
.chat-screen { position: relative; }
.chat-thread-panel { display: grid; grid-template-rows: auto auto auto minmax(0, 1fr) auto; min-height: 0; min-width: 0;
  border-left: 1px solid var(--gemboss-divider); background: var(--gemboss-surface); }
.chat-thread-panel[hidden] { display: none; }
.ctp-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 8px 8px 16px; border-bottom: 1px solid var(--gemboss-divider); }
.ctp-head h3 { margin: 0; font-size: var(--gemboss-fs-h3); color: var(--gemboss-text-strong); }
.ctp-close { min-width: 44px; min-height: 44px; }
.ctp-why, .chat-thread-panel > .ui-error { margin: 8px 16px 0; }
.ctp-list { list-style: none; margin: 0; padding: 12px 16px; overflow-y: auto; display: grid; align-content: start; gap: 14px; }
.ctp-msg { display: grid; grid-template-columns: 32px minmax(0, 1fr); gap: 10px; }
.ctp-msg__who { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: var(--gemboss-fs-caption); color: #8f959e; }
.ctp-msg__who strong { font-size: var(--gemboss-fs-body); color: var(--gemboss-text-strong); font-weight: 600; }
.ctp-msg__body { margin-top: 2px; white-space: pre-wrap; overflow-wrap: anywhere; color: #1f2329; font-size: 14px; line-height: 20px; }
.ctp-count { display: flex; align-items: center; gap: 10px; font-size: var(--gemboss-fs-caption); color: #8f959e; }
.ctp-count::after { content: ""; flex: 1; border-top: 1px solid var(--gemboss-divider); }
.ctp-composer { display: grid; gap: 8px; padding: 12px 16px 16px; border-top: 1px solid var(--gemboss-divider); }
.ctp-composer textarea { width: 100%; box-sizing: border-box; resize: vertical; min-height: 64px; }
.ctp-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.ctp-also { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; cursor: pointer; font-size: var(--gemboss-fs-body); color: var(--gemboss-text); }
.ctp-also input { width: 18px; height: 18px; margin: 0; }
#ctp-send { min-height: 44px; }
@media (max-width: 1100px) {
  .chat-thread-panel { position: absolute; top: 0; right: 0; bottom: 0; width: min(380px, 100%); z-index: 5; box-shadow: -8px 0 24px rgba(31, 35, 41, .12); }
}
/* ── Trợ lý: chọn ai trả lời + hộp cầu nối (05/10/2026) ─────────────────────────────────────────────────────────── */
.ai-engine { min-height: 44px; width: auto; max-width: 100%; font-size: var(--gemboss-fs-caption); }
.ai-bridge { display: grid; gap: 8px; margin: 4px 0 8px; padding: 12px; border: 1px solid var(--gemboss-divider); border-radius: 8px; background: var(--gemboss-hover); }
.ai-bridge p { margin: 0; font-size: var(--gemboss-fs-body); }
.ai-bridge__cmd { display: block; padding: 8px 10px; border-radius: 6px; background: var(--gemboss-surface); border: 1px solid var(--gemboss-divider);
  font-size: var(--gemboss-fs-caption); white-space: pre-wrap; overflow-wrap: anywhere; user-select: all; }
.ai-bridge .ui-btn { min-height: 44px; justify-self: start; }
/* ── Đã đọc + về tin mới nhất (lát L5, 05/10/2026) ─────────────────────────────────────────────────────────────────
   Lark: vòng 14px cạnh tin của mình, xanh #10a893 khi mọi người đã đọc; nút tròn 48px trắng có bóng ở góc dưới khi cuộn lên. */
.chat-read { display: inline-flex; vertical-align: bottom; margin-left: 6px; cursor: default; }
.chat-read svg { display: block; }
.chat-main { position: relative; }
.chat-jump { position: absolute; right: 24px; bottom: 16px; z-index: 3; width: 48px; height: 48px; border: 0; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer; background: var(--gemboss-surface); color: var(--gemboss-text-strong);
  box-shadow: 0 6px 18px 6px rgba(31, 35, 41, .03), 0 3px 6px -6px rgba(31, 35, 41, .05), 0 4px 8px 0 rgba(31, 35, 41, .08); }
.chat-jump[hidden] { display: none; }
.chat-jump:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; }
.chat-jump__n { position: absolute; top: -4px; right: -4px; min-width: 20px; height: 20px; padding: 0 5px; box-sizing: border-box; border-radius: 10px;
  background: var(--gemboss-accent); color: #fff; font-size: 11px; font-weight: 700; line-height: 20px; text-align: center; }
