/* ============================================================
   Clickbuy Member — single stylesheet · zero build
   Vùng (south/north) và chế độ màu (light/dark) là 2 attribute
   độc lập trên <html>. Cascade lo phần còn lại.
   ============================================================ */
@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:wght@400;500;600;700&display=swap');

/* ---------- BẤT BIẾN: giống nhau ở mọi vùng / chế độ ---------- */
:root {
  --bs-body-font-family: 'Be Vietnam Pro', system-ui, sans-serif;
  --bs-border-radius-sm: 4px;
  --bs-border-radius: 6px;
  --bs-border-radius-lg: 8px;
  --cb-shadow: 0 1px 3px rgb(0 0 0 / .08);
  /* Placeholder ảnh SP (product/warranty card) — resolve token theo chế độ màu tại điểm dùng */
  --cb-hatch: repeating-linear-gradient(45deg, var(--cb-surface-2), var(--cb-surface-2) 6px, var(--cb-border) 6px, var(--cb-border) 7px);

  /* Trạng thái đơn — 5 khe, né cả đỏ lẫn gold (light) */
  --cb-st1-bg:#EDE9FE; --cb-st1-fg:#5B21B6;  /* Chờ        · tím  */
  --cb-st2-bg:#DBEAFE; --cb-st2-fg:#1E40AF;  /* Đang xử lý · xanh */
  --cb-st3-bg:#DCFCE7; --cb-st3-fg:#166534;  /* Hoàn tất   · lá   */
  --cb-st4-bg:#F1F5F9; --cb-st4-fg:#475569;  /* Đã hủy     · xám  */
  --cb-st5-bg:#FDE7E9; --cb-st5-fg:#B91C1C;  /* Cần chú ý  · đỏ báo */

  /* Hạng — thang phi sắc 4 bậc (KHÔNG gold) */
  --cb-tier1-from:#E2E8F0; --cb-tier1-to:#CBD5E1; --cb-tier1-fg:#334155;
  --cb-tier2-from:#B8C2CE; --cb-tier2-to:#94A3B8; --cb-tier2-fg:#1F2937; /* CONCERN-017: chữ tối trên bạc sáng — AA 5.72:1 (trắng cũ fail 2.56:1) */
  --cb-tier3-from:#475569; --cb-tier3-to:#334155; --cb-tier3-fg:#F8FAFC;
  --cb-tier4-from:#1F2937; --cb-tier4-to:#0F172A; --cb-tier4-fg:#F8FAFC;
  /* CONCERN-019: bỏ --cb-tier-locked-* (#F1F5F9 cố định ở :root → vỡ dark). Nhóm khóa dùng .cb-tier-locked-box theme-aware (DESIGN_SYSTEM §11/§12). */

  /* Edu — hạng NGOÀI thang tích lũy ⇒ bộ token RIÊNG (chàm), CỐ Ý khác cả thang phi sắc lẫn tím --cb-st1 của trạng
     thái đơn, để một màu không mang hai nghĩa. ⚠️ KHÁC 4 token hạng ở trên: bộ này CÓ override dark (bên dưới) vì
     nó phủ cả nền thẻ lẫn viền — cặp sáng #EEF2FF/#E0E7FF trên nền tối sẽ chói và viền biến mất.
     Contrast đo: fg #3730A3 trên đầu tối #E0E7FF ≈ 8.1:1 (AA ✅). */
  --cb-edu-from:#EEF2FF; --cb-edu-to:#E0E7FF; --cb-edu-fg:#3730A3; --cb-edu-border:#C7D2FE;

  /* Tiền */
  --cb-money-discount:#166534;
  --cb-money-due:#B91C1C;
  --cb-money-paid:#475569;

  /* Sao đánh giá — xám cố định (né đỏ/gold — DESIGN_SYSTEM §6) */
  --cb-star-on:#475569;
}

/* ---------- VÙNG: chỉ brand · LUÔN đi thành CẶP ----------
   2 gradient, 2 vai trò KHÁC NHAU — đừng gộp:
   · --cb-brand-gradient  = nền THANH thương hiệu (site header / mobile bar): dải rộng, có accent sáng ở đỉnh.
   · --cb-primary-gradient = nền NÚT/tab active: dải hẹp quanh --bs-primary, để nút không sáng hơn thanh header.
   ⚠️ Hai gradient Miền Bắc lệch nhau ở ĐIỂM GIỮA (50% vs 45%) — giá trị PO cấp, KHÔNG phải lỗi copy.
   --bs-primary PHẲNG vẫn giữ nguyên vai trò: icon · viền · thanh progress · badge đếm · vạch accent nav ·
   outline thẻ hạng hiện tại (chỗ cần MỘT màu đặc, không phải một dải). */
[data-cb-region="south"] {
  --bs-primary:#D70019; --bs-primary-rgb:215,0,25;
  --cb-on-primary:#FFFFFF;          /* ← lật theo vùng */
  --cb-brand-accent:#ED2526;
  --cb-brand-gradient:linear-gradient(180deg,#ED2526 0%,#D70019 55%,#B30015 100%);
  --cb-primary-gradient:linear-gradient(180deg,#DE0A1C 0%,#D70019 100%);
  --cb-logo:url('/assets/clickbuy-logo-red.png');
  --cb-logo-on-brand:url('/assets/clickbuy-logo-white.png');
}
[data-cb-region="north"] {
  --bs-primary:#FFB301; --bs-primary-rgb:255,179,1;
  --cb-on-primary:#1F2937;          /* ← KHÔNG BAO GIỜ trắng */
  --cb-brand-accent:#FFC92B;
  --cb-brand-gradient:linear-gradient(180deg,#FFDE55 0%,#FFC92B 50%,#FFB301 100%);
  --cb-primary-gradient:linear-gradient(180deg,#FFDE55 0%,#FFC92B 45%,#FFB301 100%);
  --cb-logo:url('/assets/clickbuy-logo-gold.png');
  --cb-logo-on-brand:url('/assets/clickbuy-logo-white.png');
}

/* ---------- CHẾ ĐỘ MÀU ---------- */
[data-bs-theme="light"] {
  --cb-body-bg:#F1F3F5; --cb-body-color:#1F2937;
  --cb-surface:#FFFFFF; --cb-surface-2:#F8FAFC;
  --cb-border:#E5E7EB; --cb-muted:#6B7280;
  --cb-money-total:#111827;
}
[data-bs-theme="dark"] {
  --cb-body-bg:#0B1220; --cb-body-color:#E2E8F0;
  --cb-surface:#1E293B; --cb-surface-2:#0F172A;
  --cb-border:#334155; --cb-muted:#94A3B8;
  --cb-money-total:#F8FAFC;
  --cb-st1-bg:#2E1065; --cb-st1-fg:#C4B5FD;
  --cb-st2-bg:#1E3A5F; --cb-st2-fg:#93C5FD;
  --cb-st3-bg:#14532D; --cb-st3-fg:#86EFAC;
  --cb-st4-bg:#1E293B; --cb-st4-fg:#94A3B8;
  --cb-st5-bg:#4C1119; --cb-st5-fg:#FDA4AF;
  /* CONCERN-020: money + sao vỡ dark-on-dark ở :root (chỉ money-total được override trước đây).
     Override để né nền tối — tái dùng palette dark semantic đã có, tất cả ≥5:1 trên surface. */
  --cb-money-discount:#86EFAC;  /* lá sáng (= st3 dark) · 10.4:1 */
  --cb-money-due:#FDA4AF;        /* đỏ nhạt (= st5 dark) · 7.7:1 */
  --cb-money-paid:#94A3B8;       /* xám (= muted dark) · 5.7:1 */
  --cb-star-on:#94A3B8;          /* sao xám sáng · 5.7:1 (bản #475569 chìm dark) */
  /* Edu dark — giữ đúng hệ chàm, đảo sáng↔tối. fg #C7D2FE trên đầu sáng #312E81 ≈ 7.7:1 (AA ✅). */
  --cb-edu-from:#1E1B4B; --cb-edu-to:#312E81; --cb-edu-fg:#C7D2FE; --cb-edu-border:#3730A3;
}
/* Đỏ mận CHÌM trên nền tối (3.00) → sáng lên. Gold không cần. */
[data-bs-theme="dark"][data-cb-region="south"] {
  --bs-primary:#E11D30; --bs-primary-rgb:225,29,48; --cb-on-primary:#FFFFFF;
}

/* ---------- BASE ---------- */
body {
  font-family: var(--bs-body-font-family);
  background: var(--cb-body-bg);
  color: var(--cb-body-color);
  -webkit-font-smoothing: antialiased;
}
.cb-num, .price, .amount, .qty { font-variant-numeric: tabular-nums; }
.cb-surface { background: var(--cb-surface); }
.cb-card {
  background: var(--cb-surface);
  border: 1px solid var(--cb-border);
  border-radius: var(--bs-border-radius-lg);
  box-shadow: var(--cb-shadow);
}

/* ---------- NÚT: LUÔN dùng cặp, không hardcode ----------
   Nền nút primary = --cb-primary-gradient (§3). Viền vẫn --bs-primary PHẲNG: border-color không nhận gradient, và
   một đường viền đặc quanh dải màu là đúng thứ ta muốn (khoá mép nút). .cb-auth-submit dùng CHUNG class này
   (markup auth là `cb-auth-submit btn-primary`) ⇒ submitStyle màn auth theo miễn phí, không khai lần hai.
   §7: nút CTA + nút phụ về 500 — đặt qua chính biến của Bootstrap để mọi .btn (gồm .btn-sm phân trang) theo cùng. */
/* §9: vùng chạm ≥ 44px cho MỌI nút, ở MỌI khổ (gồm cả .btn-sm phân trang / "Mua lại"). inline-flex vì min-height
   trên inline-block chỉ nới hộp mà không canh giữa nội dung. Utility Bootstrap (.d-flex/.w-100) vẫn thắng nhờ !important.
   🔴 `gap` là BẮT BUỘC ĐI KÈM `inline-flex`, không phải trang trí: markup nút là `<i class="bi …"></i> Mua lại` —
   dấu cách nằm ở ĐẦU text node. Flexbox biến text node đó thành anonymous flex item và **cắt khoảng trắng đầu/cuối**
   ⇒ icon dính chữ ("⟳Mua lại") ở 11 nút. gap:4px trả lại đúng bề rộng dấu cách đã mất (đo: 79.36 → 83.36px, lệch
   +0.16px so với mốc inline-block trước task). ⛔ Đừng "sửa" bằng cách thêm `&nbsp;` vào markup. */
/* 🔴 `min-width` ĐI CẶP với `min-height`, không phải thừa: nút CHỈ CÓ ICON đạt 44 chiều cao nhưng vẫn hụt chiều
   ngang — đo P9-T03 ở 390px: 2 mũi tên phân trang (`_Pagination`, `.btn-sm` + 1 icon) ra **32×44**. Ràng buộc
   §10.1 #4 là 44×44, không phải 44 một chiều. Mọi .btn khác đều đã rộng hơn 44 ⇒ đặt ở đây không xê dịch cái nào. */
.btn { --bs-btn-font-weight: 500; min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; gap: 4px; }
.btn-primary {
  background: var(--cb-primary-gradient);
  color: var(--cb-on-primary);      /* ← điểm chết nếu quên */
  border-color: var(--bs-primary);
}
.btn-primary:hover, .btn-primary:focus {
  background: var(--cb-primary-gradient); color: var(--cb-on-primary);
  border-color: var(--bs-primary); filter: brightness(.94);
}
/* Hành động phá hủy: LUÔN viền, không bao giờ tô đặc */
.btn-destructive {
  background: transparent;
  border: 1px solid var(--bs-primary);
  color: var(--bs-primary);
}
.btn-destructive:hover { background: rgba(var(--bs-primary-rgb), .08); }
/* Nút phụ trung tính (Chỉ đường/Gọi/Zalo/Sửa) */
.btn-neutral {
  background: transparent; border: 1px solid var(--cb-border);
  color: var(--cb-body-color);
}

/* ---------- BADGE trạng thái đơn (icon bắt buộc trong markup) ---------- */
.cb-badge {
  display: inline-flex; align-items: center; gap: 5px;
  border-radius: var(--bs-border-radius-sm);
  padding: 3px 10px; font-size: 12px; font-weight: 500;   /* §7: badge trạng thái đơn 600 → 500 */
}
.cb-badge--st1 { background: var(--cb-st1-bg); color: var(--cb-st1-fg); }
.cb-badge--st2 { background: var(--cb-st2-bg); color: var(--cb-st2-fg); }
.cb-badge--st3 { background: var(--cb-st3-bg); color: var(--cb-st3-fg); }
.cb-badge--st4 { background: var(--cb-st4-bg); color: var(--cb-st4-fg); }
.cb-badge--st5 { background: var(--cb-st5-bg); color: var(--cb-st5-fg); }

/* Badge nguồn đơn — viền trung tính + icon, không màu brand */
.cb-source-badge {
  display: inline-flex; align-items: center; gap: 5px;
  border: 1px solid var(--cb-border); color: var(--cb-muted);
  border-radius: var(--bs-border-radius-sm);
  padding: 2px 8px; font-size: 11px; font-weight: 500;
}

/* ---------- HẠNG — thang phi sắc ---------- */
.cb-tier--1 { background: linear-gradient(135deg, var(--cb-tier1-from), var(--cb-tier1-to)); color: var(--cb-tier1-fg); }
.cb-tier--2 { background: linear-gradient(135deg, var(--cb-tier2-from), var(--cb-tier2-to)); color: var(--cb-tier2-fg); }
.cb-tier--3 { background: linear-gradient(135deg, var(--cb-tier3-from), var(--cb-tier3-to)); color: var(--cb-tier3-fg); }
.cb-tier--4 { background: linear-gradient(135deg, var(--cb-tier4-from), var(--cb-tier4-to)); color: var(--cb-tier4-fg); }
/* CONCERN-019: .cb-tier--locked deprecated (nền #F1F5F9 cố định → vỡ dark). Nhóm khóa dùng .cb-tier-locked-box (theme-aware, cuối file §P4). */

/* ---------- TIỀN ---------- */
.cb-money-total    { color: var(--cb-money-total); font-weight: 700; }
.cb-money-discount { color: var(--cb-money-discount); }
.cb-money-due      { color: var(--cb-money-due); }
.cb-money-paid     { color: var(--cb-money-paid); }

/* ---------- SAO ĐÁNH GIÁ — xám, né đỏ/gold ---------- */
.cb-star-on  { color: var(--cb-star-on); }
.cb-star-off { color: var(--cb-border); }

/* ---------- NAV (aside + offcanvas dùng CHUNG) ---------- */
.cb-nav-item {
  position: relative; display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left; padding: 9px 10px;
  border: none; background: transparent; color: var(--cb-body-color);
  border-radius: var(--bs-border-radius); font-weight: 500; min-height: 44px; /* §9: vùng chạm ≥44px (was 42) */
}
.cb-nav-item.active::before {
  content: ""; position: absolute; inset: 0;
  background: var(--bs-primary); opacity: .09; border-radius: var(--bs-border-radius);
}
.cb-nav-item.active::after {
  content: ""; position: absolute; left: 0; top: 7px; bottom: 7px;
  width: 3px; border-radius: 3px; background: var(--bs-primary);
}

/* ---------- LOGO region-dependent (background-image — KHÔNG `content:`) ----------
   🔴 `content:` trên <img> KHÔNG chạy trên Firefox (chỉ Blink/WebKit thay ảnh kiểu đó) ⇒ bản cũ hiện logo ĐỎ ở cả
   Miền Bắc. background-image chạy mọi trình duyệt ⇒ phần tử render là <span role="img" aria-label>, không còn <img>.
   Ảnh nguồn lệch tỉ lệ (đỏ 3.83 · gold/trắng 3.98) so khung 4.00 ⇒ `contain` (letterbox trong khung),
   ⛔ KHÔNG `cover` (cắt mất chữ), ⛔ không ép méo.
   🔴 --cb-logo (đỏ/gold) CHỈ an toàn trên nền TRẮNG: bản đỏ có trắng đục NƯỚNG trong glyph (33,7% pixel đặc là
   trắng) ⇒ đặt lên nền màu là mất nét. Nền thương hiệu PHẢI dùng --cb-logo-on-brand (bản trắng). */
.cb-logo {
  display: block; flex: none;
  background-image: var(--cb-logo);
  background-size: contain; background-repeat: no-repeat; background-position: left center;
}
.cb-logo--on-brand { background-image: var(--cb-logo-on-brand); }

/* ---------- A11y & motion ---------- */
:focus-visible { outline: 2px solid var(--bs-primary); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* ============================================================
   FOUNDATION SHELL (P1B) — layout · hero · nav · states.
   Chỉ component class + token. Vùng/chế độ do cascade ở trên lo.
   ============================================================ */

/* ---------- Utilities ---------- */
.cb-muted { color: var(--cb-muted); }

/* ---------- VÙNG CHẠM ≥ 44px (§9) — áp ở MỌI khổ, ⛔ KHÔNG bọc trong media query ----------
   .cb-tap là khe dùng chung; các class component bên dưới gộp vào cùng một rule để markup không phải rắc class
   thêm ở 20 chỗ. Nút <button>/<a> dạng .btn đã được lo ở khối NÚT phía trên.
   ⚠️ NGOẠI LỆ CÓ CHỦ ĐÍCH — link nằm TRONG câu văn (.cb-auth-link ở .cb-auth-foot, .cb-auth-alert-link): WCAG 2.5.8
   miễn trừ "inline target"; ép 44px ở đó sẽ thổi phồng dòng chữ chứa nó. Link đứng một mình vẫn phải đủ 44px. */
.cb-tap,
.cb-footer-link,
.cb-linkbtn,
.cb-order-back,
.cb-overview-seeall,
.cb-otp-resend-btn,
.cb-otp-change,
.cb-tab,
.cb-acc-edit-toggle,
.cb-auth-forgot .cb-auth-link {
  min-height: 44px; display: inline-flex; align-items: center;
}
/* Nút ✕ của Bootstrap (đóng offcanvas nav MOBILE · đóng modal · đóng toast) mặc định 32×32 — dưới ngưỡng, và cái
   ở offcanvas là nút thoát menu trên mobile nên bị bấm nhiều nhất. `box-sizing: border-box` để min-* cho ra ĐÚNG
   44px: Bootstrap đặt `content-box` + padding .25em ⇒ để nguyên sẽ thành 52px. Icon là background-position:center
   nên nở khung không xê dịch dấu ✕. */
.btn-close { box-sizing: border-box; min-width: 44px; min-height: 44px; }

/* ---------- SITE HEADER (P7-T01 · nền gradient P8-T01) — desktop ≥ lg ----------
   Cặp token BẮT BUỘC: nền brand + MỌI chữ/icon --cb-on-primary. KHÔNG `color: white` ở đây — gold + trắng
   = 1.54 (DESIGN_SYSTEM §2), header sẽ không đọc được ở Miền Bắc.
   Nền là --cb-brand-gradient (§3), KHÔNG --bs-primary phẳng. Vùng tối nhất của dải Nam nằm ở đáy; chữ căn giữa
   theo trục dọc + padding-top 16px ⇒ không glyph nào rơi vào dải 0–11% (đo: 4.83–5.07:1 ở mọi chiều cao thanh).
   Ẩn < lg bằng utility d-none d-lg-block trong markup — cb-mbar lo mobile. */
.cb-siteheader { background: var(--cb-brand-gradient); color: var(--cb-on-primary); }
/* Gap CO theo dải (§6 v2): 992px là khổ desktop HẸP NHẤT (header ẩn dưới ngưỡng đó) ⇒ giá trị ở base CHÍNH LÀ giá
   trị của khe 992–1099. Rộng ra thì nới ở min-width:1100px bên dưới — ⛔ không đẻ media query max-width. */
.cb-siteheader-inner {
  max-width: 1160px; margin: 0 auto; padding: 16px 32px;
  display: flex; align-items: center; gap: 16px;
}
.cb-siteheader-home { display: inline-flex; line-height: 0; }
.cb-siteheader-logo { height: 44px; width: 176px; } /* khung 4.00; ảnh letterbox bằng background-size:contain */

/* 🔴 ĐẢO NGƯỢC CÓ CHỦ ĐÍCH 2026-07-31 (thiết kế v2): search giờ là <form method=get> + <input name=key> THẬT.
   Bản cũ là ANCHOR trần chỉ mượn diện mạo ô nhập (PO chốt 2026-07-16, giữ 2026-07-30). PO lật vì: đây là
   COMMERCE và mobile ≈ 90% traffic ⇒ ô tìm kiếm là bắt buộc, không phải trang trí. Điều kiện mở khóa mà
   StorefrontOptions.DefaultSearchPath ghi sẵn ("muốn dùng path tìm kiếm thật thì phải DỰNG Ô NHẬP TRƯỚC") nay ĐÃ
   THỎA ⇒ Storefront:SearchPath có giá trị thật. Xem CANVAS-03 (DESIGN_SYSTEM §17) đã lật theo.
   🔴 PHẢI cắt kế thừa `color: --cb-on-primary` của header: Miền Nam on-primary = TRẮNG ⇒ chữ trắng trên ô trắng
   = vô hình. Cùng lớp bẫy với dropdown chi nhánh cũ (đã gỡ 2026-08-07 — xem ghi chú ở cuối khối .cb-siteheader-*).
   Chữ gợi ý dùng --cb-muted (5.13:1 trên nền trắng). */
.cb-siteheader-search {
  flex: 1; min-width: 0; max-width: 420px; margin: 0;
  display: flex; align-items: center; gap: 11px;
  padding: 12px;
  background: var(--cb-surface);
  border-radius: var(--bs-border-radius-lg);
}
.cb-siteheader-search > i { flex: none; color: var(--cb-muted); font-size: 16px; }
.cb-siteheader-links { display: flex; align-items: center; gap: 16px; margin-left: auto; }
.cb-siteheader-item {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 0; border: none; background: transparent;
  color: var(--cb-on-primary); font-size: 14px; font-weight: 500;
  text-decoration: none; cursor: pointer; white-space: nowrap;
}
.cb-siteheader-item > i { font-size: 16px; line-height: 1; }
.cb-siteheader-phone { font-weight: 600; }

/* 🗑️ ĐÃ GỠ 2026-08-07 cùng dropdown chi nhánh (PO thay bằng link tĩnh): `.cb-siteheader-stores`,
   `.cb-siteheader-store`, `.cb-siteheader-caret` — đo được 0 consumer trong markup khi gỡ.
   ⚠️ Nhưng BÀI HỌC thì giữ, vì nó vẫn đúng cho bất kỳ lớp nổi nào dựng sau: phần tử treo NGOÀI nền thương hiệu
   (dropdown/popover/tooltip trên surface) phải CẮT kế thừa `color: --cb-on-primary` của header — ở Miền Nam
   on-primary = TRẮNG ⇒ chữ trắng trên nền trắng = vô hình. Grep màu hardcode KHÔNG bắt được ca này (không có màu
   nào bị hardcode; lỗi nằm ở KẾ THỪA). P7-T01 tốn một vòng để vá. */

/* Ring :focus-visible mặc định là --bs-primary (DESIGN_SYSTEM §12) ⇒ CHÌM trên chính nền brand (brand trên brand).
   --cb-on-primary luôn tương phản AA với --bs-primary theo thiết kế của cặp ⇒ thấy được ở cả đỏ lẫn gold. */
.cb-siteheader :focus-visible, .cb-mbar :focus-visible { outline-color: var(--cb-on-primary); }

/* ---------- Ô NHẬP TÌM KIẾM — dùng chung cho thanh desktop (.cb-siteheader-search) và thanh mobile (.cb-mbar-search).
   🔴 font-size 16px là RÀNG BUỘC, không phải thẩm mỹ: iOS Safari TỰ PHÓNG TO trang khi focus một input < 16px.
   🔴 Ô nằm trên nền SURFACE chứ không phải nền brand ⇒ ring --cb-on-primary kế thừa từ hai thanh ở trên sẽ VÔ HÌNH
   ở Miền Nam (on-primary = trắng, trên ô trắng). Trả ring về --bs-primary cho riêng ô nhập. ⛔ Không outline:none —
   vòng focus bàn phím là bắt buộc (a11y §13). */
.cb-search-input {
  flex: 1; min-width: 0; border: none; background: transparent;
  color: var(--cb-body-color); font-family: inherit; font-size: 16px; font-weight: 500;
}
.cb-search-input::placeholder { color: var(--cb-muted); opacity: 1; }
.cb-siteheader .cb-search-input:focus-visible,
.cb-mbar .cb-search-input:focus-visible { outline-color: var(--bs-primary); }

/* Khe rộng (≥1100px): nới nhịp header. Dưới ngưỡng này giá trị base ở trên lo (992–1099). */
@media (min-width: 1100px) {
  .cb-siteheader-inner { gap: 28px; }
  .cb-siteheader-links { gap: 24px; }
  .cb-siteheader-search { padding: 12px 18px; }
}

/* ---------- SHELL: mobile top bar · container · 2-cột ---------- */
/* Nền GRADIENT như .cb-siteheader (canvas §5 vẽ cả hai thanh cùng dải) — hai thanh loại trừ nhau theo breakpoint
   nên để lệch treatment sẽ thành "mobile một kiểu, desktop một kiểu" cho cùng một thanh thương hiệu.
   `flex-start` (không còn `space-between`): icon giỏ đã gỡ (link chết trỏ /account — P8-T01 §5), còn 2 phần tử thì
   space-between sẽ ném logo sát mép phải. */
/* §6 v2: HAI HÀNG — hàng 1 hamburger + logo · hàng 2 ô tìm kiếm full-width. ⛔ Vẫn KHÔNG giỏ hàng (CANVAS-02). */
.cb-mbar {
  display: flex; flex-direction: column; gap: 10px;
  padding: 10px 16px 12px; background: var(--cb-brand-gradient);
}
.cb-mbar-row { display: flex; align-items: center; gap: 14px; }
/* 44×44 = vùng chạm tối thiểu (§9) — ⛔ ĐỪNG thu khung này lại khi chỉnh cỡ icon.
   Icon (glyph) canh giữa trong khung: phóng to glyph KHÔNG đụng vùng chạm, hai thứ độc lập.
   PO xin to hơn (2026-08-01): 22 → 28px. Trần an toàn là 28 — vượt nữa thì glyph chạm mép khung 44. */
.cb-mbar-btn {
  flex: none; width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  background: transparent; border: none; color: var(--cb-on-primary);
  font-size: 28px; line-height: 1; cursor: pointer; padding: 0;
}
/* §6 v2: 22 → 28px. PO xin to hơn (2026-08-01): 28 → 36px, khung giữ TỈ LỆ 4.00 (36×4 = 144).
   ⚠️ 36 < 44 nên chiều cao thanh KHÔNG đổi (hàng vẫn do nút 44px dựng) ⇒ không đẩy layout.
   Muốn to nữa phải vượt 44 ⇒ thanh cao thêm ⇒ phải đo lại, không phải đổi 1 số. */
/* Bọc logo mobile — cùng khuôn .cb-siteheader-home. `inline-flex` + `line-height: 0` để thẻ <a> KHÔNG thêm
   khoảng trắng đường-cơ-sở vào hàng flex (bản trước logo là <span> trần nên không có vấn đề này — và cũng
   không bấm được, PO báo 27/08). */
.cb-mbar-home { display: inline-flex; line-height: 0; }
.cb-mbar-logo { height: 36px; width: 144px; }
/* Ô tìm kiếm mobile: nền trắng, full-width. Placeholder ĐẦY ĐỦ ("Bạn muốn mua gì hôm nay?") — khổ mobile dư chỗ.
   ⚠️ Placeholder là THUỘC TÍNH HTML, không đổi được bằng media query ⇒ hai thanh loại trừ nhau (d-lg-none /
   d-none d-lg-block) mỗi thanh mang placeholder riêng. ⛔ KHÔNG dùng JS đổi placeholder theo resize. */
.cb-mbar-search {
  display: flex; align-items: center; gap: 10px; margin: 0;
  padding: 10px 12px; background: var(--cb-surface);
  border-radius: var(--bs-border-radius-lg);
}
.cb-mbar-search > i { flex: none; color: var(--cb-muted); font-size: 16px; }
/* Logo trong offcanvas — nền surface TRẮNG ⇒ chỗ duy nhất --cb-logo (bản đỏ/gold) dùng được. */
.cb-offcanvas-logo { height: 26px; width: 104px; }

/* Base = MOBILE (padding gọn để 320px còn chỗ cho nội dung); nới ra ở ≥ lg. */
.cb-container { max-width: 1160px; margin: 0 auto; padding: 16px 12px 64px;
  display: flex; flex-direction: column; gap: 16px; }
@media (min-width: 992px) { .cb-container { padding: 24px 20px 80px; gap: 20px; } }

.cb-shell { display: grid; grid-template-columns: 1fr; gap: 20px; align-items: start; }
@media (min-width: 992px) { .cb-shell { grid-template-columns: 250px minmax(0, 1fr); } }
.cb-aside { position: sticky; top: 16px; }
.cb-main { min-width: 0; display: flex; flex-direction: column; gap: 16px; }

/* footer public — ngoài nav tài khoản */
.cb-footer { display: flex; flex-wrap: wrap; gap: 16px 24px; padding-top: 8px;
  border-top: 1px solid var(--cb-border); }
.cb-footer-link { font-size: 13px; color: var(--cb-muted); text-decoration: none; }
.cb-footer-link:hover { color: var(--bs-primary); }

/* ---------- NAV (aside + offcanvas dùng CHUNG — 1 nguồn) ---------- */
.cb-nav { display: flex; flex-direction: column; gap: 2px; }
.cb-nav-item { text-decoration: none; cursor: pointer; }
.cb-nav-item:hover { background: var(--cb-surface-2); color: var(--cb-body-color); }
.cb-nav-item > i { position: relative; font-size: 16px; color: var(--cb-muted); }
.cb-nav-item > span { position: relative; }
.cb-nav-item.active > i { color: var(--bs-primary); }
.cb-nav-divider { border: 0; border-top: 1px solid var(--cb-border); margin: 8px 4px; }

/* ---------- HERO (P1B-T01) ---------- */
.cb-hero { padding: 20px; display: flex; flex-wrap: wrap; gap: 20px;
  align-items: center; justify-content: space-between; }
.cb-hero-identity { display: flex; align-items: center; gap: 14px; }
.cb-hero-avatar { width: 54px; height: 54px; flex: none; border-radius: 50%;
  background: var(--cb-surface-2); border: 1px solid var(--cb-border);
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 18px; color: var(--cb-muted); }
.cb-hero-idcol { display: flex; flex-direction: column; gap: 4px; }
.cb-hero-name { font-weight: 700; font-size: 17px; }
.cb-hero-phone { display: flex; align-items: center; gap: 7px; font-size: 12.5px; }
.cb-tier-badge { align-self: flex-start; border-radius: var(--bs-border-radius-sm);
  padding: 3px 10px; font-size: 12px; font-weight: 700; } /* màu ← .cb-tier--N */

.cb-hero-stats { display: flex; flex-wrap: wrap; gap: 24px; }
.cb-hero-stat-label { font-size: 12px; color: var(--cb-muted); }
.cb-hero-stat-value { font-weight: 700; font-size: 22px; }
.cb-hero-unit { font-size: 13px; font-weight: 500; }
.cb-hero-expiring { display: flex; align-items: center; gap: 5px; font-size: 11.5px; margin-top: 2px; }

.cb-hero-progress { flex: 1; min-width: 240px; background: var(--cb-surface-2);
  border-radius: var(--bs-border-radius); padding: 12px 14px; }
.cb-hero-progress-head { display: flex; justify-content: space-between; align-items: center;
  font-size: 12px; margin-bottom: 7px; gap: 8px; }
.cb-hero-progress-goal { display: inline-flex; align-items: center; gap: 8px; }
.cb-hero-progress-foot { font-size: 12px; margin-top: 8px; }
.cb-hero-strong { color: var(--cb-body-color); }
.cb-progress { height: 8px; border-radius: 4px; background: var(--cb-border); overflow: hidden; }
.cb-progress-bar { height: 100%; background: var(--bs-primary); border-radius: 4px; }
.cb-interim-tag { font-size: 10px; font-weight: 600; color: var(--cb-muted);
  border: 1px dashed var(--cb-border); border-radius: var(--bs-border-radius-sm);
  padding: 1px 6px; text-transform: uppercase; letter-spacing: .04em; white-space: nowrap; }

/* ---------- STATES: empty · error · placeholder (P1B-T03/T04) ---------- */
.cb-state { padding: 40px 24px; display: flex; flex-direction: column;
  align-items: center; gap: 10px; }
.cb-state-icon { font-size: 28px; color: var(--bs-primary); }
.cb-state-msg { font-weight: 600; font-size: 15px; }
.cb-state-cta { margin-top: 6px; }

.cb-page-title { font-size: 22px; font-weight: 700; margin: 0; }
.cb-placeholder { padding: 48px 24px; display: flex; flex-direction: column;
  align-items: center; gap: 8px; border-style: dashed; }
.cb-placeholder-icon { font-size: 28px; color: var(--bs-primary); }
.cb-placeholder-title { font-size: 16px; font-weight: 600; margin: 6px 0 0; }
.cb-placeholder-sub { font-size: 13px; margin: 0 0 6px; }
.cb-overview-placeholder { padding: 40px 24px; display: flex; flex-direction: column;
  align-items: center; gap: 10px; border-style: dashed; font-size: 13px; }
.cb-overview-placeholder > i { font-size: 26px; }

/* ---------- TOAST host (Bootstrap toast + surface token) ---------- */
.cb-toast { background: var(--cb-surface); color: var(--cb-body-color);
  border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius-lg);
  box-shadow: 0 8px 28px rgb(0 0 0 / .22); }
/* Mức độ toast (P10-T08). Màu icon chuyển từ inline `style` sang class — CONVENTIONS §5.7.
   🔴 WHY --cb-st5-* chứ KHÔNG --bs-primary: --bs-primary LẬT THEO VÙNG (gold ở Miền Bắc = "chú ý", không phải
   "hỏng"). --cb-st5 là khe "Cần chú ý" đỏ báo — ĐỘC LẬP VÙNG và theme-aware sẵn, cùng token mà .cb-field-error
   và .cb-linkbtn--danger đang dùng ⇒ lỗi ở toast và lỗi ở ô nhập nói cùng một thứ tiếng. */
.cb-toast--error { border-color: var(--cb-st5-fg); }
.cb-toast-ico--ok { color: var(--cb-st3-fg); }
.cb-toast-ico--error { color: var(--cb-st5-fg); }

/* ---------- AUTH LAYOUT (P5A-T01) — rework từ stub 1 cột/420px sang 2 cột/960px ---------- */
.cb-auth-shell { max-width: 960px; margin: 0 auto; padding: 20px 12px 56px; }
/* surface + viền + radius 8 + shadow 1 cấp: dùng CHUNG .cb-card (không lặp token) */
.cb-auth-card { overflow: hidden; }
/* Base = MOBILE 1 cột (P8-T02 §9 — lật khối max-width cũ sang min-width).
   ✅ Panel quyền lợi đứng SAU .cb-authform trong DOM (_AuthLayout) ⇒ ở 1 cột nó TỰ rơi xuống dưới form.
   ⛔ KHÔNG cần `order` — và đó là điều tốt: `order` đổi thứ tự NHÌN mà KHÔNG đổi thứ tự TAB bàn phím. */
.cb-authgrid { display: grid; grid-template-columns: 1fr; }
/* `max-width` + canh giữa CHỈ ở chế độ 1 cột (audit PO mục 25): đo trước sửa @768 — cột form rộng **742px**, hai ô
   nhập giãn trọn thẻ nên form 2 trường trông như bảng nhập liệu. 420px là số PO cấp.
   ⛔ Panel quyền lợi (.cb-panel) KHÔNG bị chạm — nó phải giữ full-width để không thành một dải hẹp lạc giữa thẻ.
   ⛔ Và ⛔ KHÔNG đổi breakpoint của .cb-authgrid: nó là 992 (CANVAS-04), không phải 820 — xem mục "chỉ BÁO" ở
   P9-T03_result.md.
   🔴 `width:100%` là BẮT BUỘC ĐI KÈM, không phải thừa: `.cb-authform` là GRID ITEM, và margin trục ngang `auto`
   trên grid item **TẮT stretch** ⇒ ô co về fit-content. Đã đo bản thiếu nó: 1280px cột form tụt **510 → 347px**
   (hồi quy ở khổ desktop) và 390px tụt **364 → 323px**. Có `width:100%` thì ô luôn lấp trọn track rồi mới bị
   `max-width` chặn, và ở ≥lg chỉ cần gỡ chặn — margin auto tự về 0 khi không còn chỗ dư. */
.cb-authform { padding: 22px 18px; min-width: 0; width: 100%; max-width: 420px; margin-inline: auto; }
@media (min-width: 992px) {
  .cb-auth-shell { padding: 36px 20px 80px; }
  .cb-authgrid { grid-template-columns: 1fr .8fr; }
  .cb-authform { padding: 32px 30px; max-width: none; }
}

/* ============================================================
   PHASE 2 (P2) — Voucher · Yêu thích · Bảo hành.
   Chỉ component class + token; vùng/chế độ do cascade ở trên lo.
   ============================================================ */

/* ---------- TABS (server-side ?tab= — voucher, sau này review) ---------- */
/* Base = MOBILE: CUỘN NGANG 1 hàng (audit PO mục 17 — đo P9-T03: `/account/orders` vỡ **2 hàng** ở 320 và 390,
   `/account/vouchers` vỡ 2 hàng ở 320).
   🔴 ĐÂY LÀ LẬT MỘT LỰA CHỌN CÓ Ý THỨC CỦA `P8-T02` (result:261 — chọn `wrap` vì "an toàn hơn cho AC #20 không tràn
   ngang"). Rủi ro mà task đó nêu ĐÃ ĐO LẠI và KHÔNG tái hiện: cuộn nằm TRONG .cb-tabs, `document.scrollWidth ===
   clientWidth` ở 3 màn × 3 khổ = 9/9 (bảng ở P9-T03_result.md). Lý do cấu trúc: .cb-tabs là con của .cb-main có
   `min-width:0` ⇒ nó KHÔNG đẩy được shell rộng ra.
   ⛔ Selector là `.cb-tab` chứ KHÔNG `> button` (canvas viết `> button`; markup CỦA TA là `<a class="cb-tab">`).
   ⚠️ `overflow-x:auto` biến .cb-tabs thành scroll container ⇒ `overflow-y` KHÔNG thể `visible` (CSS ép về auto).
   Hệ quả đã đo: vòng focus `outline-offset:2px` bị CẮT trên/dưới. Trả bằng ring ÂM offset ở .cb-tab bên dưới —
   ⛔ đừng chữa bằng padding dọc, nó đẩy gạch chân tab rời khỏi đường kẻ của thanh. */
.cb-tabs {
  display: flex; gap: 2px; border-bottom: 1px solid var(--cb-border);
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.cb-tabs::-webkit-scrollbar { display: none; }
.cb-tabs > .cb-tab { flex: none; }
.cb-tab {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 14px; font-size: 13.5px; font-weight: 500;   /* §7: tab 600 → 500 */
  color: var(--cb-muted); text-decoration: none;
  border-bottom: 2px solid transparent;
}
.cb-tab:hover { color: var(--cb-body-color); }
/* Active: chữ body-color (AA ở CẢ 2 vùng — gold #FFB301 làm chữ trên trắng fail AA) + gạch chân brand.
   Tín hiệu active = độ đậm chữ (muted→body) + underline + aria-current, KHÔNG chỉ dựa màu. */
.cb-tab.active { color: var(--cb-body-color); border-bottom-color: var(--bs-primary); }
/* Ring ÂM offset: .cb-tabs là scroll container (xem trên) nên ring dương 2px bị clip mất. `-2px` vẽ ring NẰM TRONG
   hộp tab ⇒ không bao giờ bị cắt, ở mọi khổ. ⛔ KHÔNG `outline:none` — vòng focus bàn phím là bắt buộc (a11y §13). */
.cb-tab:focus-visible { outline-offset: -2px; }
.cb-tab-count { font-weight: 500; opacity: .75; }
/* ≥ lg: về `wrap` như trước — desktop dư bề rộng, không cần thanh cuộn nào.
   `margin-bottom:-1px` (kéo gạch chân tab CHỒNG lên đường kẻ của thanh) chỉ sống ở đây: dưới 992 .cb-tabs là scroll
   container ⇒ 1px thò ra bị CẮT (đo: `clippedPx = 1` ⇒ gạch chân active chỉ còn 1/2 độ dày). Gạch chân là một
   trong ba tín hiệu active không-dựa-màu của CANVAS-06 ⇒ không để nó mỏng đi một nửa. */
@media (min-width: 992px) {
  .cb-tabs { flex-wrap: wrap; overflow: visible; }
  .cb-tab { margin-bottom: -1px; }
}

/* ---------- VOUCHER CARD (P2-T01) ---------- */
.cb-voucher-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 12px; }
.cb-voucher { display: flex; gap: 14px; padding: 16px; }
.cb-voucher-icon {
  width: 48px; height: 48px; flex: none; border-radius: var(--bs-border-radius);
  background: var(--cb-st1-bg); color: var(--cb-st1-fg);
  display: flex; align-items: center; justify-content: center; font-size: 22px;
}
.cb-voucher-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.cb-voucher-title { font-weight: 600; font-size: 14px; line-height: 1.35; }
.cb-voucher-cond { font-size: 12px; color: var(--cb-muted); }
/* Mức giảm (discountLabel) — giá trị chính của thẻ, đứng ngay dưới tên. Token --cb-money-discount = khe "giảm giá"
   sẵn có của dự án, ĐÃ đo AA ở cả light lẫn dark (CONCERN-020: 10.4:1 trên surface dark) ⇒ không khai màu mới. */
.cb-voucher-discount { font-size: 13px; font-weight: 700; color: var(--cb-money-discount); }
.cb-voucher-exp { font-size: 12px; color: var(--cb-muted); display: flex; align-items: center; gap: 4px; }
/* .cb-voucher-body là flex COLUMN ⇒ con mặc định giãn hết chiều ngang. Badge phải co về nội dung, nếu không nó thành
   một dải màu st5 chạy suốt thẻ (trông như banner lỗi, không như nhãn). */
.cb-voucher-soon { align-self: flex-start; }
.cb-voucher-coderow { display: flex; align-items: center; gap: 8px; margin-top: 2px; flex-wrap: wrap; }
.cb-voucher-code {
  font-family: monospace; font-size: 12.5px;
  background: var(--cb-surface-2); border: 1px dashed var(--cb-border);
  border-radius: var(--bs-border-radius-sm); padding: 4px 10px;
}
.cb-voucher-copy { white-space: nowrap; }

/* Hàng hành động của thẻ KHO CÔNG KHAI (P10-T05) — chỉ có ở tab "Có thể nhận".
   `margin-top:auto` đẩy nút xuống đáy thẻ: lưới auto-fit làm các thẻ CÙNG CHIỀU CAO nhưng phần chữ dài ngắn khác
   nhau (có/không có mã, điều kiện 1 hay 2 dòng) ⇒ không có nó thì hàng nút của các thẻ cạnh nhau so le. */
.cb-voucher-action { margin-top: auto; padding-top: 4px; display: flex; align-items: center; }
/* ⛔ KHÔNG dùng .btn của Bootstrap cho khe "chưa nhận được": `.btn:disabled` làm mờ opacity .65 ⇒ chữ muted trên
   surface-2 tụt dưới ngưỡng AA. Mượn nguyên bộ đo của .btn.btn-sm (min-height 44 + inline-flex + padding) như
   .cb-product-oos đã làm cho "Tạm hết hàng" — cùng một bài, cùng một lời giải. */
.cb-voucher-off {
  min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  background: var(--cb-surface-2); color: var(--cb-muted);
  border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius);
  padding: 4px 10px; font-size: 13px; font-weight: 500; cursor: not-allowed;
}

/* ---------- PRODUCT CARD (P2-T02 wishlist · dùng lại Overview P3-T03) ---------- */
/* Base = MOBILE 2 cột (audit PO mục 14). `minmax(200px,1fr)` cũ cho ra ĐÚNG 1 CỘT ở khổ máy thật — đo P9-T03:
   320px → 1 cột ảnh cao 266px · 390px → 1 cột ảnh cao 336px, 4 SP phải cuộn ~4 màn.
   Ảnh vuông đã có sẵn (`aspect-ratio:1` ở .cb-product-thumb) ⇒ ⛔ không khai lại.

   🔴 `minmax(160px,1fr)` chứ KHÔNG `repeat(2,1fr)` — ĐÂY LÀ SỐ HỌC, KHÔNG PHẢI SỞ THÍCH. `repeat(2,1fr)` đã thử và
   **LÀM TRÀN NGANG Ở 320px** (đo: `documentElement.scrollWidth` 340 vs `clientWidth` 320). Nguyên nhân đo được:
   min-content của thẻ = **159.2px**, do `.cb-product-cart` (`white-space:nowrap` — P9-T01 dựng cho mục 22) cần
   **129.2px** cộng 14px padding mỗi bên + viền. Hai thẻ + gap 10 = 328.4px, trong khi 320px chỉ còn **296px**
   ⇒ track không co dưới min-content được ⇒ tràn. Ràng buộc "0 tràn ngang ở 320" (§10.1 #4) là bất khả xâm phạm,
   nên chính SỐ CỘT phải nhường.
   `auto-fill` + sàn track **160px > 159.2px min-content** ⇒ track LUÔN đủ rộng cho thẻ, ở mọi khổ, không cần một
   ngưỡng mới nào (⛔ tập ngưỡng vẫn là {576,768,992,1100}). Kết quả đo: **320 → 1 cột** (không tràn) · **360 →
   2 cột** · **390 → 2 cột (178px/thẻ)** — đúng thứ PO xin, và đúng cơ chế PO đã duyệt cho `.cb-overview-favgrid`.

   🔬 Mốc trả về `minmax(200px,…)` là **768**, KHÔNG 992: ở 768 nó cho **3 cột** (đo: 238.66px/thẻ, lưới cao 756px);
   để base chạy tới 992 thì 768 thành 4 cột 178px — nhỏ hơn hẳn mà không ai xin. 768 là LƯỚI NỘI DUNG, không phải
   ngưỡng bố cục app (DESIGN_SYSTEM §10.1 #3) ⇒ đúng chỗ để dùng nó. */
.cb-product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
@media (min-width: 768px) { .cb-product-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; } }
.cb-product { padding: 14px; display: flex; flex-direction: column; gap: 10px; }
.cb-product-media { position: relative; }
.cb-product-thumb {
  aspect-ratio: 1; border-radius: var(--bs-border-radius); overflow: hidden;
  background: var(--cb-hatch);
  display: flex; align-items: center; justify-content: center;
  font-size: 9px; color: var(--cb-muted);
}
.cb-product-thumb img { width: 100%; height: 100%; object-fit: cover; }
.cb-product-favform { position: absolute; top: 8px; right: 8px; margin: 0; }
/* Bỏ yêu thích = phá hủy → icon VIỀN, không tô đặc (CONVENTIONS §5.3) */
.cb-product-fav {
  position: relative;
  width: 32px; height: 32px; border-radius: 50%;
  border: 1px solid var(--cb-border); background: var(--cb-surface);
  color: var(--bs-primary); cursor: pointer;
  display: flex; align-items: center; justify-content: center; font-size: 14px;
}
/* §9 vùng chạm ≥44×44 mà KHÔNG phóng to nút: nới hộp bấm bằng ::after trong suốt. Nút này nằm ĐÈ lên ảnh SP —
   phóng lên 44px thật sẽ che mất một góc ảnh ở mọi thẻ. 32 + 6×2 = 44. */
/* ⛔ KHÔNG border-radius ở đây: bo tròn biến vùng chạm thành HÌNH TRÒN đường kính 44 ⇒ 4 góc chéo rơi ra ngoài
   (đo được: điểm (16,16) tính từ tâm nằm ngoài bán kính 22). Hộp VUÔNG 44×44 mới là thứ AC yêu cầu — nó trong
   suốt nên hình dạng không ai nhìn thấy. */
.cb-product-fav::after { content: ""; position: absolute; inset: -8px; } /* 32 + 8×2 = 48 ≥ 44, dư biên đo đạc */
.cb-product-fav:hover { background: rgba(var(--bs-primary-rgb), .08); }
.cb-product-name { font-size: 13px; line-height: 1.35; min-height: 36px; }
.cb-product-price { font-weight: 700; font-size: 15px; color: var(--cb-money-total); }
/* Nhãn vùng trên giá (P6-T01 · ADR-003 rev.2) — hiện KHI VÀ CHỈ KHI RegionSource=Fallback.
   ⛔ KHÔNG tái dùng .cb-interim-tag: nhãn đó có ngữ nghĩa ĐÃ THIẾT LẬP = "giá trị tạm, chờ backend", vẽ mờ +
   viền đứt + uppercase 10px = tín hiệu "BỎ QUA TÔI". Nhãn này NGƯỢC LẠI: tuyên bố ĐÚNG, có phạm vi, khách CẦN đọc
   — nó biến "20.690.000 ₫" (có thể sai) thành "20.690.000 ₫ ở khu vực Miền Nam" (luôn đúng).
   Màu = --cb-body-color (chữ thân bài): trung tính, né CẢ đỏ lẫn gold (§5.4) · KHÔNG dùng --bs-primary làm màu chữ
   (CONCERN-021 đang DEFERRED — Bắc-sáng 1.79 · Nam-tối 3.08 = fail AA; đừng đẻ thêm bề mặt dính bẫy đó).
   ⇒ KHÔNG token nào ở đây phụ thuộc vùng ⇒ contrast BẤT BIẾN qua 4 trạng thái vùng×chế độ, không thể vỡ như
   CONCERN-020/021: sáng 14.68:1 · tối 11.86:1 trên .cb-card (đo thật, xem P6-T01_result.md).
   Icon bi-geo-alt BẮT BUỘC (markup) — màu không được là tín hiệu duy nhất (§5.6 + §12). */
.cb-region-note {
  display: flex; align-items: flex-start; gap: 5px; margin: 0;
  color: var(--cb-body-color); font-size: 12px; font-weight: 500; line-height: 1.35;
}
.cb-region-note > i { flex-shrink: 0; } /* text wrap ở cột hẹp 150px (overview) không được bóp méo icon */
/* `white-space:nowrap`: ô hẹp nhất toàn app là tile lưới yêu thích ở Tổng quan (.cb-overview-favgrid,
   minmax(150px,1fr)) — ở đó nhãn "Thêm vào giỏ" vỡ thành "Thêm vào / giỏ" (audit PO mục 22).
   Chọn nowrap thay vì rút nhãn thành "Thêm giỏ": nhãn là quyết định của PO, và số đo cho thấy chữ VẪN VỪA — chỉ
   thiếu lệnh cấm ngắt dòng. Số đo 4 nhãn ở P9-T01_result.md. */
.cb-product-cart { width: 100%; margin-top: auto; white-space: nowrap; }
/* Hộp PHẢI khớp .btn.btn-sm cạnh nó (audit PO mục 28): đo trước sửa ở 1280 — 3 nút "Thêm vào giỏ" cao **44** ·
   "Tạm hết hàng" cao **39.5** ⇒ lệch `top` **4.5px**, một nút tụt hàng. Nút `disabled` được §10.1 #4 miễn ràng buộc
   44px, nhưng KHÔNG được miễn việc thẳng hàng ⇒ mượn nguyên bộ đo của .btn: min-height 44 + inline-flex canh giữa +
   đúng padding của .btn-sm (`.25rem .5rem`). ⛔ Không đổi sang <button class="btn"> — .btn:disabled của Bootstrap
   làm mờ opacity .65, phá cặp surface-2/muted đang cố ý dùng ở đây. */
.cb-product-oos {
  width: 100%; margin-top: auto;
  min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  background: var(--cb-surface-2); color: var(--cb-muted);
  border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius);
  padding: 4px 8px; font-size: 13px; font-weight: 500; cursor: not-allowed;   /* §7: khe nút CTA (hết hàng) */
}

/* ---------- WARRANTY (P2-T03) ---------- */
.cb-warranty-intro { font-size: 13.5px; margin: 4px 0 0; }
.cb-warranty-form { padding: 20px; }
.cb-field { display: flex; flex-direction: column; gap: 6px; }
.cb-field-label { font-size: 13px; font-weight: 500; }
.cb-input-row { display: flex; gap: 8px; flex-wrap: wrap; }
/* 🔴 `flex-basis: 100%` (KHÔNG phải `flex:1` cũ) là thứ thật sự kích hoạt `flex-wrap` ở trên: với basis 0 thì ô nhập
   co lại vừa hàng với nút "Tra cứu" ⇒ wrap KHÔNG BAO GIỜ chạy ⇒ placeholder IMEI bị cắt ở mobile (audit PO mục 2).
   Nút có `white-space:nowrap` nên nó giữ nguyên bề rộng và ăn hết chỗ — ô nhập là bên phải nhường. */
.cb-input-box {
  flex: 1 1 100%; min-width: 200px; display: flex; align-items: center; gap: 8px;
  border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius);
  padding: 11px 13px; background: var(--cb-surface);
}
.cb-input-box:focus-within { border-color: var(--bs-primary); }
.cb-input-box > i { color: var(--cb-muted); }
/* font-size 16px = RÀNG BUỘC iOS Safari (§9): dưới 16px thì Safari tự phóng to trang khi focus. Áp cho MỌI ô nhập. */
.cb-input-box input {
  border: none; background: transparent; outline: none;
  color: var(--cb-body-color); font-family: inherit; font-size: 16px; width: 100%;
}
/* Mobile: nút chiếm trọn hàng thứ hai (basis 100%), KHÔNG phải viên nút con con canh trái. */
.cb-warranty-submit { white-space: nowrap; flex: 1 1 100%; }
/* ≥992: trả về ĐÚNG hành vi trước P9-T01 — `flex:1 1 0%` chính là `flex:1` cũ ⇒ 9 consumer kia của .cb-input-box
   (form sửa hồ sơ · đổi mật khẩu · modal địa chỉ · ô tìm đơn) không đổi một pixel nào ở khổ desktop. */
@media (min-width: 992px) {
  .cb-input-box { flex: 1 1 0%; }
  .cb-warranty-submit { flex: 0 0 auto; }
}
.cb-field-hint { font-size: 12px; color: var(--cb-muted); }
.cb-field-error { font-size: 12px; color: var(--cb-st5-fg); display: inline-flex; align-items: center; gap: 5px; }

.cb-warranty-card { padding: 20px; display: flex; flex-direction: column; gap: 14px; }
.cb-warranty-head { display: flex; gap: 14px; align-items: flex-start; }
.cb-warranty-thumb {
  width: 60px; height: 60px; flex: none; border-radius: var(--bs-border-radius);
  background: var(--cb-hatch);
  display: flex; align-items: center; justify-content: center;
  font-size: 8px; color: var(--cb-muted);
}
.cb-warranty-headbody { flex: 1; min-width: 0; }
.cb-warranty-titlerow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.cb-warranty-name { font-weight: 600; font-size: 15px; }
.cb-warranty-imei {
  font-family: monospace; font-size: 12px; margin-top: 8px; display: inline-block;
  background: var(--cb-surface-2); border: 1px solid var(--cb-border);
  border-radius: var(--bs-border-radius-sm); padding: 2px 8px;
}
.cb-warranty-meta {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px;
  font-size: 13.5px; padding-top: 12px; border-top: 1px solid var(--cb-border);
}
.cb-warranty-meta-label { font-size: 12px; color: var(--cb-muted); }
.cb-warranty-meta-value { font-weight: 600; margin-top: 2px; }

/* ============================================================
   PHASE 3 (P3) — Đơn hàng: Lịch sử (list) · Chi tiết (detail).
   Chỉ component class + token; vùng/chế độ do cascade ở trên lo.
   ============================================================ */

/* ---------- LỊCH SỬ ĐƠN — topbar + search (P3-T01) ---------- */
.cb-order-topbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
.cb-order-search { display: flex; flex-direction: column; gap: 4px; }
/* 🔬 240px KHÔNG ĐỦ — đo được, đừng hạ lại. Ô này KHÔNG co giãn theo khổ màn: `.cb-order-search` là flex item
   `0 1 auto` nên bề rộng ô = max-content của cột, đo được **248px y hệt nhau ở CẢ 320/390/768/992/1280**.
   Với 248px thì vùng chữ của <input> chỉ còn 192px, trong khi placeholder "Tìm mã đơn, tên sản phẩm" cần
   205.12px @16px Be Vietnam Pro ⇒ vẫn cụt 13px. 272px cho vùng chữ 216px (dư ~11px) và vẫn lọt khổ 320
   (thanh topbar rộng 296px ở đó). ⚠️ `input.scrollWidth <= clientWidth` MÙ với placeholder — nó trả true
   ở cả 5 khổ kể cả khi chữ thừa 91px; phép đo thật là bề rộng chữ đo bằng canvas. Số đo: P9-T01_result.md. */
.cb-order-search-box { min-width: 272px; }
/* Ô search vô hiệu hóa (blocker H) — tín hiệu rõ ràng là chưa dùng được, KHÔNG giả vờ hoạt động */
.cb-order-search-box input:disabled { cursor: not-allowed; }
.cb-order-search-box:has(input:disabled) { opacity: .6; }

/* ---------- ORDER CARD (list) ---------- */
.cb-order-list { display: flex; flex-direction: column; gap: 12px; }
.cb-order-card { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.cb-order-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.cb-order-code { font-weight: 700; font-size: 14px; }
.cb-order-date { margin-left: auto; font-size: 12px; color: var(--cb-muted); }
/* Ghi chú cấp đơn (vd "1/2 sản phẩm đã hủy") — tông xám st4, KHÔNG brand: đây là thông tin, không phải hành động. */
.cb-order-statusnote { display: flex; align-items: center; gap: 4px; margin: -4px 0 0; font-size: 12px; color: var(--cb-st4-fg); }
/* Base = mobile: cột tiền XUỐNG HÀNG (width:100%), ≥ lg mới về cùng hàng — xem WHY ở .cb-line-side. */
.cb-order-body { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-start; padding-top: 12px; border-top: 1px solid var(--cb-border); }
/* 🔬 44px, KHÔNG 56 (audit PO mục 26 — nguyên nhân ĐO ĐƯỢC, không phải phỏng đoán). PO đoán `min-height` hoặc
   `justify-content:space-between`; đo ở 1280 cho thấy **cả hai đều không tồn tại** (`min-height:auto` ·
   `justify-content:normal`). Thứ dựng chiều cao hàng giữa là CHÍNH Ô ẢNH: thumb 56px trong khi cột chữ cao 39.89
   và cột tiền 42 ⇒ 14px trắng dưới cột tiền rồi mới tới hàng nút — đúng "khoảng trắng thừa" PO nhìn thấy. Ô ảnh là
   TRANG TRÍ (hatch placeholder, aria-hidden) ⇒ nó không được quyền quyết chiều cao thẻ.
   44 = đơn vị đã dùng khắp dự án (vùng chạm §10.1 #4) và ĐÚNG BẰNG số mà biến thể gọn ở Tổng quan vốn đã đặt
   ⇒ override `.cb-order-card--compact .cb-order-thumb` thành no-op và ĐÃ GỠ (⛔ không để rule chết).
   ⚠️ CẬP NHẬT 2026-08-12: ô này KHÔNG còn luôn là placeholder — backend đã cấp `firstItemImageUrl` nên nó thường là
   ẢNH THẬT (câu "TRANG TRÍ" ở trên chỉ còn đúng cho nhánh --empty). Kích thước 44px GIỮ NGUYÊN ⇒ toàn bộ lý lẽ
   chiều cao ở trên vẫn đúng từng chữ, ⛔ ĐỪNG nới lên 56 vì "giờ là ảnh thật nên cho to hơn".
   Tách base / --empty theo đúng khuôn .cb-line-thumb đã có: class nằm trên chính <img>, --empty thêm nền hatch. */
.cb-order-thumb {
  width: 44px; height: 44px; flex: none; border-radius: var(--bs-border-radius); object-fit: cover;
}
.cb-order-thumb--empty {
  background: var(--cb-hatch); display: flex; align-items: center; justify-content: center;
  font-size: 8px; color: var(--cb-muted);
}
.cb-order-items { flex: 1; min-width: 0; }
.cb-order-firstitem { font-size: 13.5px; font-weight: 500; line-height: 1.4; }
/* Cắt 1 dòng + "…" — ⛔ KHÔNG phải chuộng gọn, mà vì ĐO ĐƯỢC trên dữ liệu thật: biến thể API trả về lặp lại NGUYÊN
   tên sản phẩm ("Samsung Galaxy A16 (4G) 4GB 128GB - 1 Đổi 1 12 Tháng Silver, 1 đổi 1 12 tháng" — dài hơn cả dòng
   tên ngay phía trên nó). Để nguyên thì MỘT biến thể đủ sức đẩy thẻ cao gấp đôi và phá đều hàng của cả danh sách.
   `min-width:0` mà ellipsis cần thì .cb-order-items đã có sẵn. */
.cb-order-variant {
  font-size: 12.5px; color: var(--cb-muted); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cb-order-more { font-size: 12.5px; color: var(--cb-muted); margin-top: 3px; }
.cb-order-count { font-size: 12px; color: var(--cb-muted); margin-top: 3px; }
.cb-order-totalcol { text-align: right; flex: none; width: 100%; }
@media (min-width: 992px) { .cb-order-totalcol { width: auto; } }
.cb-order-total-label { font-size: 11px; color: var(--cb-muted); }
.cb-order-total { font-size: 17px; }
.cb-order-actions { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }
/* Base = MOBILE: 2 nút CHIA ĐỀU trọn hàng (audit PO mục 18 — đo trước sửa ở 320: hàng rộng 262px nhưng 2 nút chỉ
   85.17 + 100.41 rồi dồn về phải, thừa ~69px). ≥ lg trả về `flex:none` canh phải như cũ.
   ⛔ KHÔNG khai lại `min-height:44px` ở đây: cả 2 con đều là `.btn` và đã đo ĐÚNG 44px từ chính rule .btn — khai
   lần hai là chép số vào hai chỗ, rồi hai chỗ đó lệch nhau. */
.cb-order-actions > * { flex: 1; }
@media (min-width: 992px) { .cb-order-actions > * { flex: none; } }

/* ---------- CHI TIẾT ĐƠN (P3-T02) ---------- */
.cb-order-back {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 4px;
  background: transparent; border: none; padding: 0;
  color: var(--cb-muted); font-size: 13px; font-weight: 500; text-decoration: none;
}
.cb-order-back:hover { color: var(--bs-primary); }
.cb-order-detail { padding: 20px; display: flex; flex-direction: column; gap: 20px; }
.cb-detail-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding-bottom: 16px; border-bottom: 1px solid var(--cb-border); }
.cb-detail-code { font-weight: 700; font-size: 18px; }
.cb-detail-date { margin-left: auto; font-size: 12.5px; color: var(--cb-muted); }

/* Nhãn dữ liệu minh họa (Mock interim — PO decision #2) */
.cb-detail-notice {
  display: flex; align-items: flex-start; gap: 8px; font-size: 12.5px; line-height: 1.5;
  background: var(--cb-surface-2); border: 1px solid var(--cb-border);
  border-radius: var(--bs-border-radius); padding: 10px 14px; color: var(--cb-body-color);
}
.cb-detail-notice > i { color: var(--cb-st2-fg); margin-top: 1px; }

/* ---------- STEPPER (từ /tracking) ----------
   ⚠️ Tên `.cb-stepper`/`.cb-step*` THUỘC VỀ khối này. Mini-stepper wizard quên MK dùng `.cb-authstep*` (dòng ~1116)
   — trước P9-T01 nó trùng tên và đè khối này. ⛔ Đừng khai lại `.cb-step*` ở bất kỳ đâu khác trong file.
   `flex:1` là dạng tổng quát đúng của "chia đều": số bước ĐỘNG (Detail.cshtml lặp theo steps.Count — timeline
   hủy/hoàn tiền có số bước khác 4) ⇒ ⛔ KHÔNG hardcode `width:25%`. */
.cb-stepper { display: flex; list-style: none; margin: 0; padding: 2px 0; }
.cb-step { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 6px; position: relative; text-align: center; }
.cb-step::before { content: ""; position: absolute; top: 14px; left: -50%; width: 100%; height: 2px; background: var(--cb-border); z-index: 0; }
.cb-step:first-child::before { display: none; }
.cb-step--joindone::before { background: var(--cb-st3-fg); }
.cb-step-dot {
  position: relative; z-index: 1; width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 13px;
  background: var(--cb-surface-2); border: 1px solid var(--cb-border); color: var(--cb-muted);
}
.cb-step--done .cb-step-dot { background: var(--cb-st3-bg); color: var(--cb-st3-fg); border-color: transparent; }
.cb-step--current .cb-step-dot { background: var(--cb-st2-bg); color: var(--cb-st2-fg); border-color: transparent; }
.cb-step-label { font-size: 11.5px; font-weight: 600; line-height: 1.3; }
.cb-step--todo .cb-step-label { color: var(--cb-muted); font-weight: 500; }
/* Mốc thời gian đã xuống dòng riêng nhờ flex-direction:column của .cb-step (nó là flex item ⇒ chiếm hàng riêng).
   ⛔ KHÔNG thêm `display:block` — flex container đã ép display về block-level, thêm vào là thừa. */
.cb-step-time { font-size: 11px; line-height: 1.35; color: var(--cb-muted); }

/* ---------- LINE ITEM ---------- */
.cb-line-list { display: flex; flex-direction: column; gap: 12px; }
.cb-line-item { border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius-lg); padding: 14px; display: flex; flex-wrap: wrap; gap: 14px; }
.cb-line-thumb { width: 66px; height: 66px; flex: none; border-radius: var(--bs-border-radius); object-fit: cover; }
.cb-line-thumb--empty { background: var(--cb-hatch); display: flex; align-items: center; justify-content: center; font-size: 8px; color: var(--cb-muted); }
.cb-line-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.cb-line-titlerow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.cb-line-name { font-weight: 600; font-size: 14px; }
.cb-line-meta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.cb-line-imei { font-family: monospace; font-size: 11px; background: var(--cb-surface-2); border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius-sm); padding: 2px 8px; }
.cb-line-variant { font-size: 12px; color: var(--cb-muted); }
.cb-line-warranty { font-size: 12.5px; color: var(--cb-st3-fg); }
/* 🔴 width:100% chứ KHÔNG chỉ flex-wrap trên cha: .cb-line-main có `flex:1; min-width:0` nên khi container được
   phép wrap nó sẽ CO LẠI nhường chỗ cho cột giá thay vì đẩy cột giá xuống hàng — tên sản phẩm bị bó còn ~120px.
   width:100% mới buộc wrap thật.
   🔴 LẬT 2026-07-31 (PO, audit UI mục 16). Bản cũ ghi: "Mobile xếp thành HÀNG (giá trái · 'Mua lại' phải) để không
   tốn chiều cao" — mô tả đó nay KHÔNG CÒN ĐÚNG, và lựa chọn đó đã bị PO bác. Đo trước lật ở 390px: mép phải khối
   giá 128.73 vs mép phải nút "Mua lại" 342 ⇒ LỆCH 213.27px, hai thứ thuộc cùng một cột tiền mà nằm hai đầu màn.
   Nay xếp CỘT ở mọi khổ (`align-items:flex-end` lo việc canh phải; `text-align:right` giữ cho chữ nhiều dòng).
   ⚖️ ĐÁNH ĐỔI ĐÃ GHI NHẬN, PO CHẤP NHẬN: thẻ dòng SP CAO THÊM ở mobile (đo 390px: 188.25 → 236.75, +48.5px).
   ≥992 chỉ còn khác đúng một thứ: cột co lại theo nội dung (`width:auto`) thay vì chiếm trọn hàng. */
.cb-line-side {
  text-align: right; flex: none; width: 100%;
  display: flex; gap: 8px; flex-direction: column; align-items: flex-end; justify-content: space-between;
}
@media (min-width: 992px) {
  .cb-line-side { width: auto; }
}
.cb-line-price { font-size: 15px; }
.cb-line-qty { font-size: 12px; color: var(--cb-muted); }
.cb-line-rebuy { white-space: nowrap; }

/* ---------- 2 CỘT: nhận hàng/cửa hàng · thanh toán ---------- */
/* 🔴 `align-items:start` — MỘT dòng, chữa CẢ mục 23(a) LẪN mục 27 của audit PO, vì hai chỗ PO kể là CÙNG MỘT class:
   `.cb-cols2` dùng ở Chi tiết đơn (2 hộp thông tin) và ở Tổng quan (Voucher | Yêu thích).
   Mặc định của grid là `stretch` ⇒ hộp NGẮN bị kéo cao bằng hộp DÀI. Đo trước sửa:
     · Chi tiết đơn @768 — hộp thanh toán: nội dung 189.28 trong ô cao 369.09 ⇒ **179.81px trắng**
     · Tổng quan @768 — "Voucher sắp hết hạn": nội dung 270.78 trong ô cao 699.14 ⇒ **428.36px trắng**
     · Tổng quan @1280 — cùng hộp đó: 251.89 / 735.50 ⇒ **483.61px trắng**
   ⚠️ Ngưỡng 2 cột GIỮ NGUYÊN 768 (⛔ không nâng 992): triệu chứng "trống ~300px" PO nêu là hệ quả của `stretch`,
   không phải của breakpoint — sau khi có `start` thì khoảng trắng về 0 ở CẢ 768 (đo lại: slack 0.00), nên không
   còn lý do lật quyết định đã cân nhắc và ghi ở DESIGN_SYSTEM §10.1 #3. */
.cb-cols2 { display: grid; grid-template-columns: 1fr; gap: 16px; align-items: start; }
@media (min-width: 768px) { .cb-cols2 { grid-template-columns: 1fr 1fr; } }
.cb-detail-box { border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius-lg); padding: 16px; }
.cb-detail-box-title { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; font-size: 14px; font-weight: 600; }
.cb-kv-list { display: flex; flex-direction: column; gap: 8px; font-size: 13.5px; }
.cb-kv { display: flex; justify-content: space-between; gap: 12px; }
.cb-kv-val { font-weight: 500; text-align: right; }
.cb-kv-address { display: flex; gap: 8px; padding-top: 8px; border-top: 1px solid var(--cb-border); line-height: 1.5; }
.cb-kv-address > i { color: var(--cb-muted); margin-top: 2px; flex: none; }
.cb-detail-substore { font-size: 12.5px; color: var(--cb-muted); padding-left: 22px; }
.cb-store-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }

/* ---------- THANH TOÁN 2 nhóm ---------- */
.cb-pay { display: flex; flex-direction: column; gap: 14px; }
.cb-pay-group-title { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--cb-muted); }
.cb-pay-total-group { border-top: 1px solid var(--cb-border); padding-top: 14px; }
.cb-pay-rows { display: flex; flex-direction: column; gap: 8px; font-size: 13.5px; }
.cb-pay-row { display: flex; justify-content: space-between; gap: 12px; }
.cb-pay-row--total { align-items: baseline; }
.cb-pay-total { font-size: 20px; }
.cb-pay-vat { font-size: 11px; }

/* ============================================================
   PHASE 3 (P3-T03/04/05) — Tổng quan · Đánh giá · Thông tin TK.
   Chỉ component class + token; vùng/chế độ do cascade ở trên lo.
   ============================================================ */

/* ---------- TỔNG QUAN: tiles (shortcut) + khối aggregate (P3-T03) ---------- */
/* Base = MOBILE: 1 CỘT full-width (audit PO mục 19 — lưới 3 ô vỡ "2 + 1 lẻ").
   🔬 Cửa sổ lỗi ĐO ĐƯỢC là **vw 396–587px**, KHÔNG phải "mọi mobile": với `minmax(180px,1fr)` + gap 12 và bề rộng
   nội dung = vw − 24, hai track cần 372px ⇒ 2 cột bật từ vw 396, ba track cần 564px ⇒ 3 cột chỉ từ vw 588.
   Ở 320/390 lưới VỐN ĐÃ 1 cột (đo: 296px và 366px, một track) — nên PO không thấy lỗi trên máy 390 mà thấy trên
   máy màn to hơn (414/430). Base 1 cột bịt trọn cửa sổ đó.
   🔬 Trả về `auto-fit` từ **768**, KHÔNG 992: ở 768 lưới hiện đang là **1 hàng 3 ô** (đo: 240px/ô, cao 72px);
   ép 1 cột tới 992 sẽ xếp chồng 3 ô ⇒ **+168px chiều cao ở đúng khổ 768** mà PO than "thừa chiều cao" (mục 21).
   Từ 768 trở lên lưới KHÔNG đổi một pixel so với trước task này. */
.cb-tile-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 768px) { .cb-tile-grid { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); } }
.cb-tile { display: flex; align-items: center; gap: 14px; padding: 16px; text-decoration: none; color: var(--cb-body-color); }
.cb-tile:hover { border-color: var(--bs-primary); }
.cb-tile-ico { width: 40px; height: 40px; flex: none; border-radius: var(--bs-border-radius); display: flex; align-items: center; justify-content: center; font-size: 18px; }
/* Chip icon từ token TRẠNG THÁI (né đỏ/gold — CONVENTIONS §5.4) */
.cb-tile-ico--st1 { background: var(--cb-st1-bg); color: var(--cb-st1-fg); }
.cb-tile-ico--st2 { background: var(--cb-st2-bg); color: var(--cb-st2-fg); }
.cb-tile-ico--st3 { background: var(--cb-st3-bg); color: var(--cb-st3-fg); }
.cb-tile-body { display: flex; flex-direction: column; min-width: 0; }
.cb-tile-value { font-weight: 700; font-size: 20px; line-height: 1.1; }
.cb-tile-label { font-size: 12.5px; color: var(--cb-muted); }

.cb-overview-block { padding: 20px; }
.cb-overview-block-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.cb-overview-block-title { margin: 0; font-size: 16px; font-weight: 600; }
.cb-overview-seeall { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 500; color: var(--bs-primary); text-decoration: none; white-space: nowrap; } /* §7: link 600 → 500 */
.cb-overview-seeall:hover { text-decoration: underline; }
.cb-overview-seeall > i { font-size: 11px; }
.cb-overview-empty { display: flex; align-items: center; gap: 8px; margin: 0; padding: 8px 0; font-size: 13px; color: var(--cb-muted); }
.cb-overview-orderlist { display: flex; flex-direction: column; gap: 10px; }
.cb-overview-favgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
/* Mobile chỉ hiện 2 SP (audit PO mục 19): lưới 2 cột × 3 SP ⇒ hàng dưới một ô lẻ nửa bề rộng (đo @390: 2 cột,
   2 hàng, ô 156px). Đã có "Xem tất cả" ngay trên đầu khối nên không mất đường đi tới SP thứ 3.
   🔴 WHY cắt bằng CSS chứ KHÔNG cắt ở controller/ViewComponent: điều kiện là BỀ RỘNG MÀN, thứ server không biết —
   cùng một HTML phải hiện 2 ô ở mobile và 3 ô ở desktop. Cắt phía server thì desktop mất SP thứ 3 vĩnh viễn (vi
   phạm ràng buộc "desktop phải hiện đủ 3"), và sẽ đẻ ra một tham số "số SP" mà không ai gọi được đúng.
   ⚠️ `display:flex` (không phải `block`) ở nhánh ≥lg: phải trả lại ĐÚNG giá trị của .cb-product, và selector này
   specificity (0,2,0) nên cần chính nó để đảo. */
.cb-overview-favgrid > :nth-child(n+3) { display: none; }
@media (min-width: 992px) { .cb-overview-favgrid > :nth-child(n+3) { display: flex; } }

/* Order card GỌN (Tổng quan) — nền phẳng hơn, không viền card kép */
.cb-order-card--compact { padding: 12px; gap: 8px; box-shadow: none; }
.cb-order-card--compact .cb-order-body { padding-top: 10px; }
/* `.cb-order-card--compact .cb-order-thumb { 44px }` ĐÃ GỠ ở P9-T03: base .cb-order-thumb nay CHÍNH LÀ 44px
   (audit mục 26) ⇒ override thành no-op. Biến thể gọn không đổi một pixel nào. */

/* ---------- ĐÁNH GIÁ (P3-T04) ---------- */
.cb-review-list { display: flex; flex-direction: column; gap: 12px; }
.cb-review { padding: 16px; display: flex; gap: 14px; }
.cb-review-thumb {
  width: 56px; height: 56px; flex: none; border-radius: var(--bs-border-radius);
  background: var(--cb-hatch); overflow: hidden;
  display: flex; align-items: center; justify-content: center; font-size: 8px; color: var(--cb-muted);
}
.cb-review-thumb img { width: 100%; height: 100%; object-fit: cover; }
.cb-review-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.cb-review-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cb-review-name { font-weight: 600; font-size: 14px; }
.cb-review-date { font-size: 12px; color: var(--cb-muted); white-space: nowrap; }
.cb-review-sub { font-size: 12px; color: var(--cb-muted); }
.cb-review-stars { display: flex; gap: 3px; font-size: 14px; margin: 2px 0; }
.cb-review-comment { font-size: 13px; line-height: 1.5; margin-top: 4px; }

/* Viết đánh giá — form MỞ SẴN trên card (audit UI mục 7: 5 sao phải bấm được ngay).
   Lớp <details class="cb-review-write"> + <summary class="cb-review-write-toggle"> đã GỠ 2026-07-31 ⇒ 3 rule của
   nó cũng gỡ theo (WHY đầy đủ ở _ReviewCard.cshtml). border-top nay là đường ngăn với tên SP + ngày mua. */
.cb-review-form { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--cb-border); }
.cb-review-textarea {
  border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius);
  background: var(--cb-surface); color: var(--cb-body-color);
  font-family: inherit; font-size: 16px; padding: 10px 12px; resize: vertical; width: 100%; /* 16px: §9 iOS zoom */
}
.cb-review-textarea:focus-visible { outline: none; border-color: var(--bs-primary); }
.cb-review-submit { align-self: flex-start; }

/* Sao NHẬP (radio) — xám, né đỏ/gold. DOM 5→1 + row-reverse ⇒ hiển thị 1..5 trái→phải */
.cb-rate { border: 0; padding: 0; margin: 0; min-width: 0; }
.cb-rate-legend { float: none; width: auto; margin-bottom: 4px; padding: 0; }
.cb-rate-stars { display: inline-flex; flex-direction: row-reverse; justify-content: flex-end; gap: 4px; }
.cb-rate-input { position: absolute; width: 1px; height: 1px; opacity: 0; } /* ẩn nhìn, vẫn nhận focus bàn phím */
/* Vùng chạm sao (audit UI mục 7): <label> inline chỉ cao bằng glyph = 24×24 — đủ WCAG 2.5.8 nhưng DƯỚI ngưỡng 44
   của §10.1, và từ 2026-07-31 sao là control CHÍNH của card (không còn nấp sau <details>) ⇒ nới thành hộp bấm được.
   📐 Vì sao 44 chiều cao mà KHÔNG 44 chiều rộng: 5 sao nằm CÙNG MỘT HÀNG không xuống dòng (row-reverse giữ thứ tự
   1..5), và ở 320px — khổ hẹp nhất dự án đỡ — thân card chỉ còn 194px (320 − 24 container − 32 padding card − 70
   thumb+gap). 5×44 + 4×4 gap = 236px ⇒ TRÀN. Ngân sách thật: 5w + 16 ≤ 194 ⇒ w ≤ 35. Chọn 24 + padding 0 5px = 34
   ⇒ 5×34 + 16 = 186px, vừa ở 320px và dư nhiều từ 375px trở lên. Chiều dọc đạt đủ 44. */
.cb-rate-star {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 5px;
  color: var(--cb-border); font-size: 24px; line-height: 1; cursor: pointer;
}
.cb-rate-input:checked ~ .cb-rate-star { color: var(--cb-star-on); }
.cb-rate-stars:hover .cb-rate-star { color: var(--cb-border); }             /* hover: reset hết... */
.cb-rate-star:hover, .cb-rate-star:hover ~ .cb-rate-star { color: var(--cb-star-on); } /* ...rồi tô chuỗi hover */
.cb-rate-input:focus-visible + .cb-rate-star { outline: 2px solid var(--bs-primary); outline-offset: 2px; border-radius: 2px; }

/* ---------- THÔNG TIN TÀI KHOẢN (P3-T05) ---------- */
.cb-acc-section { padding: 20px; display: flex; flex-direction: column; gap: 14px; }
.cb-acc-section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.cb-acc-section-title { margin: 0; font-size: 16px; font-weight: 600; }
.cb-acc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; font-size: 13.5px; }
.cb-acc-item { min-width: 0; }
.cb-acc-label { font-size: 12px; color: var(--cb-muted); display: inline-flex; align-items: center; gap: 6px; }
.cb-acc-value { font-weight: 500; margin-top: 2px; }
.cb-acc-hint { color: var(--cb-muted); font-weight: 400; font-size: 12px; }

.cb-acc-edit { border-top: 1px solid var(--cb-border); padding-top: 14px; }
.cb-acc-edit-toggle { display: inline-flex; align-items: center; gap: 5px; list-style: none; cursor: pointer; }
.cb-acc-edit-toggle::-webkit-details-marker { display: none; }
.cb-acc-editform { display: flex; flex-direction: column; gap: 12px; margin-top: 14px; }

/* Sổ địa chỉ */
.cb-address-list { display: flex; flex-direction: column; gap: 10px; }
.cb-address { padding: 14px; display: flex; gap: 12px; align-items: flex-start; box-shadow: none; }
.cb-address-pin { color: var(--cb-muted); margin-top: 2px; font-size: 16px; flex: none; }
.cb-address-body { flex: 1; min-width: 0; }
.cb-address-titlerow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.cb-address-label { font-weight: 600; font-size: 13.5px; }
.cb-address-default { display: inline-flex; align-items: center; gap: 4px; background: var(--cb-st3-bg); color: var(--cb-st3-fg); border-radius: var(--bs-border-radius-sm); padding: 2px 8px; font-size: 11px; font-weight: 500; } /* §7: badge "Mặc định" 600 → 500 */
.cb-address-person { font-size: 13px; margin-top: 5px; }
.cb-address-name { font-weight: 500; }
.cb-address-full { font-size: 12.5px; color: var(--cb-muted); margin-top: 3px; line-height: 1.45; }
.cb-address-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 10px; }
/* .cb-address-del đã GỠ: "Xóa" nay là .cb-linkbtn--danger, cùng cấp với 2 nút kia (audit UI mục 32). */
.cb-inline-form { margin: 0; display: inline-flex; }

/* Nút dạng chữ (Sửa · Đặt mặc định · Xóa) */
/* `min-width:44px` + canh giữa: đo P9-T03 @390 — "Sửa" 40.1×44 và "Xóa" 41.4×44, hụt chiều NGANG. Đây là 3 nút
   PO gọi đích danh ở tự-kiểm #2 và chúng đứng một mình (không nằm trong câu văn) ⇒ ⛔ không được hưởng ngoại lệ
   inline của §10.1 #4. "Đặt mặc định" vốn đã rộng hơn 44 nên không xê dịch. */
.cb-linkbtn { background: transparent; border: none; padding: 0; color: var(--bs-primary); font-family: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; min-width: 44px; gap: 4px; } /* §7: nút dạng chữ 600 → 500 */
.cb-linkbtn:hover { text-decoration: underline; }
/* Biến thể PHÁ HỦY ("Xóa" địa chỉ) — nghỉ thì xám, hover/focus mới đỏ: nó ngồi cạnh 2 hành động vô hại, không cần
   hét lên khi chưa ai định bấm. Vẫn không-chỉ-dựa-màu: có icon thùng rác + chữ "Xóa" + confirm() trước khi submit.
   🔴 WHY --cb-st5-fg chứ KHÔNG --bs-primary: --bs-primary LẬT THEO VÙNG — ở Miền Bắc nó là gold #FFB301, tức tín
   hiệu "chú ý", không phải "nguy hiểm"; và .btn-destructive cũ dùng đúng token đó nên nút "Xóa" ở Miền Bắc vốn
   đang GOLD. --cb-st5-fg là khe "Cần chú ý" đỏ-báo: ĐỘC LẬP VÙNG và đã theme-aware sẵn (#B91C1C sáng / #FDA4AF
   tối) ⇒ đỏ ở cả 4 trạng thái Nam/Bắc × sáng/tối. Đo trên nền --cb-surface: 6.5:1 (sáng) · 7.7:1 (tối) — AA. */
.cb-linkbtn--danger { color: var(--cb-muted); }
.cb-linkbtn--danger:hover, .cb-linkbtn--danger:focus-visible { color: var(--cb-st5-fg); }

.cb-acc-pwform { display: flex; flex-direction: column; gap: 12px; }

/* Liên kết tài khoản (Mock read-only) */
.cb-linked-list { display: flex; flex-direction: column; gap: 12px; }
.cb-linked-row { display: flex; align-items: center; gap: 12px; }
.cb-linked-ico { width: 38px; height: 38px; flex: none; border-radius: var(--bs-border-radius); background: var(--cb-surface-2); border: 1px solid var(--cb-border); display: flex; align-items: center; justify-content: center; font-size: 17px; }
.cb-linked-body { flex: 1; min-width: 0; }
.cb-linked-name { font-weight: 600; font-size: 13.5px; }
.cb-linked-detail { font-size: 12px; color: var(--cb-muted); }

/* Form dùng chung (modal + edit) */
.cb-form-grid2 { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 576px) { .cb-form-grid2 { grid-template-columns: 1fr 1fr; } }
.cb-req { color: var(--cb-st5-fg); }
.cb-select {
  border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius);
  background: var(--cb-surface); color: var(--cb-body-color);
  font-family: inherit; font-size: 16px; padding: 11px 13px; width: 100%; /* 16px: §9 iOS zoom */
}
.cb-select:focus-visible { outline: none; border-color: var(--bs-primary); }
.cb-check { display: flex; align-items: center; gap: 8px; font-size: 13.5px; cursor: pointer; }
.cb-check input { width: 16px; height: 16px; accent-color: var(--bs-primary); flex: none; }

/* Modal địa chỉ (Bootstrap modal + surface token) */
.cb-modal { background: var(--cb-surface); color: var(--cb-body-color); border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius-lg); }
.cb-modal-title { font-size: 16px; font-weight: 700; }
.cb-modal-body { display: flex; flex-direction: column; gap: 14px; }
.cb-modal-footer { gap: 10px; }

/* ============================================================
   PHASE 4 (P4-T01) — Hạng thành viên & Ưu đãi.
   Bậc màu dùng .cb-tier--1..4 (thang PHI SẮC, đã có ở khối HẠNG trên).
   Chỉ component class + token; vùng/chế độ do cascade lo. Brand CHỈ dùng
   cho nền/viền/gạch (không làm màu CHỮ — gold+trắng fail AA, CONVENTIONS §5.2).
   ============================================================ */

/* ---------- 4 thẻ hạng: 2×2 (mobile) → 4 cột (lg) ----------
   Base 2 cột từ mobile (audit PO mục 20 — thấy cả 4 hạng trong một tầm mắt). Đo @390: 1 cột 4 hàng cao 516px →
   2×2 cao 252px. ⛔ Rule `min-width:576px` cũ ĐÃ GỠ: base nay chính là `repeat(2,1fr)` nên nó thành no-op, và
   để lại một rule chết là để lại một lời nói dối về nơi quyết định nằm. Ngưỡng 992 giữ nguyên. */
.cb-tiers { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
@media (min-width: 992px) { .cb-tiers { grid-template-columns: repeat(4, 1fr); } }

/* Dòng dẫn "Chạm vào một hạng…" — margin âm để nó bám sát tiêu đề, không đứng như một đoạn riêng. */
.cb-tier-hint { margin: -6px 0 0; font-size: 12.5px; }

/* Thẻ hạng là <a> (chọn hạng = đổi URL ?tier=, không phải hành động đổi dữ liệu ⇒ không phải <button>).
   Reset gạch chân; KHÔNG khai `color` ở đây — màu chữ phải đến từ .cb-tier--N (§4, contrast AA đã đo).
   Vùng chạm: min-height 120 ⇒ dư ngưỡng 44px (§9). */
.cb-tier-card {
  border-radius: var(--bs-border-radius-lg); padding: 16px; min-height: 120px;
  display: flex; flex-direction: column; gap: 8px;
  text-decoration: none;
}
/* Thẻ ĐANG CHỌN: viền brand (trang trí, không phải chữ).
   ⚠️ Trước đây viền này đánh dấu "hạng hiện tại"; nay thẻ chọn được nên viền = ĐANG CHỌN, còn "hạng hiện tại"
   nhận diện bằng badge "HIỆN TẠI" + nhãn chữ — hai tín hiệu khác nhau cho hai khái niệm khác nhau. */
.cb-tier-card--selected { outline: 2px solid var(--bs-primary); outline-offset: 2px; }
.cb-tier-card-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.cb-tier-card-name { font-weight: 700; font-size: 15px; }
/* Badge "HIỆN TẠI": viền + chữ currentColor (= fg thẻ, AA theo §4) — không đốt màu brand */
.cb-tier-card-badge {
  border: 1px solid currentColor; border-radius: var(--bs-border-radius-sm);
  padding: 2px 7px; font-size: 10px; font-weight: 500; letter-spacing: .02em; white-space: nowrap; /* §7: badge "HIỆN TẠI" 700 → 500 */
}
.cb-tier-card-state { font-size: 11.5px; }            /* full opacity → giữ AA (§4) */
.cb-tier-card-threshold { font-size: 11.5px; margin-top: auto; }
/* .cb-tier-card-progress + .cb-tier-card-member + .cb-tier-card-lock đã GỠ (P9-T04): bản thiết kế không có tên
   thành viên, cũng không có icon khóa trên thẻ — trạng thái đã nói bằng NHÃN CHỮ nên icon là lặp lại thừa. */

/* ---------- Thẻ Edu — NGOÀI thang (token --cb-edu-*, không dùng cb-tier--N) ---------- */
.cb-edu-card {
  display: flex; align-items: center; gap: 12px; width: 100%;
  border-radius: var(--bs-border-radius-lg); padding: 14px 16px; min-height: 44px; /* §9 */
  background: linear-gradient(135deg, var(--cb-edu-from), var(--cb-edu-to));
  color: var(--cb-edu-fg); border: 1px solid var(--cb-edu-border);
  text-decoration: none;
}
.cb-edu-card--selected { outline: 2px solid var(--bs-primary); outline-offset: 2px; }
/* Ô icon + badge: viền currentColor thay vì nền tint cứng — cùng lý do .cb-tier-card-badge (0 hardcode, tự theo theme) */
.cb-edu-card-ico {
  width: 38px; height: 38px; flex: none; border-radius: var(--bs-border-radius-lg);
  border: 1px solid currentColor; display: flex; align-items: center; justify-content: center; font-size: 18px;
}
.cb-edu-card-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cb-edu-card-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cb-edu-card-name { font-weight: 700; font-size: 15px; }
.cb-edu-card-badge {
  border: 1px solid currentColor; border-radius: var(--bs-border-radius-sm);
  padding: 2px 7px; font-size: 10px; font-weight: 500; white-space: nowrap;
}
.cb-edu-card-desc { font-size: 11.5px; line-height: 1.4; }
.cb-edu-card-chev { margin-left: auto; font-size: 13px; flex: none; }

/* ---------- Progress nodes + thanh tiến độ ---------- */
.cb-tier-progress { padding: 22px 20px; }
.cb-tier-nodes { display: flex; align-items: center; }
.cb-tier-node {
  width: 22px; height: 22px; flex: none; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 12px;
  background: var(--cb-surface); border: 2px solid var(--cb-border); color: var(--cb-muted);
}
/* Node đã đạt: cặp brand + on-primary (AA cả 2 vùng) */
.cb-tier-node--done { background: var(--bs-primary); border-color: var(--bs-primary); color: var(--cb-on-primary); }
.cb-tier-node--current { box-shadow: 0 0 0 3px rgba(var(--bs-primary-rgb), .22); }
.cb-tier-bar { flex: 1; height: 3px; background: var(--cb-border); }
.cb-tier-bar--done { background: var(--bs-primary); }

.cb-tier-node-labels { display: flex; justify-content: space-between; margin-top: 8px; gap: 6px; }
/* Chữ nhãn = body-color (AA cả 2 vùng); "hiện tại" nhấn bằng gạch chân brand + đậm, KHÔNG tô chữ brand */
.cb-tier-node-label { font-size: 11.5px; font-weight: 600; color: var(--cb-body-color); }
.cb-tier-node-label--current {
  font-weight: 700; text-decoration: underline; text-decoration-color: var(--bs-primary);
  text-decoration-thickness: 2px; text-underline-offset: 3px;
}
.cb-tier-node-label--muted { color: var(--cb-muted); font-weight: 500; }

.cb-tier-progress-panel { margin-top: 18px; background: var(--cb-surface-2); border-radius: var(--bs-border-radius); padding: 14px 16px; }
.cb-tier-progress-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-size: 13px; margin-bottom: 8px; }
.cb-tier-strong { color: var(--cb-body-color); font-weight: 700; }
.cb-tier-progress-foot { font-size: 13px; margin: 10px 0 0; line-height: 1.5; }

/* ---------- Khối chi tiết hạng đang chọn (_TierBenefit) ---------- */
.cb-tier-box { padding: 20px; }
.cb-tier-box-title { margin: 0; font-size: 15px; font-weight: 600; }
.cb-tier-detail-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
/* "Hạng của bạn": cặp st3 (lá) — né CẢ đỏ VÀ gold, vì brand một vùng có thể là gold (CONVENTIONS §5.4) */
.cb-tier-detail-badge {
  background: var(--cb-st3-bg); color: var(--cb-st3-fg);
  border-radius: var(--bs-border-radius-sm); padding: 2px 8px; font-size: 11px; font-weight: 500; white-space: nowrap;
}
.cb-tier-condition {
  display: flex; gap: 10px; font-size: 13.5px; line-height: 1.55;
  background: var(--cb-surface-2); border-radius: var(--bs-border-radius); padding: 12px 14px;
}
.cb-tier-condition > i { color: var(--cb-muted); margin-top: 2px; flex: none; }
.cb-tier-condition-label { font-weight: 600; }

/* Luật loại trừ của Edu — cặp st5 (đỏ báo). Đây là cảnh báo THẬT (mất quyền lợi), không phải trang trí. */
.cb-tier-warn {
  display: flex; gap: 10px; font-size: 13px; line-height: 1.55; margin-top: 10px;
  background: var(--cb-st5-bg); color: var(--cb-st5-fg);
  border-radius: var(--bs-border-radius); padding: 12px 14px;
}
.cb-tier-warn > i { margin-top: 2px; flex: none; }

.cb-tier-benefits-title {
  margin: 18px 0 12px; font-size: 12px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--cb-muted);
}
.cb-tier-benefits { display: flex; flex-direction: column; gap: 10px; }
.cb-tier-benefit {
  display: flex; gap: 12px; align-items: flex-start;
  border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius-lg); padding: 12px 14px;
}
/* Icon quyền lợi: TRUNG TÍNH, KHÔNG --bs-primary như canvas vẽ. Lý do đo được: ở Miền Bắc brand là gold #FFB301,
   trên nền --cb-surface-2 (#F8FAFC) chỉ ~1.8:1 ⇒ icon gần như tàng hình. Cùng lớp lỗi mà .cb-tier-perk-ico cũ đã
   né bằng màu lá. Icon là aria-hidden (title + desc đã mang nghĩa) nên WCAG miễn, nhưng "nhìn không thấy" vẫn là lỗi. */
.cb-tier-benefit-ico {
  width: 34px; height: 34px; flex: none; border-radius: var(--bs-border-radius-lg);
  background: var(--cb-surface-2); color: var(--cb-body-color);
  display: flex; align-items: center; justify-content: center; font-size: 16px;
}
.cb-tier-benefit-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cb-tier-benefit-name { font-weight: 600; font-size: 13.5px; line-height: 1.35; }
.cb-tier-benefit-desc { font-size: 12.5px; color: var(--cb-muted); line-height: 1.5; }
.cb-tier-footnote { margin: 14px 0 0; font-size: 12.5px; color: var(--cb-muted); line-height: 1.55; }

/* ---------- Nhóm khóa: hiện, KHÔNG ẩn (DESIGN_SYSTEM §11) — theme-aware ----------
   ⚠️ 0 usage kể từ P9-T04 (bản thiết kế mới không còn khối "nhóm quyền lợi chưa mở"). GIỮ LẠI có chủ đích:
   DESIGN_SYSTEM §11 chỉ định đây là pattern CANONICAL cho locked-state (SA-REVIEW-002/CONCERN-019) — xóa là đổi
   design system, việc của @sa chứ không phải dọn dẹp tiện tay. ⛔ Đừng gỡ mà không sửa §11 kèm theo. */
.cb-tier-locked-box {
  background: var(--cb-surface-2); border: 1px dashed var(--cb-border);
  border-radius: var(--bs-border-radius-lg); padding: 22px; text-align: center;
}
.cb-tier-locked-ico { font-size: 24px; color: var(--cb-muted); opacity: .7; }
.cb-tier-locked-title { font-weight: 600; font-size: 14px; margin-top: 8px; color: var(--cb-body-color); }
.cb-tier-locked-desc { font-size: 13px; margin-top: 4px; line-height: 1.5; }

/* ============================================================
   PHASE 5A — Auth (vỏ + form + wizard quên mật khẩu).
   Vùng/chế độ do cascade ở đầu file lo; ở đây chỉ component class + token.
   .cb-auth-shell/.cb-auth-card/.cb-authgrid/.cb-authform: xem khối AUTH LAYOUT phía trên.
   ============================================================ */

/* ---------- Logo màn auth — điểm render thứ 4 của --cb-logo ----------
   Chỉ còn KHUNG (display/background do .cb-logo lo). Khung 4.00 như 3 điểm render kia.
   🔴 .cb-auth-logo-mark / .cb-auth-logo-text ĐÃ XÓA (P8-T02): logo tự vẽ (ô xoay 45° + chữ) là sót của P8-T01 §4.
   Đã kiểm 0 tham chiếu còn lại trong src/ trước khi xóa. ⛔ Đừng dựng lại — logo là ẢNH THẬT theo vùng. */
/* 28 → 44px (2026-08-08, PO). Lấy ĐÚNG khung .cb-siteheader-logo (44×176) thay vì bịa số mới: từ khi header bị gỡ
   khỏi _AuthLayout (cùng ngày), logo này là NEO THƯƠNG HIỆU DUY NHẤT trên màn đầu tiên khách thấy — mà nó đang là
   khung nhỏ nhất dự án. ⚠️ Giữ TỈ LỆ 4.00 (44×4 = 176): ảnh letterbox bằng background-size:contain nên lệch tỉ lệ
   không méo ảnh mà tạo khoảng trống chết trong khung, khó thấy hơn hẳn. */
.cb-auth-logo { height: 44px; width: 176px; margin-bottom: 24px; }

/* ---------- Thân form ---------- */
.cb-auth-body { display: flex; flex-direction: column; gap: 16px; }
.cb-auth-title { margin: 0 0 4px; font-size: 22px; font-weight: 700; }
.cb-auth-sub { margin: 0; font-size: 13.5px; color: var(--cb-muted); }
.cb-auth-strong { color: var(--cb-body-color); }
.cb-auth-foot { margin: 0; text-align: center; font-size: 13.5px; color: var(--cb-muted); }
/* Link brand-text trên surface: CONCERN-021 (Bắc-sáng 1.79 · Nam-tối 3.08 — PO hoãn tới maintenance;
   fix turnkey = repoint sang token --cb-brand-text). Giữ --bs-primary cho nhất quán với
   .cb-overview-seeall/.cb-linkbtn đã ship — KHÔNG tự đổi token ở task này. */
.cb-auth-link { font-weight: 500; color: var(--bs-primary); text-decoration: none; } /* §7: mọi link → 500 */
.cb-auth-link:hover { text-decoration: underline; }
/* Link chân trang auth: nới vùng chạm lên 44px. Điểm xuất phát là /forgot-password và /verify-otp, nơi
   `<p class="cb-auth-foot"><a>Về đăng nhập</a></p>` có link ĐỨNG MỘT MÌNH, cao **18px**, đang mượn ngoại lệ
   "link trong câu văn" (§10.1 #4) mà nó không đủ điều kiện hưởng.

   🔴 ĐỌC KỸ — `:only-child` **KHÔNG** phân biệt được hai ca đó. Bản trước của comment này khẳng định nó phân biệt
   được: **SAI**, `@cr` bắt ở P9-T03. `:only-child` chỉ đếm **phần tử con**, text node KHÔNG tính ⇒ trong
   `<p>Chưa có tài khoản? <a>Đăng ký ngay</a></p>` thì `<a>` VẪN là con-duy-nhất. Đo trên app đang chạy:
   `.cb-auth-foot > .cb-auth-link:only-child` khớp **5/5** link chân trang (Login · Register · RegisterDeferred ·
   ForgotPassword · VerifyOtp), ⛔ không phải 2. ⇒ Selector này **KHÔNG có tác dụng lọc**; nó tương đương
   `.cb-auth-foot > .cb-auth-link`.

   ⚖️ **GIỮ NGUYÊN HÀNH VI — có chủ đích, không phải bỏ qua.** 44px cho cả 5 link là **hợp chuẩn hơn**: ngoại lệ
   inline của WCAG 2.5.8 là thứ *được phép*, không phải thứ *bắt buộc*. Đánh đổi ĐÃ ĐO ở /login @390:
   `<p class="cb-auth-foot">` **20.25 → 44.00px** (+23.75), thẻ auth **1032.45 → 1056.20px** (cùng +23.75).
   ⛔ Ai muốn thu về đúng 2 ca "link đứng một mình" thì `:only-child` KHÔNG làm được — phải dùng
   **class tường minh**, và phải đo lại cả 5 màn.
   ⚠️ ĐỪNG thử `:not(:first-child)` — nó dính **CÙNG bẫy text-node**: `<a>` vẫn là phần tử con ĐẦU TIÊN
   trong `<p>Chưa có tài khoản? <a>…</a></p>` (chữ không tính) ⇒ khớp **0/5**, tức TẮT rule chứ không thu hẹp.
   Mọi selector cấu trúc đều mù với text node — chỉ class mới phân biệt được. */
.cb-auth-foot > .cb-auth-link:only-child { min-height: 44px; display: inline-flex; align-items: center; }
.cb-auth-forgot { display: flex; justify-content: flex-end; }

/* ---------- Alert (tông st5 đỏ báo / st3 lá — icon BẮT BUỘC trong markup) ---------- */
.cb-auth-alert {
  display: flex; gap: 9px; border-radius: var(--bs-border-radius);
  padding: 11px 13px; font-size: 13px; line-height: 1.5;
}
.cb-auth-alert > i { flex: none; margin-top: 1px; }
.cb-auth-alert--err { background: var(--cb-st5-bg); color: var(--cb-st5-fg); }
.cb-auth-alert--ok  { background: var(--cb-st3-bg); color: var(--cb-st3-fg); }

/* ---------- Field (DESIGN_SYSTEM §14) ---------- */
.cb-field { display: flex; flex-direction: column; gap: 6px; }
.cb-field-label { font-size: 13px; font-weight: 500; }
.cb-field-box {
  display: flex; align-items: center; gap: 8px;
  border: 1px solid var(--cb-border); border-radius: var(--bs-border-radius);
  padding: 11px 13px; background: var(--cb-surface);
}
.cb-field-box:focus-within { border-color: var(--bs-primary); }
.cb-field-box--err { border-color: var(--cb-money-due); }
.cb-field-icon { color: var(--cb-muted); flex: none; }
/* KHÔNG outline:none — vòng focus bàn phím do :focus-visible toàn cục lo (a11y §13). */
.cb-field-input {
  flex: 1; min-width: 0; border: none; background: transparent;
  color: var(--cb-body-color); font-family: inherit; font-size: 16px; /* 16px: §9 iOS zoom */
}
.cb-field-hint { font-size: 12px; color: var(--cb-muted); }
.cb-field-err { font-size: 12px; color: var(--cb-money-due); }
.cb-field-optional { color: var(--cb-muted); font-weight: 400; } /* "· không bắt buộc" trong <label> */
/* 🔴 Nút hiện/ẩn mật khẩu — PO gọi đích danh ở tự-kiểm #2, đo được **15×15.1px** (nhỏ hơn 1/8 diện tích tối thiểu).
   Nới thành 44×44 THẬT, nhưng KHÔNG được làm cao thêm .cb-field-box: margin âm ăn đúng padding của hộp
   (`11px 13px`) ⇒ hộp-lề của nút cao 44−22 = 22px = đúng chiều cao cũ ⇒ mọi ô mật khẩu giữ nguyên 46px như trước.
   ⛔ KHÔNG dùng mẹo `::after { inset }` như .cb-product-fav ở đây: vùng ảo sẽ trùm 15px sang trái, nuốt mất cú
   bấm vào đuôi ô nhập. Nút này ngồi TRONG ô chữ, con kia ngồi ĐÈ lên ảnh — khác bối cảnh, khác lời giải. */
.cb-field-toggle {
  flex: none; background: transparent; border: none; color: var(--cb-muted);
  cursor: pointer; padding: 0; font-size: 15px; line-height: 1;
  min-width: 44px; min-height: 44px; margin-block: -11px; margin-right: -13px;
  display: inline-flex; align-items: center; justify-content: center;
}

/* ---------- Nút submit + loading ---------- */
.cb-auth-submit {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; border-radius: var(--bs-border-radius); border: 1px solid var(--bs-primary);
  padding: 13px; font-size: 14px; font-weight: 500; font-family: inherit;   /* §7: nút CTA submit → 500 */
  cursor: pointer; min-height: 48px;
}
.cb-auth-submit:disabled { cursor: default; opacity: .72; }
@keyframes cb-spin { to { transform: rotate(360deg); } }
.cb-spin { display: inline-block; animation: cb-spin .7s linear infinite; }
@media (prefers-reduced-motion: reduce) { .cb-spin { animation: none; } }

/* ---------- Divider "hoặc" + nút social ---------- */
.cb-auth-or { display: flex; align-items: center; gap: 12px; color: var(--cb-muted); font-size: 12px; }
.cb-auth-or::before, .cb-auth-or::after { content: ""; flex: 1; height: 1px; background: var(--cb-border); }
/* CỘT, không hàng (P5B-T04 — canvas gốc là hàng 2 nút). WHY: nút Google BẮT BUỘC là google.accounts.id.renderButton
   (SDK không có đường nào khác cấp idToken cho nút tự vẽ) và width tối thiểu của nó là 200px. Cột form rộng 313px
   ở 375px và 450px ở desktop (đo 2026-08-08, P10-T09 — số cũ "~295px" là ước lượng, nay thay bằng số đo) ⇒ hàng 3 nút
   = 97–143px/nút ⇒ DƯỚI ngưỡng ở MỌI breakpoint ⇒ hàng là BẤT KHẢ, không phải lựa chọn thẩm mỹ. Cột cũng giải luôn
   bài "3 nút ở 375px chật" mà không cần media query nào.

   🔴 max-width 400px = TRẦN CỦA renderButton (SDK nhận 200–400px), hạ CẢ CỤM xuống cho khớp nút Google.
   ĐÂY LÀ ĐẢO NGƯỢC CÓ CHỦ ĐÍCH của luật cũ "đừng bóp Facebook/Zalo cho bằng Google" — PO quyết 2026-08-08 sau khi
   nhìn ảnh chụp /login: 3 nút không thành một hệ thì lệch đó là LỖI NHÌN THẤY, không phải giới hạn đáng bảo tồn.
   WHY đầy đủ + số đo 3 khổ: googleButtonWidth() trong wwwroot/js/clickbuy.js.
   ⛔ KHÔNG cần media query, và đó KHÔNG phải may mắn: dưới 992px `.cb-authform` bị `max-width:420px` chặn ⇒ cụm luôn
   ≤ 384px < 400 ⇒ trần này KHÔNG BAO GIỜ chạm tới ở 375/768 (đo: cả hai khổ giữ nguyên 313px/384px sau khi sửa).
   ⛔ Đảo ngược CHỈ áp cho CHIỀU NGANG — luật chiều CAO ở .cb-social-gsi bên dưới ("đừng bóp xuống 40px") VẪN NGUYÊN
   HIỆU LỰC: ở đó hạ chuẩn là mất VÙNG CHẠM 48px (§9 v2), ở đây chỉ hẹp bớt bề rộng, không nút nào mất vùng chạm.

   🔴 `width:100%` là BẮT BUỘC ĐI KÈM `margin-inline:auto`, KHÔNG phải thừa — CÙNG MỘT CÁI BẪY đã ghi ở .cb-authform
   (dòng ~470), nay tái diễn ở phiên bản FLEX: .cb-social-row là FLEX ITEM của .cb-auth-body (column), margin trục
   ngang `auto` TẮT stretch ⇒ cụm co về fit-content. Đo bản thiếu nó ở 1280px: cụm tụt 450 → 234px và kéo theo CẢ
   nút Google (googleButtonWidth đọc bề rộng CỦA CỤM ⇒ renderButton nhận 234) ⇒ 3 nút vẫn "đều" nhưng đều SAI.
   ⚠️ Bẫy này KHÔNG lộ ra nếu thử bằng cách chèn CSS vào trang đang mở: lúc đó nút Google 400px đã render sẵn nên nó
   tự chống cho fit-content = 400 ⇒ trông như PASS. Chỉ NẠP LẠI TRANG mới thấy. Sửa ở đây thì đo trên trang tải mới. */
.cb-social-row { display: flex; flex-direction: column; gap: 10px; width: 100%; max-width: 400px; margin-inline: auto; }
.cb-social-row form { margin: 0; }
/* Dùng cho CẢ <button> (Facebook) lẫn <a> (Zalo — link thuần, chạy khi JS tắt) ⇒ text-decoration:none để anchor không
   mọc gạch chân và lệch khỏi hệ 3 nút (khuôn .cb-tile/.cb-nav-item; Reboot đặt a{text-decoration:underline}). */
.cb-social-btn {
  width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px;
  border: 1px solid var(--cb-border); background: var(--cb-surface); color: var(--cb-body-color);
  border-radius: var(--bs-border-radius); padding: 11px; text-decoration: none;
  font-size: 13.5px; font-weight: 500; font-family: inherit; cursor: pointer; min-height: 48px; /* §7: nút phụ → 500 · §9 v2: 44 → 48 */
}
/* SDK chưa nạp (Google/FB) hoặc thiếu config (Zalo): trạng thái disabled phải NHÌN THẤY, không chỉ mất click. */
.cb-social-btn:disabled { cursor: not-allowed; opacity: .55; }
/* Màu thương hiệu bên thứ 3 — KHÔNG phải brand Clickbuy ⇒ không vướng §5.1 (tiền lệ Zalo đã duyệt ở P5A-T03).
   Icon là aria-hidden + đã có nhãn chữ kề bên ⇒ ngưỡng áp dụng là 1.4.11 non-text 3:1, không phải 1.4.3 text 4.5:1.
   Đo trên --cb-surface: Zalo 4.75 sáng / 3.08 tối · Facebook 4.23 sáng / 3.46 tối (bảng đầy đủ ở result P5B-T04). */
.cb-social-zalo { color: #0068FF; }
.cb-social-facebook { color: #1877F2; }
/* Khung nút thật của Google (iframe do GIS render — CSS của ta KHÔNG với tới bên trong). Rỗng cho tới khi SDK nạp
   xong ⇒ :empty để nó không chiếm một khoảng gap của .cb-social-row khi SDK hỏng/mạng chặn. */
/* min-height 48px đặt trên KHUNG BỌC để nhịp 3 nút không gãy — ⚠️ vùng bấm THẬT vẫn ~40px: `size:"large"` là bậc
   cao nhất GIS cấp và SDK KHÔNG có tham số chiều cao. Giới hạn SDK đã biết, cùng họ CANVAS-01.
   ⛔ ĐỪNG bóp Facebook/Zalo xuống 40px cho "đều" — hạ chuẩn 2 nút ta kiểm soát được để khớp 1 nút ta không kiểm soát. */
.cb-social-gsi { display: flex; align-items: center; justify-content: center; min-height: 48px; }
.cb-social-gsi:empty { display: none; }

/* ---------- Panel quyền lợi — LUÔN HIỆN, kể cả mobile (P8-T02 §9 Auth) ----------
   🔴 Bản cũ `display:none` dưới lg. PO bỏ 2026-07-31: "90% người dùng không thấy giá trị của việc tạo tài khoản"
   — ẩn đúng ở khổ chiếm 90% traffic thì panel gần như không tồn tại. Mobile: thu gọn + `border-top` (nó nằm DƯỚI
   form, không phải bên cạnh) ⇒ viền trái sẽ là một vạch treo lơ lửng. ≥ lg: về 2 cột như cũ. */
.cb-panel {
  display: flex; flex-direction: column; justify-content: center; gap: 14px;
  padding: 18px; border-top: 1px solid var(--cb-border);
  background: linear-gradient(160deg, rgba(var(--bs-primary-rgb), .10), rgba(var(--bs-primary-rgb), .02));
}
.cb-panel-title { margin: 0; font-size: 15px; font-weight: 700; line-height: 1.35; }
.cb-benefit-list { display: flex; flex-direction: column; gap: 14px; }
/* ⚠️ Khối này PHẢI đứng SAU 2 rule base ở trên: cùng specificity ⇒ thứ tự nguồn quyết định, đặt trước sẽ bị base đè. */
@media (min-width: 992px) {
  .cb-panel { gap: 22px; padding: 32px 30px; border-top: 0; border-left: 1px solid var(--cb-border); }
  .cb-panel-title { font-size: 19px; }
  .cb-benefit-list { gap: 18px; }
}
.cb-benefit { display: flex; gap: 12px; align-items: flex-start; }
/* Icon brand trên surface — cùng lớp CONCERN-021 (icon trang trí, kèm chữ nên màu không là tín hiệu duy nhất). */
.cb-benefit-icon {
  flex: none; width: 38px; height: 38px;
  border-radius: var(--bs-border-radius); background: var(--cb-surface);
  border: 1px solid var(--cb-border); display: flex; align-items: center; justify-content: center;
  color: var(--bs-primary); font-size: 17px;
}
.cb-benefit-title { font-weight: 600; font-size: 14px; line-height: 1.3; }
.cb-benefit-note { font-size: 12.5px; color: var(--cb-muted); line-height: 1.45; margin-top: 2px; }
.cb-panel-foot {
  display: flex; align-items: center; gap: 8px; padding-top: 6px;
  border-top: 1px solid var(--cb-border); font-size: 12px; color: var(--cb-muted);
}

/* ---------- Ô OTP (dùng ở P5A-T04 wizard + P5B-T02) ---------- */
.cb-otp-fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
/* gap 6px mobile → 8px ≥ lg (§9 v2). font-size 20px của .cb-otp-box đã > 16px ⇒ không dính bẫy iOS zoom. */
.cb-otp-row { display: flex; gap: 6px; justify-content: space-between; }
@media (min-width: 992px) { .cb-otp-row { gap: 8px; } }
.cb-otp-box {
  flex: 1 1 0; min-width: 0; max-width: 52px; aspect-ratio: 1;
  text-align: center; border: 1px solid var(--cb-border);
  border-radius: var(--bs-border-radius); background: var(--cb-surface);
  color: var(--cb-body-color); font-family: inherit; font-size: 20px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.cb-otp-box:focus { border-color: var(--bs-primary); }

/* ============================================================
   PHASE 5B-T02 — Đăng ký + Xác thực OTP.
   ============================================================ */

/* ---------- Link BÊN TRONG alert (nền st5) ---------- */
/* color:inherit — KHÔNG dùng --bs-primary: (a) trên nền st5 thì brand gold/đỏ tụt contrast (cùng lớp CONCERN-021),
   (b) chữ st5-fg vốn đã đo đạt AA trên st5-bg ở cả 4 trạng thái. GẠCH CHÂN là tín hiệu phi-màu để link vẫn nhận ra
   được mà KHÔNG cần đến màu (màu không là tín hiệu duy nhất — DESIGN_SYSTEM §13); §7 hạ 700 → 500 nên underline giờ
   gánh TOÀN BỘ vai trò đó — ⛔ đừng gỡ underline khi tinh chỉnh sau này. */
.cb-auth-alert-link { color: inherit; font-weight: 500; text-decoration: underline; }

/* ---------- Thanh độ mạnh mật khẩu (3 vạch) ---------- */
/* Token: --cb-money-discount (lá) khi đạt · --cb-border khi chưa. NÉ CẢ ĐỎ LẪN GOLD (CONVENTIONS §5.4) — thang
   đỏ→vàng→xanh kinh điển sẽ trùng brand của một trong hai vùng. Số VẠCH mới là tín hiệu, không phải màu. */
.cb-pw-strength { display: flex; gap: 5px; margin-top: 2px; }
.cb-pw-bar { flex: 1; height: 4px; border-radius: 2px; background: var(--cb-border); }
.cb-pw-bar--on { background: var(--cb-money-discount); }

/* ---------- Hàng "Gửi lại mã" + "Đổi số điện thoại" ---------- */
.cb-otp-actions {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; font-size: 13px;
}
.cb-otp-resend { display: inline-flex; align-items: center; gap: 6px; min-height: 24px; }
.cb-otp-countdown { color: var(--cb-muted); }
/* 2 nút chữ dưới đây tô --bs-primary: cùng lớp CONCERN-021 (Bắc-sáng 1.79 · Nam-tối 3.08 — PO hoãn tới maintenance,
   fix turnkey = repoint sang --cb-brand-text). Giữ --bs-primary cho NHẤT QUÁN với .cb-auth-link/.cb-linkbtn đã ship
   — dựng riêng bây giờ sẽ lệch phần còn lại của app, rồi lệch lần nữa khi token kia land. KHÔNG tự đổi token ở đây. */
.cb-otp-resend-btn, .cb-otp-change {
  display: inline-flex; align-items: center; gap: 4px;
  background: transparent; border: none; padding: 0;
  color: var(--bs-primary); font-family: inherit; font-size: 13px; font-weight: 500;   /* §7: nút chữ → 500 */
  text-decoration: none; cursor: pointer;
}
.cb-otp-resend-btn:hover, .cb-otp-change:hover { text-decoration: underline; }

/* ---------- Mini stepper (wizard quên mật khẩu) ----------
   🔴 TIỀN TỐ `.cb-authstep*` LÀ BẮT BUỘC, KHÔNG phải sở thích đặt tên. Bộ này TỪNG mang tên `.cb-stepper`/`.cb-step`
   — trùng nguyên xi bộ stepper tiến trình đơn hàng ở dòng ~643 (P3). Cùng specificity (0,1,0), bộ này đứng SAU
   trong file ⇒ thắng theo thứ tự nguồn ⇒ mỗi bước của stepper đơn hàng bị ép thành hình tròn 24×24, 4 nhãn +
   mốc thời gian chồng lên nhau (audit PO mục 1). Hỏng LAI nên đọc code không thấy: chỉ `flex` và `display` bị đè,
   7 thuộc tính khác bị BƠM THÊM, còn `flex-direction:column`/`position:relative`/`text-align:center` của P3 sống sót.
   ⛔ Đừng đổi ngược về `.cb-step*`. ⛔ Đừng "chữa" bằng `!important` — đó là đặt rule sai chỗ. */
.cb-authstepper { display: flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 600; }
.cb-authstep {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--cb-surface-2); border: 1px solid var(--cb-border); color: var(--cb-muted);
}
.cb-authstep--on { background: var(--bs-primary); border-color: var(--bs-primary); color: var(--cb-on-primary); }
.cb-authstep-line { flex: 1; height: 2px; background: var(--cb-border); }
.cb-authstep-line--on { background: var(--bs-primary); }
.cb-authstep-actions { display: flex; gap: 10px; }
.cb-authstep-back {
  flex: none; border: 1px solid var(--cb-border); background: transparent; color: var(--cb-body-color);
  border-radius: var(--bs-border-radius); padding: 12px 16px; min-height: 48px;   /* §9 v2: nhịp 48px cùng nút social */
  display: inline-flex; align-items: center; justify-content: center;
  font-family: inherit; font-size: 14px; font-weight: 500; cursor: pointer;   /* §7: nút phụ → 500 */
}

/* ---------- Nút đăng xuất trong _MemberNav (form POST — chống CSRF) ---------- */
/* .cb-nav-item đã reset border/background/width/text-align → <button> chỉ cần thừa kế font.
   Form là flex item bình thường của .cb-nav (KHÔNG display:contents — né lỗi a11y tree). */
.cb-nav-logout { margin: 0; }
button.cb-nav-item { font-family: inherit; font-size: inherit; line-height: inherit; }

/* ---------- Bố cục auth: xem khối AUTH LAYOUT + .cb-panel ở trên ----------
   🔴 Khối `@media (max-width: 991.98px)` cũ ở đây ĐÃ GỠ (P8-T02 §9): stylesheet này mobile-first, base = mobile và
   nâng lên bằng `min-width:992px`. ⛔ Đừng dựng lại media query max-width nào ở đây.
   Ngưỡng vẫn là lg = 992px (CONVENTIONS §12 · DESIGN_SYSTEM §10 · CANVAS-04). Canvas v2 nhắc lại 820px — KHÔNG
   theo: sẽ là ngưỡng thứ 3 lạc khỏi hệ Bootstrap của dự án, và bản v2 không đưa lý lẽ mới cho chính con số đó. */

/* ============================================================
   PHASE 8 (P8-T01) — Dải lối tắt tài khoản (_MemberShortcuts), dưới hero, trên vùng nav + nội dung.
   Là LỐI TẮT, KHÔNG phải nav: _MemberNav vẫn là 1 định nghĩa → 2 điểm render (CONVENTIONS §4).
   ============================================================ */
/* Base = MOBILE: LƯỚI 3 cột × 2 hàng — thấy đủ 6 mục cùng lúc, ⛔ KHÔNG cuộn ngang (§7 v2 lật bản P8-T01, vốn là
   một hàng `nowrap; overflow-x:auto` ⇒ 3 mục cuối ẩn sau mép màn hình).
   `position: relative` nằm ở .cb-shortcut (KHÔNG ở .cb-shortcut-ico) vì badge là ANH EM của chip trong DOM, không
   phải con của nó — xem WHY đầy đủ ở .cb-shortcut-badge. */
.cb-shortcuts {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 4px; padding: 12px 8px;
}
.cb-shortcut {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: 6px;
  padding: 8px 4px; border-radius: var(--bs-border-radius);
  color: var(--cb-body-color); text-decoration: none; text-align: center;
  font-size: 12px; font-weight: 500; min-height: 44px; white-space: normal; line-height: 1.3;
}
.cb-shortcut:hover { background: var(--cb-surface-2); color: var(--cb-body-color); }
/* Icon brand trên surface — CÙNG LỚP CONCERN-021 (đang DEFERRED): đo được Bắc-sáng #FFB301 trên #F8FAFC = 1,72:1,
   Nam-tối 3,76:1. KHÔNG vi phạm AA vì icon là TRANG TRÍ (aria-hidden, luôn có nhãn chữ ngay cạnh ⇒ màu không phải
   tín hiệu duy nhất) — cùng lý lẽ đã duyệt cho .cb-benefit-icon. Fix turnkey khi token --cb-brand-text land: repoint
   một dòng, CHUNG một đợt với .cb-auth-link/.cb-linkbtn/.cb-overview-seeall. ⛔ Đừng dựng treatment riêng ở đây —
   lệch phần còn lại của app bây giờ, rồi lệch lần nữa lúc token kia về. */
.cb-shortcut-ico {
  flex: none; width: 40px; height: 40px; border-radius: 50%;   /* 40px mobile → 32px ≥ lg (§7 v2) */
  background: var(--cb-surface-2); color: var(--bs-primary);
  display: flex; align-items: center; justify-content: center; font-size: 18px;
}
/* Chip cùng token nền với hover của mục ⇒ khi hover chip sẽ TAN vào nền. Nâng chip lên --cb-surface để nó còn thấy. */
.cb-shortcut:hover .cb-shortcut-ico { background: var(--cb-surface); }
/* Badge đếm: cặp brand đầy đủ (nền --bs-primary PHẲNG + chữ --cb-on-primary) — badge đếm KHÔNG dùng gradient (§3).
   role="img" + aria-label ở markup: số trần "3" cạnh "Mã giảm giá" là vô nghĩa với trình đọc màn hình. */
/* 🔴 VỊ TRÍ BADGE PHẢI Ở ĐÂY, TRONG CSS. ⛔ KHÔNG `style=""` trong .cshtml: inline style luôn thắng stylesheet ⇒
   rule `position:static` của khổ desktop bên dưới sẽ KHÔNG BAO GIỜ có hiệu lực và badge đè lên chữ nhãn.
   ⛔ Và KHÔNG chữa bằng `!important` — nó chỉ đổi bên nào thắng, không sửa việc để vị trí sai chỗ.
   ⚠️ Khối chứa (containing block) là .cb-shortcut chứ KHÔNG phải .cb-shortcut-ico: trong DOM badge là ANH EM của
   chip, không phải con. Nhét badge vào trong chip để "đúng khuôn" sẽ phá khổ desktop, nơi badge phải là một viên
   pill ĐỨNG RIÊNG sau nhãn. Chip rộng 40px và canh giữa ô ⇒ mép phải của nó ở `50% + 20px` ⇒ neo badge từ
   `50% + 8px` để nó nằm đúng góc trên-phải của chip. */
.cb-shortcut-badge {
  position: absolute; top: 4px; left: calc(50% + 8px);
  flex: none; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
  background: var(--bs-primary); color: var(--cb-on-primary);
  font-size: 11px; font-weight: 500;
  display: flex; align-items: center; justify-content: center;
}
/* ≥ lg (992px — DESIGN_SYSTEM §10 · CANVAS-04, ⛔ KHÔNG 900px của canvas): về một HÀNG NGANG như bản P8-T01.
   ⛔ TUYỆT ĐỐI không overflow-x ở đây — hàng phải tự vừa; hết chỗ thì `wrap` xuống dòng, không đẻ thanh cuộn. */
@media (min-width: 992px) {
  .cb-shortcuts {
    display: flex; align-items: center; justify-content: space-between;
    flex-wrap: wrap; padding: 12px 16px;
  }
  .cb-shortcut {
    position: static; flex: none; flex-direction: row; justify-content: center; gap: 8px;
    padding: 6px 8px; font-size: 13px; white-space: nowrap;
  }
  .cb-shortcut-ico { width: 32px; height: 32px; font-size: 16px; }
  .cb-shortcut-badge { position: static; }
}
