/* CSS của app Đào tạo — theo `docs/09-chuan-giao-dien.md` (chuẩn 16/09/2026).
 *
 * Chuẩn là Nexa SDK: token trong `nexa/tokens.css`, thành phần trong `nexa/components.css`.
 * File này CHỈ có ba loại rule:
 *   1. Khung ứng dụng (thanh trên, thanh trái) mà SDK không có — bố cục lấy từ bản thiết kế của BA.
 *   2. Sáu vai chữ `.t-*` và vài thành phần SDK chưa có (viên nhãn `.pill`, ảnh đại diện `.avatar`).
 *   3. Bố cục riêng của từng màn: lưới thẻ số, hàng lọc, khung chi tiết, trình nhập tệp.
 *
 * Luật (lint `frontend/lint/lint-app-css.mjs` chặn, đỏ là không build):
 *   - cỡ chữ chỉ `var(--text-*)`; đậm chỉ 400 và 600; giãn chữ chỉ -0.01em (tiêu đề) và 0.06em (nhãn);
 *   - padding/margin/gap chỉ `var(--sp-*)`, 0, auto; bán kính chỉ `var(--r-*)` hoặc 50%;
 *   - không khai lớp nút mới — nút là `nx-btn` + primary/ghost/danger của SDK;
 *   - không màu thẳng, không vh, không rẽ nhánh theme (lint của SDK, D32).
 * Kích thước VẬT (bề rộng thanh trái, ảnh đại diện, hình 16px, chiều cao nút 34px của SDK) là số
 * đo, được viết px.
 */

/* ── Màu nhấn của app, lấy từ bản vẽ của BA (23/09/2026) ──────────────────────────────────────
   Bản vẽ `LMS Admin.dc.html` dùng chàm #5b62e6 cho nút chính (198 chỗ), #4349c9 khi rê chuột,
   #343a9f cho chữ trên nền nhạt và #eef0ff cho nền nhạt. SDK mặc định màu xanh mòng két #0b7a70,
   nên app đang lệch bản vẽ ở MỌI nút chính.

   Chỉ đặt lại `--accent`: cả họ (`--accent-strong`, `--accent-soft`, `--accent-softer`,
   `--accent-ring`) DẪN XUẤT bằng `color-mix()` trong `nexa/tokens.css`, nên một dòng là đủ và
   sáng/tối vẫn tự lo. Không sửa `nexa/**` (luật chặn 1).

   `--nexa-button-color` vẫn đứng trước: khi Nexa thật cấp màu của workspace thì màu đó THẮNG —
   app theo chủ nhà, và #5b62e6 chỉ là mặc định khi host không nói gì. */
:root {
  --accent: var(--nexa-button-color, #5b62e6);
}

* { box-sizing: border-box; }

/* Tiếng Việt có dấu chồng (ế, ộ, ữ); ở cao dòng `normal` ≈ 1.21 hai dòng liền nhau dính vào nhau.
   1.5 cho thân, 1.3 cho tiêu đề và số. */
body { margin: 0; line-height: 1.5; background-color: var(--bg-hover); }
#root { height: 100%; }

/* 🔴 22/09/2026 — TRẮNG MÀN khi bấm ô tick. Ô tick và công tắc của SDK giấu `<input>` thật bằng
   `position: absolute`, nhưng `.nx-checkbox` / `.nx-switch` bọc nó lại KHÔNG được định vị. Vật
   định vị tuyệt đối mà không có tổ tiên định vị thì lấy mốc là cả trang: nó thoát khỏi lớp cuộn
   `.biz-main`, nằm ở toạ độ trang cả nghìn px, và kéo chiều cao tài liệu vượt khung. Bấm vào ô là
   trình duyệt cuộn cả trang để đưa ô đang nhận bàn phím vào tầm nhìn — khung app cao đúng 100%
   màn trôi lên, người dùng thấy một dải trắng.
   Sửa ở app vì `nexa/**` là bản chép, không được sửa (luật chặn 1). Đã ghi QUESTIONS Q-76 để sửa
   tận gốc bên `neap/_sdk`. `.role-option` của app cũng cùng lỗi. */
.nx-checkbox, .nx-switch, .role-option { position: relative; }

/* ── Sáu vai chữ (chuẩn mục 2) ────────────────────────────────────────────────────────────── */
.t-title   { margin: 0; font-size: var(--text-20); font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; color: var(--text); }
.t-heading { margin: 0; font-size: var(--text-15); font-weight: 600; line-height: 1.3; color: var(--text); }
.t-sub     { font-size: var(--text-12); font-weight: 400; color: var(--text-muted); }
.t-label   { font-size: var(--text-11); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); line-height: 1.3; }
.t-number  { font-size: var(--text-24); font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; font-variant-numeric: tabular-nums; color: var(--text); }
.t-mono    { font-family: var(--font-mono); font-size: var(--text-12); }

/* ── Khung ứng dụng theo LMS Admin 17/09: thanh trái cao hết màn (tên app · menu · người dùng),
      cột phải có thanh trên chỉ chứa tên màn ────────────────────────────────────────────────── */
.biz-root {
  display: flex; height: 100%; min-height: 0;
  font-size: var(--text-13); color: var(--text); background-color: var(--bg-hover);
}
.biz-col { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0; }
.biz-top {
  display: flex; align-items: center; gap: var(--sp-3); flex-shrink: 0; height: 56px; padding: 0 var(--sp-6);
  border-bottom: 1px solid var(--border-subtle); background-color: var(--bg);
}
.biz-top-title { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Nút quay lại của thanh trên (bản vẽ header dòng 65): ô vuông viền, chỉ hiện khi màn chi tiết đặt. */
.biz-top-back {
  display: grid; place-items: center; width: 34px; height: 34px; flex-shrink: 0; padding: 0;
  border: 1px solid var(--border); border-radius: var(--r-md); background-color: var(--bg); color: var(--text); cursor: pointer;
}
.biz-top-back:hover { background-color: var(--bg-hover); }
.biz-top-back:focus-visible { outline: 2px solid var(--accent-ring); outline-offset: 2px; }

/* Thanh điều hướng trái — rộng 232px theo bản BA. */
.biz-nav {
  width: 232px; flex-shrink: 0; display: flex; flex-direction: column; gap: var(--sp-1);
  padding: 0 var(--sp-3) var(--sp-3);
  border-right: 1px solid var(--border-subtle); background-color: var(--bg); overflow-y: auto;
}
.biz-brand { display: flex; align-items: center; gap: var(--sp-3); height: 56px; padding: 0 var(--sp-2); margin-bottom: var(--sp-3); border-bottom: 1px solid var(--border-subtle); }
.biz-mark { display: grid; place-items: center; width: 32px; height: 32px; border-radius: var(--r-md); background-color: var(--accent); color: var(--on-accent); }
.biz-me { margin-top: auto; display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-2) 0; border-top: 1px solid var(--border-subtle); cursor: help; }
.biz-me-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.35; }
.nav-group { display: flex; flex-direction: column; gap: var(--sp-1); margin-bottom: var(--sp-3); }
.nav-group-name { padding: 0 var(--sp-3) var(--sp-2); }
.nav-item {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%; height: 34px; padding: 0 var(--sp-3);
  border: none; border-radius: var(--r-md); background-color: transparent;
  color: var(--text-muted); font: inherit; text-align: left; cursor: pointer;
}
.nav-item:hover { background-color: var(--bg-hover); color: var(--text); }
.nav-item.is-active { background-color: var(--accent-soft); color: var(--accent-strong); font-weight: 600; }
/* Mục BA có thiết kế nhưng đội chưa dựng: hiện nhạt, không bấm được — không dẫn vào ngõ cụt. */
.nav-item.is-todo { cursor: default; opacity: 0.5; }
.nav-item.is-todo:hover { background-color: transparent; color: var(--text-muted); }
.nav-ico { width: 16px; height: 16px; flex-shrink: 0; }
.nav-label { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.biz-main { flex: 1; min-width: 0; overflow-y: auto; }

/* ── Một trang nội dung (bản BA: rộng tối đa 1160px) ───────────────────────────────────── */
.page { max-width: 1160px; margin: 0 auto; padding: var(--sp-6) var(--sp-6) var(--sp-12); }
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.page-head-text { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.page-desc { margin: 0; color: var(--text-muted); max-width: 66ch; text-wrap: pretty; }
.page-head-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; flex-shrink: 0; }

/* Hàng lọc: mọi ô cao 34px như nút và ô nhập của SDK, cùng một hàng. */
.page-tools { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-4); }
/* Bề rộng CỐ ĐỊNH, không `width: auto`: thẻ <select> tự rộng bằng lựa chọn dài nhất, nên thêm cây
   đơn vị ("WinMart+ Phan Xích Long") là cả dải lọc tràn xuống dòng hai. Lựa chọn dài vẫn hiện đủ
   khi bung danh sách; chỗ chật chỉ là phần đang chọn. */
.page-select { width: 180px; flex: 0 0 auto; }
.page-search {
  display: flex; align-items: center; gap: var(--sp-2); min-width: 240px; height: 34px; padding: 0 var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--r-md); background-color: var(--bg); color: var(--text-muted);
}
.page-search:focus-within { border-color: var(--accent); }
.page-search input { flex: 1; min-width: 0; border: none; outline: none; background-color: transparent; color: var(--text); font: inherit; }
.page-count { margin-left: auto; font-variant-numeric: tabular-nums; }
.page-filters { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.page-date { display: inline-flex; align-items: center; gap: var(--sp-2); color: var(--text-muted); }
.page-date .nx-input { width: auto; }
/* Nút phụ đang bật (bộ lọc mở): là TRẠNG THÁI của nút SDK, không phải nút mới. */
.nx-btn-ghost.is-on { background-color: var(--accent-softer); color: var(--accent-strong); border-color: var(--accent-ring); }

.page-error {
  margin-bottom: var(--sp-4); padding: var(--sp-3) var(--sp-4);
  border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--bg)); border-radius: var(--r-md);
  background-color: color-mix(in srgb, var(--danger) 8%, var(--bg)); color: var(--danger);
}
/* Dải nhắc phạm vi: người xem thấy một PHẦN danh bạ thì phải biết, không thì họ tưởng danh bạ chỉ
   có mấy dòng này. Sắc nhấn chứ không phải sắc lỗi — đây là thông tin, không phải sự cố. */
.page-scope {
  margin-bottom: var(--sp-4); padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--accent-ring); border-radius: var(--r-md);
  background-color: var(--accent-softer); color: var(--text-muted);
}
.page-more { display: flex; justify-content: center; padding: var(--sp-3); }

/* ── Bảng trang: `nx-table` của SDK + đầu bảng dính, đệm rộng hơn, đầu cột là nhãn in hoa ──── */
.nx-card { overflow: hidden; }
.tbl th {
  position: sticky; top: 0; z-index: 1; white-space: nowrap;
  padding: var(--sp-3) var(--sp-4); background-color: var(--bg-hover);
  font-size: var(--text-11); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; line-height: 1.3;
}
.tbl td { padding: var(--sp-3) var(--sp-4); vertical-align: middle; }
.tbl tr:last-child td { border-bottom: none; }
.tbl-wide { min-width: 960px; }
.tbl tr.is-row { cursor: pointer; }
.tbl tr.is-row:hover td { background-color: var(--bg-hover); }
.tbl .col-right { text-align: right; }
.cell-muted { color: var(--text-muted); }
.tbl .cell-muted { white-space: nowrap; }
.tbl .cell-at { font-variant-numeric: tabular-nums; color: var(--text-muted); white-space: nowrap; }
.tbl .cell-actor { font-weight: 600; white-space: nowrap; }
.tbl .cell-detail { color: var(--text-muted); }
.tbl .cell-code { font-family: var(--font-mono); font-size: var(--text-12); }

/* ── Viên nhãn: MỘT cỡ (chuẩn mục 3) ─────────────────────────────────────────────────────── */
/* `.act-pill` dùng CHUNG thân với `.pill` — trước 21/09 nó chỉ có bảy dòng tông màu ở cuối file
   mà không có rule gốc, nên mọi viên nhãn trạng thái (khung danh sách, thư viện tài liệu, môn
   học, lộ trình) đổ ra màn thành chữ trần: không nền, không bo, không nhận ra là viên nhãn. */
.pill, .act-pill {
  display: inline-flex; align-items: center; height: 22px; padding: 0 var(--sp-2); white-space: nowrap;
  border-radius: var(--r-full); font-size: var(--text-12); font-weight: 600; line-height: 1;
  background-color: color-mix(in srgb, var(--tone) 14%, var(--bg)); color: var(--tone);
}
.pill-row { display: inline-flex; gap: var(--sp-1); flex-wrap: nowrap; }
.pill.tone-create, .pill.tone-publish, .pill.tone-success { --tone: var(--success); }
.pill.tone-update, .pill.tone-link { --tone: var(--link); }
.pill.tone-delete, .pill.tone-revoke, .pill.tone-danger { --tone: var(--danger); }
.pill.tone-import, .pill.tone-accent { --tone: var(--accent); }
.pill.tone-assign, .pill.tone-warning { --tone: var(--warning); }
.pill.tone-other, .pill.tone-muted { --tone: var(--text-muted); }
/* Mật khẩu tạm: cam ngả đỏ để không trùng vai Quản lý (vàng) ở cột cạnh. */
.pill.tone-temp { --tone: color-mix(in srgb, var(--warning) 62%, var(--danger)); }

/* ── Ảnh đại diện chữ cái ─────────────────────────────────────────────────────────────────── */
.page-filters { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: 14px; }
.page-date {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-12);
  color: var(--text-muted);
}

/* ── Màn cũ chưa dựng lại theo BA (Cài đặt chung, Danh bạ) ───────────────────────────────── */
/* BA không thiết kế hai màn này. Giữ lớp cũ nhưng bỏ phần khung riêng (`.app-shell` từng tự vẽ
   thanh trên và tự cuộn) vì khung giờ do `.biz-*` lo. */

.app-shell { display: flex; flex-direction: column; min-height: 0; }

.app-topbar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 26px 26px var(--sp-4);
}
.app-topbar h1 { margin: 0; font-size: var(--text-24); font-weight: 800; letter-spacing: -0.02em; }

.role-badge {
  margin-left: auto;
  padding: 3px 10px;
  border-radius: var(--r-full);
  font-size: var(--text-11);
  font-weight: 600;
  color: var(--accent-strong);
  background-color: var(--accent-soft);
}

.tree-toolbar { display: flex; flex-wrap: wrap; gap: var(--sp-2); padding: 0 26px var(--sp-3); }
.tree-scroll { padding: 0 26px 60px; }
.tree-placeholder { padding: 34px 0; text-align: center; color: var(--text-muted); font-size: 13.5px; }
.tree-error {
  margin: 0 26px var(--sp-3);
  padding: 10px 14px;
  border-radius: var(--r-md);
  background-color: color-mix(in srgb, var(--danger) 8%, var(--bg));
  color: var(--danger);
  font-size: var(--text-13);
}

.settings-tabs { display: flex; gap: var(--sp-2); padding: 0 26px var(--sp-4); }

.setting-row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.setting-name { font-weight: 600; }
.setting-key { font-family: var(--font-mono); font-size: var(--text-11); color: var(--text-muted); }
.setting-hint { font-size: var(--text-12); color: var(--text-muted); }
.setting-desc { font-size: var(--text-12); color: var(--text-muted); max-width: 66ch; line-height: 1.5; }
.setting-unit { font-size: var(--text-12); color: var(--text-muted); }
.setting-edit { display: flex; align-items: center; gap: var(--sp-2); }
.setting-error { color: var(--danger); font-size: var(--text-12); }
.setting-flag { font-size: var(--text-11); color: var(--text-muted); }
.setting-foot { display: flex; gap: var(--sp-2); padding-top: var(--sp-3); }
.setting-group { margin-bottom: var(--sp-6); }

.catalog-meta { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

.people-toolbar { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.people-count { font-size: var(--text-12); color: var(--text-muted); }
.people-form { display: flex; flex-direction: column; gap: var(--sp-3); }
.people-roles { display: flex; gap: var(--sp-3); flex-wrap: wrap; }

.audit-detail { display: flex; flex-direction: column; gap: var(--sp-2); }
.audit-meta {
  margin: 0;
  padding: var(--sp-3);
  border-radius: var(--r-md);
  background-color: var(--bg-hover);
  font-family: var(--font-mono);
  font-size: var(--text-12);
  overflow-x: auto;
}

/* ── Màn danh sách kiểu bản "LMS Admin" ──────────────────────────────────────────────────────
   Bản thiết kế thứ hai của BA (tệp `LMS Admin.dc.html`) dựng màn danh sách thành: đầu màn có tên
   và nút hành động, bốn THẺ SỐ, hàng lọc, rồi bảng có ẢNH ĐẠI DIỆN CHỮ CÁI. BA chưa chốt nghiệp
   vụ của bản này nên chỉ lấy BỐ CỤC; cột và con số là dữ liệu thật của mình.
   Màu vẫn là token (D32) — bản thiết kế ghi mã màu thẳng. */

.page-head-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
}
.page-head-actions { display: flex; gap: 9px; flex-wrap: wrap; flex-shrink: 0; }

.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: var(--sp-2) 13px;
  border: 1px solid transparent;
  border-radius: 9px;
  background-color: var(--accent);
  color: var(--on-accent);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
}
.btn-primary:hover { opacity: 0.88; }

/* Nút phụ, cùng khổ với `.btn-primary` — dùng cạnh nhau trong một hàng nên mọi số đo phải khớp.
   🔴 Lớp này thiếu tới 18/09/2026: 13 chỗ trong màn Thư viện tài liệu viết `class="btn-ghost"` mà
   CSS không có, nên chúng ra **nút mặc định của trình duyệt** — hộp trắng chữ đen trên nền tối,
   nằm ngay cạnh nút Phát hành xanh. Lint CSS không bắt được (nó soi màu và đơn vị, không soi lớp
   dùng mà chưa khai), test cũng không — chỉ nhìn bằng mắt mới thấy. */
.btn-ghost {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: var(--sp-2) 13px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background-color: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
}
.btn-ghost:hover:not(:disabled) { background-color: var(--bg-hover); }
.btn-ghost:disabled { opacity: 0.5; cursor: not-allowed; }

/* `Tải về` và `Mở liên kết` là thẻ `<a>` mang lớp nút: bỏ gạch chân và màu liên kết mặc định, nếu
   không nó ra chữ xanh gạch chân nằm giữa một hàng nút. */
a.btn-ghost, a.btn-primary { text-decoration: none; }

/* Nút trong hàng: chỉ là chữ, hiện viền khi rê vào. Bảng có nhiều hàng, mỗi hàng hai nút viền đậm
   là một bức tường nút — bản thiết kế cũng để hành động hàng thật nhẹ. */
.btn-quiet {
  padding: 4px var(--sp-2);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background-color: transparent;
  color: var(--text-muted);
  font-family: inherit;
  font-size: var(--text-12);
  font-weight: 600;
  cursor: pointer;
}
.btn-quiet:hover:not(:disabled) { border-color: var(--border); color: var(--text); background-color: var(--bg); }
.btn-quiet:disabled { opacity: 0.4; cursor: default; }

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(156px, 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}
.stat-card {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 78px;
  padding: 13px 15px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background-color: var(--bg);
}
.stat-label {
  display: block;
  color: color-mix(in srgb, var(--text-muted) 74%, var(--bg));
}
.stat-number {
  display: block;
  margin-top: 6px;
  font-size: var(--text-22);
  font-weight: 800;
  letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* Ảnh đại diện chữ cái. Năm tông đều dẫn xuất từ token ngữ nghĩa có sẵn — không đặt mã màu mới. */
.cell-person { display: flex; align-items: center; gap: 9px; min-width: 0; }
.avatar {
  width: 28px; height: 28px; flex-shrink: 0; display: grid; place-items: center; border-radius: var(--r-full);
  font-size: var(--text-11); font-weight: 600; color: var(--on-accent); background-color: var(--tone);
}
.avatar-lg { width: 44px; height: 44px; font-size: var(--text-15); }
.avatar.tone-0 { --tone: var(--accent); }
.avatar.tone-1 { --tone: var(--link); }
.avatar.tone-2 { --tone: var(--success); }
.avatar.tone-3 { --tone: var(--warning); }
.avatar.tone-4 { --tone: color-mix(in srgb, var(--accent) 55%, var(--link)); }

.cell-person { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.cell-person-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.35; }
.cell-person-name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cell-person-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tbl tr.is-off .cell-person-name { color: var(--text-muted); }
.tbl tr.is-archived .cell-person-name { text-decoration: line-through; color: var(--text-muted); }
.tbl tr.is-archived td { opacity: 0.7; }

/* ── Thẻ số ───────────────────────────────────────────────────────────────────────────────── */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--sp-3); margin-bottom: var(--sp-4); }
.stat-card { display: flex; flex-direction: column; justify-content: space-between; gap: var(--sp-2); min-height: 84px; padding: var(--sp-4); }

/* ── Khung chi tiết (Drawer của SDK) ──────────────────────────────────────────────────────── */
.detail-head { display: flex; align-items: flex-start; gap: var(--sp-3); margin-bottom: var(--sp-4); }
.detail-title { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.detail-pills { display: flex; gap: var(--sp-1); flex-wrap: wrap; }
.detail-grid { display: grid; grid-template-columns: max-content 1fr; gap: var(--sp-2) var(--sp-4); margin: 0; }
.detail-grid dt { color: var(--text-muted); }
.detail-grid dd { margin: 0; min-width: 0; }
.detail-note { margin: var(--sp-4) 0 0; }
.detail-banner {
  margin: 0 0 var(--sp-4); padding: var(--sp-3);
  border-radius: var(--r-md); border: 1px solid color-mix(in srgb, var(--warning) 45%, var(--bg));
  background-color: color-mix(in srgb, var(--warning) 12%, var(--bg));
}
.detail-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

/* ── Biểu mẫu ─────────────────────────────────────────────────────────────────────────────── */
.field-stack { display: flex; flex-direction: column; gap: var(--sp-3); }
.field-stack > label:not(.nx-checkbox):not(.nx-switch):not(.wiz-search):not(.pick-card):not(.import-drop) { display: flex; flex-direction: column; gap: var(--sp-1); color: var(--text-muted); font-size: var(--text-12); }
.field-stack .nx-input, .field-stack .nx-select { color: var(--text); }
.field-stack label.is-locked { opacity: 0.6; }
.field-label { font-size: var(--text-12); color: var(--text-muted); }
.role-radio { display: flex; gap: var(--sp-2); }
.role-option {
  flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2); height: 34px;
  border: 1px solid var(--border); border-radius: var(--r-md); color: var(--text); cursor: pointer;
}
.role-option input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.role-option.is-on { border-color: var(--accent); background-color: var(--accent-softer); color: var(--accent-strong); font-weight: 600; }

/* ── Nhập tệp ─────────────────────────────────────────────────────────────────────────────── */
.import-steps { display: flex; gap: var(--sp-2); list-style: none; margin: 0 0 var(--sp-4); padding: 0; counter-reset: b; }
.import-steps li {
  flex: 1; counter-increment: b; padding: 0 0 var(--sp-2);
  font-size: var(--text-11); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  border-bottom: 2px solid var(--border); color: var(--text-muted);
}
.import-steps li::before { content: counter(b) '. '; }
.import-steps li.is-on { color: var(--accent); border-color: var(--accent); }
.import-steps li.is-done { color: var(--success); border-color: var(--success); }
.import-drop {
  display: flex; align-items: center; justify-content: center; min-height: 72px; padding: var(--sp-3); cursor: pointer;
  border: 1px dashed var(--accent-ring); border-radius: var(--r-md); background-color: var(--accent-softer); color: var(--text);
}
.import-drop input { display: none; }
.import-report { padding: var(--sp-3); border-radius: var(--r-md); }
.import-report.is-ok { background-color: color-mix(in srgb, var(--success) 12%, var(--bg)); border: 1px solid color-mix(in srgb, var(--success) 40%, var(--bg)); }
.import-report.is-bad { background-color: color-mix(in srgb, var(--danger) 10%, var(--bg)); border: 1px solid color-mix(in srgb, var(--danger) 40%, var(--bg)); }
.import-report p { margin: 0 0 var(--sp-2); }
.import-errors { max-height: 240px; overflow: auto; }
.import-errors .nx-table td, .import-errors .nx-table th { padding: var(--sp-1) var(--sp-2); }

/* ── Màn Cài đặt chung ────────────────────────────────────────────────────────────────────── */
.setting-group { margin-bottom: var(--sp-6); }
.setting-group > summary { font-size: var(--text-15); font-weight: 600; cursor: pointer; margin-bottom: var(--sp-2); }
.setting-row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; padding: var(--sp-3) 0; border-bottom: 1px solid var(--border-subtle); }
.setting-name { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 260px; flex: 1; }
.setting-name strong { font-weight: 600; }
.setting-key { font-family: var(--font-mono); font-size: var(--text-11); color: var(--text-muted); }
.setting-desc { font-size: var(--text-12); color: var(--text-muted); max-width: 66ch; }
.setting-edit { display: flex; align-items: center; gap: var(--sp-2); }
.setting-edit .nx-input { width: 160px; }
.setting-unit, .setting-flag, .setting-hint { font-size: var(--text-12); color: var(--text-muted); }
.setting-error { font-size: var(--text-12); color: var(--danger); }
.setting-foot { display: flex; align-items: center; gap: var(--sp-2); }
.catalog-meta { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.people-form { display: flex; flex-direction: column; gap: var(--sp-3); }

/* ── Màn Lịch sử thao tác ─────────────────────────────────────────────────────────────────── */
.act-entity { margin-left: var(--sp-2); font-family: var(--font-mono); font-size: var(--text-12); color: var(--text-muted); }
.audit-detail { display: flex; flex-direction: column; gap: var(--sp-2); }
.audit-meta { margin: 0; padding: var(--sp-3); border-radius: var(--r-md); background-color: var(--bg-hover); font-family: var(--font-mono); font-size: var(--text-12); overflow-x: auto; }

/* ── Trang chi tiết một người (CRUD chuẩn: danh sách → trang → sửa) ───────────────────────── */
.page-back { display: inline-flex; align-items: center; gap: var(--sp-1); margin-bottom: var(--sp-3); color: var(--text-muted); background: none; border: none; padding: 0; font: inherit; cursor: pointer; }
.page-back:hover { color: var(--text); }
.person-head { display: flex; align-items: center; gap: var(--sp-4); min-width: 0; }
.person-head .detail-title { gap: var(--sp-1); }
.person-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; justify-content: flex-end; }
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--sp-4); margin-bottom: var(--sp-4); }
.nx-card-body .detail-grid { gap: var(--sp-3) var(--sp-4); }
.page-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--border-subtle); }
.page-select-sm { width: auto; min-width: 120px; }

/* ── D40: ô chọn vai/nhóm dạng thẻ, bảng quyền hiệu lực, ma trận, thẻ tab của trang chi tiết ────── */
.role-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--sp-2); }
.role-card {
  display: flex; align-items: flex-start; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--r-md); background-color: var(--bg); color: var(--text);
}
.role-card input { margin-top: var(--sp-1); accent-color: var(--accent); }
.role-card.is-on { border-color: var(--accent); background-color: var(--accent-softer); }
.role-card-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
/* Trình nhiều bước (bản LMS Admin 17/09): hộp RỘNG, danh sách bước DỌC bên trái, thẻ chọn lớn.
   `:has()` để khỏi sửa `Modal` của SDK (thư mục nexa/** là khoá) — Chrome và Safari đều đã có. */
.nx-modal:has(.wiz) { width: min(1000px, 100%); }
.nx-modal:has(.wiz) .nx-modal-hd { align-items: center; }
.nx-modal:has(.wiz) .nx-modal-hd-tx { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); }
.nx-modal:has(.wiz) .nx-modal-sub {
  margin-top: 0; flex: none; padding: var(--sp-1) var(--sp-3); border-radius: var(--r-full);
  background-color: var(--bg-hover); color: var(--text); font-weight: 600;
}
.nx-modal:has(.wiz) .nx-modal-body { padding: 0; }
.wiz { display: grid; grid-template-columns: 248px 1fr; min-height: 420px; }
.wiz-steps { display: flex; flex-direction: column; gap: var(--sp-1); list-style: none; margin: 0; padding: var(--sp-4); border-right: 1px solid var(--border-subtle); background-color: var(--bg); }
.wiz-step { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3); border-radius: var(--r-md); color: var(--text-muted); }
.wiz-step.is-done { color: var(--text); }
.wiz-step.is-on { background-color: var(--accent-softer); color: var(--accent-strong); font-weight: 600; }
.wiz-num {
  flex: none; display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%;
  background-color: var(--bg-hover); color: var(--text-muted); font-size: var(--text-13); font-weight: 600;
}
.wiz-step.is-on .wiz-num { background-color: var(--accent); color: var(--on-accent); }
.wiz-step.is-done .wiz-num { background-color: var(--success); color: var(--on-accent); }
.wiz-body { padding: var(--sp-5); overflow-y: auto; }
.wiz-alt { align-self: flex-start; }
.wiz-search {
  display: flex; align-items: center; gap: var(--sp-3); height: 44px; padding: 0 var(--sp-4);
  border-radius: var(--r-lg); background-color: var(--bg-hover); color: var(--text-muted);
}
.wiz-search input { flex: 1; min-width: 0; border: none; outline: none; background-color: transparent; color: var(--text); font: inherit; }
/* Đầu trang chi tiết nhóm: tên cùng hàng với viên nhãn, dòng tóm tắt, rồi hàng nút — đúng bản thiết
   kế, khác với đầu trang hồ sơ người (nút nằm góc phải). */
.group-hd { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-5) var(--sp-6); }
.group-hd-title { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.group-hd-meta { display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap; }
.group-hd-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

/* Danh sách học viên trong nhóm: từng DÒNG có vạch ngăn, không phải bảng có đầu cột — bản thiết kế
   để vậy vì mỗi dòng là một người, không phải một ô dữ liệu để so cột. */
.member-list { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.member-row { display: flex; align-items: center; gap: var(--sp-4); padding: var(--sp-3) 0; border-bottom: 1px solid var(--border-subtle); }
.member-row:last-child { border-bottom: none; }
.member-row .cell-person { flex: 1; min-width: 0; cursor: pointer; }
.member-region { min-width: 96px; }

/* Một điều kiện là một THẺ: viên nhãn rồi câu điều kiện, nút bỏ ở cuối. */
.rule-card { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-2);
  border: 1px solid var(--border-subtle); border-radius: var(--r-md); background-color: var(--bg); }
.rule-text { flex: 1; min-width: 0; }
.rule-add { display: grid; grid-template-columns: 1fr 1fr auto; gap: var(--sp-3); align-items: end; margin-bottom: var(--sp-4); }
.rule-add > label { display: flex; flex-direction: column; gap: var(--sp-1); color: var(--text-muted); font-size: var(--text-12); }

/* Ô xem thử trong trình tạo: số người khớp rồi vài thẻ người làm mẫu. */
.match-box { padding: var(--sp-4); margin-bottom: var(--sp-3); border-radius: var(--r-md); background-color: var(--bg-hover); }
.match-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--sp-2); margin-top: var(--sp-3); }
.match-chip { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border-subtle); border-radius: var(--r-md); background-color: var(--bg); }
.match-chip.is-more { justify-content: center; border-style: dashed; }

/* Chân trình bốn bước: Quay lại bên trái, Huỷ và Tiếp tục bên phải. */
.nx-modal-ft:has(.wiz-ft-left) { justify-content: space-between; }
.wiz-ft-left, .wiz-ft-right { display: flex; gap: var(--sp-2); }

/* Điều kiện ở chế độ ĐỌC: mỗi dòng một điều kiện, nối bằng chữ VÀ như bản thiết kế — đọc ra ngay
   là "phải thoả tất cả", không phải "thoả cái nào cũng được". */
.rule-view { display: flex; flex-direction: column; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.rule-view li { display: flex; align-items: baseline; gap: var(--sp-3); }
.rule-and { min-width: 32px; }
.pick-list { display: flex; flex-direction: column; gap: var(--sp-3); max-height: 340px; overflow: auto; }
.pick-card {
  display: flex; align-items: center; gap: var(--sp-4); padding: var(--sp-3) var(--sp-4); cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--r-lg); background-color: var(--bg); color: var(--text);
}
.pick-card:hover { border-color: var(--border-strong); }
.pick-card.is-on { border-color: var(--accent); background-color: var(--accent-softer); }
.pick-card input { flex: none; width: 18px; height: 18px; accent-color: var(--accent); }
.pick-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.detail-grid.derived { margin-top: var(--sp-3); padding: var(--sp-3); border-radius: var(--r-md); background-color: var(--bg-hover); }
.detail-grid.cols-2 { grid-template-columns: max-content 1fr max-content 1fr; }
.pill-row.wrap { flex-wrap: wrap; }
.sync-note { margin: 0 0 var(--sp-3); }
.person-tabs { padding: 0 var(--sp-3); }
.nx-card-hd.inner { margin: var(--sp-4) 0 0; padding: var(--sp-3) 0; border-top: 1px solid var(--border-subtle); }
.switch-wrap { display: inline-flex; align-items: center; height: 34px; padding: 0 var(--sp-2); }
.wizard-steps li { font-size: var(--text-11); }
.matrix th.is-hi, .matrix td.is-hi { background-color: var(--accent-softer); }
.matrix td.is-dim .pill { opacity: 0.55; }
.cell-roles { max-width: 260px; }

/* ── Màn Người dùng theo ảnh 13:54 17/09: dải tab, dải lọc + nút, thân trang ─────────────────── */
.page-people { max-width: none; padding: 0; }
.band { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; padding: var(--sp-3) var(--sp-6); border-bottom: 1px solid var(--border-subtle); background-color: var(--bg); }
.band.page-tools { margin-bottom: 0; }
.band .page-head-actions { margin-left: auto; }
.page-body { max-width: 1240px; margin: 0 auto; padding: var(--sp-6) var(--sp-6) var(--sp-12); }
.seg-tabs { display: inline-flex; gap: var(--sp-1); padding: var(--sp-1); border-radius: var(--r-lg); background-color: var(--bg-hover); }
.seg { display: inline-flex; align-items: center; gap: var(--sp-2); height: 34px; padding: 0 var(--sp-4); border: none; border-radius: var(--r-md); background-color: transparent; color: var(--text-muted); font: inherit; font-weight: 600; cursor: pointer; }
.seg b { font-weight: 600; color: var(--text-muted); }
.seg.is-on { background-color: var(--bg); color: var(--text); box-shadow: var(--shadow-sm); }
.seg.is-on b { color: var(--accent); }

/* ── Trang chi tiết người dùng (17/09 chiều): đầu trang là một thẻ, thân chia KHỐI trong ba tab ──── */
.person-hd { display: flex; align-items: center; gap: var(--sp-4); padding: var(--sp-4) var(--sp-5); margin-bottom: var(--sp-4); }
.person-hd-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.person-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; justify-content: flex-end; }
.blk { padding-bottom: var(--sp-5); margin-bottom: var(--sp-5); border-bottom: 1px solid var(--border-subtle); }
.blk:last-child { padding-bottom: 0; margin-bottom: 0; border-bottom: none; }
.blk-hd { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-3); }
/* Cụm bên phải đầu khối: dòng nguồn dữ liệu và nút của khối đứng cùng nhau, để tiêu đề vẫn một mình
   bên trái. Ba con trực tiếp trong `.blk-hd` thì `space-between` đẩy dòng nguồn ra giữa, đọc rối. */
.blk-hd-side { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
/* Lưới ô NHẬP hai cột cho lúc sửa hồ sơ tại chỗ. Không dùng `.detail-grid`: lưới đó là bốn cột
   nhãn/giá trị cho chế độ đọc, nhồi ô nhập vào là nhãn và ô lệch hàng nhau. */
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4); }
.field-grid > label { display: flex; flex-direction: column; gap: var(--sp-1); color: var(--text-muted); font-size: var(--text-12); }
.field-grid .nx-input, .field-grid .nx-select { color: var(--text); }
.field-grid label.is-locked { opacity: 0.6; }
@media (max-width: 720px) { .field-grid { grid-template-columns: 1fr; } }
/* Dòng chỉ chỗ dưới biểu mẫu: nói trường nào sửa ở đâu, để người dùng không đi tìm khắp trang. */
.form-note { margin: 0 0 var(--sp-3); }
/* Cụm trong một khối: nhãn in hoa rồi lưới của nó. Mười sáu dòng liền một mạch thì mắt không có chỗ
   nghỉ; chia bốn cụm theo nghĩa thì mỗi cụm trả lời đúng một câu. Vạch ngăn mảnh hơn vạch giữa các
   KHỐI, để người đọc phân biệt được đây là cụm con chứ không phải khối mới. */
.blk-part + .blk-part { margin-top: var(--sp-5); padding-top: var(--sp-4); border-top: 1px dashed var(--border-subtle); }
.blk-part > .t-label { margin-bottom: var(--sp-3); }
/* Cột nhãn CỐ ĐỊNH bề rộng, không `max-content`: mỗi cụm là một lưới riêng nên `max-content` tính
   theo nhãn dài nhất của CHÍNH cụm đó, và bốn cụm ra bốn bề rộng khác nhau — nhìn như bốn bảng lệch
   nhau. Nhãn dài hơn thì xuống dòng, thẳng cột vẫn hơn. */
.blk-part .detail-grid.cols-2 { grid-template-columns: 220px minmax(0, 1fr) 220px minmax(0, 1fr); }
@media (max-width: 900px) { .blk-part .detail-grid.cols-2 { grid-template-columns: 180px minmax(0, 1fr); } }
.perm-fold { margin-top: var(--sp-4); }
.perm-fold > summary { cursor: pointer; font-weight: 600; padding: var(--sp-2) 0; }
.stat-grid.four { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.stat-grid.four .stat-card { min-height: 0; gap: 0; }
/* Dòng thời gian: cột ngày · chấm · nội dung. Chỉ dựng từ mốc CÓ THẬT trong hồ sơ. */
.timeline { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-4); }
.timeline li { display: grid; grid-template-columns: 96px 12px 1fr; align-items: start; gap: var(--sp-3); }
.tl-at { color: var(--text-muted); }
.tl-dot { width: 9px; height: 9px; margin-top: var(--sp-1); border-radius: 50%; background-color: var(--accent); }
.tl-text { display: flex; flex-direction: column; min-width: 0; }

.tab-count { margin-left: var(--sp-2); color: var(--text-muted); font-weight: 400; }

.cell-duty { color: var(--accent); font-weight: 600; }
.roles-table td { vertical-align: middle; }
/* Bảy cột không vừa một màn 1440px — bản vẽ cũng cuộn ngang. Nới để cột Vai trò khỏi bó xuống ba dòng. */
.roles-table { min-width: 1240px; }
.roles-table td:first-child { min-width: 240px; }

/* Trang chi tiết vai: bảng quyền hai cột, hàng "không có quyền" mờ để mắt lướt qua nhanh. */
.perm-table td { padding: var(--sp-3) var(--sp-4); }
.perm-table tr.is-none td { opacity: 0.55; }
.role-page .stat-card { min-height: 0; gap: var(--sp-1); }
.role-page .person-hd { align-items: flex-start; }
.role-page .blk-hd .nx-checkbox { font-size: var(--text-13); }

/* Hai thẻ chọn xếp DỌC ở bước 1 của trình tạo nhóm: bản thiết kế để chúng chồng lên nhau, mỗi thẻ
   một dòng mô tả, nên không dùng lưới nhiều cột như chỗ chọn vai trò. */
.role-grid.one { grid-template-columns: 1fr; }

/* ── Nhóm học viên (thư mục 04) ─────────────────────────────────────────────────────────────── */
/* Một dòng điều kiện: trường · phép so · giá trị · nút bỏ. Phép so là CHỮ chứ không phải ô chọn,
   vì mỗi trường chỉ có đúng một phép so hợp lệ (bảng RULE_FIELDS ở máy chủ). */
.rule-row { display: grid; grid-template-columns: 200px 72px minmax(0, 1fr) auto; gap: var(--sp-2); align-items: start; margin-bottom: var(--sp-2); }
.rule-op { padding-top: var(--sp-2); text-align: center; }
.rule-list { margin-bottom: var(--sp-3); }
/* Danh sách nhặt học viên: cuộn trong khung, không kéo dài cả trang. */
/* Đầu trang chi tiết nhóm: tên cùng hàng với viên nhãn, dòng tóm tắt, rồi hàng nút — đúng bản thiết
   kế, khác với đầu trang hồ sơ người (nút nằm góc phải). */
.group-hd { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-5) var(--sp-6); }
.group-hd-title { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.group-hd-meta { display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap; }
.group-hd-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

/* Danh sách học viên trong nhóm: từng DÒNG có vạch ngăn, không phải bảng có đầu cột — bản thiết kế
   để vậy vì mỗi dòng là một người, không phải một ô dữ liệu để so cột. */
.member-list { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.member-row { display: flex; align-items: center; gap: var(--sp-4); padding: var(--sp-3) 0; border-bottom: 1px solid var(--border-subtle); }
.member-row:last-child { border-bottom: none; }
.member-row .cell-person { flex: 1; min-width: 0; cursor: pointer; }
.member-region { min-width: 96px; }

/* Một điều kiện là một THẺ: viên nhãn rồi câu điều kiện, nút bỏ ở cuối. */
.rule-card { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-2);
  border: 1px solid var(--border-subtle); border-radius: var(--r-md); background-color: var(--bg); }
.rule-text { flex: 1; min-width: 0; }
.rule-add { display: grid; grid-template-columns: 1fr 1fr auto; gap: var(--sp-3); align-items: end; margin-bottom: var(--sp-4); }
.rule-add > label { display: flex; flex-direction: column; gap: var(--sp-1); color: var(--text-muted); font-size: var(--text-12); }

/* Ô xem thử trong trình tạo: số người khớp rồi vài thẻ người làm mẫu. */
.match-box { padding: var(--sp-4); margin-bottom: var(--sp-3); border-radius: var(--r-md); background-color: var(--bg-hover); }
.match-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--sp-2); margin-top: var(--sp-3); }
.match-chip { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border-subtle); border-radius: var(--r-md); background-color: var(--bg); }
.match-chip.is-more { justify-content: center; border-style: dashed; }

/* Chân trình bốn bước: Quay lại bên trái, Huỷ và Tiếp tục bên phải. */
.nx-modal-ft:has(.wiz-ft-left) { justify-content: space-between; }
.wiz-ft-left, .wiz-ft-right { display: flex; gap: var(--sp-2); }

/* Điều kiện ở chế độ ĐỌC: mỗi dòng một điều kiện, nối bằng chữ VÀ như bản thiết kế — đọc ra ngay
   là "phải thoả tất cả", không phải "thoả cái nào cũng được". */
.rule-view { display: flex; flex-direction: column; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.rule-view li { display: flex; align-items: baseline; gap: var(--sp-3); }
.rule-and { min-width: 32px; }
.pick-list { display: flex; flex-direction: column; gap: var(--sp-1); max-height: 320px; overflow-y: auto; padding: var(--sp-2); border: 1px solid var(--border-subtle); border-radius: var(--r-md); }
@media (max-width: 720px) { .rule-row { grid-template-columns: 1fr; } .rule-op { text-align: left; padding-top: 0; } }
/* Hai tông viên nhãn chỉ màn này cần. */
.act-pill.tone-accent { --tone: var(--accent); }
.act-pill.tone-link { --tone: var(--link); }
.act-pill.tone-success { --tone: var(--success); }
.act-pill.tone-warning { --tone: var(--warning); }
.act-pill.tone-muted { --tone: var(--text-muted); }
.act-pill.tone-danger { --tone: var(--danger); }
/* Mật khẩu tạm: cam ngả đỏ, để KHÔNG trùng với vai Quản lý (vàng) ở cột bên cạnh. */
.act-pill.tone-temp { --tone: color-mix(in srgb, var(--warning) 62%, var(--danger)); }

/* Ô "Trạng thái" của khung danh sách: viên nhãn ở trên, ngày cập nhật ở dưới — một cột thay cho ba
   (bản vẽ `LS_STD_COLS`, đổi 18/09). */
.ls-st { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }

/* ---- 02-lo-trinh-hoc-phan-mon-hoc: khung danh sách dùng chung ------------------------------ */
/* Năm màn của bản vẽ "LMS Admin" dùng lại đúng khuôn này (`list-shell.mjs`). Lớp riêng của từng
   màn đặt ở khối của thư mục đó, không chen vào đây. */

/* Bố cục màn danh sách: HAI DẢI TRẮNG có viền dưới (thanh thẻ, rồi hàng công cụ) nằm sát thanh
   trên, dưới cùng là vùng nội dung nền xám. Đúng bản vẽ LMS Admin, và khác hẳn bản 21/09 sáng —
   bản đó nhét cả ba tầng vào một cột trên nền xám nên màn không có điểm dừng nào cho mắt.
   Bề rộng 1180px là số của bản vẽ (`max-width:1180px` ở cả ba dải). */
.ls { display: flex; flex-direction: column; min-height: 100%; }
.ls-strip { flex-shrink: 0; border-bottom: 1px solid var(--border); background-color: var(--bg); }
.ls-strip-in { max-width: 1180px; margin: 0 auto; padding: var(--sp-3) var(--sp-6); }
.ls-tools { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.ls-acts { margin-left: auto; display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.ls-main { flex: 1; background-color: var(--bg-hover); }
.ls-main-in { max-width: 1180px; margin: 0 auto; padding: var(--sp-4) var(--sp-6) var(--sp-6);
  display: flex; flex-direction: column; gap: var(--sp-3); }
/* Trong dải, thanh thẻ không cần chừa đáy nữa — viền của dải làm việc đó. */
.ls-strip .ws-tabs { margin-bottom: 0; }
/* Ô tìm chiếm phần còn lại của hàng, đúng bản vẽ (`flex:1 1 190px`). */
.ls-tools .page-search { flex: 1 1 190px; margin: 0; }

/* Thanh thẻ của màn Khung chương trình. Bản vẽ vẽ thẻ đang mở NỔI LÊN trên nền rãnh chìm, nên
   thẻ tắt không có viền — viền cho cả bốn làm thanh trông như bốn nút rời. */
.ws-tabs {
  display: flex;
  gap: var(--sp-1);
  padding: var(--sp-1);
  margin-bottom: var(--sp-4);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--text-muted) 9%, var(--bg));
}
.ws-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border: none;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-weight: 500;
  cursor: pointer;
}
.ws-tab:hover { color: var(--text); }
.ws-tab.is-on {
  background-color: var(--bg);
  color: var(--text);
  font-weight: 600;
  box-shadow: 0 1px 2px color-mix(in srgb, var(--text) 12%, transparent);
}
/* Thẻ đang mở: hình và số đếm lấy tông chính, đúng `ico`/`cFg` của bản vẽ — chỉ đổi nền trắng
   thôi thì thẻ đang mở và thẻ tắt gần như giống nhau ở màn hình sáng. */
.ws-tab.is-on .nav-ico { color: var(--accent); }
.ws-tab.is-on .ws-tab-count { color: var(--accent); }
.ws-tab-count {
  min-width: 1.5em;
  padding: 0 var(--sp-1);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--text-muted) 16%, var(--bg));
  font-size: var(--text-12);
  text-align: center;
}

/* `table-layout: fixed`: bề rộng cột do `cols[].w` quyết, không do nội dung. Thiếu dòng này thì
   một ô dài — câu lỗi của lượt tải hỏng là ca thật — kéo cột đầu phình ra và đẩy ba cột còn lại
   ra ngoài khung, kể cả khi ô đã khai `text-overflow: ellipsis` (ellipsis chỉ ăn khi ô CÓ trần
   bề rộng). Cột đầu không khai `w` nên nó nhận phần còn lại. */
.ls-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
/* Đầu bảng và chân bảng là hai dải nền xám nằm SÁT mép thẻ, nên phải bo theo góc thẻ. Không bo
   thì hai góc trên và hai góc dưới của thẻ bị vuông lại, nhìn như thẻ hỏng. */
.ls-table thead th:first-child { border-top-left-radius: var(--r-lg); }
.ls-table thead th:last-child { border-top-right-radius: var(--r-lg); }
.ls-foot { border-radius: 0 0 var(--r-lg) var(--r-lg); }
/* Tiêu đề cột: CHỮ IN HOA nhỏ, giãn chữ, trên nền xám nhạt — vai chữ `.t-label` của chuẩn, và
   đúng `lsCols` của bản vẽ. Chữ thường cỡ 12 như bản cũ đọc ngang hàng với nội dung, nên bảng
   không có đầu. */
.ls-table th {
  padding: var(--sp-2) var(--sp-4);
  background-color: var(--bg-hover);
  border-bottom: 1px solid var(--border-subtle);
  text-align: left;
  font-size: var(--text-11);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.ls-table td { padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--border-subtle);
  vertical-align: middle; }
.ls-table tbody tr:first-child td { border-top: none; }
.ls-table .col-right { text-align: right; }
/* Cả HÀNG mở chi tiết, đúng bản vẽ (`lsRow` có `open`). Có `tabindex` và bắt phím Enter nên đi
   bằng bàn phím vẫn mở được — một hàng bấm được mà không lên được bằng Tab là hàng không tồn tại
   với người dùng trình đọc màn hình. */
.ls-table tr.is-clickable { cursor: pointer; }
.ls-table tr.is-clickable:hover { background: color-mix(in srgb, var(--text-muted) 6%, var(--bg)); }
.ls-table tr.is-clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Thanh phân trang nằm TRONG thẻ, là chân thẻ có viền trên và nền nhạt — đúng khối `lsPaged` của
   bản vẽ. Để ngoài thẻ (bản 21/09 sáng) thì nó trôi trên nền xám, không thuộc về cái gì. */
.ls-foot {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-2) var(--sp-4);
  border-top: 1px solid var(--border-subtle);
  background-color: var(--bg-hover);
}
.ls-foot .ls-pager { margin-left: auto; }
.ls-range { font-size: var(--text-12); color: var(--text-muted); }
.ls-pager { display: inline-flex; align-items: center; gap: var(--sp-1); }
.ls-page {
  min-width: 2rem;
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background-color: var(--bg);
  color: var(--text);
  font: inherit;
  cursor: pointer;
}
.ls-page:hover:not(:disabled) { border-color: var(--accent); }
.ls-page:disabled { opacity: 0.45; cursor: not-allowed; }
.ls-page.is-on { border-color: var(--accent); background: var(--accent); color: var(--on-accent); font-weight: 600; }
.ls-gap { padding: 0 var(--sp-1); color: var(--text-muted); }

/* ---- 07-tai-lieu-hoc-va-duyet: thẻ Thư viện tài liệu --------------------------------------- */
/* `.card` được dùng từ 17/09 ở màn chi tiết và quanh bảng danh sách nhưng CHƯA TỪNG được khai —
   mọi khối đáng lẽ là thẻ trắng có viền lại trôi thẳng trên nền trang. Thấy khi đối chiếu với bản
   vẽ 21/09. KHÔNG đặt `overflow: hidden`: bảng danh sách nằm trong thẻ này, và menu của nút ba
   chấm ở dòng cuối phải tràn ra ngoài được. */
.card {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background-color: var(--bg);
}

/* Thẻ đầu màn chi tiết: tên và hai viên nhãn bên trái, nút bên phải. */
.ml-head { display: flex; align-items: flex-start; gap: var(--sp-4); padding: var(--sp-4); }
.ml-head-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.ml-head-title { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.ml-head-title .page-title { margin: 0; }
.ml-head-file { font-family: var(--font-mono); font-size: var(--text-12); color: var(--text-muted);
  word-break: break-all; }
.ml-head .page-head-actions { flex-shrink: 0; }
/* Nút có hình đi kèm chữ. `.btn-ghost` là inline-flex nên chỉ cần khoảng cách. */
.btn-ghost .nav-ico, .btn-primary .nav-ico { margin-right: var(--sp-1); }

/* Nút ba chấm. Cột 52px trong bảng; menu neo theo nút nên không cần đo toạ độ. */
.col-menu { text-align: right; }
.kebab { position: relative; display: inline-block; }
.kebab-btn {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background-color: var(--bg);
  color: var(--text-muted);
  cursor: pointer;
}
.kebab-btn:hover, .kebab-btn.is-on { border-color: var(--border-strong); color: var(--text); }
.kebab-menu {
  position: absolute;
  top: calc(100% + var(--sp-1));
  right: 0;
  z-index: 30;
  min-width: 13rem;
  padding: var(--sp-1);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background-color: var(--bg);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
}
.kebab-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.kebab-item:hover:not(:disabled) { background-color: var(--bg-hover); }
.kebab-item:disabled { opacity: 0.45; cursor: not-allowed; }
.kebab-item.tone-warning { color: var(--warning); }
.kebab-item.tone-danger { color: var(--danger); }
.kebab-item.tone-success { color: var(--success); }

/* 🔴 Hộp thoại của nút ba chấm dựng NGAY TRONG ô bảng (`<td class="col-menu">`), nên nó thừa kế
   `text-align: right` của cột đó và mọi dòng chữ trong hộp dạt sang phải — thấy trên trình duyệt
   21/09. Đặt lại ở lớp ngoài cùng của hộp thay vì bỏ căn phải của cột. */
.nx-modal-scrim { text-align: left; }

/* Hộp xác nhận: tên tài liệu nổi lên trên câu hệ quả. */
.ml-confirm { display: flex; flex-direction: column; gap: var(--sp-2); }
.ml-confirm-name { font-weight: 600; }
.ml-confirm-name.tone-warning { color: var(--warning); }
.ml-confirm-name.tone-danger { color: var(--danger); }

.ml-back {
  border: none;
  background: none;
  padding: 0 0 var(--sp-1);
  color: var(--link);
  font: inherit;
  font-size: var(--text-12);
  cursor: pointer;
}
.ml-back:hover { text-decoration: underline; }
.ml-sec-title { padding: var(--sp-3); font-weight: 600; border-bottom: 1px solid var(--border); }

/* Ba khối dữ kiện của màn chi tiết (bản vẽ `LMS v2 · Chi tiết tài liệu`, `d2.groups`) — thay sáu
   thẻ số của bản 18/09. Lưới tự xuống hàng: ba khối cạnh nhau trên màn rộng, xếp dọc trên màn hẹp,
   không có điểm gãy nào phải canh bằng tay. */
.ml-groups {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: var(--sp-3);
  align-items: start;
}
.ml-group .ml-fields { display: flex; flex-direction: column; padding: var(--sp-2) var(--sp-3); }
/* Nhãn bên trái, giá trị bên phải: mắt dò nhãn theo một cột thẳng, và giá trị dài tự xuống dòng
   mà không đẩy nhãn đi đâu. */
.ml-field {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--border);
}
.ml-field:last-child { border-bottom: none; }
.ml-field-label { flex-shrink: 0; color: var(--text-muted); font-size: var(--text-12); }
.ml-field-value { min-width: 0; text-align: right; font-weight: 600; overflow-wrap: anywhere; }
.ml-field-mono { min-width: 0; text-align: right; font-family: var(--font-mono);
  font-size: var(--text-12); color: var(--text-muted); overflow-wrap: anywhere; }

/* Khối "Đang dùng trong" (`d2.uses`): bài học nào đang ghim tài liệu này. */
.ml-uses { margin: 0; padding: 0; list-style: none; }
.ml-uses li { display: flex; align-items: baseline; gap: var(--sp-2);
  padding: var(--sp-3); border-bottom: 1px solid var(--border); }
.ml-uses li:last-child { border-bottom: none; }

/* Lời nhắc "bản mới là bản nháp" trong hộp Tải bản mới. Tông TRUNG TÍNH chứ không phải `--warning`
   như khối ngay dưới nó: bản mới nằm im không phải chuyện xấu, và hai khối cùng màu cam cạnh nhau
   thì khối thật sự cần chú ý mất hết sức nặng. */
.ml-note-draft { padding: var(--sp-3); border-radius: var(--r-md);
  border: 1px solid color-mix(in srgb, var(--text-muted) 30%, var(--bg));
  background-color: color-mix(in srgb, var(--text-muted) 6%, var(--bg));
  color: var(--text); line-height: 1.5; }

.ml-versions { margin: 0; padding: 0; list-style: none; }
.ml-versions li {
  display: grid;
  grid-template-columns: 3.5rem 1fr auto auto auto auto;
  gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3);
  border-bottom: 1px solid var(--border);
}
.ml-versions li:last-child { border-bottom: none; }
/* 22/09 bỏ dải màu bên trái của bản hiện hành (D142): viên nhãn *Hiện hành* đã nói đúng câu đó rồi,
   và hai tín hiệu cho một chuyện là thứ người dùng đã bắt một lần (21/09). Dải màu duy nhất còn lại là
   nền của hàng ĐANG XEM (`.is-xem`) — một chuyện khác hẳn. */
.ml-ver { font-family: var(--font-mono); font-weight: 600; }
.ml-ver-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ml-ver-file { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Sáu cột: số bản · tên tệp và ghi chú · người tải và ngày · dung lượng · viên nhãn con trỏ nhóm ·
   "Xem"/"Đang xem". Cột người và viên nhãn *Hiện hành*/*Lưu trữ* đều theo bản vẽ v2
   (`d2.versions[].who`, `.at`, `.curLabel`). */
.ml-versions li { grid-template-columns: 3.5rem 1fr auto auto auto auto; }
.ml-ver-nguoi { display: flex; flex-direction: column; gap: var(--sp-1); text-align: right; }
.ml-ver-who { font-size: var(--text-12); white-space: nowrap; }
.ml-versions li.is-clickable { cursor: pointer; }
.ml-versions li.is-clickable:hover,
.ml-versions li.is-clickable:focus-visible { background-color: var(--bg-hover); }
.ml-versions li.is-xem { background-color: var(--accent-softer); }
.ml-ver-xem { color: var(--text-muted); font-size: var(--text-12); white-space: nowrap; }
.ml-versions li.is-xem .ml-ver-xem { color: var(--accent); font-weight: 600; }

/* Khung xem nội dung ngay trong màn. Chiều cao theo %, KHÔNG vh: app sống trong iframe do host
   cấp kích thước (bẫy đã ghi ở CLAUDE.md). */
.ml-sec-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.ml-xem-trong { padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-3); align-items: flex-start; }
.ml-link-url { color: var(--text-muted); font-size: var(--text-12); word-break: break-all; }
/* Trình đọc PDF tự dựng (`pdf-view.mjs`). Không còn `.ml-xem-pdf` vì iframe đã bỏ — xem lý do ở
   `vendor/pdfjs/README.md`. */
.pdfv { display: flex; flex-direction: column; }
.pdfv-bar { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border); }
/* Nút một ký tự (‹ › − +) thì khổ ngang của `.btn-ghost` rộng gấp ba chữ — thu về vuông. */
.pdfv-bar .btn-ghost { padding: var(--sp-1) 0; min-width: 30px; justify-content: center; }
.pdfv-cach { flex: 1; }
.pdfv-so { color: var(--text-muted); font-size: var(--text-13); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pdfv-khung { max-height: 34rem; overflow: auto; padding: var(--sp-3);
  background-color: var(--bg-hover); display: flex; justify-content: center; }
/* Nền trắng dưới canvas: pdf.js KHÔNG tô nền cho vùng trắng của trang, nên thiếu dòng này thì ở
   giao diện tối, trang PDF nhìn như thủng lỗ. `--on-accent` là token duy nhất luôn trắng ở cả hai
   giao diện — tờ giấy thì trắng bất kể người dùng chọn sáng hay tối. */
.pdfv-canvas { display: block; box-shadow: var(--shadow-md); background-color: var(--on-accent); }
.ml-xem-anh { display: block; max-width: 100%; margin: 0 auto; padding: var(--sp-3); }
.ml-xem-video { display: block; width: 100%; max-height: 34rem; background-color: var(--bg-hover); }
/* Lời nhắn "không phát được" chỉ hiện khi thẻ video bắn `error` — xem `Viewer`. */
.ml-video-bao { display: none; }
.ml-video-hong .ml-xem-video { display: none; }
.ml-video-hong .ml-video-bao { display: flex; }

.ml-form { display: flex; flex-direction: column; gap: var(--sp-3); }
.ml-form label { display: flex; flex-direction: column; gap: var(--sp-1); }
.ml-form .req { color: var(--danger); }

.seg { display: inline-flex; gap: var(--sp-1); padding: var(--sp-1); border-radius: var(--r-md);
  background: color-mix(in srgb, var(--text-muted) 9%, var(--bg)); }
.seg-btn { padding: var(--sp-1) var(--sp-3); border: none; border-radius: var(--r-sm);
  background: transparent; color: var(--text-muted); font: inherit; cursor: pointer; }
.seg-btn.is-on { background-color: var(--bg); color: var(--text); font-weight: 600; }

.ml-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-5) var(--sp-3);
  border: 1px dashed var(--border);
  border-radius: var(--r-md);
  text-align: center;
  cursor: pointer;
}
.ml-drop:hover { border-color: var(--accent); }
.ml-drop input { display: none; }
.ml-drop-title { font-weight: 600; }
.ml-drop-sub { font-size: var(--text-12); color: var(--text-muted); }

.ml-files { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--sp-2); }
/* Mỗi tệp một khối: dòng đầu là tên tệp + cỡ + nút bỏ, dòng dưới là ô đặt tên tài liệu (bắt buộc,
   bản vẽ 18/09). */
.ml-files li { display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-2); border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--text-muted) 7%, var(--bg)); }
.ml-files li.is-bad { background: color-mix(in srgb, var(--danger) 10%, var(--bg)); }
.ml-file-head { display: flex; align-items: center; gap: var(--sp-3); }
.ml-file-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ml-file-bad { color: var(--danger); font-weight: 600; }
.ml-file-x { border: 0; background: none; cursor: pointer; color: var(--text-muted);
  padding: 0 var(--sp-1); font-size: var(--text-14); line-height: 1; }
.ml-file-x:hover { color: var(--danger); }

/* Thanh tổng dung lượng của cả lượt — bản vẽ đổi màu khi gần trần; ở đây dùng token ngữ nghĩa
   thay vì ba mã màu mới (D32). */
.ml-tong { display: flex; align-items: center; gap: var(--sp-3); }
.ml-tong-bar { flex: 1; height: 6px; border-radius: var(--r-full); overflow: hidden;
  background: color-mix(in srgb, var(--text-muted) 20%, transparent); }
.ml-tong-bar span { display: block; height: 100%; background: var(--accent); }

/* Dòng "đang tải lên" nằm đầu danh sách. */
.ml-up-bar { display: flex; align-items: center; gap: var(--sp-2); justify-content: flex-end; }
.ml-up-bar span { display: block; height: 6px; width: 72px; border-radius: var(--r-full); background: var(--accent); }
.ml-up-bar b { font-variant-numeric: tabular-nums; font-weight: 600; }
.ml-up-acts { display: flex; align-items: center; gap: var(--sp-2); justify-content: flex-end;
  flex-wrap: nowrap; }
.ml-up-acts .btn-ghost { padding: var(--sp-1) var(--sp-2); white-space: nowrap; }

/* Nút "Chọn tệp khác" ở dòng tải hỏng: là `<label>` bọc `<input type=file>` chứ không phải
   `<button>`, vì chỉ thẻ label mới mở được hộp chọn tệp mà không cần JavaScript giả lập click. */
.ml-repick { cursor: pointer; }
.ml-repick input { display: none; }

/* Dải "v2 → v3" ở đầu hộp Tải bản mới. */
.ml-verstep { display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3); border-radius: var(--r-md);
  background-color: color-mix(in srgb, var(--text-muted) 7%, var(--bg)); }
.ml-verstep-arrow { color: var(--text-muted); }
.ml-verstep .cell-muted { margin-left: auto; }
.ml-ver-chip { padding: 2px var(--sp-2); border: 1px solid var(--border); border-radius: var(--r-sm);
  background-color: var(--bg); font-family: var(--font-mono); font-weight: 600; color: var(--text-muted); }
.ml-ver-chip.is-new { border-color: var(--accent); background-color: var(--accent-softer); color: var(--accent); }

/* Dòng tệp đã chọn trong hộp Tải bản mới. */
.ml-file-chip { display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2); border-radius: var(--r-sm);
  background-color: color-mix(in srgb, var(--text-muted) 7%, var(--bg)); }

/* Dải báo bản vừa lưu, đầu màn chi tiết (`dd.hasBanner` của bản vẽ). */
.ml-banner { display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3); border: 1px solid color-mix(in srgb, var(--success) 45%, var(--bg));
  border-radius: var(--r-md); background-color: color-mix(in srgb, var(--success) 10%, var(--bg)); }
.ml-banner-ver { font-family: var(--font-mono); font-weight: 600; }

.ml-switch { flex-direction: row !important; align-items: center; gap: var(--sp-2) !important; }

/* Khối ảnh hưởng. Dùng ở hai nơi: khối `.ml-warn` trong hộp Tải bản mới (chỉ đọc) và thẻ
   `.ml-impact-card` trên màn chi tiết (có ô tick). Viên số dùng chung. */
.ml-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.ml-chip { padding: 2px var(--sp-2); border-radius: var(--r-full);
  border: 1px solid color-mix(in srgb, var(--warning) 45%, var(--bg));
  background-color: color-mix(in srgb, var(--warning) 10%, var(--bg));
  font-size: var(--text-12); }
.ml-chip b { font-variant-numeric: tabular-nums; }
/* Số 0 thì làm nhạt chứ không giấu: thấy đủ bốn ô mới đọc được "không ảnh hưởng gì". */
.ml-chip.is-zero { border-color: var(--border); background-color: var(--bg); color: var(--text-muted); }

.ml-warn { display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-3); border-radius: var(--r-md);
  border: 1px solid color-mix(in srgb, var(--warning) 45%, var(--bg));
  background-color: color-mix(in srgb, var(--warning) 7%, var(--bg)); }
.ml-warn-title { font-weight: 600; }
.ml-warn-row { display: flex; align-items: baseline; gap: var(--sp-2); }

.ml-impact-card .ml-impact { padding: var(--sp-3); }
.ml-impact { display: flex; flex-direction: column; gap: var(--sp-3); }
.ml-impact-foot { display: flex; justify-content: flex-end; }
.ml-pages { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--sp-1); }
.ml-pages label { display: flex; align-items: baseline; gap: var(--sp-2); padding: var(--sp-2);
  border-radius: var(--r-sm); cursor: pointer; }
.ml-pages label:hover { background: color-mix(in srgb, var(--text-muted) 7%, var(--bg)); }
.ml-page-name { font-weight: 600; }
/* Nhãn loại của một dòng ảnh hưởng ("BÀI HỌC"). Chữ hoa nhỏ, đúng `r.tag` của bản vẽ. */
.ml-tag { padding: 1px var(--sp-2); border-radius: var(--r-sm);
  background-color: color-mix(in srgb, var(--text-muted) 12%, var(--bg));
  color: var(--text-muted); font-size: var(--text-11); font-weight: 600; letter-spacing: 0.04em; }
.stat-fact { font-weight: 600; }

/* ── Thẻ Môn học và Lộ trình của màn Khung chương trình (thư mục 02) ──────────────────────── */
/* Lớp riêng `cl-*` chứ không mượn `ml-*` của Thư viện tài liệu: hai thẻ do hai thư mục sở hữu,
   mượn lớp của nhau là một hôm người kia sửa thì màn này lệch mà không ai biết vì sao. */
.cl-sec { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-4); }
.cl-pages { display: flex; flex-direction: column; gap: var(--sp-3); }
.cl-page { display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-3); border: 1px solid var(--border-subtle); border-radius: var(--r-md); }
.cl-page-n { font-size: var(--text-12); font-weight: 600; color: var(--text-muted); }
/* Cây lộ trình. Mỗi học phần là MỘT THẺ TRẮNG trên nền xám, đúng khối `pd.modules` của bản vẽ
   "LMS Admin": đầu thẻ là số thứ tự, tên và viên nhãn; dưới là viên nhãn luật; rồi tới từng hàng
   môn học. Ô nhập của học phần nằm trong hộp "Sửa học phần" chứ không rải ra thẻ — bốn ô nhập
   nhân ba học phần là mười hai ô mở sẵn trên một màn, đọc không ra đâu là nội dung đâu là cấu
   hình (đó là màn ngày 21/09, đã bỏ). */
.cl-tree { display: flex; flex-direction: column; gap: var(--sp-3); }
.cl-mod { display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--r-lg); background-color: var(--bg); }
.cl-mod-hd { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4); }
.cl-no { flex-shrink: 0; display: inline-flex; align-items: center; height: 20px; padding: 0 var(--sp-2);
  border-radius: var(--r-sm); background-color: color-mix(in srgb, var(--text-muted) 12%, var(--bg));
  color: var(--text-muted); font-family: var(--font-mono); font-size: var(--text-11); font-weight: 600; }
.cl-mod-title { min-width: 0; font-size: var(--text-15); font-weight: 600; }
.cl-mod-meta { font-size: var(--text-12); color: var(--text-muted); }
.cl-mod-acts { margin-left: auto; flex-shrink: 0; display: flex; gap: var(--sp-1); }
/* Đầu thẻ bài học là NÚT gập/mở cả thẻ, nên phải gỡ hết trang trí mặc định của thẻ button. */
.cl-mod-hd.is-nut { width: 100%; border: none; background: none; font: inherit; color: inherit;
  text-align: left; cursor: pointer; }
.cl-mod-hd.is-nut:hover { background-color: var(--bg-hover); }
.cl-chev { margin-left: auto; flex-shrink: 0; display: grid; place-items: center; color: var(--text-muted); }
.cl-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); padding: 0 var(--sp-4) var(--sp-3); }
.cl-chip { display: inline-flex; align-items: center; height: 22px; padding: 0 var(--sp-3);
  border-radius: var(--r-full); white-space: nowrap; font-size: var(--text-12); font-weight: 600;
  background-color: var(--accent-softer); color: var(--accent); }
/* Luật mặc định (mở theo thứ tự, xong theo lộ trình) là chuyện BÌNH THƯỜNG, nên để tông xám. Chỉ
   thứ người cấu hình tự đặt mới lấy tông accent — nhìn một cái là thấy chỗ nào đã can thiệp. */
.cl-chip.is-thuong { background-color: color-mix(in srgb, var(--text-muted) 10%, var(--bg)); color: var(--text-muted); }
.cl-row { display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--border-subtle); }
/* Mã môn dài ngắn khác nhau ("KCT-ATVSTP" mười ký tự, "LT01" bốn) nên khoá cứng bề ngang là
   chữ tràn ra ngoài ô. Đặt bề ngang TỐI THIỂU để các hàng vẫn thẳng cột, rồi cho nới theo chữ. */
.cl-code { min-width: 66px; height: 32px; padding: 0 var(--sp-2); flex-shrink: 0; display: grid; place-items: center;
  border-radius: var(--r-sm); background-color: var(--accent-softer); color: var(--accent);
  font-family: var(--font-mono); font-size: var(--text-11); font-weight: 600; }
.cl-row-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.cl-row-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cl-row-sub { font-size: var(--text-12); color: var(--text-muted); }
.cl-empty { margin: 0 var(--sp-4) var(--sp-4); padding: var(--sp-4); text-align: center;
  border: 1px dashed var(--border-strong); border-radius: var(--r-md);
  background-color: var(--bg-hover); font-size: var(--text-12); color: var(--text-muted); }
.cl-mod-ft { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  padding: var(--sp-2) var(--sp-4); border-top: 1px solid var(--border-subtle);
  background-color: var(--bg-hover); }
.cl-mod-ft .cell-muted { margin-left: auto; font-size: var(--text-12); }
.cl-items { display: flex; flex-direction: column; gap: var(--sp-2); }
.cl-item { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm); background-color: var(--bg-hover); }
.cl-check { display: flex; flex-direction: column; gap: var(--sp-2); }
.cl-check-row { display: flex; align-items: flex-start; gap: var(--sp-2); font-size: var(--text-13); }
.cl-diff { display: flex; flex-direction: column; gap: var(--sp-1); font-size: var(--text-13); }

/* ── Khung chương trình v2: nút quay lại và hộp soạn trang (thư mục 02) ────────────────────── */
/* Nút quay lại hình vuông ở thanh trên, đúng bản vẽ. */
.tr-back { display: grid; place-items: center; width: 32px; height: 32px; flex-shrink: 0;
  border: 1px solid var(--border); border-radius: var(--r-md); background-color: var(--bg);
  color: var(--text-muted); cursor: pointer; }
.tr-back:hover { background-color: var(--bg-hover); color: var(--text); }

/* Đầu một trang trong hộp soạn nội dung bài học. */
.cl-page-hd { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  flex-wrap: wrap; }

/* ── Thẻ Phiếu điểm của chi tiết lộ trình (thư mục 02 dựng, D79; khối `p2.isScore` của bản vẽ) ── */
/* Hai thẻ cạnh nhau: nhóm điểm bên trái, tính thử bên phải. Ở khổ hẹp thì chồng lên nhau, cùng mốc
   1100px với hai cột của thẻ Cấu trúc để hai thẻ trong một trang đổi dáng cùng lúc. */
.pd-2cot { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--sp-3); align-items: start; }
@media (max-width: 1100px) { .pd-2cot { grid-template-columns: minmax(0, 1fr); } }
.pd-nhom, .pd-thu { display: flex; flex-direction: column; overflow: hidden; }
.pd-cho { padding: var(--sp-4); }
.pd-hd-phai { display: flex; align-items: center; gap: var(--sp-2); }

/* Bản 8 (D81, bản vẽ 13:42): mỗi nhóm một hàng đầu gồm tên · cách tính · trọng số · ngưỡng đạt nhóm ·
   nút xoá, mỗi ô có nhãn nhỏ phía trên; hàng đầu xuống dòng khi hẹp thay vì ép cột cố định. */
.pd-trang { display: flex; flex-direction: column; gap: var(--sp-3); }
.pd-dong { display: flex; align-items: flex-end; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }
.pd-c-ten { flex: 1 1 150px; min-width: 0; display: flex; align-items: center; gap: var(--sp-2); align-self: center; }
.pd-o-cot { display: flex; flex-direction: column; gap: var(--sp-1); }
.pd-cach { width: 160px; }
.pd-hang { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); }
.pd-hang + .pd-hang { border-top: 1px solid var(--border-subtle); }
.pd-ten { flex: 1; min-width: 0; font-weight: 600; }
/* Tên nhóm sửa tại chỗ như bản vẽ: không viền cho tới khi rê chuột hay bấm vào, để hàng nhóm đọc
   như một dòng tiêu đề chứ không như một biểu mẫu. */
.pd-ten.nx-input:not(:hover):not(:focus) { border-color: transparent; background-color: transparent; }
.pd-so { width: 56px; text-align: right; font-variant-numeric: tabular-nums; }
.pd-c-cach .nx-input { width: 100%; }

/* Số thứ tự nhóm: ô vuông tô màu của nhóm. Cùng màu đó đi theo nhóm xuống dòng công thức và sang
   chấm của khối Tính thử — mắt nối ba chỗ bằng màu, không phải đọc tên. Sáu tông dẫn từ token. */
.pd-dot { width: 22px; height: 22px; flex-shrink: 0; display: grid; place-items: center;
  border-radius: var(--r-sm); background-color: var(--tone); color: var(--on-accent);
  font-size: var(--text-11); font-weight: 600; }
.pd-cham { width: 8px; height: 8px; flex-shrink: 0; border-radius: var(--r-full); background-color: var(--tone); }
.pd-cham.is-le { background-color: var(--border-strong); }
/* Thứ tự tông để hai nhóm liền nhau không bao giờ gần màu: màu chính của app ngả xanh lá, nên
   `--success` đứng xa nó nhất trong dãy. */
:is(.pd-dot, .pd-cham, .pd-ct-chip).tone-0 { --tone: var(--accent); }
:is(.pd-dot, .pd-cham, .pd-ct-chip).tone-1 { --tone: var(--warning); }
:is(.pd-dot, .pd-cham, .pd-ct-chip).tone-2 { --tone: color-mix(in srgb, var(--danger) 55%, var(--link)); }
:is(.pd-dot, .pd-cham, .pd-ct-chip).tone-3 { --tone: var(--link); }
:is(.pd-dot, .pd-cham, .pd-ct-chip).tone-4 { --tone: var(--success); }
:is(.pd-dot, .pd-cham, .pd-ct-chip).tone-5 { --tone: var(--danger); }

/* Thành phần điểm trong nhóm (bản 8): một dòng gồm viên nhãn loại · tên kèm học phần chứa nó · hệ số
   (chỉ khi nhóm tính có hệ số) · ngưỡng đạt riêng · nút bỏ. Thụt vào bằng đúng bề ngang ô số thứ tự
   nhóm, để đọc ra là con của nhóm. */
.pd-tp { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-2) 0 var(--sp-2) var(--sp-8); border-top: 1px solid var(--border-subtle); }
.pd-tag { flex-shrink: 0; padding: 0 var(--sp-2); border-radius: var(--r-sm); font-size: var(--text-11); font-weight: 600;
  white-space: nowrap; color: var(--warning); background-color: color-mix(in srgb, var(--warning) 14%, var(--bg)); }
.pd-tag.is-rubric { color: var(--success); background-color: color-mix(in srgb, var(--success) 14%, var(--bg)); }
.pd-tp-ten { flex: 1 1 180px; min-width: 0; display: flex; flex-direction: column; }
.pd-tp-chu { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--text-12); font-weight: 600; }
.pd-tp-them { display: flex; align-items: center; gap: var(--sp-2); padding-left: var(--sp-8); }
.pd-dau-so .p2-seg-nut { min-width: 26px; font-size: var(--text-12); }
.pd-heso { width: 40px; height: 22px; padding: 0 var(--sp-1); text-align: center; font-size: var(--text-12); }
.pd-chip-x { display: grid; place-items: center; width: 20px; height: 20px; border: none;
  border-radius: var(--r-full); background: none; color: var(--text-muted); cursor: pointer; }
.pd-chip-x:hover { background-color: var(--border-subtle); }
.pd-chip-x .nav-ico { width: 12px; height: 12px; }
.pd-them { width: auto; height: 28px; border-style: dashed; border-color: var(--accent);
  border-radius: var(--r-full); background-color: var(--bg); color: var(--accent); font-size: var(--text-12); font-weight: 600;
  max-width: 100%; padding: 0 var(--sp-3); border-width: 1px; }
.pd-rong { font-size: var(--text-12); font-weight: 600; color: var(--warning); }

/* Dải "Chưa tính điểm": học phần chưa vào nhóm nào. Tông cảnh báo vì đây là lý do chặn phát hành
   (PUB-011); bấm một viên nhãn là tạo nhóm riêng cho học phần đó. */
.pd-le { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid color-mix(in srgb, var(--warning) 35%, var(--bg));
  background-color: color-mix(in srgb, var(--warning) 8%, var(--bg)); }
.pd-le-nhan { font-size: var(--text-12); font-weight: 600; color: var(--warning); }
.pd-le-chip { display: inline-flex; align-items: center; gap: var(--sp-1); height: 26px; padding: 0 var(--sp-3);
  border: 1px solid color-mix(in srgb, var(--warning) 35%, var(--bg)); border-radius: var(--r-full);
  background-color: var(--bg); color: var(--warning); font: inherit; font-size: var(--text-12); cursor: pointer; }
.pd-le-chip:hover:not(:disabled) { background-color: color-mix(in srgb, var(--warning) 12%, var(--bg)); }
.pd-le-chip .nav-ico { width: 12px; height: 12px; }

.pd-o { display: flex; align-items: center; gap: var(--sp-2); }
.pd-o-nhan { flex-shrink: 0; white-space: nowrap; font-size: var(--text-12); font-weight: 600; color: var(--text-muted); }

/* Dòng công thức: mỗi nhóm một viên nhãn nhạt cùng màu với số thứ tự của nhóm. */
.pd-cong-thuc { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--border-subtle); font-size: var(--text-12); }
.pd-ct-dau { font-weight: 600; }
.pd-ct-phan { display: inline-flex; align-items: center; gap: var(--sp-2); }
.pd-ct-chip { padding: var(--sp-1) var(--sp-2); border-radius: var(--r-sm); font-weight: 600;
  color: var(--tone); background-color: color-mix(in srgb, var(--tone) 14%, var(--bg)); }
.pd-ct-dat { flex-basis: 100%; font-weight: 600; color: var(--accent); }

/* Thẻ Cách xét kết quả (bản 8): hai lối xét, rồi các ô của lối đang chọn trên cùng một hàng. */
.pd-cach-xet { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-3) var(--sp-6); padding: var(--sp-3) var(--sp-4); }
.pd-cach-xet .p2-seg { flex-wrap: nowrap; }
.pd-dat-xong { align-self: center; padding: var(--sp-1) var(--sp-3); border-radius: var(--r-md); font-size: var(--text-12); font-weight: 600;
  color: var(--success); background-color: color-mix(in srgb, var(--success) 10%, var(--bg));
  border: 1px solid color-mix(in srgb, var(--success) 30%, var(--bg)); }

/* Khối Tính thử: nhập điểm từng học phần, đọc điểm từng nhóm và điểm lộ trình. */
.pd-thu-hang { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-4); }
.pd-thu-hang + .pd-thu-hang { border-top: 1px solid var(--border-subtle); }
.pd-thu-ten { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--text-12); font-weight: 600; }
.pd-thu-nhom { display: flex; flex-direction: column; gap: var(--sp-1); padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--border-subtle); background-color: var(--bg-hover); }
.pd-thu-dong { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--text-12); }
.pd-thu-dong .pd-thu-ten { font-weight: 400; }
.pd-thu-so { width: 48px; text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }
.pd-thu-so.is-liet { color: var(--danger); }
.pd-thu-tong { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--border-subtle); }
.pd-thu-tong .pd-o-nhan { flex: 1; }

/* ---- 02-lo-trinh-hoc-phan-mon-hoc: chi tiết lộ trình theo bản vẽ 24/09 (D80) --------------- */
/* Bố cục lấy từ bản vẽ `LMS v2 · Chi tiết lộ trình` (khối `p2`); cỡ chữ, đậm, bán kính và màu lấy
   từ chuẩn của app (`docs/09-chuan-giao-dien.md`), không lấy số của mockup. Phiếu đo ở
   `docs/features/02-lo-trinh-hoc-phan-mon-hoc/man-hinh.md` mục 16. */

.p2-trang { gap: var(--sp-3); }
.p2-so-deu { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Thẻ đầu trang: tên + viên nhãn, dòng dữ kiện, ba nút bên phải; thanh thẻ nằm TRONG thẻ đầu. */
.p2-dau { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); }
.p2-dau-tren { display: flex; align-items: flex-start; gap: var(--sp-3); flex-wrap: wrap; }
.p2-dau-trai { flex: 1 1 320px; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.p2-dau-ten { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.p2-dau-ten .page-title { margin: 0; }
.p2-ma { display: inline-flex; align-items: center; height: 22px; padding: 0 var(--sp-2); border-radius: var(--r-sm);
  background-color: var(--accent-softer); color: var(--accent); font-family: var(--font-mono);
  font-size: var(--text-11); font-weight: 600; }
.p2-dau-ke { display: flex; align-items: center; gap: var(--sp-1) var(--sp-4); flex-wrap: wrap;
  font-size: var(--text-12); color: var(--text-muted); }
.p2-dau-ke b { font-weight: 600; color: var(--text); }
.p2-dau-nut { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.p2-dau-nut .nav-ico, .p2-them-goc .nav-ico, .p2-the-hd .nav-ico { margin-right: var(--sp-1); }
.p2-the-thanh { align-self: flex-start; margin: 0; }
/* Nút Phát hành: xanh khi phát hành được, xám có khoá khi còn lý do chặn (§10.2). */
.nx-btn.is-xanh { background-color: var(--success); border-color: var(--success); }
.nx-btn.is-chan { color: var(--text-muted); }

/* Hộp "Chưa phát hành được": neo dưới cụm nút, liệt kê lý do. */
.p2-chan-goc { position: relative; display: inline-flex; align-items: center; gap: var(--sp-2); }
.p2-chan { position: absolute; top: calc(100% + var(--sp-2)); right: 0; z-index: 40; width: 22rem; max-width: 90vw;
  display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4);
  border: 1px solid color-mix(in srgb, var(--danger) 30%, var(--bg)); border-radius: var(--r-md);
  background-color: var(--bg); box-shadow: var(--shadow-lg); }
.p2-chan-hd { display: flex; align-items: center; gap: var(--sp-2); font-weight: 600; color: var(--danger); }
.p2-chan-hd .p2-ico { margin-left: auto; }
.p2-chan ul { margin: 0; padding: 0 0 0 var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-1);
  font-size: var(--text-12); text-wrap: pretty; }
.p2-chan li::marker { color: var(--danger); }

/* Dải thông báo sau khi lưu (§10.4): nói ra luật "sửa cấu hình không hồi tố". */
.p2-bao { display: flex; align-items: flex-start; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3);
  border: 1px solid color-mix(in srgb, var(--success) 35%, var(--bg)); border-radius: var(--r-md);
  background-color: color-mix(in srgb, var(--success) 8%, var(--bg)); color: var(--success); font-size: var(--text-12); }
.p2-bao-than { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.p2-bao-ten { font-weight: 600; font-size: var(--text-13); }

/* Hai cột: cây (bề rộng kéo được, `--p2-cay`) · thanh kéo · bảng thuộc tính. Mở rộng thì cây ẩn. */
.p2-2cot { display: flex; align-items: flex-start; gap: var(--sp-3); }
.p2-2cot > .p2-cay { flex: 0 1 var(--p2-cay, 380px); min-width: 280px; }
.p2-2cot > .p2-panel { flex: 1 1 240px; min-width: 240px; }
.p2-2cot.is-rong > .p2-panel { flex-basis: 100%; }
@media (max-width: 900px) { .p2-2cot { flex-direction: column; align-items: stretch; } .p2-keo { display: none; } }
.p2-keo { flex: 0 0 auto; align-self: stretch; width: 8px; min-height: 120px; margin: 0 calc(-1 * var(--sp-2));
  border-radius: var(--r-full); cursor: col-resize; }
.p2-keo:hover { background-color: var(--accent-soft); }

.p2-cay { display: flex; flex-direction: column; overflow: hidden; }
.p2-cay-hd, .p2-the-hd { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  flex-wrap: wrap; padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border-subtle); }
.p2-the-cho { padding: var(--sp-4); }
.p2-cay-ds { display: flex; flex-direction: column; }

/* Một hàng cây: ô hình theo loại, tên (xuống dòng được), dòng meta, viên nhãn; nút chỉ ở hàng đang chọn. */
.p2-hang { display: flex; align-items: center; gap: var(--sp-2); min-height: 44px;
  padding: var(--sp-1) var(--sp-3) var(--sp-1) var(--sp-3); border-top: 1px solid var(--border-subtle);
  box-shadow: inset 3px 0 0 transparent; cursor: pointer; }
.p2-hang:hover { background-color: var(--bg-hover); }
.p2-hang.is-on { background-color: var(--accent-softer); box-shadow: inset 3px 0 0 var(--accent-soft); }
.p2-hang:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.p2-hang.is-keo { opacity: 0.45; }
.p2-hang.is-qua { box-shadow: inset 0 -2px 0 var(--accent); }
.p2-chev { display: grid; place-items: center; width: 22px; height: 22px; flex-shrink: 0; padding: 0;
  border: none; border-radius: var(--r-sm); background: none; color: var(--text-muted); cursor: pointer; }
.p2-chev:hover { background-color: var(--border-subtle); }
.p2-chev.is-trong { cursor: default; }
.p2-chev .nav-ico { width: 14px; height: 14px; }

/* Ô hình theo loại nút. Bốn tông theo `P2K` của bản vẽ, dẫn từ token ngữ nghĩa. */
.p2-kind { display: grid; place-items: center; width: 26px; height: 26px; flex-shrink: 0; border-radius: var(--r-sm);
  background-color: color-mix(in srgb, var(--tone) 14%, var(--bg)); color: var(--tone); }
.p2-kind .nav-ico { width: 14px; height: 14px; }
.p2-panel-hd .p2-kind { width: 30px; height: 30px; }
:is(.p2-kind, .p2-pill-kind).is-path { --tone: var(--accent); }
:is(.p2-kind, .p2-pill-kind).is-module { --tone: var(--warning); }
:is(.p2-kind, .p2-pill-kind).is-course { --tone: var(--link); }
:is(.p2-kind, .p2-pill-kind).is-lesson { --tone: var(--text-muted); }
:is(.p2-kind, .p2-pill-kind).is-cert { --tone: color-mix(in srgb, var(--accent) 50%, var(--danger)); }

.p2-hang-than { flex: 1 1 140px; min-width: 120px; display: flex; flex-direction: column; gap: var(--sp-1); padding: var(--sp-1) 0; }
.p2-hang-ten { font-weight: 600; line-height: 1.35; overflow-wrap: break-word; text-wrap: pretty; }
.p2-hang-meta { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Viên nhãn không bao giờ bị cắt, không xuống dòng (§14.3). */
.p2-hang-nhan { display: flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; }
.p2-hang-nhan .pill { flex-shrink: 0; }
.p2-hang-nut { flex-shrink: 0; display: flex; align-items: center; gap: var(--sp-1); }
.p2-doi-ten { height: 28px; padding: 0 var(--sp-2); font-weight: 600; }

/* Nút chỉ có hình, dùng trong hàng cây, đầu bảng thuộc tính, cây điều kiện. */
.p2-ico { display: grid; place-items: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0;
  border: 1px solid var(--border); border-radius: var(--r-sm); background-color: var(--bg);
  color: var(--text-muted); cursor: pointer; }
.p2-ico:hover:not(:disabled) { background-color: var(--bg-hover); color: var(--text); border-color: var(--border-strong); }
.p2-ico:disabled { opacity: 0.4; cursor: not-allowed; }
.p2-ico .nav-ico { width: 14px; height: 14px; }
.p2-ico.is-nho { width: 24px; height: 24px; }
.p2-ico.is-trong { border-color: transparent; background: none; }
.p2-ico.is-xoa { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 22%, var(--bg)); }
.p2-ico.is-xoa:hover:not(:disabled) { background-color: color-mix(in srgb, var(--danger) 8%, var(--bg)); color: var(--danger); }
.p2-link { padding: var(--sp-1) var(--sp-2); border: none; border-radius: var(--r-sm); background: none;
  color: var(--link); font: inherit; font-size: var(--text-12); font-weight: 600; cursor: pointer; white-space: nowrap; }
.p2-link:hover:not(:disabled) { background-color: var(--accent-softer); }
.p2-link:disabled { color: var(--text-muted); opacity: 0.6; cursor: default; }
.nx-btn.p2-nut-nho { height: 28px; padding: 0 var(--sp-2); font-size: var(--text-12); }
.p2-nut-nho .nav-ico { width: 14px; height: 14px; margin-right: var(--sp-1); }

/* ── Bảng thuộc tính ── */
.p2-panel { display: flex; flex-direction: column; }
.p2-panel-hd { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border-subtle); background-color: var(--bg-hover);
  border-radius: var(--r-lg) var(--r-lg) 0 0; }
.p2-panel-ten { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.p2-panel-ten > * { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.p2-panel-title { font-weight: 600; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; line-height: 1.3; }
.p2-pill-kind { --tone: var(--accent); }
.p2-crumbs { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; padding: var(--sp-2) var(--sp-4);
  border-bottom: 1px solid var(--border-subtle); font-size: var(--text-12); }
.p2-crumb { display: inline-flex; align-items: center; gap: var(--sp-2); }
.p2-crumb button { padding: 0; border: none; background: none; font: inherit; color: var(--text-muted); cursor: pointer; }
.p2-crumb button:hover { color: var(--link); }
.p2-crumb button.is-on { color: var(--text); font-weight: 600; }
.p2-crumb-sep { color: var(--border-strong); }
.p2-panel-body { display: flex; flex-direction: column; }
/* Dòng TÓM TẮT dính đáy màn khi bảng dài hơn màn (§14.1). */
.p2-panel-ft { position: sticky; bottom: 0; z-index: 4; display: flex; align-items: flex-start; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--border); background-color: var(--bg-hover);
  border-radius: 0 0 var(--r-lg) var(--r-lg); }
.p2-sum-nhan { flex-shrink: 0; padding: 0 var(--sp-2); border: 1px solid var(--border); border-radius: var(--r-sm);
  background-color: var(--bg); line-height: 1.6; }
.p2-sum-chu { flex: 1; min-width: 0; font-size: var(--text-12); font-weight: 600; color: var(--accent); text-wrap: pretty; }

/* Một khối: đầu có hình, tên in hoa, bộ đếm; thân là lưới ô; cuối là một câu tóm tắt. */
.p2-khoi { display: flex; flex-direction: column; border-top: 1px solid var(--border-subtle); }
.p2-khoi:first-child { border-top: none; }
.p2-khoi-hd { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4) var(--sp-2);
  color: var(--text-muted); }
.p2-khoi-hd > .nav-ico { width: 14px; height: 14px; flex-shrink: 0; }
.p2-khoi-ten { flex: 1; min-width: 0; }
.p2-dem { flex-shrink: 0; font-variant-numeric: tabular-nums; }
.p2-sum { margin: 0; padding: var(--sp-2) var(--sp-4) var(--sp-4); font-size: var(--text-12); color: var(--text-muted); text-wrap: pretty; }

/* Lưới ô: tự xếp một hoặc hai cột theo bề ngang bảng (`auto-fill, minmax(296px, 1fr)` của bản vẽ). */
.p2-luoi { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(296px, 100%), 1fr)); align-items: start; }
.p2-o-rong { grid-column: 1 / -1; min-width: 0; }
.p2-o-dong { min-width: 0; }
.p2-f { padding: var(--sp-1) var(--sp-4); }
.p2-f-l { font-size: var(--text-12); color: var(--text-muted); }
.p2-f-head { padding-top: var(--sp-3); }
.p2-f-cot { display: flex; flex-direction: column; gap: var(--sp-1); padding-top: var(--sp-2); padding-bottom: var(--sp-2); }
/* Dáng DÒNG: nhãn trái, ô phải tối đa 228px — mắt dò nhãn theo một cột thẳng. */
.p2-f-dong { display: flex; align-items: center; gap: var(--sp-2); min-height: 40px; }
.p2-f-dong > .p2-f-l { flex: 1 1 44%; min-width: 0; text-wrap: pretty; }
.p2-f-o { flex: 1 1 52%; min-width: 0; max-width: 228px; display: flex; align-items: center; }
.p2-f-o > .oc, .p2-f-o > .nx-input { width: 100%; }
/* Trường đang ĐẶT RIÊNG: vạch 2px ở mép trái nhãn (§14.2). Chưa dùng tới cho tới khi có kho môn học. */
.p2-f.has-ov { box-shadow: inset 2px 0 0 var(--accent); }
.p2-o-so { display: flex; align-items: center; gap: var(--sp-2); }
.p2-o-so.is-dv { width: 100%; }
.p2-o-so.is-dv .oc { flex: 1; min-width: 0; }
.nx-input.p2-so { width: 72px; font-variant-numeric: tabular-nums; }
.p2-o-ro { font-weight: 600; }
.p2-o-tog { display: flex; align-items: center; }
.p2-f-chk, .p2-f-gate { display: flex; align-items: center; gap: var(--sp-2); min-height: 40px; }
.p2-f-gate .nx-checkbox { flex: 1; min-width: 0; }
.p2-seg { display: flex; flex-wrap: wrap; gap: var(--sp-1); padding: var(--sp-1); border-radius: var(--r-md);
  background: color-mix(in srgb, var(--text-muted) 9%, var(--bg)); }
.p2-seg-nut { flex: 1 1 auto; padding: var(--sp-1) var(--sp-3); border: none; border-radius: var(--r-sm);
  background: transparent; color: var(--text-muted); font: inherit; font-size: var(--text-12); font-weight: 600;
  white-space: nowrap; cursor: pointer; }
.p2-seg-nut.is-on { background-color: var(--bg); color: var(--text); box-shadow: var(--shadow-sm); }
.p2-seg-nut:disabled { cursor: default; }
.p2-seg.is-nho { padding: var(--sp-1); }
.p2-seg.is-nho .p2-seg-nut { flex: 0 0 auto; padding: var(--sp-1) var(--sp-2); font-size: var(--text-11); }
.p2-radio { display: flex; flex-direction: column; gap: var(--sp-1); }
.p2-radio-nut { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-1) 0; border: none;
  background: none; color: var(--text-muted); font: inherit; text-align: left; cursor: pointer; }
.p2-radio-nut.is-on { color: var(--text); font-weight: 600; }
.p2-cham { width: 15px; height: 15px; flex-shrink: 0; border: 1.5px solid var(--border-strong); border-radius: 50%;
  background-color: var(--bg); box-shadow: inset 0 0 0 3px var(--bg); }
.p2-radio-nut.is-on .p2-cham { border-color: var(--accent); background-color: var(--accent); }
.p2-note, .p2-prev { display: flex; align-items: flex-start; gap: var(--sp-2); margin: var(--sp-1) var(--sp-4) var(--sp-2);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--r-md); font-size: var(--text-12); text-wrap: pretty;
  border: 1px solid var(--border); background-color: var(--bg-hover); }
.p2-note .nav-ico, .p2-prev .nav-ico { width: 14px; height: 14px; flex-shrink: 0; margin-top: 0; }
.p2-note.tone-warn { border-color: color-mix(in srgb, var(--warning) 35%, var(--bg));
  background-color: color-mix(in srgb, var(--warning) 8%, var(--bg)); color: var(--warning); }
.p2-note.tone-bad { border-color: color-mix(in srgb, var(--danger) 30%, var(--bg));
  background-color: color-mix(in srgb, var(--danger) 7%, var(--bg)); color: var(--danger); }
.p2-prev { border-style: dashed; border-color: var(--accent-soft); color: var(--accent); }
.p2-acts { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; padding-top: var(--sp-2); padding-bottom: var(--sp-3); }
.p2-acts .nav-ico { margin-right: var(--sp-1); }

/* Danh sách chọn nhiều (`fMulti`). */
.p2-multi { display: flex; flex-direction: column; gap: var(--sp-2); padding-top: var(--sp-2); padding-bottom: var(--sp-3); }
.p2-multi-hd { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.p2-multi-nut { margin-left: auto; display: flex; gap: var(--sp-1); }
.p2-multi-ds { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden; }
.p2-multi-dong { display: flex; align-items: center; gap: var(--sp-2); min-height: 40px; padding: var(--sp-1) var(--sp-3);
  border-top: 1px solid var(--border-subtle); }
.p2-multi-dong:first-child { border-top: none; }
.p2-multi-dong.is-on { background-color: var(--accent-softer); }
.p2-multi-dong .nx-checkbox { flex: 1; min-width: 0; }
.p2-multi-dong.is-on .nx-checkbox-label { font-weight: 600; }

/* ── Cây điều kiện ── */
/* ── Khối Nhiệm vụ ── */
.p2-nv { display: flex; flex-direction: column; gap: var(--sp-2); padding: 0 var(--sp-4) var(--sp-2); }
.p2-nv-rong { padding: var(--sp-4); border: 1px dashed var(--border-strong); border-radius: var(--r-md);
  background-color: var(--bg-hover); text-align: center; font-size: var(--text-12); color: var(--text-muted); }
.p2-nv-the { display: flex; flex-direction: column; border: 1px solid var(--border-subtle); border-radius: var(--r-md); overflow: hidden; }
.p2-nv-the.is-mo { background-color: var(--bg-hover); }
.p2-nv-hd { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); }
.p2-nv-grip { display: grid; place-items: center; color: var(--border-strong); }
.p2-nv-grip .nav-ico { width: 13px; height: 13px; }
.p2-nv-hinh { display: grid; place-items: center; width: 26px; height: 26px; flex-shrink: 0; border-radius: var(--r-sm);
  background-color: color-mix(in srgb, var(--tone) 14%, var(--bg)); color: var(--tone); }
.p2-nv-hinh .nav-ico { width: 14px; height: 14px; }


.p2-nv-hinh.is-attend { --tone: color-mix(in srgb, var(--accent) 50%, var(--danger)); }
.p2-nv-hinh.is-rubric { --tone: var(--success); }
.p2-nv-ten { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.p2-nv-input { width: 100%; min-width: 0; padding: 0; border: none; background: none; font: inherit; font-weight: 600;
  color: var(--text); outline: none; }
.p2-nv-input:focus-visible { box-shadow: 0 1px 0 var(--accent); }
.p2-nv-than { border-top: 1px solid var(--border-subtle); background-color: var(--bg); padding: var(--sp-1) 0 var(--sp-2); }
.p2-nv-them .kebab-menu { min-width: 12rem; }

/* ── Ô chọn có ô tìm (`OChon`) ── */
.oc { position: relative; display: inline-flex; min-width: 0; }
.oc-nut { display: flex; align-items: center; gap: var(--sp-2); width: 100%; min-width: 0; height: 34px;
  padding: 0 var(--sp-2) 0 var(--sp-3); border: 1px solid var(--border); border-radius: var(--r-md);
  background-color: var(--bg); color: var(--text); font: inherit; text-align: left; cursor: pointer; }
.oc-nut:hover:not(:disabled), .oc-nut.is-mo { border-color: var(--accent); }
.oc-nut:focus-visible { outline: 2px solid var(--accent-ring); outline-offset: 1px; }
.oc-nut:disabled { opacity: 0.6; cursor: not-allowed; }
.oc-nut.is-rong .oc-chu { color: var(--text-muted); }
.oc-nut .nav-ico { width: 14px; height: 14px; flex-shrink: 0; color: var(--text-muted); }
.oc-chu { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.oc-hop { position: fixed; z-index: 80; display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--border); border-radius: var(--r-md); background-color: var(--bg); box-shadow: var(--shadow-lg); }
.oc-tim { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--border-subtle); }
.oc-tim .nav-ico { width: 14px; height: 14px; color: var(--text-muted); }
.oc-tim input { flex: 1; min-width: 0; border: none; background: none; font: inherit; color: var(--text); outline: none; }
.oc-ds { overflow-y: auto; padding: var(--sp-1); outline: none; }
.oc-nhom { padding: var(--sp-2) var(--sp-2) var(--sp-1); }
.oc-muc { display: flex; align-items: center; gap: var(--sp-2); width: 100%; padding: var(--sp-2); border: none;
  border-radius: var(--r-sm); background: none; color: var(--text); font: inherit; text-align: left; cursor: pointer; }
.oc-muc:hover, .oc-muc.is-hi { background-color: var(--bg-hover); }
.oc-muc.is-on { color: var(--accent); font-weight: 600; }
.oc-muc .nav-ico { width: 14px; height: 14px; flex-shrink: 0; }
.oc-muc-chu { flex: 1; min-width: 0; }
.oc-rong { padding: var(--sp-3); text-align: center; font-size: var(--text-12); color: var(--text-muted); }

/* ── Thẻ Chứng chỉ: hàng mẫu ── */
.p2-mau .p2-hang-nhan { margin-top: var(--sp-1); }

/* ── Thẻ Phiên bản & Nhật ký (bản 10, D87): hai khung cạnh nhau, mỗi khung 10 dòng một trang ── */
.p2-ls { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: var(--sp-4); align-items: start; }
.p2-ls-khung { display: flex; flex-direction: column; min-width: 0; }
.p2-ls-khung .p2-the-hd .detail-actions { margin-left: auto; }
.p2-ls-dem { display: inline-grid; place-items: center; min-width: 22px; height: 20px; padding: 0 var(--sp-2);
  border-radius: var(--r-full); background-color: var(--bg-hover); color: var(--text-muted); font-size: var(--text-11); font-weight: 600; }
.p2-ls-than { display: flex; flex-direction: column; }
.p2-ls-chan { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-4); border-top: 1px solid var(--border-subtle); }
.p2-ls-chan > .t-sub { flex: 1; min-width: 0; }
.p2-ls-trang { min-width: 44px; text-align: center; font-size: var(--text-12); font-weight: 600; }
/* Dòng "Buổi có mặt" tự sinh: tên không sửa được, in mờ như chữ chỉ đọc. */
.p2-nv-the.is-tu-sinh .p2-nv-input:disabled { color: var(--text-muted); }

/* ── Dòng phiên bản trong khung ── */
.p2-ver { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--border-subtle); }
.p2-ver.is-hien { background-color: color-mix(in srgb, var(--success) 6%, var(--bg)); }
.p2-ver-so { flex-shrink: 0; padding: 0 var(--sp-2); border: 1px solid var(--border); border-radius: var(--r-sm);
  background-color: var(--bg); font-family: var(--font-mono); font-size: var(--text-12); font-weight: 600; line-height: 1.8; }
.p2-ver-than { flex: 1 1 260px; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.p2-ver-note { font-weight: 600; text-wrap: pretty; }

/* ── Dòng nhật ký trong khung ── */
.p2-log { display: flex; align-items: flex-start; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--border-subtle); }
.p2-log-than { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.p2-log-dau { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.p2-log-dt { font-weight: 600; text-wrap: pretty; }
.p2-log-ct { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.p2-log-ct .nav-ico { width: 12px; height: 12px; flex-shrink: 0; }
.p2-log-tu, .p2-log-den { padding: 0 var(--sp-2); border-radius: var(--r-sm); font-weight: 600; line-height: 1.7; text-wrap: pretty; }
.p2-log-tu { background-color: var(--bg-hover); color: var(--text-muted); }
.p2-log-den { background-color: var(--accent-softer); color: var(--accent); }
.p2-log-ai { font-size: var(--text-12); }

/* ── Khung danh sách: nút Bộ lọc gom (`lsFl2`) ── */
.ls-loc { position: relative; display: inline-flex; }
.ls-loc .nav-ico { margin-right: var(--sp-1); }
.ls-loc-so { display: inline-grid; place-items: center; min-width: 18px; height: 18px; margin-left: var(--sp-2);
  padding: 0 var(--sp-1); border-radius: var(--r-full); background-color: var(--accent); color: var(--on-accent);
  font-size: var(--text-11); font-weight: 600; }
.ls-loc-hop { position: absolute; top: calc(100% + var(--sp-1)); right: 0; z-index: 40; width: 18rem;
  display: flex; flex-direction: column; padding: var(--sp-1); border: 1px solid var(--border); border-radius: var(--r-md);
  background-color: var(--bg); box-shadow: var(--shadow-lg); }
.ls-loc-dau { display: flex; align-items: center; gap: var(--sp-2); width: 100%; padding: var(--sp-2) var(--sp-3);
  border: none; border-radius: var(--r-sm); background: none; color: var(--text); font: inherit; text-align: left; cursor: pointer; }
.ls-loc-dau:hover { background-color: var(--bg-hover); }
.ls-loc-dau > span:first-child { flex: 1; font-weight: 600; }
.ls-loc-dau .nav-ico { width: 14px; height: 14px; color: var(--text-muted); }
.ls-loc-hien { font-size: var(--text-12); color: var(--text-muted); }
.ls-loc-hien.is-on { color: var(--accent); font-weight: 600; }
.ls-loc-ds { display: flex; flex-direction: column; padding: 0 var(--sp-3) var(--sp-2); }
.ls-loc-muc { padding: var(--sp-1) 0; }
.ls-loc-xoa { margin-top: var(--sp-1); padding: var(--sp-2) var(--sp-3); border: none; border-top: 1px solid var(--border-subtle);
  background: none; color: var(--link); font: inherit; font-size: var(--text-12); font-weight: 600; text-align: left; cursor: pointer; }

/* ---- 02-lo-trinh-hoc-phan-mon-hoc: bản 8 (D81, bản vẽ 24/09 13:42) ------------------------------ */
/* Khối Điều kiện hoàn thành: phần TỰ ĐỘNG THEO CẤU HÌNH PHÍA TRÊN — mỗi dòng một điều kiện đọc từ khối
   khác, viên nhãn nguồn ở cuối; dòng còn thiếu cấu hình tô vàng, cùng tông với mọi chỗ "chưa đủ". */
.p2-tu-sinh { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-1) var(--sp-4) var(--sp-3); }
.p2-tu-sinh-dong { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border-subtle); border-radius: var(--r-md); background-color: var(--bg-hover); }
.p2-tu-sinh-dong .nav-ico { width: 13px; height: 13px; flex-shrink: 0; color: var(--success); }
.p2-tu-sinh-dong.is-thieu { border-color: color-mix(in srgb, var(--warning) 35%, var(--bg));
  background-color: color-mix(in srgb, var(--warning) 8%, var(--bg)); color: var(--warning); }
.p2-tu-sinh-dong.is-thieu .nav-ico { color: var(--warning); }
.p2-tu-sinh-chu { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--text-12); font-weight: 600; }
.p2-tu-sinh-nguon { flex-shrink: 0; padding: 0 var(--sp-2); border: 1px solid var(--border); border-radius: var(--r-sm);
  background-color: var(--bg); color: var(--text-muted); font-size: var(--text-11); font-weight: 600; white-space: nowrap; }
/* Dòng phạm vi của môn học (§2.3): sửa ở đây chỉ áp cho lộ trình này. */
.p2-pham-vi { display: flex; align-items: center; gap: var(--sp-2); margin: 0; padding: var(--sp-2) var(--sp-4);
  border-bottom: 1px solid var(--border-subtle); background-color: color-mix(in srgb, var(--accent) 6%, var(--bg));
  color: var(--accent); font-size: var(--text-12); font-weight: 600; }
.p2-pham-vi .nav-ico { width: 13px; height: 13px; flex-shrink: 0; }

/* ---- 02-lo-trinh-hoc-phan-mon-hoc: câu hỏi cuối bài soạn trong bài học (D82) ------------------ */
/* Một thẻ mỗi câu, cùng dáng thẻ trang nội dung (`cl-page`). Đáp án là danh sách dòng: nút tròn
   (một đáp án, đúng/sai) hoặc vuông (nhiều đáp án) đánh dấu đáp án đúng, rồi ô chữ. Dòng đúng tô nền
   thành công để liếc là thấy đáp án nào đang được chọn là đúng. */
.lq-cau { display: flex; flex-direction: column; gap: var(--sp-2); }
.lq-cau .cl-page-hd { justify-content: flex-start; }
.lq-cau .cl-page-hd .detail-actions { margin-left: auto; }
.lq-loai-o { display: flex; align-items: center; gap: var(--sp-2); }
.lq-loai { width: 170px; }
.lq-dap-an { display: flex; flex-direction: column; gap: var(--sp-1); }
.lq-da { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--border-subtle); border-radius: var(--r-md); background-color: var(--bg); }
.lq-da.is-dung { border-color: color-mix(in srgb, var(--success) 40%, var(--bg));
  background-color: color-mix(in srgb, var(--success) 8%, var(--bg)); }
.lq-dung { flex-shrink: 0; display: grid; place-items: center; width: 20px; height: 20px; padding: 0;
  border: 1.5px solid var(--border-strong); border-radius: var(--r-full); background-color: var(--bg);
  color: var(--on-accent); cursor: pointer; }
.lq-dung.is-vuong { border-radius: var(--r-sm); }
.lq-dung.is-on { border-color: var(--success); background-color: var(--success); }
.lq-dung .nav-ico { width: 12px; height: 12px; }
.lq-dung:disabled { cursor: default; }
.lq-da-o { flex: 1; min-width: 0; }
.lq-da-o.nx-input:not(:hover):not(:focus) { border-color: transparent; background-color: transparent; }
.lq-da-chu { flex: 1; font-size: var(--text-12); font-weight: 600; }
.lq-them-da { align-self: flex-start; }
.lq-thieu { display: flex; align-items: center; gap: var(--sp-2); margin: 0; font-size: var(--text-12);
  font-weight: 600; color: var(--warning); }
.lq-thieu .nav-ico { width: 13px; height: 13px; flex-shrink: 0; }
.lq-loi { margin: var(--sp-1) 0 0; padding-left: var(--sp-5); }

/* ── Màn Khoá học (thư mục 04) — hình lấy từ bản vẽ `LMS Admin.dc.html` 22/09 ────────────────
   Thẻ chi tiết của bản vẽ là một CHỒNG KHỐI XẾP GẤP, không phải nhiều thẻ ngang: mỗi khối có tên
   bên trái, câu tóm tắt ở giữa, mũi tên bên phải; mở ra mới thấy các dòng nhãn–giá trị. Nhờ câu
   tóm tắt mà đóng hết vẫn đọc được tình trạng khoá, đó là lý do bản vẽ chọn hình này. */
.band-col { flex-direction: column; align-items: stretch; }
.acc-stack { display: flex; flex-direction: column; gap: var(--sp-3); }
.acc { border: 1px solid var(--border); border-radius: var(--r-lg); background-color: var(--bg);
  overflow: hidden; }
.acc-hd { display: flex; align-items: center; gap: var(--sp-3); width: 100%;
  padding: var(--sp-3) var(--sp-4); border: 0; background: none; text-align: left; cursor: pointer; }
.acc-hd:hover { background-color: var(--bg-hover); }
.acc-title { flex-shrink: 0; font-size: var(--text-14); font-weight: 600; color: var(--text); }
.acc-sum { flex: 1; min-width: 0; font-size: var(--text-12); color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.acc-chev { flex-shrink: 0; color: var(--text-muted); }
.acc-body { padding: var(--sp-3) var(--sp-4) var(--sp-4); border-top: 1px solid var(--border-subtle); }
.acc-row { display: flex; align-items: baseline; gap: var(--sp-4); padding: var(--sp-2) 0; }
.acc-row + .acc-row { border-top: 1px solid var(--border-subtle); }
.acc-row-label { width: 190px; flex-shrink: 0; font-size: var(--text-12); color: var(--text-muted); }
.acc-row-value { flex: 1; min-width: 0; font-size: var(--text-13); color: var(--text); }
.sess-list { margin-top: var(--sp-2); }

/* Mã khoá và nguồn: hai viên nhãn nhỏ ở đầu trang, mã dùng chữ đều để đọc ra ký tự. */
.code-chip { padding: var(--sp-1) var(--sp-2); border-radius: var(--r-sm);
  background-color: var(--bg-hover); color: var(--text-muted);
  font-family: var(--font-mono); font-size: var(--text-11); }
.src-chip { align-self: flex-start; padding: var(--sp-1) var(--sp-3); border: 1px solid var(--accent-ring);
  border-radius: var(--r-full); background-color: var(--accent-softer); color: var(--accent-strong);
  font-size: var(--text-12); font-weight: 600; }
.chip-row { display: flex; gap: var(--sp-1); flex-wrap: wrap; margin-top: var(--sp-2); }

/* Một dòng người: ảnh chữ · tên và dòng phụ · viên nhãn bên phải. Bản vẽ dùng hình này cho cả
   danh sách học viên lẫn danh sách giảng viên, nên một lớp dùng chung cho cả hai. */
.person-row { display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) 0; border-top: 1px solid var(--border-subtle); }
.person-row:first-child { border-top: 0; }
.card-hd { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  flex-wrap: wrap; margin-bottom: var(--sp-2); }
.t-title-sm { font-size: var(--text-15); font-weight: 600; color: var(--text); }
.pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--sp-2); max-height: 260px; overflow-y: auto; }

/* ---- 09-cau-hoi-cuoi-bai ---- */
/* Màn Ngân hàng đề. Chỉ hình học và nhịp; mọi màu qua var(--token) (D32). */

/* 🔴 BỐN LỚP DƯỚI ĐÂY LÀ TẠM, sinh ra lúc gộp nhánh 22/09.
   Bản thiết kế LMS Admin 17/09 thay khung danh sách cũ (`.page-card`, `.page-table`,
   `.page-empty`, `.row-actions`) bằng khung dùng chung `list-shell` (`.ls-*`) của thư mục 02, và
   xoá hẳn bốn lớp cũ. Màn Ngân hàng đề viết TRƯỚC lúc đó nên vẫn đang dùng chúng — không chép lại
   thì bảng ngân hàng đề mất viền, mất nền đầu cột và mất cả khung thẻ.
   Chép lại ở ĐÂY, trong khối của thư mục 09, chứ không khôi phục vào phần dùng chung: đây là nợ
   của riêng màn này. Việc đúng là chuyển `LessonQuestions.mjs` sang `list-shell` rồi XOÁ khối này.
   Đã ghi ở REPORT.md của thư mục 09. */
.page-card {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background-color: var(--bg);
  overflow: hidden;
}
.page-table-scroll { overflow-x: auto; }
.page-table { width: 100%; min-width: 880px; border-collapse: collapse; font-size: var(--text-13); }
.page-table th {
  text-align: left;
  white-space: nowrap;
  padding: var(--sp-2) var(--sp-4);
  background-color: var(--bg-hover);
  border-bottom: 1px solid var(--border);
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: var(--text-11);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.35;
}
.page-table td { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border-subtle); line-height: 1.45; }
.page-table tr:last-child td { border-bottom: none; }
.page-empty {
  padding: var(--sp-8) var(--sp-6);
  text-align: center;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background-color: var(--bg);
  font-size: var(--text-13);
  color: var(--text-muted);
}
.row-actions { display: inline-flex; gap: var(--sp-1); justify-content: flex-end; }


/* Thẻ đầu của chi tiết bộ đề, theo bản vẽ (dòng 1743) và ảnh anh Long gửi 23/09: trái là tên +
   viên nhãn + dòng meta, phải là ba nút và nút ba chấm. `overflow: visible` vì `.page-card` mặc
   định cắt tràn, mà menu ba chấm phải rơi ra ngoài thẻ. */
.qb-head {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-4);
  overflow: visible;
}
.qb-head-main { min-width: 0; display: grid; gap: var(--sp-2); }
.qb-head-title { margin: 0; font-size: var(--text-18); font-weight: 600; letter-spacing: -0.01em; }
.qb-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
/* Mã bộ đề và số bản: ô chữ đơn cách, góc nhỏ — bản vẽ tách chúng khỏi viên nhãn tròn. */
.qb-code {
  padding: 0 var(--sp-2);
  line-height: 1.6;
  border-radius: var(--r-sm);
  background: var(--bg-hover);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: var(--text-11);
  font-weight: 600;
}
.qb-code.is-ver { background: var(--accent-softer); color: var(--accent-strong); }
.qb-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-4);
  color: var(--text-muted);
  font-size: var(--text-13);
}
.qb-meta strong { color: var(--text); }
.qb-meta-bad { color: var(--danger); font-weight: 600; }
/* Nút của thẻ đầu là nút SDK (`nx-btn`, chuẩn mục 3) có hình đi kèm chữ; `.nx-btn` đã có `gap`.
   Nút AI "Sắp có": tắt nhưng KHÔNG mờ — bản vẽ để nó nền nhạt chữ nhạt, đọc được là "có đường này". */
.qb-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2); }
.qb-actions .nx-btn { font-size: var(--text-12); padding: 0 var(--sp-3); }
.qb-actions .nx-btn.qb-ai { background: var(--bg-hover); color: var(--text-muted); opacity: 1; cursor: default; }
/* Nút ba chấm cỡ lớn, ngang hàng với ba nút cạnh nó. */
.kebab-btn.is-lg { width: 34px; height: 34px; }

/* Dải giải thích vì sao bản này sửa được hay không. Nền nhạt để tách khỏi phần số liệu ngay trên. */
.qb-note {
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-md);
  background-color: var(--bg-hover);
  color: var(--text-muted);
  font-size: var(--text-13);
}

.qb-section { padding: var(--sp-4); margin-top: var(--sp-3); }

/* Danh sách câu: MỖI CÂU MỘT THẺ, cách nhau một nhịp — đúng bản vẽ, không còn một thẻ dài kẻ dòng. */
.qb-qs { display: grid; gap: var(--sp-3); margin-top: var(--sp-3); }
.qb-q { display: grid; gap: var(--sp-2); padding: var(--sp-4); overflow: visible; }
.qb-q-head { display: flex; align-items: flex-start; gap: var(--sp-3); }
/* Ô số câu: vuông 26px, góc bo, nền nhạt. */
.qb-q-no {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border-radius: var(--r-md);
  background: var(--bg-hover);
  color: var(--text);
  font-size: var(--text-12);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.qb-q-stem { flex: 1; min-width: 0; font-size: var(--text-14); font-weight: 600; text-wrap: pretty; }
.qb-q-tags { flex-shrink: 0; display: inline-flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; justify-content: flex-end; }
.qb-q-acts { display: inline-flex; gap: var(--sp-1); margin-left: var(--sp-1); }
/* Nút HÌNH 28px: sửa · nhân bản · gỡ — `IconButton` của SDK, thêm viền như bản vẽ. Nút gỡ viền đỏ
   nhạt để tách khỏi hai nút kia. */
.nx-iconbtn.qb-ibtn { width: 28px; height: 28px; border: 1px solid var(--border); color: var(--text); }
.nx-iconbtn.qb-ibtn.is-danger { border-color: color-mix(in srgb, var(--danger) 35%, var(--border)); color: var(--danger); }
.nx-iconbtn.qb-ibtn.is-danger:not(:disabled):hover { background: color-mix(in srgb, var(--danger) 8%, var(--bg)); color: var(--danger); }
.nx-iconbtn.qb-ibtn:focus-visible { outline: 2px solid var(--accent-ring); outline-offset: 2px; }
.qb-ibtn .nav-ico { width: 13px; height: 13px; }

/* Lưới phương án: thụt vào ngang ô số (26px + một nhịp), nhiều cột trên màn rộng, một cột khi hẹp. */
.qb-opts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--sp-2);
  padding-left: calc(26px + var(--sp-3));
}
.qb-opt {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-md);
  font-size: var(--text-13);
  font-weight: 600;
  color: var(--text);
}
/* Ô tròn trước mỗi phương án; ở đáp án đúng nó đầy màu xanh và mang dấu tích. */
.qb-dot {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-full);
  background: var(--bg);
  color: var(--on-accent);
}
.qb-dot .nav-ico { width: 11px; height: 11px; }
.qb-opt-text { flex: 1; min-width: 0; text-wrap: pretty; }
/* Ô đáp án đúng. CHỈ hiện với quản trị: người xem không nhận `correct` nên không ô nào có lớp này. */
.qb-opt.is-right {
  border-color: color-mix(in srgb, var(--success) 45%, var(--border));
  background-color: color-mix(in srgb, var(--success) 7%, var(--bg));
  color: var(--success);
}
.qb-opt.is-right .qb-dot { border-color: var(--success); background: var(--success); }

/* Giải thích: một dòng chữ nhạt thụt cùng lưới phương án, không đóng khung — đúng bản vẽ. */
.qb-explain { padding-left: calc(26px + var(--sp-3)); color: var(--text-muted); font-size: var(--text-12); text-wrap: pretty; }

/* Hàng ngân hàng bấm được: đổi nền khi rê và có viền khi lấy tiêu điểm bằng bàn phím. */
.qb-row { cursor: pointer; }
.qb-row:hover { background-color: var(--bg-hover); }
.qb-row:focus-visible { outline: 2px solid var(--accent-ring); outline-offset: -2px; }
.qb-warn { margin-left: var(--sp-1); }
.qb-date { display: block; margin-top: var(--sp-1); }

/* ── Biểu mẫu soạn câu hỏi ── */
.qb-form { display: grid; gap: var(--sp-3); }
.qb-form label { display: grid; gap: var(--sp-1); font-size: var(--text-13); font-weight: 600; }
.qb-form-label { font-size: var(--text-13); font-weight: 600; }
.qb-form-opts { display: grid; gap: var(--sp-2); }
.qb-form-opt { display: flex; align-items: center; gap: var(--sp-2); }
.qb-form-opt .nx-input { flex: 1; }
.qb-form-add { justify-self: start; }

/* Nút đánh dấu đáp án đúng. Vuông tròn 32px để bấm được bằng ngón tay, và đổi hẳn màu khi bật —
   người soạn phải thấy ngay câu nào đã có đáp án mà không cần đọc kỹ. */
.qb-pick {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-full);
  background: none;
  color: var(--text-muted);
  font-size: var(--text-12);
  font-weight: 600;
  cursor: pointer;
}
.qb-pick.is-on {
  border-color: var(--success);
  background-color: color-mix(in srgb, var(--success) 16%, transparent);
  color: var(--success);
}
.qb-pick:focus-visible { outline: 2px solid var(--accent-ring); outline-offset: 2px; }

.qb-form-src { display: grid; gap: var(--sp-2); }
.qb-form-src-row { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: var(--sp-2); }

/* Bước 038 (22/09): điểm, dải ô số, hộp nhập tệp và hộp cấu hình chấm điểm.
   Màu chỉ dùng token — lint chặn màu viết thẳng. */

/* Hàng hai cột Mức độ · Điểm trong biểu mẫu câu hỏi, đúng bản vẽ. */
.qb-form-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }

/* Xem trước của hộp nhập tệp: cùng khuôn thẻ câu, nhưng cuộn được vì tệp có thể vài trăm câu.
   Trần tính bằng `rem` chứ KHÔNG `vh`: app sống trong iframe do host cấp kích thước, `vh` đo theo
   viewport trình duyệt nên lệch (bẫy đã ghi ở CLAUDE.md). */
.qb-preview { max-height: 26rem; overflow-y: auto; }

/* Hộp cấu hình chấm điểm. */
.qb-score-body { display: grid; gap: var(--sp-3); }
/* Nhãn NẰM TRÊN ô, như mọi biểu mẫu khác của màn. Không có luật này thì `<label>Cách chia điểm
   <Select/></label>` xếp ngang và nhãn dính vào ô chọn. */
/* Nhãn NẰM TRÊN ô chọn. `.nx-input` của SDK có `width: 100%` nên ô nhập tự xuống dòng dưới nhãn,
   còn `.nx-select` KHÔNG có — nó rộng theo nội dung và nằm ngay cạnh chữ. Đặt bề rộng ở đây để hàng
   "Cách chia điểm" xếp giống bốn ô phía trên nó. */
.qb-score-mode { display: grid; gap: var(--sp-1); font-size: var(--text-13); font-weight: 600; }
.qb-score-mode .nx-select { width: 100%; }
.qb-score-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}
.qb-score-sum {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  background: var(--bg-hover);
  font-variant-numeric: tabular-nums;
}
.qb-score-sum.is-ok strong { color: var(--success); }
.qb-score-sum.is-bad strong { color: var(--danger); }
.qb-score-cell { width: 110px; }
.qb-note.is-bad { border-color: var(--danger); color: var(--danger); }
.qb-switches { display: grid; gap: var(--sp-2); margin-top: var(--sp-3); }

/* Ba thẻ trong trang chi tiết bộ đề (23/09) — Câu hỏi · Phiên bản · Nhật ký.
   Khác thanh thẻ `.ws-tabs` ở đầu màn: đó là thẻ giữa các KHO, đây là thẻ trong MỘT bộ đề. */
.qb-tabs {
  display: inline-flex;
  gap: var(--sp-1);
  margin-top: var(--sp-3);
  padding: var(--sp-1);
  border-radius: var(--r-lg);
  background: var(--bg-hover);
}
.qb-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border: none;
  border-radius: var(--r-md);
  background: none;
  color: var(--text-muted);
  font-size: var(--text-13);
  font-weight: 600;
  cursor: pointer;
}
.qb-tab.is-on { background: var(--bg); color: var(--text); box-shadow: var(--shadow-sm); }
.qb-tab.is-on .nav-ico { color: var(--accent-strong); }
.qb-tab .nav-ico { width: 14px; height: 14px; }
.qb-tab:focus-visible { outline: 2px solid var(--accent-ring); outline-offset: 2px; }

/* Khung chung của thẻ Phiên bản và thẻ Nhật ký. */
.qb-panel { margin-top: var(--sp-3); padding: 0; overflow: hidden; }
.qb-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border-subtle);
}
.qb-panel-title { font-size: var(--text-14); font-weight: 600; }

/* Một dòng phiên bản. */
.qb-ver {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--border-subtle);
}
.qb-ver:first-of-type { border-top: 0; }
/* Bản ĐANG PHÁT HÀNH tô nhạt — đó là bản học viên đang làm, không phải bản số lớn nhất. */
.qb-ver.is-current { background: var(--accent-softer); }
/* Bản ĐANG MỞ trên màn: viền trái đậm để biết dòng "Xem" nào ứng với thứ đang thấy. */
.qb-ver.is-viewing { box-shadow: inset 3px 0 0 var(--accent); }
.qb-ver-main { flex: 1 1 240px; min-width: 0; }
.qb-ver-who, .qb-ver-at { flex-shrink: 0; font-size: var(--text-12); }
.qb-ver-acts { display: flex; gap: var(--sp-2); flex-shrink: 0; }

/* Một dòng nhật ký. */
.qb-log {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--border-subtle);
}
.qb-log:first-of-type { border-top: 0; }
.qb-log-main { flex: 1; min-width: 0; display: grid; gap: var(--sp-1); }
.qb-log-top { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.qb-log-target { font-size: var(--text-13); font-weight: 600; }
.qb-log-detail {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  font-size: var(--text-12);
  color: var(--text-muted);
}
.qb-log-fromto { display: inline-flex; align-items: center; gap: var(--sp-2); }
.qb-log-right { flex-shrink: 0; display: grid; justify-items: end; }

/* Ô tròn chữ cái của người — dùng ở cột Cập nhật và ở mỗi dòng nhật ký. */
.qb-who { display: inline-flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.qb-who-dot {
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-size: var(--text-11);
  font-weight: 600;
}

/* Bọc ngoài danh sách bộ đề: `ListShell` tự lo khung, chỗ này chỉ để treo hai hộp thoại. */
.qb-wrap { display: contents; }

/* Hàng phương án trong hộp sửa câu — mỗi phương án MỘT HÀNG, đúng bản vẽ. */
.qb-form-opt {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-lg);
  background: var(--bg);
}
/* Hàng của đáp án đúng tô nhạt và viền xanh — nhìn một cái biết đáp án nào đang được chọn. */
.qb-form-opt.is-right { border-color: var(--success); background: color-mix(in srgb, var(--success) 8%, var(--bg)); }
.qb-form-opt-key {
  width: 18px;
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: var(--text-12);
  font-weight: 600;
  color: var(--text-muted);
}
.qb-form-opt .nx-input { flex: 1; min-width: 0; }
.qb-form-opt-del { flex-shrink: 0; }

/* ---- 02-lo-trinh-hoc-phan-mon-hoc: bản 6 — kho môn học, trọng số tiến độ (D84, D85, bản vẽ 25/09) ---- */

/* Bài học của BẢN GỐC trong cây lộ trình: chỉ đọc, nên nhạt hơn và không có tay kéo. */
.p2-hang.is-lib { color: var(--text-muted); }
.p2-hang.is-lib .p2-hang-ten { font-weight: 400; }
.p2-cay-rong { margin: 0; padding: var(--sp-4); text-align: center; }

/* Khối Trọng số tiến độ (`p2BlockWeights`): tổng · thanh tiến · mỗi con một dòng. */
.p2-ts { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-1) var(--sp-4) var(--sp-3); }
.p2-ts-dau { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.p2-ts-tong { font-size: var(--text-15); font-weight: 600; font-variant-numeric: tabular-nums; }
.p2-ts-tong.is-du { color: var(--success); }
.p2-ts-tong.is-lech { color: var(--danger); }
.p2-ts-nut { margin-left: auto; }
.p2-ts-thanh { display: block; height: 6px; border-radius: var(--r-full); background-color: var(--border-subtle); overflow: hidden; }
.p2-ts-thanh > span { display: block; height: 100%; border-radius: var(--r-full); background-color: var(--success); }
.p2-ts-thanh > span.is-lech { background-color: var(--warning); }
.p2-ts-ds { display: flex; flex-direction: column; border: 1px solid var(--border-subtle); border-radius: var(--r-md); }
.p2-ts-dong { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); }
.p2-ts-dong + .p2-ts-dong { border-top: 1px solid var(--border-subtle); }
.p2-ts-dong.is-tu-chon { color: var(--text-muted); }
.p2-ts-ten { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 0;
  padding: 0; border: 0; background: none; color: inherit; font: inherit; font-size: var(--text-12); font-weight: 600;
  text-align: left; cursor: pointer; }
.p2-ts-ten > span:first-child { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.p2-ts-ten:hover > span:first-child { color: var(--accent-strong); }
.p2-ico.is-khoa { color: var(--accent-strong); background-color: var(--accent-softer); }

/* Thẻ Ngưỡng đạt bài thi của Phiếu điểm (chế độ Theo hoàn thành). */
.pd-nguong-thi .pd-tp { padding-left: var(--sp-4); padding-right: var(--sp-4); }
.pd-tag.is-exam { color: var(--accent-strong); background-color: var(--accent-softer); }

/* Hộp Tạo lộ trình / Tạo môn học (`cnVals`). */
.p2-tao-loi { color: var(--danger); font-size: var(--text-11); font-weight: 400; }
.p2-tao-2o { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: var(--sp-3); }
.p2-tao-cach { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-2); }
.p2-tao-o { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--r-md); background-color: var(--bg); color: var(--text);
  font: inherit; font-size: var(--text-12); font-weight: 600; cursor: pointer; text-align: left; }
.p2-tao-o:hover { border-color: var(--accent-soft); }
.p2-tao-o.is-on { border-color: var(--accent); background-color: var(--accent-softer); color: var(--accent-strong); }
.p2-tao-cham { width: 12px; height: 12px; flex-shrink: 0; border: 1px solid var(--border-strong); border-radius: var(--r-full); }
.p2-tao-o.is-on .p2-tao-cham { border-color: var(--accent); background-color: var(--accent); box-shadow: inset 0 0 0 2px var(--bg); }
.p2-tao-tt { margin-right: auto; }
@media (max-width: 520px) { .p2-tao-2o, .p2-tao-cach { grid-template-columns: minmax(0, 1fr); } }

/* Chi tiết môn học: dải cam "Đang sửa bản gốc" và hộp ảnh hưởng khi gỡ xuất bản. */
.p2-ban-goc { display: flex; align-items: flex-start; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3);
  border: 1px solid color-mix(in srgb, var(--warning) 35%, var(--bg)); border-radius: var(--r-md);
  background-color: color-mix(in srgb, var(--warning) 8%, var(--bg)); color: var(--warning); font-size: var(--text-12); }
.p2-ban-goc .nav-ico { flex-shrink: 0; width: 14px; height: 14px; }
.p2-anh-huong { display: flex; align-items: flex-start; gap: var(--sp-3); padding: var(--sp-3);
  border-radius: var(--r-md); background-color: color-mix(in srgb, var(--danger) 8%, var(--bg)); color: var(--text); font-size: var(--text-12); }
.p2-anh-huong > .nav-ico { flex-shrink: 0; width: 17px; height: 17px; color: var(--danger); }

/* Cột Nơi đang dùng của danh sách môn: một liên kết mở thẳng thẻ Nơi đang dùng. */
.p2-lien-ket { padding: 0; border: 0; background: none; color: var(--accent-strong); font: inherit; font-size: var(--text-12);
  font-weight: 600; text-align: left; cursor: pointer; }
.p2-lien-ket:hover { text-decoration: underline; }

/* Thẻ Nơi đang dùng (`p2UsedVals`): bảng năm cột, bấm hàng mở đúng nút trong lộ trình. */
.p2-dung { display: flex; flex-direction: column; overflow-x: auto; }
.p2-dung-hang { display: grid; grid-template-columns: minmax(180px, 1.3fr) minmax(140px, 1fr) 92px 110px 124px;
  align-items: center; gap: var(--sp-3); min-width: 680px; padding: var(--sp-2) var(--sp-4);
  border: 0; border-top: 1px solid var(--border-subtle); background: none; color: var(--text); font: inherit;
  font-size: var(--text-12); text-align: left; }
button.p2-dung-hang { cursor: pointer; }
button.p2-dung-hang:hover { background-color: var(--bg-hover); }
.p2-dung-hang.is-dau { color: var(--text-muted); font-size: var(--text-11); font-weight: 600; }
.p2-dung-hang .is-phai { text-align: right; }
.p2-dung-lt { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-1); }
.p2-dung-ten { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.p2-dung-hp { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- 02-lo-trinh-hoc-phan-mon-hoc: bản 7 — hộp xem nhanh (`peekVals`, bản vẽ 25/09 11:44) ---- */
.p2-xn-nhan { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-3); }
.p2-xn-rows { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-2); }
.p2-xn-row { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border-subtle); border-radius: var(--r-md); background-color: var(--bg-hover); }
.p2-xn-chan { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); width: 100%; }
.p2-xn-gt { font-weight: 600; font-size: var(--text-13); font-variant-numeric: tabular-nums; }
.p2-xn-sec { margin-top: var(--sp-3); border: 1px solid var(--border-subtle); border-radius: var(--r-md); }
.p2-xn-sec-hd { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3); background-color: var(--bg-hover); border-bottom: 1px solid var(--border-subtle); }
.p2-xn-it { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3); font-size: var(--text-12); }
.p2-xn-it + .p2-xn-it { border-top: 1px solid var(--border-subtle); }
.p2-xn-it-ten { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.p2-xn-it > .t-sub { flex-shrink: 0; }
.p2-xn-rong { padding: var(--sp-3); text-align: center; }
@media (max-width: 640px) { .p2-xn-rows { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
