/**
 * Layout CSS cho Thekoco theme (Homepage theo Figma design/koco.jpg + trang WooCommerce/blog).
 * Dùng token từ tokens.css, không hardcode giá trị màu/spacing. 3 breakpoint: desktop (mặc định,
 * >1024px), tablet (768-1024px), mobile (<768px) — xem docs/DesignSystem.md.
 */

/* CHG-016 (2026-08-24, Block 02 Hero Typography Fix): 'Georgia' (CHG-015) BỊ LOẠI — screenshot thật
 * (Product Owner gửi) cho thấy các ký tự tiếng Việt CÓ DẤU ĐÔI (circumflex + thanh điệu xếp chồng —
 * "ố/ồ/ổ/ỗ/ộ", "ấ/ầ/ẩ/ẫ/ậ", "ắ/ằ/ẳ/ẵ/ặ") render TÁCH RỜI dấu khỏi chữ (VD "Chống" → "Chô´ng"). Đã
 * xác minh KHÔNG PHẢI lỗi dữ liệu (`Normalizer::isNormalized($title, FORM_C)` = true cho cả 3 slide
 * — chuỗi NFC chuẩn, không phải chuỗi NFD bị tách dấu từ nguồn). Root cause THẬT: font hệ điều hành
 * 'Georgia' trên Windows THIẾU glyph precomposed cho đúng nhóm ký tự "dấu đôi" này (nhóm khó nhất
 * của Unicode Latin Extended Additional, U+1EA0–U+1EF9) — dù CÓ hỗ trợ dấu đơn (ả/ệ/ỗ trong "Bảo Vệ
 * Mỗi" hiển thị đúng, không lỗi). Không còn tin tưởng được "system serif = an toàn tiếng Việt" (giả
 * định sai ở CHG-015) — đổi sang web font TẢI THẬT (glyph nhúng sẵn trong file font, không phụ thuộc
 * OS/trình duyệt thay thế) để đảm bảo render giống nhau ở MỌI máy. 'Noto Serif' (Google) được chọn
 * vì được thiết kế RÕ RÀNG để phủ đủ toàn bộ khối Unicode đa ngôn ngữ (bản thân tên "Noto" = "No
 * tofu" — không thiếu glyph), xác nhận qua Google Fonts API (`fonts.googleapis.com/css?
 * family=Noto+Serif&subset=vietnamese` trả về @font-face hợp lệ, không lỗi subset). Dùng `@import`
 * (không phải Google Fonts CDN/npm package — không phải "dependency mới" theo nghĩa thư viện/build
 * tool, chỉ là 1 file .css/.woff2 tải qua URL, giống hệt cách hầu hết website tiếng Việt xử lý vấn
 * đề này) để giữ đúng scope "chỉ sửa style.css + block-hero.php". Fallback cuối cùng nếu Google
 * Fonts không tải được: `var(--font-family-base)` (Helvetica Neue) — ĐÃ XÁC NHẬN render tiếng Việt
 * đúng ở mọi nơi khác trên site, KHÔNG fallback về Georgia/Times New Roman (đã biết lỗi). */
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif:ital,wght@0,400;0,600;0,700&display=swap');

/* CHG-032 (2026-08-26, Sprint UI-12 — Best Sellers Typography, Project Owner decision D-FIGMA-003):
 * "HEADINGS/TITLES: Playfair" + "BODY/CONTENT: Be Vietnam Pro" là 2 role typography CHÍNH THỨC mới
 * cho phần triển khai Best Sellers (không áp dụng site-wide trong đợt này — Hero/Sale/New Products
 * VẪN dùng 'Noto Serif' như cũ, KHÔNG đổi, đúng phạm vi POC chỉ Best Sellers).
 * - "Playfair" → 'Playfair Display' (tên family thật trên Google Fonts, "Playfair" không phải tên
 *   family hợp lệ — suy luận hợp lý duy nhất khi Product Owner ghi "Playfair").
 * - Cả 2 font ĐÃ XÁC MINH có subset "vietnamese" hợp lệ trên Google Fonts CSS2 API (cùng tiêu chuẩn
 *   kiểm chứng đã áp dụng cho 'Noto Serif' ở CHG-016 — tránh lặp lại lỗi dấu đôi tiếng Việt tách rời
 *   đã từng gặp với Georgia) — `curl` trực tiếp API với User-Agent Chrome trả về block `/* vietnamese
 *   */` cho cả Playfair Display và Be Vietnam Pro.
 * - Be Vietnam Pro tải 4 weight (400/500/600/700) đủ dùng cho toàn bộ text trong Best Sellers (eyebrow
 *   semibold, tab-filter, product meta, giá). Playfair Display tải 3 weight (400/600/700) — Best
 *   Sellers heading hiện dùng weight 400 (regular, kế thừa từ CHG-023), giữ nguyên. */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600;700&family=Be+Vietnam+Pro:wght@400;500;600;700&display=swap');

/* ---------- Reset & base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
    margin: 0;
    font-family: var(--font-family-base);
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
    color: var(--color-text);
    background: var(--color-bg);
}
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; margin: 0; padding: 0; }
h1, h2, h3, p { margin: 0; }
button { font: inherit; cursor: pointer; }
input { font: inherit; }
.screen-reader-text {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(1px, 1px, 1px, 1px); white-space: nowrap;
}
/* Sprint 06 RC1 Validation (2026-08-05): bug thật — Skip Link tồn tại trong DOM (header.php) nhưng
   không có biến thể nào hiện `.screen-reader-text` ra khi được focus, vô dụng với người dùng bàn
   phím có mắt. Chỉ khớp phần tử THẬT SỰ nhận được focus (VD skip link) — không ảnh hưởng các
   `.screen-reader-text` không focusable khác (nhãn ô tìm kiếm...). */
.screen-reader-text:focus {
    position: fixed; top: var(--space-2); left: var(--space-2); z-index: 9999;
    width: auto; height: auto; overflow: visible; clip: auto; white-space: normal;
    padding: var(--space-2) var(--space-4); background: var(--color-brand-primary);
    color: var(--color-text-on-dark); border-radius: var(--radius-base);
}

/* Sprint 04 Product UX Polish (2026-08-05): bug thật phát hiện qua browser — không component nào
   của theme (.btn/.site-nav/.product-card/.tab-filter/.header-action...) có rule :focus/:focus-
   visible riêng (đã quét toàn bộ document.styleSheets để xác nhận), nghĩa là focus ring hoàn toàn
   phụ thuộc mặc định trình duyệt, chưa từng được verify hiển thị rõ trên nền tối hay bị cắt bởi
   border-radius: 9999px của nút pill — vi phạm WCAG 2.4.7. Dùng :focus-visible (không phải :focus)
   để CHỈ hiện khi điều hướng bàn phím, không đổi hành vi click chuột hiện tại. */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, [tabindex]:focus-visible {
    outline: 2px solid var(--color-brand-accent);
    outline-offset: 2px;
}

.container {
    max-width: 1320px;
    margin-inline: auto;
    padding-inline: var(--space-6);
}

.btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-6);
    border-radius: var(--radius-full);
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-sm);
    border: 1px solid transparent;
    transition: opacity var(--duration-base) var(--easing-standard);
}
.btn:hover { opacity: 0.85; }
.btn--dark { background: var(--color-brand-primary); color: var(--color-text-on-dark); }
.btn--gold { background: var(--color-brand-accent); color: var(--color-brand-primary); }
.btn--outline-light { background: transparent; color: var(--color-text-on-dark); border-color: var(--color-border-dark); }
/* PD-05 (2026-09-09, Product Purchase CTA) — variant MỚI cho nút "Mua ngay" (Product Detail) — đo
   pixel THẬT trên `design/23-8/chi tiet san pham/Chi-tiet-San-pham.jpg`: viền + chữ #D5AF34/#D6AF36
   ≈ ĐÚNG `--color-brand-accent` (#d6b035), nền trong suốt (trùng nền trang, không phải khối màu).
   Cùng hình dạng với `.btn--outline-light` có sẵn (chỉ đổi 2 giá trị `color`/`border-color` sang
   gold) — mở rộng ĐÚNG hệ thống variant `.btn--*` đã có, không phát minh Component mới. */
.btn--outline-gold { background: transparent; color: var(--color-brand-accent); border-color: var(--color-brand-accent); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* Generic Input (Sprint Shopping Experience Foundation) — Generic hoá từ rule trước đây chỉ scoped
   riêng Product Detail (`.thekoco-product-action__form .quantity .qty`) thành 2 rule dùng chung
   không gắn trang cụ thể — áp dụng tự động cho MỌI `.quantity`/`.qty` của WooCommerce (Product
   Detail/Cart Page/Mini Cart) mà không cần sửa lại Product Detail. `.thekoco-input` dùng cho text
   field thường (VD Coupon) — cùng ngôn ngữ hình với `.qty` (viền + `--radius-base`). */
.quantity { display: inline-flex; }
.quantity .qty {
    width: 64px; height: 48px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-base);
    text-align: center;
    font-size: var(--font-size-base);
}
.thekoco-input {
    height: 48px;
    padding-inline: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-base);
    background: var(--color-bg);
    color: var(--color-text);
    font-size: var(--font-size-sm);
}

.section-eyebrow {
    text-align: center;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: var(--font-size-xs);
    color: var(--color-brand-accent);
    margin-bottom: var(--space-2);
}
.section-heading {
    text-align: center;
    font-size: var(--font-size-2xl);
    color: var(--color-brand-accent);
    font-weight: var(--font-weight-bold);
    margin-bottom: var(--space-8);
}
.section-heading--center { color: var(--color-brand-accent); }

/* Sprint UI-05: Best Sellers là block duy nhất scope các override typography này — không đổi
   `.section-eyebrow`/`.section-heading` dùng chung để tránh ảnh hưởng Magazine/New Products.
   CHG-023 (2026-08-25, Round 2 — đo lại `design/23-8/trang chu/Desktop - 8.jpg` bằng PHP GD, xem
   docs/CHANGE_LOG.md § CHG-023): round trước (UI-05/CHG-022) SAI 2 điểm quan trọng, đã bị Product
   Owner chỉ ra qua screenshot thật:
   1) Eyebrow màu SAI — round trước dùng `color: var(--color-brand-accent)` kế thừa từ
      `.section-eyebrow` gốc (gold), nhưng đo trực tiếp pixel ảnh gốc tại dòng "ĐƯỢC YÊU THÍCH NHẤT"
      cho `rgb(183,179,167)` — xám ấm nhạt, GẦN `--color-text-on-dark-muted` (#a2a2a2), KHÔNG PHẢI
      gold. Override riêng `color` tại đây (base `.section-eyebrow` vẫn gold cho Magazine/New
      Products, không đổi).
   2) Title font-family/case SAI HẲN CHẤT — round trước kế thừa `.section-heading` gốc
      (`text-transform:uppercase`, sans-serif `--font-family-base` = Helvetica Neue đậm), nhìn như
      heading ecommerce phổ thông. Ảnh gốc dùng font SERIF thanh lịch, chữ THƯỜNG (chỉ "S" hoa —
      "Sản phẩm bán chạy", đúng y hệt chuỗi PHP gốc, không phải chuỗi viết hoa riêng). Đo bbox chữ
      vàng (glyph cao 54px kể cả dấu/đuôi chữ) khớp gần đúng `--font-size-4xl` (48px) hiện có —
      KHÔNG đổi font-size, chỉ đổi family/case/weight. Dùng 'Noto Serif' — ĐÃ có sẵn trong project
      (tải qua `@import` ở đầu file cho `.block-hero__title`, xem CHG-016), không thêm font mới; ảnh
      gốc dày vừa phải (không đậm như Hero) → dùng weight 400 (regular) thay vì bold. */
/* CHG-032 (D-FIGMA-003): toàn bộ text BODY/CONTENT trong Best Sellers (eyebrow/tab-filter/product
   meta/giá — mọi thứ không phải "title") kế thừa Be Vietnam Pro qua khai báo ở gốc `.block-best-
   sellers`, không cần lặp lại `font-family` ở từng selector con. `.section-heading`/`.product-
   card__title` (title role) override riêng bên dưới sang Playfair Display. */
.block-best-sellers { font-family: 'Be Vietnam Pro', var(--font-family-base); }

/* CHG-039 (Round 6 — Vertical Rhythm): đo TRỰC TIẾP từ Figma MCP `get_metadata` (Phase 1, các node
   text trong Homepage `1992:252`, EXACT — không phải ước lượng):
     eyebrow "Được yêu thích nhất": y=1227, height=38 → bottom=1265
     heading "Sản phẩm bán chạy": y=1281, height=38 → eyebrow→heading gap = 1281−1265 = 16px
     tab-filter "Frame 15": y=1376, height=38 → heading→tabs gap = 1376−1319 = 57px
     card container "Frame 36434": y=1470 → tabs→cards gap = 1470−1414 = 56px
   Trước Round này, `margin-bottom` của eyebrow/heading/tabs kế thừa token site-wide chung
   (--space-3=12px / --space-8=32px / --space-8=32px) — chưa từng đối chiếu với 3 số đo thật ở trên.
   Sai lệch LỚN NHẤT ở heading→tabs (32px kế thừa vs 57px đo được) và tabs→cards (32px vs 56px) — ĐÚNG
   nguyên nhân Product Owner mô tả "cards bắt đầu quá gần tabs, toàn Block bị nén dọc". Sửa bằng giá
   trị số đo trực tiếp (không dùng token gần đúng), scope riêng Best Sellers — không đổi
   `.section-eyebrow`/`.section-heading`/`.tab-filter` gốc dùng chung Magazine/New Products. */
/* CHG-045 (Round 9 — Eyebrow contrast, đúng Q-002 Product Owner nêu): đo lại pixel-thật (x=549,
   y=1227 — TOẠ ĐỘ ĐÚNG, sample trước đây ở Round 1/CHG-023 dùng x sai) cho `#b7b3a8` — TRÙNG KHỚP với
   giá trị brand/tab đã đo ở Round 8 (CHG-042/043), và trùng luôn ước lượng ban đầu của CHG-023
   ("rgb(183,179,167)"). Cả 3 phép đo độc lập hội tụ 1 giá trị — độ tin cậy cao. Rule Round 8 (CHG-042)
   trước đó VẪN dùng token cũ `--color-text-on-dark-muted` (#a2a2a2, tối/xỉn hơn rõ rệt) cho riêng
   Eyebrow — bị bỏ sót khi cập nhật Brand/Tab cùng đợt. Sửa nốt cho đồng bộ. */
.block-best-sellers .section-eyebrow {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.15em;
    margin-bottom: 16px;
    color: #b7b3a8;
}
.block-best-sellers .section-heading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: var(--font-size-4xl);
    font-weight: var(--font-weight-regular);
    letter-spacing: normal;
    line-height: var(--line-height-tight);
    margin-bottom: 57px;
}
.block-best-sellers .tab-filter { margin-bottom: 56px; }
.block-best-sellers .section-heading__dash { width: 40px; height: 2px; background: var(--color-brand-accent); flex-shrink: 0; font-size: 0; }

/* CHG-032 (D-FIGMA-002 + D-FIGMA-003): 2 quyết định Product Owner áp dụng riêng cho Product Card
   BÊN TRONG Best Sellers — KHÔNG sửa `.product-card__title`/`.product-card__cat` gốc (dùng chung
   Sale/New Products/Shop/Search/Related, ngoài phạm vi POC này).
   1) Tên sản phẩm = vai trò "Title" theo D-FIGMA-003 → Playfair Display (cùng họ heading, khác
      Noto Serif đang dùng cho `.block-sale__products .product-card__title` — 2 block khác nhau,
      không cần đồng nhất). Xem HUMAN_CONFIRMATION_QUEUE.md Q-011 (liệu tên sản phẩm có tính là
      "Title" hay nên giữ Be Vietnam Pro như "Body" — chọn Playfair vì tên sản phẩm là phần tử nổi
      bật nhất, có vai trò tương tự heading, trong mỗi card).
   2) Category text (`.product-card__cat`, VD "Chăm sóc da") kế thừa `--color-text-muted` (#6b6b6b)
      từ rule gốc — trên nền tối Best Sellers (`--color-brand-primary` #1f232e) tạo độ tương phản
      thấp (~3:1, cận biên AA). D-FIGMA-002 cho phép chọn 1 trong 2 giá trị "Neutral/3" xung đột của
      Figma (`#B8BBC2` hoặc `#A2A1A5`) theo đánh giá thị giác — CHỌN `#A2A1A5` vì nó GẦN TRÙNG giá trị
      `--color-text-on-dark-muted` (#a2a2a2) đã được đo pixel-thật từ chính ảnh gốc cho eyebrow cùng
      Block này (CHG-023) — tức là 2 nguồn độc lập (đo pixel thật + Variable Figma do Product Owner
      chọn) hội tụ về cùng 1 tông màu, cho độ tin cậy cao hơn hẳn `#B8BBC2` (không khớp bất kỳ giá trị
      đã đo nào). KHÔNG khai báo đây là giá trị Design System chính thức — chỉ áp dụng cục bộ trong
      Best Sellers, ghi rõ trong CHANGE_LOG.md. */
/* CHG-042 (Round 8): đo pixel title "Collagen Face Mask (5pcs)" (card 1) — glyph bounding box (kể cả
   dấu ngoặc/ký tự có đuôi) cao ~18px trên nền, tương ứng font-size ước tính ~20-22px cho font serif —
   RÕ RÀNG lớn hơn hẳn 16px hiện tại (kế thừa rule gốc dùng chung). Dùng `--font-size-xl` (22px, token
   có sẵn) thay vì bịa số mới — nằm trong khoảng đo được, tái dùng token theo đúng nguyên tắc file này
   đã áp dụng ở nơi khác (VD Radius/Spacing) thay vì hardcode. */
/* CHG-057 (2026-08-27, Final Micro-Fix — Product Title Font Size, xem docs/CHANGE_LOG.md): đo lại
   computed font-size thật trước khi sửa (`getComputedStyle`) = 22px (`var(--font-size-xl)`), không
   phải 20px như mô tả trong yêu cầu — chỉnh đúng theo TARGET yêu cầu (18px) bất kể số "trước" nêu
   trong yêu cầu, ghi rõ số đo thật trong Change Log. 18px khớp CHÍNH XÁC token có sẵn
   `--font-size-lg` — tái dùng, không hardcode `18px` thô. */
.block-best-sellers .product-card__title { font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base); font-size: var(--font-size-lg); line-height: 1.25; }

/* CHG-032 Round 2 (2026-08-26 — self-correction sau khi zoom lại `Desktop - 8.jpg` tại đúng vùng
   text "Dưỡng da"/"VEON", x=375-460,y=1885-1905, PHP GD lọc pixel "ấm" R>90 và R>B+20 để tách glyph
   khỏi nền): màu ĐO ĐƯỢC hội tụ quanh #716847–#7b6f49 (vàng-nâu ám, trung bình ~#786d49) — RÕ RÀNG
   KHÔNG PHẢI xám trung tính (#A2A1A5/#B8BBC2, cả 2 giá trị Neutral/3 xung đột của Figma). Round 1
   trước đó gán #A2A1A5 dựa trên suy luận "category = Neutral/3 role" mà CHƯA zoom đủ gần để thấy rõ
   tông màu thật — SAI, đã tự sửa ở đây theo evidence mới. Cùng tông với `#766740` đo được độc lập
   cho Category trong Flash Sale (CHG-031, block-sale.php khác — không dùng chung selector) — 2 phép
   đo độc lập cho 2 block khác nhau hội tụ cùng 1 gam màu, củng cố độ tin cậy. Dùng hex trực tiếp
   (không suy ra bằng opacity đè lên `--color-brand-accent`, vì phép thử blend-alpha không khớp kênh
   Blue đo được — xem Change Log § CHG-032 Round 2 Evidence) — cùng cách file này đã làm cho
   `--card-badge-sale-bg` (1 hex cục bộ riêng, không đẩy lên tokens.css). D-FIGMA-002 (Neutral/3) do
   đó KHÔNG áp dụng cho phần tử này — xem CHANGE_LOG.md cho việc Neutral/3 áp dụng ở đâu thay vào đó. */
/* CHG-042 (Round 8): category "Mặt nạ" đo glyph height ~11px → font-size ước tính ~14-15px, nhỉnh
   hơn 13px hiện tại một chút — vẫn PHẢI nhỏ hơn Title (đúng hierarchy "supporting information" theo
   yêu cầu §6 Task Round 8), không tăng bằng Brand/Rating. */
.block-best-sellers .product-card__cat { color: #786d49; font-size: 14px; }

/* CHG-032 Round 2: 3 phát hiện thêm từ zoom pixel-thật (crop x=355-685,y=1470-1930 và x=696,y=1650,
   xem docs/ui-qa/best-sellers/BEST_SELLERS_DESIGN_SPEC.md § Evidence):
   1) Card có border 1px màu #2d3146 (khớp CHÍNH XÁC giá trị đọc được qua Figma MCP `get_design_context`
      trên node 1992:341 ở Phase 1 — 2 nguồn độc lập, đo pixel + MCP, trùng khớp) — hiện `.product-
      card--dark` KHÔNG có border nào.
   2) Radius đo/MCP = 6px, không phải 8px (`--radius-base` đang dùng) — override cục bộ, KHÔNG đổi
      token `--radius-base` (ảnh hưởng toàn site).
   3) Card thực chất 2 tông: vùng ảnh nền #141821 (khớp `--color-footer-lower-bg` đang dùng, không
      đổi), vùng text bên dưới nền SÁNG HƠN #2c3343 riêng biệt (đo pixel rõ ràng, ranh giới là 1 đường
      kẻ mảnh) — hiện `.product-card--dark .product-card__body` không có background riêng, toàn Card
      chỉ 1 tông. Dùng margin âm để "tràn" panel body ra sát mép ngoài Card (undo padding cha CHỈ cho
      phần tử này), bo lại đúng 2 góc dưới bằng radius mới. `overflow:hidden` trên Card cha để bo góc
      panel body không tràn ra ngoài khối bo tròn ngoài cùng. */
/* CHG-034 (Round 4): `.product-card` render với `box-sizing: content-box` thật sự tại runtime (xác
   nhận qua `getComputedStyle` — không phải do global reset dòng 43 `*{box-sizing:border-box}` sai,
   mà do 1 rule khác đặc hiệu hơn ghi đè, chưa xác định chính xác nguồn — nghi WooCommerce core CSS
   vì `.product-card` nằm trong khu vực `.thekoco-woocommerce`/theme loop, nhưng KHÔNG đủ thời gian
   trong phạm vi Round này để truy hết nguồn — xem Open Question trong Report). Hệ quả: `padding`
   (32px) + `border` (2px, mới thêm CHG-032) bị CỘNG THÊM vào ngoài `flex-basis` đã khai báo thay vì
   nằm gọn TRONG nó — đây là NGUYÊN NHÂN THẬT (không phải flex-basis % trong container overflow như
   suy đoán ban đầu) khiến Card render RỘNG HƠN 34px so với công thức, và là lý do chính khiến 4 Card
   tại 1920px tràn ra ngoài `.product-grid` (đo trước khi sửa: card 487px, card4Right=2032px > cả
   viewport 1920px). Ép `border-box` cục bộ (chỉ trong Best Sellers) để `flex-basis`/`clamp()` ở dưới
   phản ánh ĐÚNG kích thước render cuối cùng. */
/* CHG-036 (2026-08-26, Round 5 — Equal Card Height): Product Owner phát hiện nền vùng thông tin
   (`.product-card__body`, #2c3343) KẾT THÚC Ở ĐỘ CAO KHÁC NHAU giữa các Card cùng hàng (title 1 dòng
   vs 2 dòng → body content cao khác nhau). Root cause: `.product-grid` (flex row) mặc định
   `align-items:stretch` NÊN CHÍNH `.product-card--dark` (outer box) đã cao bằng Card cao nhất cùng
   hàng — nhưng `.product-card--dark` trước đây là `display:block` thường, nên phần CHIỀU CAO DƯ RA
   đó không "chảy" xuống cho `.product-card__body` (chỉ cao bằng NỘI DUNG thật của nó) — khoảng dư
   nằm Ở NGOÀI `__body`, hiện màu nền CARD (không phải màu nền BODY), tạo cảm giác mỗi Card có "đường
   kết thúc nền thông tin" ở độ cao khác nhau dù bản thân Card đã cao bằng nhau.
   Sửa bằng CSS layout chuẩn (flex + stretch), KHÔNG hardcode height riêng, KHÔNG margin giả, KHÔNG
   background giả, KHÔNG position:absolute: biến `.product-card--dark` thành flex column (Thumb ở
   trên, Body ở dưới) rồi cho Body `flex:1 1 auto` — Body giờ LUÔN lấp hết phần chiều cao còn lại của
   Card (đã được stretch bởi hàng Grid cha), nền `#2c3343` của nó vì vậy LUÔN phủ kín tới đúng mép
   dưới Card ở MỌI Card cùng hàng, bất kể nội dung dài ngắn khác nhau. */
/* CHG-049 (2026-08-26, Round 13 — Final Polish P2 v4, Real Box Geometry): Round 12 (CHG-048) dùng
   `linear-gradient` để vẽ 1 dải màu Info giả ở đáy Thumb — Product Owner từ chối: đó là "vẽ hiệu ứng
   giống bo góc", không phải "bo góc thật của box". Root cause thật (đã inspect lại từ đầu):
   - `.product-card--dark` (Card cha): nền `var(--color-footer-lower-bg)` (màu Media), phủ TOÀN BỘ
     Card (cả 2 vùng), padding đều 4 cạnh.
   - `.product-card__thumb` (Media): `background:transparent` — không có nền THẬT của riêng nó, chỉ
     "mượn" màu Card cha lộ qua. Đây là lý do border-radius đặt trực tiếp lên Thumb luôn VÔ HÌNH (bo
     1 vùng trong suốt thì không có gì để mắt thấy) — mọi round trước đều phải "vẽ thêm" 1 màu giả
     (pseudo-element hoặc gradient) để bù cho lỗ hổng này.
   - `.product-card__body` (Info): nền THẬT `#2c3343` riêng, đã tự "thoát" ra sát mép ngoài Card
     (trái/phải/dưới) bằng margin âm — nhưng chỉ 3 cạnh đó, không chạm cạnh TRÊN (giáp Thumb).
   GIẢI PHÁP THẬT (không gradient, không pseudo-element, không notch — đổi quan hệ NỀN giữa Card cha
   và 2 con, đúng như yêu cầu):
   1) Đổi nền CHÍNH `.product-card--dark` (Card cha) từ màu Media sang thẳng màu Info `#2c3343` —
      an toàn vì bước 2+3 dưới đây khiến Thumb + Body cùng nhau phủ kín 100% diện tích nhìn thấy
      được, TRỪ đúng 2 góc bo — nền Card cha lúc này chỉ còn "lộ" ra đúng tại đó, không ở đâu khác.
   2) Cho `.product-card__thumb` 1 nền THẬT của chính nó (`var(--color-footer-lower-bg)`, đúng màu
      Media đang hiển thị, không đổi cảm nhận) + margin âm để tự thoát ra sát mép TRÊN/TRÁI/PHẢI
      Card (đối xứng với cách Body đã thoát mép DƯỚI/TRÁI/PHẢI) + padding bù lại đúng bằng margin âm
      để ảnh/badge/wishlist bên trong (position:absolute, neo theo padding-box của Thumb) đứng ĐÚNG
      Y HỆT vị trí cũ, không lệch dù 1px (đã tính toán: margin -16px + padding +16px = net 0 dịch
      chuyển cho nội dung bên trong, chỉ border-box của Thumb tự nó mở rộng ra biên).
   3) `border-radius: 0 0 6px 6px` áp trực tiếp lên Thumb (chỉ 2 góc dưới) — vì giờ Thumb có nền THẬT
      + đã lan ra sát biên Card, khi bo góc, phần bị cắt sẽ lộ ĐÚNG nền Card cha phía sau — nay là màu
      Info `#2c3343` — bo góc THẬT, màu lộ ra THẬT, không giả lập gì cả.
   `.product-card__body` HOÀN TOÀN không đổi 1 dòng nào — vị trí/kích thước/nền giữ nguyên tuyệt đối. */
.block-best-sellers .product-card--dark {
    box-sizing: border-box;
    border: 1px solid #2d3146;
    border-radius: 6px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    background: #2c3343;
}
.block-best-sellers .product-card--dark .product-card__thumb {
    flex: 0 0 auto;
    background: var(--color-footer-lower-bg);
    margin: calc(var(--space-4) * -1) calc(var(--space-4) * -1) 0;
    padding: var(--space-4) var(--space-4) 0;
    border-radius: 0 0 6px 6px;
}

/* CHG-041 (2026-08-26, Round 8 — P0-2 Metadata Structure): Product Owner đối chiếu Figma cho thấy
   Rating (sao + số review) phải nằm CÙNG HÀNG với Brand, canh phải — "L'Occitane [[space]] ★★★★★ 121"
   — xác nhận bằng zoom pixel-thật trên `Desktop - 8.jpg` (crop card 1, x=40-330,y=1793-1811, xem
   Report Round 8 § Evidence). Implementation trước Round này render Rating là 1 `<p>` RIÊNG, đứng
   SAU Category (Brand→Title→Category→Rating→Price) — sai thứ tự Figma (Brand→[Rating cùng hàng]→
   Title→Category→Price).
   KHÔNG sửa PHP/HTML (`product-card.php` — component DÙNG CHUNG Best Sellers/Sale/New Products/Shop/
   Search/Related, đổi DOM order ở đây sẽ kéo theo thay đổi hiển thị cho MỌI nơi khác, vi phạm "chỉ
   sửa Best Sellers"). Thay vào đó dùng CSS GRID với `grid-template-areas` đặt lại VỊ TRÍ HIỂN THỊ của
   `.product-card__brand`/`.product-card__rating` mà KHÔNG cần đổi thứ tự trong DOM — Grid area cho
   phép 2 phần tử không liền kề trong HTML vẫn hiển thị cùng 1 hàng. CHỈ áp dụng cho `.block-best-
   sellers` (scoped) — mọi Block khác (Sale/New Products/Shop) vẫn giữ nguyên `display:flex;flex-
   direction:column` cũ, Rating vẫn nằm dưới Category y hệt trước Round này, KHÔNG regression.
   Hàng trống `". ."` giữa Category và Price là 1 ROW co giãn (`1fr`) hấp thụ hết khoảng trống còn dư
   của Body (đã được stretch cao bằng Card cao nhất cùng hàng, cơ chế CHG-036/Round 5 — GIỮ NGUYÊN,
   chỉ đổi `display:flex`→`display:grid` cho Body, phần `flex:1 1 auto` khiến Body co giãn ĐÚNG BẰNG
   trong `.product-card--dark` cha vẫn giữ nguyên không đổi) — thay thế đúng vai trò `margin-top:auto`
   cũ từng dùng để neo Price xuống đáy (xoá rule đó, không cần nữa vì Grid tự làm việc này). */
/* CHG-047 (2026-08-26, Round 11 — Final Polish P2 v2, Ownership Correction): Round 10 (CHG-046) đặt
   `border-radius: 6px 6px 0 0` trực tiếp trên `.product-card__body` — Product Owner review lại, xác
   nhận kết quả VỀ MẶT HÌNH ẢNH đúng nhưng SAI VỀ MẶT SỞ HỮU: theo cấu trúc Figma, phần bo góc PHẢI
   thuộc về PRODUCT MEDIA (khối ảnh + badge + wishlist phía trên), KHÔNG PHẢI PRODUCT INFO (khối text
   phía dưới) — info panel không được có border-radius riêng dưới bất kỳ hình thức nào. Đã BỎ hẳn
   `border-radius` khỏi rule này (xem thay thế: `.product-card__thumb::after`, dòng ~300) — Body giờ
   HOÀN TOÀN vuông ở mọi góc (đúng yêu cầu), hiệu ứng bo tròn tại điểm tiếp giáp được vẽ bởi 1 pseudo-
   element gắn vào Thumb thay thế, xem chi tiết + lý do kỹ thuật tại đó. Margin/padding/flex GIỮ
   NGUYÊN không đổi (không ảnh hưởng kích thước/khoảng cách Card). */
.block-best-sellers .product-card--dark .product-card__body {
    background: #2c3343;
    margin: 0 calc(var(--space-4) * -1) calc(var(--space-4) * -1);
    padding: var(--space-5) var(--space-4) var(--space-4);
    flex: 1 1 auto;
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto auto 1fr auto;
    column-gap: var(--space-3);
    grid-template-areas:
        "brand rating"
        "title title"
        "cat   cat"
        "spacer spacer"
        "price price";
}
/* CHG-042 (Round 8 — P0-1 Typography): đo pixel trực tiếp text "EARTHNIQ"/"VEON" (brand, card 1-2,
   `Desktop - 8.jpg`) — glyph height ~11px (chữ hoa) → font-size ước tính ~15px (hiện tại 13px, kế
   thừa rule gốc dùng chung — SỬA riêng cho Best Sellers). Màu đo được hội tụ #b7b3a8 — ẤM và SÁNG
   HƠN rõ rệt `--color-text-on-dark-muted` (#a2a2a2) đang dùng — cùng tông với chữ Tab (xem CHG-043).
   Chữ trong ảnh gốc KHÔNG nghiêng (rule gốc `.product-card__brand` có `font-style:italic` — SAI so
   Figma) và VIẾT HOA + letter-spacing rõ (brand lưu trong DB dạng "L'Occitane"/"Earthniq" — thường,
   không hoa — Figma áp `text-transform:uppercase` khi hiển thị, không phải do nhập hoa sẵn). */
.block-best-sellers .product-card__brand {
    grid-area: brand; align-self: center; margin-bottom: 0;
    font-size: 15px; font-style: normal; text-transform: uppercase; letter-spacing: 0.04em;
    color: #b7b3a8;
}
.block-best-sellers .product-card__rating {
    grid-area: rating; justify-self: end; align-self: center; margin-bottom: 0;
}
.block-best-sellers .product-card__rating-stars { font-size: 14px; }
.block-best-sellers .product-card__rating-count { font-size: 13px; }
.block-best-sellers .product-card__title { grid-area: title; }
.block-best-sellers .product-card__cat { grid-area: cat; }
.block-best-sellers .product-card__price { grid-area: price; }
/* CHG-042 (Round 8): giá "450.000đ" đo glyph height ~14px (số, không dấu đuôi) → font-size ước tính
   ~19-20px — Price là focal point mạnh nhất theo yêu cầu §6 Task Round 8, dùng 20px (đậm hơn hẳn
   16px hiện tại, cũng lớn hơn Title 22px một chút để 2 giá trị không trùng nhau nhưng vẫn đủ nổi bật
   theo đúng tinh thần "giá là 1 trong những focal point mạnh nhất"). Giá cũ/discount tăng theo tỉ lệ
   tương ứng (13→15px), vẫn nhỏ hơn giá mới — giữ đúng hierarchy giá mới > giá cũ/badge giảm giá. */
/* CHG-044 (Round 8 — Price Order + Redundant Discount Text): zoom pixel-thật card "VEON" (on-sale
   thật trong Figma, xem Report Round 9 § Evidence) cho thấy 2 điểm khác implementation trước Round
   này:
   1) Thứ tự hiển thị: Giá MỚI (vàng, nổi bật) đứng TRƯỚC, Giá CŨ (gạch ngang, mờ) đứng SAU — DOM
      hiện tại render `price-old` trước `price-new` (comment doc-block cũ ở đầu file "Giá cũ → Giá
      mới" xác nhận đúng thứ tự DOM này), nên với `.product-card__price{display:flex}` (base, không
      đổi) thứ tự HIỂN THỊ trước Round này là Cũ→Mới, ngược Figma. Sửa bằng flex `order` (không đổi
      DOM/PHP, không ảnh hưởng Block khác dùng chung).
   2) `.product-card__price-discount` ("-44%") KHÔNG xuất hiện trong hàng Giá của Figma — % giảm giá
      CHỈ hiện dưới dạng Badge overlay trên ảnh (đã có sẵn từ CHG-022, và nay có thêm CHG-044 badge-
      pair cho trường hợp cần cả 2 badge cùng lúc — xem `.product-card__badges` ở trên). Hiện `-44%`
      LẶP LẠI thêm 1 lần trong hàng Giá là dư thừa so Figma — ẩn (không xoá `<span>` khỏi PHP, vẫn
      dùng chung Sale/New Products nếu có nhu cầu riêng — chỉ ẩn hiển thị trong scope Best Sellers). */
.block-best-sellers .product-card__price-new { font-size: 20px; order: 1; }
.block-best-sellers .product-card__price-old { font-size: 15px; order: 2; }
.block-best-sellers .product-card__price-discount { display: none; }

/* CHG-032 Round 2: nút "Thêm vào giỏ hàng" hiện dùng `--color-bg-subtle` (#f7f5f2, kem gần trắng) —
   đo pixel thật (x=500,y=1745) cho `#d6d5d0`/`#d5d4cf`, khớp gần như tuyệt đối `Accent/3` Figma
   (#D6D5CF, chênh 1 đơn vị/kênh — sai số nén JPEG) — RÕ RÀNG tối/xám hơn `--color-bg-subtle` đáng kể,
   không phải cùng 1 màu. Hex cục bộ (không token nào khớp sẵn, cùng cách xử lý `--card-badge-sale-
   bg` phía trên). */
.block-best-sellers .product-card__add-cart { background: #d6d5cf; }

/* CHG-033 (2026-08-26, Round 3 — Pilot Controlled Implementation): TỰ SỬA LẠI quyết định CHG-032
   Round 2 ở trên (đã xoá) sau khi Product Owner chỉ ra qua visual comparison: ảnh sản phẩm đang
   RENDER QUÁ LỚN, "như 1 khối ảnh vuông độc lập" tách rời khỏi Card, không giống Figma.
   Zoom pixel-thật vào ĐÚNG 1 ô ảnh (crop x=365-671,y=1470-1780 trên `Desktop - 8.jpg`, xem
   docs/ui-qa/best-sellers/REPORT_HOMEPAGE_BEST_SELLERS_2026-08-26.md § Evidence) đo được: ảnh sản
   phẩm thật (phần lọ kem nhìn thấy được, không tính vùng trong suốt xung quanh trong chính file ảnh)
   chỉ chiếm CHIỀU RỘNG ~53% và CHIỀU CAO ~46% của khối ảnh vuông (306×310px), canh giữa trong dải
   trống giữa hàng Badge (trên) và nút CTA (dưới) — HOÀN TOÀN KHÁC quy tắc `90%` cũ (CHG-022, đo cho
   `.product-card--dark .product-card__thumb img` dùng chung — xem dòng ~647).
   → Root cause thật của "card nhìn thô/ảnh như khối vuông riêng": KHÔNG PHẢI do thiếu 1 lớp nền
   trung gian (Round 2 SAI khi thêm `background:var(--color-bg-subtle)` ở đây — cách đó chỉ che màu
   trắng ảnh gắt hơn nhưng KHÔNG giải quyết vấn đề gốc là ảnh quá lớn, thậm chí còn tạo ra đúng 1
   "khối vuông sáng độc lập" mà Product Owner đang phàn nàn, vì hộp nền đó gần như phủ kín Card).
   Root cause THẬT là % kích thước ảnh sai — sửa bằng cách trả `background` về `transparent` (kế
   thừa lại rule gốc dòng ~720, không cần khai báo lại) VÀ giảm % ảnh xuống đúng tỉ lệ đo được, xem
   rule ngay dưới. Ảnh nền trắng đặc thật (CHG-031) vẫn còn (Product Owner cấm tự xoá nền ảnh), nhưng
   ở kích thước nhỏ hơn nhiều, mảng trắng dư ra không còn chiếm phần lớn Card như trước — chấp nhận
   được, không phải giải pháp hoàn hảo (xem Q-001 report). */
/* CHG-037 (2026-08-26, Round 5 — Image Prominence Re-eval): 55% (CHG-033, Round 3) được đo/chốt
   TRÊN Card đã bị lỗi box-sizing (render ~348-487px, RỘNG HƠN THẬT khoảng 34px/cạnh vì content-box
   — xem CHG-034) — cùng 1 tỉ lệ % đó, sau khi Round 4 sửa box-sizing về đúng kích thước Card thật
   (~282–323px, xem CHG-035), cho ảnh NHỎ HƠN HẲN về số pixel tuyệt đối dù % không đổi — đúng phản ánh
   Product Owner: "ảnh trở nên quá nhỏ, sản phẩm nhìn lọt thỏm". KHÔNG PHẢI do Round 3 đo sai — mà do
   base để tính % (kích thước Card) đã đổi hẳn ở Round 4/5, nên % cũ không còn phù hợp, cần đánh giá
   lại trên chính composition MỚI. Tăng lên 70% — ảnh trở thành focal point rõ ràng hơn (yêu cầu Round
   5 §2), vẫn CHỪA khoảng thở quanh ảnh (không chạm sát border như 90% cũ của CHG-022) — 70% là điểm
   cân bằng chọn qua so sánh trực tiếp nhiều mốc (55/62/70/78%) bằng screenshot thật, KHÔNG có số đo
   Figma MCP mới do rate limit vẫn còn hiệu lực (xem Report Round 5 § Open Question — con số này cần
   Designer xác nhận lại khi MCP khả dụng). `object-fit:contain` (kế thừa rule gốc, không đổi) đã đảm
   bảo giữ đúng aspect ratio ảnh gốc, không crop, không méo — chỉ đổi % khung chứa, không đổi cách fit. */
/* CHG-046 (Round 9 — Image Frame Radius): Product Owner chỉ ra ảnh sản phẩm thật (nền trắng đặc sẵn
   trong file, giới hạn asset đã ghi nhận từ CHG-031/Round 6 §5, KHÔNG được tự xoá nền) nhìn như "1
   hình chữ nhật trắng cứng đặt giữa khung ảnh" vì 4 góc vuông 90°. Figma KHÔNG thể xác nhận trực
   tiếp radius cho chính khối này — ảnh demo gốc trong Figma không có nền trắng nên hiện tượng này
   không tồn tại trong Figma để đo (khác Round khác, nơi luôn có evidence Figma trực tiếp — ghi rõ
   giới hạn này, không suy đoán bừa). Theo đúng yêu cầu "reuse token hiện có thay vì bịa số mới": tái
   dùng CHÍNH giá trị 6px đã dùng cho bo góc ngoài cùng của Card (`.product-card--dark`, MCP-exact từ
   Phase 1) — bo góc ảnh THẬT (`<img>`) cùng 1 mức với Card chứa nó, nhất quán nội bộ 1 component,
   không bịa 8px/12px/16px tuỳ ý. `overflow:hidden` không cần thiết trên chính thẻ `<img>` (radius áp
   trực tiếp lên box của nó, trình duyệt tự clip nội dung ảnh bên trong theo góc bo) — không crop/méo
   ảnh, không đổi `object-fit:contain`/tỉ lệ đang dùng, chỉ bo 4 góc của khung ảnh đã render. */
.block-best-sellers .product-card--dark .product-card__thumb img { width: 70%; height: 70%; border-radius: 6px; }

/* ---------- Header ---------- */
/* Sprint 04: đo trực tiếp pixel từ design/koco.jpg (topbar 40px, header 94px, nav 53px;
   gold #d6b035, topbar bg #20232c, header/nav bg #070707, search bg #464646).
   Redesign 2026-08-23: màu đổi sang bản Figma chốt mới (design/23-8/) — topbar bg #781035 (đỏ
   mận), header/nav bg #1f232e (navy đậm), search bg #393d46 — xem token trong tokens.css, không
   sửa giá trị màu ở file này (toàn bộ đã dùng var(), xem doc-comment tokens.css cho lý do đổi).

   Container width (UI Audit 2026-08-23): đo lệch trái ~13-15px ở CẢ 3 hàng (topbar/logo/nav) so
   với reference — không phải lỗi riêng từng phần tử, mà do `.container` DÙNG CHUNG toàn site
   (max-width 1320px + padding 24px) tạo margin ngoài ~23px ở viewport 1366px, trong khi reference
   gần như full-width (content bắt đầu ~35-46px từ mép, gần đúng bằng padding-inline không kèm
   margin ngoài). ĐÃ CÓ tiền lệ scope override `.container` riêng cho 1 section trong file này
   (`.block-best-sellers .container { max-width: none; }`, xem bên dưới) — áp dụng ĐÚNG pattern đó
   cho Header, không đổi `.container` gốc (tránh ảnh hưởng section khác ngoài phạm vi audit này).

   Round 2 (2026-08-23, CHG-001 — xem docs/CHANGE_LOG.md) — SỬA SAI của chính lần trước: đặt
   `max-width: 1600px` không có căn cứ đo từ reference (chỉ là "cap hợp lý cho màn hình cực rộng"
   tự nghĩ ra). Screenshot thật ở viewport 1920px xác nhận: 1600px < 1920px nên vẫn tạo margin
   ngoài ~160px mỗi bên + padding 32px = logo cách mép trái ~192-220px — ĐÚNG lỗi ban đầu, chỉ dịch
   sang breakpoint khác. Reference chỉ có 1 kích thước gốc (Figma xuất ở 1366px, không có bản thiết
   kế riêng cho 1920+), nhưng bản chất "gần full-width, logo sát mép trái" của nó KHÔNG phải đặc
   điểm riêng của 1366px — bỏ hẳn `max-width`, dùng padding-inline làm khoảng cách mép duy nhất, ở
   MỌI viewport (đúng tinh thần "full-width layout" nếu reference không cho thấy giới hạn cứng nào). */
.site-header .container { max-width: none; padding-inline: var(--space-8); }

.topbar { background: var(--color-topbar-bg); color: var(--color-text-on-dark); font-size: 13px; height: 40px; }
.topbar__inner { display: flex; justify-content: space-between; align-items: center; height: 40px; gap: var(--space-4); flex-wrap: wrap; }
.topbar__ship { margin: 0; color: var(--color-brand-accent); }
.topbar__links { display: flex; align-items: center; gap: var(--space-3); color: var(--color-text-on-dark); }
/* Sprint Framework Localization Phase 3: bọc riêng phần "Đăng ký" trong 1 wrapper để có thể ẩn trên
   mobile mà vẫn giữ Language Switcher hiển thị (xem @media 768px). CHG-050 (2026-08-26, Header
   Surgical Correction C): nội dung bên trong wrapper này đổi (chỉ còn "|" + "Đăng ký", bỏ "Ưu đãi
   thành viên" — xem header.php), nhưng class/rule này (kể cả rule ẩn mobile) GIỮ NGUYÊN không đổi.
   `.topbar__perk` xoá — không còn markup nào tham chiếu (link "Ưu đãi thành viên" đã bỏ). */
.topbar__links-primary { display: flex; align-items: center; gap: var(--space-3); }
.topbar__sep { color: var(--color-border-dark); }
.topbar__links a:hover { opacity: 0.8; }

/* ================= Language Switcher (Sprint Framework Localization Phase 3) =================
   CHG-050 (2026-08-26, Header Surgical Correction C): đổi từ dropdown (toggle+panel absolute) sang
   inline list phẳng "VN / EN" — khớp Figma reference (xem docs/ui-qa/HEADER_GAP_ANALYSIS_2026-08-26.md).
   Rule `__toggle`/`__panel`/`__current` cũ đã xoá cùng markup dropdown (component.php) — không còn
   consumer nào dùng, không phải dead code sót lại ngoài phạm vi. Màu/font-size KHÔNG khai báo riêng —
   kế thừa nguyên `.topbar`/`.topbar__links` (color: var(--color-text-on-dark), font-size: 13px) đúng
   yêu cầu "giữ nguyên typography Topbar". */
.thekoco-language-switcher { display: inline-flex; align-items: center; gap: var(--space-1); }
.thekoco-language-switcher__sep { color: var(--color-border-dark); }
.thekoco-language-switcher__option { color: inherit; }
.thekoco-language-switcher__option:hover { opacity: 0.8; }
.thekoco-language-switcher__option.is-active { font-weight: var(--font-weight-semibold); color: var(--color-brand-accent); }

.site-header__main { background: var(--color-brand-primary); }
/* Round 2 (2026-08-23, CHG-002 — xem docs/CHANGE_LOG.md): đổi Grid `1fr auto 1fr` (UI_TODO.md §
   Review lần 2 cũ) sang Flex. Lý do: Grid `1fr auto 1fr` ép 2 cột 2 bên BẰNG NHAU tuyệt đối theo
   định nghĩa, nên Search (cột giữa) luôn nằm ĐÚNG GIỮA container bất kể logo/icon rộng bao nhiêu —
   khớp yêu cầu "search luôn giữa" nhưng lại ép Search PHẢI có width cố định (khai báo cứng ở
   `.site-search`, không có cách nào cho nó "chiếm phần lớn không gian còn lại" trong 1 Grid ép đối
   xứng như vậy). Reference cho thấy Search phải RỘNG RÕ RỆT, ưu tiên hơn việc căn đúng-giữa-tuyệt-
   đối — đổi sang Flex (logo auto, search flex:1, icons auto) để Search tự hấp thụ toàn bộ khoảng
   trống còn lại sau khi trừ logo/icons, đúng bản chất "search chiếm phần lớn không gian trung tâm"
   thay vì bị giữ ở 1 width cố định. Đánh đổi: Search không còn nằm CHÍNH XÁC giữa hình học của
   container nếu logo/icons 2 bên rộng khác nhau — chấp nhận được vì đây đúng compostion nhìn thấy
   trong reference (không phải suy đoán). */
/* CHG-005 (2026-08-23, xem docs/CHANGE_LOG.md): `justify-content: space-between` với 3 phần tử
   ngang hàng (logo/search/actions) chia khoảng trống thừa làm 2 phần BẰNG NHAU theo đúng định
   nghĩa CSS — nhưng đo lại reference (border-scan chính xác, không ước lượng) cho thấy khoảng
   Logo↔Search (327px) và Search↔Icons (232px) KHÔNG bằng nhau, đây là nguyên nhân gốc khiến tâm
   ngang Search lệch ~50px so với reference dù width Search đã đúng (CHG-004). Gộp Search+Actions
   thành 1 nhóm con `.site-header__search-actions` (xem bên dưới + header.php) rồi đặt cạnh Logo:
   khoảng cách Logo↔nhóm co giãn hết phần trống còn lại (`flex: 1 1 auto` trên nhóm), khoảng cách
   Search↔Icons TRONG nhóm cố định nhỏ — tái tạo đúng tỷ lệ bất đối xứng bằng cấu trúc Flex thật.
   Còn 2 phần tử (logo, nhóm) nên bỏ `justify-content: space-between` — không còn tác dụng khi item
   thứ 2 đã tự `flex: 1` chiếm hết phần còn lại. `height: 103px → 95px`: đo lại reference (quét màu
   viền 700px cột giữa) main-row cao 95px (y=40→135 trong ảnh 1366×200), không phải 103px — root
   cause khiến tâm dọc Search lệch 4.5px (91.5 vs 87), vì `.site-search` căn giữa theo chiều cao
   hàng cha (`align-items:center`). */
/* CHG-007 (2026-08-23): tách Logo/Search/Icons thành 3 phần tử độc lập — Search dùng `margin-left`
   CỐ ĐỊNH (314.3px). Đúng tại 1366px nhưng SAI ở viewport rộng hơn: Logo/Icons đúng co giãn theo
   viewport (áp sát 2 mép, đo khớp reference) nhưng Search "đóng băng" ở toạ độ 1366px tuyệt đối, nên
   khi khoảng-giữa (Logo→Icons) giãn ra theo viewport, Search bị kẹt lại bên trái — đo thật ở 1920px:
   search.centerX=707px, viewport center=960px, lệch 253px (screenshot thật xác nhận, không phải suy
   đoán — xem docs/CHANGE_LOG.md CHG-008).

   CHG-008 (2026-08-23, xem docs/CHANGE_LOG.md): thay `margin-left` cố định bằng 2 SPACER co giãn
   theo TỶ LỆ đã đo được ở reference (314.3:227, tương đương 58.1%:41.9% — xem `.site-search`/
   `.site-header__actions` bên dưới cho giá trị `flex-grow` chính xác), dùng pseudo-element
   `::before`/`::after` của chính `.site-header__inner` (không cần thêm div/markup mới) kết hợp
   `order` để chèn đúng vị trí giữa Logo↔Search và Search↔Icons. Khi viewport = 1366px (reference),
   2 spacer render đúng bằng 314.3px/227px (khớp tuyệt đối số đo cũ, không đổi). Khi viewport RỘNG
   HƠN, phần dư ra được chia theo ĐÚNG tỷ lệ 314.3:227 thay vì dồn hết vào 1 bên (CHG-005/006) hay
   đứng yên tuyệt đối (CHG-007) — Search giờ trôi dần về giữa theo đúng tỷ lệ đã đo, không bịa số
   mới. Logo/Icons giữ nguyên cơ chế đã đúng (`flex:0 0 auto`), không đổi. */
.site-header__inner {
    display: flex;
    align-items: center;
    height: 95px;
}
.site-header__inner::before {
    content: "";
    order: 2;
    flex: 314.3 314.3 0px;
}
.site-header__inner::after {
    content: "";
    order: 4;
    flex: 227 227 0px;
}
.site-logo { display: flex; align-items: center; flex: 0 0 auto; order: 1; }
/* Sprint Framework Customer — Header Consistency: WooCommerce core (woocommerce-layout.css) có
   rule `.woocommerce img, .woocommerce-page img { height: auto; max-width: 100%; }` — specificity
   (0,1,1) — CAO HƠN rule cũ `.site-logo__mark { height: 80px }` (0,1,0), nên trên MỌI trang có
   body class `woocommerce`/`woocommerce-page` (Product Detail/Shop/Category/Brand/Cart/Checkout/
   Search) logo bị render theo kích thước gốc của file ảnh thay vì 80px — Homepage không có class
   này nên không bị. Qualify thêm `.site-logo` (class cha có sẵn, không phải class mới) để tăng
   specificity lên (0,2,0), thắng chắc chắn rule của WooCommerce trên mọi trang — không cần
   `!important`, không đụng header.php. */
.site-logo .site-logo__mark { height: 80px; width: auto; }

/* UI Audit 2026-08-23: đo trực tiếp design/23-8/trang chu/Desktop - 8.jpg — search box reference
   rộng ~591px (406px→997px ở viewport 1366px, đo bằng ruler-crop), bản cũ width cố định 560px.
   Round 2 (CHG-002): width cố định (kể cả 600px đã tăng) là NGUYÊN NHÂN GỐC khiến Search không
   giãn theo viewport — ở 1920px vẫn y hệt 600px như ở 1366px, nhìn "hụt" hẳn so với khoảng trống
   thật có. Đổi sang `flex: 1` (search hấp thụ hết khoảng trống còn lại trong `.site-header__inner`,
   xem rule đó) + `max-width: 780px` (chặn KHÔNG cho giãn vô hạn trên màn cực rộng — 780px lúc đó là
   ƯỚC LƯỢNG, không có bằng chứng đo, ghi rõ trong Change Log CHG-002).

   CHG-004 (2026-08-23) — đo LẠI reference bằng pixel-detection chính xác (quét màu viền search box
   qua 7 dòng y độc lập, không phải ruler-crop ước lượng bằng mắt): left=418px, right=994px →
   width=576px THẬT (khác con số ~591px ước lượng trước đó ở dòng comment trên — SAI SỐ ~15px/2.6%
   giữa 2 lần đo, giữ nguyên comment cũ để thấy rõ chênh lệch, không xoá). max-width 780px đo lại so
   với 576px thật: rộng hơn +204px (+35.4%, vượt xa ngưỡng ±5%) — SỬA. `max-width: 780px` →
   `max-width: 576px` (đúng bằng số đo, không làm tròn). Kiến trúc `flex: 1 1 auto` GIỮ NGUYÊN —
   vẫn co giãn được ở viewport khác, chỉ đổi giá trị CAP. */
/* CHG-005: `flex: 1 1 auto` (Search tự hấp thụ khoảng trống của `.site-header__inner`) đổi sang
   `flex: 0 1 576px` (max-width thật, đo được ở CHG-004) — không PHÌNH quá 576px nữa, chỉ CO khi
   thiếu chỗ. `height: 48px → 50px`: đo lại reference (quét viền tại cột giữa Search, x=706) chiều
   cao thật 50px (y=62→112), không phải 48px — Priority 2 CHG-005.
   CHG-007 (2026-08-23): bỏ nhóm `.site-header__search-actions` (CHG-005) — Search không còn nằm
   trong 1 flex-group chung với Icons nữa. Đổi sang `margin-left: 314.3px` CỐ ĐỊNH — SAU ĐÓ phát
   hiện SAI ở viewport rộng (xem `.site-header__inner` — search.centerX kẹt ở 707px bất kể viewport,
   lệch 253px so với tâm màn hình 1920px, ảnh chụp thật xác nhận).
   CHG-008 (2026-08-23, xem docs/CHANGE_LOG.md): bỏ `margin-left` cố định — 2 khoảng cách 2 bên
   Search giờ do spacer `::before`/`::after` của `.site-header__inner` đảm nhiệm (xem rule đó),
   Search chỉ cần khai báo `order: 3` để nằm đúng vị trí giữa 2 spacer theo `order` (1=Logo,
   2=spacer-trước, 3=Search, 4=spacer-sau, 5=Icons). `flex: 0 1 576px` giữ nguyên — Search vẫn
   không tự GROW (spacer đảm nhiệm phần giãn), chỉ SHRINK khi viewport hẹp không đủ chỗ. */
/* CHG-050 (2026-08-26, Header Surgical Correction A+B — xem docs/ui-qa/HEADER_GAP_ANALYSIS_2026-08-26.md):
   A) Reference đo được search box là OUTLINE-ONLY (viền ~1px #464646, nền trùng màu Header #1f232e,
      KHÔNG phải pill nền sáng) — đổi `background: var(--color-search-bg)` (#393d46, sai) sang
      `#1f232e` (đúng nền Header, tái dùng — đây là giá trị var(--color-brand-primary) nhưng ghi hex
      trực tiếp vì reference đo được khớp CHÍNH XÁC màu Header, không phải suy luận qua token khác)
      + thêm `border: 1px solid #464646` (đo pixel trực tiếp, giá trị mới, chưa có token tương ứng).
   B) `box-sizing: border-box` — sửa bug render 608px thay vì 576px khai báo (padding-inline 32px bị
      cộng ra NGOÀI max-width thay vì tính vào trong, do content-box — cùng pattern bug đã gặp ở
      Best Sellers CHG-034). Local fix, KHÔNG đụng global reset dòng 43. */
.site-search { position: relative; display: flex; align-items: center; background: #1f232e; border: 1px solid #464646; box-sizing: border-box; border-radius: var(--radius-full); height: 50px; padding-inline: var(--space-5) var(--space-3); flex: 0 1 576px; min-width: 0; max-width: 576px; order: 3; }
.site-search input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--color-text-on-dark); font-size: var(--font-size-sm); }
/* Sprint 04 Product UX Polish (2026-08-05): bug thật phát hiện qua keyboard Tab test thật —
   `outline: 0` ở trên (đúng cho trạng thái thường, tránh viền mặc định phá vỡ hình viên thuốc) VÔ
   TÌNH thắng luôn cả rule :focus-visible toàn site mới thêm (cùng specificity, đứng sau trong file)
   — khiến riêng ô tìm kiếm Header hoàn toàn không có focus indicator nào khi dùng bàn phím. Override
   lại đúng ngữ cảnh này. */
.site-search input:focus-visible { outline: 2px solid var(--color-brand-accent); outline-offset: 2px; }
.site-search input::placeholder { color: var(--color-text-on-dark); opacity: 0.65; }
.site-search__icon { display: grid; place-items: center; color: var(--color-text-on-dark-muted); background: none; border: 0; padding: var(--space-2); }
.site-search__icon--submit { color: var(--color-brand-accent); }
.site-search__icon--submit:hover { opacity: 0.8; }

/* CHG-007: `margin-left: auto` — Icons áp mép phải THẬT, độc lập với Search (khác CHG-005/006: lúc
   đó Icons đi theo Search trong 1 flex-group chung, "đóng băng" cùng nhau). Verify tại 1366px:
   actions.right=1334px — đúng.
   CHG-008 (2026-08-23, xem docs/CHANGE_LOG.md): bỏ `margin-left: auto` — khoảng cách Search↔Icons
   giờ do spacer `::after` của `.site-header__inner` đảm nhiệm (flex-grow theo đúng tỷ lệ 227/541.3
   đo được ở reference, xem rule đó), không còn cần `margin-left:auto` "hấp thụ hết phần còn lại"
   nữa (2 cơ chế chồng nhau sẽ vô hiệu hoá lẫn nhau). Actions chỉ cần `order: 5` để đứng sau spacer
   thứ 2 theo đúng thứ tự flex. */
.site-header__actions { display: flex; align-items: center; flex: 0 0 auto; gap: var(--space-5); color: var(--color-brand-accent); order: 5; }
/* Sprint 06 RC1 Validation (2026-08-05): bug thật — đo getBoundingClientRect() thật xác nhận 3 icon
   Header (Wishlist/Account/Cart) chỉ 21x21px (đúng bằng SVG bên trong, padding:0) — dưới ngưỡng tối
   thiểu WCAG 2.5.8 AA (24x24px). min-width/height không đổi kích thước icon hiển thị (SVG vẫn 21x21,
   canh giữa qua place-items:center có sẵn), chỉ mở rộng vùng bấm được. */
.header-action { position: relative; display: grid; place-items: center; min-width: 24px; min-height: 24px; transition: opacity var(--duration-base) var(--easing-standard); }
.header-action:hover { opacity: 0.75; }
/* Sprint Shopping Experience Foundation: gộp chung badge số lượng (trước chỉ có `.cart-count`
   scoped riêng `.header-action--cart`, nay dùng chung `.header-action__badge` cho cả Cart lẫn
   Wishlist — Generic hoá, không đổi hiển thị Cart). */
.header-action__badge {
    position: absolute; top: -6px; right: -10px; background: #ffffff; color: var(--color-brand-primary);
    font-size: 10px; font-weight: var(--font-weight-bold); border-radius: var(--radius-full);
    min-width: 18px; height: 18px; display: grid; place-items: center; padding-inline: 2px;
}
.header-action--cart-wrap { position: relative; }
.header-action--cart-wrap .header-action--cart { background: none; border: 0; cursor: pointer; padding: 0; color: inherit; font: inherit; }
/* Sprint 07 Mobile Product Discovery (2026-08-05): nút chỉ cần thiết trên mobile (`.site-search`
   inline đã đủ dùng ở Desktop) — ẩn mặc định, hiện lại trong @media (max-width: 768px) bên dưới. */
.header-action--search-toggle { display: none; background: none; border: 0; cursor: pointer; padding: 0; color: inherit; font: inherit; }

.site-nav { background: var(--color-brand-primary); border-top: 1px solid rgba(255, 255, 255, 0.08); }
.site-nav__inner { display: flex; align-items: center; gap: var(--space-10); height: 44px; }
.site-nav__toggle { display: none; align-items: center; gap: var(--space-2); background: none; border: 0; color: var(--color-text-on-dark); }
/* UI Audit 2026-08-23 (round cũ): đổi từ `gap` cố định (36px) sang `justify-content: space-between`
   với lý do "reference trải tới x≈1330" — số đo NÀY SAI (đo bằng ước lượng mắt thường trên ruler-
   crop, không phải kỹ thuật quét màu chính xác).
   CHG-008 (2026-08-23, xem docs/CHANGE_LOG.md) — SỬA LẠI, đo lại bằng kỹ thuật quét cụm chữ vàng
   (text-cluster detection, cùng phương pháp đã dùng cho Search/Logo/Icons — CHG-004/005): 10 item
   nav trong reference có khoảng cách GẦN NHƯ ĐỀU NHAU giữa các item (42-50px, trung bình ~45px) —
   nhưng item CUỐI kết thúc ở x=1214, cách mép phải hàng (~x=1334) tới 120px, LỚN HƠN HẲN khoảng
   cách giữa các item (~45px). Nếu dùng `space-between` thật, item cuối PHẢI áp sát mép phải (gap=0)
   — không khớp. Kết luận: reference dùng khoảng cách CỐ ĐỊNH (~45px) giữa các item, KHÔNG stretch
   full-width — quyết định trước (`justify-content:space-between`) dựa trên số đo sai, nay sửa lại
   theo số đo đúng (đã đo lại 2 lần độc lập, cùng kết quả, xem Change Log § Evidence). `flex: 1` bỏ
   (không cần chiếm hết chiều rộng nữa), `justify-content: space-between` → `flex-start` (mặc định,
   item left-pack theo đúng thứ tự, bắt đầu từ trái — khớp `left edge = Logo left = 32px`), `gap`
   đổi từ token `var(--space-6)=24px` sang giá trị đo được `45px`. */
.site-nav__menu { display: flex; align-items: center; gap: 45px; flex-wrap: wrap; height: 100%; }
.site-nav__menu li { display: flex; align-items: center; height: 100%; }
.site-nav__menu a {
    display: flex; align-items: center; height: 100%;
    color: var(--color-text-on-dark); font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold);
    text-transform: uppercase; letter-spacing: 0.06em;
    transition: color var(--duration-base) var(--easing-standard);
}
.site-nav__menu a:hover { color: var(--color-brand-accent); }
.site-nav__menu .nav-item--highlight > a { color: var(--color-brand-accent); }
.site-nav__menu .nav-item--highlight > a:hover { color: var(--color-text-on-dark); }

/* Active Nav State (Phase 2A.2 Visual Refinement, 2026-09-03 — Figma `Tap-chi.jpg`: "TẠP CHÍ"
   hiện gạch chân gold khi đang ở `/tap-chi/`, chữ vẫn màu trắng/kem, KHÔNG đổi sang gold như
   VEON/LUMI/EARTHNIQ). `.current-menu-item` do WordPress core tự thêm (khớp URL chính xác) HOẶC do
   `thekoco_mark_active_nav_items()` (inc/navigation.php) thêm cho ngữ cảnh "trang con" của 1 section
   (Product Detail/Category con/Article Detail) — style 1 chỗ DUY NHẤT áp dụng mọi trường hợp, không
   hardcode riêng item nào. Dùng `::after` định vị tuyệt đối (thay vì `border-bottom` toàn bộ chiều
   cao 44px của `.site-nav__inner`) để gạch chân nằm SÁT dưới chữ đúng như Figma, không lùi xuống tận
   mép dưới thanh Nav. `position:relative` chỉ thêm cho item active — không ảnh hưởng layout item khác.
   Loại trừ `.menu-item-home` (class WordPress core tự thêm cho item nào có URL === Trang chủ, VD
   "Bảo vệ sức khỏe"/"Thương hiệu" hiện đang là link tạm `/#health`/`/#` chưa gán trang thật — phát
   hiện qua regression test trên Homepage): những item này được core đánh dấu `current-menu-item`
   trên chính Trang chủ dù về mặt điều hướng KHÔNG đại diện section nào, gây 2 gạch chân cùng lúc.
   Loại trừ bằng ĐÚNG class ngữ nghĩa WordPress cung cấp sẵn — không đụng nội dung/URL Menu (ngoài
   phạm vi Sprint này), không hardcode theo tên item. */
.site-nav__menu .current-menu-item:not(.menu-item-home) > a,
.site-nav__menu .current-menu-parent:not(.menu-item-home) > a,
.site-nav__menu .current-menu-ancestor:not(.menu-item-home) > a { position: relative; }
.site-nav__menu .current-menu-item:not(.menu-item-home) > a::after,
.site-nav__menu .current-menu-parent:not(.menu-item-home) > a::after,
.site-nav__menu .current-menu-ancestor:not(.menu-item-home) > a::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: 8px; height: 2px;
    background: var(--color-brand-accent); border-radius: var(--radius-full);
}

/* Dropdown Submenu (Sprint Product Phase 1 — Vision/Mission/Core Values Page, 2026-09-15, xem
   D-VK-003 docs/01_Project/DECISION_LOG.md): item Primary Nav đầu tiên có menu con — "Về Koco" giờ
   là cha của "Tầm nhìn, Sứ mệnh & Giá trị cốt lõi" (ensureNavMenu() hỗ trợ `children`, xem
   Seeder.php). WordPress core tự thêm `.menu-item-has-children` + render `<ul class="sub-menu">`
   lồng bên trong — không cần đổi header.php (wp_nav_menu() mặc định `depth=0` = không giới hạn độ
   sâu). Hover/focus-within để hỗ trợ cả chuột lẫn bàn phím (Tab). Style dùng lại đúng token màu Nav
   hiện có (`--color-brand-primary`/`--color-brand-accent`) — không tạo bảng màu mới. Mọi item khác
   KHÔNG có `.sub-menu` nên style này không ảnh hưởng visual hiện tại của 8 item còn lại. */
.site-nav__menu li.menu-item-has-children { position: relative; }
.site-nav__menu .menu-item-has-children > a::after {
    content: ""; display: inline-block; width: 6px; height: 6px; margin-left: var(--space-2);
    border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
    transform: translateY(-2px) rotate(45deg); opacity: 0.7;
}
/* Bug Fix (2026-09-15, phát hiện lúc QA overflow cho trang "Tầm nhìn..." ở 1366px, xem
   docs/CHANGE_LOG.md § CHG-074): `left:0` khiến `.sub-menu` (min-width 300px) tràn khỏi mép phải
   viewport khi item cha nằm gần cuối Nav (VD "Về Koco", item thứ 9/10) — dropdown vẫn CHIẾM
   layout space dù `visibility:hidden` (chưa hover), tạo horizontal overflow toàn trang ở MỌI trang
   dùng Header chung, không riêng trang Tầm nhìn (phát hiện ở đây vì đây là nơi đầu tiên QA đối
   chiếu overflow ở đúng 1366px). Đổi `right:0` (thay `left:0`) — hiện tại CHỈ 1 item có `.sub-menu`
   ("Về Koco", gần cuối Nav) nên right-anchor luôn đúng; nếu sau này có item gần ĐẦU Nav cũng cần
   dropdown, cân nhắc lại (không phải trường hợp hiện tại). */
.site-nav__menu .sub-menu {
    list-style: none; margin: 0; padding: var(--space-2) 0;
    position: absolute; top: 100%; right: 0; min-width: 300px;
    background: var(--color-brand-primary); border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: var(--radius-base); box-shadow: var(--shadow-lg);
    opacity: 0; visibility: hidden; transform: translateY(6px);
    transition: opacity var(--duration-base) var(--easing-standard), transform var(--duration-base) var(--easing-standard), visibility var(--duration-base);
    z-index: 60;
}
.site-nav__menu li.menu-item-has-children:hover > .sub-menu,
.site-nav__menu li.menu-item-has-children:focus-within > .sub-menu {
    opacity: 1; visibility: visible; transform: translateY(0);
}
.site-nav__menu .sub-menu li { height: auto; width: 100%; }
.site-nav__menu .sub-menu a {
    display: block; height: auto; width: 100%; padding: var(--space-3) var(--space-5);
    text-transform: none; letter-spacing: normal; font-weight: var(--font-weight-medium); white-space: normal;
}
.site-nav__menu .sub-menu a:hover { color: var(--color-brand-accent); background: rgba(255, 255, 255, 0.04); }
.site-nav__menu .sub-menu .current-menu-item > a { color: var(--color-brand-accent); }
.site-nav__menu .sub-menu .current-menu-item > a::after { content: none; }

/* ---------- Hero ---------- */
/* Review lần 3: track dùng aspect-ratio đúng bằng ảnh gốc (inline style từ PHP, tính theo
   native_width/native_height của slide — xem block-hero.php) để không méo ảnh ở mọi độ rộng.
   Layout Rebalance (Final): aspect-ratio một mình không đủ — ở màn rộng hơn native_width (1366px,
   tức đa số laptop/màn hình thật: 1440/1536/1920) track cao vượt xa thiết kế (VD 1920px → cao
   ~706px thay vì 502px thiết kế), khiến Header+Hero chiếm gần hết màn hình đầu tiên, người dùng
   phải scroll mới thấy nội dung — đúng góp ý "Hero quá cao". Thêm `max-height` bằng đúng
   native_height (502px, tham chiếu thiết kế) để khoá TRẦN chiều cao — ảnh vẫn không méo vì
   `.block-hero__slide` đã dùng `background-size:cover` sẵn (chỉ crop 2 bên khi box rộng hơn tỷ lệ
   ảnh gốc, không bao giờ giãn/méo). Dưới 1366px hành vi giữ nguyên như cũ (aspect-ratio tự co, còn
   thấp hơn 502 nên max-height chưa can thiệp). */
.block-hero { position: relative; overflow: hidden; background-color: var(--color-bg-subtle); }
.block-hero__track { position: relative; width: 100%; max-height: 502px; }
/* Sprint UI-04: fade animation (thay vì display:none/flex cắt cứng) — luôn display:flex, chuyển
   bằng opacity để có hiệu ứng mờ dần mượt giữa các slide. */
.block-hero__slide {
    display: flex; position: absolute; inset: 0; background-size: cover; background-position: center;
    align-items: center; color: var(--color-text-on-dark); text-align: left;
    opacity: 0; visibility: hidden; z-index: 0;
    transition: opacity var(--duration-slow) var(--easing-standard), visibility 0s var(--duration-slow);
}
.block-hero__slide.is-active { opacity: 1; visibility: visible; z-index: 1; transition-delay: 0s; }
/* Review lần 2: slide cover (đúng vị trí gốc trong koco.jpg) đặt text ở phần TRÊN, không canh
   giữa — canh giữa khiến chữ đè lên mặt model. Padding tính theo % chiều cao track (không phải px
   cố định) để giữ đúng tỉ lệ khi track co giãn theo aspect-ratio. Slide split vẫn canh giữa. */
/* CHG-015 (2026-08-24, Block 02 Visual Polish — Round 2): ROOT CAUSE của "title chìm vào nền" —
   dòng này override màu chữ từ `--color-text-on-dark` (đúng, kế thừa từ `.block-hero__slide` base
   line ~380) THÀNH `--color-text` (#111111, gần đen) CHỈ cho layout cover — trong khi `.block-hero__title`
   không có `color` riêng nên ăn theo giá trị SAI này, tạo chữ tối trên ảnh nền (đặc biệt rõ ở slide
   "Timeless Aura" — vùng ảnh sau chữ tối màu). Sửa lại đúng token gốc (không tạo token mới). Thêm
   `::before` scrim gradient (LẦN ĐẦU áp dụng cho Desktop/Tablet — trước đây chỉ Mobile có, xem
   `@media max-width:768px` bên dưới) để đảm bảo đọc được cả khi vùng ảnh sau chữ sáng màu, nhưng
   CHỈ phủ ~60% bên trái (đúng vùng `.block-hero__text` max-width 480px) — nửa phải ảnh (nơi sản
   phẩm thường nằm) không bị che. */
/* CHG-016: ROOT CAUSE THẬT của "CTA biến mất" (không chỉ do số dòng title) — `padding-top: 14%`
   luôn resolve theo CHIỀU RỘNG containing block (đặc thù CSS spec cho padding-top/bottom, kể cả khi
   người viết kỳ vọng theo chiều cao), trong khi `.block-hero__track` có `max-height: 502px` CỐ ĐỊNH
   — ở màn hình rộng (đo được: 2560px → padding-top = 358px, chiếm 71% trong 1 box chỉ cao 502px),
   phần còn lại cho eyebrow+title+subtitle+CTA co lại tới mức CTA bị đẩy tràn khỏi track (đo được
   overflow -71px tại 2560px — TRÀN THẬT, `.block-hero{overflow:hidden}` cắt mất CTA, đúng hiện
   tượng "CTA biến mất" báo cáo). `clamp(0px, 14%, 200px)` giữ NGUYÊN hành vi 14% ở mọi viewport ĐÃ
   duyệt trước đây (1366px baseline → 14%=191px < trần 200px, KHÔNG đổi; Tablet 1024px → 14%=143px <
   200px, KHÔNG đổi) — chỉ chặn trần khi vượt ngưỡng (Desktop rộng >1430px), không phải hack
   breakpoint mới, không đổi hành vi Mobile (đã có rule riêng `padding-top: 0` trong @media 768px,
   không đụng tới). */
.block-hero__slide--cover { color: var(--color-text-on-dark); align-items: flex-start; padding-top: clamp(0px, 14%, 200px); }
.block-hero__slide--cover::before {
    content: "";
    position: absolute; inset: 0;
    background: linear-gradient(100deg, rgba(8, 8, 8, 0.68) 0%, rgba(8, 8, 8, 0.36) 42%, rgba(8, 8, 8, 0) 62%);
    pointer-events: none;
    z-index: 0;
}
.block-hero__slide--cover .block-hero__content { position: relative; z-index: 1; }
/* CHG-015 (đo thực tế bằng canvas pixel-sampling — xem docs/CHANGE_LOG.md § CHG-015 Evidence):
   slide "Daily UV Defense" có vùng ảnh SÁNG (kem/hồng nhạt, rgb~231,218,218) ngay sau text — riêng
   scrim gradient KHÔNG đủ (làm tối cả patch ảnh trông như 1 khối chữ nhật phủ lên, "overlay nặng"
   nếu tăng alpha đủ mạnh để bù). Dùng text-shadow (kỹ thuật được duyệt trong yêu cầu gốc) làm cơ
   chế đọc-được CHÍNH — tạo viền tối mềm quanh từng ký tự, hoạt động độc lập với tông màu ảnh nền
   thật (không phụ thuộc scrim), không tạo hình khối nhìn thấy được trên ảnh. Chỉ áp dụng cho layout
   cover (nơi có ảnh nền); split dùng bg_color đặc, không cần. */
.block-hero__slide--cover .block-hero__text {
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55), 0 4px 18px rgba(0, 0, 0, 0.35);
}
.block-hero__content { display: flex; align-items: center; justify-content: space-between; width: 100%; gap: var(--space-10); }
.block-hero__text { max-width: 480px; }
.block-hero__eyebrow { text-transform: uppercase; letter-spacing: 0.12em; color: var(--color-brand-accent); font-size: var(--font-size-sm); margin-bottom: var(--space-3); }
/* CHG-016 (thay CHG-015): 'Georgia' bị loại vì vỡ dấu đôi tiếng Việt (xem comment @import ở đầu
   file). Dùng 'Noto Serif' (web font, tải qua @import phía trên) — ĐÃ XÁC MINH bằng Google Fonts
   `text=` subsetting API với đúng 5 ký tự dấu đôi (ố/ấ/ắ/ộ/ỗ — U+1ED1/1EA5/1EAF/1ED9/1ED7): trả về
   @font-face hợp lệ (không rỗng/không lỗi) → xác nhận font THẬT SỰ có glyph, không suy đoán.
   Fallback cuối `var(--font-family-base)` (Helvetica Neue, sans) — KHÔNG fallback về Georgia/Times
   New Roman (đã biết lỗi) — nếu Google Fonts không tải được, giảm về sans-serif đã xác nhận đúng
   dấu tiếng Việt ở mọi nơi khác trên site, còn hơn tiếp tục dùng font vỡ dấu.
   `line-clamp`/`-webkit-line-clamp: 3` (MỚI): root cause của CTA "biến mất" ở slide tiêu đề dài
   không phải do 1 con số font-size cụ thể sai, mà do chiều cao `.block-hero__title` KHÔNG CÓ TRẦN —
   title càng dài, càng nhiều dòng, càng đẩy CTA xuống, tới mức tràn khỏi `.block-hero__track`
   (overflow:hidden → CTA bị cắt mất, không phải "layout sai" mà là "không có giới hạn"). Chặn cứng
   tối đa 3 dòng bất kể độ dài chuỗi — CTA từ nay LUÔN ở vị trí xác định, không phụ thuộc số dòng
   heading (đúng yêu cầu gốc, không phải hack pixel/breakpoint riêng lẻ như cách tiếp cận CHG-015 cũ
   đã chứng minh không đáng tin cậy giữa các môi trường render khác nhau). */
.block-hero__title {
    font-family: 'Noto Serif', var(--font-family-base);
    font-size: calc(var(--font-size-3xl) + 6px);
    line-height: var(--line-height-tight);
    margin-bottom: var(--space-4);
    font-weight: var(--font-weight-bold);
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
}
.block-hero__subtitle { font-size: var(--font-size-base); color: var(--color-text-on-dark-muted); margin-bottom: var(--space-5); }
/* CHG-015: XOÁ override cũ `.block-hero__slide--cover .block-hero__subtitle { color:
   var(--color-text-muted) }` — đây là root cause #2 của vấn đề contrast (làm subtitle tối trên ảnh
   nền, cùng lỗi gốc như title). Không override gì thêm — subtitle quay lại đúng rule base ngay phía
   trên (`--color-text-on-dark-muted`, đã đúng sẵn, KHÔNG cần rule mới). */
/* CHG-015: CTA nhỏ/yếu — root cause là `padding var(--space-2) var(--space-5)` (8px/20px) + `font-size
   var(--font-size-xs)` (12px), nhỏ hơn CẢ phiên bản Mobile (space-3/space-6, sm=14px, xem @media
   768px bên dưới — ngược đời, Desktop CTA nhỏ hơn Mobile). Tăng lên, TÁI DÙNG token có sẵn (không
   hardcode số mới), border-radius/pill giữ nguyên (kế thừa `.btn` base, không đổi). */
.block-hero .btn { padding: var(--space-3) var(--space-8); font-size: var(--font-size-base); }
/* CHG-015: `.btn--outline-light` (base, style.css khác) dùng `border-color: var(--color-border-dark)`
   (#2a2a2a, gần đen) — đúng cho Best Sellers (nền navy đặc `--color-brand-primary`, KHÔNG ĐỘNG tới
   rule base vì Best Sellers vẫn dùng), nhưng SAI cho Hero (nền ảnh đa sắc, viền gần đen nhiều khi vô
   hình). Override CHỈ trong phạm vi `.block-hero` (compound selector, không !important) — không ảnh
   hưởng `.btn--outline-light` ở Best Sellers. */
.block-hero .btn--outline-light { border-color: var(--color-text-on-dark); }
/* CHG-014 (Block 02 Narrow Fix): wrapper cho CTA chính + CTA phụ (khi slide bật secondary CTA) —
   chỉ 1 CTA thì wrapper co về đúng 1 nút, không đổi layout hiện có. */
.block-hero__cta-group { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.block-hero__image { flex-shrink: 0; max-width: 380px; }
.block-hero__image img { border-radius: var(--radius-base); max-height: 400px; object-fit: contain; margin-inline: auto; }
.block-hero__arrow {
    position: absolute; top: 50%; transform: translateY(-50%); background: rgba(255,255,255,0.8); color: #111;
    border: 0; width: 40px; height: 40px; border-radius: var(--radius-full); font-size: var(--font-size-xl); cursor: pointer;
    display: grid; place-items: center; z-index: 2;
}
.block-hero__arrow--prev { left: var(--space-6); }
.block-hero__arrow--next { right: var(--space-6); }
.block-hero__dots { position: absolute; bottom: var(--space-6); left: 50%; transform: translateX(-50%); display: flex; gap: var(--space-2); z-index: 2; }
.block-hero__dot { width: 8px; height: 8px; border-radius: var(--radius-full); border: 0; background: rgba(255, 255, 255, 0.4); padding: 0; cursor: pointer; }
.block-hero__dot.is-active { background: var(--color-brand-accent); }

/* ---------- Brand strip ---------- */
/* Review lần 2: mỗi brand-item là 1 ô kích thước CỐ ĐỊNH (không theo tỉ lệ ảnh gốc — 14 file logo
   có kích thước gốc rất khác nhau, 8px-14px cao). object-fit: scale-down = "contain" nhưng KHÔNG
   BAO GIỜ phóng to quá kích thước gốc của ảnh (chỉ thu nhỏ nếu cần) — thoả cả 2 yêu cầu "căn giữa
   bằng object-fit" và "không upscale". Desktop: flex:1 chia đều, đủ 14 logo 1 hàng, không cuộn. */
/* Review lần 3: giảm chiều cao dải xuống gần với cảm giác dải mỏng trong thiết kế gốc
   (~52px thay vì ~72px trước đó). */
.block-brand-strip { background: var(--color-brand-accent); }
.block-brand-strip__track { display: flex; flex-wrap: nowrap; align-items: stretch; padding: var(--space-3) var(--space-6); }
.block-brand-strip__item {
    flex: 1 1 0; min-width: 0; display: grid; place-items: center; height: 26px;
    opacity: 0.85; transition: opacity var(--duration-base) var(--easing-standard);
}
.block-brand-strip__item:hover { opacity: 1; }
/* CHG-017 (2026-08-24, Block "Brand Strip" polish): ROOT CAUSE của "visual weight không cân bằng"
   giữa các logo — đo trực tiếp 14 file ảnh thật (`getimagesize()`, không suy đoán): kích thước gốc
   dao động 21×14 tới 126×8px, tỉ lệ khung hình lệch nhau RẤT XA (1.5:1 ở wella.png tới 15.75:1 ở
   carolina-herrera.png). `object-fit: scale-down` KHÔNG BAO GIỜ phóng to ảnh vượt kích thước gốc —
   vì mọi file đều có chiều cao gốc ≤14px (nhỏ hơn nhiều so với box cao 26px), phần lớn logo hiển thị
   ĐÚNG BẰNG kích thước gốc tí hon (9px, 8px, 12px...) thay vì lấp đầy box, khiến logo "yếu" hơn hẳn
   logo có sẵn chiều cao gần 14px. Đổi sang `object-fit: contain` — vẫn giữ NGUYÊN tỉ lệ khung hình
   (không stretch/distort/crop), nhưng phóng to đúng mức để lấp đầy chiều cao/rộng box (Annayake
   9px→~14px, Wella 14px→lấp đầy 26px) — 12/14 logo cải thiện rõ, 2 logo tỉ lệ quá dẹt (Carolina
   Herrera 15.75:1, Estée Lauder đã vượt bề rộng box) không đổi vì đã bị giới hạn bởi bề rộng box từ
   trước (contain cho kết quả giống hệt scale-down trong 2 trường hợp này — không có tác dụng phụ).
   Không đổi bất kỳ file logo nào, không đổi box height/width, không đổi màu. */
.block-brand-strip__item img { max-width: 100%; max-height: 100%; object-fit: contain; }

/* ---------- Badge ---------- */
/* Sprint UI-04: đo lại từ koco.jpg — bo góc nhẹ (không pill tròn hẳn), nền đỏ mận đậm cho Hot. */
.thekoco-badge {
    position: absolute; top: var(--space-3); left: var(--space-3); z-index: 2;
    font-size: 12px; font-weight: var(--font-weight-semibold); padding: 6px 14px;
    border-radius: 6px; color: #fff; text-transform: none; letter-spacing: 0.02em;
}
.thekoco-badge--hot { background: var(--color-danger); }
.thekoco-badge--sale { background: var(--color-brand-primary); border: 1px solid rgba(255,255,255,0.15); }
.thekoco-badge--new { background: var(--color-brand-accent); color: var(--color-brand-primary); }
.thekoco-badge--bestseller { background: #2a2a2a; color: var(--color-brand-accent); border: 1px solid var(--color-brand-accent); }
.thekoco-badge--featured { background: #fff; color: var(--color-brand-primary); }
/* Sprint Product/Brand/Sale Foundation: 4 variant mới — CHỈ THÊM, không sửa 5 variant trên (đúng
   quy tắc Golden Component ở docs/ui/GOLDEN_COMPONENTS.md § Quy tắc thay đổi). Toàn bộ dùng token
   màu có sẵn (--color-info/--color-warning/--color-brand-secondary/--color-text-muted), không
   thêm hex mới. */
.thekoco-badge--combo { background: var(--color-info); }
.thekoco-badge--limited { background: var(--color-warning); }
.thekoco-badge--coming-soon { background: var(--color-brand-secondary); }
.thekoco-badge--outofstock { background: var(--color-text-muted); }

/* ---------- Product card & grid ---------- */
/* Sprint UI-04: Product Card dùng chung nhiều nơi (Home/Category/Brand/Search/Related/New/Best
   Sellers) — không dùng giao diện WooCommerce mặc định. Đo pixel từ koco.jpg: ảnh gần vuông
   (~262x260, tỉ lệ 1:1 chấp nhận được), badge/wishlist là overlay trên ảnh (không phải card box
   riêng), text bám sát ngay dưới ảnh gần như không padding, brand→tên cách rất ít, tên→category
   cách rõ hơn. */
.tab-filter { display: flex; justify-content: center; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-8); }
/* Sprint 04 Product UX Polish (2026-08-05): màu chữ trạng thái chưa active đo được contrast 3.78:1
   trên nền tối (rgb(7,7,7) của Best Sellers) khi dùng --color-text-muted (thiết kế cho nền sáng) —
   FAIL WCAG AA (cần ≥4.5:1). Đổi sang --color-text-on-dark-muted (đã có sẵn, đã dùng đúng ở footer
   link, đo PASS 6.15:1) — dùng lại token có sẵn, không tạo màu mới.
   CHG-022 (Visual Matching — Best Sellers): đo trực tiếp `Desktop - 8.jpg` — pill KHÔNG dùng viền
   outline, mà fill nền đặc (#141821, ĐÚNG `--color-footer-lower-bg` đã đo ở CHG-011, tái dùng token
   có sẵn); trạng thái active fill nền gold `--color-brand-accent` + chữ tối, không phải border gold
   như bản cũ. */
.tab-filter__btn {
    background: var(--color-footer-lower-bg); border: none; color: var(--color-text-on-dark-muted);
    padding: var(--space-3) var(--space-6); border-radius: var(--radius-full); font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    transition: all var(--duration-base) var(--easing-standard);
}
.tab-filter__btn.is-active { background: var(--color-brand-accent); color: var(--color-brand-primary); font-weight: var(--font-weight-semibold); }

/* CHG-043 (Round 8 — P0-1 Typography, Category Tabs): Product Owner chỉ ra Tab hiện tại "quá nhỏ,
   quá mờ, thiếu visual weight" — đối chiếu pixel-thật trên `Desktop - 8.jpg` (tab "Dưỡng da"/"Tất cả",
   xem Report Round 8 § Evidence):
   - Chữ Tab CHƯA active: màu đo được hội tụ #b7b3a8 — sáng/ấm hơn rõ rệt `--color-text-on-dark-muted`
     (#a2a2a2) đang dùng (cùng tông màu với Brand — xem CHG-042, không phải trùng hợp, cả 2 dùng
     `--color-text-on-dark-muted` gốc trước Round này).
   - Pill active: nền đo được `#cba94f` — 1 tông gold KHÁC (nhạt/ấm hơn) `--color-brand-accent`
     (#d6b035) đang dùng cho Price/Heading — đo SẠCH (n=360/360 pixel đồng nhất trong vùng lấy mẫu,
     không phải nhiễu nén JPEG) — Tab dùng 1 sắc gold RIÊNG, không trùng token site-wide, hex cục bộ
     (cùng cách xử lý `--card-badge-sale-bg`/category color ở trên).
   - Font-weight nhìn đậm/rõ nét hơn 500 (`--font-weight-medium`) hiện tại — đổi sang 600 (semibold).
   - Font-size: pill height đo được (~38px, khớp MCP `1992:1005` Phase 1) gần khớp 14px hiện tại +
     padding, nhưng combo "size nhỏ + màu mờ" tạo cảm giác "bé" theo đúng mô tả Product Owner — tăng
     nhẹ lên 16px (đủ khác biệt cảm nhận, không phá tỉ lệ pill/padding hiện có).
   Chỉ scope Best Sellers (`.tab-filter` hiện chỉ Best Sellers dùng, nhưng scope tường minh để an toàn
   nếu Block khác dùng lại `.tab-filter` sau này). */
.block-best-sellers .tab-filter__btn {
    font-size: 16px;
    font-weight: var(--font-weight-semibold);
    color: #b7b3a8;
}
/* CHG-045 (2026-08-26, Round 10 — Final Polish P1, Active Tab Contrast): Product Owner đánh dấu
   "Tất cả" (active) nhìn mờ/washed-out dù nền gold đã đúng — root cause KHÔNG phải opacity/filter/
   text-shadow (đã rà soát, không có), mà là CASCADE SPECIFICITY: rule gốc `.tab-filter__btn.is-active`
   (dòng 849, 2 class) đặt `color: var(--color-brand-primary)` (#1f232e, tối, đúng cho nền gold) —
   nhưng rule `.block-best-sellers .tab-filter__btn` (dòng 867, CŨNG 2 class = ĐỒNG SPECIFICITY,
   đứng SAU trong file) đặt `color: #b7b3a8` cho MỌI tab kể cả `.is-active` (không có selector loại
   trừ `.is-active`) — CSS lấy rule sau cùng khi specificity bằng nhau, nên Active bị "ăn" nhầm màu
   chữ xám mờ (dùng cho tab KHÔNG active) đè lên màu tối vốn đúng, tạo cảm giác opacity/nhoè dù không
   hề có opacity nào. KHÔNG cần hex mới: chỉ tái khẳng định lại đúng token `--color-brand-primary` đã
   có sẵn (dùng đúng ở rule gốc dòng 849) trong 1 selector đủ specificity (3 class, thêm `.is-active`)
   để thắng cascade. Contrast #1f232e trên #cba94f rất cao — đạt yêu cầu "rõ nét, sắc nét". */
.block-best-sellers .tab-filter__btn.is-active { background: #cba94f; color: var(--color-brand-primary); }

.product-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--space-5); }

.product-card { position: relative; }
.product-card__thumb {
    position: relative; display: flex; align-items: center; justify-content: center;
    border-radius: var(--radius-base); overflow: hidden; aspect-ratio: 1 / 1;
    background: var(--color-surface-dark);
    transition: transform var(--duration-base) var(--easing-standard);
}
.product-card:hover .product-card__thumb { transform: translateY(-2px); }
.product-card__thumb img { width: 82%; height: 82%; object-fit: contain; object-position: center; }
/* CHG-022: `.product-card__thumb-link` bọc riêng ảnh (thay vì cả `.product-card__thumb` là `<a>`
   như trước) — lý do: Best Sellers cần thêm nút "Thêm vào giỏ hàng" (`.product-card__add-cart`, xem
   dưới) là 1 `<a>` overlay khác trong CÙNG khối ảnh; 2 thẻ `<a>` lồng nhau là HTML không hợp lệ
   (trình duyệt tự đóng thẻ ngoài, gãy DOM). Tách link ảnh ra làm 1 lớp phủ riêng (position:absolute
   inset:0) NGANG HÀNG với Badge/Wishlist/Add-to-cart thay vì bọc chúng — không đổi hành vi click
   ảnh→trang sản phẩm, không đổi hiển thị ở bất kỳ Block nào đang dùng chung component. */
.product-card__thumb-link { position: absolute; inset: 0; z-index: 1; display: flex; align-items: center; justify-content: center; }
/* Wishlist — Action Button thật (không phải hình vẽ trên ảnh), click được, không có nền tròn để
   khớp thiết kế (chỉ icon outline). */
.product-card__wish {
    position: absolute; top: var(--space-3); right: var(--space-3); z-index: 2; width: 24px; height: 24px;
    background: none; border: 0; padding: 0; color: var(--color-text-on-dark-muted); cursor: pointer;
    display: grid; place-items: center; transition: color var(--duration-base) var(--easing-standard);
}
.product-card__wish:hover { color: var(--color-brand-accent); }
.product-card__wish.is-active { color: var(--color-danger); }
.product-card__wish.is-active svg { fill: currentColor; }

/* CHG-044 (Round 8 — Badge + Discount Coexistence): `.product-card__badges` CHỈ tồn tại trong DOM
   khi PHP truyền `show_discount_badge=true` VÀ sản phẩm thật sự on-sale VÀ badge trạng thái không
   phải đã là 'sale' (xem product-card.php — tránh lặp "-44% | -44%"). Vì vậy rule này an toàn để
   KHÔNG scope riêng `.block-best-sellers` — Sale/New Products không bao giờ render phần tử này (cờ
   mặc định false, không truyền ở `block-sale.php`/`block-new_products.php`), nên rule không có cơ
   hội áp dụng ở đó dù không scope. Badge con dùng `position:static` (thay vì `absolute` mặc định
   của `.thekoco-badge` — override CHỈ áp dụng cho badge NẰM TRONG wrapper này, không đổi hành vi
   `.thekoco-badge` ở nơi khác) để 2 Badge xếp ngang hàng bằng flex thay vì chồng lên nhau ở cùng 1
   toạ độ absolute. */
.product-card__badges { position: absolute; top: var(--space-3); left: var(--space-3); z-index: 2; display: flex; gap: 6px; }
.product-card__badges .thekoco-badge { position: static; top: auto; left: auto; }

/* CHG-022: overlay "Thêm vào giỏ hàng" — hiện khi hover/focus ảnh, chỉ khi PHP truyền
   `show_add_to_cart` (chỉ Best Sellers bật, xem product-card.php doc-comment). Ẩn mặc định bằng
   opacity (không `display:none`) để giữ được transition mượt + vẫn đọc được bằng screen reader. */
.product-card__add-cart {
    position: absolute; left: var(--space-3); right: var(--space-3); bottom: var(--space-3); z-index: 2;
    display: block; text-align: center; text-decoration: none;
    background: var(--color-bg-subtle); color: var(--color-brand-primary);
    font-size: 13px; font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.04em;
    padding: var(--space-3) var(--space-4); border-radius: var(--radius-base);
    opacity: 0; transform: translateY(6px); pointer-events: none;
    transition: opacity var(--duration-base) var(--easing-standard), transform var(--duration-base) var(--easing-standard);
}
.product-card:hover .product-card__add-cart,
.product-card:focus-within .product-card__add-cart {
    opacity: 1; transform: translateY(0); pointer-events: auto;
}
.product-card__add-cart.loading { opacity: 0.6; }

.product-card__body { padding-top: var(--space-2); }
.product-card--dark .product-card__body { color: var(--color-text-on-dark); }
.product-card__brand { font-size: 13px; font-style: italic; font-weight: var(--font-weight-regular); color: var(--color-text-on-dark-muted); margin-bottom: 2px; }
.product-card--light .product-card__brand { color: var(--color-text-muted); }
.product-card__title { font-size: 16px; line-height: var(--line-height-tight); font-weight: var(--font-weight-medium); margin-bottom: var(--space-2); }
.product-card__title a:hover { color: var(--color-brand-accent); }
.product-card__cat { font-size: 13px; color: var(--color-text-muted); margin-bottom: var(--space-2); }
.product-card__rating { display: flex; align-items: center; gap: var(--space-1); margin-bottom: var(--space-2); }
.product-card__rating-stars { color: var(--color-brand-accent); font-size: 13px; letter-spacing: 1px; }
.product-card__rating-count { font-size: 12px; color: var(--color-text-muted); }
.product-card--dark .product-card__rating-count { color: var(--color-text-on-dark-muted); }
.product-card__price { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; font-weight: var(--font-weight-semibold); }
.product-card__price-old { color: var(--color-text-muted); font-weight: var(--font-weight-regular); font-size: 13px; text-decoration: line-through; }
.product-card__price-new { color: var(--color-brand-accent); font-size: 16px; }
.product-card--light .product-card__price-new { color: var(--color-brand-primary); }
.product-card__price-discount { color: var(--color-danger); font-size: 13px; font-weight: var(--font-weight-semibold); }

.product-card--light .product-card__body { background: #fff; padding: var(--space-3) var(--space-4) var(--space-4); }
.product-card--light { background: #fff; border-radius: var(--radius-base); overflow: hidden; box-shadow: var(--shadow-sm); transition: box-shadow var(--duration-base) var(--easing-standard); }
.product-card--light:hover { box-shadow: var(--shadow-lg); }
.product-card--light .product-card__thumb { border-radius: 0; background: #fff; }

/* Sprint UI-05: polish riêng cho Product Card kiểu 'dark' (chỉ Best Sellers dùng style này —
   Sale/New Products dùng 'light', không bị ảnh hưởng). Ảnh nổi bật hơn, tên giới hạn 2 dòng,
   giá mới đậm/nổi bật hơn, giá cũ mờ ~50%, wishlist lùi vào để cân đối.
   CHG-022 (Visual Matching — Best Sellers, đo `Desktop - 8.jpg` bằng PHP GD): trước đây chỉ
   `.product-card__thumb` có nền riêng (`--color-surface-dark` #161616), phần text bên dưới KHÔNG có
   box — sai thiết kế gốc: cả Card (ảnh + text) nằm chung 1 khối nền bo góc duy nhất, màu đo được
   #141821 (ĐÚNG `--color-footer-lower-bg`, xem CHG-011 — không phải `--color-surface-dark`, lệch
   tông). Chuyển nền/bo góc/đệm lên `.product-card--dark` (Card gốc), thumb bên trong trong suốt;
   Badge/Wishlist đưa về sát mép trong của đệm mới (top/right: 0) thay vì cộng dồn 2 lớp đệm. */
.product-card--dark {
    background: var(--color-footer-lower-bg);
    border-radius: var(--radius-base);
    padding: var(--space-4);
}
.product-card--dark .product-card__thumb { background: transparent; }
.product-card--dark .product-card__thumb img { width: 90%; height: 90%; }
.product-card--dark .thekoco-badge { top: 0; left: 0; }
.product-card--dark .product-card__wish { top: 0; right: 0; }
.product-card--dark .product-card__body { padding-top: var(--space-5); }
.product-card--dark .product-card__title {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.product-card--dark .product-card__price-old { opacity: 0.5; }
.product-card--dark .product-card__price-new { font-weight: var(--font-weight-bold); }
/* CHG-022: màu Badge đo trực tiếp `Desktop - 8.jpg` KHÁC bản `.thekoco-badge--*` dùng chung (xem
   § Badge phía trên) — Hot/Bán chạy = nền đỏ mận đặc #781035 (ĐÚNG `--color-topbar-bg`, tái dùng
   token có sẵn) + chữ trắng; Sale (-X%) = nền kem #f7f4e5 (không token nào khớp, thêm 1 biến cục bộ
   `--card-badge-sale-bg` NGAY TẠI đây — chỉ dùng trong phạm vi Best Sellers, không đẩy lên
   tokens.css vì chưa xác nhận có dùng lại ở Block khác) + chữ cùng màu mận. Override CHỈ trong
   `.product-card--dark` (= chỉ Best Sellers, xem chú thích ADR-008 dưới `thekoco_render_product_badge()`
   — vẫn giữ nguyên quy tắc "1 Badge/sản phẩm", KHÔNG dựng cơ chế 2-badge dù ảnh gốc cho thấy Card
   #2 có cả "-10%" lẫn "Bán chạy" cùng lúc — xem Change Log § Vấn đề còn tồn tại). */
.product-card--dark .thekoco-badge--hot,
.product-card--dark .thekoco-badge--bestseller,
.product-card--dark .thekoco-badge--featured {
    background: var(--color-topbar-bg); color: #fff; border: none;
}
.product-card--dark .thekoco-badge--sale {
    --card-badge-sale-bg: #f7f4e5;
    background: var(--card-badge-sale-bg); color: var(--color-topbar-bg); border: none;
}

/* ======= Sprint "Final Audit & Fix Product Card UI — Category Page" (2026-09-08) =======
   ROOT CAUSE (audit code + đo trực tiếp `design/23-8/san pham/San-pham.jpg` bằng .NET GetPixel,
   KHÔNG suy đoán): `.product-card--dark` hiện coi TOÀN BỘ card là 1 khối phẳng — 1 background + 1
   padding dùng chung cho cả ảnh (`.product-card__thumb { background: transparent }`, dòng ~1046)
   lẫn phần chữ, trong khi design chia RÕ 2 vùng màu khác nhau thật sự (không phải cảm nhận chủ
   quan): Media Area #141821 (≈ `--color-footer-lower-bg` có sẵn) và Info Area #2c3343
   (`--color-card-info-bg`, token mới, xem tokens.css). Đây CHÍNH LÀ nguyên nhân khiến Wishlist "nhìn
   như" thuộc về card thay vì đúng vùng ảnh dù code Wishlist đã đúng `position:relative` với
   `.product-card__thumb` từ trước (D-PL... CHG-022) — 2 vùng không có ranh giới màu nên mắt không
   phân biệt được, không phải lỗi vị trí DOM/CSS của riêng Wishlist.
   PHẠM VI: chỉ `.thekoco-shop` (Shop/Category/Brand archive + Search Results — cả 2 NGỮ CẢNH đã
   dùng chung `.product-card--dark` bên trong `.thekoco-shop` từ D-PL01B-003/005) — KHÔNG đụng
   `.product-card--dark` gốc. Lưu ý: Best Sellers (Homepage) KHÔNG mắc lỗi 1-khối-phẳng này — đã tự
   giải quyết ĐỘC LẬP từ trước (CHG-041/047/049, xem `.block-best-sellers .product-card--dark` +
   `.product-card__thumb`/`.product-card__body`, dòng ~322-370) bằng margin âm/padding bù đối xứng,
   dùng CHÍNH XÁC 2 màu giống hệt (`#2c3343`/`--color-footer-lower-bg`) — xác nhận qua browser thật
   rằng phép đo pixel của Sprint này khớp 100% với giá trị Best Sellers đã dùng từ trước, không phải
   trùng hợp: đây LÀ 1 giá trị thiết kế duy nhất của hệ thống, chỉ chưa được tổng quát hoá thành
   token dùng chung trước đây. `--color-card-info-bg` (tokens.css) giờ là ĐÚNG token nên dùng cho mọi
   Card kiểu 'dark' cần Info Area riêng biệt — Best Sellers vẫn giữ nguyên literal `#2c3343` (không
   sửa, ngoài phạm vi Sprint "chỉ Category Page"), có thể dọn dẹp sau (Future Improvement, xem
   Report) để cả 2 nơi cùng tham chiếu 1 token duy nhất. */
.thekoco-shop .product-card--dark {
    background: transparent;
    padding: 0;
    overflow: hidden; /* clip cả 2 vùng theo đúng 1 bo góc chung của card (border-radius kế thừa từ rule gốc) */
}
.thekoco-shop .product-card--dark .product-card__thumb {
    background: var(--color-footer-lower-bg);
    border-radius: 0; /* bo góc do card cha đảm nhiệm (overflow:hidden) — tránh 2 lớp bo góc chồng nhau */
}
/* Badge/Wishlist trước đây TRÔI NỔI cách mép card đúng bằng padding CỦA CARD CHA (--space-4) vì
   `.product-card__thumb` từng nằm LỌT BÊN TRONG padding đó. Giờ thumb đã tràn hết chiều rộng Card
   (card cha hết padding, xem rule trên) — phải tự bù lại khoảng cách này TRÊN CHÍNH Badge/Wishlist,
   nếu không chúng sẽ dính sát mép bo góc (mất hẳn khoảng thở, sai design). Dùng ĐÚNG `--space-3` —
   giá trị đã dùng cho biến thể 'light' mặc định của `.product-card__wish` (dòng ~972), không phát
   minh số mới. */
.thekoco-shop .product-card--dark .product-card__wish { top: var(--space-3); right: var(--space-3); }
.thekoco-shop .product-card--dark .thekoco-badge { top: var(--space-3); left: var(--space-3); }

/* Info Area — nền riêng + Rating dời từ giữa Tên sản phẩm/Giá (vị trí cũ, SAI theo design — xem
   product-card.php DOM order gốc) lên CHUNG HÀNG với Brand, ngay đầu Info Area (đúng vị trí đo được
   trên design: Brand trái — Rating phải, cùng 1 baseline, ngay dưới Media Area). TÁI DÙNG NGUYÊN kỹ
   thuật CSS Grid `grid-template-areas` đã có sẵn cho `.block-sale__products`/`.block-new-products`
   (xem style.css dòng ~1573/~1841 — cùng bài toán "Brand + Rating cùng hàng", không phát minh cách
   mới) — KHÔNG sửa `product-card.php`/DOM order (Brand/Title/Cat/Rating/Price giữ nguyên thứ tự
   trong HTML, dữ liệu/logic rating không đổi) — chỉ ĐỊNH VỊ LẠI bằng CSS Grid, đúng yêu cầu brief
   "tạo cấu trúc CSS/layout hợp lý, không dùng absolute positioning cứng nhắc gây overlap".
   Ô "brand" trống khi sản phẩm không có Brand (1 số sản phẩm brand rỗng, xem product-card.php) →
   Rating vẫn đứng đúng bên phải hàng 1, layout không lệch — không hardcode theo từng sản phẩm.
   Hàng "cat" chỉ thật sự có nội dung ở Shop root/Brand archive (`hide_category=false`); Category
   archive (`hide_category=true`) không render `.product-card__cat` → hàng tự co về 0, không cần rule
   riêng cho 2 trường hợp (CSS Grid tự xử lý hàng rỗng). */
.thekoco-shop .product-card--dark .product-card__body {
    background: var(--color-card-info-bg);
    padding: var(--space-4);
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
        "brand  rating"
        "title  title"
        "cat    cat"
        "price  price";
    column-gap: var(--space-3);
}
.thekoco-shop .product-card--dark .product-card__brand {
    grid-area: brand; align-self: center; margin-bottom: 0;
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.thekoco-shop .product-card--dark .product-card__rating { grid-area: rating; justify-self: end; align-self: center; margin-bottom: 0; }
.thekoco-shop .product-card--dark .product-card__title { grid-area: title; }
.thekoco-shop .product-card--dark .product-card__cat { grid-area: cat; }
.thekoco-shop .product-card--dark .product-card__price { grid-area: price; }
/* Title dùng font Serif — TÁI DÙNG chính giá trị đã dùng cho vai trò giống hệt ("tên sản phẩm") ở
   `.block-sale__products .product-card__title` (dòng ~1594) — đối chiếu `San-pham.jpg` xác nhận chữ
   "Tinh chất phục hồi ban đêm 30ml" có chân serif rõ, khác hẳn Brand/Giá (sans-serif) — KHÔNG phát
   minh font mới. */
.thekoco-shop .product-card--dark .product-card__title { font-family: 'Noto Serif', var(--font-family-base); }

/* BUG FIX (phát hiện qua audit thật — verify `document.styleSheets`, KHÔNG suy đoán): CSS gốc của
   `.product-card__add-cart` (dòng ~996) khai `position:absolute`, nhưng WooCommerce core
   (`woocommerce.css`, selector `.woocommerce a.button`, specificity (0,2,1) — CAO HƠN selector gốc
   (0,1,0)) ghi đè thành `position:relative` — khiến nút "Thêm vào giỏ hàng" KHÔNG còn là overlay nổi
   trên ảnh, mà rơi vào flow bình thường bên trong `.product-card__thumb` (flex,
   `justify-content:center`) → ĐÂY LÀ NGUYÊN NHÂN THẬT của "button trắng lớn đè lên ảnh" Product
   Owner báo — KHÔNG PHẢI hover-state/quick-add/fallback UI nào cố ý, thuần bug specificity CSS.
   San-pham.jpg (design) KHÔNG hiện CTA nào ở trạng thái tĩnh — khớp Ý ĐỒ GỐC đã ghi rõ trong comment
   CHG-022 ("overlay hiện khi hover/focus") — tức bug specificity này vô tình làm SAI ngay chính hành
   vi mà code TỰ NÓ đã định nghĩa, không phải yêu cầu thiết kế mới. Sửa bằng tăng specificity (3
   class) thay vì `!important`. CHỈ áp dụng trong `.thekoco-shop`. Verify lại qua browser thật: Best
   Sellers (Homepage) KHÔNG dính bug này — `.woocommerce a.button` (WC core) chỉ match phần tử có
   tổ tiên mang class `.woocommerce`, mà Best Sellers nằm trên page template thường (không có
   `.woocommerce` bao ngoài, xác nhận `closest('.woocommerce')` trả `null`) nên selector đó KHÔNG
   bao giờ áp dụng ở đó — Best Sellers vốn đã hiển thị `position:absolute` đúng từ trước, KHÔNG cần
   sửa. Bug CHỈ xảy ra ở đúng những trang có `.woocommerce` bao ngoài thật (Shop/Category/Brand/
   Search — mọi nơi `.thekoco-shop` xuất hiện), đúng phạm vi Sprint này.
   Sau khi sửa, hành vi hover-reveal-only (đúng CHG-022 + đúng San-pham.jpg) tự hoạt động — KHÔNG cần
   đổi gì khác; business logic Add to Cart (URL/AJAX/quantity/data-product_id) giữ NGUYÊN 100%. */
.thekoco-shop .product-card__thumb .product-card__add-cart { position: absolute; }

/* CHG-018 (2026-08-24, Block 3 — Trust Benefits/USP): dải 4 cam kết thương hiệu, chưa từng tồn tại
   trước đợt này (xem docs/CHANGE_LOG.md § CHG-018 — audit xác nhận không có file/DOM nào trước đó).
   CHG-021 (Visual Matching — Third Correction, đo TRỰC TIẾP bằng PHP GD trên
   `design/23-8/trang chu/Desktop - 8.jpg`, không suy đoán — xem chi tiết đo đạc trong
   docs/CHANGE_LOG.md § CHG-021 Evidence):
   1) Nền: CHG-020 gộp chung `--color-brand-primary` với Best Sellers — ĐO LẠI xác nhận SAI, 2
      section dùng 2 tông khác nhau thật (#181820 vs #1f232e, ranh giới rõ tại y≈1164 trong ảnh
      gốc) — tách riêng token `--color-trust-bg` (tokens.css), không dùng chung nữa.
   2) Container: đổi từ `.container` (max-width 1320px, gây margin ngoài lớn ở viewport rộng —
      ĐÚNG root cause "4 USP dồn vào giữa") sang override `max-width:none` — TÁI DÙNG NGUYÊN pattern
      đã có tiền lệ trong chính file này cho `.block-best-sellers .container`/`.site-header
      .container` (xem comment dòng ~163-178), không tự nghĩ ra cách mới.
   3) Divider: XOÁ HẲN — đo ảnh gốc không có đường kẻ dọc nào giữa 4 item, chỉ có khoảng cách. */
/* F007 (2026-08-26, Final Pixel Audit — xem docs/ui-qa/100-percent-authentic/AUDIT.md): đo lại 3
   mốc ĐỘC LẬP, không mơ hồ trên `Desktop - 8.jpg` (chuyển màu nền cứng, không phải cạnh chữ):
   section-top(y=866, giáp Brand Strip) → section-bottom(y=1164, giáp Best Sellers) → tổng chiều cao
   THẬT = 298px. Đo riêng icon-top(y=922, cạnh icon rõ, không mơ hồ) → padding-top THẬT = 56px. Suy
   ra padding-bottom = 298 − 56 − content(181, icon-top→desc-bottom) = 61px — khớp ĐỘC LẬP với đo
   trực tiếp desc-bottom(y=1103) → section-bottom: 1164−1103=61px (2 cách đo hội tụ cùng 1 số, không
   phải nhiễu đo). `padding-block: var(--space-12)` (48px đều 2 phía) hiện tại NGẮN HƠN thật ở CẢ 2
   phía (-8px trên, -13px dưới) — tổng section thấp hơn 15px so với thiết kế, đúng như Product Owner
   quan sát bằng mắt "bị nén chiều cao". Không token nào khớp 56/61 sẵn có (--space-12=48,
   --space-16=64) — dùng giá trị đo được trực tiếp (cùng tiền lệ đã áp dụng cho Header/Footer khi
   không có token khớp, VD CHG-004 Header Search 576px). CHỈ sửa Desktop (rule gốc) — Tablet/Mobile
   (`@media 1024px`/`768px` bên dưới) giữ nguyên `padding-block: var(--space-8)` không đổi, vì
   KHÔNG có Figma frame Tablet/Mobile riêng để đo đối chứng (tránh suy đoán ngoài phạm vi có bằng
   chứng — đúng nguyên tắc "không sửa theo cảm tính"). */
.block-trust { background: var(--color-trust-bg); padding-top: 56px; padding-bottom: 61px; }
.block-trust .container { max-width: none; }
.block-trust__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-6); }
.block-trust__item {
    display: flex; flex-direction: column; align-items: center; text-align: center;
    padding-inline: var(--space-4);
}
/* CHG-021: icon — đo trực tiếp vùng icon trong ảnh gốc (item 1: y=922–997) = ~75px cao, ĐÚNG BẰNG
   kích thước gốc file PNG (68×77–78×74px, đo ở CHG-019) — nghĩa là design hiển thị icon ở GẦN ĐÚNG
   kích thước gốc, không thu nhỏ. 56px (CHG-020) vẫn nhỏ hơn thật ~25%. Đổi thành 72px (gần khớp
   kích thước gốc trung bình 4 file, an toàn không phóng to vượt native → không mờ). Khoảng cách
   icon→value đo được 24px = ĐÚNG `--space-6`; value→description đo được 36px = ĐÚNG `--space-9` —
   2 giá trị này khớp token có sẵn (không phải trùng hợp ngẫu nhiên, giữ nguyên thay vì gap đều). */
.block-trust__icon { display: inline-flex; margin-bottom: var(--space-6); }
.block-trust__icon img { width: 72px; height: 72px; object-fit: contain; }
/* CHG-020 (giữ nguyên từ lần trước — vẫn đúng): value/description dùng token "chữ sáng trên nền
   tối" (Hero cũng dùng), không phải màu mới. */
.block-trust__value { font-size: var(--font-size-2xl); font-weight: var(--font-weight-bold); color: var(--color-text-on-dark); line-height: var(--line-height-tight); margin-bottom: var(--space-9); }
.block-trust__desc { font-size: var(--font-size-sm); color: var(--color-text-on-dark-muted); line-height: var(--line-height-base); }

.block-best-sellers { background: var(--color-brand-primary); padding-block-start: 56px; padding-block-end: var(--space-16); }
.block-best-sellers .container { max-width: none; }

/* CHG-022 (Visual Matching — Best Sellers): thiết kế gốc là CAROUSEL 4 card/hàng (không Grid tĩnh
   5 cột) + thanh scrollbar tối giản bên dưới, KHÔNG có nút "Xem tất cả" (đã xác nhận Product Owner,
   xoá hẳn `.block-best-sellers__more` cũ). Tái dùng NGUYÊN cơ chế `data-thekoco-carousel`/
   `data-carousel-track` đã có ở Flash Sale/New Products (assets/js/main.js) — không viết carousel
   riêng.
   CHG-034 (2026-08-26, Round 4 — Responsive Architecture Fix, SỐ LIỆU ĐÃ SỬA Ở CHG-035 BÊN DƯỚI):
   `.container` của Block này full-bleed (`max-width:none` ở trên) là ĐÚNG (không đổi) — Section vẫn
   full-bleed để nền màu tràn hết viewport, khớp cảm giác hiện có. NHƯNG công thức card width CHG-022
   cũ (`flex:0 0 calc((100% - gap*3)/4)`, ép ĐÚNG 4 card lấp kín 100% CONTAINER bất kể container rộng
   bao nhiêu) là ROOT CAUSE lỗi Product Owner phát hiện đầu tiên ở viewport 1920px: đo thật bằng
   Puppeteer (`getBoundingClientRect`) tại 1920px cho card rộng 487px/card — right edge của card 4 nằm
   ở x=2032px, VƯỢT QUA CHÍNH viewport 1920px. Nguyên nhân kỹ thuật thật (điều tra sâu hơn ở CHG-034
   gốc): KHÔNG PHẢI lỗi flexbox edge-case như suy đoán ban đầu, mà do `.product-card` render với
   `box-sizing:content-box` (nguồn gốc rule ghi đè chưa xác định — xem Open Question), khiến padding+
   border (34px) bị CỘNG THÊM ngoài flex-basis đã khai báo — đã sửa bằng `box-sizing:border-box` cục
   bộ (giữ nguyên, xem `.product-card--dark` bên dưới).

   CHG-035 (2026-08-26, Round 5 — Composition Fix): CHG-034 (Round 4) sửa đúng root cause kỹ thuật
   NHƯNG chọn sai chiến lược composition — đổi sang `clamp(280px, 22vw, 320px)` khiến SỐ LƯỢNG card
   hiển thị mỗi màn hình tự tăng theo viewport (đo được ~5.5 card tại 1920px) — Product Owner review
   screenshot thật, xác nhận đây KHÔNG phải mục tiêu: "viewport rộng → thêm nhiều card" không phải tư
   duy đúng cho Block này, Desktop composition phải ƯU TIÊN GIỮ ~4 card/màn hình (giống Figma) ở MỌI
   viewport, không phải tối ưu số lượng card hiển thị.
   Sửa: đổi từ "giữ ĐỘ RỘNG CARD cố định, để SỐ LƯỢNG card tự đổi" (CHG-034) sang NGƯỢC LẠI — "giữ SỐ
   LƯỢNG card gần cố định (~4 + 1 phần nhỏ peek), để ĐỘ RỘNG CARD tự đổi trong 1 biên độ hẹp". Cơ chế:
   1) `.block-best-sellers__carousel` nhận `max-width:1400px` MỚI (không có ở CHG-034) — đây là ĐÒN
      BẨY CHÍNH chặn phình to ở màn rộng: từ 1400px viewport trở lên, khung nhìn Carousel NGỪNG giãn
      thêm dù Section/`.container` cha vẫn full-bleed (nền màu vẫn tràn hết, chỉ khung Card bị khoá).
      Mốc 1400 chọn gần khớp độ rộng khả dụng thật tại chính viewport 1366px hiện có (~1318px sau
      padding) — tức Card ở 1920px sẽ RẤT GẦN kích thước ở 1366px (tăng ~7%, không phải +59% như
      CHG-034 cũ), giữ đúng "1366px là REFERENCE COMPOSITION" cho MỌI viewport lớn hơn.
   2) Công thức Card đổi từ chia `/4` (ép ĐÚNG 4 card, 0 peek) sang chia `/4.15` — hệ số phụ (0.15)
      dành SẴN 1 khoảng nhỏ làm "peek" card kế tiếp (đúng hành vi carousel đã có ở CHG-022, không xoá)
      MÀ VẪN tỉ lệ nhất quán ở MỌI viewport (không phải peek biến thiên tuỳ ý theo viewport như trước).
   Kết quả tính toán (xem Report Round 5 § Test để đối chiếu số đo Puppeteer thật): Card ~303px tại
   1366 (khớp Figma MCP 306px, Phase 1), ~282px tại 1280, ~321px tại 1440, ~323px tại 1920 (do khung
   Carousel đã khoá ở max-width:1400) — biên độ toàn dải chỉ 282–323px, rất hẹp, đúng "cùng 1
   composition responsive" ở mọi viewport thay vì để Card tự do co giãn theo viewport không giới hạn. */
/* CHG-039 (2026-08-26, Round 6 — Reference-First Correction): Round 5 (CHG-035) SAI — tự đặt hệ số
   chia `/4.15` mà KHÔNG đối chiếu số đo thật từ Figma, kết quả là card thứ 5 vẫn LỘ RÕ trong khung
   nhìn ở 1366px (đo lại bằng Puppeteer trước khi sửa Round này: `card5Left=1316.5px < viewportWidth
   1366px` → `card5Visible: true`) — Product Owner xác nhận qua screenshot đây là FAIL, không phải
   "gần đúng". Root cause của Round 5: chọn số 4.15 chỉ để "có vẻ hợp lý" (dành sẵn peek), không dựa
   trên bất kỳ số đo Figma nào — đúng lỗi Task Round 6 cảnh báo ("clamp/chia số tuỳ ý").

   Đo LẠI TRỰC TIẾP từ Figma MCP `get_metadata` (Phase 1, node `1992:341` "Frame 36434" — EXACT, đã
   có sẵn từ trước, không phải suy luận): container 4-card = x=34,y=1470,width=1300,height=311, chứa
   4 rounded-rectangle tại x=0/331/662/993 (relative), mỗi cái rộng 306px → gap = 331−306 = **25px**
   (KHÔNG PHẢI 20px/`var(--space-5)` đang dùng — sai số nhỏ tồn tại từ CHG-022, chưa từng sửa qua các
   Round). Content width 4 card + 3 gap = 306×4+25×3 = 1299px. Container(Frame 36434) x=34 → lề trái
   34px; lề phải = 1366−34−1299 = 33px — GẦN NHƯ ĐỐI XỨNG ~34px mỗi bên (KHÔNG PHẢI full-bleed sát
   mép, và KHÔNG PHẢI `padding-inline:24px` — `var(--space-6)` — đang dùng qua `.container` gốc).
   Đây là bằng chứng CHÍNH XÁC NHẤT có được (MCP, không phải ước lượng pixel) cho toàn bộ composition.

   Sửa 4 điểm, tất cả bám THẲNG vào số đo trên, KHÔNG có hệ số "cho an toàn" nào tự thêm:
   1) `.block-best-sellers .container` — `padding-inline` đổi từ 24px (kế thừa) → **34px** (khớp lề
      Figma đo được).
   2) `.block-best-sellers__carousel` — `max-width` đổi từ 1400px (số Round 5 tự chọn) →
      **1300px** (khớp content-width 1299px đo được, làm tròn lên 1px cho dễ chia hết).
   3) `gap` đổi từ `var(--space-5)` (20px) → **25px** trực tiếp (khớp Figma, không dùng token site-
      wide vì token đó chưa từng khớp giá trị đo thật của riêng Block này).
   4) Công thức Card đổi từ chia `/4.15` (tự bịa, dành dư peek) → **chia đúng `/4`** — ĐÚNG 4 card lấp
      kín 100% khung Carousel ở BẤT KỲ viewport nào ≤ 1300px+68px(padding)=1368px, và bị khoá cứng ở
      đúng kích thước 1366px cho MỌI viewport rộng hơn (nhờ `max-width:1300px` ở carousel) — nghĩa là
      1280/1366/1440/1920 dùng CHUNG một composition, không có viewport nào tự "thêm card" hay "lộ
      peek" — đúng yêu cầu §4 Task Round 6 ("card thứ 5 không được tự nhiên xuất hiện trong vùng
      visible composition"). Track vẫn cuộn được tới card 5-10 qua `overflow-x:auto` + scrollbar đã
      có (không đổi cơ chế carousel, chỉ đổi kích thước khung nhìn — đúng phân biệt "track có 5+ sản
      phẩm" khác với "viewport hiển thị 4 sản phẩm" mà Task yêu cầu). */
/* CHG-040 (2026-08-26, Round 7 — Section Architecture Correction): CHG-039 (Round 6) SAI Ở TẦNG KIẾN
   TRÚC, không phải sai con số. Đã biến 1 KẾT QUẢ ĐO (content-width = 1299px, NHƯNG chỉ đúng TẠI
   viewport 1366px) thành 1 QUY TẮC CỐ ĐỊNH (`max-width:1300px` cho MỌI viewport). Product Owner chỉ
   ra: 1299 ≈ 1366 − 34 − 34 — tức đó là "viewport trừ padding 2 bên" TẠI ĐÚNG viewport 1366, không
   phải "carousel bị khoá cứng 1300px". Áp `max-width + margin-inline:auto` lên `.block-best-sellers
   __carousel` khiến Block, dù bản thân `<section>` vẫn full-bleed (background tràn viewport, KHÔNG
   đổi), NHƯNG phần NỘI DUNG (Carousel/Card) bị nhốt thành 1 cụm cố định 1300px NẰM GIỮA màn hình ở
   viewport rộng (1920/1440) — 2 bên lộ khoảng trống lớn, đúng lỗi "module nhỏ nằm giữa trang" Product
   Owner phát hiện qua screenshot thật (KHÔNG phải lỗi số đo Puppeteer, DOM đo đúng 306px/card nhưng
   COMPOSITION Ở CẤP SECTION sai — Technical QA Round 6 PASS trong khi Visual QA đúng ra phải FAIL,
   Round 6 đã tự chấm sai chỗ này).

   Sửa bằng cách bỏ HẲN `max-width`/`margin-inline:auto` khỏi `.block-best-sellers__carousel` — không
   còn "tầng capped-and-centered" nào giữa Section full-bleed và Card. Card lấy lại công thức CHG-022
   gốc — chia ĐÚNG /4 theo 100% khung nhìn thật (= `.container` full-bleed, trừ `padding-inline:34px`
   2 bên, KHÔNG trừ thêm gì khác) — tại MỌI viewport, không riêng 1366:
     1920px: (1920−68−75)/4 = 444px/card — Card LỚN HƠN Figma (306px) NHƯNG đây là hệ quả tất yếu của
       "Section full-width thật + đúng 4 card" tại màn rất rộng, không phải bug — nếu ép Card giữ
       nguyên 306px ở 1920 mà vẫn đúng 4 card KHÔNG lộ peek thì BẮT BUỘC phải giới hạn chiều rộng nội
       dung lại (chính là cách CHG-039 làm, và ĐÃ bị bác bỏ ở Round này vì tạo cảm giác "đảo giữa
       trang"). Product Owner xác nhận ưu tiên "Section trải đúng full-width, không đảo" cao hơn "Card
       giữ nguyên y hệt 306px ở mọi viewport" — đây là đánh đổi CÓ CHỦ ĐÍCH, không phải bỏ sót.
     1366px: (1366−68−75)/4 = 305.75 ≈ 306px — VẪN khớp Figma EXACT tại đúng viewport reference,
       không đổi so với Round 6.
     1280px: (1280−68−75)/4 ≈ 284px. 1440px: (1440−68−75)/4 ≈ 324px.
   `padding-inline:34px` (đo Figma exact, giữ nguyên từ CHG-039 — số đo Figma này KHÔNG sai, chỉ có
   max-width là sai) và `gap:25px` (Figma exact, giữ nguyên) — KHÔNG đổi 2 giá trị này, chỉ bỏ tầng
   max-width/margin-auto đã gây lỗi. Card vẫn LUÔN đúng 4/hàng bằng công thức `/4` (không dư peek) ở
   MỌI viewport — không regression yêu cầu "không lộ card 5" của Round 6. */
.block-best-sellers .container { padding-inline: 34px; }
.block-best-sellers__carousel .product-grid {
    display: flex; gap: 25px; overflow-x: auto; scroll-snap-type: x proximity; scrollbar-width: none;
}
.block-best-sellers__carousel .product-grid::-webkit-scrollbar { display: none; }
.block-best-sellers__carousel .product-grid .product-card {
    flex: 0 0 calc((100% - (25px * 3)) / 4);
    scroll-snap-align: start;
}
/* Scrollbar: đo trực tiếp `Desktop - 8.jpg` (thanh dài ~588px, đặt giữa, dày ~3px, đoạn đã "xem"
   tô gold `--color-brand-accent`, phần còn lại xám nhạt) — đây là thanh cuộn thu nhỏ phản ánh
   scrollLeft/scrollWidth thật của `[data-carousel-track]` (xem JS `updateScrollbar()` trong
   main.js), không phải progress bar đếm bước. */
/* CHG-039 (Round 6): cards→scrollbar gap kế thừa `var(--space-8)`=32px — pixel-ước-lượng trực tiếp
   trên `Desktop - 8.jpg` (crop y=1940-2080, card bottom≈y1975, scrollbar top≈y2030) cho ~55px, gần
   khớp giá trị dùng cho tabs→cards (56px, đo EXACT qua MCP ở trên) hơn hẳn 32px hiện tại — dùng
   THỐNG NHẤT 56px cho nhất quán nhịp điệu dọc (giá trị này là ƯỚC LƯỢNG PIXEL, không phải MCP exact
   như 3 số phía trên — độ tin cậy thấp hơn, xem Report Round 6 § Open Question). */
.block-best-sellers__scrollbar {
    position: relative;
    max-width: 588px; margin: 56px auto 0;
    height: 3px; border-radius: var(--radius-full);
    background: rgba(255, 255, 255, 0.25);
    cursor: pointer; /* CHG-038: xác nhận thị giác — thanh giờ click được thật, không còn là hình trang trí */
}
/* CHG-038: bản thân thanh (không riêng thumb) cũng chỉ cao 3px — quá mảnh để bấm chính xác bằng
   chuột thật (không chỉ vấn đề test Puppeteer). Mở rộng hit-area TOÀN THANH bằng `::before` trong
   suốt (không đổi 3px hiển thị), bắt sự kiện click rồi tự nổi bọt (bubble) lên đúng phần tử `.block-
   best-sellers__scrollbar` đang gắn listener — không cần đổi JS. */
.block-best-sellers__scrollbar::before {
    content: ''; position: absolute; left: 0; right: 0; top: -8px; bottom: -8px;
}
.block-best-sellers__scrollbar-thumb {
    position: absolute; top: 0; left: 0; height: 100%;
    background: var(--color-brand-accent); border-radius: inherit;
    cursor: grab;
}
.block-best-sellers__scrollbar-thumb:active { cursor: grabbing; }
/* CHG-038: vùng bấm/kéo thật của thumb rộng 3px (đúng thiết kế) quá mảnh để thao tác chuột/ngón tay
   chính xác — mở rộng vùng NHẬN sự kiện (hit area) bằng `::before` trong suốt, cao hơn hẳn phần nhìn
   thấy, mà KHÔNG đổi kích thước hiển thị 3px (giữ đúng Figma). Chuẩn UX pattern cho control mảnh
   (tương tự browser range input có thumb nhỏ nhưng hit-area lớn hơn nhiều so với phần vẽ). */
.block-best-sellers__scrollbar-thumb::before {
    content: ''; position: absolute; left: 0; right: 0; top: -10px; bottom: -10px;
}

/* ---------- Flash Sale (Sprint UI-06, Final Review — Full Width) ----------
   ROOT CAUSE lỗi "Flash Sale bị thu nhỏ giữa màn hình": Review 5 khoá `max-width:1366px` thẳng
   lên `.block-sale` để giữ Card không bị "trông nhỏ" so với Background ở màn rộng — nhưng đó là
   sửa sai TẦNG. Hero (`.block-hero`) không hề có vấn đề này dù CŨNG không có max-width, vì Hero
   tách biệt 2 tầng: Section luôn Full Width (ảnh nền dùng `background-size:cover` nên không bao
   giờ méo dù rộng bao nhiêu), còn CONTENT (text/nút) mới là thứ được giới hạn — bọc trong
   `.container` (max-width:1320px, đúng token toàn site) nằm BÊN TRONG section full-width đó (xem
   `class="container block-hero__content"` trong block-hero.php). Flash Sale trước đây khoá nhầm
   ở tầng Section nên nhìn như 1 khung nhỏ giữa trang, khác hẳn Hero/Best Sellers.
   Sửa: bỏ `max-width` khỏi `.block-sale` (Full Width thật, hoà vào flow trang) + đổi
   `background-size: 100% 100%` (ép giãn đúng khung) → `cover` (giống Hero, không méo ảnh dù
   Section rộng/hẹp) + bọc nội dung trong `.container` (xem `.block-sale__inner` bên dưới) để
   Logo/Countdown/Card luôn nằm trong cột nội dung chuẩn 1320px giống mọi Section khác trên trang
   — Card không còn "nhỏ so với Background" vì vị trí Card giờ neo theo `.container` cố định,
   không phụ thuộc chiều rộng thật của Section nữa.
   CSS custom property (không hardcode màu/ảnh): --campaign-color, --campaign-image. */
/* Layout Rebalance (Final): cùng lỗi Hero — aspect-ratio một mình khiến Section cao vượt xa 665px
   thiết kế ở màn rộng hơn 1366px (VD 1920px → ~934px), đẩy Card/Logo/Countdown "lọt thỏm" phía
   trên còn hơn nửa dưới chỉ toàn Background. Thêm `max-height:665px` khoá trần đúng bằng chiều cao
   thiết kế — Card GIỮ NGUYÊN kích thước (không đụng), Background luôn vừa đủ bao quanh nội dung.
   `background-position: left center` (không phải `center`) để phần bị crop khi Section rộng hơn
   tỷ lệ ảnh luôn là bên PHẢI (vùng ảnh người mẫu, ít nội dung quan trọng), giữ nguyên vùng nền
   vàng bên trái nơi đặt Logo/Countdown/Card. */
/* CHG-025 (2026-08-25, Visual Matching Round tiếp theo Best Sellers): đo trực tiếp
   `design/23-8/trang chu/Desktop - 8.jpg` bằng PHP GD — Section cao ĐÚNG 704px (không phải 665px
   của bản koco.jpg cũ), khớp 1:1 với ảnh nền mới `sale-summer-bg.png` (1366×704, xem
   block-sale.php). Bố cục đổi từ 1 cột (xếp dọc) sang 2 CỘT: trái = nội dung chữ (Eyebrow/Heading/
   Mô tả/Countdown/CTA), phải = Carousel sản phẩm — đo được cột trái rộng ~340px, cột phải bắt đầu
   tại x=452 (tính từ mép trái Section, TRÙNG khớp `padding-left:56px` + 340px + gap 56px). Toàn bộ
   phần Header-row/Logo/Bolt (SVG tia sét) của bản cũ đã XOÁ khỏi template — bản mới không có logo
   đồ hoạ, không có icon bolt, xem doc-comment `block-sale.php`. */
.block-sale {
    position: relative;
    width: 100%;
    aspect-ratio: 1366 / 704;
    max-height: 704px;
    background-color: var(--campaign-color, var(--color-bg-subtle));
    background-image: var(--campaign-image);
    background-size: cover;
    background-position: left center;
    color: var(--color-brand-primary);
    overflow: hidden;
}
.block-sale__content {
    position: absolute; inset: 0; z-index: 1;
}
/* Layout Rebalance (Final) § 3 (vẫn đúng — giữ nguyên lý do): KHÔNG dùng class `.container`
   (max-width:1320px + margin-inline:auto CĂN GIỮA) — Flash Sale là bố cục gắn chặt vào ảnh nền,
   Content PHẢI sát mép trái ở mọi độ rộng màn hình, không được trôi ra giữa Section như `.container`
   tự động căn giữa. `display:flex` MỚI ở CHG-025 để chia 2 cột trái/phải — chiều cao 100% để
   `align-items:center` canh giữa dọc cả 2 cột theo tâm Section (đúng cảm giác ảnh gốc).
   CHG-026 (2026-08-25, bug fix — khoảng trống lớn bên phải, phần 2): `max-width:1320px` (kế thừa từ
   bản CHG-025 đầu tiên, bắt chước hệ số `.container`) là ROOT CAUSE THỨ HAI của "khoảng trống kem
   lớn bên phải" — ở viewport rộng (Product Owner chụp ảnh ~1920px), `.block-sale__inner` bị khoá
   cứng ở 1320px rồi nằm SÁT TRÁI (không `margin:auto`), để lộ nguyên phần Section CÒN LẠI (1920−1320
   ≈ 600px) là nền trống bên phải — ĐÚNG lỗi đã từng xảy ra với `.block-trust`/`.block-best-sellers`
   trước đây (xem CHG-021 "container 1320px gây margin ngoài lớn ở viewport rộng"). Bỏ hẳn
   `max-width` — TÁI DÙNG NGUYÊN pattern đã áp dụng cho Hero/Best Sellers/Trust (full-width, chỉ dùng
   padding-inline làm khoảng cách mép), để `.block-sale__col--products` (flex:1) hấp thụ TOÀN BỘ
   phần rộng dư ra, Carousel/Track (đã `width:100%` ở fix phần 1) lấp đúng hết cột phải ở MỌI độ
   rộng màn hình, không riêng 1366px. */
/* CHG-029 (2026-08-25, Round 5 — SỬA MÔ HÌNH, không phải sửa số đo): CHG-028 khoá CỨNG 2 cột đúng
   pixel đo được ở viewport thiết kế gốc (340px/884px) — ĐÚNG tại 1366px, nhưng SAI mô hình: khoá
   cứng nghĩa là ở màn hình RỘNG HƠN (1920px), 340px chỉ còn chiếm ~17.7% viewport (340/1920) thay vì
   giữ đúng TỈ LỆ thị giác ~27.8% mà chính ảnh gốc thể hiện (340/(340+884)=27.8% trong VÙNG NỘI DUNG,
   không tính phần nền dư ra ngoài vùng 1366px gốc) — cột chữ nhìn "lọt thỏm" khi màn hình rộng, dù
   con số 340px về kỹ thuật vẫn "đúng" theo phép đo. Product Owner chỉ ra: 340px/884px là SỐ ĐO của
   1 bản thiết kế 1366px, KHÔNG PHẢI kích thước cố định cho mọi viewport — TỈ LỆ mới là thứ cần bảo
   toàn (340:884, tương đương ~27.8%:72.2%), không phải chính con số px.
   Đổi `.block-sale__inner` từ `display:flex` (2 cột cố định px) sang `display:grid` với
   `grid-template-columns: 340fr 884fr` — đơn vị `fr` không giới hạn ở số nguyên nhỏ, nhận thẳng
   340/884 làm TRỌNG SỐ tỉ lệ thật (không làm tròn về 1:3/25%:75% ước lượng) — tại viewport mà tổng
   không gian 2 cột (sau khi trừ padding/gap) ĐÚNG BẰNG 340+884=1224px, kết quả trả về CHÍNH XÁC
   340px/884px (giữ nguyên số đo pixel đã xác nhận đúng ở CHG-025/028 tại 1366px); ở viewport RỘNG
   HƠN, 2 cột giãn theo ĐÚNG TỈ LỆ 340:884 thay vì cột chữ đứng yên còn cột Carousel/nền hấp thụ hết
   phần dư — khắc phục đúng root cause "panel text lọt thỏm ở 1920px" mà không quay lại lỗi "giãn vô
   hạn không giới hạn Card" của CHG-027 (xem cách giới hạn Card bên dưới, tại `.block-sale__products
   .product-card`). Padding (56px/24px) và gap giữa 2 cột (56px) GIỮ NGUYÊN cố định — không có căn cứ
   đo cho thấy 2 giá trị này cần tỉ lệ theo viewport, khác bản chất với độ RỘNG 2 cột nội dung. */
.block-sale__inner {
    display: grid; grid-template-columns: 340fr 884fr; align-items: center; gap: 56px;
    height: 100%;
    padding-left: 56px; padding-right: 24px;
}
.block-sale__col--content { min-width: 0; }
.block-sale__col--products { min-width: 0; }

/* Logo đồ hoạ (field `logo_image`, xem Repository) — vẫn giữ trong kiến trúc cho campaign khác cần
   dùng, campaign hiện tại KHÔNG dùng (xem block-sale.php — nhánh else render chữ thật bên dưới).
   Hiển thị nguyên kích thước gốc, không set width/height ép tỉ lệ. */
.block-sale__logo { display: block; }

/* Eyebrow/Heading — CHỮ THẬT (CHG-025), đo trực tiếp `Desktop - 8.jpg`: Eyebrow màu tối nhạt
   (`--color-text-muted`, uppercase, letter-spacing rộng — cùng "ngôn ngữ" Eyebrow đã dùng ở Best
   Sellers, chỉ đổi biến thể sáng/tối theo nền). Heading dùng 'Noto Serif' (ĐÃ có sẵn cho
   Hero/Best Sellers, tái dùng — không thêm font mới), 2 dòng: dòng 1 màu `--color-brand-primary`
   (navy, TRÙNG màu chữ đo được #1f232e), dòng 2 accent màu `--color-topbar-bg` (mận, TRÙNG #781035
   đo được) — không còn kiểu chữ trắng viền đỏ/italic/uppercase của bản cũ.
   CHG-030 (2026-08-25, Round 6 — Fluid Typography): CHG-029 sửa ĐÚNG mô hình bề RỘNG cột (grid tỉ
   lệ 340fr/884fr) nhưng bỏ sót TYPOGRAPHY — font-size vẫn neo cứng theo token cố định (đúng số đo
   1366px, không đổi theo viewport), khiến chữ "đứng yên" trong khi cột/Card đã lớn hơn theo tỉ lệ ở
   màn rộng → tạo cảm giác chữ "lọt thỏm" giữa khoảng trống ngày càng lớn quanh nó (Product Owner chỉ
   ra qua screenshot 1920px). Áp dụng "fluid typography" (công thức nội suy tuyến tính theo `vw`,
   dùng `clamp()` để không tăng vượt quá viewport tham chiếu 1920px — không phải thư viện/dependency
   mới, thuần CSS) cho TẤT CẢ font-size trong cột chữ + Countdown: SÀN (`clamp()` giá trị nhỏ nhất) =
   ĐÚNG token cũ = ĐÚNG số đo pixel tại 1366px (không đổi Design Reference đã xác nhận đúng), TRẦN =
   giá trị lớn hơn có chủ đích tại 1920px (viewport Product Owner dùng đối chiếu), nội suy `vw` ở
   giữa cho MỌI viewport 1366–1920 (không chỉ 2 điểm rời rạc). Công thức chung:
   `clamp(MIN, calc(B + A*100vw), MAX)` với `A=(MAX−MIN)/554`, `B=MIN−A*1366` (554 = 1920−1366). */
.block-sale__eyebrow {
    text-transform: uppercase; letter-spacing: 0.1em;
    font-size: clamp(12px, calc(7.07px + 0.361vw), 14px); /* 12px@1366 → 14px@1920 */
    font-weight: var(--font-weight-semibold); color: var(--color-text-muted);
    margin-bottom: var(--space-3);
}
/* F010 (2026-08-26, Re-audit — Typography Prominence, xem docs/ui-qa/sale-summer/AUDIT.md § Round 2):
   CHG-030 neo SÀN 36px dựa trên 1 lần đo trước đó — re-đo bằng PHP GD (đếm mật độ pixel tối theo hàng,
   KHÔNG suy đoán) trên CHÍNH `Desktop - 8.jpg`, hiệu chỉnh chéo bằng Eyebrow (đo lại = 11px ink, đúng
   khớp 12px font-size đã xác nhận đúng → phương pháp đo tin cậy, không có sai số scale toàn cục).
   Heading "Ưu đãi mùa hè" đo được: ink span (đỉnh dấu → baseline) = 39px trên Reference, ĐEM SO SÁNH
   TRỰC TIẾP (không suy đoán) với chính dòng chữ đó trên ảnh chụp live tại 1366px (`reaudit-1366-full.
   png`) = chỉ 26px ink — tỉ lệ 26/39 ≈ 0.67, khớp với cảm nhận thị giác Product Owner "Heading không đủ
   nổi bật". SÀN cũ 36px là số đo SAI (round trước đo nhầm dòng/không hiệu chỉnh chéo) — SÀN đúng ước
   tính ≈ 36×(39/26) ≈ 54px; chọn 44px (tăng thận trọng, xác nhận lại bằng screenshot AFTER thay vì tin
   tuyệt đối 1 phép đo ngoại suy đơn lẻ — đúng nguyên tắc "geometry + visual đều phải PASS"). TRẦN giữ
   nguyên tỉ lệ tăng 1.278× như CHG-030 (36→46) → 44×1.278≈56px. */
.block-sale__heading {
    font-family: 'Noto Serif', var(--font-family-base);
    font-size: clamp(44px, calc(14.41px + 2.166vw), 56px); /* 44px@1366 → 56px@1920, F010 re-measure */
    font-weight: var(--font-weight-regular);
    line-height: 1.15; margin-bottom: var(--space-4);
}
.block-sale__heading-line { display: block; color: var(--color-brand-primary); }
.block-sale__heading-line--accent { color: var(--color-topbar-bg); }
/* CHG-029: bỏ `max-width:320px` cố định — cột chữ giờ co giãn theo tỉ lệ (xem `.block-sale__inner`
   trên), khoá cứng max-width cho riêng đoạn mô tả sẽ để lại khoảng trắng thừa bên phải NGAY TRONG
   cột đã giãn rộng hơn, ngược lại đúng mục tiêu "text có prominence, không lọt thỏm giữa khoảng
   trống". Để đoạn mô tả tự nhiên lấp hết bề rộng cột (2 dòng ở 1366px như đã đo, hơi rộng/ít dòng
   hơn khi cột giãn ở màn lớn — chấp nhận được, không phải lỗi).
   CHG-030: font-size fluid 14px@1366 → 17px@1920 (cùng công thức trên) — trước đây 14px cố định
   ("--font-size-sm") bị Product Owner chê "chữ phụ mờ nhạt" khi đặt cạnh cột đã giãn rộng. */
.block-sale__desc {
    font-size: clamp(14px, calc(6.60px + 0.542vw), 17px);
    color: var(--color-text); margin-bottom: var(--space-6);
}

/* Countdown (CHG-025): đo trực tiếp `Desktop - 8.jpg` — KHÔNG còn Box trắng bo góc như bản cũ
   (koco.jpg), chỉ số to đậm màu mận (`--color-topbar-bg`, TRÙNG #781035 đo được) đặt trực tiếp trên
   nền, Label chữ thường/tối màu (`--color-text`) nằm NGAY DƯỚI số (không phải bên cạnh trong cùng 1
   Box). Digit bbox đo được cao ~28px → font-size gần `--font-size-3xl` (36px, digit không có chân
   xuống nên chiều cao thật < font-size).
   CHG-030: `.sale-countdown__value`/`__sep` fluid 36px@1366→46px@1920 (CÙNG công thức Heading — 2
   nhóm chữ này đã dùng chung 1 kích thước từ CHG-025 để giữ "cùng khối thị giác", giữ nguyên quan hệ
   đó khi scale). `__label` fluid 12px→14px (cùng công thức Eyebrow, cùng vai trò "chữ phụ nhỏ"). */
.sale-countdown { display: flex; align-items: flex-start; gap: 14px; margin-bottom: var(--space-6); }
.sale-countdown__unit { display: flex; flex-direction: column; align-items: center; }
.sale-countdown__value {
    font-family: 'Noto Serif', var(--font-family-base);
    font-size: clamp(36px, calc(11.34px + 1.805vw), 46px);
    font-weight: var(--font-weight-bold);
    color: var(--color-topbar-bg); line-height: 1.1;
}
.sale-countdown__label {
    font-size: clamp(12px, calc(7.07px + 0.361vw), 14px);
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--color-text); margin-top: var(--space-1);
}
.sale-countdown__sep {
    font-family: 'Noto Serif', var(--font-family-base);
    font-size: clamp(36px, calc(11.34px + 1.805vw), 46px);
    font-weight: var(--font-weight-bold);
    color: var(--color-brand-primary); line-height: 1.1; align-self: flex-start;
}

/* CTA — nền mận (`--color-topbar-bg`, TRÙNG màu đo được), KHÔNG phải `.btn--dark` (navy) như bản
   cũ vô tình dùng. Dùng biến thể riêng thay vì sửa `.btn--dark` dùng chung nhiều nơi khác.
   CHG-030: font-size + padding fluid (14px/12px-24px @1366 → 16px/14px-30px @1920, cùng công thức
   nội suy) — override riêng `.block-sale__btn`, KHÔNG sửa `.btn` gốc (dùng chung rất nhiều nơi khác
   trên site) — CTA giờ tăng nhẹ theo cùng nhịp Heading/Countdown thay vì đứng yên khi cột đã giãn,
   tránh cảm giác "quá nhỏ so với phần chữ phía trên" Product Owner lo ngại. */
/* F011 (2026-08-27, Precision Fix — CTA Alignment): `.btn` gốc là `display:inline-flex`, kích
   thước co theo nội dung (`fit-content`), nằm ở mép trái do KHÔNG có wrapper riêng trong
   `block-sale.php` (đứng cùng cấp với heading/desc/countdown, tất cả đều căn trái mặc định của block
   flow) — đúng bản chất "lệch trái" Product Owner thấy trên ảnh chụp thật, KHÔNG phải bug định vị.
   Yêu cầu: CHỈ căn giữa riêng Button trong PHẠM VI cột content (không phải toàn viewport), KHÔNG đổi
   heading/desc/countdown (vẫn phải giữ căn trái — không sửa `.block-sale__col--content`/`.block-sale
   __inner`, tránh kéo theo mọi phần tử khác). Đổi `display:inline-flex`→`flex` (block-level, VẪN giữ
   `align-items:center` kế thừa từ `.btn` để icon/text bên trong Button không đổi) + `width:fit-
   content` (không kéo giãn full-width) + `margin-inline:auto` — margin auto tự căn giữa Button
   trong đúng bề rộng cột cha, hoạt động ĐÚNG ở cả Desktop (`.block-sale__col--content` là 1 ô Grid,
   block flow bình thường) LẪN Tablet/Mobile (`.block-sale__inner` chuyển `display:flex;flex-
   direction:column;align-items:flex-start` — margin auto trên 1 flex-item luôn được ưu tiên hơn
   `align-items` của cha theo đặc tả CSS Flexbox, nên Button vẫn tự căn giữa dù cha đặt `flex-start`
   cho MỌI phần tử khác). KHÔNG đổi màu/typography/border-radius/kích thước padding — chỉ đổi 3
   thuộc tính layout nêu trên. */
.block-sale__btn {
    background: var(--color-topbar-bg); color: #fff;
    text-transform: uppercase; letter-spacing: 0.06em; font-weight: var(--font-weight-semibold);
    font-size: clamp(14px, calc(9.07px + 0.361vw), 16px);
    padding-block: clamp(12px, calc(7.07px + 0.361vw), 14px);
    padding-inline: clamp(24px, calc(9.21px + 1.083vw), 30px);
    display: flex; width: fit-content; margin-inline: auto;
}
.block-sale__btn:hover { opacity: 0.9; }

/* Carousel — CHG-027 (2026-08-25, Round 3): đổi hẳn cách đặt Arrow theo yêu cầu Product Owner mới
   — "Arrow trái nằm ở CẠNH TRÁI vùng Carousel, Arrow phải nằm ở CẠNH PHẢI vùng Carousel" (không còn
   kiểu "breathing space" nổi trong khoảng gap giữa 2 cột như CHG-025/026). Cả 2 nút giờ
   `position:absolute` ĐÈ nhẹ lên mép Card đầu/cuối (nền `rgba(255,255,255,0.85)` bán trong suốt sẵn
   có đủ tương phản, không cần thêm đệm/padding riêng cho Track — đúng pattern carousel phổ biến,
   không đè lên cột chữ vì neo theo `.block-sale__carousel`, KHÔNG theo viewport). `width:100%` giữ
   nguyên (CHG-026, vẫn đúng) để Track lấp hết khung nhìn đã giới hạn 4 Card (`.block-sale__col--
   products`, xem trên) — `overflow-x:auto` kích hoạt cuộn thật cho Card thứ 5 trở đi. */
.block-sale__carousel { position: relative; width: 100%; }
.block-sale__arrow--prev { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); z-index: 2; }
.block-sale__arrow--next { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); z-index: 2; }
.block-sale__products {
    display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x proximity;
    scrollbar-width: none; width: 100%;
}
.block-sale__products::-webkit-scrollbar { display: none; }
/* CHG-029: Card width đổi từ CỐ ĐỊNH `288px` sang responsive `calc((100% - 2×10px) / 3)` — TÁI DÙNG
   ĐÚNG pattern đã có tiền lệ trong chính file này cho Best Sellers
   (`.block-best-sellers__carousel .product-grid .product-card { flex: 0 0 calc((100% - (space-5*3))/4) }`,
   CHG-022), chỉ đổi mẫu số từ 4 xuống 3 (đúng "3 Card/khung nhìn" của thiết kế gốc, xem CHG-028).
   `100%` ở đây là bề rộng CỦA TRACK (`.block-sale__products`, đã `width:100%` so với cột Carousel đã
   giãn theo tỉ lệ ở `.block-sale__inner`) — 3 Card LUÔN lấp vừa khít khung nhìn bất kể cột Carousel
   rộng bao nhiêu (288px đúng tại 1366px vì cột Carousel đúng 884px tại đó; lớn hơn ở màn rộng hơn vì
   cột Carousel cũng giãn theo — xem Evidence CHG-029). Card thứ 4 trở đi vẫn NẰM NGOÀI khung nhìn 3
   Card (vì `flex:0 0 ...` không co lại để nhét thêm Card), chỉ xem được qua cuộn — giữ nguyên đúng
   bản chất carousel đã có từ CHG-025/026, không quay lại lỗi "giãn vô hạn không giới hạn số Card"
   của CHG-027. */
/* F010 (2026-08-26, Re-audit — 3 Card Không Đồng Đều): `getComputedStyle` xác nhận
   `.product-card` đang render `box-sizing: content-box` (KHÔNG PHẢI do rule này — nghi từ CSS lõi
   WooCommerce, root cause chưa xác định, ĐÃ gặp + sửa y hệt 2 lần trước — Best Sellers CHG-034, Header
   Search CHG-050 — luôn sửa cục bộ, KHÔNG sửa global). `flex-basis` tính đúng 289.44px (content-box),
   nhưng padding 16px×2 CỘNG THÊM vào ngoài (đúng hành vi content-box) → width RENDER THẬT = 321.44px/
   card × 3 + 2×10px gap = 983px > 888px khung nhìn Track → Card thứ 3 bị TRÀN/CLIP ở mép phải (không
   phải "Card 3 nhỏ hơn" như cảm nhận ban đầu — Card 3 CÙNG kích thước 321.44px, chỉ là bị viewport cắt
   mất phần vượt quá, tạo ảo giác "không đồng đều"). Ép `border-box` cục bộ để render THẬT khớp con số
   `flex-basis` đã tính đúng — 3 Card sẽ lấp vừa khít 888px như thiết kế, không Card nào bị clip. */
.block-sale__products .product-card { flex: 0 0 calc((100% - (2 * 10px)) / 3); scroll-snap-align: start; box-sizing: border-box; }
/* CHG-025: đổi style Card từ 'light' (nền trắng, bản cũ) sang 'dark' (tái dùng NGUYÊN component đã
   xây cho Best Sellers — CHG-022) — nhưng nền đo được ở đây là `--color-brand-primary` (#1f232e),
   KHÁC màu nền dùng cho Best Sellers (`--color-footer-lower-bg` #141820) — override riêng bằng
   selector `.block-sale__products .product-card--dark`, KHÔNG đổi giá trị mặc định của
   `.product-card--dark` (vẫn đúng cho Best Sellers, xem style.css dòng ~660). */
.block-sale__products .product-card--dark { background: var(--color-brand-primary); }
.block-sale__products .product-card__thumb { aspect-ratio: 1 / 1.08; }

/* CHG-031 (2026-08-25, Round 7 — Final Polish, đo lại `Desktop - 8.jpg` bằng PHP GD, crop+zoom card
   Flash Sale x=440–745,y=2205–2660): 2 phát hiện quan trọng, KHÔNG suy đoán:
   1) Ảnh sản phẩm trong ảnh gốc (Earthniq demo) KHÔNG có light-container riêng — nền tối Card lộ
      thẳng quanh ảnh — NHƯNG đó là vì ảnh demo gốc CÓ nền tối/trong suốt tương thích sẵn. Ảnh SẢN
      PHẨM THẬT đang dùng ở Campaign này (Obagi/Image Skincare/Chanel — `design/assets/products/
      *.png`) đã kiểm tra bằng PHP GD: `imagecolorat()` tại 4 góc = `rgb(255,255,255) alpha=0` (nền
      TRẮNG ĐẶC, KHÔNG trong suốt) — chỉ riêng La Prairie có alpha thật (127=trong suốt hoàn toàn).
      KHÔNG tự Photoshop/cắt nền các file PNG đặc (rủi ro viền/halo giả nếu tự động hoá thiếu chính
      xác, đúng cảnh báo Product Owner) — dùng Option 2 (image container màu nền GẦN VỚI ảnh, không
      phải màu tương phản) để nền trắng "hoà vào" thay vì lộ thành 1 miếng trắng thô trên nền tối.
   2) Product title trong ảnh gốc dùng font SERIF (cùng họ 'Noto Serif' đã dùng cho Heading/Countdown
      của Block này — không phải sans-serif dùng chung ở Best Sellers/New Products) — đo màu chữ
      `#f7f3e7`/`#f8f5e6` TRÙNG `--color-text-on-dark` (#f4ede7, đã đúng qua kế thừa, không cần đổi
      màu). Category "Chăm sóc da" đo màu `#766740` — vàng mận nhạt, KHÔNG PHẢI xám trung tính
      (`--color-text-muted` #6b6b6b đang dùng qua kế thừa, SAI màu) — dùng lại `--color-brand-accent`
      giảm opacity thay vì thêm hex mới.
   TOÀN BỘ override dưới đây SCOPE dưới `.block-sale__products` — KHÔNG sửa `.product-card__thumb-
   link`/`.product-card__title`/`.product-card__cat` gốc (dùng chung Best Sellers/New Products/Shop/
   Search/Related), tránh đúng rủi ro Product Owner cảnh báo. */
/* F010 (2026-08-26, Re-audit — Badge/Wishlist đè lên khối ảnh): pixel scan xác nhận đáy Badge
   (`.thekoco-badge`, `top:var(--space-3)`=12px + cao ~30px → đáy ở y≈42px, đo trong `.product-card__
   thumb`) và mép TRÊN khối ảnh trắng (`thumb-link`, `inset:var(--space-3)`=12px, CÙNG hệ toạ độ vì cả
   2 đều position:absolute trực tiếp trong `.product-card__thumb`) chạm nhau — quét màu ngang y=44→46
   KHÔNG có khoảng nền tối nào xen giữa (Badge cream → Box cream liền mạch), xác nhận CHỒNG ĐÈ THẬT,
   không phải ảo giác. Tăng RIÊNG inset top lên 48px (đủ chứa Badge 12+~30=42px + đệm thở ~6px) — Heart
   (`top:var(--space-3)`, cùng offset) hưởng lợi cùng lúc vì chỉ đổi 1 giá trị top của `inset` shorthand
   3-value (top | trái-phải | dưới), 3 cạnh còn lại giữ nguyên 12px. Không hardcode theo từng Card —
   Badge/Heart luôn định vị TƯƠNG ĐỐI so với `.product-card__thumb` nên áp dụng đều cho mọi Card bất kể
   ảnh sản phẩm khác tỉ lệ. Hệ quả phụ: khối ảnh nhỏ lại tương ứng (cao hơn 36px ở đáy trên so với
   trước) → cũng góp phần giảm cảm giác "ảnh quá to" (Vấn đề #4) mà không cần đổi riêng `img` width. */
.block-sale__products .product-card__thumb-link {
    inset: 48px var(--space-3) var(--space-3);
    background: var(--color-bg-subtle);
    border-radius: var(--radius-base);
}
.block-sale__products .product-card__thumb img { width: 75%; height: 75%; }

/* F010 (2026-08-26, Re-audit — Vấn đề #5, Product Info Hierarchy): Reference xác nhận Brand+Rating
   CÙNG HÀNG (Brand trái, Rating phải) — render hiện tại xếp Rating RIÊNG 1 hàng, NẰM DƯỚI Category
   (Brand→Title→Category→Rating→Price), sai thứ tự yêu cầu. `.block-best-sellers` đã có sẵn đúng pattern
   này (CHG-041, `grid-template-areas` đặt lại VỊ TRÍ HIỂN THỊ mà KHÔNG đổi DOM order trong `product-
   card.php` — component dùng chung Best Sellers/Sale/New Products/Shop/Search/Related). Tái dùng ĐÚNG
   kỹ thuật, re-scope cho `.block-sale__products` — KHÔNG sửa PHP, KHÔNG sửa rule gốc `.product-card__
   body` (dùng chung). KHÁC Best Sellers: `.product-card--dark` mặc định của Flash Sale (style.css dòng
   ~1016) KHÔNG có `display:flex;flex-direction:column` (rule đó chỉ scope `.block-best-sellers
   .product-card--dark`, xem CHG-036/041) nên KHÔNG thêm `flex:1 1 auto` ở đây (không có tác dụng ngoài
   flex container, đúng đặc tả CSS) — hàng "spacer" `1fr` co về 0 tự nhiên vì Body ở đây là auto-height
   thường (không bị stretch cao hơn nội dung), Price nằm ngay sau Category — không có seam màu nền lệch
   như Best Sellers từng gặp vì `.block-sale__products .product-card__body` không có màu nền riêng.
   Phát hiện thêm khi regression-test ở 390px: cột `1fr` (Brand) của Grid có `min-width:auto` ngầm định
   (đặc tính CSS Grid track, giống hệt lỗi `min-width:auto` của Flex item) — Brand dài ("Image Skincare")
   không co được xuống dưới độ rộng nội dung của chính nó, đẩy cả Body/Card RỘNG HƠN `flex-basis:168px`
   đã định (đo được 179px thay vì 168px — TÁI HIỆN đúng lớp lỗi "3 Card không đồng đều" ở quy mô Mobile,
   dù nguyên nhân khác desktop). Sửa bằng `min-width:0` + `text-overflow:ellipsis` trên `.product-card__
   brand` (xem rule ngay dưới) — Brand quá dài sẽ cắt "…" gọn gàng thay vì đẩy rộng Card. */
.block-sale__products .product-card__body {
    padding-top: 10px;
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto auto 1fr auto;
    column-gap: var(--space-3);
    grid-template-areas:
        "brand rating"
        "title title"
        "cat   cat"
        "spacer spacer"
        "price price";
}
.block-sale__products .product-card__brand {
    grid-area: brand; align-self: center; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    margin-bottom: 0;
    font-size: clamp(13px, calc(8.07px + 0.361vw), 15px); /* 13px@1366 → 15px@1920, cùng công thức fluid CHG-030 */
}
.block-sale__products .product-card__title {
    grid-area: title;
    font-family: 'Noto Serif', var(--font-family-base);
    font-size: clamp(15px, calc(7.60px + 0.542vw), 18px);
    margin-bottom: 8px;
}
.block-sale__products .product-card__cat {
    grid-area: cat;
    color: var(--color-brand-accent); opacity: 0.7;
    font-size: clamp(13px, calc(8.07px + 0.361vw), 15px);
    margin-bottom: 8px;
}
.block-sale__products .product-card__rating { grid-area: rating; justify-self: end; align-self: center; margin-bottom: 0; }
.block-sale__products .product-card__rating-stars,
.block-sale__products .product-card__rating-count { font-size: clamp(13px, calc(8.07px + 0.361vw), 15px); }
.block-sale__products .product-card__price { grid-area: price; }
/* F008 (2026-08-26, "Ưu đãi mùa hè" Final Pixel Audit — xem docs/ui-qa/sale-summer/AUDIT.md): đo lại
   trực tiếp trên `Desktop - 8.jpg` (zoom vùng giá Card 1) — reference hiển thị GIÁ MỚI (vàng, đậm)
   TRƯỚC, GIÁ CŨ (xám, gạch ngang) SAU, KHÔNG có % giảm giá dạng chữ inline nào cạnh giá (chỉ có badge
   góc ảnh "-10%"). Live hiện đang NGƯỢC (giá cũ trước, giá mới sau — đúng thứ tự DOM gốc của
   `product-card.php`, dùng chung) VÀ có thêm text "-42%" (etc.) chen giữa — TRÙNG lỗi đã tìm thấy và
   sửa cho Best Sellers (`.block-best-sellers .product-card__price-new{order:1}` v.v., xem CHG-044/
   Round 9/10) nhưng round đó chỉ scope `.block-best-sellers`, CHƯA áp dụng cho `.block-sale`. Áp
   dụng ĐÚNG pattern đã có sẵn (không tạo cách mới), scope riêng `.block-sale__products` — KHÔNG sửa
   `product-card.php`/CSS gốc `.product-card__price*` (dùng chung New Products/Search/Related/...). */
.block-sale__products .product-card__price-new { font-size: clamp(16px, calc(8.60px + 0.542vw), 19px); order: 1; }
.block-sale__products .product-card__price-old { font-size: clamp(13px, calc(8.07px + 0.361vw), 15px); order: 2; }
.block-sale__products .product-card__price-discount { display: none; }
.block-sale__arrow {
    flex-shrink: 0; width: 36px; height: 36px; border-radius: var(--radius-full); border: 0;
    background: rgba(255,255,255,0.85); color: var(--color-brand-primary); font-size: var(--font-size-xl);
    cursor: pointer; display: grid; place-items: center; transition: background var(--duration-base) var(--easing-standard);
}
.block-sale__arrow:hover { background: #fff; }

/* Ảnh banner riêng: hiện ở Tablet (≤1024px, aspect-ratio 21/9) và Mobile (≤768px, 16/9) — Desktop ẩn
   vì ảnh đã là background của section (xem 2 media query bên dưới).
   F009 (2026-08-26, Final Verification — xem docs/ui-qa/sale-summer/AUDIT.md): đo được `<img>` bên
   trong `.block-sale__banner` (`<picture>`) chưa có `width`/`object-fit` nào — browser mặc định
   render đúng KÍCH THƯỚC GỐC của file (`sale-lifestyle.webp`, 466×569px thật), không co giãn theo
   khung `.block-sale__banner` đã có `aspect-ratio` — tại 768px/1024px, ảnh chỉ lấp 466px trong khung
   rộng 768-1024px (61-46%), để lộ khoảng trống nền kem lớn bên phải (screenshot xác nhận). Ở 390px
   không bị lộ khoảng trống (390px < 466px tự nhiên nên `max-width:100%` sẵn có của theme reset đã ép
   vừa khít, đúng tỉ lệ gốc, KHÔNG méo) — đây là lý do hiện tượng chỉ thấy rõ ở Tablet, không phải
   Mobile. Thêm `width:100%; height:100%; object-fit:cover;` để ảnh LUÔN lấp đúng khung đã định sẵn
   (aspect-ratio khung ≠ tỉ lệ ảnh gốc portrait 466:569, nên cần `cover` để không méo hình, giống hệt
   cách `background-size:cover` đã dùng cho ảnh nền Desktop ngay phía trên). KHÔNG đổi `aspect-ratio`
   của khung (giữ nguyên 21/9 Tablet, 16/9 Mobile, đã có từ trước) — chỉ sửa đúng `<img>` con để nó
   thật sự lấp đầy khung đã định, không đổi kiến trúc/breakpoint. */
.block-sale__banner { display: none; }
.block-sale__banner img { width: 100%; height: 100%; object-fit: cover; }

/* ---------- Promo banners / "Thương hiệu của KOCO" (CHG-050, 2026-08-27 — Audit & Rebuild) ----------
   Đo trực tiếp `design/23-8/trang chu/Desktop - 8.jpg` (khớp pixel node Figma Homepage `1992:252`,
   xem docs/DESIGN_SPECIFICATION.md + docs/ui-qa/promo-banners/AUDIT.md cho evidence đầy đủ):
   - Section bg quét dọc x=5 hội tụ rgb(31,35,46) = ĐÚNG `--color-brand-primary` (#1f232e), bắt đầu
     ngay sau Flash Sale (y=2814, khớp mốc block-sale.php đã ghi "y=2110→2814").
   - Eyebrow "THƯƠNG HIỆU CỦA KOCO" bắt đầu y≈2917, tức padding-block-start ≈103px (dùng tròn 100px).
   - 3 Card đo được: x=[35,449]/[477,891]/[921,1334] (viewport 1366px) → card rộng ~414px, gap
     28-30px giữa các card, lề trái/phải section ~33-35px — khớp GẦN CHÍNH XÁC pattern override
     `.container{padding-inline:34px}` Best Sellers đã dùng (1366-68=1298 ≈ 1298-1299 đo được) →
     tái dùng override đó thay vì bịa số mới.
   - Card cao đo được y=[3104,3575] → 414×471, khớp gần đúng size asset MỚI 414×473 (image 387/388.png
     + z7954...1.png, xem seed-data.php CHG-050) → đổi aspect-ratio theo asset mới, KHÔNG còn dùng
     452×485 (asset CŨ — ảnh người mẫu, sai thiết kế, đã bị thay thế).

   CHG-051 (2026-08-27, Final Visual Match — Round 2, xem docs/CHANGE_LOG.md): CHG-050 pass tất cả
   DOM/CSS measurement nhưng CHƯA được đối chiếu visual thật — audit lại phát hiện heading→cards
   TOÀN SECTION đo thiếu 31px, bằng chứng ĐỘC LẬP với đo font-ink (2 cạnh cứng, không phụ thuộc
   font metric):
     - Section bg top (y=2814, hard edge màu) → Card top (y=3104, hard edge ảnh) = 290px ĐO ĐƯỢC.
     - Tổng đã implement trước Round này (100 padding-start + eyebrow-box + 16 margin + heading-box
       + 64 margin-bottom) = 259px đo qua `getBoundingClientRect` — THIẾU đúng 31px so với 290px.
     - Không thể tách chính xác 31px này cho riêng eyebrow-gap hay heading-gap bằng đo ink (test
       calibration trên chính Best Sellers — nơi CÓ số liệu EXACT từ Figma get_metadata — cho thấy
       ink overshoot ±7-9px không đủ ổn định để chia nhỏ). Áp dụng TOÀN BỘ 31px vào ĐÚNG 1 chỗ có
       bằng chứng ink-gap rõ nhất + xác nhận bằng crop trực quan (khoảng trống thấy rõ bằng mắt,
       xem docs/ui-qa/promo-banners/_verify-heading-to-card-gap.png): heading→cards, 64px→96px
       (259+32=291≈290, sai số 1px chấp nhận được do làm tròn). Đây là MINIMAL FIX — chỉ 1 giá trị
       đổi, không đụng padding-block-start/eyebrow margin (trong ngưỡng sai số đo ink, không đủ bằng
       chứng để sửa, theo đúng "không hardcode nếu không cần thiết"). */
/* CHG-052 (2026-08-27, Typography/Scale/Admin Audit, xem docs/CHANGE_LOG.md):
   1) FONT-FAMILY BUG — block này chưa từng khai báo `font-family: 'Be Vietnam Pro'` ở gốc, khác
      MỌI block "body text" khác cùng file (`.block-best-sellers { font-family: 'Be Vietnam Pro',
      var(--font-family-base) }` dòng ~171). Hệ quả: eyebrow/subtitle/CTA (không có font-family
      riêng) fallback thẳng về `var(--font-family-base)` = Helvetica Neue — SAI hoàn toàn so với
      thiết kế (xác nhận qua `getComputedStyle` live: fontFamily trả về "Helvetica Neue..." thay vì
      Be Vietnam Pro đã tải sẵn qua @import đầu file). Đây là nguyên nhân chính khiến typography
      "yếu hơn design" — Helvetica Neue là fallback web-safe generic, không phải font chủ đích.
   2) CARD SCALE KHÔNG CÓ CẬN TRÊN — `max-width: none` copy nguyên từ Best Sellers (nơi card có
      width CỐ ĐỊNH theo px trong 1 carousel overflow-scroll, "none" chỉ để container không bị cắt
      bớt track) nhưng áp dụng SAI ngữ cảnh cho `.block-promo-banners__grid` (Grid `repeat(3,1fr)`
      — cột 1fr chia ĐỀU không gian container, nên "max-width:none" khiến card PHÌNH TO KHÔNG GIỚI
      HẠN theo viewport: đo được 409px@1366 → 434px@1440 → 487px@1600 → 594px@1920 (+45% trong khi
      viewport chỉ +41%, tăng nhanh hơn cả viewport vì gap/padding cố định chiếm tỷ lệ nhỏ dần).
      Product Owner xác nhận bằng mắt "card lớn hơn reference" ở màn rộng — khớp đúng root cause kỹ
      thuật này. Fix ROUND 1: BỎ `max-width: none`, cho kế thừa `max-width: 1320px` gốc của
      `.container`.

   CHG-053 (2026-08-27, Final Visual Correction — REVERT quyết định #2 của CHG-052, xem
   docs/CHANGE_LOG.md): Product Owner xem trên browser thật, xác nhận `max-width:1320px` ở màn rộng
   khiến 3 Card bị "lọt thỏm" giữa 1 nền tối rộng — ĐỔI BẢN CHẤT composition từ "Full-width Editorial
   Grid" (đúng thiết kế: card trải gần hết viewport, chỉ chừa lề 34px) thành "Centered Boxed
   Container" (card đóng khung nhỏ giữa nền tối, để lộ khoảng trống 2 bên rất lớn ở màn ≥1600px —
   nhìn "trống"/"lạc lõng", KHÔNG phải diện mạo editorial thiết kế hướng tới). CHG-052 đúng về mặt
   SỐ ĐO (409→594px, +45%) nhưng SAI về đánh giá — "card to hơn ở màn rộng" là ĐẶC ĐIỂM CỐ Ý của
   Full-width Editorial Grid (ảnh lớn dần theo khung hình rộng dần), không phải bug. Component gây
   "lọt thỏm" không phải do Card to — mà do ÁP MỘT KHUNG max-width lên 1 Section vốn được thiết kế
   để trải ngang. REVERT: `max-width: 1320px` → `max-width: none` (khôi phục nguyên trạng trước
   CHG-052 cho property này) — Card lại fluid-scale tự nhiên theo Grid `1fr`, 594px@1920px KHÔNG còn
   bị coi là lỗi (xem bảng Proportional Composition trong CHANGE_LOG.md § CHG-053). */
.block-promo-banners { background: var(--color-brand-primary); padding-block: 100px; font-family: 'Be Vietnam Pro', var(--font-family-base); }
.block-promo-banners .container { max-width: none; padding-inline: 34px; }
.block-promo-banners .section-eyebrow { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); letter-spacing: 0.15em; margin-bottom: 16px; color: #b7b3a8; }
.block-promo-banners .section-heading {
    display: flex; align-items: center; justify-content: center; gap: var(--space-4);
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: var(--font-size-4xl); font-weight: var(--font-weight-regular);
    letter-spacing: normal; line-height: var(--line-height-tight); margin-bottom: 96px;
}
.block-promo-banners .section-heading__dash { width: 40px; height: 2px; background: var(--color-brand-accent); flex-shrink: 0; font-size: 0; }

.block-promo-banners__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; }
.promo-banner { position: relative; display: block; aspect-ratio: 414 / 473; overflow: hidden; border-radius: var(--radius-base); }
.promo-banner img { width: 100%; height: 100%; object-fit: cover; object-position: center; transition: transform var(--duration-slow) var(--easing-standard); }
/* Polish (2026-08-01, giữ nguyên từ bản trước): scale nhẹ + overlay đen mờ khi hover, cảm giác
   cao cấp hơn, không đổi hành vi UX. */
.promo-banner:hover img { transform: scale(1.015); }
.promo-banner::after {
    content: ""; position: absolute; inset: 0; background: rgba(0, 0, 0, 0.04);
    opacity: 0; transition: opacity var(--duration-slow) var(--easing-standard); pointer-events: none;
}
.promo-banner:hover::after { opacity: 1; }

/* Content overlay: intro (title+tagline+divider) neo TRÊN, CTA neo DƯỚI — dùng flex space-between
   thay vì 2 khối position:absolute rời rạc (Card chỉ có 1 vùng nội dung logic, không phải 2 layer
   độc lập — đúng tinh thần "không position:absolute hack" đã áp dụng ở Best Sellers CHG-049). */
/* CHG-051: padding 24px→28px (đo ink title-top/CTA-bottom hội tụ ~30-31px cả 2 phía, đối xứng —
   28px là giá trị bảo thủ hơn con số đo thô để trừ hao overshoot ink, tái dùng chính giá trị 28px
   đã dùng cho `.block-promo-banners__grid` gap ở trên, không thêm hằng số mới ngoài block này). */
.promo-banner__content {
    position: absolute; inset: 0; padding: 28px;
    display: flex; flex-direction: column; justify-content: space-between;
    pointer-events: none;
}
.promo-banner__intro { display: flex; flex-direction: column; }
/* CHG-053 (Typography — visual-weight re-audit, xem docs/CHANGE_LOG.md): CHG-050/052 dùng
   `--font-size-2xl` (28px) cho Title dựa trên đo cap-height ink "VEON" (21px) → ước tính font-size
   thật ~30px (21÷0.7, tỉ lệ cap-height chuẩn của Playfair Display) — 28px NẰM TRONG sai số nhưng ở
   biên dưới. Product Owner xem browser thật, xác nhận Title vẫn chưa đủ nổi bật để nhận diện brand
   ngay — bump lên 32px (giữa 2 token 2xl/28 và 3xl/36, LỆCH VỀ PHÍA số đo ~30px, không nhảy hẳn lên
   36px vì không có bằng chứng đo được đến mức đó). Zoom 4x glyph "VEON" xác nhận stroke Playfair
   Display Regular (mảnh-dày tương phản kiểu Didone) — GIỮ weight 400, không phải bold (đổi weight
   sẽ làm mất đúng "editorial serif" character của thiết kế, chỉ đổi size). */
.promo-banner__title {
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: 32px; font-weight: var(--font-weight-regular);
    text-transform: uppercase; letter-spacing: 0.04em; line-height: 1.2;
}
/* CHG-053: Subtitle 14px (--font-size-sm) đo ink cap 16px (đã gồm dấu tiếng Việt "ố/ỏ") — số đo
   không rõ ràng chứng minh sai, nhưng Product Owner xem browser thật thấy Tagline "trông như
   metadata nhỏ" — bump lên `--font-size-base` (16px, token có sẵn, không bịa số) để tăng độ đọc rõ.
   CHG-054 (2026-08-27, Micro Typography Fix, xem docs/CHANGE_LOG.md): sau CHG-053, Product Owner
   xem screenshot browser thật — 16px vẫn "mảnh/nhạt" vì weight vẫn 400 (regular, kế thừa từ
   `.block-promo-banners { font-family: 'Be Vietnam Pro' }` không có weight riêng) trong khi CTA
   cùng size 16px nhưng weight 600 — 2 dòng cạnh nhau (Subtitle/CTA) lệch visual weight rõ. Đổi
   weight 400→600 (`--font-weight-semibold`, ĐÚNG giá trị CTA đang dùng — không bịa số mới, chủ động
   khớp CTA theo đúng yêu cầu "subtitle gần visual weight CTA"). CHỈ đổi property này — không đổi
   size/line-height/margin/color/position (đã đúng, không có bằng chứng cần sửa thêm). */
.promo-banner__subtitle { display: block; font-style: normal; font-size: var(--font-size-base); font-weight: var(--font-weight-semibold); line-height: 1.4; margin-top: var(--space-2); }
/* CHG-051: divider width đo được 41px (quét pixel y=3208, x=59-100) — làm tròn 40px, TRÙNG giá trị
   `.section-heading__dash` đã dùng trong CHÍNH block này (dòng phía trên) — không phải trùng hợp,
   tái dùng đúng 1 giá trị đã có, không thêm hằng số mới. */
.promo-banner__divider { display: block; width: 40px; height: 1px; background: currentColor; opacity: 0.7; margin-top: var(--space-3); }
/* CHG-053: CTA đổi 14px/500 → 16px (`--font-size-base`, khớp Subtitle, cùng "hàng" thị giác) /600
   (semibold, đủ trọng lượng để đọc như 1 interactive element rõ ràng — không chỉ là text phụ) —
   Product Owner: "CTA không được nhỏ đến mức gần như biến mất ở đáy card". */
.promo-banner__cta { font-size: var(--font-size-base); font-weight: var(--font-weight-semibold); }

/* Text theme per-card (CHG-050): đo pixel hội tụ #1f232e (=--color-brand-primary) cho nền sáng
   Veon/Lumi, #fff cho nền đỏ đậm Earthniq — KHÔNG suy đoán, 2 giá trị đo trực tiếp trên ảnh gốc. */
.promo-banner--dark .promo-banner__content { color: var(--color-brand-primary); }
.promo-banner--light .promo-banner__content { color: #fff; }

/* ---------- New products (Sprint UI-08, Review 2 — Visual Hierarchy Fix) ----------
   Đo trực tiếp từ koco.jpg: KHÔNG phải Grid tĩnh 3 cột — là Carousel (Intro trái cố định + Product
   cuộn ngang qua mũi tên), Card so le vị trí dọc (zigzag: card thứ 2/4/6... cao hơn card 1/3/5...
   ~57px). Background = đúng file Designer `design/san_pham/bg_san_pham.jpg`.

   Root cause Review 2 (không phải lỗi CSS lẻ — lỗi ngân sách chiều rộng):
   Intro(338px) + gap(24) + 3×Card(300px) + 2×gap(24) = 1310px, TRONG KHI khung nội dung khả dụng
   ở 1366px viewport chỉ có 1272px (1320 max-width `.container` trừ 2×24px padding-inline) — thiếu
   38px. Card 3 bị `overflow-x:auto` của track che mất ~38px bên phải — `getBoundingClientRect` xác
   nhận width vẫn đúng 300px (KHÔNG phải bug flex-shrink), chỉ là bị occlude — nhưng thị giác vẫn
   đọc là "Card 3 nhỏ hơn". Sửa: giảm nhẹ Intro (338→320px) và Card (300→292px), tăng gap Intro→
   Carousel (24→32px, đúng góp ý "breathing space") — tổng 320+32+3×292+2×20=1268px, vừa khít trong
   1272px, không Card nào bị cắt. Đồng thời giảm `padding-block` (90→60px) để chiều cao Section gần
   với chiều cao gốc ảnh nền (545px) hơn — trước đó Section cao 672px buộc `background-size:cover`
   phải phóng to ảnh nền quá mức (672/545=1.23×) mới lấp đầy, đúng lỗi "Background zoom quá lớn". */
/* ---------- New Products (CHG-056, 2026-08-27 — Structural Redesign, xem docs/CHANGE_LOG.md +
   docs/ui-qa/new-products/AUDIT.md cho evidence đầy đủ) ----------
   Đo trực tiếp `design/23-8/trang chu/Desktop - 8.jpg`. Bỏ HẲN layout cũ (intro column 320px +
   carousel cuộn ngang + zigzag) — thiết kế mới là section-header căn giữa (tái dùng nguyên
   `.section-eyebrow`/`.section-heading` đã dùng ở Best Sellers/Promo Banners) phía trên 1 GRID TĨNH
   4 cột cùng hàng, CTA "XEM TẤT CẢ" (pill viền) căn giữa bên dưới.
   Bỏ `background-image: bg-new-products.jpg` — thiết kế mới không thể hiện texture nền (AUDIT.md
   §12/§22), giữ flat `--color-brand-primary` như mọi section tối khác. `padding-block` GIỮ NGUYÊN
   60px (không có bằng chứng pixel để đổi — không tìm được ranh giới màu giữa Section này và Promo
   Banners phía trên, cả 2 cùng nền #1F232E liền mạch, xem AUDIT.md §4A). */
/* CHG-056: `font-family: 'Be Vietnam Pro'` ở gốc — bài học trực tiếp từ CHG-052 (Promo Banners mắc
   đúng lỗi này: thiếu dòng này khiến eyebrow/brand/category/price/CTA fallback về Helvetica Neue
   thay vì Be Vietnam Pro đã tải sẵn qua @import). Áp dụng ngay từ đầu cho New Products, không đợi
   phát hiện qua Product Owner lần nữa. */
.block-new-products { background-color: var(--color-brand-primary); padding-block: 60px; font-family: 'Be Vietnam Pro', var(--font-family-base); }
.block-new-products .container { max-width: none; padding-inline: 34px; }
.block-new-products .section-eyebrow { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); letter-spacing: 0.15em; margin-bottom: 16px; color: #b7b3a8; }
/* Heading→Card gap đo được 71px (y=3854 card top − y=3783 heading ink bottom, xem AUDIT.md §13) —
   làm tròn 72px. Family/size/color TÁI DÙNG nguyên `.block-best-sellers .section-heading`/
   `.block-promo-banners .section-heading` (cùng 1 pattern site-wide, không đo lại từ đầu). */
.block-new-products .section-heading {
    display: flex; align-items: center; justify-content: center; gap: var(--space-4);
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: var(--font-size-4xl); font-weight: var(--font-weight-regular);
    letter-spacing: normal; line-height: var(--line-height-tight); margin-bottom: 72px;
}
.block-new-products .section-heading__dash { width: 40px; height: 2px; background: var(--color-brand-accent); flex-shrink: 0; font-size: 0; }

/* Grid 4 cột FLUID (repeat(4, minmax(0,1fr))) — KHÔNG max-width cố định trên `.container` (bài học
   trực tiếp từ CHG-052→053 ở Promo Banners: áp max-width khiến Block "lọt thỏm" ở màn rộng, đã bị
   Product Owner revert). Card 304px/gap 27px/lề 34-35px CHỈ đúng tại reference 1366px — ở viewport
   rộng hơn, card tự giãn theo đúng tỷ lệ 4 cột đều, không bị chặn cứng. */
.block-new-products__grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 27px; }

/* Card dùng chung `style:'dark'` — TÁI DÙNG hầu hết giá trị đã đo/duyệt cho `.block-best-sellers
   .product-card--dark` (CHG-032→049): AUDIT.md §5/§10 xác nhận card 2 tông trong thiết kế mới của
   New Products (~304×508px, ảnh gần vuông ~304×309px, nền body #2c3144) khớp GẦN NHƯ TRÙNG với Best
   Sellers (306×310px, #2c3343) — cùng 1 "họ" Card thị giác, không phải trùng hợp. Toàn bộ rule dưới
   đây COPY NGUYÊN giá trị Best Sellers, scope riêng `.block-new-products` (KHÔNG sửa base
   `.product-card`/`.block-best-sellers` — đúng kỷ luật shared-component đã áp dụng xuyên suốt
   CHG-050→055). */
.block-new-products .product-card--dark {
    box-sizing: border-box;
    border: 1px solid #2d3146;
    border-radius: 6px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    background: #2c3343;
}
.block-new-products .product-card--dark .product-card__thumb {
    flex: 0 0 auto;
    background: var(--color-footer-lower-bg);
    margin: calc(var(--space-4) * -1) calc(var(--space-4) * -1) 0;
    padding: var(--space-4) var(--space-4) 0;
    border-radius: 0 0 6px 6px;
}
.block-new-products .product-card--dark .product-card__thumb img { width: 70%; height: 70%; border-radius: 6px; }
.block-new-products .product-card--dark .product-card__body {
    background: #2c3343;
    margin: 0 calc(var(--space-4) * -1) calc(var(--space-4) * -1);
    padding: var(--space-5) var(--space-4) var(--space-4);
    flex: 1 1 auto;
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto auto 1fr auto;
    column-gap: var(--space-3);
    grid-template-areas:
        "brand rating"
        "title title"
        "cat   cat"
        "spacer spacer"
        "price price";
}
.block-new-products .product-card__brand {
    grid-area: brand; align-self: center; margin-bottom: 0;
    font-size: 15px; font-style: normal; text-transform: uppercase; letter-spacing: 0.04em;
    color: #b7b3a8;
}
.block-new-products .product-card__rating { grid-area: rating; justify-self: end; align-self: center; margin-bottom: 0; }
.block-new-products .product-card__rating-stars { font-size: 14px; }
.block-new-products .product-card__rating-count { font-size: 13px; }
/* CHG-057: font-size 22px(`--font-size-xl`)→18px(`--font-size-lg`) — đồng bộ với Best Sellers, xem
   comment đầy đủ tại `.block-best-sellers .product-card__title`. */
.block-new-products .product-card__title {
    grid-area: title;
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: var(--font-size-lg); line-height: 1.25;
}
.block-new-products .product-card__cat { grid-area: cat; color: #786d49; font-size: 14px; }
.block-new-products .product-card__price { grid-area: price; }
.block-new-products .product-card__price-new { font-size: 20px; order: 1; }
.block-new-products .product-card__price-old { font-size: 15px; order: 2; }
/* "-X%" đã hiện dạng Badge overlay trên ảnh (show_discount_badge) — ẩn lặp lại trong hàng giá,
   cùng lý do CHG-044 đã áp dụng cho Best Sellers. */
.block-new-products .product-card__price-discount { display: none; }

/* CTA "XEM TẤT CẢ" — pill viền, không mũi tên, căn giữa dưới Grid (AUDIT.md §20/§21: card bottom
   y=4362, CTA vùng y≈4400-4460 → khoảng cách ~40-95px, dùng 48px là điểm giữa hợp lý). One-off,
   scope thẳng trong block này (YAGNI — chưa có bằng chứng cần 1 class `.btn--outline` dùng chung). */
.block-new-products__cta { display: flex; justify-content: center; margin-top: 48px; }
.block-new-products__cta-btn {
    display: inline-flex; align-items: center; justify-content: center;
    padding: 14px 36px;
    border: 1px solid rgba(244, 237, 231, 0.35);
    border-radius: var(--radius-full);
    background: transparent;
    color: var(--color-text-on-dark);
    font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold);
    text-transform: uppercase; letter-spacing: 0.08em;
    transition: border-color var(--duration-base) var(--easing-standard), color var(--duration-base) var(--easing-standard);
}
.block-new-products__cta-btn:hover { border-color: var(--color-brand-accent); color: var(--color-brand-accent); }

/* ---------- Trend / Topic Recommendation (ADR-018, 2026-08-27) ----------
   "Chủ đề xu hướng" — module content-orchestration MỚI (Campaign/Topic quản trị qua wp-admin, xem
   framework/marketing/src/TrendRepository.php), KHÔNG phải block "Bảo vệ sức khỏe" hardcode. CSS
   này đo trực tiếp `design/23-8/trang chu/Desktop - 8.jpg` (vùng y≈4480-5050, section hiện tại
   đang hiển thị Campaign "Bảo vệ sức khỏe" — nội dung ĐẦU TIÊN dùng cơ chế này, không phải điểm
   neo thiết kế cố định). Layout 2 cột: trái = editorial (eyebrow/heading/mô tả/highlight box),
   phải = panel danh sách Topic. Đặc biệt: eyebrow/heading của Block NÀY đo được KHÁC hẳn quy ước
   sitewide (eyebrow gold #D6B035 thay vì xám #b7b3a8, heading trắng-kem thay vì gold, căn TRÁI
   không phải giữa, KHÔNG có decorative dash) — đây là khác biệt CÓ THẬT của thiết kế, không phải
   nhầm lẫn khi implement, nên KHÔNG tái dùng `.section-heading` (dash pattern) cho Heading, chỉ
   tái dùng `.section-eyebrow` cho phần eyebrow (đã đúng token gold sẵn có). */
/* CHG-060 (2026-08-27, Trend Layout Correction — Visual Re-Audit): CHG-058/059 làm ĐÚNG kiến trúc
   (Campaign/Topic/Admin/Taxonomy) nhưng SAI layout frontend — Product Owner review screenshot thật,
   chỉ ra 3 lỗi thị giác. Đo lại TRỰC TIẾP bằng Python/Pillow trên `Desktop - 8.jpg` (không suy đoán):

   1) SECTION BỊ CO — `.block-trend` trước đó tự thân đã full-bleed (không max-width), nhưng
      `.block-trend .container` KHÔNG có override `max-width:none` nên kế thừa `.container` gốc
      (`max-width:1320px; padding-inline:24px`) — ở viewport >1368px, nội dung bị nhốt vào 1 cụm
      1320px NẰM GIỮA màn hình, 2 bên lộ khoảng trống lớn. ĐÚNG lỗi kiến trúc đã từng xảy ra + ĐÃ
      ĐƯỢC GIẢI QUYẾT cho Best Sellers ở CHG-040 ("Section Architecture Correction") — áp dụng lại
      NGUYÊN VẸN pattern đó: `max-width:none` + `padding-inline` đo riêng, KHÔNG thêm max-width/
      margin-auto nào ở tầng nội dung bên trong (xem CHG-040 giải thích đầy đủ vì sao thêm cap ở
      tầng trong tạo lại đúng lỗi "card nhỏ giữa trang"). Đo `padding-inline`: box disclaimer
      (rounded rect, mép sắc — đo tin cậy hơn text) x=36→477 (441px, khớp EXACT "Rectangle 19360
      441×86" đã ghi ở DESIGN_SPECIFICATION.md); panel phải x=616→1336 (720px, khớp EXACT "Rectangle
      19359 720×375"); lề phải 1366−1336=30px. Cả 2 số đo (36px/30px) khớp rất gần giá trị
      34px đã dùng CHUNG cho Best Sellers/Trust (cùng canvas 1366, cùng lưới) — dùng THỐNG NHẤT 34px
      thay vì 2 số lẻ 36/30 (chênh lệch nằm trong sai số nén JPEG/antialiasing, không phải 2 giá trị
      thật khác nhau).

   2) BACKGROUND HOÀ VÀO NỀN CHUNG — xem `--color-trend-bg` (#070707) mới thêm ở tokens.css, thay
      `--color-brand-primary` (#1f232e, màu nền chung Homepage) đang dùng NHẦM.

   3) TỶ LỆ 2 CỘT SAI — `.block-trend__intro` cũ `flex:0 0 380px` (CỐ ĐỊNH, không đổi theo viewport)
      trong khi `.block-trend__topics` `flex:1 1 auto` (ăn hết phần còn lại) — đúng lỗi "LEFT quá
      nhỏ, RIGHT ăn hết". Đo trực tiếp: lề trái tới panel phải (34→616) = 582px "khu trái", panel
      720px "khu phải", tổng 1302px ≈ khớp content-width 1366−34×2=1298px (chênh lệch nằm trong sai
      số đo JPEG). Tỷ lệ đo được: trái 582/1302=44.7%, phải 720/1302=55.3% — làm tròn **45%/55%**
      (implementation decision — làm tròn số đo, KHÔNG phải số đo thô, nhưng nằm NGAY TRONG khoảng
      Product Owner đề nghị 45–50%/50–55%). Chuyển từ flex-basis PX cố định sang PHẦN TRĂM — cả 2 cột
      co giãn tỷ lệ theo viewport (không dùng max-width cap ở cột phải, ĐÚNG triết lý CHG-040: card/
      panel lớn hơn ở màn rộng là hệ quả tất yếu của "Section full-width thật", không phải bug).
      `gap: 40px` — giá trị tròn gần nhất với khoảng trống ước lượng còn lại sau khi trừ 2 khu đo
      được (implementation decision, không tách riêng đo được từ ảnh do 582px "khu trái" đã bao gồm
      cả gap lẫn nội dung, không phân tách rạch ròi được bằng mắt/pixel).

   Heading font-size: đo cap-height "Chăm sóc từ bên" (dòng 1, top≈y4712→baseline≈y4762, cao ~50px)
   ĐỐI CHIẾU cùng phương pháp với heading "Sản phẩm bán chạy" đã biết chắc font-size 48px
   (`--font-size-4xl`, Best Sellers) — cap-height đo được ~50px cho CẢ HAI, tỷ lệ 1:1 → Trend heading
   cũng ≈48px, KHÔNG PHẢI 36px (`--font-size-3xl`) đang dùng — đổi sang `--font-size-4xl` (token có
   sẵn, không bịa giá trị mới). */
/* CHG-061 (2026-08-27, Final Visual Audit & Pixel-Match Correction): CHG-060 tính tỷ lệ 45/55 bằng
   cách lấy "582px" (khoảng cách lề trái → panel trái) làm "LEFT", nhưng 582px ĐÃ GỘP CHUNG cả text
   trái LẪN khoảng trống (gap) trước panel — SAI theo đúng cảnh báo của Task này ("không được lấy
   LEFT+RIGHT rồi gọi là tổng nếu LEFT đang bao gồm gap"). Đo lại TÁCH RIÊNG 3 đại lượng bằng Python/
   Pillow trên `Desktop - 8.jpg` (đúng x=615/panel trái làm mốc, KHÔNG đổi):
     - LEFT (rightmost content thật của cột trái — quét TOÀN BỘ y=4546-5140, x=0-615, tìm x lớn nhất
       có pixel khác nền #070707): x=476 (mép phải khối disclaimer "Rectangle 19360", cũng là điểm xa
       nhất trong toàn bộ cột trái — heading dòng 1 chỉ tới x=433, description tới x=453, đều KHÔNG
       vượt qua 476) → LEFT width = 476−36(lề trái, đo dưới) = **440px**.
     - GAP (khoảng trống THẬT giữa 2 cột, xác nhận bằng mắt qua crop phóng to — KHÔNG có nội dung ẩn
       nào trong khoảng này): 615(panel trái)−476(left content phải) = **139px**.
     - RIGHT (panel, đo lại 5 điểm y khác nhau trong panel — x=615→1334 TUYỆT ĐỐI ĐỒNG NHẤT ở cả 5
       điểm, độ tin cậy cao nhất trong toàn bộ phép đo): **720px** — khớp EXACT "Rectangle 19359
       720×375" đã ghi từ trước.
     - Kiểm chứng chéo: LEFT+GAP+RIGHT = 440+139+720 = 1299px ≈ khớp content-width đo được
       1366−36(lề trái)−32(lề phải, 1366−1334)=1298px (chênh lệch 1px, trong sai số làm tròn).

   → Tỷ lệ ĐÚNG (loại gap ra khỏi 2 cột): LEFT/(LEFT+RIGHT) = 440/1160 = **37.9%**, RIGHT = 62.1% —
   KHÁC HẲN "45/55" của CHG-060 (CHG-060 nhầm gap vào LEFT khiến LEFT bị tính THỪA 139px). RIGHT-panel
   rất ổn định/hình chữ nhật rõ nét ở CẢ 5 điểm đo — đây là 1 "card" kích thước CỐ ĐỊNH theo thiết kế
   (720px), không phải 1 cột tỷ lệ %.

   CHG-062 (2026-08-27, Final Column Geometry Correction): CHG-061 Ở TRÊN vẫn SAI — chọn LEFT
   `flex:1 1 0%` (grow KHÔNG GIỚI HẠN để lấp phần dư) trong khi RIGHT `flex:0 1 720px` (grow:0, bị
   khoá cứng ở 720px) — tại viewport RỘNG, LEFT ăn hết toàn bộ phần dư trong khi RIGHT đứng yên, nên
   LEFT VƯỢT QUA RIGHT về độ rộng — ĐẢO NGƯỢC hierarchy hình học của Design (đo thật tại 1920px:
   LEFT=978px > RIGHT=720px — Product Owner phát hiện đúng qua screenshot thật ở viewport họ dùng).
   Root cause: nhầm "RIGHT là 1 card cố định" (ĐÚNG) thành "LEFT phải co giãn KHÔNG GIỚI HẠN" (SAI) —
   trong khi bản chất, CẢ 2 CỘT đều là kích thước CỐ ĐỊNH theo đúng đo đạc Reference (không riêng
   RIGHT), phần KHÔNG cố định trong composition này chính là GAP.

   Sửa CHG-062 (px cố định + `justify-content:space-between`): giữ ĐÚNG 440/720 tại MỌI viewport ≥
   1366px, nhưng khoảng dư ở viewport rộng đổ HẾT vào 1 GAP DUY NHẤT — đo thật tại 1920px: GAP=677px
   = **35.5%** content-width, trong khi LEFT chỉ còn 23.1%, RIGHT chỉ còn 37.8% (đo bằng
   `getBoundingClientRect()` chia cho content-width thật) — TỶ LỆ bị lệch hẳn so với Reference dù số
   PX của riêng LEFT/RIGHT không đổi. Product Owner đúng: khoá cứng 2 cột bằng PX tuyệt đối, dù giữ
   đúng hierarchy LEFT<RIGHT, vẫn tạo "khoảng trắng chết" phi lý ở viewport lớn — SAI tinh thần
   "block trải rộng theo chiều ngang" của Homepage.

   CHG-063 (2026-08-27, Redesign Responsive Geometry): chuyển ĐO PX SANG TỶ LỆ %. Đo lại TRỰC TIẾP
   trên `Desktop - 8.jpg` (re-verify, không kế thừa mù số cũ): RIGHT panel 720px (5/5 điểm y đồng
   nhất), LEFT content rightmost-extent 476px, lề trái 36px → LEFT=440px, GAP=615−476=139px,
   content-width=1366−36−32=1298px (giống hệt CHG-061/062, số đo KHÔNG đổi — chỉ CÁCH ÁP DỤNG đổi).
   Tỷ lệ trên content-width: **LEFT=440/1298=33.90%, GAP=139/1298=10.71%, RIGHT=720/1298=55.47%**
   (tổng 100.08%, sai số làm tròn không đáng kể) — khớp gần như chính xác với ước lượng Product Owner
   đưa ra (34%/11%/55%).

   Chuyển từ Flexbox (px cố định hoặc flex-grow không kiểm soát — cả 2 cách đều đã SAI ở CHG-061/062)
   sang **CSS Grid với `grid-template-columns` + `column-gap` bằng PHẦN TRĂM** — đây chính là trường
   hợp "composition cần column sizing rõ ràng" nên ưu tiên Grid thay vì Flex. Percentage trong Grid
   track VÀ Grid gap đều resolve trực tiếp trên content-box của Grid container (không phụ thuộc lẫn
   nhau như flex-basis/flex-grow từng gây lỗi 2 lần trước) — LEFT/GAP/RIGHT giờ LUÔN giữ ĐÚNG tỷ lệ
   33.9%/10.7%/55.5% ở MỌI viewport, tăng viewport → cả 3 phần tăng theo tỷ lệ, không phần nào bị
   "đóng băng" khiến phần còn lại phải hấp thụ hết chênh lệch. */
.block-trend { background-color: var(--color-trend-bg); padding-block-start: 113px; padding-block-end: 106px; font-family: 'Be Vietnam Pro', var(--font-family-base); }
.block-trend .container { max-width: none; padding-inline: 34px; }
.block-trend .section-eyebrow { text-align: left; color: var(--color-brand-accent); margin-bottom: var(--space-3); }
.block-trend__grid { display: grid; grid-template-columns: 33.9% 55.4%; column-gap: 10.7%; align-items: start; }
.block-trend__intro { min-width: 0; color: var(--color-text-on-dark); }
.block-trend__heading {
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: var(--font-size-4xl); font-weight: var(--font-weight-regular);
    line-height: 1.2; color: var(--color-text-on-dark); margin-bottom: var(--space-4);
}
/* CHG-061: màu đo lại TRỰC TIẾP trên `Desktop - 8.jpg` — mode-color (màu lặp lại nhiều nhất, loại
   pixel nền/antialiasing) tại nhiều dòng của đoạn mô tả cho ra rgb(183,179,168) = **#B7B3A8** (xuất
   hiện 33 lần liên tiếp tại 1 dòng quét — độ tin cậy cao), SÁNG HƠN RÕ RỆT `--color-text-on-dark-muted`
   (#A2A2A2 = rgb(162,162,162)) đang dùng — đúng cảnh báo Task "không được để text mờ hơn thiết kế".
   Trùng khớp CHÍNH XÁC màu `#b7b3a8` đã dùng sẵn cho `.block-trend__count` bên dưới (không phải
   trùng hợp — cùng 1 màu "text phụ sáng" dùng nhất quán trong Block). */
.block-trend__desc { color: #b7b3a8; font-size: var(--font-size-sm); line-height: 1.6; margin-bottom: var(--space-6); }
/* Highlight/notice box — nền maroon đậm đo được #2E0A16 (khác `--color-topbar-bg` #781035 — tối
   hơn hẳn, không phải cùng token, ghi hex cục bộ giống các trường hợp khác trong file này khi
   không có token khớp sẵn), icon sao vàng + chữ nghiêng kem. */
.block-trend__highlight {
    display: flex; gap: var(--space-3); align-items: flex-start;
    background: #2e0a16; border-radius: var(--radius-base); padding: var(--space-4);
}
.block-trend__highlight-icon { flex-shrink: 0; color: var(--color-brand-accent); margin-top: 2px; }
.block-trend__highlight p { font-style: italic; font-size: 13px; line-height: 1.6; color: var(--color-text-on-dark); margin: 0; }

/* Panel Topic — nền olive-brown đo được #3B3513, hàng hover/focus sáng hơn #62561C (đo pixel trực
   tiếp, không có token site-wide khớp — 2 tông màu CHỈ dùng trong Block này). CHG-063: bỏ hẳn
   `flex`/`max-width` cố định (CHG-062) — width giờ do CSS Grid track `55.4%` ở `.block-trend__grid`
   quyết định, panel co giãn TỶ LỆ theo viewport thay vì khoá cứng 720px (xem giải thích đầy đủ ở
   `.block-trend__grid`). */
.block-trend__topics { min-width: 0; background: #3b3513; border-radius: var(--radius-base); overflow: hidden; }
.block-trend__topic { position: relative; display: flex; align-items: stretch; }
.block-trend__topic + .block-trend__topic { border-top: 1px solid rgba(255, 255, 255, 0.08); }
.block-trend__topic:hover, .block-trend__topic:focus-within { background: #62561c; }
.block-trend__topic-toggle {
    flex: 1 1 auto; display: flex; align-items: center; gap: var(--space-4);
    background: none; border: 0; padding: var(--space-4) var(--space-5);
    font-family: inherit; text-align: left; cursor: pointer; color: var(--color-text-on-dark);
}
/* Icon — khung tròn viền vàng 56px (cùng kích thước icon Trust Benefits đã dùng, đúng quy ước site
   cho "icon vòng tròn" — không bịa kích thước mới), icon SVG bên trong tự co theo `<img>` con. */
.block-trend__icon {
    flex: 0 0 auto; width: 56px; height: 56px; border-radius: var(--radius-full);
    border: 1px solid var(--color-brand-accent); display: grid; place-items: center;
}
.block-trend__icon img { width: 28px; height: 28px; object-fit: contain; }
/* CHG-061: màu đo lại — mode-color tại nhiều dòng chữ "Collagen peptide" ra rgb(255,255,255) THUẦN
   (30 lần lặp lại tại 1 dòng quét, không lẫn màu nào khác — độ tin cậy cao), KHÔNG PHẢI
   `--color-text-on-dark` (#F4EDE7, hơi ngả kem) đang dùng — Topic label sáng nhất trong hệ thống chữ
   của Block (khác Heading/Description dùng tông kem/xám ấm hơn) — đúng hierarchy "tên Topic là thông
   tin quan trọng nhất trong 1 hàng". */
.block-trend__label { flex: 1 1 auto; font-size: var(--font-size-base); font-weight: var(--font-weight-medium); color: #ffffff; }
.block-trend__meta { flex: 0 0 auto; display: flex; align-items: center; gap: var(--space-3); }
.block-trend__count { font-size: 12px; font-weight: var(--font-weight-semibold); letter-spacing: 0.06em; text-transform: uppercase; color: #b7b3a8; }
.block-trend__chevron { display: none; font-size: var(--font-size-lg); color: var(--color-text-on-dark-muted); transition: transform var(--duration-base) var(--easing-standard); }

/* CTA — pill maroon đo được #761133 (≈ `--color-topbar-bg` #781035, dùng thẳng token đã có, chênh
   lệch trong sai số nén JPEG). CHG-060 sửa lại quyết định DI-07 của CHG-058 (lúc đó chọn "luôn hiện"
   vì ưu tiên an toàn accessibility khi chưa đo được) — đo lại TRỰC TIẾP background từng hàng trên
   `Desktop - 8.jpg`: hàng 1/3/4 = rgb(59,53,19) (#3B3513, olive nền chuẩn), hàng 2 (hàng có CTA) =
   rgb(99,84,29) (#63541D — GẦN KHỚP `#62561c` hover-bg đã có sẵn) — 3/4 hàng KHÔNG có CTA hiện, chỉ
   1 hàng có CTA THAY THẾ count VÀ nền sáng hơn cùng lúc → xác nhận đây là HOVER STATE thật (ảnh tĩnh
   bắt đúng khoảnh khắc con trỏ dừng ở hàng đó), không phải nội dung cố định cho riêng 1 hàng — giải
   quyết dứt điểm DI-07. CTA giờ ẩn mặc định, hiện khi `:hover`/`:focus-within` (bàn phím Tab vẫn kích
   hoạt qua `:focus-within`, giữ đúng accessibility parity đã cam kết — KHÔNG mất khả năng tiếp cận
   bằng bàn phím, chỉ đổi lúc nào nó xuất hiện). Mobile (`@media 768px`) không đổi — panel luôn hiện
   khi accordion mở qua `[data-trend-expanded]`, không phụ thuộc rule hover này (không có "hover" trên
   cảm ứng). */
.block-trend__panel { flex: 0 0 auto; display: none; align-items: center; padding-inline: var(--space-5); }
/* Bọc trong `min-width:769px` (ngược với `@media max-width:768px` accordion bên dưới) — CỐ Ý không
   để rule hover này chạy trong vùng Mobile dù specificity cao hơn rule collapse mặc định của
   accordion, tránh trường hợp trình duyệt cảm ứng nào đó mô phỏng `:hover` khi chạm (hành vi không
   đồng nhất giữa các OS/browser di động) vô tình lộ CTA ngoài ý muốn khi accordion đang đóng — đúng
   yêu cầu "không đổi responsive behavior của Mobile". */
@media (min-width: 769px) {
    .block-trend__topic:hover .block-trend__panel, .block-trend__topic:focus-within .block-trend__panel { display: flex; }
    .block-trend__topic:hover .block-trend__count, .block-trend__topic:focus-within .block-trend__count { display: none; }
}
.block-trend__cta {
    display: inline-flex; align-items: center; white-space: nowrap;
    background: var(--color-topbar-bg); color: var(--color-text-on-dark);
    font-size: 11px; font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.08em;
    border-radius: var(--radius-full); padding: 8px 16px; text-decoration: none;
    transition: opacity var(--duration-base) var(--easing-standard);
}
.block-trend__cta:hover { opacity: 0.85; }

/* ---------- Magazine / Article ----------
   CHG-065 (2026-08-27, Tạp chí Koco Rebuild — xem docs/06_Test/AUDIT_TAP_CHI_KOCO.md): bản Sprint
   UI-09 dưới đây (`.article-card`/`.article-card__*`) đo từ `design/koco.jpg` — reference CŨ, đã
   thay bởi bản redesign 23-8 (`design/23-8/trang chu/Desktop - 8.jpg`, node Figma `1992:252`).
   Design mới dùng cấu trúc card HOÀN TOÀN khác (ảnh ngang 1.13:1 + text xếp DƯỚI ảnh, không còn
   overlay/gradient) — nhưng `.article-card`/`.article-card__*` vẫn đang là Golden Component dùng
   CHUNG cho Blog Archive (`index.php`) + Related Posts (`single.php`), 2 nơi này KHÔNG đổi theo
   Design mới trong Sprint này (ngoài phạm vi — xem audit mục 17/18) nên GIỮ NGUYÊN 100% các rule
   dưới đây, không xoá/sửa. Presentation mới cho riêng Homepage Magazine dùng bộ class
   `.article-card--magazine*` riêng ở cuối phần này (component `article-card--magazine.php` mới,
   tách biệt khỏi Golden Component gốc). */
.article-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.article-card { position: relative; aspect-ratio: 423 / 595; border-radius: var(--radius-sm); overflow: hidden; background: var(--color-bg-subtle); }
.article-card__link { display: block; position: relative; width: 100%; height: 100%; color: inherit; text-decoration: none; }
.article-card__link img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Gradient scrim: chỉ tối dần ở 1/3 dưới ảnh (đúng thiết kế — 2/3 trên ảnh giữ nguyên, không phủ
   tối toàn bộ) để Title/Date/Description đọc rõ trên nền ảnh sáng. */
.article-card__link::after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(to top, rgba(0,0,0,0.88) 0%, rgba(0,0,0,0.55) 32%, rgba(0,0,0,0) 62%);
    pointer-events: none;
}
.article-card__overlay { position: absolute; left: 0; right: 0; bottom: 0; z-index: 1; padding: var(--space-5) var(--space-5) var(--space-5); }
.article-card__title {
    color: #fff; font-size: 19px; font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight); margin-bottom: var(--space-2);
}
.article-card:hover .article-card__title { color: var(--color-brand-accent); }
.article-card__date { display: block; color: var(--color-brand-accent); font-size: var(--font-size-xs); margin-bottom: var(--space-3); }
.article-card__excerpt {
    color: var(--color-text-on-dark-muted); font-size: var(--font-size-sm); line-height: var(--line-height-base);
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.article-card__more { color: var(--color-brand-accent); font-weight: var(--font-weight-medium); white-space: nowrap; }

/* ---------- Homepage Magazine (CHG-065, Tạp chí Koco Rebuild) ----------
   Đo TRỰC TIẾP bằng Python/Pillow trên `design/23-8/trang chu/Desktop - 8.jpg` (crop y=5100-6200,
   xem docs/06_Test/AUDIT_TAP_CHI_KOCO.md mục 4/5/10 cho toàn bộ số đo). Section full-width (không
   bị `.container` mặc định co vào giữa — cùng pattern đã áp dụng cho Trend/Best Sellers/Trust,
   padding-inline 34px khớp lề chuẩn site). Heading tái dùng NGUYÊN pattern
   `.section-eyebrow`/`.section-heading__dash` đã có ở Best Sellers/Promo Banners/New Products
   (Playfair Display, không tạo font-family mới). */
.block-magazine {
    background: var(--color-brand-primary); color: var(--color-text-on-dark);
    font-family: 'Be Vietnam Pro', var(--font-family-base);
    padding-block: 90px 120px;
}
.block-magazine .container { max-width: none; padding-inline: 34px; }
.block-magazine .section-eyebrow {
    font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold);
    letter-spacing: 0.15em; color: #b7b3a8; margin-bottom: 25px;
}
.block-magazine .section-heading {
    display: flex; align-items: center; justify-content: center; gap: var(--space-4);
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: var(--font-size-4xl); font-weight: var(--font-weight-regular);
    text-transform: none; letter-spacing: normal; line-height: var(--line-height-tight);
    margin-bottom: 80px;
}
.block-magazine .section-heading__dash { width: 40px; height: 2px; background: var(--color-brand-accent); flex-shrink: 0; font-size: 0; }
.block-magazine .article-grid { gap: 26px; }

/* Card — "ảnh trên, text dưới", chiều cao TỰ NHIÊN theo nội dung (KHÔNG aspect-ratio/height cố
   định trên toàn Card — 3 bài excerpt/title dài khác nhau nên card cao khác nhau, đúng Design). */
.article-card--magazine__link { display: block; color: inherit; text-decoration: none; }
/* Double-radius halo fix — CHG-067 (2026-09-03) ban đầu chỉ thêm `border-radius:inherit` cho
   `<img>` (vẫn giữ, đúng hướng — 2 lớp bo cùng bán kính thay vì lệch nhau). CHG-067.1 (tái kiểm tra
   bằng browser thật sau khi Product Owner báo screenshot vẫn thấy halo — xem
   `docs/06_Test/CHANGE_LOG_TAP_CHI_V2.md` § CHG-067.1 cho toàn bộ quá trình re-investigate):
   `border-radius:inherit` khớp SỐ nhưng KHÔNG loại được hoàn toàn 1 viền anti-alias dưới-pixel
   (`aspect-ratio:417/369` trong grid 3 cột luôn ra kích thước LẺ px — đo lại tại 1366px:
   410.328125×363.09375 — không phải số nguyên ở BẤT KỲ breakpoint 3-cột nào, đây là bản chất của
   `aspect-ratio` + `fr` unit, không thể "sửa hết" bằng CSS bo góc). Root cause SAU CÙNG không phải
   "2 lớp bo lệch nhau" mà là "viền dưới-pixel không tránh được LUÔN LỘ MÀU NỀN CỦA WRAPPER" — nền cũ
   `var(--color-bg-subtle)` (#f7f5f2, gần trắng, dùng làm placeholder trước khi ảnh tải xong) tương
   phản CỰC MẠNH với nền trang navy `#1f232e` nên viền dưới-pixel (dù chỉ ~1 device pixel) vẫn NHÌN
   THẤY RÕ trên thực tế dù `getComputedStyle` báo 2 bán kính khớp nhau. Fix ĐÚNG: đổi màu nền wrapper
   sang khớp CHÍNH XÁC nền trang tại 2 nơi component này đang dùng — `.block-magazine` (Homepage) và
   `body.blog` (`/tap-chi/`) ĐỀU dùng `var(--color-brand-primary)` (navy) làm nền — viền dưới-pixel
   (nếu còn) giờ là navy-trên-navy, không thể phân biệt bằng mắt thường, thay vì kem-trên-navy như cũ.
   KHÔNG đổi token `--color-bg-subtle` (dùng chung rất nhiều nơi nền sáng khác — Best Sellers/Cart/
   Account/TOC...), chỉ override giá trị cục bộ tại ĐÚNG 1 selector này. Giữ `border-radius:inherit`
   trên `<img>` (vẫn đúng, giảm diện tích viền dù không xoá được 100%) — 2 fix cộng lại xử lý dứt
   điểm cả nguyên nhân kỹ thuật (bo lệch) lẫn hệ quả nhìn thấy được (tương phản màu). KHÔNG đổi
   aspect-ratio/object-fit/kích thước/crop ảnh. */
.article-card--magazine__media {
    position: relative; display: block; overflow: hidden;
    aspect-ratio: 417 / 369; border-radius: var(--radius-sm); background: var(--color-brand-primary);
}
.article-card--magazine__media img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
.article-card--magazine__date {
    display: block; margin-top: 22px;
    color: #b8bbc2; font-size: var(--font-size-xs);
}
.article-card--magazine__title {
    margin-top: 15px;
    color: var(--color-text-on-dark); font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: var(--font-size-lg); font-weight: var(--font-weight-bold); line-height: var(--line-height-tight);
}
.article-card--magazine:hover .article-card--magazine__title { color: var(--color-brand-accent); }
.article-card--magazine__excerpt {
    margin-top: 25px;
    color: var(--color-text-on-dark-muted); font-size: var(--font-size-sm); line-height: var(--line-height-base);
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.article-card--magazine__more {
    display: flex; align-items: center; gap: var(--space-2); margin-top: 25px;
    color: var(--color-brand-accent); font-size: var(--font-size-xs); font-weight: var(--font-weight-medium);
    text-transform: uppercase; letter-spacing: 0.08em; white-space: nowrap;
}

/* ---------- Footer (Sprint UI-10, Pixel Perfect — "Luxury Hero Footer") ----------
   Root cause bản cũ: Footer là footer WordPress thông thường (nền phẳng, Newsletter tách Section
   riêng, menu hardcode) trong khi thiết kế là 1 khối Hero cao, nền ảnh thật (đã có sẵn logo KOCO +
   đường cong — không tự vẽ gì thêm), Newsletter hoà chung nền với Footer, không tách rời.

   Đo trực tiếp koco.jpg: Logo circle đỉnh y 15-125 (đường kính ~110px), Newsletter heading y~205,
   desc y~270, form y~316-355 (rộng ~550px), 4 cột Nav bắt đầu y~460, Bottom bar (nền phẳng, tách
   khỏi ảnh nền) bắt đầu y~683. Ảnh nền gốc `bg_footer.png` 1366×771 — dùng background-size:cover +
   background-position: center top (KHÔNG center center — ảnh có model bên phải và Logo bám đỉnh,
   center-center sẽ crop mất phần đỉnh có Logo). */
/* Review 3 (2026-08-01) — root cause thật của "Desktop sát Logo / Mobile đè lên Logo": trước đó
   dùng `background-size:cover`, scale ảnh phụ thuộc CẢ chiều rộng LẪN chiều cao container (dùng
   max(scale_w, scale_h)) — ở màn hẹp+cao (Mobile) container thường cao hơn ảnh gốc theo tỷ lệ nên
   cover phóng ảnh theo chiều CAO, khiến Logo to lên không đoán trước được, còn `padding-top` lại là
   1 số px CỐ ĐỊNH không biết ảnh đã phóng bao nhiêu — 2 đại lượng không đồng bộ nên mỗi breakpoint
   phải dò riêng bằng tay (đúng như anh chỉ ra "phụ thuộc viewport").
   Sửa tận gốc: đổi `background-size: 100% auto` — ảnh LUÔN scale theo đúng chiều RỘNG container
   (auto giữ nguyên tỷ lệ gốc 1366:771), không còn phụ thuộc chiều cao nữa → vị trí Logo trong ảnh
   giờ là hàm TUYẾN TÍNH của viewport width, tính được chính xác ở mọi breakpoint bằng 1 công thức
   duy nhất, không cần đoán/override riêng cho Mobile nữa.
   padding-top = 10.47% (= đáy Logo gốc y=143 / bề rộng ảnh gốc 1366, quy đổi % — CSS tính % của
   padding-top theo WIDTH của containing block nên số này tỷ lệ đúng với vị trí Logo đã scale) +
   67px cố định (khoảng thở đo từ koco.jpg, giữ NGUYÊN giá trị tuyệt đối ở mọi màn hình vì đây là
   khoảng cách đọc-được cho Text, không phải thuộc tính của ảnh nên không nên co giãn theo %). Kết
   quả: gap Logo→Heading LUÔN ~67px dù Desktop hay Mobile — không còn cần `padding-top` riêng cho
   Mobile (đã bỏ override cũ trong media query <=768px).
   Hệ quả phụ phát hiện khi verify Mobile: `100% auto` khiến ảnh dừng đúng ở mép dưới gốc (y=771
   native) thay vì kéo dài hết Section như `cover` cũ — lộ ra `background-color` bên dưới. Sample
   màu bằng Python tại chính mép dưới `bg_footer.png` = rgb(32,35,44), KHÔNG trùng
   `--color-brand-primary` (#070707) đang dùng làm fallback → tạo 1 đường ranh giới cứng nhìn thấy
   rõ ở Mobile (nơi ảnh kết thúc sớm hơn nhiều so với Desktop). Đổi fallback sang đúng màu mép ảnh
   để 2 vùng nối liền mạch, không còn seam. */
/* CHG-010 (2026-08-24, xem docs/CHANGE_LOG.md — Footer UI Audit): `.site-footer` dùng `.container`
   MẶC ĐỊNH (`max-width:1320px; margin-inline:auto`, dòng 51) — CÙNG LỖI đã sửa cho Header ở CHG-001
   (`max-width` cố định + centering làm content lùi xa mép trái/phải khi viewport rộng). Đo thật xác
   nhận: ở 1920px, `.site-footer__columns.left = 324px` (Logo Header cùng viewport chỉ 32px, đã sửa
   đúng từ CHG-001/008) — Footer và Header LỆCH HẲN alignment nhau, tạo cảm giác 2 khối không cùng hệ
   thống dù đứng trên cùng 1 trang. Tại viewport reference (1366px) cũng đã lệch nhẹ: cột đầu tiên
   bắt đầu ở x=47px trong khi reference (`Desktop - 8.jpg`, đo cụm chữ "KOCO" cột đầu) ở x≈34-35px —
   khớp với padding 32px của Header (`var(--space-8)`), không phải công thức centering 47px hiện
   tại. Áp dụng ĐÚNG pattern đã dùng cho Header: bỏ `max-width`, dùng padding-inline làm khoảng cách
   mép duy nhất — Footer giờ full-width như Header, nhất quán trên toàn trang. */
.site-footer .container { max-width: none; padding-inline: var(--space-8); }

.site-footer__hero {
    background-image: url('../images/bg-footer.png');
    /* UI Audit 2026-08-23 — sửa lại đánh giá sai ở lần trước ("ảnh nền tự thân đã khớp mockup"):
       so trực tiếp reference (design/23-8/trang chu/Footer.png, KHÔNG có ảnh chụp cọ trang điểm/
       model) với ảnh cũ `bg-footer.png` (1366×771, CÓ lồng ảnh chụp mờ phía sau sóng+logo) — 2 ảnh
       khác hẳn nhau, không phải chỉ lệch tông màu. Đổi hẳn sang asset PHẲNG đúng reference
       (1372×205, chỉ có sóng gợn + đường chấm gold + logo tròn "KOCO KOREAN CORNER" trên nền
       trong suốt — che phủ bằng background-color bên dưới, không còn ảnh chụp). Ảnh MỚI thấp hơn
       hẳn ảnh cũ (205px so với 771px ở cùng bề rộng gốc) — logo giờ nằm gần y=148/1372 ≈ 10.79%
       chiều rộng (đo lại bằng Python/Pillow, khác 10.47% cũ vì ảnh khác, không phải do tính sai).
       CHG-011 (2026-08-24, xem docs/CHANGE_LOG.md — Footer UI Audit): `background-color` SAI —
       dùng `--color-brand-primary` (#1f232e) trong khi mép dưới THẬT của chính file `bg-footer.png`
       là rgb(20,24,32) (đo trực tiếp bằng Python/Pillow trên PNG, không nhiễu nén) — tạo seam nhìn
       thấy rõ ngay dưới ảnh trang trí (Product Owner chỉ ra qua screenshot thật, đối chiếu đúng với
       reference: toàn bộ vùng dưới sóng — Newsletter/Columns/Bottom-bar — phải tối hơn phần trên,
       không phải 1 màu phẳng xuyên suốt). Đổi sang `--color-footer-lower-bg` (khớp đúng mép ảnh,
       xem tokens.css). */
    background-size: 100% auto; background-position: center top; background-color: var(--color-footer-lower-bg);
    background-repeat: no-repeat;
    color: var(--color-text-on-dark);
    padding-top: calc(10.79% + 67px); padding-bottom: 56px;
}
.site-footer__newsletter { text-align: center; max-width: 680px; margin-inline: auto; margin-bottom: 110px; }
/* UI Audit 2026-08-23: reference có 2 gạch ngang trang trí 2 bên heading ("— … —") — thêm bằng
   ::before/::after (thuần CSS, không đổi markup/HTML trong footer.php). Font-family giữ NGUYÊN theo
   token hiện có (reference dùng font serif riêng cho heading này — dự án chưa có token font thứ 2,
   không tự ý thêm — xem FOOTER_DESIGN_AUDIT_2026-08-26.md § Open Questions, vẫn CHƯA sửa ở đây).
   F002 (2026-08-26, Footer Implementation — xem docs/ui-qa/footer/01-newsletter): `text-transform:
   uppercase` SAI — reference viết chữ thường có hoa đầu câu ("Nhận ưu đãi riêng mỗi tuần"), không
   phải toàn bộ hoa — bỏ property này (khác với font-family, đây KHÔNG cần token mới, chỉ cần bỏ 1
   dòng). */
.site-footer__newsletter-heading {
    color: var(--color-brand-accent); font-size: var(--font-size-3xl); font-weight: var(--font-weight-bold);
    letter-spacing: 0.02em; margin-bottom: var(--space-8);
    display: flex; align-items: center; justify-content: center; gap: var(--space-4);
}
.site-footer__newsletter-heading::before,
.site-footer__newsletter-heading::after {
    content: ""; flex: 0 0 auto; width: 32px; height: 1px; background: currentColor; opacity: 0.6;
}
.site-footer__newsletter-desc { color: var(--color-text-on-dark-muted); margin-bottom: var(--space-8); }
/* Pill lồng pill: khung ngoài viền mảnh bo tròn tuyệt đối, Button vàng lồng bên trong sát mép phải
   (đúng thiết kế — không phải input/button tách rời như bootstrap form cũ). */
.site-footer__newsletter-form {
    display: flex; align-items: center; max-width: 550px; margin-inline: auto;
    background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.28); border-radius: var(--radius-full);
    padding: 5px; height: 52px; box-sizing: border-box;
}
.site-footer__newsletter-form input {
    flex: 1; height: 100%; background: transparent; border: 0; outline: 0;
    padding-inline: var(--space-6); color: var(--color-text-on-dark); font-size: var(--font-size-sm);
}
.site-footer__newsletter-form input::placeholder { color: var(--color-text-on-dark-muted); }
.site-footer__newsletter-form button {
    height: 100%; flex-shrink: 0; background: var(--color-brand-accent); color: var(--color-brand-primary);
    border: 0; border-radius: var(--radius-full); padding-inline: var(--space-8); font-weight: var(--font-weight-bold);
    cursor: pointer;
}
.site-footer__newsletter-note { color: var(--color-success); margin-top: var(--space-3); }
/* H01 (RM-011): trạng thái lỗi (email không hợp lệ/rate-limit/server) — tái dùng token
   --color-danger đã có sẵn (tokens.css), không thêm màu mới. */
.site-footer__newsletter-note.is-error { color: var(--color-danger); }

/* Review 2 (2026-08-01): Grid 4 cột BẰNG NHAU (1fr mỗi cột) thay vì fr tự ước lượng theo độ rộng
   content (1fr 1.6fr 1.2fr 1.3fr cũ) — đúng chỉ đạo "căn theo Logo/Grid 4 cột, không căn theo độ
   rộng nội dung". Cột "Mua sắm" cần 2 cột con (Chăm sóc da.../Phụ kiện...) dùng NESTED GRID riêng
   bên trong ô của chính nó (`.footer-col__split`, xem bên dưới) — không làm ảnh hưởng độ rộng 3
   cột còn lại, tránh lỗi "Phụ kiện bị hút sát sang cột Hỗ trợ" do trước đây dùng flex+gap cố định
   bên trong 1 cột có fr riêng không đồng bộ với 3 cột kia. */
/* Review 5 (2026-08-01) — đo trên "koco.jpg" (KHÔNG phải `Desktop - 8.jpg` dùng xuyên suốt
   CHG-001→009): 263:394:340:301 ≈ 1:1.5:1.29:1.14.
   CHG-010 (2026-08-24, xem docs/CHANGE_LOG.md — Footer UI Audit): SỬA LẠI — đo lại trực tiếp trên
   `Desktop - 8.jpg` bằng kỹ thuật quét cụm chữ vàng (gold-text-cluster detection, cùng phương pháp
   CHG-004/005/008), lấy vị trí bắt đầu (left) của 4 heading "KOCO/MUA SẮM/HỖ TRỢ/LIÊN HỆ":
   32/406/687/1028px (viewport 1366px) → trừ gap (32px = `--space-8`) giữa mỗi cặp → độ rộng cột
   THẬT: 342/249/309/306px — ĐẢO NGƯỢC hẳn so với Review 5: cột "Koco" RỘNG NHẤT (không phải hẹp
   nhất), cột "Mua sắm" HẸP NHẤT (không phải rộng nhất) — vì reference "Mua sắm" chỉ có 1 danh sách
   dọc 4 item (không tách 2 cột con như hiện tại, xem `.footer-col__split`), không cần rộng. Quy đổi
   tỷ lệ mới (không hardcode px, vẫn responsive): 342:249:309:306 ≈ 1.37:1:1.24:1.23. */
.site-footer__columns { display: grid; grid-template-columns: 1.37fr 1fr 1.24fr 1.23fr; gap: var(--space-8); }
.footer-col__title { color: var(--color-brand-accent); font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: var(--space-5); }
.footer-col ul { display: flex; flex-direction: column; gap: var(--space-3); }
/* UI Audit 2026-08-23: social icon chuyển từ hàng riêng ở Bottom bar vào cột "Về Koco" (đúng
   reference — xem footer.php). Ghi đè `.footer-col ul` (mặc định flex-direction:column, dùng cho
   danh sách link) thành hàng ngang — đây là 1 nhóm ICON, không phải danh sách link đọc dọc. */
.footer-col__social { flex-direction: row !important; gap: var(--space-4) !important; margin-bottom: var(--space-6); }
/* Kích thước/render y hệt `.site-footer__logo-list` cũ (20x20, object-fit:contain, KHÔNG filter —
   ảnh social đã đúng màu sẵn dùng cho nền tối, filter suy đoán dễ làm sai màu icon thật). */
.footer-col__social a { display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; opacity: 0.85; }
.footer-col__social a:hover { opacity: 1; }
.footer-col__social img { width: 100%; height: 100%; object-fit: contain; }
.footer-col a { color: var(--color-text-on-dark-muted); font-size: var(--font-size-sm); line-height: var(--line-height-base); }
.footer-col a:hover { color: var(--color-brand-accent); }
/* Cột con Mua sắm — LỊCH SỬ: từng dùng `.footer-col--split .footer-col__split` (2 cột con, gap
   20px→36px ở round "koco.jpg", rồi 36px→16px ở CHG-010) để hiển thị hết 8 item menu `footer-shop`.
   CHG-011 (2026-08-24, xem docs/CHANGE_LOG.md — Footer UI Audit): Product Owner xác nhận trực tiếp
   qua screenshot — reference KHÔNG có cột "Phụ kiện/Veon/Lumi/Earthniq" ở Footer (các tên thương
   hiệu này đã hiển thị sẵn ở Header Nav). Bỏ hẳn cơ chế chia 2 cột — `footer.php` giờ giới hạn hiển
   thị 4 item đầu của menu (`array_slice($shopItems, 0, 4)`), render 1 `<ul>` đơn giản dùng chung CSS
   `.footer-col ul`/`.footer-col a` có sẵn (không cần rule riêng nữa) — xoá rule
   `.footer-col--split .footer-col__split` (không còn phần tử nào dùng 2 class này trong markup). */
.footer-contact { gap: var(--space-4) !important; }
.footer-contact li { display: flex; align-items: flex-start; gap: var(--space-3); color: var(--color-text-on-dark-muted); font-size: var(--font-size-sm); line-height: var(--line-height-tight); }
.footer-contact li svg { flex-shrink: 0; margin-top: 2px; color: var(--color-brand-accent); }
.footer-contact li a { color: inherit; }
.footer-contact li a:hover { color: var(--color-brand-accent); }

/* Bottom bar: nền phẳng TÁCH khỏi ảnh nền (đúng thiết kế — ảnh nền chỉ phủ tới hết cột Nav, không
   phủ xuống Bottom bar). Review 5: tăng breathing space (đo koco.jpg — khoảng cách từ chỗ nền ảnh
   kết thúc tới dòng Payment/Social rộng hơn hẳn 24px cũ) — padding-block 24px→40px; Copyright (tự
   thêm ở Sprint UI-10 gốc, không có trong mockup) cũng nới rộng cho cân xứng, không để sát. */
/* CHG-009 (2026-08-23, xem docs/CHANGE_LOG.md — Footer UI Audit): reference (`Desktop - 8.jpg`,
   vùng footer) cho thấy Copyright + "Phương thức thanh toán" nằm CHUNG 1 hàng (Copyright trái,
   Payment phải) — bản trước tách thành 2 khối `.container` riêng, mỗi khối tự `border-top` (2
   đường viền chồng nhau, sai reference). Gộp `.site-footer__copyright` vào làm phần tử con TRỰC
   TIẾP của `.site-footer__bottom` (xem footer.php) — `justify-content: space-between` sẵn có tự
   dàn 2 bên, không cần CSS mới cho layout. Bỏ `text-align:center; padding-block; border-top` cũ
   (dành cho block đứng riêng) trên `.site-footer__copyright`, chỉ giữ màu/cỡ chữ. */
/* CHG-011: cùng lý do đổi màu ở `.site-footer__hero` — reference cho thấy Bottom-bar tiếp tục màu
   tối hơn (`--color-footer-lower-bg`), không phải `--color-brand-primary` (màu Topbar/Header, sáng
   hơn) — vẫn ĐÚNG về hướng, nhưng CHƯA đúng SẮC ĐỘ chính xác (xem F004 ngay dưới).
   F004 (2026-08-26, Footer Implementation — xem docs/ui-qa/footer/07-bottom-bar): đo lại pixel trực
   tiếp trên reference (`Desktop - 8.jpg`, vùng Bottom bar) cho thấy Bottom bar sáng hơn NHẸ so với
   vùng Columns phía trên (#1a1e27 so với #141821 — 2 tông riêng biệt, không phải cùng 1 màu như
   CHG-011 dùng chung `--color-footer-lower-bg`). KHÔNG đổi `--color-footer-lower-bg` (token dùng
   chung cho `.site-footer__hero` + nhiều nơi khác NGOÀI Footer — Best Sellers `.product-card--dark`/
   `.tab-filter__btn`, xem tokens.css/style.css — đổi token này sẽ ảnh hưởng ngoài phạm vi Footer).
   Thay bằng 1 giá trị hex cục bộ, CHỈ dùng ở đúng 1 selector này, không tạo token global mới cho 1
   nơi dùng duy nhất. */
.site-footer__bottom-bar { background: #1a1e27; }
/* F005 (2026-08-26, Footer Implementation — xem docs/ui-qa/footer/07-payment-logos): CHG-009 dùng
   `justify-content: space-between` cho ĐÚNG 2 con (Copyright/Payment) — với `.site-footer__payment`
   rộng ~521px trong khi `.container` (1302px content) rộng hơn NHIỀU, `space-between` đẩy trọn khối
   Payment sát HẲN mép phải (đo live: right=1334, đúng mép container) — nhưng đo lại pixel trên
   reference (`footer-figma-reference-crop.jpg`, vùng Bottom bar) cho thấy khối Payment kết thúc ở
   x≈926 trong khi mép phải content thật ở x≈1334 — LỆCH ~408px, không hề sát mép phải. Đo tâm khối
   Payment trên reference (x≈665) gần khớp tâm HÌNH HỌC của toàn hàng (x≈683, lệch ~18px — trong sai
   số đo pixel JPEG) hơn hẳn so với "sát phải" — kết luận: Payment được CĂN GIỮA theo TOÀN BỘ chiều
   rộng hàng, không phải bám mép phải.
   Đổi `display:flex` → `display:grid; grid-template-columns: 1fr auto 1fr` — Copyright (con 1) nằm
   cột 1fr đầu (giữ nguyên vị trí sát trái, không đổi), Payment (con 2) nằm cột `auto` (rộng đúng bằng
   nội dung) NẰM GIỮA 2 cột 1fr bằng nhau — 2 cột 1fr tự động chia đều phần không gian còn thừa 2 bên,
   cho kết quả Payment CĂN GIỮA đúng toàn hàng mà KHÔNG cần phần tử thứ 3/spacer giả nào (cột 3 rỗng,
   vẫn được Grid dành chỗ dù không có nội dung). Số học khớp với đo trên reference (dự đoán 390→911,
   đo được 373→894 — chênh đều ~17px cả 2 đầu, đúng sai số đo pixel, không phải model sai).
   `justify-items: start` thêm vào để Copyright/Payment neo ĐẦU cột của chính nó (không bị `stretch`
   mặc định của Grid kéo giãn) — không đổi text-align vốn có. Bỏ `justify-content`/`flex-wrap` (không
   còn ý nghĩa với Grid). `align-items:center` (dọc) GIỮ NGUYÊN. Responsive: rule Mobile @768px (bên
   dưới, dòng ~2559) ĐƯỢC CẬP NHẬT ĐỒNG BỘ để giữ đúng hành vi xếp dọc cũ (flex-direction:column không
   còn tác dụng trên Grid) — không đổi thêm breakpoint nào khác, không đổi hành vi Mobile hiển thị. */
.site-footer__bottom { display: grid; grid-template-columns: 1fr auto 1fr; justify-items: start; align-items: center; gap: var(--space-4); border-top: 1px solid var(--color-border-dark); padding-block: var(--space-10); font-size: var(--font-size-sm); color: var(--color-text-on-dark-muted); }
.site-footer__copyright { font-size: var(--font-size-xs); color: var(--color-text-on-dark-muted); }

/* Review 4 (2026-08-01): Payment/Social đổi text → logo thật (file từ design/logo_thuong_hieu/,
   xem koco-config.php `footer.payment_logos`/`footer.social_links`). Logo GIỮ NGUYÊN tỷ lệ/màu —
   chỉ scale qua CSS (không filter/shadow/stroke/opacity, đúng yêu cầu "Logo Rules").
   Review 5 (round cũ, đối chiếu 1 file khác — "koco.jpg", không phải `Desktop - 8.jpg` hiện dùng
   xuyên suốt CHG-001→008): đổi label thành uppercase/bold — SAI so với reference hiện tại. CHG-009
   đo lại trực tiếp trên `Desktop - 8.jpg`: "Phương thức thanh toán" viết THƯỜNG (chỉ "P" hoa đầu
   câu), font-weight thường, không bold — bỏ `text-transform:uppercase; font-weight:semibold`. */
.site-footer__payment { display: flex; align-items: center; gap: var(--space-8); flex-wrap: wrap; }
.site-footer__bottom-label { white-space: nowrap; letter-spacing: 0.04em; color: var(--color-text-on-dark); }
.site-footer__logo-list { display: flex; align-items: center; gap: var(--space-3); list-style: none; }
/* Payment: bọc khung trắng bo góc thống nhất cho cả 4 logo (PayPal/Visa/Mastercard đã có sẵn nền
   trắng ngay trong file gốc — khung này chỉ tạo khoảng đệm đều, không chồng viền; MoMo vốn nền
   trong suốt nên khung là nơi DUY NHẤT tạo nền trắng cho nó, khớp đúng thiết kế). */
.site-footer__payment-badge { display: flex; align-items: center; justify-content: center; background: #fff; border-radius: var(--radius-sm); height: 27px; padding-inline: var(--space-2); box-sizing: border-box; }
.site-footer__payment-badge img { height: 18px; width: auto; display: block; }
/* `.site-footer__social` (hàng riêng ở Bottom bar) đã xoá — social icon chuyển vào cột "Về Koco"
   (UI Audit 2026-08-23, xem `.footer-col__social` + footer.php), giữ đúng "Social: icon vàng thật,
   KHÔNG khung nền" nhưng ở vị trí mới. */

/* ---------- WooCommerce / Blog / Page wrapper ---------- */
.thekoco-woocommerce { padding-block: var(--space-10); min-height: 50vh; }
.thekoco-woocommerce .products { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-6); list-style: none; padding: 0; }
.thekoco-woocommerce ul.products li.product { text-align: left; }
.thekoco-woocommerce a.button, .thekoco-woocommerce button.button {
    background: var(--color-brand-primary); color: #fff; border-radius: var(--radius-full);
    padding: var(--space-2) var(--space-5); border: 0; font-size: var(--font-size-sm);
}
.thekoco-woocommerce .price { color: var(--color-brand-accent); font-weight: var(--font-weight-bold); }
.thekoco-archive, .thekoco-single-post, .thekoco-page-content { padding-block: var(--space-10); min-height: 40vh; }
.thekoco-archive__title, .thekoco-single-post__title, .thekoco-page-content__title { font-size: var(--font-size-2xl); margin-bottom: var(--space-6); }
.thekoco-breadcrumb { font-size: var(--font-size-xs); color: var(--color-text-muted); margin-bottom: var(--space-4); }

/* ================= Blog Content Platform (Sprint 01 Blog SEO & Content Platform, 2026-08-04) =================
   Article Card hợp nhất về `template-parts/components/article-card.php` — style `.article-card*`
   giữ NGUYÊN không đổi (xem block Magazine Homepage phía trên). Phần dưới đây CHỈ style khung Single
   Post/Archive mới, tái dùng token/pattern 2 cột đã có (`.thekoco-checkout__layout`). */
.thekoco-single-post__layout { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 3fr); gap: var(--space-10); align-items: start; }
.thekoco-single-post__header { margin-bottom: var(--space-6); }
.thekoco-single-post__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-4); color: var(--color-text-muted); font-size: var(--font-size-sm); margin-top: var(--space-3); }
.thekoco-single-post__meta a { color: var(--color-text-muted); }
.thekoco-single-post__meta a:hover { color: var(--color-brand-primary); }
.thekoco-single-post__thumb { margin-bottom: var(--space-6); border-radius: var(--radius-base); overflow: hidden; }
.thekoco-single-post__content { line-height: var(--line-height-loose); margin-bottom: var(--space-8); }
.thekoco-single-post__content h2 { font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold); margin-block: var(--space-8) var(--space-3); scroll-margin-top: var(--space-10); }
.thekoco-single-post__content h3 { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); margin-block: var(--space-6) var(--space-2); scroll-margin-top: var(--space-10); }
.thekoco-single-post__content p { margin-bottom: var(--space-4); }
.thekoco-single-post__content ul, .thekoco-single-post__content ol { margin-bottom: var(--space-4); padding-left: var(--space-6); }

/* Pill — dùng chung cho Category badge + Tag list (Single Post), KHÔNG tạo Badge variant mới. */
.thekoco-pill {
    display: inline-flex; align-items: center;
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--color-border); border-radius: var(--radius-full);
    font-size: var(--font-size-xs); color: var(--color-text-muted);
    margin-bottom: var(--space-3);
}
.thekoco-pill:hover { border-color: var(--color-brand-primary); color: var(--color-brand-primary); }
.thekoco-pill--tag { margin-bottom: 0; }

/* Table Of Contents — hộp tĩnh đầu bài viết (KHÔNG sticky, khác sidebar — chỉ ẩn/hiện khi nội dung
   có ≥1 heading, xem thekoco_parse_content_headings()). */
.thekoco-toc { background: var(--color-bg-subtle); border-radius: var(--radius-base); padding: var(--space-5) var(--space-6); margin-bottom: var(--space-8); }
.thekoco-toc__heading { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); margin-bottom: var(--space-2); }
.thekoco-toc__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.thekoco-toc__item a { color: var(--color-text-muted); font-size: var(--font-size-sm); }
.thekoco-toc__item a:hover { color: var(--color-brand-primary); }
.thekoco-toc__item--h3 { padding-left: var(--space-4); }

.thekoco-single-post__tags { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-bottom: var(--space-6); }
.thekoco-single-post__tags-label { font-size: var(--font-size-sm); color: var(--color-text-muted); }

.thekoco-single-post__share { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-8); padding-bottom: var(--space-8); border-bottom: 1px solid var(--color-border); }
.thekoco-single-post__share-label { font-size: var(--font-size-sm); color: var(--color-text-muted); }
.thekoco-single-post__share a, .thekoco-single-post__share-copy { color: var(--color-text-muted); background: transparent; border: 0; cursor: pointer; display: inline-flex; position: relative; }
.thekoco-single-post__share a:hover, .thekoco-single-post__share-copy:hover { color: var(--color-brand-primary); }
.thekoco-single-post__share-note { position: absolute; top: -28px; left: 50%; transform: translateX(-50%); white-space: nowrap; background: var(--color-brand-primary); color: var(--color-text-on-dark); font-size: var(--font-size-xs); padding: var(--space-1) var(--space-2); border-radius: var(--radius-base); }

/* Prev/Next — style class gốc WordPress core `the_post_navigation()` sinh ra, không tự viết markup. */
.thekoco-post-nav { margin-bottom: var(--space-10); }
.thekoco-post-nav .nav-links { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
.thekoco-post-nav .nav-next { text-align: right; }
.thekoco-post-nav a { display: block; padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-base); }
.thekoco-post-nav a:hover { border-color: var(--color-brand-primary); }
.thekoco-post-nav__dir { display: block; font-size: var(--font-size-xs); color: var(--color-text-muted); margin-bottom: var(--space-1); }
.thekoco-post-nav__title { display: block; font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); }

.thekoco-related-posts { margin-top: var(--space-4); }
.thekoco-related-posts .section-heading { text-align: left; margin-bottom: var(--space-6); }

/* H04-C (2026-09-19) — Related Products trên Magazine Article, sibling ngay dưới "Các tin khác"
   (`.thekoco-related-posts` trên) trong cùng `<article class="thekoco-single-post__main">`. Cùng
   khoảng cách `margin-top` với sibling — không phát minh spacing mới. Card-level styling (2 tông
   nền độc lập, grid-template-areas, brand/rating cùng hàng...) TÁI DÙNG NGUYÊN
   `.thekoco-product-related`/`.thekoco-product-related__heading`/`.product-card--dark` đã có sẵn
   cho Product Detail (PD-19/PD-20 FINAL, dòng ~3797-3934) — xem rule grid riêng ngay cạnh khối đó
   cho lý do KHÔNG tái dùng nguyên `.thekoco-product-related .product-grid` (giả định cố định 4 sản
   phẩm, không đúng cho Magazine Article — 1-6 sản phẩm biến động theo Admin H04-B). */
.thekoco-single-post__related-products { margin-top: var(--space-4); }

/* Sidebar — sticky trên Desktop (đúng tiền lệ `.thekoco-cart-summary`), tắt sticky ở Tablet/Mobile
   (xem @media, giống pattern position:static đã dùng cho Cart Summary). */
.thekoco-post-sidebar { position: sticky; top: var(--space-6); display: flex; flex-direction: column; gap: var(--space-8); }
.thekoco-post-sidebar__heading { font-size: var(--font-size-base); font-weight: var(--font-weight-semibold); margin-bottom: var(--space-4); }
.thekoco-post-sidebar__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-4); }
.thekoco-post-sidebar__item a { display: flex; align-items: center; gap: var(--space-3); color: inherit; }
.thekoco-post-sidebar__thumb { flex: 0 0 auto; width: 64px; height: 64px; border-radius: var(--radius-base); overflow: hidden; }
.thekoco-post-sidebar__thumb img { width: 100%; height: 100%; object-fit: cover; }
.thekoco-post-sidebar__text { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.thekoco-post-sidebar__title { font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.thekoco-post-sidebar__item a:hover .thekoco-post-sidebar__title { color: var(--color-brand-primary); }
.thekoco-post-sidebar__date { font-size: var(--font-size-xs); color: var(--color-text-muted); }
/* ^ `.thekoco-toc*`/`.thekoco-post-nav*`/`.thekoco-post-sidebar*` (dòng 2463-2504) GIỮ NGUYÊN dù
   Phase 2B (dưới đây) đã bỏ 3 khối này khỏi markup `single.php` — component vẫn hợp lệ, có thể tái
   dùng lại sau (VD nếu Product Owner đổi ý, hoặc dùng cho ngữ cảnh khác) — xem
   docs/06_Test/IMPLEMENTATION_ARTICLE_DETAIL_TAP_CHI.md. */

/* ================= Article Detail — Phase 2B Dark Theme (2026-09-03, xem
   docs/06_Test/AUDIT_ARTICLE_DETAIL_TAP_CHI.md + IMPLEMENTATION_ARTICLE_DETAIL_TAP_CHI.md) =========
   Design: `design/23-8/tap chi/chi-tiet-tap-chi.jpg` — nền navy xuyên suốt, layout 1 cột full-width
   canh giữa (bỏ Sidebar), H1 serif lớn canh giữa, body/H2 dark-theme, ảnh trong content góc vuông.
   Scope bằng `body.single-post` (WordPress core tự thêm class này cho MỌI bài `post` — đã verify DOM
   thật, cùng cơ chế `body.blog` dùng cho Magazine Listing ở CHG-066) — KHÔNG dùng selector rộng kiểu
   `body.single-post h2`/`body.single-post p`, mọi rule đều gắn thẳng vào class cụ thể
   (`.thekoco-single-post__*`) để sau này tách CPT Magazine riêng (nếu có) không phải viết lại từ đầu,
   chỉ cần đổi điều kiện scope. */
body.single-post { background: var(--color-brand-primary); }
/* Container Width (CHG-068.1, 2026-09-03) — TÁI DÙNG NGUYÊN pattern đã duyệt cho Magazine Listing
   (CHG-066.1 § Issue #1, `body.blog .thekoco-archive { max-width:none; padding-inline:34px; }`) —
   KHÔNG phát minh width system mới, KHÔNG sửa `.container` global. `.thekoco-single-post` VỪA LÀ
   `.container` (cùng 1 phần tử markup `<div class="container thekoco-single-post">`, xem
   `single.php`), nên rule 2-class-selector này override đúng đích: bỏ `max-width:1320px` (full-bleed
   theo viewport, không còn co lại giữa màn hình rộng), lề cố định 34px mọi viewport — cùng số đo
   Figma đã dùng cho `/tap-chi/` (~33-34px), không hardcode `1278px`. Category/Tag/Search/Account/Shop
   (đều dùng `.container` nhưng KHÔNG có `body.single-post`) hoàn toàn không bị ảnh hưởng. */
body.single-post .thekoco-single-post { max-width: none; padding-inline: 34px; color: var(--color-text-on-dark); }

/* Breadcrumb (CHG-068.2, 2026-09-03) — Root cause: `template-parts/components/breadcrumb.php` dùng
   CHUNG cho toàn site, CSS gốc (`.thekoco-breadcrumb*`, dòng ~2434+) dùng token LIGHT-theme
   (`--color-text-muted:#6b6b6b`, `--color-text:#111111`) — đúng cho nền trắng (Product/Category/
   Search/Account) nhưng CHƯA TỪNG có override dark-theme cho Article Detail (khác Magazine Listing
   đã có `body.blog .thekoco-breadcrumb*` từ CHG-066/067). Đo DOM thật xác nhận: link
   `rgb(107,107,107)` — mờ trên nền navy `rgb(31,35,46)`; **current page `rgb(17,17,17)` — gần như
   VÔ HÌNH** (tỷ lệ tương phản ~1.05:1, đúng bug "current page bị chìm" đã báo). Separator hiện tại
   `rgb(226,226,226)` (`--color-border`) tình cờ ĐÃ đủ sáng — vẫn đổi sang `--color-text-on-dark-muted`
   để cùng "gia đình" độ sáng với link (nhất quán hierarchy Link/Separator/Current), không phải vì
   separator cũ sai.
   TÁI DÙNG NGUYÊN token + hover-color đã dùng cho `body.blog .thekoco-breadcrumb*` (CHG-066/067) —
   CHỈ đổi scope `body.blog` → `body.single-post`, KHÔNG phát minh màu mới, KHÔNG sửa
   `breadcrumb.php` (markup/separator character/spacing đã đúng từ CHG-067, không cần đụng). Hover
   dùng `--color-brand-accent` (gold) CHỈ khi hover — trạng thái nghỉ (resting state) của Link/
   Separator/Current đều KHÔNG gold, đúng AC-05. */
body.single-post .thekoco-breadcrumb { color: var(--color-text-on-dark-muted); }
body.single-post .thekoco-breadcrumb__item a { color: var(--color-text-on-dark-muted); }
body.single-post .thekoco-breadcrumb__item a:hover { color: var(--color-brand-accent); }
body.single-post .thekoco-breadcrumb__item [aria-current="page"] { color: var(--color-text-on-dark); }
body.single-post .thekoco-breadcrumb__item:not(:last-child)::after { color: var(--color-text-on-dark-muted); }

/* Bỏ Sidebar (mục 5.2 Prompt Phase 2B) — `<aside>` đã bỏ khỏi markup, `.thekoco-single-post__layout`
   không còn cần grid 2 cột, `<article>` (con DUY NHẤT còn lại) tự nhiên chiếm full width
   `.container` có sẵn — KHÔNG hardcode width mới. */
body.single-post .thekoco-single-post__layout { display: block; }

/* H1 — serif lớn, canh giữa, tái dùng ĐÚNG font-family/font-size/font-weight đã dùng cho
   `.block-magazine .section-heading` (Playfair Display đã load sẵn toàn site từ CHG-066.1, KHÔNG
   import font mới) — KHÔNG giới hạn `max-width` để ép xuống 2 dòng như ảnh Design: độ dài tiêu đề
   khác nhau theo từng bài, ép `max-width` cố định sẽ làm tiêu đề NGẮN bị xuống dòng sai/thiếu tự
   nhiên — 2 dòng trong Design là hệ quả của ĐỘ DÀI văn bản bài mẫu, không phải yêu cầu bố cục cứng. */
body.single-post .thekoco-single-post__title {
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: var(--font-size-4xl); font-weight: var(--font-weight-regular);
    line-height: var(--line-height-tight); text-align: center; color: var(--color-text-on-dark);
}

/* Meta — "Tạp chí Koco | DD/MM/YYYY", 1 dòng canh giữa (đổi hẳn cấu trúc theo yêu cầu Prompt mục 8,
   KHÔNG còn `__author`/`__modified`/`__reading-time` trong markup — 3 class CSS cũ tương ứng chưa
   từng có rule riêng, tự bỏ theo, không cần dọn gì thêm). */
body.single-post .thekoco-single-post__meta { justify-content: center; color: var(--color-text-on-dark-muted); }
body.single-post .thekoco-single-post__meta-sep { color: var(--color-text-on-dark-muted); opacity: 0.6; }

/* Hero Image — full article width, ~2:1 (ước lượng đo `chi-tiet-tap-chi.jpg`, KHÔNG có Figma MCP
   sống để đo chính xác — xem AUDIT mục 8), góc VUÔNG (bỏ `border-radius` cũ). `object-fit:cover`
   đúng nguyên tắc "không bao giờ làm méo ảnh" (Prompt mục 9) — CROP theo tâm ảnh thay vì kéo giãn
   sai tỷ lệ. Ảnh nguồn hiện tại chỉ 417×369px (đã verify qua `naturalWidth` — xem Known Limitations
   trong Implementation Report) nên sẽ bị phóng to/mềm nét trên màn rộng — đây là giới hạn ASSET, CSS
   này không thể và không nên "sửa" bằng cách làm méo ảnh. */
body.single-post .thekoco-single-post__thumb { border-radius: 0; aspect-ratio: 2 / 1; overflow: hidden; }
body.single-post .thekoco-single-post__thumb img { width: 100%; height: 100%; object-fit: cover; object-position: center; }

/* Article Body — H2 sáng, Paragraph hơi mềm hơn (đúng phân cấp Design: heading rõ hơn body text). */
body.single-post .thekoco-single-post__content h2,
body.single-post .thekoco-single-post__content h3 { color: var(--color-text-on-dark); font-weight: var(--font-weight-medium); }
body.single-post .thekoco-single-post__content p,
body.single-post .thekoco-single-post__content ul,
body.single-post .thekoco-single-post__content ol { color: var(--color-text-on-dark-muted); }

/* Ảnh đơn lẻ trong content (Workstream 7 Pattern B — ảnh lớn full-width) — góc vuông, có khoảng
   thở trên/dưới, KHÔNG đổi `object-fit`/tỷ lệ (ảnh Admin tự chèn, giữ nguyên crop gốc). Kế thừa
   `img{max-width:100%;height:auto;display:block}` (reset toàn site, dòng 53) — chỉ bổ sung margin +
   bỏ radius trong ngữ cảnh Article Detail. */
body.single-post .thekoco-single-post__content img { margin-block: var(--space-8); border-radius: 0; }

/* Content Image Group (Workstream 7 Pattern A — nhóm 3 ảnh ngang) — utility class Admin tự thêm qua
   Text mode của Classic Editor (dự án dùng Classic Editor cho `post`, xem ADR-016 — KHÔNG đổi quyết
   định Editor, KHÔNG cần Gutenberg Gallery/Columns block). Component MỚI DUY NHẤT của Phase 2B —
   scope trong `.thekoco-single-post__content` nên không ảnh hưởng ảnh ở bất kỳ đâu khác ngoài Article
   Detail. 3 → 2 → 1 cột responsive, KHÔNG hardcode kích thước pixel từng ảnh (chỉ `aspect-ratio`,
   ước lượng gần-vuông theo Design, Admin có thể co giãn tự nhiên theo viewport). */
.thekoco-single-post__content .thekoco-content-image-group {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4);
    margin-block: var(--space-8);
}
.thekoco-single-post__content .thekoco-content-image-group img {
    margin-block: 0; aspect-ratio: 1 / 1; object-fit: cover; width: 100%; height: 100%; border-radius: 0;
}

/* Tags — canh giữa (Design "TAGS:" canh giữa; Product Owner quyết định giữ label tiếng Việt "THẺ:"
   viết hoa thay vì đổi hẳn tiếng Anh — xem Prompt Phase 2B mục 13), pill dark-theme (border sáng mờ
   thay vì `--color-border-dark` — token đó gần trùng màu nền navy, ĐÃ xác nhận là bug thật ở
   breadcrumb CHG-067.1, không lặp lại lỗi tương tự ở đây). */
body.single-post .thekoco-single-post__tags { justify-content: center; }
body.single-post .thekoco-single-post__tags-label { color: var(--color-text-on-dark-muted); text-transform: uppercase; letter-spacing: 0.04em; }
body.single-post .thekoco-pill.thekoco-pill--tag { color: var(--color-text-on-dark-muted); border-color: rgba(255, 255, 255, 0.28); }
body.single-post .thekoco-pill.thekoco-pill--tag:hover { color: var(--color-brand-accent); border-color: var(--color-brand-accent); }

/* Share — canh giữa, 5 icon (Facebook/Twitter/YouTube/Instagram/TikTok, xem `single.php`), KHÔNG còn
   border-bottom ngăn cách (Design không có đường kẻ) — override CỤC BỘ trong scope dark-theme, KHÔNG
   sửa rule gốc `.thekoco-single-post__share` (dòng 2475, vẫn `border-bottom` mặc định nếu context nào
   khác tái dùng sau này). Icon màu sáng mờ (outline "trắng nhạt" theo Design) thay vì
   `--color-text-muted` (màu cho nền sáng, gần như vô hình trên navy). */
body.single-post .thekoco-single-post__share { justify-content: center; border-bottom: 0; padding-bottom: 0; }
body.single-post .thekoco-single-post__share-label { color: var(--color-text-on-dark-muted); text-transform: uppercase; letter-spacing: 0.04em; }
body.single-post .thekoco-single-post__share a { color: var(--color-text-on-dark-muted); }
body.single-post .thekoco-single-post__share a:hover { color: var(--color-brand-accent); }

/* Related Posts heading — ĐÃ ĐÚNG màu gold sẵn (`.section-heading` base, dòng 143) — chỉ bổ sung
   font-family Playfair cho khớp họ heading Magazine (đồng bộ `.block-magazine .section-heading`,
   KHÔNG phát minh style mới). Card kế thừa NGUYÊN `article-card--magazine.php` (đã dùng cho Magazine
   Listing, CHG-066/067) — KHÔNG sửa component, tự full-width sau khi bỏ Sidebar (CHG-068) + full
   viewport (CHG-068.1, mục Container Width phía trên). */
body.single-post .thekoco-related-posts .section-heading { font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base); }
/* Grid gap 26px (CHG-068.1) — TÁI DÙNG đúng giá trị `body.blog .article-grid { gap: 26px }` đã duyệt
   cho Magazine Listing (CHG-066.1, cùng giá trị `.block-magazine .article-grid` gốc CHG-065) — Related
   Posts ở Article Detail trước đây kế thừa `.article-grid { gap: 18px }` mặc định (dùng chung Category/
   Tag/Search) vì không có scope `body.blog`/`body.single-post` riêng cho gap. Đồng bộ 3 nơi dùng
   `article-card--magazine.php` (Homepage/Listing/Detail-related) về CÙNG 1 gap. */
body.single-post .thekoco-related-posts .article-grid { gap: 26px; }

/* Reading Progress — thanh cố định đầu viewport, chỉ render ở Single Post (data attribute gate),
   không ảnh hưởng layout page (position: fixed, không chiếm chỗ document flow). */
.thekoco-reading-progress { position: fixed; top: 0; left: 0; right: 0; height: 3px; background: var(--color-bg-subtle); z-index: 100; }
.thekoco-reading-progress__bar { display: block; height: 100%; width: 0%; background: var(--color-brand-primary); transition: width 0.1s linear; }

/* ================= Shop/Category/Brand Archive (Sprint WooCommerce Foundation) =================
   `.thekoco-breadcrumb` (base font-size/color/margin) đã tồn tại từ `single.php` — style thêm ở
   đây chỉ áp dụng cho cấu trúc <ol><li> mới, không đụng usage <p> cũ. */
/* Separator "/" → ">" (Phase 2A.2 Visual Refinement, 2026-09-03 — Figma `Tap-chi.jpg`: "Trang chủ
   >  Tạp chí", khoảng cách 2 bên ">" rộng hơn hẳn kiểu "/" cũ). Sửa ở ĐÚNG 1 chỗ DUY NHẤT
   (`.thekoco-breadcrumb__item`) — component dùng chung Shop/Category/Search/Product/Cart/Checkout/
   Account/Magazine/Article Detail (xem `template-parts/components/breadcrumb.php`), không có ngoại
   lệ ghi nhận cho trang nào cần giữ "/" riêng → đổi global, mọi nơi tự hưởng đồng bộ, không tạo
   override theo route. `gap`/`margin-left` đổi từ `--space-1` (4px) sang `--space-2` (8px, token có
   sẵn, không phát minh giá trị mới) — khớp khoảng thở rộng hơn quan sát được quanh ">" trong Figma. */
.thekoco-breadcrumb__list { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); list-style: none; margin: 0; padding: 0; }
.thekoco-breadcrumb__item:not(:last-child)::after { content: '>'; margin-left: var(--space-2); color: var(--color-border); }
.thekoco-breadcrumb__item a { color: var(--color-text-muted); }
.thekoco-breadcrumb__item a:hover { color: var(--color-brand-primary); }
.thekoco-breadcrumb__item [aria-current="page"] { color: var(--color-text); }

.thekoco-shop { padding-block: var(--space-10); min-height: 50vh; }
.thekoco-shop__header { margin-bottom: var(--space-8); }
.thekoco-shop__title { font-size: var(--font-size-3xl); font-weight: var(--font-weight-bold); color: var(--color-text); margin-bottom: var(--space-2); }
.thekoco-shop__description { color: var(--color-text-muted); line-height: var(--line-height-base); max-width: 760px; }

.thekoco-shop__toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-6); padding-bottom: var(--space-4); border-bottom: 1px solid var(--color-border); }

.thekoco-product-count { margin: 0; color: var(--color-text-muted); font-size: var(--font-size-sm); }

.thekoco-sorting__select {
    height: 40px;
    padding-inline: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-base);
    background: var(--color-bg);
    color: var(--color-text);
    font-size: var(--font-size-sm);
}

.thekoco-pagination { display: flex; justify-content: center; margin-top: var(--space-10); }
.thekoco-pagination__list { display: flex; align-items: center; gap: var(--space-2); list-style: none; margin: 0; padding: 0; }
.thekoco-pagination__item a,
.thekoco-pagination__item span {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 40px; height: 40px; padding-inline: var(--space-2);
    border-radius: var(--radius-base); border: 1px solid var(--color-border);
    font-size: var(--font-size-sm); color: var(--color-text);
}
.thekoco-pagination__item a:hover { border-color: var(--color-brand-primary); }
.thekoco-pagination__item span.current { background: var(--color-brand-primary); color: var(--color-text-on-dark); border-color: var(--color-brand-primary); }
.thekoco-pagination__item span.dots { border: 0; }

.thekoco-empty-state { text-align: center; padding-block: var(--space-16); }
/* Sprint Framework Localization Phase 3: khung trang 404 (mới) — chỉ cần min-height, empty-state.php tự đủ padding. */
.thekoco-404 { min-height: 50vh; display: flex; align-items: center; justify-content: center; }
.thekoco-empty-state__title { font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold); color: var(--color-text); margin-bottom: var(--space-2); }
.thekoco-empty-state__message { color: var(--color-text-muted); margin-bottom: var(--space-6); }

/* ================= Product Listing Foundation (Sprint PL-01, 2026-09-04) =================
   Category Hero + Sidebar Layout + Catalog Filter Sidebar — CHỈ ảnh hưởng Shop/Category/Brand
   archive (`.thekoco-category-hero`/`.thekoco-shop__layout`/`.thekoco-filter-sidebar--catalog`
   là class MỚI, không đổi rule nào đã có). `.thekoco-shop__layout .product-grid` là selector CÓ
   NGỮ CẢNH — KHÔNG đổi `.product-grid` gốc (dòng ~951, dùng chung Best Sellers/New Products/Sale/
   Related/Search) — đúng nguyên tắc "scoped, không sửa Shared CSS" của Sprint PL-01. */

/* Sprint PL-01B (2026-09-04, D-PL01B-001/002): Hero ĐÃ VIẾT LẠI — trước đây ảnh/gradient là 1 `<div>`
   BO GÓC RIÊNG bên trong `.thekoco-category-hero__container` (giới hạn 1320px, trông như "card cạnh
   khối chữ") — SAI cấu trúc thiết kế gốc. Giờ ảnh/gradient là `background-image` của CHÍNH
   `<section>` (biến CSS `--hero-bg`, gán qua inline style trong `category-hero.php` — `url(...)` nếu
   Admin đã upload, gradient trang trí nếu chưa) — TRẢI HẾT CHIỀU RỘNG THẬT (section không có
   `max-width`), chữ nổi lên qua lớp `::before` scrim — TÁI DÙNG NGUYÊN pattern
   `.block-hero__slide--cover::before` đã có sẵn cho Homepage Hero (xem dòng ~766), chỉ đổi hướng màu
   (scrim SÁNG màu kem — vì Category Hero dùng chữ tối `--color-text` trên nền sáng, khác Homepage
   Hero dùng chữ sáng trên nền tối).
   Sprint "Hero Content Width" (2026-09-04, D-PL01C-005): `.thekoco-category-hero__container` TRƯỚC
   ĐÂY mang class `.container` (1320px, `margin-inline:auto` → TỰ CANH GIỮA) — ở viewport >1320px,
   khối chữ bị đẩy vào giữa màn hình thay vì áp sát mép trái ĐÚNG `San-pham.jpg` (design gốc, đo trực
   tiếp: breadcrumb/H1/mô tả đều bắt đầu ở x≈35px từ mép trái canvas 1366px). Giờ dùng gutter CỐ
   ĐỊNH `padding-inline:34px` (KHÔNG center, KHÔNG mang class `.container` nữa) — TÁI DÙNG ĐÚNG giá
   trị `34px` đã dùng cho `.thekoco-shop` (Listing bên dưới Hero) và mọi block full-bleed khác trong
   file này (Best Sellers/Promo Banners/New Products/Trend/Magazine) — không phát minh số mới, và
   khớp đúng số đo từ design (35px ≈ 34px, sai số đo bằng mắt trên ảnh nén JPEG). KHÔNG sửa
   `.container` gốc (rule dùng chung, không đụng) — chỉ đổi CLASS ÁP DỤNG lên phần tử này (bỏ
   `.container`, thêm CSS riêng ngay dưới đây). */
.thekoco-category-hero {
    position: relative; overflow: hidden;
    background-color: var(--color-bg-subtle);
    background-image: var(--hero-bg); background-size: cover; background-position: center;
    padding-block: var(--space-16) var(--space-20);
}
.thekoco-category-hero::before {
    content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
    background: linear-gradient(100deg, rgba(255, 253, 247, 0.94) 0%, rgba(255, 253, 247, 0.62) 42%, rgba(255, 253, 247, 0) 68%);
}
.thekoco-category-hero__container { position: relative; z-index: 1; padding-inline: 34px; }
.thekoco-category-hero__content { max-width: 560px; }
.thekoco-category-hero__content .thekoco-breadcrumb { margin-bottom: var(--space-6); }
.thekoco-category-hero__title { font-size: var(--font-size-4xl); font-weight: var(--font-weight-bold); color: var(--color-text); margin-bottom: var(--space-3); }
.thekoco-category-hero__description { color: var(--color-text-muted); line-height: var(--line-height-base); }

/* Sprint PL-01F (2026-09-04, D-PL01C-006) — Breadcrumb trong Category Hero: ĐÚNG chuẩn Component
   `.thekoco-breadcrumb*` là RULE DÙNG CHUNG toàn site (Shop/Category/Brand/Search/Product/Cart/
   Checkout/My Account/Blog — xem comment gốc dòng ~2651) — override RIÊNG trong Category Hero qua
   selector CÓ NGỮ CẢNH `.thekoco-category-hero .thekoco-breadcrumb*`, KHÔNG sửa rule gốc (dòng
   ~2434/2656-2660) — TÁI DÙNG chính pattern đã có sẵn cho `.thekoco-shop`/`body.blog`/
   `body.single-post` (mỗi ngữ cảnh 1 khối override riêng, không đụng nhau, không đụng gốc).
   ROOT CAUSE "quá nhỏ/quá nhạt" (Product Owner báo, đối chiếu `San-pham.jpg`):
   1) `font-size: var(--font-size-xs)` (12px, rule gốc) — nhỏ hơn rõ rệt so với đo được trên design
      (~14-16px) → tăng lên `--font-size-sm` (14px, token có sẵn, không phát minh số mới) — vẫn nhỏ
      hơn H1 (`--font-size-4xl`=48px) RÕ RỆT, không cạnh tranh thị giác.
   2) Separator `::after` dùng `color: var(--color-border)` (#e2e2e2 — token dành cho viền 1px mảnh,
      KHÔNG phải màu chữ) → trên nền sáng Hero gần như VÔ HÌNH (tỷ lệ tương phản ~1.1:1) — ĐÂY LÀ
      NGUYÊN NHÂN CHÍNH khiến cả dải breadcrumb "chìm" dù chữ chính vẫn đọc được — đổi sang
      `--color-text-muted` (cùng màu item text, token ĐÃ DÙNG cho chữ breadcrumb, không phát minh
      màu mới) — nhẹ nhàng nhưng đủ nhìn thấy, đúng yêu cầu "separator nhẹ nhàng, không nặng".
   3) Item hiện tại (`[aria-current="page"]`) đã dùng `--color-text` (tối hơn, đúng hierarchy) nhưng
      CÙNG font-weight với các item khác → thêm `font-weight: var(--font-weight-medium)` (500, token
      có sẵn) để "current category dễ nhận biết" (brief mục 4) mà không cần đổi màu.
   4) `gap: var(--space-2)` (8px, rule gốc) hơi chật khi chữ lớn hơn (mục 1) → tăng lên `--space-3`
      (12px) — CHỈ trong Hero, danh sách breadcrumb Shop/Product/Blog khác giữ nguyên 8px.
   Hover/focus: thêm `:focus-visible` cạnh `:hover` (trước đây chỉ có `:hover`) — đúng yêu cầu "trạng
   thái hover/focus rõ" (brief mục 5), TÁI DÙNG màu hover đã có (`--color-brand-primary`). */
.thekoco-category-hero .thekoco-breadcrumb { font-size: var(--font-size-sm); }
.thekoco-category-hero .thekoco-breadcrumb__list { gap: var(--space-3); }
.thekoco-category-hero .thekoco-breadcrumb__item a:hover,
.thekoco-category-hero .thekoco-breadcrumb__item a:focus-visible { color: var(--color-brand-primary); }
.thekoco-category-hero .thekoco-breadcrumb__item [aria-current="page"] { font-weight: var(--font-weight-medium); }
.thekoco-category-hero .thekoco-breadcrumb__item:not(:last-child)::after { color: var(--color-text-muted); }

/* Sprint PL-01A (2026-09-04, D-PL01A-001): `.thekoco-shop` full-bleed — ĐÚNG pattern
   `.container{max-width:none}` đã dùng cho Header/Footer/Best Sellers/Promo Banners/New Products/
   Trend/Magazine/`body.blog .thekoco-archive` (xem khối "Magazine Listing — Dark Theme" bên dưới,
   cùng lý do: `.container` mặc định 1320px làm nội dung hẹp hơn hẳn Homepage/Magazine). Dùng ĐÚNG
   `padding-inline: 34px` — giá trị đã tồn tại sẵn (không phát minh số mới), cùng giá trị các block
   full-bleed khác đang dùng. KHÔNG đổi `.container` gốc — chỉ override có ngữ cảnh `.thekoco-shop`. */
.thekoco-shop { max-width: none; padding-inline: 34px; }

/* Sprint PL-01B (2026-09-04, D-PL01B-003): "Nền Product Listing đang trắng thuần, KHÔNG khớp môi
   trường thị giác KOCO" — Product Owner xác nhận qua đối chiếu ảnh chụp thật với thiết kế gốc. Khôi
   phục nền tối — TÁI DÙNG NGUYÊN token đã dùng cho Homepage (`--color-brand-primary`, chính token
   Trust/Best Sellers/Header/Footer đang dùng — KHÔNG phát minh màu mới) + chữ sáng
   (`--color-text-on-dark*`, cùng token Magazine Dark Theme `body.blog` đã dùng, xem khối bên dưới).
   Scope `.thekoco-shop` — ĐÚNG class đã full-bleed ở D-PL01A-001, phủ toàn bộ Shop/Category/Brand
   VÀ Search Results (search-loop-content.php CŨNG mang class `.thekoco-shop`, side effect ĐÃ XÁC
   NHẬN có chủ đích — Search giờ cùng ngôn ngữ thị giác, không "trắng lạc quẻ" giữa 1 site chủ yếu
   nền tối, xem CHANGE_LOG_PRODUCT_LISTING_PL01B.md). Category Hero (section riêng, PHÍA TRÊN
   `.thekoco-shop`) GIỮ NGUYÊN nền sáng `--color-bg-subtle` — khớp `.block-hero` Homepage (dòng
   ~731, CŨNG dùng `--color-bg-subtle`) — "predominantly white" mà brief PL-01B phàn nàn là vùng
   Listing (sidebar/toolbar/card) bên DƯỚI Hero, không phải Hero. */
.thekoco-shop { background: var(--color-brand-primary); color: var(--color-text-on-dark); }
.thekoco-shop .thekoco-breadcrumb__item a { color: var(--color-text-on-dark-muted); }
.thekoco-shop .thekoco-breadcrumb__item a:hover { color: var(--color-brand-accent); }
.thekoco-shop .thekoco-breadcrumb__item [aria-current="page"] { color: var(--color-text-on-dark); }
.thekoco-shop .thekoco-breadcrumb__item:not(:last-child)::after { color: var(--color-text-on-dark-muted); }
.thekoco-shop__title { color: var(--color-text-on-dark); }
.thekoco-shop__description { color: var(--color-text-on-dark-muted); }
.thekoco-shop__toolbar { border-bottom-color: var(--color-border-dark); }
.thekoco-product-count { color: var(--color-text-on-dark-muted); }
.thekoco-sorting__select { background: var(--color-footer-lower-bg); border-color: var(--color-border-dark); color: var(--color-text-on-dark); }
.thekoco-empty-state__title { color: var(--color-text-on-dark); }
.thekoco-empty-state__message { color: var(--color-text-on-dark-muted); }
.thekoco-empty-state .btn--dark { background: var(--color-brand-accent); color: var(--color-brand-primary); }

/* Pagination — TÁI DÙNG NGUYÊN cặp giá trị `body.blog .thekoco-pagination*` đã duyệt cho Magazine
   Dark Theme (xem khối bên dưới, dòng ~2960+) thay vì phát minh màu mới — cùng vấn đề "chữ/viền
   mặc định gần như vô hình trên nền navy", cùng cách sửa (gold accent cho trang hiện tại). */
.thekoco-shop .thekoco-pagination__item a,
.thekoco-shop .thekoco-pagination__item span {
    color: var(--color-text-on-dark); border-color: var(--color-border-dark);
}
.thekoco-shop .thekoco-pagination__item a:hover { border-color: var(--color-brand-accent); }
.thekoco-shop .thekoco-pagination__item span.current {
    background: var(--color-brand-accent); color: var(--color-brand-primary); border-color: var(--color-brand-accent);
}

/* Filter Sidebar (Category/Shop/Brand VÀ Search — `.thekoco-filter-sidebar` dùng chung cả
   `catalog-filter-sidebar.php` lẫn `filter-sidebar.php`, class `__label`/`__select` chỉ Search dùng,
   `__summary`/`__checkbox-list`/`__category-link`/`__chip` chỉ Listing dùng — cả 2 nhóm cần dark-
   aware để không bị "chữ tối trên nền tối" sau khi `.thekoco-shop` đổi nền). "Gold section titles"
   (brief PL-01B mục 11) = `--color-brand-accent`, ĐÚNG token accent duy nhất của site. */
.thekoco-shop .thekoco-filter-sidebar { background: var(--color-footer-lower-bg); border-color: var(--color-border-dark); }
.thekoco-shop .thekoco-filter-sidebar__label { color: var(--color-brand-accent); }
.thekoco-shop .thekoco-filter-sidebar__select { background: var(--color-brand-primary); border-color: var(--color-border-dark); color: var(--color-text-on-dark); }
.thekoco-shop .thekoco-filter-sidebar__summary { color: var(--color-brand-accent); }
.thekoco-shop .thekoco-filter-sidebar__summary::after { color: var(--color-text-on-dark-muted); }
.thekoco-shop .thekoco-filter-sidebar__section { border-bottom-color: var(--color-border-dark); }
.thekoco-shop .thekoco-filter-sidebar__checkbox { color: var(--color-text-on-dark); }
.thekoco-shop .thekoco-filter-sidebar__checkbox input,
.thekoco-shop .thekoco-filter-sidebar__group input[type="checkbox"] { accent-color: var(--color-brand-accent); }
.thekoco-shop .thekoco-filter-sidebar__category-link { color: var(--color-text-on-dark-muted); }
.thekoco-shop .thekoco-filter-sidebar__category-link:hover { color: var(--color-brand-accent); }
.thekoco-shop .thekoco-filter-sidebar__category-link.is-active { color: var(--color-brand-accent); }
.thekoco-shop .thekoco-filter-sidebar__count { color: var(--color-text-on-dark-muted); }
.thekoco-shop .thekoco-filter-sidebar__checkbox-list { scrollbar-color: var(--color-border-dark) transparent; }
.thekoco-shop .thekoco-filter-sidebar__checkbox-list::-webkit-scrollbar-thumb { background: var(--color-border-dark); }
/* Tag chip — dark theme: viền/nền tối mặc định, chip ĐÃ CHỌN dùng gold accent (đúng brief §6 "dark
   background, gold/yellow accent, white/light text" — KHÔNG phát minh màu mới, TÁI DÙNG
   `--color-brand-accent` đã dùng cho mọi trạng thái "active/selected" khác trong Sidebar này, VD
   Category `.is-active`). */
.thekoco-shop .thekoco-filter-sidebar__tag-label { border-color: var(--color-border-dark); color: var(--color-text-on-dark-muted); }
.thekoco-shop .thekoco-filter-sidebar__tag-label:hover { border-color: var(--color-brand-accent); }
.thekoco-shop .thekoco-filter-sidebar__tag-input:checked + .thekoco-filter-sidebar__tag-label {
    background: var(--color-brand-accent); border-color: var(--color-brand-accent); color: var(--color-brand-primary);
}
.thekoco-shop .thekoco-filter-sidebar__tag-input:focus-visible + .thekoco-filter-sidebar__tag-label { outline-color: var(--color-brand-accent); }
.thekoco-shop .thekoco-filter-sidebar__tag-list { scrollbar-color: var(--color-border-dark) transparent; }
.thekoco-shop .thekoco-filter-sidebar__tag-list::-webkit-scrollbar-thumb { background: var(--color-border-dark); }
.thekoco-shop .thekoco-filter-sidebar__chip { background: var(--color-brand-primary); border-color: var(--color-border-dark); color: var(--color-text-on-dark); }
.thekoco-shop .thekoco-filter-sidebar__chip span { color: var(--color-text-on-dark-muted); }
.thekoco-shop .thekoco-filter-sidebar__reset { color: var(--color-text-on-dark-muted); }
.thekoco-shop .thekoco-input { background: var(--color-brand-primary); border-color: var(--color-border-dark); color: var(--color-text-on-dark); }
.thekoco-shop .thekoco-filter-sidebar__price-sep { color: var(--color-text-on-dark-muted); }
.thekoco-shop .thekoco-filter-sidebar__submit { background: var(--color-brand-accent); color: var(--color-brand-primary); }
.thekoco-shop .thekoco-filter-sidebar__submit:hover { opacity: 0.9; }
.thekoco-shop .screen-reader-text { color: inherit; }

/* Filter Toggle button (mobile/tablet "Bộ lọc") — cùng dark treatment. */
.thekoco-shop .thekoco-shop__filter-toggle-label { border-color: var(--color-border-dark); color: var(--color-text-on-dark); }

/* Sprint PL-01A (D-PL01A-002): Sidebar chiều rộng CỐ ĐỊNH TRONG KHOẢNG (không phải tỉ lệ `fr` — tỉ lệ
   sẽ làm sidebar phình to vô lý ở màn hình rất rộng, VD 3fr ở viewport 1920px ≈ 570px, quá khổ cho 1
   Filter Sidebar). Vùng Grid nhận toàn bộ không gian còn lại (`minmax(0,1fr)`) — fluid, không giới hạn
   trên, đúng yêu cầu "khai thác hết chiều rộng khả dụng". */
.thekoco-shop__layout { display: grid; grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); gap: var(--space-8); align-items: start; }
.thekoco-shop__sidebar { position: sticky; top: var(--space-6); }

/* Sprint PL-01A (D-PL01A-002): Grid FLUID (`auto-fill`/`minmax`) thay vì cố định 3 cột — số cột tự
   tính theo chiều rộng khả dụng thật (viewport − gutter − sidebar − gap) chia cho chiều rộng tối
   thiểu 1 Card (280px, đủ chỗ ảnh/badge/wishlist/brand/tên 2 dòng/rating/giá/CTA — xem
   CHANGE_LOG_PRODUCT_LISTING_PL01A.md để có bảng tính đầy đủ theo từng breakpoint bắt buộc test).
   CÓ NGỮ CẢNH (`.thekoco-shop__layout .product-grid`) — KHÔNG đụng `.product-grid` gốc (dùng chung
   Best Sellers/New Products/Sale/Related/Search, vẫn giữ `repeat(5,1fr)` như cũ). */
.thekoco-shop__layout .product-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }

/* Sprint PL-01A (D-PL01A-002): "Filter Toggle" — bọc `catalog-filter-sidebar.php` trong 1
   `<details>` native (KHÔNG JS). Desktop: LUÔN hiện nội dung (ép `display:block!important`, bỏ qua
   `open`/`:not([open])` của trình duyệt) + ẩn nút "Bộ lọc". ≤1024px (xem @media bên dưới): nút "Bộ
   lọc" hiện, nội dung mặc định ĐÓNG (không set `open` trong HTML) — bấm nút để mở, đúng pattern
   "Filter button → Filter drawer/expandable panel" (brief PL-01A mục 9), tránh "chỉ co nhỏ Sidebar
   desktop lại" (đã bị cấm rõ ràng). */
.thekoco-shop__filter-toggle-label {
    display: none; align-items: center; gap: var(--space-2);
    padding: var(--space-3) var(--space-4); border: 1px solid var(--color-border);
    border-radius: var(--radius-base); font-size: var(--font-size-sm); font-weight: var(--font-weight-medium);
    color: var(--color-text); cursor: pointer; list-style: none;
}
.thekoco-shop__filter-toggle-label::-webkit-details-marker { display: none; }
.thekoco-shop__filter-toggle-body { display: block; }

/* Catalog Filter Sidebar — tái dùng NGUYÊN `.thekoco-filter-sidebar*` đã có (Search), chỉ thêm class
   MỚI cho phần Category page cần mà Search chưa có (chip/reset/section collapsible/category nav). */
.thekoco-filter-sidebar__chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-5); }
.thekoco-filter-sidebar__chip {
    display: inline-flex; align-items: center; gap: var(--space-1);
    padding: var(--space-1) var(--space-3); border-radius: var(--radius-full);
    background: var(--color-bg-subtle); border: 1px solid var(--color-border);
    font-size: var(--font-size-xs); color: var(--color-text);
}
.thekoco-filter-sidebar__chip:hover { border-color: var(--color-brand-primary); }
.thekoco-filter-sidebar__chip span { color: var(--color-text-muted); font-size: var(--font-size-sm); }
.thekoco-filter-sidebar__reset { font-size: var(--font-size-xs); color: var(--color-text-muted); text-decoration: underline; align-self: center; }
.thekoco-filter-sidebar__reset:hover { color: var(--color-danger); }

.thekoco-filter-sidebar__section { border-bottom: 1px solid var(--color-border); padding-bottom: var(--space-4); }
.thekoco-filter-sidebar__section + .thekoco-filter-sidebar__section,
.thekoco-filter-sidebar__form .thekoco-filter-sidebar__section:first-child { margin-top: 0; }
.thekoco-filter-sidebar__form { display: flex; flex-direction: column; gap: var(--space-5); }
.thekoco-filter-sidebar__summary {
    cursor: pointer; font-size: var(--font-size-sm); font-weight: var(--font-weight-medium);
    color: var(--color-text); padding-block: var(--space-1); list-style: none;
}
.thekoco-filter-sidebar__summary::-webkit-details-marker { display: none; }
.thekoco-filter-sidebar__summary::after { content: '+'; float: right; color: var(--color-text-muted); }
details[open] > .thekoco-filter-sidebar__summary::after { content: '−'; }

.thekoco-filter-sidebar__category-list { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.thekoco-filter-sidebar__category-link { display: flex; align-items: center; justify-content: space-between; padding-block: var(--space-1); color: var(--color-text-muted); font-size: var(--font-size-sm); }
.thekoco-filter-sidebar__category-link:hover { color: var(--color-brand-primary); }
/* Sprint "Filter Sidebar / Left Scroll" (2026-09-04): "Danh mục" giờ luôn hiện category cấp 1 +
   con của nhánh đang active (thay vì chỉ-con-của-hiện-tại, luôn rỗng — xem catalog-filter-sidebar.php
   § 1). `is-active` đánh dấu category đang xem thật (không phải hover); nested list thụt lề nhẹ để
   phân biệt cấp cha/con, TÁI DÙNG `--space-4`/`--font-size-xs` có sẵn, không phát minh giá trị mới. */
.thekoco-filter-sidebar__category-link.is-active { color: var(--color-brand-primary); font-weight: var(--font-weight-medium); }
.thekoco-filter-sidebar__category-list--nested { margin-top: var(--space-1); padding-left: var(--space-4); gap: 0; }
.thekoco-filter-sidebar__category-list--nested .thekoco-filter-sidebar__category-link { font-size: var(--font-size-xs); }

.thekoco-filter-sidebar__checkbox-list { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-3); max-height: 220px; overflow-y: auto; }
.thekoco-filter-sidebar__checkbox { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); font-size: var(--font-size-sm); color: var(--color-text); cursor: pointer; }
.thekoco-filter-sidebar__checkbox input { flex: 0 0 auto; }
.thekoco-filter-sidebar__count { color: var(--color-text-muted); font-size: var(--font-size-xs); margin-left: auto; }

/* Sprint "Filter Sidebar / TAG" (2026-09-04) — Tag dạng CHIP compact (khác Brand/Attribute dạng
   checkbox-list dọc, đúng brief §6 "không dùng checkbox giống Brand"). Input checkbox thật giữ
   accessible/focusable (KHÔNG display:none — sẽ mất focus/screen-reader), chỉ ẩn TRỰC QUAN bằng
   position:absolute+opacity:0 (kỹ thuật "visually hidden" chuẩn, không dùng display:none). `<label>`
   kế bên đóng vai trò chip hiển thị — style qua sibling selector `:checked + label`, KHÔNG cần JS. */
.thekoco-filter-sidebar__tag-list { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); max-height: 220px; overflow-y: auto; }
.thekoco-filter-sidebar__tag { display: inline-flex; }
.thekoco-filter-sidebar__tag-input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.thekoco-filter-sidebar__tag-label {
    display: inline-flex; align-items: center; gap: var(--space-1);
    padding: var(--space-1) var(--space-3); border-radius: var(--radius-full);
    border: 1px solid var(--color-border); font-size: var(--font-size-xs); color: var(--color-text);
    cursor: pointer; background: transparent; line-height: 1.4;
}
.thekoco-filter-sidebar__tag-label:hover { border-color: var(--color-brand-primary); }
.thekoco-filter-sidebar__tag-input:checked + .thekoco-filter-sidebar__tag-label {
    background: var(--color-brand-primary); border-color: var(--color-brand-primary); color: var(--color-text-on-dark);
}
.thekoco-filter-sidebar__tag-input:focus-visible + .thekoco-filter-sidebar__tag-label { outline: 2px solid var(--color-brand-primary); outline-offset: 2px; }
.thekoco-filter-sidebar__tag-count { opacity: 0.75; font-size: var(--font-size-xs); }
.thekoco-filter-sidebar__tag-list { scrollbar-width: thin; scrollbar-color: var(--color-border) transparent; }
.thekoco-filter-sidebar__tag-list::-webkit-scrollbar { height: 6px; width: 6px; }
.thekoco-filter-sidebar__tag-list::-webkit-scrollbar-track { background: transparent; }
.thekoco-filter-sidebar__tag-list::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: var(--radius-full); }
/* Scrollbar mảnh cho danh sách checkbox dài (Thương hiệu/Tag/Attribute — mục "Scroll Sidebar UI" của
   brief) — thay scrollbar mặc định trình duyệt (dày, tương phản kém trên nền sáng lẫn nền tối) bằng
   1 thanh mảnh dùng token màu có sẵn. `scrollbar-width`/`scrollbar-color` (Firefox) +
   `::-webkit-scrollbar*` (Chrome/Edge/Safari) — cả 2 khai báo cùng lúc, an toàn (trình duyệt tự bỏ
   qua cú pháp không hỗ trợ), không cần JS. */
.thekoco-filter-sidebar__checkbox-list { scrollbar-width: thin; scrollbar-color: var(--color-border) transparent; }
.thekoco-filter-sidebar__checkbox-list::-webkit-scrollbar { width: 6px; }
.thekoco-filter-sidebar__checkbox-list::-webkit-scrollbar-track { background: transparent; }
.thekoco-filter-sidebar__checkbox-list::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: var(--radius-full); }

/* Add to Cart fallback ("Xem sản phẩm"/"Chọn sản phẩm", Variable/không đủ điều kiện mua trực tiếp,
   brief mục 17) — tái dùng NGUYÊN khung/vị trí `.product-card__add-cart` đã có, chỉ đổi màu chữ nền
   để phân biệt trực quan với hành động "Thêm vào giỏ hàng" thật. */
.product-card__add-cart--view { background: var(--color-bg); color: var(--color-brand-primary); border: 1px solid var(--color-border); }

/* Mobile: Add to Cart overlay KHÔNG được phụ thuộc hover (brief mục 18) — CHỈ scope trong
   `.thekoco-shop` (Listing) để KHÔNG đổi hành vi Best Sellers/New Products carousel trên Homepage
   (nơi DUY NHẤT khác đang dùng `.product-card__add-cart` trước Sprint PL-01, xem style.css dòng
   ~1003 khối gốc `:hover`/`:focus-within`). Best Sellers giữ nguyên hover-only. */
@media (hover: none), (max-width: 768px) {
    /* Giữ NGUYÊN `position: absolute`/toạ độ gốc (overlay đáy ảnh, xem rule gốc dòng ~998) — chỉ bỏ
       phụ thuộc hover/focus (thumb là `overflow:hidden` + `aspect-ratio` cố định, đổi sang
       `position:static` sẽ bị cắt mất do tràn khung, KHÔNG dùng cách đó). */
    .thekoco-shop .product-card__add-cart { opacity: 1; transform: translateY(0); pointer-events: auto; }
}

/* Sprint PL-01A (D-PL01A-002) — `<details open>` LUÔN render `open` thật trong HTML (xem
   `shop-loop-content.php`) — KHÔNG dùng CSS ép `display:block!important` như bản nháp đầu tiên: đã
   verify qua `getBoundingClientRect()` thật rằng Chromium dựng nội dung `<details>` ĐÓNG bằng cơ chế
   tương đương `content-visibility:hidden` ở CHÍNH `<details>` — ép `display` lên phần tử CON không
   thắng được, `.thekoco-shop__sidebar` vẫn báo `height:0` cho layout cha dù nội dung bên trong đã
   "hiện". Render `open` thật là cách ĐÚNG chuẩn, không cần CSS/JS đặc biệt gì thêm cho Desktop.
   Trên ≤1024px, chỉ CẦN hiện nút "Bộ lọc" — toggle 2 chiều (đóng/mở) là hành vi GỐC của
   `<summary>`/`<details>`, không cần rule CSS nào khác. */
.thekoco-shop__filter-toggle-body { margin-top: var(--space-3); }

/* Breakpoint 1024px — D-PL01A-002 (lý do đầy đủ, không phải số tuỳ ý): với Sidebar 280px + gap 32px +
   gutter 68px, 2 cột Card tối thiểu 280px cần chiều rộng khả dụng ≥ 892px (2×280+32+280+68) — dưới
   mốc đó, layout "Sidebar cạnh Grid" chỉ còn đủ chỗ cho ĐÚNG 1 cột (nhìn rời rạc/lãng phí không gian,
   đúng lỗi PL-01 mắc phải ở 1024px/834px). 1024 là mốc tròn AN TOÀN phía trên ngưỡng 892px thật —
   dưới mốc này chuyển hẳn sang "Filter Toggle" (ẩn Sidebar khỏi layout, Grid chiếm toàn bộ chiều
   rộng) thay vì chỉ co hẹp tỉ lệ Sidebar/Grid như PL-01 làm (không giải quyết được vấn đề). */
@media (max-width: 1024px) {
    /* Sprint PL-01B: Hero giờ 1 cột chữ (không còn `__visual` riêng — ảnh đã là background của
       chính `<section>`, tự co giãn theo `background-size:cover`, không cần rule đổi tỉ lệ khung
       nữa). Bớt padding-block cho vừa màn hình nhỏ hơn. */
    .thekoco-category-hero { padding-block: var(--space-10) var(--space-12); }
    .thekoco-category-hero__content { max-width: none; }

    .thekoco-shop__layout { grid-template-columns: 1fr; gap: var(--space-5); }
    .thekoco-shop__sidebar { position: static; }
    .thekoco-shop__filter-toggle-label { display: flex; }
    .thekoco-shop__filter-toggle[open] .thekoco-shop__filter-toggle-label { border-radius: var(--radius-base) var(--radius-base) 0 0; border-bottom: 0; }

    /* Grid tầng Tablet (≤1024px, KHÔNG có Sidebar cạnh — xem trên) — min-width 240px (nhỏ hơn 280px
       tầng Desktop vì không còn phải chia chỗ với Sidebar, Card vẫn đủ rộng dù min nhỏ hơn). Kết quả
       ĐÃ ĐO THẬT qua computed style (`getComputedStyle`, không suy đoán): 1024px→3 cột×300px,
       834px→2 cột×365px, 768px→2 cột×352px — mọi Card đều RỘNG HƠN đáng kể so PL-01 (từng ép 3 cột
       CẠNH Sidebar tại 2 mốc này → Card chỉ ~195-255px, chật). Số cột giảm (3→2) nhưng Card RỘNG HƠN
       — đúng ưu tiên "card usability" over "column count" (brief PL-01A mục 7). */
    .thekoco-shop__layout .product-grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
    .thekoco-filter-sidebar__checkbox-list { max-height: none; }
}

@media (max-width: 768px) {
    .thekoco-category-hero { padding-block: var(--space-8) var(--space-10); }
    /* D-PL01C-005: `.thekoco-category-hero__container` không còn mang class `.container` (bỏ ở Sprint
       "Hero Content Width") nên không tự thừa hưởng rule mobile `.container{padding-inline:var(--space-4)}`
       có sẵn — khai lại TƯỜNG MINH ở đây, ĐÚNG giá trị 16px sitewide đang dùng ở mọi nơi khác
       (không phát minh số mới, chỉ đảm bảo không "mất" hành vi cũ do đổi class). */
    .thekoco-category-hero__container { padding-inline: var(--space-4); }
    .thekoco-category-hero__title { font-size: var(--font-size-2xl); }
    /* Scrim rộng hơn ở mobile — cột chữ chiếm gần hết chiều rộng, cần vùng sáng lớn hơn để đọc được
       dù ảnh nền (nếu có) sáng/tối bất kỳ. */
    .thekoco-category-hero::before {
        background: linear-gradient(100deg, rgba(255, 253, 247, 0.96) 0%, rgba(255, 253, 247, 0.8) 55%, rgba(255, 253, 247, 0.35) 100%);
    }
}

/* Breakpoint 600px — D-PL01A-002: dưới mốc này, min-width 240px (tầng Tablet) cho Card quá to/ít cột
   trên điện thoại (VD 390px màn hình → chỉ 1 cột) — không khớp quy ước 2 cột đã dùng xuyên suốt site
   cho Product Grid trên di động (Best Sellers/New Products/Sale, xem `.product-grid` gốc `@media
   768px { grid-template-columns: repeat(2,1fr) }`). Hạ min-width xuống 150px giữ 2 cột ở mọi mốc bắt
   buộc test dưới 600px (430/390/375/360). KHÔNG tự đặt lại `padding-inline` ở đây — gutter 16px đã
   được đảm nhiệm bởi rule `.container{padding-inline:var(--space-4)}` sẵn có, áp dụng từ ≤768px
   (`.thekoco-shop` vẫn mang class `.container`) — ĐO THẬT xác nhận gutter đã là 16px xuyên suốt
   768px→360px, không cần khai báo lại. Số cột đo được (xem CHANGE_LOG_PRODUCT_LISTING_PL01A.md):
   600px→3×181px, 430px→2×193px, 390px→2×173px, 375px→2×165px, 360px→2×158px. */
@media (max-width: 600px) {
    .thekoco-shop__layout .product-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--space-3); }
}

/* ================= Magazine Listing — Dark Theme (Phase 2A, 2026-08-28) =================
   Xem docs/06_Test/AUDIT_TAP_CHI_V2.md mục 4 (P0 "nền trắng thay vì navy") +
   docs/06_Test/IMPLEMENTATION_TAP_CHI_V2.md. Scope CHỈ `/tap-chi/` qua body class `blog` —
   WordPress core TỰ thêm class này khi `is_home()` true (KHÔNG cần PHP mới), và ĐÃ xác nhận qua
   browser thật: Category → "archive category-…", Tag → "archive tag-…", Search →
   "search search-results" — KHÔNG có "blog" ở cả 3 ngữ cảnh đó, nên rule dưới đây KHÔNG áp dụng
   nhầm cho Category/Tag/Search dù cùng dùng chung `index.php`. Màu/font tái dùng NGUYÊN token đã
   duyệt cho `.block-magazine` (CHG-065, Golden) — không tạo giá trị mới.
   `article-card--magazine.php` (đổi wiring ở index.php) tự mang đúng màu title/date/excerpt/CTA
   cho nền tối — KHÔNG cần override riêng ở đây. */
body.blog { background: var(--color-brand-primary); }
body.blog .thekoco-archive {
    color: var(--color-text-on-dark);
    font-family: 'Be Vietnam Pro', var(--font-family-base);
    /* Phase 2A.1 (2026-08-28, Issue #1 — Container quá hẹp): `.thekoco-archive` đồng thời MANG
       class `.container` (cùng 1 phần tử trong index.php: `<div class="container thekoco-archive">`)
       nên `max-width:1320px; padding-inline:24px` mặc định của `.container` áp thẳng lên đây, khiến
       nội dung co hẹp hơn nhiều so Figma. Đo TRỰC TIẾP bằng Python/Pillow trên `design/23-8/tap chi/
       Tap-chi.jpg` (canvas 1366×3310, background navy #1f232e đo được — TRÙNG KHỚP CHÍNH XÁC
       `--color-brand-primary`): card 1 trái x=33, card 3 phải x=1335 → content width = 1303px,
       lề trái/phải ≈ 33–34px mỗi bên (breadcrumb "Trang chủ" đo lề trái x=36 — khớp cùng 1 hệ căn
       lề với Grid, sai số ≤3px do JPEG anti-alias). 33–34px ĐÚNG BẰNG pattern full-bleed
       `padding-inline: 34px` đã dùng cho `.block-magazine .container`/Best Sellers/Trend/Trust —
       tái dùng NGUYÊN giá trị đó, không phát minh số mới. Chỉ override khi VỪA có `body.blog` VỪA
       có `.thekoco-archive` (đặc hiệu hơn `.container` gốc) — Category/Tag/Search/Single Post/
       Account (cũng dùng `.container`) KHÔNG bị ảnh hưởng vì không có `body.blog`. */
    max-width: none;
    padding-inline: 34px;
}
body.blog .thekoco-archive__title { color: var(--color-text-on-dark); }
body.blog .article-grid { gap: 26px; }

/* Phase 2A.1 (2026-08-28, Issue #2 — Typography). Đo TRỰC TIẾP màu chữ bằng Python/Pillow trên
   `Tap-chi.jpg` (lấy pixel "sáng nhất"/ít blend với nền nhất trong mỗi hàng chữ, giảm nhiễu JPEG):
   - Date đo được rgb(184,187,194) ≈ #B8BBC2 — TRÙNG KHỚP CHÍNH XÁC giá trị đã code sẵn trong
     `.article-card--magazine__date` (Golden Component) → KHÔNG cần sửa gì, xác nhận đã đúng.
   - Title đo được rgb(248,245,230) ≈ #F8F5E6 — rất gần `--color-text-on-dark` (#F4EDE7, chênh
     ≤8/255 mỗi kênh, trong sai số nén JPEG) → GIỮ NGUYÊN token, không hardcode giá trị mới.
   - CTA gold đo được rgb(213–214,175–176,52–54) ≈ #D5AF34/#D6B035 — TRÙNG gần như tuyệt đối
     `--color-brand-accent` (#D6B035) → KHÔNG đổi màu CTA.
   - Excerpt đo được rgb(184–186,178–180,167–170) ≈ #B8B3A9 — LỆCH RÕ so với
     `--color-text-on-dark-muted` (#A2A2A2, xám thuần, không có tông ấm) — nhưng khớp gần như tuyệt
     đối với giá trị `#b7b3a8` ĐÃ CÓ SẴN trong chính file này (dùng cho `.section-eyebrow` — cùng vai
     trò "text phụ màu ấm trên nền tối"). Tái dùng NGUYÊN giá trị `#b7b3a8` đã tồn tại, không phát
     minh màu mới — chỉ áp dụng cho card trong `body.blog` (Magazine Listing), KHÔNG sửa rule gốc
     `.article-card--magazine__excerpt` (dùng chung Homepage — Homepage giữ nguyên #A2A2A2 cũ). */
body.blog .article-card--magazine__excerpt { color: #b7b3a8; }

/* CTA "ĐỌC THÊM →": đo khoảng cách chữ↔mũi tên trên Figma ≈ 12px (text kết thúc x≈100, mũi tên bắt
   đầu x≈112) — hiện tại `gap: var(--space-2)` (8px). Đổi sang `--space-3` (12px, token có sẵn,
   không hardcode số mới). Font-weight tăng từ `--font-weight-medium` (500) → `--font-weight-semibold`
   (600) — đo định tính qua ảnh phóng to (mục IMPLEMENTATION_TAP_CHI_V2.md phần Phase 2A.1): nét chữ
   CTA trong Figma đậm/chắc hơn rõ rệt so với 500 hiện tại, không đo được số weight chính xác tuyệt
   đối từ ảnh raster (giới hạn phương pháp, xem Known Limitations) nhưng hướng "đậm hơn 1 bậc" có
   căn cứ hình ảnh rõ ràng. Cỡ chữ (12px/`--font-size-xs`) và letter-spacing (0.08em) đo cap-height
   CTA ≈9px tại 1366px viewport — khớp đúng dự kiến cho font-size 12px (cap-height ~0.72× font-size),
   KHÔNG đổi 2 giá trị này. CHỈ scope `body.blog` — Homepage Magazine giữ nguyên gap 8px/weight 500. */
body.blog .article-card--magazine__more {
    gap: var(--space-3);
    font-weight: var(--font-weight-semibold);
}

body.blog .thekoco-breadcrumb { color: var(--color-text-on-dark-muted); }
body.blog .thekoco-breadcrumb__item a { color: var(--color-text-on-dark-muted); }
body.blog .thekoco-breadcrumb__item a:hover { color: var(--color-brand-accent); }
body.blog .thekoco-breadcrumb__item [aria-current="page"] { color: var(--color-text-on-dark); }
/* CHG-067.1 (2026-09-03) — Bug thật: `--color-border-dark` (#2a2a2a) gần như CÙNG ĐỘ TỐI với nền
   trang `body.blog` (`--color-brand-primary` #1f232e) — đo contrast thật qua `getComputedStyle`:
   rgb(42,42,42) trên rgb(31,35,46), tỷ lệ tương phản ~1.15:1, KHÔNG PHẢI lỗi content/pseudo-element
   (content đã đúng `'>'` từ CHG-067, `display`/`position`/`opacity` đều bình thường — đã verify) mà
   THUẦN là ký tự tồn tại nhưng gần như vô hình do trùng màu nền. `--color-border-dark` đúng vai trò
   của nó ở chỗ khác (viền mảnh CẦN kín đáo trên nền tối, VD `.thekoco-pagination`) — không đổi token
   đó, chỉ đổi giá trị dùng RIÊNG cho separator breadcrumb sang `--color-text-on-dark-muted` (TÁI DÙNG
   đúng token đã áp dụng cho label breadcrumb ở 2 dòng trên — separator giờ cùng độ sáng với chữ
   "Trang chủ", tương phản rõ trên nền navy, không phát minh màu mới). */
body.blog .thekoco-breadcrumb__item:not(:last-child)::after { color: var(--color-text-on-dark-muted); }

/* Pagination: base rule dùng `color: var(--color-text)`/`border-color: var(--color-border)` —
   cả 2 gần như vô hình trên nền navy mới, cần override tường minh (không phải suy đoán Figma —
   đây là fix TƯƠNG PHẢN bắt buộc để pagination còn đọc được, độc lập với màu chính xác Figma chưa
   đo được, xem AUDIT_TAP_CHI_V2.md mục 16.4). "Current page" đổi từ nền navy (trùng hệt page
   background mới → biến mất) sang gold accent — tái dùng `--color-brand-accent` đã là màu
   "trạng thái nổi bật trên nền tối" chuẩn của site (CTA/dash-line/date accent), không phải màu
   mới phát minh riêng cho Pagination. */
body.blog .thekoco-pagination__item a,
body.blog .thekoco-pagination__item span {
    color: var(--color-text-on-dark);
    border-color: var(--color-border-dark);
}
body.blog .thekoco-pagination__item a:hover { border-color: var(--color-brand-accent); }
body.blog .thekoco-pagination__item span.current {
    background: var(--color-brand-accent);
    color: var(--color-brand-primary);
    border-color: var(--color-brand-accent);
}

/* Empty State (0 bài viết) — cùng lý do tương phản như Pagination ở trên; `.btn--dark` mặc định
   nền navy sẽ biến mất trên nền navy mới nên đổi riêng nút CTA trong ngữ cảnh này sang gold. */
body.blog .thekoco-empty-state__title { color: var(--color-text-on-dark); }
body.blog .thekoco-empty-state__message { color: var(--color-text-on-dark-muted); }
body.blog .thekoco-empty-state .btn--dark { background: var(--color-brand-accent); color: var(--color-brand-primary); }

/* ================= Product Detail — Hero (PD-04 Fix Visual Foundation, 2026-09-09) =================
   xem CHANGE_LOG_PRODUCT_DETAIL_PD04.md. Đo pixel THẬT trên `design/23-8/chi tiet san pham/
   Chi-tiet-San-pham.jpg` (.NET `Bitmap.GetPixel()`, quét dọc/ngang nhiều điểm, không áng chừng)
   xác nhận nền khu vực Breadcrumb+Gallery+Summary = navy đậm #1F232E — TRÙNG token
   `--color-brand-primary` ĐÃ CÓ. Site trước PD-04 render khu vực này trên nền TRẮNG — sai Visual
   Foundation (không phải lỗi CSS nhỏ), đã tự ghi nhận là Known Limitation ở PD-03 và nay được Product
   Owner xác nhận xử lý.
   `<section class="thekoco-product-hero">` (xem `single-product.php`) TÁI DÙNG NGUYÊN pattern
   full-bleed dark section đã có sẵn nhiều nơi (VD `.block-best-sellers`, dòng ~1221:
   `<section class="block-X"><div class="container">`) — KHÔNG đổi `.container` gốc dùng chung toàn
   site, KHÔNG phát minh cách bọc mới. Attributes ("Mô tả/Thành phần/Hướng dẫn") + Related Products
   ("Sản phẩm khác") NẰM NGOÀI hero (`<div class="container thekoco-product">` riêng), GIỮ NGUYÊN nền
   trắng — ĐÚNG pattern xen kẽ sáng/tối đo được trên chính design gốc (Hero tối → nội dung dài nền
   trắng → "Sản phẩm khác" tối trở lại — phần "Sản phẩm khác" tối NGOÀI PHẠM VI đợt này, xem Known
   Issue trong Implementation Report). */
.thekoco-product-hero { background: var(--color-brand-primary); padding-block: var(--space-10); }
/* PD-16 (2026-09-11 — xem CHANGE_LOG_PRODUCT_DETAIL_PD16.md §1): Divider giữa Hero và "Thông tin
   sản phẩm" — pixel-sample THẬT trên `design/23-8/chi tiet san pham/Chi-tiet-San-pham.jpg` (PHP GD
   `imagecolorat()`, quét dọc lẫn ngang) tìm thấy 1 đường kẻ ngang MỎNG (1px trong ảnh JPG export) ở
   ranh giới Hero/Attributes, màu đo được `#56595e` trên nền `#1F232E` — quy đổi ra alpha trắng
   ≈0.24-0.25 (`rgba(255,255,255,0.24)` khớp cả 3 kênh RGB trong sai số làm tròn JPEG). Đường kẻ này
   trải ĐỦ CHIỀU RỘNG VIEWPORT (đo x=0→1364/1366px, KHÔNG bị giới hạn theo gutter 34px của
   `.container`) — đặt `border-bottom` trực tiếp trên CHÍNH `.thekoco-product-hero` (section full-bleed
   ĐÃ CÓ SẴN, không tạo wrapper/section mới, không dùng width:100vw/margin âm) là cách DUY NHẤT vừa
   khớp đúng độ rộng đo được vừa đúng yêu cầu "divider phải là 1 phần của section boundary/layout,
   KHÔNG dùng border của table". Đây là dòng CSS DUY NHẤT chạm vào `.thekoco-product-hero` ở PD-16 —
   không đổi bất kỳ giá trị Gallery/Summary nào khác (padding-block/background giữ nguyên 100%). */
.thekoco-product-hero { border-bottom: 1px solid rgba(255, 255, 255, 0.24); }
/* PRODUCT DETAIL BACKGROUND FOUNDATION FIX (2026-09-11): Audit browser thật xác nhận
   `.container.thekoco-product` (bọc Attributes "Mô tả/Thành phần/Hướng dẫn" VÀ Related Products "Sản
   phẩm khác") KHÔNG có background riêng (`rgba(0,0,0,0)` — trong suốt) — `<body>` cũng không có
   background (mặc định trắng của trình duyệt) → toàn bộ vùng y≈1001-1853px (đo tại 1366px viewport)
   lộ nền TRẮNG của body, đúng "white strip" report mô tả. Đối chiếu lại TRỰC TIẾP
   `design/23-8/chi tiet san pham/Chi-tiet-San-pham.jpg` bằng pixel-sample thật (không đoán): TOÀN BỘ
   trang từ Hero tới hết "Hướng dẫn" dùng CHÍNH XÁC `#1F232E` (≡ `--color-brand-primary`, cùng tông
   Hero) — sửa lại 1 giả định SAI của PD-04 (từng ghi "Mô tả nền trắng, ĐÚNG pattern thiết kế" — audit
   lại lần này bằng pixel-sample thật, không phải nhìn lướt, xác nhận đó là ĐỌC SAI design gốc).
   "Sản phẩm khác" + Newsletter + Footer đổi sang `#141821` (≡ `--color-footer-lower-bg`, đã dùng sẵn
   cho `.site-footer__hero`) — để khớp CHÍNH XÁC pixel này cho riêng "Sản phẩm khác" cần kỹ thuật
   full-bleed (`width:100vw` + margin âm) trên `.thekoco-product-related`, rủi ro làm lệch vị trí
   card/heading nếu tính padding bù trừ sai — CHỌN PHƯƠNG ÁN AN TOÀN HƠN: dùng NGUYÊN 1 tông
   `--color-brand-primary` cho toàn bộ `.thekoco-product` (Attributes + Related), không restructure
   HTML, không đổi bất kỳ width/padding/margin nào đang quyết định layout — chỉ thêm `background`.
   Chênh lệch 2 tông rất nhẏ (#1F232E vs #141821, cùng họ navy tối) — ưu tiên ĐÚNG "không còn white
   strip" (yêu cầu rõ nhất, lặp lại nhiều lần) + "không rủi ro lệch layout" hơn khớp tuyệt đối 1 biên
   màu phụ, xem Change Log § Known Issue để Product Owner quyết định nếu cần độ chính xác cao hơn. */
.thekoco-product { padding-block: var(--space-10); background: var(--color-brand-primary); }

/* PRODUCT DETAIL — LAYOUT WIDTH + GALLERY ALIGNMENT (2026-09-11): `.container` GỐC (dòng ~86,
   max-width 1320px + padding-inline 24px, dùng chung toàn site) tạo margin ngoài lớn dần theo
   viewport (VD ~324px mỗi bên ở 1920px) — ĐÚNG root cause đã audit + sửa cho Header (xem comment
   dòng ~487-502, "Container width UI Audit 2026-08-23"). Đo lại TRỰC TIẾP trên
   design/23-8/chi tiet san pham/Chi-tiet-San-pham.jpg (1366×3950, pixel-scan thật — breadcrumb/
   topbar/thumbnail đều ra content bắt đầu tại x≈34-35px từ mép) — khớp CHÍNH XÁC con số `34px` đã
   dùng cho `.block-best-sellers`/`.block-promo-banners`/`.block-new-products .container` (dòng
   ~1328/1810/1912) — TÁI DÙNG NGUYÊN giá trị đã có, không đo ra số mới. Scope theo
   `body.single-product` (WooCommerce tự thêm class này, ĐÃ dùng cho breadcrumb dòng ~3277) — áp
   dụng CHO CẢ 2 nơi `.container` xuất hiện trên trang (Hero VÀ khối Mô tả/Thành phần/Hướng dẫn/Sản
   phẩm khác bên dưới, xem `single-product.php`) trong 1 rule DUY NHẤT — ĐÚNG yêu cầu "ONE PAGE → ONE
   CONSISTENT CONTAINER SYSTEM", không tạo class container mới, không sửa `.container` gốc (tránh
   ảnh hưởng trang khác ngoài phạm vi). */
body.single-product .container { max-width: none; padding-inline: 34px; }
/* PRODUCT DETAIL BACKGROUND FOUNDATION FIX (2026-09-11): safety net ở tầng `<body>` — dù
   `.thekoco-product-hero`/`.thekoco-product` (dòng ~3272/3291) đã tô đúng màu cho toàn bộ nội dung
   thật, `<body>` bản thân KHÔNG có màu nền (mặc định trắng trình duyệt) — nếu sản phẩm nào đó có nội
   dung NGẮN HƠN chiều cao viewport (VD mô tả rất ngắn, ít related products), phần viewport THỪA bên
   dưới `</footer>` (nếu Footer cũng ngắn) sẽ lộ màu body trắng. Đặt trực tiếp trên `<body>` (không
   phải 1 wrapper con) để đảm bảo đúng yêu cầu "Background phải tiếp tục đúng khi nội dung trang dài
   HOẶC ngắn hơn viewport" — phủ toàn bộ viewport bất kể chiều dài nội dung thật. */
body.single-product { background: var(--color-brand-primary); }

/* Breadcrumb trên nền tối — TÁI DÙNG NGUYÊN pattern đã chứng minh hoạt động ở 3 nơi
   (`body.single-post`/`.thekoco-shop`/`body.blog`, xem dòng ~2645/2892/3211), KHÔNG tạo cách làm
   mới. `.thekoco-breadcrumb*` DÙNG CHUNG toàn site — bắt buộc scope `body.single-product` (WooCommerce
   tự thêm body class này cho mọi trang sản phẩm), KHÔNG sửa rule gốc (dòng ~2534/2756-2760). */
body.single-product .thekoco-breadcrumb { color: var(--color-text-on-dark-muted); }
body.single-product .thekoco-breadcrumb__item a { color: var(--color-text-on-dark-muted); }
body.single-product .thekoco-breadcrumb__item a:hover { color: var(--color-brand-accent); }
body.single-product .thekoco-breadcrumb__item [aria-current="page"] { color: var(--color-text-on-dark); }
/* CHG-067.1 (2026-09-03, đã xảy ra ở body.blog — xem dòng ~3215): `--color-border-dark` (#2a2a2a)
   gần như CÙNG ĐỘ TỐI với `--color-brand-primary` (#1f232e, contrast ~1.15:1) — KHÔNG lặp lại lỗi
   đó, dùng `--color-text-on-dark-muted` (đã đúng ở 2 dòng trên) cho dấu '>' — tái dùng đúng fix đã
   verify, không phát minh giá trị mới. */
body.single-product .thekoco-breadcrumb__item:not(:last-child)::after { color: var(--color-text-on-dark-muted); }

/* GALLERY LEFT-GUTTER ALIGNMENT FIX (2026-09-11): Root cause của "khoảng trống lớn bên trái" ở màn
   hình rộng (VD 1920px) — KHÔNG phải lỗi container/gutter (`body.single-product .container` ở dòng
   ~3288 đã đúng, verify lại: `.thekoco-product-gallery` (wrapper ngoài) LUÔN bắt đầu tại x=34px ở
   MỌI viewport, kể cả 1920px). Lỗi thật nằm ở CHÍNH grid 2 cột này: `minmax(0,5fr) minmax(0,4fr)` ép
   cột Gallery LUÔN chiếm đúng 5/9 tổng chiều rộng bất kể nội dung cần bao nhiêu — ở màn rộng (1920px)
   cột Gallery bị ép rộng ~998px trong khi nội dung thật (thumbnail 128px + gap 32px + main image đã
   bị chặn `max-width:620px` ở PD-11) chỉ cần tối đa ~780px → dư ra ~218px BÊN TRONG cột Gallery. Vì
   `.thekoco-product-gallery` dùng `flex-direction:row-reverse` (để đảo thumbnail sang trái mà không
   đổi DOM, xem comment dưới), phần dư đó bị đẩy về ĐẦU của trục đã đảo — tức là hiện ra như khoảng
   trắng THỪA ở bên TRÁI của thumbnail, đúng triệu chứng report mô tả.
   Sửa ĐÚNG NGUYÊN NHÂN (không phải CSS hack/margin/translate): đổi từ CSS Grid tỷ lệ cố định (5fr:4fr)
   sang CSS Flex với Gallery co theo NỘI DUNG (`.thekoco-product__gallery-col`) và Summary/Information
   CHIẾM HẾT phần còn lại (`.thekoco-product__summary-col`, `flex:1`) — đúng yêu cầu "Gallery bám
   gutter trái, Information mở rộng sang phải dùng width còn dư", không phụ thuộc tỷ lệ fr cố định
   nữa. Ở màn hẹp hơn Gallery vẫn co lại bình thường (flex-shrink không đổi).

   `.thekoco-product__gallery-col` dùng `flex: 0 1 auto` — ĐÃ THỬ 2 LẦN, đều KHÔNG hoạt động đúng:
   `flex-basis: auto` trên 1 flex item mà bản thân nó LÀ flex container (`.thekoco-product-gallery`,
   xem dưới) hoá ra KHÔNG tự tính bằng tổng flex-basis của các con cháu như kỳ vọng — verify bằng
   browser thật ra kích thước SAI (384px rồi 224px ở 2 lần thử, đều ≠ ~600-760px kỳ vọng), kể cả sau
   khi đổi `.thekoco-product-gallery` sang `display:inline-flex` (tưởng sẽ ép shrink-wrap đúng, verify
   lại vẫn sai) — auto-sizing lồng flex-trong-flex (nested flex intrinsic sizing) không đáng tin cậy
   trong engine đang dùng, KHÔNG cố dùng tiếp dù đúng theo spec lý thuyết.
   Sửa bằng flex-basis TƯỜNG MINH thay vì dựa vào "auto" — 2 giá trị CÓ CĂN CỨ (không đoán), chọn
   đúng qua CSS `:has()` (hỗ trợ tốt mọi trình duyệt hiện đại, đúng "Latest Stable" theo CLAUDE.md):
   — Sản phẩm CÓ gallery (`__thumbs` tồn tại): 760px = 128 (thumb) + 32 (gap) + 600 (main basis)
   — Sản phẩm CHỈ 1 ảnh (không `__thumbs`): 600px = đúng bằng main basis, không cộng thumb/gap thừa
   Giá trị mặc định của rule là 600px (trường hợp phổ biến hơn về mặt kiến trúc — ảnh chính LUÔN tồn
   tại, thumbnail là tuỳ chọn), `:has()` ghi đè lên 760px khi thumbnail thực sự render. Vì kích thước
   khớp CHÍNH XÁC nội dung ở cả 2 trường hợp, không còn "phần dư" để `row-reverse` đẩy sai hướng nữa —
   không cần `justify-content` đặc biệt nào khác. */
.thekoco-product__main {
    display: flex;
    gap: var(--space-10);
}

/* MOBILE GALLERY BLANK-SPACE FIX (2026-09-11) — RESPONSIVE ISOLATION: `flex-basis`/`min-width` của
   `__gallery-col`/`__summary-col` (600px/760px/420px) CHỈ có ý nghĩa ở layout HÀNG NGANG (Desktop,
   Gallery cạnh Summary — xem PD-12). Trước đây các giá trị này khai ở SCOPE GỐC (không media query)
   rồi cố "reset" lại ở `@media (max-width:1024px)` — ĐÂY LÀ ROOT CAUSE của khoảng trắng khổng lồ sau
   thumbnail trên Mobile: rule reset `.thekoco-product__gallery-col { flex-basis: auto; }` (specificity
   1 class = 0,1,0) THUA rule gốc `.thekoco-product__gallery-col:has(.thekoco-product-gallery__thumbs)
   { flex-basis: 760px; }` (specificity 1 class + 1 :has(1 class) = 0,3,0 — CAO HƠN, bất kể thứ tự
   media query hay nằm sau trong file) — CSS specificity KHÔNG liên quan gì tới media query, "reset"
   chỉ thắng nếu specificity bằng hoặc cao hơn. Kết quả: `.thekoco-product__gallery-col` vẫn bị khoá
   `flex-basis:760px` trên Mobile — trong `flex-direction:column` (Mobile, xem @media dưới), giá trị
   đó áp vào CHIỀU CAO (main-axis đổi hướng) thay vì chiều rộng → cột Gallery bị ép CAO ĐÚNG 760px dù
   nội dung thật (main 343px + gap + thumbnail 2 hàng ≈ 551px đo được) NGẮN HƠN NHIỀU → dư ra ~209px
   khoảng trắng ĐÚNG giữa thumbnail và Product Information, khớp CHÍNH XÁC triệu chứng report mô tả.

   SỬA ĐÚNG ROOT CAUSE (không phải giảm margin/thêm spacer): thay vì "đặt giá trị Desktop ở scope gốc
   rồi cố ghi đè ở Mobile" (dễ thua specificity, đã chứng minh), CHUYỂN HẲN toàn bộ 3 rule chỉ-dành-
   cho-Desktop này vào `@media (min-width: 1025px)` (đúng biên đối xứng với `@media max-width:1024px`
   đã có sẵn cho layout Mobile/Tablet, dòng ~4013) — Mobile/Tablet đơn giản KHÔNG BAO GIỜ nhận các rule
   này, không cần "reset" gì cả vì chưa từng bị áp — loại bỏ hoàn toàn cuộc chiến specificity, đúng
   tinh thần "base styles + breakpoint-specific overrides" thay vì 1 giá trị dùng chung nhiều nơi. */
@media (min-width: 1025px) {
    .thekoco-product__gallery-col { flex: 0 1 600px; min-width: 0; } /* mặc định = chỉ main (không thumbnail) */
    .thekoco-product__gallery-col:has(.thekoco-product-gallery__thumbs) { flex-basis: 760px; } /* có thumbnail = main + thumb + gap */
    /* `min-width: 0` (không phải giá trị cụ thể) ban đầu khiến hàng CTA (Số lượng+Thêm vào giỏ hàng+Mua
       ngay, đo được rộng tự nhiên 483px ở 1366px, KHÔNG dư) bị ép hẹp tới mức vỡ dòng ở khoảng viewport
       Desktop hẹp/gần breakpoint Tablet (VD 1100px → summary chỉ còn 217px) — vì Gallery không tự nhường
       trước khi Summary min-width:0 chịu ép tới cạn. Đặt `min-width: 420px` (dưới 483px đo được ~60px,
       đủ margin co giãn nhỏ, KHÔNG đủ để hàng CTA vỡ dòng) — buộc Gallery (flex-shrink:1) phải co lại
       TRƯỚC khi Summary bị ép dưới mức đọc được, đúng thứ tự ưu tiên Design ("Information mở rộng, không
       phải bị bóp"). */
    .thekoco-product__summary-col { flex: 1 1 0%; min-width: 420px; }
}

/* Gallery — khung ảnh trên nền tối. Đo design: #141821 = ĐÚNG `--color-footer-lower-bg` (KHÁC nền
   Hero #1F232E — 2 tông tối phân biệt để tách "khung ảnh" khỏi "nền trang", đúng design, tái dùng
   token có sẵn).

   PRODUCT DETAIL — LAYOUT WIDTH + GALLERY ALIGNMENT (2026-09-11): cột thumbnail đo pixel thật trên
   design/23-8/chi tiet san pham/Chi-tiet-San-pham.jpg (scan ngang/dọc qua màu nền box
   `#2c3343`≈(44,51,67) so với hero bg `#1f232e`(31,35,46)) — thumbnail box ~112×109px (vuông,
   112px), gap DỌC giữa các thumb ~29-30px VÀ gap NGANG giữa cột thumb với ảnh chính ~31px — CÙNG 1
   giá trị (đúng như hiển thị), khớp token có sẵn `--space-8` (32px, lệch ~2px do nhiễu đo JPEG/bo
   góc, không phát minh literal px mới). Cột thumbnail nằm BÊN TRÁI ảnh chính trong Design — DOM vẫn
   giữ `__main` TRƯỚC `__thumbs` (đúng thứ tự ngữ nghĩa "ảnh chính trước", xem gallery.php) và dùng
   `flex-direction: row-reverse` để đảo thứ tự HIỂN THỊ mà không đổi DOM/tab order. Thumbnail column
   `align-items: stretch` (mặc định flex) để tự cao bằng ảnh chính (vuông, cao theo `flex:1`) — thừa
   thumbnail (>~4) tự cuộn dọc trong đúng chiều cao đó (`overflow-y:auto`), khớp yêu cầu "vượt quá
   chiều cao thì có scroll phù hợp" mà không cần thêm nút mũi tên JS (Design có icon mũi tên nhưng
   hiện tại 100% sản phẩm có gallery (1 SKU) chỉ có ĐÚNG 4 ảnh — vừa khít không cần cuộn; scroll native
   là fallback đủ dùng, tránh over-engineer 1 component JS mới ngoài yêu cầu). Tablet/Mobile (≤1024px,
   cùng breakpoint `.thekoco-product__main` đã stack 1 cột) revert về layout ngang cũ (thumb dưới ảnh
   chính) — xem @media bên dưới. */
.thekoco-product-gallery { display: flex; max-width: 100%; flex-direction: row-reverse; align-items: flex-start; gap: var(--space-8); } /* max-width:100% ngăn tràn ra ngoài cột cha nếu cha co hẹp hơn flex-basis đã khai */
/* GALLERY SCALE & THUMBNAIL RATIO FIX (2026-09-11): 3 lỗi phát hiện khi đối chiếu lại Design (không
   chỉ nhìn screenshot hiện tại):
   1) `object-fit: cover` + `aspect-ratio: 1/1` trên ảnh main — ảnh nguồn thật KHÔNG vuông (VD
      `earthniq-serum.png` 160×192, tỷ lệ 0.833, cao hơn rộng). Cover buộc scale theo CHIỀU RỘNG để
      lấp đầy khung vuông → chiều cao ảnh vượt khung ~20% → bị CẮT ~27px mỗi đầu trên/dưới (đo tại
      viewport 1366px, khung 546px) — mất nắp hộp phía trên + đế đĩa thuỷ tinh phía dưới, đúng
      triệu chứng "zoom quá mức/sản phẩm sát mép" trong report. Design (đo lại
      `Chi-tiet-San-pham.jpg`) cho thấy sản phẩm có khoảng thở RÕ quanh khung, không hề bị crop biên
      trên/dưới. Sửa: `object-fit: contain` — ảnh luôn hiển thị TRỌN VẸN, không crop/méo, tự
      letterbox 2 bên bằng đúng `background` khung đã có sẵn (không cần thêm màu mới).
   2) Cột thumbnail CỐ ĐỊNH 112px trong khi ảnh main `flex:1` KHÔNG giới hạn trên — tỷ lệ main:thumb
      vì vậy PHÌNH THEO VIEWPORT (không cố định như Design đo được ở 1 mốc 1366px), màn hình càng
      rộng ảnh main càng áp đảo thumbnail. Sửa: thêm `max-width` cho main (chặn phình vô hạn ở màn
      rất rộng, vẫn co bình thường ở màn hẹp nhờ `flex:1` không đổi) + tăng nhẹ thumbnail (112→128px,
      đúng yêu cầu "đủ lớn để sử dụng thực tế") — đưa tỷ lệ về gần khớp Design (~585:112≈5.2:1 đo
      được) ở dải viewport Desktop thực tế (1280-1600px), không chỉ đúng tại đúng 1 con số 1366px.
   3) (Bug tự phát hiện khi verify lại bằng browser thật sau khi sửa #1/#2, xem
      CHANGE_LOG_PRODUCT_DETAIL_GALLERY_SCALE.md) — `align-items: stretch` (mặc định flex) trên
      `.thekoco-product-gallery` kéo `__main` cao BẰNG cột thumbnail (VD 4 thumb 128px = 608px cao)
      trong khi width của `__main` do `flex:1` quyết định riêng (≠608px) → khung ảnh chính bị MÉO
      THÀNH HÌNH CHỮ NHẬT (531×608 đo được), không còn vuông như `aspect-ratio:1/1` trên `<img>` dự
      định — ảnh bên trong (đã `contain`) bị lọt thỏm giữa khung méo, để trống bất thường phía dưới.
      Sửa triệt để: chuyển `aspect-ratio:1/1` LÊN CHÍNH container `__main` (không chỉ trên `<img>`) +
      `align-items: flex-start` ở cha (ngăn stretch méo hình) — khung ảnh chính LUÔN vuông tuyệt đối,
      độc lập chiều cao cột thumbnail. Cột thumbnail không còn tự "stretch" theo main nữa nên chuyển
      cơ chế scroll-khi-vượt-quá sang `max-height` cố định (xem `__thumbs` bên dưới) thay vì dựa vào
      stretch — vẫn đúng yêu cầu "vượt quá chiều cao thì scroll", chỉ đổi CÁCH đạt được, không đổi UX. */
.thekoco-product-gallery__main {
    /* GALLERY LEFT-GUTTER ALIGNMENT FIX (2026-09-11): `flex: 1` (flex-grow:1, tự phình lấp đầy
       khoảng trống) đổi thành `flex: 0 1 600px` (KHÔNG grow — đúng yêu cầu "Information mở rộng
       dùng phần dư, KHÔNG phải main image phình thêm"; basis 600px xấp xỉ số đo Design thật ~585px
       tại 1366px reference, PD-09). Đây CHÍNH LÀ phần khiến cột Gallery ở rule cha (`.thekoco-product__
       gallery-col`) giờ có kích thước NỘI DUNG rõ ràng (128+32+600=760px) thay vì phụ thuộc 1 tỷ lệ
       fr — flex-shrink:1 vẫn giữ để co lại an toàn ở màn hẹp/Tablet. */
    flex: 0 1 600px;
    min-width: 0;
    max-width: 620px; /* an toàn kép — 600px đã là basis không-grow nên hiếm khi chạm, giữ lại phòng hờ */
    aspect-ratio: 1 / 1; /* khung LUÔN vuông tuyệt đối — xem lý do #3 ở comment trên */
    padding: var(--space-8); /* khoảng thở quanh sản phẩm, đo được rõ trong Design — object-fit:contain
                                 riêng không đủ tạo khoảng thở này với mọi ảnh nguồn (phụ thuộc tỷ lệ
                                 ảnh gốc), padding cố định đảm bảo nhất quán cho MỌI ảnh sản phẩm */
    box-sizing: border-box;
    border-radius: var(--radius-base);
    overflow: hidden;
    background: var(--color-footer-lower-bg); /* PD-04: --color-bg-subtle (be sáng, sai trên nền tối) → --color-footer-lower-bg */
}
.thekoco-product-gallery__main img { width: 100%; height: 100%; object-fit: contain; }
.thekoco-product-gallery__thumbs {
    display: flex;
    flex-direction: column;
    flex: 0 0 128px;
    gap: var(--space-8);
    max-height: 620px; /* khớp max-width của __main (2 khung vuông cùng kích thước tối đa) — cuộn dọc
                           khi số thumbnail vượt quá (~4 thumb 128px vừa khít, thumb #5 trở đi cuộn) */
    overflow-y: auto;
}
.thekoco-product-gallery__thumb {
    flex-shrink: 0;
    width: 128px; height: 128px;
    padding: var(--space-2); /* breathing room nhỏ — thumb bé, không lấy padding lớn như __main
                                 (sẽ làm ảnh quá nhỏ khó nhận diện) */
    box-sizing: border-box;
    border-radius: var(--radius-base);
    overflow: hidden;
    border: 2px solid transparent;
    background: var(--color-footer-lower-bg); /* PD-04: đồng bộ __main */
    transition: border-color 0.15s ease; /* hover/focus feedback nhẹ — xem rule :hover/:focus-visible bên dưới */
}
.thekoco-product-gallery__thumb img { width: 100%; height: 100%; object-fit: contain; }
.thekoco-product-gallery__thumb.is-active { border-color: var(--color-brand-accent); } /* KHÔNG đổi — gold vốn đã nổi rõ trên nền tối */
.thekoco-product-gallery__thumb:not(.is-active):hover,
.thekoco-product-gallery__thumb:not(.is-active):focus-visible {
    /* `--color-border-dark` (#2a2a2a) tưởng dùng được nhưng contrast chỉ 1.24:1 trên
       `--color-footer-lower-bg` (#141820) — GẦN NHƯ VÔ HÌNH, ĐÚNG bug đã ghi nhận CHG-067.1 (cùng
       token, nền tối khác). Đo lại trước khi dùng (không lặp lại lỗi cũ): `--color-text-on-dark-muted`
       (#a2a2a2) cho contrast 6.96:1 — rõ ràng, vẫn nhẹ hơn hẳn is-active (gold), không lẫn 2 trạng thái. */
    border-color: var(--color-text-on-dark-muted);
}

/* Summary — Typography & Visual Hierarchy (PD-03) + Dark Theme (PD-04): PD-03 từng ghi nhận "nền
   Summary design là navy nhưng site render trắng" là Known Limitation NGOÀI phạm vi Typography-only
   — PD-04 xử lý đúng phần đó theo yêu cầu Product Owner. Toàn bộ token light-theme
   (`--color-text`/`--color-text-muted`) đổi sang cặp on-dark TƯƠNG ỨNG ĐÃ CÓ SẴN trong tokens.css
   (`--color-text-on-dark`/`--color-text-on-dark-muted`, dùng chung Header/Footer/Blog dark hero) —
   KHÔNG tạo token màu mới. Rule `.thekoco-product-summary__brand*` (PD-02 đã bỏ, không còn HTML nào
   gọi `template-parts/product/brand.php`) — dead code, dọn luôn vì đang viết lại đúng khối này. */
.thekoco-product-summary__title {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    color: var(--color-text-on-dark); /* PD-04: --color-text (#111, vô hình trên nền tối) → --color-text-on-dark */
    margin-bottom: var(--space-2);
}
.thekoco-product-summary .product-card__rating { margin-bottom: var(--space-4); }
.thekoco-product-summary .product-card__rating-count { margin-left: var(--space-1); color: var(--color-text-on-dark-muted); } /* PD-04: thêm color — --color-text-muted (Golden Component gốc) gần như vô hình trên nền tối */
.thekoco-product-summary__price {
    display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap;
    font-weight: var(--font-weight-semibold);
    margin-bottom: var(--space-6);
}
.thekoco-product-summary__price-old { color: var(--color-text-on-dark-muted); font-weight: var(--font-weight-regular); font-size: var(--font-size-base); text-decoration: line-through; } /* PD-04: --color-text-muted → --color-text-on-dark-muted */
.thekoco-product-summary__price-new {
    color: var(--color-brand-accent); /* KHÔNG đổi — gold vốn đã nổi bật sẵn trên nền tối, đúng màu đo được trên design */
    font-size: var(--font-size-3xl);
    font-weight: var(--font-weight-bold);
    line-height: var(--line-height-tight);
}
.thekoco-product-summary__price-discount { color: var(--color-danger); font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); } /* KHÔNG đổi — đủ tương phản trên nền tối */
.thekoco-product-summary__short-desc {
    color: var(--color-text-on-dark-muted); /* PD-04: --color-text-muted → --color-text-on-dark-muted */
    line-height: var(--line-height-base);
    max-width: 46ch;
    margin-bottom: var(--space-6);
}

/* Action — Quantity/Add to Cart + Wishlist/Share, trên nền tối (PD-04) */
/* Divider phía trên Quantity/CTA — đo design: 1 đường kẻ mảnh. KHÔNG dùng `--color-border-dark`
   (xem cảnh báo CHG-067.1 phía trên — token đó gần vô hình trên `--color-brand-primary`) — tái dùng
   kỹ thuật `rgba(255,255,255,...)` ĐÃ CHỨNG MINH hoạt động trên CÙNG nền tối này
   (`.site-footer__newsletter-form` border `rgba(255,255,255,0.28)`, xem dòng ~2399), giảm opacity
   cho vai trò divider (kín đáo hơn khung input) thay vì border-box. */
.thekoco-product-action { border-top: 1px solid rgba(255, 255, 255, 0.12); padding-top: var(--space-6); }
.thekoco-product-action__cart .stock { font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); color: var(--color-text-on-dark-muted); margin: 0 0 var(--space-3); } /* PD-04: --color-text-muted → --color-text-on-dark-muted */
.thekoco-product-action__form { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-5); }
/* Quantity stepper (PD-05, thay thế bản PD-04 chỉ có input trơn) — đo pixel THẬT trên design ra
   khối "− 1 +" LÀ 1 hộp be sáng liền khối (#D6D5D0), KHÁC hẳn phần còn lại (chữ sáng trên nền tối)
   — đây là lựa chọn thiết kế có chủ đích (control tương tác nổi bật kiểu vật lý trên nền tối), không
   phải nhầm lẫn theme. Không có token nào khớp tuyệt đối #D6D5D0 — dùng `--color-border` (#e2e2e2,
   TOKEN GẦN NHẤT đã có, cùng họ "xám nhạt trung tính") làm nền thay vì tự thêm token mới cho 1 khối
   nhỏ. `.quantity` (wrapper `<div>` do WooCommerce tự render) mang khung/nền — `.qty` (input) và 2
   nút `.thekoco-qty-btn` bên trong TRONG SUỐT, ăn theo khung chung → nhìn như 1 khối liền, đúng
   design. `.quantity .qty` DÙNG CHUNG (Product Detail/Cart Page/Mini Cart, xem comment gốc dòng
   ~109) — CHỈ override scoped trong Product Detail, Cart Page/Mini Cart giữ nguyên 100%. */
.thekoco-product-action__form .quantity {
    display: inline-flex; align-items: stretch;
    height: 48px;
    background: var(--color-border);
    border-radius: var(--radius-base);
    overflow: hidden;
}
.thekoco-product-action__form .quantity .qty {
    width: 44px; height: 100%;
    background: transparent; border: 0;
    color: var(--color-text);
    font-size: var(--font-size-base); font-weight: var(--font-weight-medium);
    text-align: center;
    -moz-appearance: textfield; /* Ẩn spinner mặc định trình duyệt — đã có nút +/- riêng */
}
.thekoco-product-action__form .quantity .qty::-webkit-outer-spin-button,
.thekoco-product-action__form .quantity .qty::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.thekoco-qty-btn {
    width: 36px; height: 100%;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: 0; padding: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-lg); line-height: 1;
    cursor: pointer;
}
.thekoco-qty-btn:hover { color: var(--color-text); background: rgba(0, 0, 0, 0.06); }
.thekoco-qty-btn:disabled { opacity: 0.35; cursor: not-allowed; background: transparent; }
/* Add to Cart / Mua ngay — cùng kích thước hàng, khác màu để tạo hierarchy (§3.3 PD-03 kế thừa:
   "Sale Price > Old Price"-style — ở đây là "Add to Cart nổi bật hơn Mua ngay" theo đúng §8 brief
   PD-05 "hierarchy giữa Thêm vào giỏ hàng và Mua ngay": nền đặc (maroon) > viền rỗng (gold outline). */
.thekoco-product-action__submit,
.thekoco-product-action__buy-now { height: 48px; padding-inline: var(--space-8); font-size: var(--font-size-base); }
/* Add to Cart — đo màu nút trên design ra ĐÚNG #781035 = `--color-topbar-bg` (token ĐÃ CÓ, PD-04).
   KHÔNG dùng `.btn--dark` mặc định (nền `--color-brand-primary`, TRÙNG HỆT nền Hero → nút "biến
   mất"). Override CHỈ scoped `.thekoco-product-action__submit` — KHÔNG sửa `.btn--dark` gốc (dùng
   chung 20+ nơi: Cart/Checkout/My Account/Login...). */
.thekoco-product-action__submit.btn--dark { background: var(--color-topbar-bg); }
/* Mua ngay (PD-05, MỚI) — dùng variant `.btn--outline-gold` (thêm ở dòng ~107), KHÔNG cần override
   riêng gì thêm ngoài kích thước hàng ở rule chung phía trên. */
.thekoco-product-action__extra { display: flex; align-items: center; gap: var(--space-6); flex-wrap: wrap; }
/* Wishlist — design không có khung/pill quanh "Yêu thích" (chỉ icon + chữ trơn, giống Share) — bỏ
   viền pill bằng `border-color: transparent` (KHÔNG xoá hẳn `border`, tránh xê dịch layout do đổi
   box-sizing), giữ nguyên padding làm vùng bấm thoải mái (accessibility, wishlist vẫn secondary
   action so với Add to Cart theo đúng yêu cầu). */
.thekoco-product-action__wish {
    display: inline-flex; align-items: center; gap: var(--space-2);
    background: transparent; border: 1px solid transparent; /* PD-04: var(--color-border) → transparent, đúng design không có viền */
    border-radius: var(--radius-full); padding: var(--space-2) var(--space-4);
    color: var(--color-text-on-dark-muted); /* PD-04: --color-text-muted → --color-text-on-dark-muted */
    font-size: var(--font-size-sm);
}
.thekoco-product-action__wish.is-active { border-color: var(--color-danger); color: var(--color-danger); } /* KHÔNG đổi — vẫn nổi rõ khi active, kể cả trên nền tối */
.thekoco-product-action__share { display: inline-flex; align-items: center; gap: var(--space-3); }
.thekoco-product-action__share-label { font-size: var(--font-size-sm); color: var(--color-text-on-dark-muted); } /* PD-04 */
.thekoco-product-action__share a { color: var(--color-text-on-dark-muted); display: inline-flex; } /* PD-04 màu (áp dụng cho SVG Email currentColor) + PD-06 display:inline-flex (căn giữa icon <img> mới thêm) */
.thekoco-product-action__share a:hover { color: var(--color-brand-accent); opacity: 0.8; } /* PD-04 màu (Email SVG) + PD-06 opacity (feedback hover cho <img>, ảnh PNG không đổi màu theo currentColor được) */
/* PD-06: 5 icon mạng xã hội (Facebook/Twitter/YouTube/Instagram/TikTok) giờ là <img> (asset PNG thật
   từ `design/assets/social/`, xem `action.php`) thay vì SVG vẽ tay — đã đúng màu xám mờ sẵn từ file
   gốc (không cần filter/currentColor). Chỉ cần đồng bộ kích thước với icon Email (SVG, 16x16 có sẵn). */
.thekoco-product-action__share img { display: block; width: 16px; height: 16px; object-fit: contain; }

/* Product Meta (PD-02) trên nền tối (PD-04) */
.thekoco-product-meta {
    margin-top: var(--space-6);
    border-top: 1px solid rgba(255, 255, 255, 0.12); /* PD-04: thêm divider phía trên, đúng design (cùng kỹ thuật với .thekoco-product-action) */
    padding-top: var(--space-6);
    display: flex; flex-direction: column; gap: var(--space-2);
}
.thekoco-product-meta__row { margin: 0; font-size: var(--font-size-sm); line-height: var(--line-height-loose); }
.thekoco-product-meta__label { color: var(--color-text-on-dark-muted); } /* PD-04: --color-text-muted → --color-text-on-dark-muted */
.thekoco-product-meta__value { color: var(--color-text-on-dark); font-weight: var(--font-weight-medium); } /* PD-04: --color-text → --color-text-on-dark */
.thekoco-product-meta__value a { color: var(--color-text-on-dark); text-decoration: underline; text-underline-offset: 2px; } /* PD-04 */
.thekoco-product-meta__value a:hover { color: var(--color-brand-accent); } /* PD-04: --color-brand-primary (vô hình trên nền navy) → gold */

/* Payment Methods + Shipping (PD-06) — đo pixel THẬT trên design (.NET GetPixel):
   - Khối "Thanh toán": khung bo góc, nền `--color-payment-box-bg` (nâu-olive, token mới — xem
     tokens.css), label đậm sáng bên trái, 4 logo trong chip nền trắng bên phải.
   - Dòng Shipping: icon xe tải (Feather Icons "truck", ĐÚNG family icon inline-SVG project đã dùng
     xuyên suốt — footer contact/wishlist heart/share email đều cùng nguồn Feather, xem action.php/
     footer.php — không có asset PNG nào cho icon này trong thư mục design, không tạo dependency
     icon-font mới) + text muted, CÙNG tier "Secondary" đã áp dụng cho Stock/Short Description. */
.thekoco-product-payment {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-4);
    background: var(--color-payment-box-bg);
    border-radius: var(--radius-lg);
    padding: var(--space-4) var(--space-5);
    margin-top: var(--space-5);
}
.thekoco-product-payment__label { font-weight: var(--font-weight-bold); color: var(--color-text-on-dark); white-space: nowrap; }
.thekoco-product-payment__list { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-3); list-style: none; margin: 0; padding: 0; }
.thekoco-product-payment__badge {
    display: flex; align-items: center; justify-content: center;
    width: 52px; height: 34px;
    background: var(--color-bg);
    border-radius: var(--radius-sm);
    overflow: hidden;
}
.thekoco-product-payment__badge img { max-width: 100%; max-height: 100%; object-fit: contain; }

.thekoco-product-shipping {
    display: flex; align-items: center; gap: var(--space-2);
    margin: var(--space-4) 0 0;
    color: var(--color-text-on-dark-muted);
    font-size: var(--font-size-sm);
}
.thekoco-product-shipping__icon { flex-shrink: 0; color: var(--color-text-on-dark-muted); }

/* Attributes/Product Content (lịch sử PD-15→PD-17 — xem CHANGE_LOG_PRODUCT_DETAIL_PD15.md/PD16.md/
   CHANGE_LOG_PD16_FINAL.md/CHANGE_LOG_PD17.md cho từng vòng trước, đã thay layout 5-cột nhiều lần):

   PD-18 (2026-09-14 — xem CHANGE_LOG_PD18.md): Product Owner xác nhận DỨT ĐIỂM layout 5-cột/grid
   label:value (PD-15→PD-17, dù đã sửa contrast/width/typography nhiều vòng) là **SAI CONCEPT**, không
   phải lỗi CSS nhỏ — Design gốc thật ra thể hiện khối này dưới dạng Editorial/Product Content section
   (H2 "Thông tin sản phẩm" bọc ngoài, bên trong là các sub-section CÓ THẬT trong
   `Chi-tiet-San-pham.jpg` — "Mô tả"/"Thành phần"/"Hướng dẫn", mỗi sub-section có heading + gạch chân
   riêng + nội dung dạng ĐOẠN VĂN, không phải bảng thuộc tính ngắn). Bỏ HẲN grid/table label:value —
   không tinh chỉnh spacing/width của layout cũ thêm lần nào nữa (đúng yêu cầu tường minh).
   Kiến trúc DATA mới (xem PHP `attributes.php` cho chi tiết đầy đủ): "Mô tả" đọc từ
   `$product->get_description()` (post_content — field CHUẨN WooCommerce, không phải Attribute
   System); "Thành phần"/"Hướng dẫn"/mọi field dài khác đọc ĐỘNG qua
   `AttributeTemplateResolver`/`AttributeDefinition`, lọc `dataType === TYPE_TEXTAREA` (dữ liệu dài,
   khác nhóm TYPE_TEXT/TAXONOMY/SELECT/WC_ATTRIBUTE — vốn là spec NGẮN, nay KHÔNG còn render ở khối
   này nữa, xem Known Issue) — ĐÚNG yêu cầu "Admin thêm 1 field TEXTAREA mới → tự xuất hiện, không cần
   sửa component". Giữ nguyên các phần ĐÃ pixel-sample-verified từ vòng trước (không đo lại):
   - Gạch chân heading (`::after`, 40px/1px/`--color-text-on-dark`) — đo trực tiếp dưới "Mô tả" trong
     Design — nay áp dụng cho CẢ heading section con ("Mô tả"/"Thành phần"/...), vì các heading đó
     CHÍNH LÀ "Mô tả" đã đo, không phải suy luận thêm.
   - Không Card/Surface (PD-17 xác nhận Design không có) — nội dung nằm PHẲNG trên nền `.thekoco-product`.
   - Divider Hero→Attributes (`.thekoco-product-hero { border-bottom }`, PD-16) — KHÔNG đụng.
   - Paragraph text màu/line-height TÁI DÙNG NGUYÊN `.thekoco-product-summary__short-desc` (PD-04) —
     precedent ĐÃ CÓ SẴN cho "đoạn văn dài trên nền tối" ngay trên chính trang này, không suy luận từ
     Product Card (context khác). `max-width: 760px` cho riêng CỘT ĐỌC (không phải cả section) — tái
     dùng đúng con số 760px từng dùng cho table PD-09 (không phát minh số mới), đúng độ rộng đọc dễ
     chịu cho văn bản dài — section/heading/divider vẫn dùng ĐỦ content width của `.thekoco-product`. */
.thekoco-product-attributes { margin-bottom: var(--space-12); }
.thekoco-product-attributes__heading {
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    color: var(--color-text-on-dark);
    margin: 0 0 var(--space-6);
    padding-bottom: var(--space-3);
    position: relative;
}
.thekoco-product-attributes__heading::after {
    content: ''; position: absolute; left: 0; bottom: 0;
    width: var(--space-10); height: 1px; background: var(--color-text-on-dark);
}
.thekoco-product-attributes__section { margin-top: var(--space-10); }
.thekoco-product-attributes__section:first-child { margin-top: 0; }
.thekoco-product-attributes__section-heading {
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--color-text-on-dark);
    margin: 0 0 var(--space-4);
    padding-bottom: var(--space-2);
    position: relative;
}
.thekoco-product-attributes__section-heading::after {
    content: ''; position: absolute; left: 0; bottom: 0;
    width: var(--space-10); height: 1px; background: var(--color-text-on-dark);
}
/* Paragraph — TÁI DÙNG NGUYÊN màu/line-height của `.thekoco-product-summary__short-desc` (PD-04,
   dòng ~3539), precedent có sẵn cho "đoạn văn dài trên nền tối" trên CHÍNH trang này. */
.thekoco-product-attributes__section-content {
    max-width: 760px;
    color: var(--color-text-on-dark-muted);
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
}
.thekoco-product-attributes__section-content p { margin: 0 0 var(--space-4); }
.thekoco-product-attributes__section-content p:last-child { margin-bottom: 0; }

/* Related Products (PD-15, 2026-09-11 — xem CHANGE_LOG_PRODUCT_DETAIL_PD15.md §3-4). Heading trước
   đây không khai màu → kế thừa `body { color: var(--color-text) }` (#111111) — vô hình trên nền
   tối, cùng loại bug với Attributes ở trên. Pixel-sample thật trên
   design/23-8/chi tiet san pham/Chi-tiet-San-pham.jpg xác nhận "Sản phẩm khác" dùng màu VÀNG
   (khớp CHÍNH XÁC `--color-brand-accent` đã có sẵn) + font serif — TÁI DÙNG nguyên cặp
   màu/font-family đã dùng cho heading vàng khác trên site (`.thekoco-related-posts .section-heading`,
   dòng ~2597/2739 — cùng pattern "gold heading trên nền tối", không tạo token/font mới). KHÔNG đổi
   sang class `.section-heading` dùng chung (class đó `text-align:center` — sai layout, "Sản phẩm
   liên quan" luôn canh trái) — chỉ set màu/font trực tiếp trên class riêng đã có
   `.thekoco-product-related__heading` để tránh ảnh hưởng các nơi khác dùng `.section-heading`. */
.thekoco-product-related__heading {
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    margin-bottom: var(--space-6);
    color: var(--color-brand-accent); /* PD-15: không có màu → kế thừa --color-text (#111), vô hình trên nền tối */
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base); /* PD-15: khớp họ font heading vàng đã dùng (Magazine/Related Posts) */
}

/* Related Products — Full-bleed background section (PD-15, 2026-09-11): PD-14 từng cố ý GIỮ
   "Sản phẩm khác" chung 1 tông `--color-brand-primary` với Attributes để tránh rủi ro full-bleed
   ảnh hưởng gutter 34px (xem Known Issue, CHANGE_LOG_PRODUCT_DETAIL_PD14.md §10) — pixel-sample lại
   xác nhận Design dùng tông KHÁC cho riêng khối này: `--color-footer-lower-bg` (#141821, đã dùng
   cho `.site-footer__hero`), không phải `--color-brand-primary` (#1F232E, dùng cho Hero/Attributes).
   PD-15 tách khối này ra 1 `<section>` full-bleed ĐỘC LẬP (xem `single-product.php`) — TÁI DÙNG
   NGUYÊN pattern `<section class="X"><div class="container">` đã dùng cho `.thekoco-product-hero`
   (không phát minh cách bọc mới, không dùng width:100vw/margin âm). Gutter 34px của `.container`
   bên trong tự động ÁP DỤNG ĐÚNG nhờ rule scope theo trang có sẵn
   `body.single-product .container { padding-inline: 34px }` (dòng ~3306, đã comment rõ "áp dụng
   CHO CẢ 2 nơi .container xuất hiện trên trang" — không cần thêm CSS gutter mới). */
.thekoco-product-related-section { background: var(--color-footer-lower-bg); padding-block: var(--space-10); }

/* Related Products — Card visual sync (PD-19, 2026-09-14 — xem CHANGE_LOG_PD19.md §3-4). Audit thật
   (đọc code + đo browser + đối chiếu pixel Design) xác nhận Related Products card trước Sprint này
   THIẾU 3 điểm mà Homepage Best Sellers VÀ Shop/Category Listing (2 nơi Product Owner xác nhận đẹp/
   đúng) đều đã có — cả 3 điểm đều lộ rõ khi zoom trực tiếp khu vực "Sản phẩm khác" trong
   `design/23-8/chi tiet san pham/Chi-tiet-San-pham.jpg` (crop card 1 "EARTHNIQ", y≈2400-2900):
   1) Card 1-tông phẳng (`background` duy nhất `--color-footer-lower-bg` cho cả ảnh lẫn text) — Design
      + Shop Listing đều có 2 tông: Media Area (`--color-footer-lower-bg`) và Info Area RIÊNG
      (`--color-card-info-bg` #2c3343, đã có token). TÁI DÙNG NGUYÊN kỹ thuật
      `.thekoco-shop .product-card--dark` (dòng ~1102-1152, đã Product Owner xác nhận đúng ở Shop) —
      background/padding/overflow ở Card cha, nền Media dời vào `.product-card__thumb`, Badge/
      Wishlist bù lại khoảng cách `--space-3` (đã mất do Thumb tràn hết Card).
   2) Brand + Rating ĐANG XẾP DỌC (Rating trước Sprint này còn chưa render vì thiếu cờ
      `show_rating`, xem `related-products.php`) — Design đo được RÕ Brand trái/Rating phải CÙNG 1
      HÀNG (VD "EARTHNIQ ... ★★★★★ 22"). TÁI DÙNG NGUYÊN kỹ thuật `grid-template-areas` đã dùng cho
      `.thekoco-shop`/`.block-best-sellers`/`.block-sale__products`/`.block-new-products` (KHÔNG phát
      minh cách mới, KHÔNG đổi DOM order trong `product-card.php`).
   3) Brand không viết hoa — pixel-sample TRỰC TIẾP "EARTHNIQ"/"VEON" trong chính khu vực "Sản phẩm
      khác" của Design này xác nhận chữ HOA — TÁI DÙNG nguyên trị `text-transform:uppercase` +
      `font-size:15px` + màu `#b7b3a8` đã dùng cho `.block-best-sellers .product-card__brand`
      (dòng ~395-399, cùng giá trị, không suy luận số mới). Tên sản phẩm dùng font Serif
      (`Noto Serif`) — zoom pixel-thật "Collagen Face Mask (5pcs)" xác nhận có chân serif rõ, khớp
      ĐÚNG font đã dùng cho `.thekoco-shop .product-card--dark .product-card__title` (dòng ~1157).

   TOÀN BỘ thay đổi bên dưới CHỈ scope `.thekoco-product-related` — KHÔNG sửa `.product-card`/
   `.product-card--dark` gốc, KHÔNG sửa `.block-best-sellers`/`.thekoco-shop` (0 rủi ro Homepage/Shop
   Listing regression, đã regression-test cả 2 nơi sau khi thêm rule này — xem Change Log §7).

   PD-20 (2026-09-14 — xem CHANGE_LOG_PD20.md §1): Product Owner review lại, screenshot vẫn cho thấy
   "4 card bị kéo giãn, section rỗng, khoảng trống lớn bên phải" — đo TRỰC TIẾP browser thật (không
   đoán) phát hiện ROOT CAUSE chưa từng xử lý ở PD-19: `.product-grid` (dòng ~957, class GỐC dùng
   chung Best Sellers/Sale/New Products/Related) hardcode `grid-template-columns: repeat(5, 1fr)` —
   Related Products LUÔN có ĐÚNG 4 sản phẩm (`related-products.php`: `'posts_per_page' => 4`) nên
   GRID vẫn chia 5 cột đều nhau, 4 card thật chiếm 4 cột đầu, cột thứ 5 TRỐNG — đo được chính xác
   **260.6px khoảng trống chết ở mép phải** tại 1366px (`gridTemplateColumns` trả về 5 giá trị
   240.59px, `emptySpaceRight = gridRight - lastCardRight = 260.6px`). Đây KHÔNG PHẢI lỗi "card quá
   rộng" — NGƯỢC LẠI, mỗi card đang HẸP HƠN đáng lẽ (240px thay vì phải rộng hơn) vì bị chia sẻ không
   gian với 1 cột ảo không tồn tại.
   Pixel-sample lại TRỰC TIẾP Design (`Chi-tiet-San-pham.jpg`, quét ngang y=2790 tìm ranh giới màu
   Info Area `#2c3343` ↔ nền section `#141821`) đo được: card thật ≈296px, cách đều nhau — XÁC NHẬN
   layout Design là ĐÚNG 4 cột đều (không phải 5 cột/carousel/layout khác). Tính lại: content width
   1283px (gutter 34px đã chốt) − 3×gap (`--space-5`=20px, giữ nguyên gap gốc) ÷ 4 = **305.8px/card**
   — khớp rất gần con số đo Design (296px, chênh ~10px trong sai số quét JPEG bước nhảy 8px) — ĐỦ căn
   cứ để khẳng định `repeat(4, 1fr)` (khớp ĐÚNG số lượng sản phẩm luôn cố định = 4, không phải đoán
   liều theo cảnh báo của task) là lựa chọn đúng, KHÔNG phải "tự ý đơn giản hoá".
   QUAN TRỌNG — bài học specificity PD-13: `.thekoco-product-related .product-grid` (2 class,
   specificity 0,2,0) CAO HƠN base `.product-grid { repeat(3,1fr)/(2,1fr) }` bên trong
   `@media (max-width:1024px)`/`@media (max-width:768px)` (1 class, 0,1,0) — nếu khai KHÔNG kèm
   media query, rule 4-cột sẽ THẮNG ở MỌI viewport kể cả Mobile (media query không cộng thêm
   specificity, xem CHANGE_LOG_PRODUCT_DETAIL_PD13.md), phá layout responsive Tablet/Mobile đã đúng
   từ trước. Bọc trong `@media (min-width:1025px)` (breakpoint Desktop-only ĐÃ có sẵn, dùng chung
   Gallery/Summary từ PD-12/13) — CHỈ áp dụng 4 cột ở Desktop, Tablet/Mobile giữ NGUYÊN 3/2 cột như
   base `.product-grid` đã xử lý đúng, không đụng. */
@media (min-width: 1025px) {
    .thekoco-product-related .product-grid { grid-template-columns: repeat(4, 1fr); }
}

/* H04-C (2026-09-19) — Magazine Article Related Products KHÔNG dùng rule `repeat(4, 1fr)` ở trên:
   Product Detail luôn có ĐÚNG 4 sản phẩm (`related-products.php`: `posts_per_page => 4`, cố định),
   nhưng Magazine Article có 1-6 sản phẩm biến động (Admin H04-B chọn tự do, có thể chỉ 1-2 sản
   phẩm) — cột cố định sẽ tái hiện ĐÚNG bug "khoảng trống chết" PD-20 đã tìm và sửa ở trên, thậm chí
   RÕ hơn (2 cột trống thay vì 1). Dùng `auto-fill`/`minmax()` — kỹ thuật ĐÃ CÓ SẴN cho chính vấn đề
   "số lượng Card biến động" ở Shop Listing (`.thekoco-shop__layout .product-grid`, D-PL01A-002,
   dòng ~3031/3174/3203 — giá trị breakpoint/minmax TÁI DÙNG NGUYÊN, đã đo thật nhiều mốc, không suy
   đoán số mới) thay vì phát minh kỹ thuật khác cho cùng 1 loại vấn đề.
   Selector 3 class (`.thekoco-single-post__related-products .thekoco-product-related .product-grid`,
   specificity 0,3,0) CAO HƠN rule `repeat(4,1fr)` ở trên (0,2,0, bài học PD-13: media query không
   cộng thêm specificity) — CHỈ áp dụng trong scope Magazine Article, KHÔNG sửa/đụng rule gốc phía
   trên, 0 rủi ro Product Detail (rule đó vẫn nguyên vẹn cho `.thekoco-product-related` KHÔNG nằm
   trong `.thekoco-single-post__related-products`). */
.thekoco-single-post__related-products .thekoco-product-related .product-grid {
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
@media (max-width: 1024px) {
    .thekoco-single-post__related-products .thekoco-product-related .product-grid {
        grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    }
}
@media (max-width: 600px) {
    .thekoco-single-post__related-products .thekoco-product-related .product-grid {
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
        gap: var(--space-3);
    }
}

/* PD-20 FINAL (2026-09-14 — xem VISUAL_AUDIT_RELATED_PRODUCT_CARD.md + CHANGE_LOG_PD20_FINAL.md):
   Product Owner cung cấp screenshot browser THẬT + `product_source.txt` (HTML thật) — audit lại từ
   đầu (KHÔNG chỉ đọc property) phát hiện ROOT CAUSE THẬT khác hẳn 2 vòng sửa trước:
   `.thekoco-product-related .product-card--dark { background: transparent; }` (Round 1) lộ ra
   `.thekoco-product-related-section { background: var(--color-footer-lower-bg) }` (PD-15) phía sau
   — và giá trị NÀY TRÙNG TUYỆT ĐỐI với `.product-card__thumb { background: var(--color-footer-lower-bg) }`
   — khiến vùng ảnh "hoà tan" vào nền trang, xoá mất ranh giới Card ở đúng chỗ Badge/Wishlist/Ảnh
   đứng, chỉ còn Info Area (`#2c3343`, khác biệt) nổi lên như 1 khối riêng. Kỹ thuật `transparent` này
   TÁI DÙNG ĐÚNG từ `.thekoco-shop` (nơi nó hoạt động tốt vì trang Shop dùng nền `--color-brand-primary`
   #1f232e ≠ thumb #141820) — nhưng SAI NGỮ CẢNH khi áp cho Related Products (section bg TRÙNG thumb
   bg). Đã đo `gapBetweenThumbAndBody=0px` cả 2 vòng trước — xác nhận DOM/cấu trúc ĐÚNG từ đầu, vấn đề
   100% ở tầng màu sắc.
   GIẢI PHÁP: đổi sang tái dùng kỹ thuật Best Sellers (Golden Reference #1) — Card cha có màu nền
   THẬT ĐỘC LẬP (`#2c3343`, không phụ thuộc màu trang xung quanh) + Thumb bleed ra sát mép Card bằng
   âm margin bù padding (kỹ thuật đã tinh chỉnh qua CHG-036→049, xem comment gốc dòng ~296-327) — kỹ
   thuật này MẠNH HƠN transparent vì không phụ thuộc màu nền trang/section chứa nó, đúng nhu cầu
   Related Products. Border `1px solid #2d3146` (giá trị THẬT từ Best Sellers, không phát minh) cho
   Card 1 ranh giới rõ ràng bất kể ngữ cảnh. Giữ nguyên `border-radius: var(--radius-base)` (8px, đã
   dùng xuyên suốt Related Products từ PD-19, KHÔNG đổi sang literal 6px của Best Sellers để tránh
   thay đổi không cần thiết ngoài phạm vi cohesion). Giữ nguyên 70% tỉ lệ ảnh (đã đúng, khớp Best
   Sellers, xem VISUAL_AUDIT §2 — gap Badge↔Ảnh không phải nguyên nhân, đã đo Best Sellers CŨNG có
   gap tương tự 42.59px mà vẫn đọc đúng "1 Card" nhờ Card có màu nền thật). */
.thekoco-product-related .product-card--dark {
    box-sizing: border-box;
    border: 1px solid #2d3146;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    background: var(--color-card-info-bg);
}
.thekoco-product-related .product-card--dark .product-card__thumb {
    flex: 0 0 auto;
    background: var(--color-footer-lower-bg);
    margin: calc(var(--space-4) * -1) calc(var(--space-4) * -1) 0;
    padding: var(--space-4) var(--space-4) 0;
    border-radius: 0 0 var(--radius-base) var(--radius-base);
}
.thekoco-product-related .product-card--dark .product-card__thumb img { width: 70%; height: 70%; border-radius: 6px; }
.thekoco-product-related .product-card--dark .product-card__wish { top: var(--space-3); right: var(--space-3); }
.thekoco-product-related .product-card--dark .thekoco-badge { top: var(--space-3); left: var(--space-3); }
.thekoco-product-related .product-card--dark .product-card__body {
    background: var(--color-card-info-bg);
    margin: 0 calc(var(--space-4) * -1) calc(var(--space-4) * -1);
    padding: var(--space-5) var(--space-4) var(--space-4);
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas:
        "brand  rating"
        "title  title"
        "cat    cat"
        "price  price";
    column-gap: var(--space-3);
}
.thekoco-product-related .product-card--dark .product-card__brand {
    grid-area: brand; align-self: center; margin-bottom: 0;
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    text-transform: uppercase; letter-spacing: 0.04em; font-style: normal; font-size: 15px; color: #b7b3a8;
}
.thekoco-product-related .product-card--dark .product-card__rating { grid-area: rating; justify-self: end; align-self: center; margin-bottom: 0; }
.thekoco-product-related .product-card--dark .product-card__title { grid-area: title; font-family: 'Noto Serif', var(--font-family-base); }
.thekoco-product-related .product-card--dark .product-card__cat { grid-area: cat; }
.thekoco-product-related .product-card--dark .product-card__price { grid-area: price; }

/* ================= Mini Cart dropdown (Sprint Shopping Experience Foundation) =================
   Panel bọc ngoài `.widget_shopping_cart_content` (nội dung bên trong do WooCommerce tự render/
   AJAX-refresh, xem header.php) — không style trực tiếp `.widget_shopping_cart_content` để tránh
   nhầm lẫn với div bị AJAX thay thế nguyên khối. */
.thekoco-mini-cart-panel {
    position: absolute; top: calc(100% + var(--space-4)); right: 0; z-index: 40;
    width: 360px; max-width: calc(100vw - var(--space-6) * 2);
    background: var(--color-bg); color: var(--color-text);
    border-radius: var(--radius-base); box-shadow: var(--shadow-lg);
    padding: var(--space-5);
}
.thekoco-mini-cart__summary { margin: 0 0 var(--space-4); display: flex; justify-content: space-between; font-weight: var(--font-weight-semibold); font-size: var(--font-size-sm); color: var(--color-text-muted); }
.thekoco-mini-cart__list { list-style: none; margin: 0 0 var(--space-4); padding: 0; max-height: 320px; overflow-y: auto; display: flex; flex-direction: column; gap: var(--space-4); }
.thekoco-mini-cart__item { display: grid; grid-template-columns: 56px 1fr auto; gap: var(--space-3); align-items: start; }
.thekoco-mini-cart__thumb img { width: 56px; height: 56px; object-fit: cover; border-radius: var(--radius-base); }
.thekoco-mini-cart__name { margin: 0 0 var(--space-1); font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); }
.thekoco-mini-cart__name a { color: var(--color-text); }
.thekoco-mini-cart__price { margin: 0 0 var(--space-2); font-size: var(--font-size-sm); color: var(--color-text-muted); }
.thekoco-mini-cart__qty { display: inline-flex; align-items: center; gap: var(--space-2); }
.thekoco-mini-cart__qty-btn {
    width: 22px; height: 22px; display: grid; place-items: center;
    border: 1px solid var(--color-border); border-radius: var(--radius-sm);
    background: var(--color-bg); color: var(--color-text); font-size: var(--font-size-sm); padding: 0;
}
.thekoco-mini-cart__qty-value { min-width: 16px; text-align: center; font-size: var(--font-size-sm); }
.thekoco-mini-cart__remove { color: var(--color-text-muted); font-size: var(--font-size-lg); line-height: 1; }
.thekoco-mini-cart__remove:hover { color: var(--color-danger); }
.thekoco-mini-cart__footer { border-top: 1px solid var(--color-border); padding-top: var(--space-4); }
.thekoco-mini-cart__total { display: flex; justify-content: space-between; margin: 0 0 var(--space-4); font-weight: var(--font-weight-semibold); }
.thekoco-mini-cart__buttons { display: flex; gap: var(--space-3); }
.thekoco-mini-cart__buttons .btn { flex: 1; justify-content: center; }
.thekoco-mini-cart__empty { margin: 0; text-align: center; color: var(--color-text-muted); font-size: var(--font-size-sm); }

/* ================= Cart Page (Sprint Shopping Experience Foundation) =================
   `.thekoco-cart__layout` theo đúng pattern 2 cột đã lập từ `.thekoco-product__main` (Product
   Detail) — tỉ lệ khác (item list cần rộng hơn) nên đặt class riêng, không tái dùng thẳng class đó
   (khác ngữ cảnh trang). */
.thekoco-cart { padding-block: var(--space-10); min-height: 50vh; }
.thekoco-cart__title { font-size: var(--font-size-3xl); font-weight: var(--font-weight-bold); margin-bottom: var(--space-8); }

.thekoco-cart__layout { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 4fr); gap: var(--space-10); align-items: start; margin-bottom: var(--space-12); }

.thekoco-cart-list { display: flex; flex-direction: column; gap: var(--space-5); margin-bottom: var(--space-6); }
.thekoco-cart-item {
    display: grid;
    grid-template-columns: 88px minmax(0, 1fr) auto auto auto;
    gap: var(--space-4); align-items: center;
    padding-bottom: var(--space-5); border-bottom: 1px solid var(--color-border);
}
.thekoco-cart-item__thumb img { width: 88px; height: 88px; object-fit: cover; border-radius: var(--radius-base); }
.thekoco-cart-item__brand { margin: 0 0 var(--space-1); font-size: 12px; font-style: italic; color: var(--color-text-muted); }
.thekoco-cart-item__name { margin: 0; font-weight: var(--font-weight-medium); }
.thekoco-cart-item__name a { color: var(--color-text); }
.thekoco-cart-item__price { margin: var(--space-1) 0 0; font-size: var(--font-size-sm); color: var(--color-text-muted); }
.thekoco-cart-item__subtotal { font-weight: var(--font-weight-semibold); white-space: nowrap; }
.thekoco-cart-item__remove.product-remove a {
    display: grid; place-items: center; width: 28px; height: 28px;
    border-radius: var(--radius-full); color: var(--color-text-muted); font-size: var(--font-size-lg); line-height: 1;
}
.thekoco-cart-item__remove.product-remove a:hover { color: var(--color-danger); background: var(--color-bg-subtle); }

.thekoco-cart__actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-4); padding-top: var(--space-2); }

.thekoco-cart-summary { background: var(--color-bg-subtle); border-radius: var(--radius-lg); padding: var(--space-6); position: sticky; top: var(--space-6); }
.thekoco-cart-summary__heading { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); margin-bottom: var(--space-5); }
.thekoco-cart-summary__row { display: flex; justify-content: space-between; gap: var(--space-3); padding-block: var(--space-2); font-size: var(--font-size-sm); }
.thekoco-cart-summary__row--discount .thekoco-cart-summary__value { color: var(--color-danger); }
.thekoco-cart-summary__row--total { border-top: 1px solid var(--color-border); margin-top: var(--space-3); padding-top: var(--space-4); font-size: var(--font-size-lg); font-weight: var(--font-weight-bold); }
.thekoco-cart-summary__checkout { margin-top: var(--space-6); }
.thekoco-cart-summary__checkout-btn { width: 100%; justify-content: center; }
.thekoco-cart-summary__note { display: block; color: var(--color-success); font-size: var(--font-size-xs); margin-top: var(--space-1); }

/* Promotion Summary (Sprint 02 — Promotion User Experience) — tóm tắt ưu đãi đang áp dụng, đặt
   phía trên Subtotal trong `.thekoco-cart-summary` (Cart) và `.woocommerce-checkout-review-order-
   table` (Checkout, cùng Component). */
.thekoco-promo-summary { margin-bottom: var(--space-5); padding-bottom: var(--space-4); border-bottom: 1px solid var(--color-border); }
.thekoco-promo-summary__heading { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-text-muted); margin-bottom: var(--space-2); }
.thekoco-promo-summary__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.thekoco-promo-summary__item { display: flex; align-items: center; gap: var(--space-2); font-size: var(--font-size-sm); color: var(--color-text); }
.thekoco-promo-summary__icon { display: grid; place-items: center; width: 22px; height: 22px; flex-shrink: 0; border-radius: var(--radius-full); background: var(--color-success); color: #fff; font-size: 11px; font-weight: var(--font-weight-bold); }
.thekoco-promo-summary__note { margin-top: var(--space-2); font-size: var(--font-size-xs); color: var(--color-text-muted); }

/* Promotion Code Form (Sprint 02) — Coupon UI thật, thay ô nhập mã native WooCommerce không dùng
   tới. Tái dùng `.thekoco-input`/`.btn`/token màu có sẵn — không tạo Input Component mới. */
.thekoco-promo-panel { margin-bottom: var(--space-5); padding-bottom: var(--space-4); border-bottom: 1px solid var(--color-border); }
.thekoco-promo-panel__form { display: flex; gap: var(--space-2); }
.thekoco-promo-panel__form .thekoco-input { flex: 1; min-width: 0; }
.thekoco-promo-panel__message { margin-top: var(--space-2); font-size: var(--font-size-xs); }
.thekoco-promo-panel__message.is-success { color: var(--color-success); }
.thekoco-promo-panel__message.is-error { color: var(--color-danger); }
.thekoco-promo-panel__chips { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.thekoco-promo-chip { display: inline-flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-3); border-radius: var(--radius-full); background: var(--color-bg); border: 1px solid var(--color-border); font-size: var(--font-size-xs); }
.thekoco-promo-chip__label { color: var(--color-text); }
.thekoco-promo-chip__remove { display: grid; place-items: center; width: 18px; height: 18px; border-radius: var(--radius-full); color: var(--color-text-muted); line-height: 1; }
.thekoco-promo-chip__remove:hover { color: var(--color-danger); background: var(--color-bg-subtle); }

/* Gift badge — tái dùng NGUYÊN biến thể `.thekoco-badge--featured` có sẵn (không tạo màu mới), chỉ
   thêm định vị nhỏ cho ngữ cảnh cạnh tên sản phẩm (Cart/Mini Cart/Checkout Review Order). */
.thekoco-gift-badge { display: inline-flex; margin-left: var(--space-2); font-size: 10px; padding: 2px 8px; position: static; }

/* Recently Viewed — tái dùng `.product-grid` (Golden) + `.thekoco-product-related__heading` có
   sẵn (Product Detail), không tạo heading/grid mới. */
.thekoco-recently-viewed { margin-top: var(--space-12); }

/* Order Summary (Checkout) — thêm 2 modifier nhỏ vào family `.thekoco-cart-summary__*` sẵn có
   (KHÔNG tạo Component mới, đúng yêu cầu Sprint Checkout) cho danh sách sản phẩm compact. */
.thekoco-cart-summary__items { margin-bottom: var(--space-4); padding-bottom: var(--space-2); border-bottom: 1px solid var(--color-border); }
.thekoco-cart-summary__row--item .thekoco-cart-summary__label { flex: 1; }
.thekoco-cart-summary__row--shipping { flex-direction: column; align-items: stretch; }

/* ================= Checkout Page (Sprint WooCommerce Checkout Foundation) =================
   Layout theo đúng pattern 2 cột đã lập từ `.thekoco-cart__layout` — tỉ lệ khác (field bên trái
   cần rộng hơn) nên đặt class riêng, đúng tiền lệ đã áp dụng khi tách `.thekoco-cart__layout`
   khỏi `.thekoco-product__main`. */
.thekoco-checkout { padding-block: var(--space-10); min-height: 50vh; }
.thekoco-checkout__title { font-size: var(--font-size-3xl); font-weight: var(--font-weight-bold); margin-bottom: var(--space-8); }
.thekoco-checkout__layout { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: var(--space-10); align-items: start; }

/* Billing/Shipping — chỉ bọc khung, field thật là `woocommerce_form_field()` gốc (KHÔNG sửa PHP).
   Style trực tiếp lên class gốc WooCommerce bằng ĐÚNG token đã dùng cho `.thekoco-input`, để field
   Checkout nhìn giống hệt Input đã Generic hoá mà không cần chỉnh sửa cách WooCommerce sinh field. */
.thekoco-checkout-fields { margin-bottom: var(--space-8); }
.thekoco-checkout-fields__heading { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); margin-bottom: var(--space-5); }
.thekoco-checkout-fields .form-row { margin-bottom: var(--space-4); }
.thekoco-checkout-fields label { display: block; margin-bottom: var(--space-2); font-size: var(--font-size-sm); color: var(--color-text-muted); }
.thekoco-checkout-fields .input-text,
.thekoco-checkout-fields select,
.thekoco-checkout-fields textarea {
    width: 100%;
    height: 48px;
    padding-inline: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-base);
    background: var(--color-bg);
    color: var(--color-text);
    font-size: var(--font-size-sm);
    font-family: inherit;
}
.thekoco-checkout-fields textarea { height: auto; padding-block: var(--space-3); min-height: 96px; }
.thekoco-checkout-fields .woocommerce-input-wrapper { display: block; }
.thekoco-checkout-fields .select2-container .select2-selection--single { height: 48px; border: 1px solid var(--color-border); border-radius: var(--radius-base); }
.thekoco-checkout-fields .select2-container .select2-selection__rendered { line-height: 48px; padding-left: var(--space-4); }
.thekoco-checkout-fields .select2-container .select2-selection__arrow { height: 46px; }
.woocommerce-form__label-for-checkbox { display: flex; align-items: center; gap: var(--space-2); font-size: var(--font-size-sm); }

/* Coupon (Checkout) — bọc `promotion-code-form.php` (xem section Promotion Code Form ở trên), style
   khoảng cách riêng cho vị trí đầu trang Checkout (trước `.thekoco-checkout__layout`). */
.thekoco-checkout-coupon-wrap { margin-bottom: var(--space-8); }

/* Shipping method + Payment method list — style tối giản nhắm class gốc WooCommerce
   (`.woocommerce-shipping-methods`, `.wc_payment_method`), không override thêm file mới. */
.woocommerce-shipping-methods { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.woocommerce-shipping-methods li { display: flex; align-items: center; gap: var(--space-2); font-size: var(--font-size-sm); }

.thekoco-checkout-payment .wc_payment_methods { list-style: none; margin: 0 0 var(--space-6); padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.thekoco-checkout-payment .wc_payment_method { border: 1px solid var(--color-border); border-radius: var(--radius-base); padding: var(--space-4); }
.thekoco-checkout-payment .wc_payment_method > label { display: flex; align-items: center; gap: var(--space-2); font-weight: var(--font-weight-medium); cursor: pointer; }
.thekoco-checkout-payment .payment_box { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--color-border); font-size: var(--font-size-sm); color: var(--color-text-muted); }
.thekoco-checkout-payment .woocommerce-terms-and-conditions-wrapper { margin-bottom: var(--space-5); font-size: var(--font-size-sm); }
.thekoco-checkout-payment__place-order { display: flex; flex-direction: column; align-items: stretch; gap: var(--space-3); }
.thekoco-checkout-payment__submit { justify-content: center; width: 100%; }

/* Thank You — card MỚI (không có Component cũ phù hợp để tái dùng), style tối giản dùng chung token. */
.thekoco-order-confirmation { display: flex; justify-content: center; padding-block: var(--space-10); }
.thekoco-order-confirmation__card { max-width: 560px; width: 100%; text-align: center; background: var(--color-bg-subtle); border-radius: var(--radius-lg); padding: var(--space-10) var(--space-8); }
.thekoco-order-confirmation__icon { color: var(--color-success); margin-bottom: var(--space-4); }
.thekoco-order-confirmation__message { font-size: var(--font-size-lg); font-weight: var(--font-weight-medium); margin-bottom: var(--space-8); }
.thekoco-order-confirmation__details { list-style: none; margin: 0 0 var(--space-8); padding: 0; text-align: left; border-top: 1px solid var(--color-border); }
.thekoco-order-confirmation__details li { display: flex; justify-content: space-between; padding-block: var(--space-3); border-bottom: 1px solid var(--color-border); font-size: var(--font-size-sm); }
.thekoco-order-confirmation__actions .btn { justify-content: center; }
.thekoco-order-confirmation__card--failed .thekoco-order-confirmation__message { color: var(--color-danger); }

/* Search Results (Sprint Search + Filter Framework, MVP) — tái dùng `.thekoco-shop`/
   `.thekoco-shop__header`/`__title`/`__toolbar`/`.product-grid` NGUYÊN VẸN (không duplicate CSS đã
   có cho Shop/Category/Brand archive). Chỉ 3 lớp MỚI thật sự: layout 2 cột (Sidebar + Main, theo
   đúng pattern `.thekoco-cart__layout`/`.thekoco-checkout__layout` đã lập), Filter Sidebar, dropdown
   gợi ý Header Search, và style `<mark>` cho Keyword Highlight. */
.thekoco-search__layout { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 9fr); gap: var(--space-8); align-items: start; }

.thekoco-filter-sidebar { display: flex; flex-direction: column; gap: var(--space-5); padding: var(--space-5); border: 1px solid var(--color-border); border-radius: var(--radius-base); }
.thekoco-filter-sidebar__group { display: flex; flex-direction: column; gap: var(--space-2); }
.thekoco-filter-sidebar__label { font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); color: var(--color-text); }
.thekoco-filter-sidebar__select { height: 44px; padding-inline: var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-base); background: var(--color-bg); color: var(--color-text); font-size: var(--font-size-sm); }
.thekoco-filter-sidebar__price-row { display: flex; align-items: center; gap: var(--space-2); }
.thekoco-filter-sidebar__price-row .thekoco-input { width: 100%; height: 44px; }
.thekoco-filter-sidebar__price-sep { color: var(--color-text-muted); }
.thekoco-filter-sidebar__checkbox { display: flex; align-items: center; gap: var(--space-2); font-size: var(--font-size-sm); color: var(--color-text); cursor: pointer; }
.thekoco-filter-sidebar__submit { width: 100%; justify-content: center; }

.site-search__suggestions { position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 20; background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-base); box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12); max-height: 420px; overflow-y: auto; }
.thekoco-search-suggestions__group { padding: var(--space-3) var(--space-4); }
.thekoco-search-suggestions__group:not(:last-child) { border-bottom: 1px solid var(--color-border); }
.thekoco-search-suggestions__group-title { margin: 0 0 var(--space-2); font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-text-muted); }
.thekoco-search-suggestions__item { display: flex; align-items: center; gap: var(--space-3); padding-block: var(--space-2); color: var(--color-text); text-decoration: none; }
.thekoco-search-suggestions__item:hover { color: var(--color-brand-primary); }
.thekoco-search-suggestions__thumb img { width: 36px; height: 36px; object-fit: cover; border-radius: var(--radius-base); }
.thekoco-search-suggestions__name { flex: 1; font-size: var(--font-size-sm); }
.thekoco-search-suggestions__price { font-size: var(--font-size-xs); color: var(--color-text-muted); }

.thekoco-search-highlight { background: var(--color-brand-accent); color: inherit; padding: 0 2px; border-radius: 2px; }

/* Framework Customer (My Account) — tái dùng NGUYÊN `.btn`/Golden Button, `.thekoco-breadcrumb`/
   `.thekoco-empty-state`/`.thekoco-pagination` (Empty State/Pagination trong Order History), và
   ĐÚNG token đã lập cho `.thekoco-checkout-fields` (copy giá trị, đổi tên wrapper cho đúng ngữ
   cảnh — không tạo giá trị mới, đúng yêu cầu "Không tạo CSS trùng lặp"). Layout 2 cột theo đúng
   pattern `.thekoco-cart__layout`/`.thekoco-checkout__layout`/`.thekoco-search__layout` đã lập. */
.thekoco-account { padding-block: var(--space-10); min-height: 50vh; }
.thekoco-account__title { font-size: var(--font-size-3xl); font-weight: var(--font-weight-bold); margin-bottom: var(--space-8); }
.thekoco-account__layout { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 9fr); gap: var(--space-8); align-items: start; }

/* Account Navigation */
.thekoco-account-nav { padding: var(--space-5); border: 1px solid var(--color-border); border-radius: var(--radius-base); }
.thekoco-account-nav__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.thekoco-account-nav__link { display: block; padding: var(--space-3) var(--space-4); border-radius: var(--radius-base); color: var(--color-text-muted); text-decoration: none; font-size: var(--font-size-sm); }
.thekoco-account-nav__link:hover { background: var(--color-bg-subtle); color: var(--color-text); }
.thekoco-account-nav__item.is-active .thekoco-account-nav__link { background: var(--color-brand-primary); color: var(--color-text-on-dark); }

/* Dashboard */
.thekoco-account-dashboard__greeting { margin-bottom: var(--space-6); color: var(--color-text-muted); }
.thekoco-account-dashboard__links { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* Login / Register — `.u-columns`/`.u-column1`/`.u-column2`/`.col2-set` là class gốc WooCommerce
   (dùng chung với `my-address.php`) nhưng KHÔNG có CSS mặc định đi kèm — đây là lần đầu cần style,
   không phải trùng lặp. */
.thekoco-account-login.u-columns { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-10); }
.thekoco-account-login__heading { font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold); margin-bottom: var(--space-5); }
.thekoco-account-login__row-actions { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }
.thekoco-account-login__remember { display: flex; align-items: center; gap: var(--space-2); font-size: var(--font-size-sm); color: var(--color-text-muted); }
.thekoco-account-login__note { font-size: var(--font-size-sm); color: var(--color-text-muted); }
.woocommerce-LostPassword { margin-top: var(--space-3); font-size: var(--font-size-sm); }
.woocommerce-LostPassword a { color: var(--color-brand-primary); text-decoration: underline; }

/* Field styling dùng chung cho Login/Register/Edit Address/Lost Password — copy ĐÚNG token của
   `.thekoco-checkout-fields`. */
.thekoco-account-fields .form-row { margin-bottom: var(--space-4); }
.thekoco-account-fields label { display: block; margin-bottom: var(--space-2); font-size: var(--font-size-sm); color: var(--color-text-muted); }
.thekoco-account-fields .input-text,
.thekoco-account-fields select,
.thekoco-account-fields textarea {
    width: 100%;
    height: 48px;
    padding-inline: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-base);
    background: var(--color-bg);
    color: var(--color-text);
    font-size: var(--font-size-sm);
    font-family: inherit;
}
.thekoco-account-fields .select2-container .select2-selection--single { height: 48px; border: 1px solid var(--color-border); border-radius: var(--radius-base); }
.thekoco-account-fields .select2-container .select2-selection__rendered { line-height: 48px; padding-left: var(--space-4); }

/* Order History */
.thekoco-account-orders__table { width: 100%; border-collapse: collapse; margin-bottom: var(--space-6); }
.thekoco-account-orders__table th, .thekoco-account-orders__table td { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--color-border); text-align: left; font-size: var(--font-size-sm); }
.thekoco-account-orders__action { display: inline-flex; }

/* Order Detail */
.thekoco-account-order-detail__summary { margin-bottom: var(--space-6); color: var(--color-text-muted); }
.thekoco-account-order-detail__summary mark { background: var(--color-brand-accent); color: inherit; padding: 0 4px; border-radius: 2px; }
.thekoco-account-order-detail__updates-title { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); margin-bottom: var(--space-4); }
.thekoco-account-order-detail__updates { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-4); }
.thekoco-account-order-detail__updates .woocommerce-OrderUpdate-inner { padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-base); }
.thekoco-account-order-detail__updates .woocommerce-OrderUpdate-meta { font-size: var(--font-size-xs); color: var(--color-text-muted); margin-bottom: var(--space-2); }

/* Address Book */
.thekoco-account-addresses__desc { margin-bottom: var(--space-6); color: var(--color-text-muted); }
.thekoco-account-addresses__grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-6); }
.thekoco-account-addresses__card { padding: var(--space-5); border: 1px solid var(--color-border); border-radius: var(--radius-base); }
.thekoco-account-addresses__card-header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-4); }
.thekoco-account-addresses__card address { font-style: normal; color: var(--color-text-muted); line-height: var(--line-height-base); }

/* ================= Tablet (<=1024px) ================= */
@media (max-width: 1024px) {
    /* Container header (UI Audit 2026-08-23): thu hẹp dần theo breakpoint, khớp cách `.container`
       gốc đã giảm padding ở Mobile (var(--space-4), xem @media 768px bên dưới) — tránh header giữ
       nguyên padding Desktop (36px) trên màn hẹp trong khi mọi section khác đã co lại. */
    .site-header .container { max-width: none; padding-inline: var(--space-6); }
    /* Round 2 (CHG-002): grid-template-columns không còn nghĩa gì (base đã đổi sang Flex) — xoá,
       chỉ còn cần đổi gap/height cho compact hơn Desktop. Bỏ luôn `width: 320px` cố định trên
       Search (cùng lỗi gốc như bản Desktop cũ) — để `flex:1` (kế thừa từ base) tự co giãn theo
       khoảng trống thật ở breakpoint này, không hardcode số px riêng cho từng breakpoint. */
    .site-header__inner { height: 80px; }
    .site-logo .site-logo__mark { height: 52px; }
    .site-search { height: 40px; }
    /* CHG-008 (2026-08-23): CHG-007 cần override `margin-left` riêng cho breakpoint này (số suy ra
       theo tỷ lệ, không đo được). Cơ chế spacer `::before`/`::after` (base, xem `.site-header__inner`)
       không cần override — `flex-grow` tự tính lại theo bất kỳ chiều rộng nào còn lại, kể cả khi
       "leftover" nhỏ/âm so với baseline 1366px (khi đó Search co lại qua `flex: 0 1 576px` sẵn có,
       2 spacer co về gần 0 theo đúng tỷ lệ, không cần số suy ra riêng cho từng breakpoint nữa). Xác
       nhận không overflow bằng đo lại (xem Change Log § Validation). */
    .site-nav__inner { gap: var(--space-5); }
    .site-nav__menu { gap: var(--space-5); }
    .block-hero__image { max-width: 280px; }
    .block-hero__image img { max-height: 320px; }
    /* CHG-015 (Block 02 Visual Polish — Round 2): track co xuống ~371px cao ở breakpoint này (aspect
       ratio 1366:502 scale theo width hẹp hơn) — giữ nguyên `.block-hero__title` desktop
       (calc(3xl+6px)=42px) làm CTA slide dài nhất TRÀN xuống dưới track (đo được: -38px, tràn thật,
       xem docs/CHANGE_LOG.md § CHG-015 Evidence). Giảm về calc(2xl+4px)=32px riêng cho Tablet — đo
       lại xác nhận dư ≥48px cho mọi slide, không còn tràn. */
    .block-hero__title { font-size: calc(var(--font-size-2xl) + 4px); }
    /* Brand strip: <1024px cho phép cuộn ngang (14 logo không đủ chỗ 1 hàng ở màn hẹp) — chỉ
       Desktop mới bắt buộc hiển thị đủ 1 hàng không cuộn theo yêu cầu Review lần 2. */
    .block-brand-strip__track { overflow-x: auto; scrollbar-width: none; }
    .block-brand-strip__track::-webkit-scrollbar { display: none; }
    .block-brand-strip__item { flex: 0 0 auto; width: 110px; }
    /* CHG-018 (Block 3 — Trust Benefits): 4 cột vẫn vừa 1 hàng ở Tablet, chỉ giảm khoảng cách/đệm
       theo tỷ lệ màn hẹp hơn.
       CHG-021: icon 72px (Desktop, tỉ lệ icon/item ≈24% ở item width 300px) sẽ thành ≈31.6% nếu giữ
       nguyên 72px trên item Tablet hẹp hơn (228px) — lệch khỏi tỉ lệ Desktop. Giảm về 56px (≈24.5%,
       khớp tỉ lệ Desktop, đồng thời TRÙNG giá trị icon Desktop của CHG-020 trước đây — không phải số
       tuỳ tiện mới). */
    .block-trust { padding-block: var(--space-8); }
    .block-trust__grid { gap: var(--space-4); }
    .block-trust__item { padding-inline: var(--space-2); }
    .block-trust__icon img { width: 56px; height: 56px; }
    .product-grid { grid-template-columns: repeat(3, 1fr); }
    .block-best-sellers .section-heading { font-size: var(--font-size-3xl); }
    /* CHG-022: Tablet — 3 card/hàng vừa đủ (fluid % vẫn theo /4 sẽ quá nhỏ), giống cách Sale/New
       Products đổi flex-basis riêng theo breakpoint thay vì giữ nguyên số cột Desktop. */
    .block-best-sellers__carousel .product-grid .product-card { flex: 0 0 calc((100% - (var(--space-5) * 2)) / 3); }
    /* CHG-025 (Tablet): bố cục 2 cột cố định px (340px + gap 56px + 884px Carousel = 1280px) KHÔNG
       vừa viewport 1024px (chỉ còn ~944px sau padding) — tái dùng ĐÚNG pattern đã có sẵn trong
       chính file này cho `.block-new-products__grid` (2 cột desktop → xếp dọc ở Tablet/Mobile,
       carousel full-width bên dưới), không tự nghĩ cách mới. Bỏ khoá `aspect-ratio`/absolute
       positioning (không đủ chiều cao cho nội dung xếp dọc) — GIỐNG HỆT chiến lược đã dùng cho
       Mobile (<768px, xem bên dưới), chỉ khác kích thước chữ/card lớn hơn 1 chút cho Tablet. */
    .block-sale { aspect-ratio: auto; max-height: none; background-image: none; }
    .block-sale__content { position: static; }
    /* CHG-029: base giờ là `display:grid` (2 cột tỉ lệ) — Tablet/Mobile cần khai lại `display:flex`
       tường minh để `flex-direction:column` có tác dụng (grid không có khái niệm flex-direction). */
    .block-sale__inner { display: flex; flex-direction: column; align-items: flex-start; max-width: none; height: auto; padding: var(--space-6) var(--space-4); gap: var(--space-5); }
    .block-sale__col--content { flex-basis: auto; }
    .block-sale__col--products { width: 100%; }
    .block-sale__logo { width: 148px; height: auto; }
    .block-sale__heading { font-size: var(--font-size-xl); }
    .sale-countdown__value { font-size: var(--font-size-xl); }
    .sale-countdown__sep { font-size: var(--font-size-xl); }
    .block-sale__products .product-card { flex-basis: 200px; }
    .block-sale__products .product-card__title { font-size: 13px; }
    .block-sale__banner { display: block; aspect-ratio: 21/9; }
    /* Arrow (Tablet) — CHG-027: cùng kiểu overlay như Desktop (đè mép Card, `position:absolute`
       sẵn ở base rule, không cần override lại vị trí) — chỉ thu nhỏ kích thước nút cho vừa Card
       200px. Không còn nhánh "trả về position:static" như bản CHG-026 cũ (base rule giờ overlay ở
       MỌI breakpoint, không đặt navigation phụ thuộc riêng 1 kích thước màn hình — đúng yêu cầu). */
    .block-sale__arrow { width: 28px; height: 28px; }
    /* CHG-056 (Tablet): Grid 4→2 cột — không có design reference riêng cho Tablet (xem AUDIT.md
       §10/§23), 2 cột là lựa chọn an toàn thấp rủi ro nhất cho Grid 4 cột gốc (giữ card đủ rộng để
       đọc, không quá nhỏ như 4 cột ép vào ~220px). */
    .block-new-products__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    /* ADR-018 (Tablet): 2 cột → xếp dọc, cùng chiến lược đã dùng cho New Products/Sale ở breakpoint
       này (không có design reference riêng cho Tablet). Lịch sử bug ở breakpoint này (chỉ để tham
       khảo, KHÔNG còn áp dụng — đã đổi hẳn sang Grid ở CHG-063): CHG-061 dùng `flex-basis` px cố
       định trên Flex column từng bị hiểu nhầm thành CHIỀU CAO khi đổi trục; CHG-062 dùng
       `max-width` px cần reset tường minh ở Tablet. CHG-063 (2026-08-27): đổi hẳn desktop sang CSS
       Grid (`grid-template-columns`) — Grid KHÔNG có vấn đề "trục chính đổi hướng" như Flex (mỗi
       track luôn là 1 CỘT bất kể hướng đọc), nên chỉ cần đổi số cột về 1, không cần reset max-width/
       flex-basis phức tạp như 2 CHG trước — code Tablet/Mobile ĐƠN GIẢN HƠN hẳn nhờ chọn đúng công
       cụ (Grid) cho đúng bài toán (column sizing rõ ràng). */
    .block-trend__grid { grid-template-columns: 1fr; column-gap: 0; row-gap: var(--space-6); }
    .article-grid { grid-template-columns: repeat(2, 1fr); }
    /* CHG-065 (Tablet): không có Design reference riêng cho Tablet (chỉ Desktop, xem
       AUDIT_TAP_CHI_KOCO.md mục 15/23) — 3→2 cột cùng chiến lược đã dùng cho Best Sellers/New
       Products ở breakpoint này, giữ Section padding/heading tỷ lệ tương tự. */
    .block-magazine { padding-block: var(--space-10); }
    .block-magazine .section-heading { font-size: var(--font-size-3xl); }
    .site-footer__columns { grid-template-columns: repeat(2, 1fr); }
    .thekoco-woocommerce .products { grid-template-columns: repeat(3, 1fr); }

    /* GALLERY LEFT-GUTTER ALIGNMENT FIX (2026-09-11): rule gốc đổi từ Grid (`grid-template-columns`)
       sang Flex (xem dòng ~3304) — reset tương ứng ở đây từ `grid-template-columns:1fr` sang
       `flex-direction:column` (cùng ý nghĩa "1 cột", chỉ đổi thuộc tính đúng theo display mới).
       MOBILE GALLERY BLANK-SPACE FIX (2026-09-11): `flex-basis`(600/760px)/`min-width`(420px) của
       `__gallery-col`/`__summary-col` giờ chỉ khai trong `@media (min-width:1025px)` (dòng ~3339) —
       KHÔNG còn áp dụng ở breakpoint này nên KHÔNG cần reset gì ở đây nữa (trước đây từng cố "reset"
       bằng `flex-basis:auto` nhưng thua specificity của rule `:has()` gốc, gây khoảng trắng lớn sau
       thumbnail trên Mobile — xem giải thích đầy đủ tại rule gốc). `align-items:stretch` (mặc định
       flex, không bị ghi đè) tự làm cả 2 cột full-width khi xếp dọc, không cần khai thêm. */
    .thekoco-product__main { flex-direction: column; gap: var(--space-6); }
    /* PRODUCT DETAIL — LAYOUT WIDTH + GALLERY ALIGNMENT (2026-09-11): CÙNG breakpoint đã stack
       Gallery/Summary về 1 cột ở trên — cột thumbnail cố định 112px đứng cạnh ảnh chính (Desktop)
       không còn phù hợp khi cả khối co hẹp lại theo Tablet/Mobile (thumbnail sẽ ăn hết không gian
       hẹp, ảnh chính bị ép méo tỷ lệ). KHÔNG có Design reference riêng cho Tablet/Mobile (chỉ
       Desktop, đúng tiền lệ ghi nhận nhiều nơi khác trong file này) — revert về layout ngang đơn
       giản (ảnh chính TRÊN, dải thumbnail NGANG dưới, tự xuống dòng) đã hoạt động ổn định từ Sprint
       WooCommerce Product Detail Foundation, an toàn thấp rủi ro nhất, không tự nghĩ ra bố cục mới. */
    .thekoco-product-gallery { max-width: none; flex-direction: column; align-items: stretch; }
    /* GALLERY SCALE FIX: `align-items: flex-start` (Desktop, khai ở rule gốc) chỉ đúng khi
       flex-direction là ROW (ngăn __main bị stretch méo theo chiều cao cột thumbnail — xem giải
       thích đầy đủ ở rule gốc). Đổi flex-direction sang COLUMN ở breakpoint này khiến trục cross đổi
       từ dọc→ngang — nếu vẫn giữ `flex-start` sẽ làm __main KHÔNG giãn hết chiều rộng (co lại theo
       nội dung, vỡ layout) vì `flex:1` giờ chi phối trục chính (dọc) chứ không còn là chiều rộng.
       Trả về `stretch` (mặc định flex, đúng hành vi cần — full-width) để __main lấp đầy chiều rộng
       như trước PD-11. `max-width: none` reset `max-width:100%` (rule gốc, chặn tràn khi cha co hẹp
       hơn flex-basis) — không cần thiết ở layout cột (không còn cạnh tranh flex-basis với thumbnail
       cùng hàng nữa), giữ full-width tự nhiên. */
    .thekoco-product-gallery__main { max-width: none; flex-basis: auto; }
    /* GALLERY SCALE FIX: max-width 620px (Desktop) chỉ có ý nghĩa khi main image đứng CẠNH cột
       thumbnail cố định (chặn phình quá tỷ lệ Design) — ở layout 1 cột Tablet/Mobile, main image là
       khối DUY NHẤT trên hàng, giữ cap sẽ để trống 2 bên vô lý so với Summary/section khác cùng
       full-width, không đúng "responsive tốt".
       GALLERY LEFT-GUTTER ALIGNMENT FIX: thêm `flex-basis: auto` (reset `flex:0 1 600px` ở rule gốc)
       — bắt buộc, vì `.thekoco-product-gallery` đổi `flex-direction:column` ở breakpoint này (dòng
       ~3979) khiến trục CHÍNH đổi từ ngang→dọc; nếu vẫn giữ basis 600px, giá trị đó áp vào CHIỀU CAO
       thay vì chiều rộng → xung đột trực tiếp với `aspect-ratio:1/1` (chiều rộng full-width do
       `align-items:stretch` khác chiều cao cố định 600px) → LẶP LẠI đúng bug méo hình đã sửa ở PD-11
       §3 nhưng ở breakpoint khác. `flex-basis:auto` để aspect-ratio tự quyết định hoàn toàn. */
    .thekoco-product-gallery__thumbs {
        flex-direction: row;
        flex: 0 0 auto;
        max-height: none; /* GALLERY SCALE FIX: max-height 620px (Desktop) chỉ áp dụng cho cột DỌC —
            ở layout hàng ngang Tablet/Mobile (flex-wrap tự xuống dòng), height tự nhiên theo nội
            dung, giữ cap dọc ở đây vô nghĩa/có thể cắt nhầm hàng dưới. */
        flex-wrap: wrap;
        overflow-y: visible;
    }

    .thekoco-cart__layout { grid-template-columns: 1fr; gap: var(--space-6); }
    .thekoco-cart-summary { position: static; }
    .thekoco-cart-item { grid-template-columns: 72px minmax(0, 1fr) auto auto auto; gap: var(--space-3); }
    .thekoco-cart-item__thumb img { width: 72px; height: 72px; }
    .thekoco-mini-cart-panel { width: 320px; }

    .thekoco-checkout__layout { grid-template-columns: 1fr; gap: var(--space-6); }

    .thekoco-single-post__layout { grid-template-columns: 1fr; gap: var(--space-8); }
    .thekoco-post-sidebar { position: static; }

    .thekoco-search__layout { grid-template-columns: 1fr; gap: var(--space-6); }

    .thekoco-account__layout { grid-template-columns: 1fr; gap: var(--space-6); }
    .thekoco-account-login.u-columns { grid-template-columns: 1fr; gap: var(--space-8); }
    .thekoco-account-addresses__grid { grid-template-columns: 1fr; }
}

/* CHG-012 Fix (2026-08-24, Block 01 Review): thêm item thứ 9 "Về Koco" vào Primary Nav (Designer
   Reference có 9 item, không phải 8 — xem docs/CHANGE_LOG.md § CHG-012) khiến `.site-nav__menu`
   (flex-wrap: wrap, base) xuống 2 hàng trong khoảng ~1025–1205px — đo trực tiếp bằng
   getBoundingClientRect(): 9 item cần 837px (@ gap 20px, breakpoint Tablet ≤1024px) / ~1037px
   (@ gap 45px mặc định, >1024px), trong khi khoảng trống thực tế của `.site-nav__inner` chỉ đủ tới
   ~1205px thì mới đứng vừa 1 hàng với gap mặc định. Domain của lỗi rộng hơn khoảng Tablet 768–1024px
   sẵn có (kéo dài lên tới desktop hẹp ~1200px) — không thể vá bằng cách chỉnh gap trong khối
   @media 1024px hiện có (chỉ cover tới 1024, không đủ) hay ép gap nhỏ hơn (tại 769px sẽ cần ~4px —
   mất khoảng thở, phá typography). Thay vào đó: tách 1 breakpoint MỚI, riêng cho `.site-nav` (KHÔNG
   đụng khối `@media (max-width: 1024px)` phía trên — khối đó còn chứa Search/Hero/Product Grid/Cart
   không liên quan Block 01), tái dùng NGUYÊN bộ rule collapse-thành-hamburger đã có sẵn ở khối
   Mobile bên dưới (đã qua QA/production) — chỉ đổi threshold áp dụng SỚM HƠN cho riêng 4 selector
   `.site-nav__toggle`/`.site-nav__menu`/`.site-nav__menu li`/`.site-nav.is-open`/`.site-nav__menu a`.
   Không !important, không negative margin, không đổi token/breakpoint global — chỉ 1 threshold mới
   (1210px, có buffer an toàn so với điểm đo được ~1205px) áp riêng cho Nav. */
@media (max-width: 1210px) {
    .site-nav__toggle { display: inline-flex; }
    .site-nav__menu { display: none; width: 100%; flex-direction: column; align-items: flex-start; gap: 0; height: auto; }
    .site-nav__menu li { height: auto; width: 100%; }
    .site-nav.is-open .site-nav__inner { height: auto; flex-direction: column; align-items: flex-start; padding-block: var(--space-3); }
    .site-nav.is-open .site-nav__menu { display: flex; }
    .site-nav__menu a { display: block; height: auto; width: 100%; padding-block: var(--space-2); }
    /* Dropdown → luôn hiện, xếp phẳng bên dưới item cha (menu mobile vốn đã là 1 danh sách dọc mở
       toàn bộ — hover không khả dụng trên cảm ứng) thay vì `position:absolute` (sẽ trôi ra ngoài
       khối menu dọc, xem comment khối Dropdown Submenu phía trên cho lý do style gốc). */
    .site-nav__menu .sub-menu {
        position: static; opacity: 1; visibility: visible; transform: none;
        min-width: 0; border: none; box-shadow: none; background: transparent;
        padding: 0 0 var(--space-2) var(--space-4);
    }
    .site-nav__menu .sub-menu a { padding-block: var(--space-2); }
    .site-nav__menu .menu-item-has-children > a::after { transform: translateY(-1px) rotate(45deg); }
}

/* ================= Mobile (<=768px) ================= */
@media (max-width: 768px) {
    .container { padding-inline: var(--space-4); }
    .site-header .container { max-width: none; padding-inline: var(--space-4); }
    /* PRODUCT DETAIL — LAYOUT WIDTH + GALLERY ALIGNMENT (2026-09-11): `body.single-product
       .container` (dòng ~3272, specificity cao hơn `.container` gốc) không tự co theo rule
       `.container { padding-inline: var(--space-4) }` ở trên — CÙNG lý do Header phải khai lại
       riêng (dòng liền trên). Áp dụng lại đúng padding Mobile chung của site, không giữ 34px cố
       định (34px hợp lý cho Desktop/Tablet rộng, quá lớn so với màn 375-390px). */
    body.single-product .container { padding-inline: var(--space-4); }
    .topbar { height: auto; }
    .topbar__inner { justify-content: center; text-align: center; height: auto; padding-block: var(--space-2); }
    .topbar__links-primary { display: none; }
    /* Round 2 (CHG-002): base đã là Flex — ở đây `.site-search` bị display:none (xem rule bên
       dưới), còn lại đúng 2 phần tử hiển thị (logo + actions); `justify-content: space-between`
       tái tạo đúng bố cục "logo trái, actions phải" mà `grid-template-columns: 1fr auto` cũ làm. */
    .site-header__inner { justify-content: space-between; height: 68px; }
    .site-logo .site-logo__mark { height: 38px; }
    .site-header__main { position: relative; }
    .header-action--search-toggle { display: grid; }
    /* Sprint 07 Mobile Product Discovery (2026-08-05): `.site-search` KHÔNG bao giờ tồn tại 2 bản —
       vẫn đúng 1 form đã dùng ở Desktop, chỉ đổi từ nằm trong lưới Header (ẩn) sang panel
       `position:absolute` phủ hết chiều rộng Header khi mở, neo vào `.site-header__main` ở trên.
       Autocomplete AJAX (`main.js`) không cần sửa gì — vẫn cùng 1 input/1 endpoint. */
    .site-search { display: none; }
    .site-search.is-open {
        display: flex;
        position: absolute;
        top: 100%; left: 0; right: 0;
        width: auto; max-width: none;
        margin: 0; border-radius: 0;
        padding-inline: var(--space-4);
        z-index: 30;
        box-shadow: var(--shadow-lg);
    }
    /* RC1 Checklist (2026-08-05): fix Sprint 06 — input-zoom 16px bị mất khi Sprint 07 viết lại
       khối này. Dưới 16px, Safari/Chrome iOS tự zoom trang khi chạm vào input. */
    #thekoco-search-input { font-size: 16px; }
    .site-nav__inner { height: 48px; }
    .site-nav__toggle { display: inline-flex; }
    .site-nav__menu { display: none; width: 100%; flex-direction: column; align-items: flex-start; gap: 0; height: auto; }
    .site-nav__menu li { height: auto; width: 100%; }
    .site-nav.is-open .site-nav__inner { height: auto; flex-direction: column; align-items: flex-start; padding-block: var(--space-3); }
    .site-nav.is-open .site-nav__menu { display: flex; }
    .site-nav__menu a { display: block; height: auto; width: 100%; padding-block: var(--space-2); }

    /* Review lần 3 — Mobile Hero: thiết kế RIÊNG, không tái dùng bố cục Desktop.
       Ảnh cover lấy đúng phần có model (background-position lệch phải), text đặt ở dải scrim
       tối phía dưới (không đè lên mặt mẫu vì mặt mẫu nằm ở nửa trên khung hình). */
    .block-hero__track { aspect-ratio: auto; height: 460px; }
    .block-hero__slide--cover {
        background-position: 78% center;
        align-items: flex-end;
        padding-top: 0;
    }
    .block-hero__slide--cover::before {
        content: "";
        position: absolute; inset: 0;
        background: linear-gradient(to top, rgba(7,7,7,0.82) 0%, rgba(7,7,7,0.45) 45%, rgba(7,7,7,0) 75%);
        pointer-events: none;
    }
    .block-hero__slide--cover .block-hero__content { position: relative; z-index: 1; }
    .block-hero__slide--cover .block-hero__text { color: var(--color-text-on-dark); }
    .block-hero__slide--cover .block-hero__subtitle { color: var(--color-text-on-dark-muted); }
    .block-hero__content { flex-direction: column; align-items: flex-start; text-align: left; gap: var(--space-3); width: 100%; padding-bottom: var(--space-6); }
    .block-hero__text { max-width: 100%; }
    .block-hero__title { font-size: var(--font-size-xl); }
    .block-hero__subtitle { font-size: var(--font-size-sm); }
    /* CTA rõ ràng hơn ở mobile (ngược lại phần "giảm size" chỉ áp dụng cho Desktop). */
    .block-hero .btn { padding: var(--space-3) var(--space-6); font-size: var(--font-size-sm); }
    .block-hero__image { max-width: 220px; }
    .block-hero__image img { max-height: 220px; }
    /* Arrow nhỏ gọn hơn */
    .block-hero__arrow { width: 28px; height: 28px; font-size: var(--font-size-base); }
    .block-hero__arrow--prev { left: var(--space-3); }
    .block-hero__arrow--next { right: var(--space-3); }
    /* Pagination (dots) gọn hơn */
    .block-hero__dots { bottom: var(--space-3); gap: var(--space-1); }
    .block-hero__dot { width: 6px; height: 6px; }

    /* CHG-018 (Block 3 — Trust Benefits): 4 item không đủ chỗ 1 hàng ở mobile (icon+value+desc mỗi
       item cần tối thiểu ~80px rộng để không vỡ chữ) — chuyển sang lưới 2×2 theo đúng gợi ý trong
       yêu cầu gốc (mục 11), KHÔNG dùng transform:scale.
       CHG-021: divider đã XOÁ HẲN ở base rule (không còn `.block-trust__item:not(:last-child)`
       nào để override nữa — dọn theo, tránh để lại CSS chết vô nghĩa). */
    .block-trust { padding-block: var(--space-8); }
    .block-trust__grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-6) var(--space-4); }
    .block-trust__item { padding-inline: 0; }
    .block-trust__value { font-size: var(--font-size-xl); }
    /* CHG-020: icon 56px cố định (Desktop/Tablet) chiếm tới 34% chiều rộng item ở layout 2×2 mobile
       (164px/item, đo được `iconToItemRatio: 0.34` — so với ~19% ở Desktop) — đúng dạng "icon quá
       lớn trên mobile" mà yêu cầu gốc mục 14 cảnh báo trước. Giảm về 44px riêng cho Mobile (~27%,
       gần tỉ lệ Tablet hơn), vẫn `object-fit:contain` (không đổi logic fit, chỉ đổi khung hiển thị). */
    .block-trust__icon img { width: 44px; height: 44px; }
    .product-grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-4); }
    /* Sprint UI-05: card lớn hơn, ảnh nổi bật hơn ở mobile (chỉ style 'dark' = Best Sellers). */
    .product-card--dark .product-card__thumb img { width: 92%; height: 92%; }
    .product-card--dark .product-card__body { padding-top: var(--space-3); }
    .tab-filter { gap: var(--space-2); margin-bottom: var(--space-6); }
    .tab-filter__btn { padding: var(--space-2) var(--space-4); }
    .block-best-sellers .section-heading { font-size: var(--font-size-xl); gap: var(--space-2); letter-spacing: 0; }
    .block-best-sellers .section-heading__dash { width: 16px; }
    /* CHG-022: Mobile — carousel co về ~2 card/khung nhìn (chừa mép cho biết còn cuộn được), theo
       đúng tinh thần carousel mobile của Sale (168px)/New Products (200px) đã có. */
    .block-best-sellers__carousel .product-grid { gap: var(--space-4); }
    .block-best-sellers__carousel .product-grid .product-card { flex: 0 0 180px; }
    .block-best-sellers__scrollbar { max-width: none; margin-inline: var(--space-4); }
    /* Mobile: bỏ khoá aspect-ratio (nội dung cần nhiều chiều cao hơn tỷ lệ 1366:665 cho phép ở màn
       hẹp), nền quay lại màu đặc (theme_color), ảnh banner hiện dưới dạng box riêng NẰM DƯỚI
       content (DOM content→banner giữ nguyên thứ tự — không cần đảo), countdown vẫn nằm trên
       carousel, Product Card scroll ngang. Màn hẹp không đủ chỗ cho hiệu ứng gradient-lộ-ảnh
       full-bleed nên tách banner ra thành ảnh riêng như bố cục gốc UI-06 v1. */
    .block-sale { aspect-ratio: auto; max-height: none; background-image: none; }
    .block-sale__content { position: static; }
    /* CHG-029: cùng lý do đã ghi ở Tablet — khai lại `display:flex` tường minh (base là grid). */
    .block-sale__inner {
        display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-5);
        max-width: none; height: auto;
        padding-block: var(--space-8); padding-right: var(--space-4); padding-left: var(--space-4);
    }
    .block-sale__col--content { flex-basis: auto; }
    .block-sale__col--products { width: 100%; }
    .block-sale__banner { display: block; aspect-ratio: 16/9; }
    .block-sale__logo { width: 180px; height: auto; }
    .block-sale__heading { font-size: var(--font-size-xl); }
    .sale-countdown { gap: var(--space-2); margin-bottom: var(--space-4); }
    .sale-countdown__value { font-size: var(--font-size-lg); }
    .sale-countdown__sep { font-size: var(--font-size-lg); }
    .sale-countdown__label { font-size: 10px; }
    .block-sale__products .product-card { flex-basis: 168px; }
    /* Arrow (Mobile) — CHG-027: cùng kiểu overlay đè mép Card như Desktop/Tablet, chỉ thu nhỏ nút. */
    .block-sale__arrow { width: 30px; height: 30px; }

    /* CHG-050 (Mobile): giữ nguyên cấu trúc Card (title/tagline/divider/CTA) — chỉ thu Section
       padding + Heading size, cùng tỷ lệ Best Sellers đã áp dụng cho breakpoint này (CHG-023/039). */
    .block-promo-banners { padding-block: var(--space-10); }
    .block-promo-banners .section-heading { font-size: var(--font-size-xl); gap: var(--space-2); letter-spacing: 0; margin-bottom: var(--space-8); }
    .block-promo-banners .section-heading__dash { width: 16px; }
    .block-promo-banners__grid { grid-template-columns: 1fr; gap: var(--space-4); }

    /* CHG-056 (Mobile): Grid 4→1 cột, cùng tỷ lệ thu Section/Heading đã áp dụng cho Promo Banners
       ngay phía trên (không có design reference riêng cho Mobile, xem AUDIT.md §10/§23). */
    .block-new-products { padding-block: var(--space-10); }
    .block-new-products .section-heading { font-size: var(--font-size-xl); gap: var(--space-2); letter-spacing: 0; margin-bottom: var(--space-8); }
    .block-new-products .section-heading__dash { width: 16px; }
    .block-new-products__grid { grid-template-columns: 1fr; gap: var(--space-5); }

    /* ADR-018 (Mobile — Product Decisions §XIII, "Phương án B", đã chốt): full-width accordion,
       không ép layout 2 cột Desktop. Chevron hiện, panel CTA ẩn mặc định — main.js toggle thuộc
       tính `hidden` + xoay chevron. KHÔNG dùng JS cho chính hành vi điều hướng (link CTA vẫn là
       `<a href>` thật, JS chỉ toggle hiển thị — đúng yêu cầu §XVI). CHG-063: bỏ
       `.block-trend__grid`/`.block-trend__intro` ở breakpoint này — dư thừa, khối `@media
       max-width:1024px` phía trên đã reset `grid-template-columns: 1fr` áp dụng xuyên suốt mọi
       breakpoint hẹp hơn (768/480/375), không cần khai báo lại. */
    .block-trend { padding-block: var(--space-10); }
    .block-trend__heading { font-size: var(--font-size-2xl); }
    .block-trend__topic { flex-direction: column; align-items: stretch; }
    .block-trend__chevron { display: block; }
    .block-trend__topic[data-trend-expanded="true"] .block-trend__chevron { transform: rotate(180deg); }
    .block-trend__panel { display: none; padding: 0 var(--space-5) var(--space-4); justify-content: flex-start; }
    .block-trend__topic[data-trend-expanded="true"] .block-trend__panel { display: flex; }

    .article-grid { grid-template-columns: 1fr; }
    /* CHG-065 (Mobile): cùng tỷ lệ thu Section/Heading đã áp dụng cho Best Sellers/Promo Banners/
       New Products ở breakpoint này (không có Design reference riêng cho Mobile). */
    .block-magazine { padding-block: var(--space-10); }
    .block-magazine .section-heading { font-size: var(--font-size-xl); gap: var(--space-2); letter-spacing: 0; margin-bottom: var(--space-8); }
    .block-magazine .section-heading__dash { width: 16px; }
    .site-footer__columns { grid-template-columns: 1fr; }
    /* Review 4: Bottom bar xếp dọc ở Mobile (đúng § 8) — Payment rồi Social, căn trái thay vì
       space-between (không đủ chỗ ngang cho cả 2 hàng logo).
       F005 (2026-08-26): base rule đổi `display:flex` → `display:grid` (xem dòng ~1808) khiến
       `flex-direction:column` ở đây hết tác dụng — đổi tương đương sang `grid-template-columns: 1fr`
       (1 cột duy nhất, Grid tự xếp Copyright/Payment thành 2 HÀNG theo đúng thứ tự DOM, giống hệt
       flex-column cũ) + `justify-items:start` (thay `align-items:flex-start` cũ — đúng trục ngang
       tương ứng trong Grid) để giữ NGUYÊN hành vi/hiển thị Mobile đã có, không đổi gì thêm. */
    .site-footer__bottom { grid-template-columns: 1fr; justify-items: start; }

    /* Review 3 (2026-08-01): KHÔNG còn cần override padding-top riêng cho Mobile — công thức
       `calc(10.47% + 67px)` ở base rule đã tự anchor theo đáy Logo (background giờ `100% auto`,
       scale tuyến tính theo width, không còn phụ thuộc chiều cao container như `cover` cũ) nên gap
       Logo→Heading tự động giữ ~67px ở MỌI breakpoint, không cần dò riêng số cho Mobile nữa (bug
       "Heading đè Logo" ở Mobile trước đây do gốc rễ là `cover` phụ thuộc chiều cao — đã sửa tận
       gốc ở base rule, không phải vá bằng padding lớn hơn). Chỉ giữ padding-bottom + giảm cỡ chữ
       Heading (để không xuống quá nhiều dòng ở màn hẹp). */
    .site-footer__hero { padding-bottom: var(--space-8); }
    .site-footer__newsletter-heading { font-size: var(--font-size-xl); }
    .site-footer__newsletter-form { flex-direction: column; height: auto; border-radius: var(--radius-base); padding: var(--space-2); gap: var(--space-2); }
    .site-footer__newsletter-form input { height: 44px; padding-inline: var(--space-4); }
    .site-footer__newsletter-form button { height: 44px; width: 100%; }

    .thekoco-woocommerce .products { grid-template-columns: repeat(2, 1fr); }

    .thekoco-shop__title { font-size: var(--font-size-2xl); }
    .thekoco-shop__toolbar { flex-direction: column; align-items: flex-start; }
    .thekoco-sorting, .thekoco-sorting__select { width: 100%; }
    .thekoco-pagination__list { flex-wrap: wrap; }

    /* PRODUCT DETAIL — LAYOUT WIDTH + GALLERY ALIGNMENT (2026-09-11): thumb 112px (Desktop, đo
       pixel thật từ Design 1366px) quá lớn cho dải ngang ở màn 375-390px (4 thumb×112px = 448px,
       tràn) — Design không có bản Mobile riêng, trả về đúng kích thước 72px đã hoạt động ổn định
       trước đợt sửa này (an toàn thấp rủi ro nhất, không tự nghĩ số mới cho Mobile). */
    .thekoco-product-gallery__thumb { width: 72px; height: 72px; }
    .thekoco-product-summary__title { font-size: var(--font-size-xl); }
    /* PD-03 (§11 Responsive): trước đây title/price CÙNG xl(22px) ở Mobile — giữ nguyên vấn đề
       "Title/Price cùng cấp" mà PD-03 vừa sửa ở Desktop. Price vẫn phải nổi bật hơn Title ở MỌI
       breakpoint (§3.3) — 2xl(28px) đủ lớn để giữ hierarchy, đủ nhỏ để không tràn ở 390px (`.thekoco-
       product-summary__price` đã có `flex-wrap: wrap`, giá cũ tự xuống dòng nếu không đủ chỗ). */
    .thekoco-product-summary__price-new { font-size: var(--font-size-2xl); }
    .thekoco-product-action__form { gap: var(--space-3); }
    .thekoco-product-action__extra { gap: var(--space-4); }
    /* PD-18 (2026-09-14): layout grid 5-cột (PD-16/17) đã bị gỡ hoàn toàn — thay bằng editorial
       section (xem comment chính ở trên). Giảm nhẹ khoảng cách GIỮA CÁC SECTION ở Mobile
       (`--space-10` 40px → `--space-8` 32px) — tránh "khoảng trắng lớn" trên màn hẹp theo đúng yêu
       cầu §8, không đổi cấu trúc/max-width cột đọc (760px tự nhiên = 100% width ở mobile, không cần
       override riêng). */
    .thekoco-product-attributes__section { margin-top: var(--space-8); }

    /* Cart Page — 5 cột không đủ chỗ ở Mobile, chuyển sang grid-template-areas 3 hàng/item. */
    .thekoco-cart__title { font-size: var(--font-size-2xl); }
    .thekoco-cart-item {
        grid-template-columns: 64px 1fr auto;
        grid-template-areas:
            "thumb info   remove"
            "thumb qty    qty"
            "thumb price  price"
            "thumb subtotal subtotal";
        row-gap: var(--space-2);
    }
    .thekoco-cart-item__thumb { grid-area: thumb; }
    .thekoco-cart-item__thumb img { width: 64px; height: 64px; }
    .thekoco-cart-item__info { grid-area: info; }
    .thekoco-cart-item__remove { grid-area: remove; }
    .thekoco-cart-item__qty { grid-area: qty; }
    .thekoco-cart-item__subtotal { grid-area: subtotal; }
    .thekoco-cart__actions { flex-direction: column; align-items: stretch; }
    .thekoco-cart__update { width: 100%; justify-content: center; }

    .thekoco-mini-cart-panel { width: calc(100vw - var(--space-4) * 2); right: calc(-1 * var(--space-4)); }

    .thekoco-checkout__title { font-size: var(--font-size-2xl); }
    .thekoco-order-confirmation__card { padding: var(--space-8) var(--space-5); }

    .thekoco-promo-panel__form { flex-direction: column; align-items: stretch; }

    .thekoco-filter-sidebar__price-row { flex-direction: column; align-items: stretch; }
    .site-search__suggestions { position: fixed; top: auto; left: var(--space-4); right: var(--space-4); }

    .thekoco-account__title { font-size: var(--font-size-2xl); }
    .thekoco-account-dashboard__links { flex-direction: column; align-items: stretch; }
    .thekoco-account-orders__table { display: block; overflow-x: auto; }
    .thekoco-account-addresses__card-header { flex-direction: column; align-items: flex-start; }

    .thekoco-single-post__title { font-size: var(--font-size-xl); }
    /* Phase 2B (2026-09-03): `body.single-post .thekoco-single-post__title` (khối Dark Theme phía
       trên, specificity 2 class) giờ ĐÈ rule 1-class ngay phía trên tại MỌI viewport — kể cả trong
       `@media` này — vì specificity CSS không phụ thuộc thứ tự `@media`, chỉ phụ thuộc số lượng
       class/id/element. Không có Design Reference cho Mobile (audit mục 13, "No official mobile
       Source of Truth") — chọn `--font-size-2xl` (28px) làm cỡ hợp lý theo phán đoán kỹ thuật, cùng
       token đã dùng cho `.thekoco-account__title` ở ngay trên (dòng 3479, cùng ngữ cảnh Page Title
       thu nhỏ ở Mobile) — không tự sáng tác giá trị mới. */
    body.single-post .thekoco-single-post__title { font-size: var(--font-size-2xl); }
    .thekoco-toc { padding: var(--space-4); }
    .thekoco-post-nav .nav-links { grid-template-columns: 1fr; }
    .thekoco-post-nav .nav-next { text-align: left; }
}

/* ==========================================================================================
   Trang "Về KoCo – Korean Corner" (Sprint Product Phase 1 — Về KoCo Page, 2026-09-14)
   Reference: gioi-thieu.jpg (design/23-8/trang gioi thieu/gioi thieu/) — mọi màu nền dưới đây đo
   TRỰC TIẾP bằng System.Drawing.Bitmap.GetPixel() trên chính file ảnh đó (không áng chừng bằng
   mắt, cùng phương pháp CHG-021/CHG-030 đã dùng cho Homepage):
     - Header/Nav/Hero/"Vì sao chọn KoCo"/"Danh mục sản phẩm"/Company Info: #1F232E đồng nhất
       (x=10, y=75..3050) → TÁI DÙNG `--color-brand-primary` (không phải giá trị mới).
     - Breadcrumb bar: #2C3343 (x=10,y=220) → TÁI DÙNG `--color-card-info-bg` (đã có sẵn, dùng cho
       Info Area Product Card — cùng navy sáng hơn 1 nấc, không phải trùng hợp: đây là "màu dải
       ngang phụ trên nền navy chính" đã có tiền lệ trong Design System).
     - "Điều KoCo giữ vững": #181820 (x=10,y=1800..2180) → TÁI DÙNG `--color-trust-bg` (đã dùng cho
       Trust Benefits Homepage, cùng giá trị chính xác).
     - Gold accent chữ heading: #D7AF35 (828,691) ≈ `--color-brand-accent` (#d6b035, lệch nhiễu nén
       JPEG, không phải 2 màu khác nhau) → TÁI DÙNG, không thêm token màu mới nào cho page này.
   Component "— Heading —" (2 gạch 2 bên) TÁI DÙNG pattern đã có ở
   `.site-footer__newsletter-heading::before/::after` (footer.php) — không phát minh cách mới.
   Section "Vì sao chọn KoCo" (4 cột, icon+text căn giữa, không viền/khung) TÁI DÙNG pattern
   `.block-trust` (Homepage Trust Benefits) — cùng ngôn ngữ hình, đổi tên class riêng vì đây là
   Page khác (không dùng chung selector với Homepage để tránh 2 nơi vô tình ảnh hưởng lẫn nhau khi
   sửa sau này, nhưng SPACING/GRID/breakpoint giống hệt `.block-trust` đã có, không tự nghĩ số mới).

   FIX ROUND 2 (2026-09-15, Product Owner review trực tiếp screenshot thật — xem
   docs/06_Test/IMPLEMENTATION_VE_KOCO.md § Fix Round 2 cho bằng chứng đầy đủ):
   1) Container quá hẹp: bản đầu bọc Why-KoCo/Values/Categories/Company trong `.container` GỐC
      (max-width:1320px) không override gì thêm — ở 1366px gần như full-bleed (1320+2×24≈1368) nên
      KHÔNG lộ ra lúc review tại đúng viewport đó, nhưng ở 1440/1920px tạo margin trắng 2 bên rất lớn
      (600px tổng ở 1920px) — ĐÚNG bug "content lọt thỏm giữa màn hình" Product Owner chỉ ra. Site đã
      có sẵn pattern chuẩn cho "section rộng, không muốn cap 1320" — `.block-trust .container`/
      `.block-best-sellers .container`/`.block-promo-banners .container`/`.block-new-products
      .container` đều `max-width:none` + `padding-inline` cố định (34px, đo từ design gốc) — ÁP DỤNG
      LẠI ĐÚNG pattern đó cho Why-KoCo/Values/Categories/Company/Breadcrumb bar (Hero CỐ Ý giữ nguyên
      cột đọc hẹp 820px — đúng design, xem `.ve-koco-hero__container`, không phải bug).
   2) Typography: heading dùng `var(--font-family-base)` (Helvetica Neue) + `font-weight-bold` mặc
      định của `body` — SAI hoàn toàn tinh thần "elegant serif" của reference. Đổi sang
      `'Playfair Display'` (ĐÃ import sẵn ở đầu file, dòng 40, role "HEADINGS/TITLES" chính thức
      theo D-FIGMA-003/CHG-032 — không thêm font mới) weight 400 (regular, cùng weight Best Sellers
      đang dùng) thay vì bold — đúng "serif, nhẹ, premium" yêu cầu.

   FIX ROUND 3 (2026-09-15, visual refinement cuối — đối chiếu lại pixel `gioi-thieu.jpg` thay vì
   suy đoán, xem docs/06_Test/IMPLEMENTATION_VE_KOCO.md § Fix Round 3):
   1) Hero paragraph SAI text-align — đo pixel 2 dòng đoạn giới thiệu đầu tiên: dòng dài VÀ dòng
      ngắn hơn đều bắt đầu CÙNG 1 x trái → LEFT-ALIGNED (Round 1/2 suy đoán nhầm thành centered,
      cột hẹp 820px). Sửa: `.ve-koco-hero__paragraphs` text-align:left, width ≈ container (không
      còn cột hẹp 820px).
   2) Large Brand Image: Designer bổ sung asset thật `anh-cua-hang.png` (không tồn tại lúc Round
      1/2 audit) — thay hẳn panel logo/gradient placeholder.
   3) Typography nhiều chỗ quá nhỏ (`--font-size-sm`=14px cho mọi body text) — tăng lên
      `--font-size-base`=16px (Why-KoCo/Values description, Hero paragraph, Company Info), title
      card `--font-size-base`→`--font-size-lg` (18px) để giữ hierarchy Title>Body rõ ràng hơn khi
      cả 2 cùng lớn lên.
   ========================================================================================== */

.page-ve-koco { background: var(--color-brand-primary); }

/* Container strategy: TÁI DÙNG pattern full-bleed đã có (`.block-trust .container`/`.block-
   new-products .container`...) — max-width:none + padding-inline cố định 34px, ĐÚNG giá trị các
   block đó đang dùng, không tự nghĩ số mới. Hero KHÔNG nằm trong danh sách này — `.ve-koco-hero__
   container` (bên dưới) giữ cột đọc hẹp 820px có chủ đích, đúng design (đoạn giới thiệu dạng
   editorial, không phải grid cần full-bleed). */
.ve-koco-breadcrumb-bar .container,
.ve-koco-hero .container,
.ve-koco-why .container,
.ve-koco-values .container,
.ve-koco-categories .container,
.ve-koco-company .container {
    max-width: none;
    padding-inline: 34px;
}

/* Component dùng chung "— Heading —" — trích xuất TỪ `.site-footer__newsletter-heading` (footer.php)
   thành 1 class riêng cho Page này (không sửa lại rule gốc của Footer, tránh side-effect ngoài
   scope Sprint — xem §21 Scope Control). Font: Playfair Display 400 — xem ghi chú Fix Round 2. */
.ve-koco-section-heading {
    color: var(--color-brand-accent);
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: calc(var(--font-size-3xl) + 4px); font-weight: var(--font-weight-regular);
    letter-spacing: 0.01em; text-align: center; margin-bottom: var(--space-12);
    display: flex; align-items: center; justify-content: center; gap: var(--space-5);
}
.ve-koco-section-heading::before, .ve-koco-section-heading::after {
    content: ""; flex: 0 0 auto; width: 36px; height: 1px; background: currentColor; opacity: 0.55;
}

.ve-koco-breadcrumb-bar { background: var(--color-card-info-bg); padding-block: var(--space-4); }
.ve-koco-breadcrumb-bar .thekoco-breadcrumb { margin-bottom: 0; color: var(--color-text-on-dark-muted); }
.ve-koco-breadcrumb-bar .thekoco-breadcrumb__item a { color: var(--color-text-on-dark-muted); }
.ve-koco-breadcrumb-bar .thekoco-breadcrumb__item a:hover { color: var(--color-brand-accent); }
.ve-koco-breadcrumb-bar .thekoco-breadcrumb__item [aria-current="page"] { color: var(--color-text-on-dark); }
.ve-koco-breadcrumb-bar .thekoco-breadcrumb__item:not(:last-child)::after { color: var(--color-text-on-dark-muted); }

/* Hero / Introduction — Fix Round 3 (2026-09-15, đối chiếu lại pixel trên gioi-thieu.jpg): đo trực
   tiếp 2 dòng đoạn giới thiệu — line 1 (dài) VÀ line 2 (ngắn hơn) đều bắt đầu CÙNG 1 toạ độ x trái
   (x≈54, viewport 1366px) → xác nhận đoạn văn LEFT-ALIGNED (không phải centered — nếu centered,
   dòng ngắn hơn phải thụt vào 2 bên đều nhau, không thẳng lề trái). Chiều rộng đo được ~1236px
   (x=54..1290) ≈ gần trọn container (1366 − 2×34 ≈ 1298) — KHÔNG phải cột đọc hẹp 820px của bản
   trước (đó là suy đoán sai ở Round 1, không đo từ ảnh gốc). H1/Subtitle VẪN centered (đo tâm text
   ≈ tâm viewport) — chỉ đoạn giới thiệu (`__paragraph`) đổi sang trái, dùng CHUNG container
   full-bleed 34px với section khác (xem danh sách override phía trên), không còn wrapper hẹp
   riêng. */
.ve-koco-hero { padding-block: var(--space-20) var(--space-16); }
.ve-koco-hero__container { text-align: center; }
.ve-koco-hero__heading {
    color: var(--color-brand-accent);
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: calc(var(--font-size-4xl) + 6px); font-weight: var(--font-weight-regular);
    letter-spacing: 0.01em; line-height: var(--line-height-tight); margin-bottom: var(--space-5);
    display: flex; align-items: center; justify-content: center; gap: var(--space-4);
}
.ve-koco-hero__heading::before, .ve-koco-hero__heading::after {
    content: ""; flex: 0 0 auto; width: 40px; height: 1px; background: currentColor; opacity: 0.55;
}
.ve-koco-hero__subtitle {
    color: var(--color-text-on-dark); font-size: var(--font-size-xl); font-weight: var(--font-weight-medium);
    margin-bottom: var(--space-10);
}
/* Đoạn giới thiệu — LEFT-ALIGNED, rộng gần hết container (xem ghi chú đo pixel phía trên). Wrapper
   riêng (không dùng chung `text-align:center` của `.ve-koco-hero__container`) để 2 khối căn khác
   nhau trong cùng section mà không cần thêm `<div>` lồng nhau trong template. */
.ve-koco-hero__paragraphs { text-align: left; max-width: 1180px; margin-inline: auto; }
.ve-koco-hero__paragraph {
    color: var(--color-text-on-dark-muted); font-size: var(--font-size-base);
    line-height: var(--line-height-loose); margin-bottom: var(--space-5);
}
.ve-koco-hero__paragraph:last-child { margin-bottom: 0; }

/* Vì sao chọn KoCo — 4 cột, icon+text căn giữa (cùng ngôn ngữ hình `.block-trust`) */
.ve-koco-why { padding-block: var(--space-16); }
.ve-koco-why__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-10); }
.ve-koco-why__card { text-align: center; padding-inline: var(--space-3); }
.ve-koco-why__icon { display: inline-flex; align-items: center; justify-content: center; height: 60px; margin-bottom: var(--space-5); }
.ve-koco-why__icon img { width: auto; height: 100%; max-width: 60px; object-fit: contain; }
.ve-koco-why__title {
    color: var(--color-text-on-dark); font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight); margin-bottom: var(--space-3);
}
.ve-koco-why__desc { color: var(--color-text-on-dark-muted); font-size: var(--font-size-base); line-height: var(--line-height-loose); }

/* Large Brand Image — Fix Round 3 (2026-09-15): Designer bổ sung asset thật `anh-cua-hang.png`
   (ảnh mặt tiền cửa hàng, 1366×509) — thay hoàn toàn panel logo/gradient placeholder của Round 1-2.
   Full-width, KHÔNG container/gutter (đúng "full-width visual break" §7) — `aspect-ratio` giữ đúng
   tỉ lệ gốc thay vì height cố định (an toàn hơn ở viewport khác 1366px, không co giãn méo ảnh). */
.ve-koco-brand-panel { display: block; line-height: 0; }
.ve-koco-brand-panel__media {
    display: block; width: 100%; aspect-ratio: 1366 / 509;
    object-fit: cover; object-position: center;
}

/* Điều KoCo giữ vững — 4 cột, icon+title cùng hàng, mô tả bên dưới căn trái (đo nền #181820) */
.ve-koco-values { background: var(--color-trust-bg); padding-block: var(--space-16); }
.ve-koco-values__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-10); }
.ve-koco-values__card { padding-inline: var(--space-3); }
.ve-koco-values__head { display: flex; align-items: center; gap: var(--space-4); margin-bottom: var(--space-3); }
.ve-koco-values__icon { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; width: 48px; height: 48px; }
.ve-koco-values__icon img { width: 100%; height: 100%; object-fit: contain; }
.ve-koco-values__title {
    color: var(--color-text-on-dark); font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight); margin: 0;
}
.ve-koco-values__desc { color: var(--color-text-on-dark-muted); font-size: var(--font-size-base); line-height: var(--line-height-loose); }

/* Danh mục sản phẩm — 3 card ngang, TOÀN BỘ card là 1 vùng bấm được (Fix Round 2: trước đây chỉ
   `.media`/`.cta` là <a>, title đứng ngoài — đổi cấu trúc: `.ve-koco-categories__card` chính là thẻ
   `<a>` bọc hết title/ảnh/CTA, xem page-ve-koco.php). Hover: ảnh scale rất nhẹ (1.03) + CTA đổi màu
   — không animation mạnh. */
.ve-koco-categories { padding-block: var(--space-16); }
.ve-koco-categories__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-10); }
.ve-koco-categories__card {
    display: block; color: inherit; text-decoration: none;
    border-radius: var(--radius-base);
}
.ve-koco-categories__card:focus-visible { outline-offset: 4px; }
.ve-koco-categories__title {
    color: var(--color-text-on-dark); font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight); margin: 0 0 var(--space-4);
}
.ve-koco-categories__media { display: block; aspect-ratio: 414 / 473; border-radius: var(--radius-base); overflow: hidden; }
.ve-koco-categories__media img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform var(--duration-slow) var(--easing-standard); }
.ve-koco-categories__card:hover .ve-koco-categories__media img,
.ve-koco-categories__card:focus-visible .ve-koco-categories__media img { transform: scale(1.03); }
.ve-koco-categories__cta {
    display: inline-flex; align-items: center; gap: var(--space-2); color: var(--color-brand-accent);
    font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); margin-top: var(--space-4);
    transition: gap var(--duration-base) var(--easing-standard);
}
.ve-koco-categories__card:hover .ve-koco-categories__cta,
.ve-koco-categories__card:focus-visible .ve-koco-categories__cta { gap: var(--space-3); opacity: 0.85; }

/* Company Information — Fix Round 3: font-size sm(14px)→base(16px), margin giữa dòng tăng nhẹ để
   dễ đọc hơn khi đứng cạnh Product Categories (card title 18px) — vẫn giữ left-aligned + width vừa
   phải (không kéo full-bleed hết viewport, đúng đo pixel reference — đây là block thông tin ngắn,
   không phải grid). */
.ve-koco-company { padding-block: var(--space-4) var(--space-20); }
.ve-koco-company__inner { max-width: 900px; }
.ve-koco-company__inner p { color: var(--color-text-on-dark-muted); font-size: var(--font-size-base); line-height: var(--line-height-loose); margin-bottom: var(--space-3); }
.ve-koco-company__inner p:last-child { margin-bottom: 0; }
.ve-koco-company__inner strong { color: var(--color-text-on-dark); font-weight: var(--font-weight-semibold); }
.ve-koco-company__contact a { color: var(--color-brand-accent); }
.ve-koco-company__contact a:hover { text-decoration: underline; }

@media (max-width: 1024px) {
    .ve-koco-breadcrumb-bar .container,
    .ve-koco-hero .container,
    .ve-koco-why .container, .ve-koco-values .container,
    .ve-koco-categories .container, .ve-koco-company .container { padding-inline: var(--space-6); }
    .ve-koco-hero { padding-block: var(--space-16) var(--space-12); }
    .ve-koco-hero__heading { font-size: var(--font-size-3xl); }
    .ve-koco-why__grid, .ve-koco-values__grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-8) var(--space-6); }
    .ve-koco-categories__grid { grid-template-columns: repeat(3, 1fr); gap: var(--space-5); }
}

@media (max-width: 768px) {
    .ve-koco-hero { padding-block: var(--space-12) var(--space-10); }
    .ve-koco-breadcrumb-bar .container,
    .ve-koco-hero .container,
    .ve-koco-why .container, .ve-koco-values .container,
    .ve-koco-categories .container, .ve-koco-company .container { padding-inline: var(--space-4); }
    .ve-koco-hero__heading { font-size: var(--font-size-2xl); gap: var(--space-3); }
    .ve-koco-hero__heading::before, .ve-koco-hero__heading::after { width: 24px; }
    .ve-koco-hero__subtitle { font-size: var(--font-size-lg); margin-bottom: var(--space-8); }
    .ve-koco-hero__paragraphs { max-width: none; }
    .ve-koco-section-heading { font-size: var(--font-size-2xl); margin-bottom: var(--space-8); }
    .ve-koco-why, .ve-koco-values, .ve-koco-categories { padding-block: var(--space-12); }
    /* §16 Responsive: "4-column features → 2 columns tablet → 1 column mobile" — mỗi card ở đây có
       cả tiêu đề lẫn mô tả dài (khác `.block-trust` Homepage chỉ có icon+số ngắn, lý do CHG-018 chọn
       2×2 ở Mobile cho block đó) — 1 cột đọc trọn câu mô tả không bị ngắt dòng hẹp/khó đọc. */
    .ve-koco-why__grid, .ve-koco-values__grid { grid-template-columns: 1fr; gap: var(--space-8); }
    .ve-koco-categories__grid { grid-template-columns: 1fr; gap: var(--space-10); }
    .ve-koco-categories__grid .ve-koco-categories__card { max-width: 340px; margin-inline: auto; }
    .ve-koco-company { padding-block: var(--space-2) var(--space-14); }
}

/* ==========================================================================
   Trang "Tầm nhìn, sứ mệnh và thông điệp" (Sprint Product Phase 1 — Vision/Mission/Core Values
   Page). Reference: Tam-nhin-su-menh.jpg (design/23-8/trang gioi thieu/tam nhin/) — TOÀN BỘ giá
   trị dưới đây đo TRỰC TIẾP bằng Python/Pillow `Image.getpixel()`/scan trên chính file ảnh đó
   (không áng chừng bằng mắt, cùng phương pháp CHG-021/CHG-039 đã dùng cho Homepage/Về KoCo):

   REDO ROUND 1 (2026-09-15, Product Owner review trực tiếp — 3 lỗi lớn phát hiện qua screenshot
   thật, xem docs/06_Test/IMPLEMENTATION_VISION_MISSION.md § Redo Round 1 cho bằng chứng pixel đầy
   đủ):
   1) Container sai — bản đầu bọc Breadcrumb/Intro/Cards trong `.container` GỐC (max-width:1320px)
      → tạo margin trắng lớn ở viewport >1366px. Site đã có sẵn pattern chuẩn cho "section rộng,
      không muốn cap 1320" (`.block-best-sellers .container`/`.ve-koco-*` đều
      `max-width:none;padding-inline:34px` — ĐÚNG 34px đo được ở trang này: breadcrumb/intro text
      left edge = x=34px trên canvas 1366px) — áp dụng lại ĐÚNG pattern đó, không phát minh giá trị
      mới.
   2) Nền/texture sai — bản đầu để nền TRẮNG cho Breadcrumb/Intro/Cards + tự vẽ pattern chéo bằng
      CSS `repeating-linear-gradient`. Đo pixel thật: Breadcrumb = #1F232E (TÁI DÙNG
      `--color-brand-primary`), Intro = #2C3343 (TÁI DÙNG `--color-card-info-bg`, đã có sẵn — cùng
      "dải nền phụ sáng hơn 1 nấc trên nền navy chính" như `.ve-koco-breadcrumb-bar` dùng), khu vực
      quanh 2 Card = #1F232E. Pattern chéo KHÔNG phải CSS — nó nằm SẴN trong chính asset
      `anh-tam-nhin.jpg` (verify trực tiếp: crop vùng bên phải ảnh gốc, thấy rõ hoạ tiết chéo nhạt
      đã in sẵn) — hiển thị ảnh full-width là đủ, không cần lớp CSS pattern riêng.
   3) Hero composition sai — bản đầu chia ảnh 68% + pattern CSS 32% như 2 khối riêng. Đo thật: ảnh
      `anh-tam-nhin.jpg` hiển thị FULL-BLEED 100% bề rộng dải Hero (ảnh CHẠM mép trái, x=0 — pattern
      chéo là MỘT PHẦN của ảnh, không phải khối riêng), panel gold là 1 lớp overlay ĐÈ LÊN ảnh (đo
      % trên canvas 1366×503 gốc: left=52.7%, right-inset=6.15%, top-inset=11.5%,
      bottom-inset=27.4% — dùng % thay vì px cố định để giữ đúng tỉ lệ overlap khi ảnh full-bleed
      giãn theo viewport). Icon tròn đo được KÍCH THƯỚC GỐC ~92px (khớp `icon tam nhin.svg`
      93×93, không co giãn theo ảnh), tâm icon nằm NGAY TRÊN đường biên trái của panel (không phải
      nằm gọn bên trong panel như bản đầu).
   Ngoài 3 lỗi trên: Typography — body text trên panel gold là SANS-SERIF BOLD (không phải regular,
   đo trực tiếp nét chữ dày trên `Trở thành cầu nối...`); ảnh 2 Card hiển thị ĐÚNG tỉ lệ gốc
   587×174/586×174 (dẹt hơn nhiều so với 474×260 đã dùng ở bản đầu); Card là hình chữ nhật TRẮNG
   PHẲNG nổi trên nền tối — không border/shadow/radius nào quan sát được trên reference (đã bỏ).
   ========================================================================== */
.page-vision { background: var(--color-brand-primary); }

/* Breadcrumb bar — nền tối #1F232E (đo được, TÁI DÙNG token), chữ theo cặp token
   text-on-dark/text-on-dark-muted — CÙNG PATTERN `.ve-koco-breadcrumb-bar`/`body.single-post
   .thekoco-breadcrumb*` đã có (không phát minh cách override mới, `.thekoco-breadcrumb*` gốc
   dùng token light-theme nên PHẢI override lại ở đây, đúng scope `.vision-breadcrumb-bar`, không
   sửa rule gốc dùng chung toàn site). */
.vision-breadcrumb-bar { background: var(--color-brand-primary); padding-block: var(--space-4); }
.vision-breadcrumb-bar .container { max-width: none; padding-inline: 34px; }
.vision-breadcrumb-bar .thekoco-breadcrumb { margin-bottom: 0; }
.vision-breadcrumb-bar .thekoco-breadcrumb__item a { color: var(--color-text-on-dark-muted); }
.vision-breadcrumb-bar .thekoco-breadcrumb__item a:hover { color: var(--color-brand-accent); }
.vision-breadcrumb-bar .thekoco-breadcrumb__item [aria-current="page"] { color: var(--color-text-on-dark); font-weight: var(--font-weight-semibold); }
.vision-breadcrumb-bar .thekoco-breadcrumb__item:not(:last-child)::after { color: var(--color-text-on-dark-muted); }

/* Intro — §4 Important: text căn trái, full-bleed + padding cố định 34px (KHÔNG phải
   max-width:1320px căn giữa), nền panel info riêng #2C3343 (đo được, TÁI DÙNG
   `--color-card-info-bg`), chữ màu kem sáng trên nền tối. */
.vision-intro { background: var(--color-card-info-bg); padding-block: var(--space-8); }
.vision-intro .container { max-width: none; padding-inline: 34px; }
.vision-intro__text {
    text-align: left; color: var(--color-text-on-dark); font-size: var(--font-size-base);
    line-height: var(--line-height-loose);
}

/* Hero — 1 ảnh DUY NHẤT full-bleed 100% bề rộng (pattern chéo đã in sẵn trong chính
   `anh-tam-nhin.jpg`, KHÔNG dựng lại bằng CSS), panel gold + icon là overlay tuyệt đối ĐÈ LÊN
   ảnh, định vị bằng % (đúng tỉ lệ đo được trên canvas gốc 1366×503) để overlap giữ nguyên đúng tỉ
   lệ ở MỌI bề rộng viewport — không px cố định (trừ Icon, đo được là kích thước GỐC không co giãn
   theo ảnh, xem `.vision-hero__icon`). */
.vision-hero { background: var(--color-brand-primary); }
.vision-hero__inner { position: relative; width: 100%; }
.vision-hero__media { width: 100%; aspect-ratio: 1366 / 503; overflow: hidden; }
.vision-hero__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* FIX ROUND 2 (2026-09-15, Product Owner visual polish — đo lại riêng phần panel bằng Python/
   Pillow, xem docs/06_Test/IMPLEMENTATION_VISION_MISSION.md § Fix Round 2): panel KHÔNG
   vertical-center (`justify-content:center` bản trước là suy đoán) — đo pixel xác nhận content
   TOP-ANCHORED, lệch bất đối xứng (top gap 61px ≠ bottom gap 22px trên canvas gốc). Heading/Body
   left edge đo được x=791, panel left=720 → padding-left THẬT ~71px (space-16/64px, gần nhất trong
   thang token). Icon (92px, đo Fix Round 1) đặt `top:40px` khớp CHÍNH XÁC y đo được (532, = panel
   top 492 + 40). */
.vision-hero__panel {
    position: absolute; left: 52.7%; right: 6.15%; top: 11.5%; bottom: 27.4%;
    background: var(--color-brand-accent); color: var(--color-text-on-dark);
    padding: 56px var(--space-8) var(--space-6) var(--space-16);
    overflow: visible;
}
.vision-hero__icon {
    position: absolute; left: -46px; top: 40px;
    display: inline-flex; align-items: center; justify-content: center;
    filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.25));
}
.vision-hero__icon img { display: block; width: 92px; height: 92px; }
.vision-hero__heading {
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: var(--font-size-3xl); font-weight: var(--font-weight-bold);
    line-height: var(--line-height-tight);
    margin: 0 0 var(--space-6);
}
.vision-hero__body {
    font-size: var(--font-size-xl); font-weight: var(--font-weight-bold);
    line-height: 1.6;
}

/* Cards — Sứ mệnh / Giá trị cốt lõi, nền section tối #1F232E (TÁI DÙNG --color-brand-primary,
   cùng token page background — cards không đổi màu nền RIÊNG, chỉ page đổi), Card TRẮNG PHẲNG
   (không border/shadow/radius — reference không có) nổi trên nền tối. Container full-bleed +
   padding 34px cùng pattern Breadcrumb/Intro ở trên (KHÔNG dùng `.container` mặc định 1320px).
   Ảnh Card đúng tỉ lệ GỐC 587×174/586×174 (`object-fit:cover` chỉ để tránh lệch khi container co
   giãn, KHÔNG đổi tỉ lệ hiển thị đi). Equal height 2 card qua CSS Grid mặc định (`align-items:
   stretch`). */
.vision-cards { background: var(--color-brand-primary); padding-block: var(--space-20) var(--space-16); }
.vision-cards .container { max-width: none; padding-inline: 34px; }
.vision-cards__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-8); }
.vision-card { display: flex; flex-direction: column; height: 100%; background: var(--color-bg); }
.vision-card__media { aspect-ratio: 587 / 174; }
.vision-card__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vision-card__body { position: relative; padding: 0 var(--space-8) var(--space-8); flex: 1 1 auto; display: flex; flex-direction: column; }
.vision-card__head { height: 0; }
.vision-card__icon {
    position: absolute; left: var(--space-8); top: -40px;
    display: inline-flex; align-items: center; justify-content: center;
    filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.15));
}
.vision-card__icon img { display: block; width: 88px; height: 88px; }
.vision-card__heading {
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: var(--font-size-2xl); font-weight: var(--font-weight-bold);
    color: var(--color-text); margin: var(--space-10) 0 var(--space-5);
}
.vision-card__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-4); }
.vision-card__list li {
    position: relative; padding-left: var(--space-5);
    color: var(--color-text); font-size: var(--font-size-base); line-height: var(--line-height-loose);
}
.vision-card__list li::before {
    content: ""; position: absolute; left: 0; top: 0.7em; width: 6px; height: 6px;
    border-radius: var(--radius-full); background: var(--color-brand-accent);
}
.vision-card__subheading {
    font-family: var(--font-family-base); font-weight: var(--font-weight-bold); font-size: var(--font-size-lg);
    color: var(--color-topbar-bg); margin-bottom: var(--space-4);
}
.vision-card__desc { color: var(--color-text-muted); font-size: var(--font-size-base); line-height: var(--line-height-loose); }

@media (max-width: 1024px) {
    .vision-breadcrumb-bar .container,
    .vision-intro .container,
    .vision-cards .container { padding-inline: var(--space-6); }
    .vision-hero__panel { padding: var(--space-6); }
    .vision-hero__heading { font-size: var(--font-size-2xl); }
    .vision-cards__grid { gap: var(--space-6); }
    .vision-card__heading { font-size: var(--font-size-xl); }
}

@media (max-width: 768px) {
    .vision-breadcrumb-bar .container,
    .vision-intro .container,
    .vision-cards .container { padding-inline: var(--space-4); }
    .vision-intro { padding-block: var(--space-6); }
    .vision-hero__inner { display: flex; flex-direction: column; }
    .vision-hero__media { width: 100%; aspect-ratio: 4 / 3; }
    .vision-hero__panel {
        position: static; width: auto; left: auto; right: auto; top: auto; bottom: auto;
        padding: var(--space-10) var(--space-6) var(--space-8);
    }
    .vision-hero__icon { top: -32px; left: var(--space-6); }
    .vision-hero__icon img { width: 56px; height: 56px; }
    .vision-hero__heading { font-size: var(--font-size-2xl); margin-top: var(--space-5); }
    .vision-cards { padding-block: var(--space-14) var(--space-12); }
    .vision-cards__grid { grid-template-columns: 1fr; gap: var(--space-10); }
    .vision-card__icon img { width: 56px; height: 56px; }
    .vision-card__heading { margin-top: var(--space-8); }
}

/* ==========================================================================================
   Trang "Hợp tác cùng KoCo" (Sprint Product Phase 1, 2026-09-15) — xem D-VK-004,
   docs/01_Project/DECISION_LOG.md + docs/06_Test/IMPLEMENTATION_HOP_TAC.md.

   Container strategy: TÁI DÙNG NGUYÊN pattern full-bleed đã dùng cho `.ve-koco-*`/`.vision-*`
   (max-width:none + padding-inline:34px cố định) — ĐÃ áp dụng ngay từ đầu cho MỌI section của
   trang này (không lặp lại lỗi "container hẹp" mà `.ve-koco-*` từng mắc ở Round 1, xem comment
   §4700 phía trên). KHÔNG có section nào của trang này dùng `.container` gốc (max-width:1320px).

   Màu sắc: đo TRỰC TIẾP bằng PHP GD (máy dev không có Python, dùng phương án thay thế đã có tiền
   lệ — CHG-050/PD-06) trên `design/23-8/trang gioi thieu/Hop-tac-cung-Koco.jpg` — xem
   docs/06_Test/IMPLEMENTATION_HOP_TAC.md § Color Mapping cho toạ độ/giá trị đo đầy đủ. Kết quả:
   Hero/Capabilities/Market/Advantages/Process/CTA dùng chung `--color-brand-primary` (KHÔNG đổi
   nền — chỉ "Năng lực vận hành hiện tại" có nền khác, đúng yêu cầu §7); Operations section dùng
   `--color-trust-bg` (khớp tuyệt đối #181820); bảng data rows dùng `--color-border-dark` (đo
   #282428, chênh ≤2 đơn vị/kênh = nhiễu nén, cùng nguyên tắc đã áp dụng cho `--color-card-info-bg`
   trước đây); CTA chính dùng `--color-topbar-bg` (khớp tuyệt đối #781035); CTA phụ + Advantages
   card dùng 2 token MỚI `--color-btn-light-bg`/`--color-card-neutral-bg` (tokens.css, không token
   nào sẵn có gần đúng); thanh Quy trình hợp tác dùng lại NGUYÊN `--color-payment-box-bg` (khớp
   tuyệt đối #494539).

   Component dùng chung "— Heading —": TÁI DÙNG kỹ thuật `.ve-koco-section-heading` (::before/
   ::after 2 line vàng, Playfair Display) nhưng đặt class riêng `.hop-tac-section-heading` — đúng
   "Scope Control" đã áp dụng nhất quán ở 2 trang trước (không sửa lại rule gốc dùng chung, tránh
   side-effect ngoài phạm vi Sprint). CTA Button TÁI DÙNG NGUYÊN `.btn` (shape/padding/hover có sẵn,
   style.css dòng ~92) — chỉ override background/color qua modifier riêng `.hop-tac-cta__btn--*`
   (cùng pattern `.block-sale__btn` — "biến thể riêng thay vì sửa `.btn--dark`/thêm `.btn--*` mới
   dùng chung", xem dòng ~1596).
   ========================================================================================== */

.page-hop-tac { background: var(--color-brand-primary); }

.hop-tac-breadcrumb-bar .container,
.hop-tac-hero .container,
.hop-tac-capabilities .container,
.hop-tac-operations .container,
.hop-tac-market .container,
.hop-tac-advantages .container,
.hop-tac-process .container,
.hop-tac-cta .container {
    max-width: none;
    padding-inline: 34px;
}

.hop-tac-breadcrumb-bar { background: var(--color-card-info-bg); padding-block: var(--space-4); }
.hop-tac-breadcrumb-bar .thekoco-breadcrumb { margin-bottom: 0; color: var(--color-text-on-dark-muted); }
.hop-tac-breadcrumb-bar .thekoco-breadcrumb__item a { color: var(--color-text-on-dark-muted); }
.hop-tac-breadcrumb-bar .thekoco-breadcrumb__item a:hover { color: var(--color-brand-accent); }
.hop-tac-breadcrumb-bar .thekoco-breadcrumb__item [aria-current="page"] { color: var(--color-text-on-dark); }
.hop-tac-breadcrumb-bar .thekoco-breadcrumb__item:not(:last-child)::after { color: var(--color-text-on-dark-muted); }

/* "— Heading —" dùng chung cho mọi section title của trang này (H2) — xem doc-comment khối trên
   cho lý do tách class riêng thay vì tái dùng `.ve-koco-section-heading`. */
.hop-tac-section-heading {
    color: var(--color-brand-accent);
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: calc(var(--font-size-3xl) + 4px); font-weight: var(--font-weight-regular);
    letter-spacing: 0.01em; text-align: center; margin-bottom: var(--space-12);
    display: flex; align-items: center; justify-content: center; gap: var(--space-5);
}
.hop-tac-section-heading::before, .hop-tac-section-heading::after {
    content: ""; flex: 0 0 auto; width: 36px; height: 1px; background: currentColor; opacity: 0.55;
}

/* Hero — H1 + subtitle CENTER, đoạn giới thiệu (`__intro`) LEFT-ALIGN full-bleed riêng (đúng yêu
   cầu §5 "description KHÔNG căn giữa") — 2 wrapper `.container` riêng trong cùng section để 2 khối
   căn khác nhau mà không cần `text-align` lồng nhau, cùng kỹ thuật `.ve-koco-hero__paragraphs`. */
.hop-tac-hero { padding-block: var(--space-20) var(--space-10); }
.hop-tac-hero__container { text-align: center; }
.hop-tac-hero__heading {
    color: var(--color-brand-accent);
    font-family: 'Playfair Display', 'Noto Serif', var(--font-family-base);
    font-size: calc(var(--font-size-4xl) + 6px); font-weight: var(--font-weight-regular);
    letter-spacing: 0.01em; line-height: var(--line-height-tight); margin-bottom: var(--space-5);
    display: flex; align-items: center; justify-content: center; gap: var(--space-4);
}
.hop-tac-hero__heading::before, .hop-tac-hero__heading::after {
    content: ""; flex: 0 0 auto; width: 40px; height: 1px; background: currentColor; opacity: 0.55;
}
.hop-tac-hero__subtitle {
    color: var(--color-text-on-dark); font-size: var(--font-size-xl); font-weight: var(--font-weight-medium);
    margin: 0;
}
.hop-tac-hero__intro-wrap { margin-top: var(--space-8); }
.hop-tac-hero__intro {
    color: var(--color-text-on-dark-muted); font-size: var(--font-size-base);
    line-height: var(--line-height-loose); margin: 0;
    /* UI Polish (2026-09-15, xem docs/06_Test/IMPLEMENTATION_HOP_TAC.md § P — Hero Intro Width
       Fix): bản polish trước để `max-width: 1180px;` (giá trị copy nhầm từ 1 bản nháp cũ, không đo
       từ reference) khiến block bị bó hẹp + lệch trái trong `.hop-tac-hero__intro-wrap` (chính là
       `.container` full-bleed, `max-width:none; padding-inline:34px` — content area thật RỘNG hơn
       1180px nhiều ở ≥1366px, xem `.hop-tac-hero .container`) — BỎ hẳn `max-width`, không thay bằng
       số cứng khác: phần tử block mặc định `width:auto` đã tự lấp đầy ĐÚNG content width của
       `.container` cha (kế thừa đúng layout system hiện tại, không cần khai báo `width:100%` tường
       minh). KHÔNG đổi `color`/`font-size`/`line-height`/`margin`. */
    text-align: justify;
}

/* "KoCo làm được gì cho đối tác" — grid 2 cột desktop (đúng §6, KHÔNG dùng card UI nặng — không
   border/shadow, chỉ spacing + typography). */
.hop-tac-capabilities { padding-block: var(--space-4) var(--space-16); }
.hop-tac-capabilities__grid {
    display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-10) var(--space-12);
}
.hop-tac-capabilities__title {
    color: var(--color-text-on-dark); font-family: var(--font-family-heading);
    font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold);
    margin: 0 0 var(--space-3); display: flex; gap: var(--space-2);
}
/* UI Polish (2026-09-15, xem docs/06_Test/IMPLEMENTATION_HOP_TAC.md § O): số thứ tự đúng thiết kế
   là TRẮNG (`--color-text-on-dark`, cùng token đã dùng cho heading kế bên — KHÔNG phải gold), bản
   trước dùng nhầm `--color-brand-accent`. Không đổi heading/vị trí/font-size/gap. */
.hop-tac-capabilities__index { color: var(--color-text-on-dark); flex: 0 0 auto; }
.hop-tac-capabilities__desc {
    color: var(--color-text-on-dark-muted); font-size: var(--font-size-base);
    line-height: var(--line-height-loose); margin: 0;
    /* UI Polish (2026-09-15): đối chiếu pixel Designer Reference — dòng 1 mỗi mô tả kéo dài gần
       khớp mép phải cột trước khi xuống dòng 2 ngắn hơn, đúng đặc điểm text justify. */
    text-align: justify;
}

/* "Năng lực vận hành hiện tại" — nền RIÊNG (`--color-trust-bg`, khác hẳn section trước/sau, đúng
   §7 + đo pixel xác nhận). Table semantic thật (§19 Accessibility — không dùng div giả bảng). */
.hop-tac-operations { background: var(--color-trust-bg); padding-block: var(--space-16); }
.hop-tac-operations__table-wrap { max-width: 940px; margin-inline: auto; overflow-x: auto; }
.hop-tac-operations__table { width: 100%; border-collapse: collapse; }
.hop-tac-operations__table th, .hop-tac-operations__table td {
    padding: var(--space-4) var(--space-6); text-align: left; font-size: var(--font-size-base);
    border-bottom: 1px solid var(--color-border-dark);
}
.hop-tac-operations__table thead th {
    color: var(--color-text-on-dark-muted); font-weight: var(--font-weight-medium);
    font-size: var(--font-size-sm); text-transform: uppercase; letter-spacing: 0.04em;
    border-bottom: 1px solid var(--color-border-dark);
}
.hop-tac-operations__table tbody tr { background: var(--color-border-dark); }
.hop-tac-operations__table tbody tr + tr td, .hop-tac-operations__table tbody tr + tr th {
    border-top: 1px solid var(--color-trust-bg);
}
.hop-tac-operations__table tbody th {
    color: var(--color-text-on-dark); font-weight: var(--font-weight-medium); width: 42%;
}
.hop-tac-operations__table tbody td { color: var(--color-text-on-dark-muted); }
.hop-tac-operations__note {
    max-width: 940px; margin: var(--space-6) auto 0; color: var(--color-text-on-dark-muted);
    font-size: var(--font-size-sm); line-height: var(--line-height-loose);
}

/* "Thị trường" — đơn giản, đúng §8 (không map/chart/icon). */
.hop-tac-market { padding-block: var(--space-16) var(--space-14); text-align: center; }
.hop-tac-market__text {
    color: var(--color-text-on-dark-muted); font-size: var(--font-size-base); margin: 0;
}

/* "Lợi thế của KoCo" — 6 block ngang desktop (§9), card nhẹ (`--color-card-neutral-bg`, không
   shadow/border nặng — đúng §16). */
.hop-tac-advantages { padding-block: 0 var(--space-16); }
.hop-tac-advantages__grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--space-4); }
.hop-tac-advantages__card {
    background: var(--color-card-neutral-bg); border-radius: var(--radius-base);
    padding: var(--space-5) var(--space-4); height: 100%;
}
.hop-tac-advantages__desc {
    color: var(--color-text-on-dark); font-size: var(--font-size-sm);
    line-height: var(--line-height-base); margin: 0;
    /* UI Polish (2026-09-15, xem docs/06_Test/IMPLEMENTATION_HOP_TAC.md § O — item "C" của yêu cầu
       polish, áp dụng vì cùng pattern body text VÀ đối chiếu pixel Designer Reference xác nhận rõ:
       khoảng cách giữa các từ trong card bị kéo giãn rõ rệt ở nhiều dòng — VD "Năng lực OEM/ ODM đã
       được kiểm chứng bằng hai", "Xử lý trọn gói khâu" — đặc điểm điển hình của text justify trong
       cột hẹp, không phải left). */
    text-align: justify;
}

/* "Quy trình hợp tác" — 6 hàng ngang, circle số + bar nền (§10). */
.hop-tac-process { padding-block: 0 var(--space-16); }
.hop-tac-process__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); max-width: 940px; margin-inline: auto; }
.hop-tac-process__step { display: flex; align-items: center; gap: 0; }
.hop-tac-process__number {
    flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: var(--radius-full);
    background: var(--color-btn-light-bg); color: var(--color-brand-primary);
    /* UI Polish (2026-09-15, xem docs/06_Test/IMPLEMENTATION_HOP_TAC.md § O): số đậm nét hơn, đúng
       yêu cầu reference — `--font-weight-semibold` (600) → `--font-weight-bold` (700, token đã có
       sẵn trong hệ thống). Giữ nguyên kích thước circle/màu nền/màu số/vị trí/overlap. */
    font-weight: var(--font-weight-bold); font-size: var(--font-size-sm);
    position: relative; z-index: 1; margin-right: -14px;
}
.hop-tac-process__bar {
    flex: 1 1 auto; background: var(--color-payment-box-bg); color: var(--color-text-on-dark);
    border-radius: var(--radius-full); padding: var(--space-3) var(--space-6) var(--space-3) var(--space-8);
    font-size: var(--font-size-base);
}

/* CTA — TÁI DÙNG `.btn` (shape/padding/hover), chỉ override màu qua modifier riêng (cùng pattern
   `.block-sale__btn`, xem doc-comment khối trên). */
.hop-tac-cta { padding-block: 0 var(--space-20); text-align: center; }
.hop-tac-cta__inner { display: flex; align-items: center; justify-content: center; gap: var(--space-4); flex-wrap: wrap; }
.hop-tac-cta__btn { text-transform: uppercase; letter-spacing: 0.03em; font-weight: var(--font-weight-semibold); }
.hop-tac-cta__btn--primary { background: var(--color-topbar-bg); color: var(--color-text-on-dark); }
.hop-tac-cta__btn--secondary { background: var(--color-btn-light-bg); color: var(--color-brand-primary); }

@media (max-width: 1024px) {
    .hop-tac-capabilities__grid { gap: var(--space-8); }
    .hop-tac-advantages__grid { grid-template-columns: repeat(3, 1fr); gap: var(--space-4) var(--space-3); }
}

@media (max-width: 768px) {
    .hop-tac-breadcrumb-bar .container,
    .hop-tac-hero .container, .hop-tac-capabilities .container,
    .hop-tac-operations .container, .hop-tac-market .container,
    .hop-tac-advantages .container, .hop-tac-process .container,
    .hop-tac-cta .container { padding-inline: var(--space-6); }
    .hop-tac-hero { padding-block: var(--space-16) var(--space-10); }
    .hop-tac-hero__heading { font-size: var(--font-size-3xl); }
    .hop-tac-section-heading { font-size: var(--font-size-2xl); margin-bottom: var(--space-8); }
    .hop-tac-capabilities__grid { grid-template-columns: 1fr; gap: var(--space-8); }
    .hop-tac-capabilities { padding-block: var(--space-2) var(--space-12); }
    .hop-tac-operations { padding-block: var(--space-12); }
    .hop-tac-operations__table th, .hop-tac-operations__table td { padding: var(--space-3) var(--space-4); }
    .hop-tac-market { padding-block: var(--space-12) var(--space-10); }
    .hop-tac-advantages { padding-block: 0 var(--space-12); }
    .hop-tac-advantages__grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-4); }
    .hop-tac-process { padding-block: 0 var(--space-12); }
    .hop-tac-process__bar { font-size: var(--font-size-sm); }
    .hop-tac-cta { padding-block: 0 var(--space-14); }
}

@media (max-width: 480px) {
    .hop-tac-hero__heading { font-size: var(--font-size-2xl); gap: var(--space-3); }
    .hop-tac-hero__heading::before, .hop-tac-hero__heading::after { width: 24px; }
    .hop-tac-hero__subtitle { font-size: var(--font-size-lg); }
    .hop-tac-advantages__grid { grid-template-columns: 1fr; }
    .hop-tac-process__step { align-items: flex-start; }
    .hop-tac-process__bar { padding: var(--space-3) var(--space-4) var(--space-3) var(--space-6); line-height: var(--line-height-base); }
    .hop-tac-cta__inner { flex-direction: column; }
    /* box-sizing:border-box tường minh (phòng thủ) — đo được `box-sizing` hiệu lực trên `.btn` tại
       runtime là `content-box` (tương tác cascade giữa reset `*{box-sizing:border-box}` của theme,
       style.css dòng 43, với `*{box-sizing:inherit}` của WordPress block-library core CSS load sau
       — ngoài phạm vi Sprint này, không sửa rule global) khiến `width:100%` cộng thêm padding/
       border của `.btn` gốc (style.css dòng ~92-113), gây tràn ngang ~26px trên mobile (375px) —
       xác nhận qua `getBoundingClientRect()` thật. Set trực tiếp trên class riêng của trang này,
       không đụng `.btn`/reset global dùng chung. */
    .hop-tac-cta__btn { width: 100%; justify-content: center; box-sizing: border-box; }
}

/* Sprint 04 Product UX Polish (2026-08-05): bug thật phát hiện qua browser — grep xác nhận 0 kết
   quả cho "prefers-reduced-motion" trong toàn file trước Sprint này. Hero slider fade/swipe,
   Reading Progress bar, carousel scroll-behavior, mọi transition/animation hover/dropdown/tab-
   filter... đều chạy vô điều kiện, không tôn trọng cài đặt hệ điều hành của người dùng nhạy cảm
   chuyển động. Khối này CHỈ áp dụng khi người dùng đã bật preference — không đổi hành vi mặc định
   hiện tại cho ai chưa bật. Dùng pattern chuẩn (rút gần về 0 thay vì display:none) — an toàn hơn
   tắt hẳn animation/transition vì không phá bất kỳ logic JS nào lỡ đợi sự kiện animationend/
   transitionend (đã grep `main.js` xác nhận hiện tại không có, nhưng giữ pattern an toàn cho
   tương lai). */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}
