/* ============================================================
   Vinpack — layout.css
   Header dính, panel mega, dropdown, ngăn kéo di động.

   NẠP SAU global.css. inc/enqueue.php khai vinpack-global làm dependency
   nên thứ tự nguồn là cố định, và nhiều rule ở đây cố ý hoà độ ưu tiên với
   global.css rồi thắng bằng thứ tự nguồn.

   BA LUẬT CỦA FILE NÀY — tests/test-css-cascade.php mục 5 canh cả ba:

   1. KHÔNG khai custom property nào. Token thuộc về global.css và
      theme.json. Một `--z-header` thứ hai ở đây sẽ thắng theo thứ tự nguồn
      và âm thầm đổi mọi thứ dùng token đó ở nơi khác — mà bộ giải cascade
      không mô hình hoá var(), nên sẽ không có khẳng định nào lộ ra.

   2. KHÔNG dùng !important. Mọi thứ ở đây thắng bằng độ ưu tiên hoặc bằng
      thứ tự nguồn, hai thứ đo được.

   3. KHÔNG khai `outline` ở bất kỳ đâu. Vòng focus của mục 10 global.css ở
      (0,1,0); một `.vp-nav-trigger:focus{outline:none}` là (0,1,1) và nuốt
      trọn nó. Đó đúng là lỗi đắt nhất của Phase A, và các phần tử mới ở đây
      là chỗ nó dễ quay lại nhất.

   VÀ MỘT LUẬT NỮA, VỀ `hidden`:

   Nền mờ của ngăn kéo và tabpanel của mega đóng/mở bằng THUỘC TÍNH `hidden`
   do JS bật tắt. UA có sẵn `[hidden]{display:none}`. Một dòng
   `.vp-drawer-backdrop{display:block}` trông vô hại sẽ đè nó và làm nền mờ
   phủ kín trang mọi lúc. Nên mọi rule chạm tới `display` của hai thứ đó đều
   phải đi qua `:not([hidden])`. Cùng hình dạng với lỗi
   `.art-content table{display:block}` của Phase A.
   ============================================================ */

/* ============================================================
   1. Thanh header
   ============================================================ */

/*
 * box-shadow:none PHẢI khai tường minh ở đây. Nếu chỉ có rule .scrolled thì
 * bộ giải trả "không rule nào" cho trạng thái nghỉ và không ai chứng minh
 * được rằng bóng CHƯA có trước khi cuộn — mà bóng thường trực đúng là cách
 * class .scrolled trở thành đồ trang trí.
 */
.site-header{
  position:sticky;
  top:0;
  z-index:var(--z-header);
  background:var(--navy);
  border-bottom:1px solid var(--border);
  box-shadow:none;
  transition:background var(--dur-state) var(--ease),
             box-shadow var(--dur-state) var(--ease);
}

.site-header.scrolled{ background:var(--white); box-shadow:var(--sh-2); }

/*
 * VÌ SAO NỀN HEADER ĐỔI, VÀ CÁI GIÁ CỦA VIỆC ĐÓ — Phase D Task 2.
 *
 * Trước Task này `.site-header` là `background:var(--white)` ở CẢ HAI trạng
 * thái, và `.scrolled` chỉ thêm bóng. Mockup thì ngược lại: nghỉ là nền --navy,
 * cuộn xuống mới thành trắng. Hai biến thể logo của mockup (`.for-dark` /
 * `.for-light`) chỉ có nghĩa khi nền THẬT SỰ đổi — trên một header trắng thường
 * trực thì "bản cho nền tối" là trắng-trên-trắng, tức một cái lỗ.
 *
 * Nên nền đổi, và mọi thứ nằm trên nền đó phải đổi theo. Trạng thái nghỉ cần
 * đè tường minh ở BỐN chỗ, mỗi chỗ một lý do đo được:
 *
 *   - .nav-links > li > a và .vp-nav-trigger: `color:var(--text)` #1A1A1A trên
 *     --navy #001BB7 chỉ đạt 1.52:1 — gần như không đọc được.
 *   - .vp-drawer-toggle: `color:var(--navy)` trên --navy là 1:1, ba gạch
 *     hamburger biến mất hẳn ở đúng mốc ≤900px nơi nó là lối điều hướng DUY
 *     NHẤT.
 *   - .btn-primary: `background:var(--navy)` trên nền --navy cũng là 1:1, và
 *     viền của .btn vốn trong suốt nên nút không còn đường bao nào. Đây đúng
 *     là cơ chế đã làm .btn-primary tàng hình trong hero, chỉ khác con số
 *     (--navy trên --navy-dark là 1.23:1).
 *   - Logo: xem mục ngay dưới.
 *
 * KHÔNG đè `.vp-panel` và ruột của nó: panel mega/dropdown có nền --white
 * riêng, chữ trong đó vẫn nằm trên trắng. Vì vậy mọi bộ chọn dưới đây dùng bộ
 * nối CON TRỰC TIẾP `> li > a`, không dùng bộ nối con cháu — một
 * `.site-header:not(.scrolled) .nav-links a` sẽ bôi trắng cả link trong panel,
 * tức trắng trên trắng. Mockup đã vấp đúng chỗ này và phải vá lại ở cuối file
 * của nó.
 */
.site-header:not(.scrolled) .nav-links > li > a,
.site-header:not(.scrolled) .vp-nav-trigger{ color:var(--white); }

.site-header:not(.scrolled) .nav-links > li > a:hover,
.site-header:not(.scrolled) .nav-links > li > a[aria-current="page"],
.site-header:not(.scrolled) .vp-nav-trigger:hover,
.site-header:not(.scrolled) .vp-nav-trigger[aria-expanded="true"]{ color:var(--cobalt-light); }

.site-header:not(.scrolled) .vp-drawer-toggle{ color:var(--white); }

/*
 * Nút CTA trên nền navy. Công thức lấy nguyên của `.on-navy .btn-primary` ở
 * global.css mục 6, kể cả lý do: nền --cobalt-ink cho chữ trắng 5.37:1, còn
 * đường bao thì do viền --cobalt vẽ (3.67:1 so với --navy, đạt WCAG 1.4.11).
 *
 * `:hover` PHẢI viết rõ chứ không được bỏ trống. `.btn-primary:hover` ở
 * global.css là (0,2,0); bộ chọn dưới đây là (0,4,0) nên vẫn thắng — nhưng
 * global.css đã ghi lại đúng cái bẫy này một lần rồi, và viết rõ thì kết quả
 * không phụ thuộc vào việc ai đó có đổi độ ưu tiên ở đầu kia hay không.
 */
/*
 * Feedback khách đợt 1, STT 1: nút "Liên hệ báo giá" nền TRẮNG ở trạng thái
 * nghỉ (ảnh STT01_minhhoa_1.png khoanh đỏ đúng nút này) — thay cho nền
 * --cobalt-ink cũ. Hover giữ chữ navy, chỉ đổi nền sang --bg-alt (xanh rất
 * nhạt) để có phản hồi mà không cần thêm token mới. Khi ĐÃ CUỘN
 * (.site-header.scrolled) nút đổi sang nền --navy/chữ trắng — ghi tường minh
 * dù đây trùng giá trị mặc định của .btn-primary ở global.css, vì mặc định
 * đó có thể đổi mà không ai để ý ảnh hưởng tới header.
 */
.site-header:not(.scrolled) .vp-header-cta{
  background:var(--white);
  color:var(--navy);
  border-color:var(--white);
}

.site-header:not(.scrolled) .vp-header-cta:hover{
  background:var(--bg-alt);
  color:var(--navy);
  border-color:var(--bg-alt);
}

.site-header.scrolled .vp-header-cta,
.site-header.scrolled .vp-header-cta:hover{
  background:var(--navy);
  color:var(--white);
  border-color:var(--navy);
}

.nav{
  position:relative;
  display:flex;
  align-items:center;
  /*
   * gap:var(--sp-8) (96px, thay --gap-inline 12px cũ) — feedback khách đợt 1,
   * STT 1: đo thật ở 1280px trước sửa, gap logo->vp-nav = 293.6px (do
   * .vp-nav từng mang margin-left:auto, đã chuyển sang .nav-actions ở trên).
   * Ảnh site cũ (STT01_bangoc_3.png) cho khoảng logo->"TRANG CHỦ" ~116px trên
   * 1588px (~7.3%), quy đổi 1280px ~93px — --sp-8 (96px) là token gần nhất.
   * .nav-actions đã tự đẩy phải bằng margin-left:auto nên gap 96px ở đây
   * KHÔNG đẩy nút báo giá ra khỏi mép phải, chỉ cộng thêm khoảng tối thiểu.
   */
  gap:var(--sp-8);
  max-width:var(--wrap-max);
  margin:0 auto;
  padding:0 var(--wrap-x);
  min-height:76px;
}

.logo{
  display:inline-flex;
  align-items:center;
  flex:0 0 auto;
  text-decoration:none;
}

/*
 * SỬA — khách: "Logo tăng kích thước thêm 1/3". 40px × 4/3 = 53.3px, làm
 * tròn 53px. `.nav` vẫn min-height:76px (mục 1 ngay trên) — 53px logo còn dư
 * ~23px đệm dọc, không cần tăng chiều cao thanh header theo.
 */
.logo img{ display:block; max-height:53px; width:auto; }

.logo-text{
  font-family:var(--font-head);
  font-weight:800;
  font-size:20px;
  color:var(--navy);
}

/*
 * HỢP ĐỒNG B3 ĐÃ ĐỔI Ở ĐÂY — Phase D Task 2. Đọc trước khi sửa tiếp.
 *
 * B3 khai hai dòng:
 *     .logo-white{ display:none; }         (0,1,0) — chưa bao giờ có tác dụng
 *     .logo .logo-white{ display:none; }   (0,2,0) — dòng thật sự thắng
 * với lý do "header là nền trắng nên bản trắng phải ẩn". Lý do đó đã hết đúng
 * kể từ khi nền header lúc nghỉ là --navy: trên nền đó bản TRẮNG mới là bản
 * phải hiện, còn bản navy mới là bản phải ẩn.
 *
 * Hệ quả đo được của hợp đồng cũ: header in <img class="logo-img logo-white">
 * và (0,2,0) ẩn nó đi, trong khi logo_navy chưa có ảnh — nên header KHÔNG hiện
 * logo nào cả. Không test nào đỏ, vì cả hai rule đều có mặt và viết đúng.
 *
 * Cả hai dòng bị GỠ, không phải nới lỏng. Thay vào là hai rule theo TRẠNG THÁI:
 *
 *   .site-header:not(.scrolled) .logo .for-light  → (0,3,1)
 *   .site-header.scrolled       .logo .for-dark   → (0,3,1)
 *
 * Cả hai thắng `.logo img{display:block}` (0,1,1) bằng ĐỘ ƯU TIÊN, không nhờ
 * thứ tự nguồn. Chúng loại trừ nhau nên không bao giờ cùng khớp một phần tử.
 * Không cần rule `display:block` đối ứng: `.logo img` đã là nền, và thêm một
 * cặp rule nữa chỉ tạo thêm chỗ để hoà độ ưu tiên.
 *
 * `.for-dark` = dùng khi header NỀN TỐI (nghỉ) = bản logo TRẮNG.
 * `.for-light` = dùng khi header NỀN SÁNG (đã cuộn) = bản logo NAVY.
 * Tên gọi theo NỀN, không theo màu mực — lấy nguyên của mockup.
 *
 * MỘT BIẾN THỂ DUY NHẤT thì site-header.php in <img> KHÔNG mang for-dark/
 * for-light, nên không rule nào dưới đây khớp và `.logo img` cho nó hiện ở cả
 * hai trạng thái. Cố ý: một lỗ trống ở nửa số trạng thái tệ hơn một logo lệch
 * màu nền.
 *
 * CHÂN TRANG KHÔNG CÒN LIÊN QUAN. Đoạn này TRƯỚC ĐÂY ghi rằng
 * `.site-footer .footer-logo .logo-white` ở (0,3,0) là rule thắng cho display
 * của logo trắng trong chân trang. Câu đó NAY SAI: khách yêu cầu bỏ hẳn ảnh
 * logo khỏi chân trang (STT 12), site-footer.php không in `<img>` nữa, và rule
 * kia đã bị xoá — xem chú thích tại chỗ nó từng nằm, cuối file này.
 *
 * Ghi lại thay vì xoá sạch vì đây đúng loại chú thích nguy hiểm nhất: nó mô tả
 * một rule không còn tồn tại, và người đọc sau sẽ đi tìm rule đó. `.logo-white`
 * ngoài chân trang vẫn do mục 1 đặt display:none — phần ĐÓ không đổi, và hai
 * dòng ngay dưới đây vẫn là hợp đồng thật cho header.
 */
.site-header:not(.scrolled) .logo .for-light{ display:none; }
.site-header.scrolled .logo .for-dark{ display:none; }

/* margin-left:auto CHUYỂN sang .nav-actions (mục 1b) — xem giải thích ở .nav bên dưới. */
.vp-nav{ margin-left:0; }

/* ============================================================
   1b. Khối hành động bên phải header — ô tìm kiếm và nút CTA
   ============================================================
   Ô tìm kiếm nay là MỘT NÚT KÍNH LÚP MỞ RA POPUP, không còn là form luôn
   hiện. Lý do đổi nằm ở đầu template-parts/header/site-header.php và
   template-parts/header/search-popup.php.

   MÀU CỦA VỎ Ô TÌM KIẾM CỐ Ý KHÔNG ĐỔI THEO `.scrolled`. Nền vỏ là --white
   và viền là --cobalt ở cả hai trạng thái, vì --cobalt là màu duy nhất trong
   bộ token đạt ngưỡng 3:1 của WCAG 1.4.11 trên CẢ HAI nền:
       --cobalt #5C90F5 trên --navy  #001BB7 = 3.67:1
       --cobalt #5C90F5 trên --white #FFFFFF = 3.11:1
   Một cặp rule đổi theo trạng thái ở đây chỉ thêm chỗ để sai mà không mua được
   gì. Nút CTA thì KHÔNG có màu nào chung như vậy nên nó buộc phải đổi — xem
   mục 1.

   `.nav-search` KHÔNG còn `overflow:hidden`. Rule đó có từ thời ô nhập và nút
   gửi dính liền nhau bên trong vỏ; giữ lại thì nó CẮT CỤT popup, vì popup là
   con position:absolute của chính vỏ này. Đây là loại lỗi trông như "popup
   không mở" trong khi nó mở đúng và chỉ bị xén.

   VỀ Z-INDEX — ĐỌC MỤC 6 TRƯỚC KHI ĐỔI SỐ Ở ĐÂY.

   `.site-header` là position:sticky + z-index nên nó TẠO một ngữ cảnh xếp
   chồng. Popup và nền mờ của nó nằm trong đó, nên hai số dưới đây chỉ xếp thứ
   tự VỚI NHAU và với ngăn kéo — chúng KHÔNG so được với .vp-fab hay bất cứ
   thứ gì ngoài header. Dùng đúng thang của ngăn kéo:
       calc(var(--z-fab) + 2)   nền mờ
       calc(var(--z-fab) + 3)   vỏ

   Popup và ngăn kéo HOÀ nhau ở bậc +3, và điều đó không sao: nút kính lúp nằm
   trong `.nav-actions` (ẩn ở ≤900px) còn nút hamburger chỉ hiện ở ≤900px, nên
   không có viewport nào mở được cả hai. Nếu ngày nào `.nav-actions` hiện ở màn
   hẹp thì phải tách hai bậc ra — và phải đo lại bằng elementFromPoint chứ
   không phải bằng cách đổi số rồi đoán, xem mục 6.

   `.nav-search-item` KHAI `color` Ở CẤP CLASS, VÀ ĐÓ LÀ BẮT BUỘC.

   WordPress core in một stylesheet NỘI TUYẾN chứa
   `a:where(:not(.wp-element-button)){color:var(--wp--preset--color--navy)}`
   ở độ ưu tiên (0,0,1). tests/test-css-cascade.php KHÔNG đọc được stylesheet
   đó, nên nếu link kết quả chỉ dựa vào `body a:where(…)` của global.css thì
   bộ giải sẽ nói một đằng và trình duyệt làm một nẻo. `.nav-search-item` ở
   (0,1,0) thắng cả hai một cách chắc chắn, không phụ thuộc thứ tự nguồn.

   NỀN POPUP LÀ MÀU ĐẶC, KHÔNG PHẢI GRADIENT. Bộ giải chỉ đọc
   `background-color`; một nền gradient ở đây nghĩa là mọi con số tương phản
   của mục này nói về một trang không tồn tại.
   ============================================================ */

.nav-actions{
  display:flex;
  align-items:center;
  flex:0 0 auto;
  gap:var(--sp-3);
  /*
   * margin-left:auto (thay cho var(--sp-4) cũ) — feedback khách đợt 1, STT 1:
   * đẩy kính lúp + nút báo giá sát mép phải, để .nav's gap (mục logo-menu,
   * xem `.nav` ở trên) là khoảng CỐ ĐỊNH duy nhất chi phối vị trí menu ngay
   * sau logo, đúng như ảnh STT01_bangoc_3.png.
   */
  margin-left:auto;
}

/*
 * SỬA — khách: "bỏ button kính lúp thành icon, làm giống mẫu". Khung viền
 * --cobalt + nền --white cũ (đoạn dưới đây) khiến icon trông như một NÚT có
 * hộp, trong khi ảnh mẫu chỉ là một icon trần không viền/không nền. Bỏ hẳn
 * border/background của `.nav-search`.
 *
 * HỆ QUẢ CẦN BÙ: khung trắng cũ tồn tại không phải ngẫu nhiên — nó là cách
 * giữ icon LUÔN đọc được trên CẢ HAI nền header (navy lúc chưa cuộn, trắng
 * lúc đã cuộn) mà không cần đổi màu icon theo trạng thái (--cobalt là màu
 * DUY NHẤT đạt 3:1 trên cả hai nền, xem docblock gốc đã xoá). Bỏ khung thì
 * phải đổi màu icon theo `.scrolled` để bù lại — ĐÚNG khuôn `.nav-links a`/
 * `.logo`/`.btn-primary` đã dùng ngay phía trên (mục 1), không phải một cơ
 * chế mới.
 *
 * Đo lại (không đoán): trắng #FFFFFF trên --navy #001BB7 = 11.41:1;
 * --cobalt-ink #3366CC trên --white #FFFFFF = 5.36:1. Cả hai vượt xa 3:1
 * của WCAG 1.4.11 cho biên dạng thành phần giao diện — nên chỗ tương phản
 * mất đi từ việc bỏ khung, MÀU ICON tự nó bù đủ, không cần khung.
 */
.nav-search{
  position:relative;
  display:flex;
  align-items:center;
}

/* --- Nút kính lúp -------------------------------------------------- */

.nav-search-toggle{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:38px;
  height:38px;
  padding:0;
  border:0;
  border-radius:var(--r-sm);
  background:none;
  color:var(--cobalt-ink);
  cursor:pointer;
  transition:color var(--dur-micro) ease;
}

/* Nền navy lúc chưa cuộn — icon trắng, cùng lý lẽ .nav-links a/.logo ở mục 1. */
.site-header:not(.scrolled) .nav-search-toggle{ color:var(--white); }

/*
 * KHÔNG còn :hover đổi nền — feedback khách đợt 1, STT 1: "kính lúp tìm
 * kiếm không để button có hiệu ứng blur khi rê chuột vào". Vẫn giữ
 * :focus-visible đổi nền, vì bỏ luôn dấu hiệu focus là chặn người dùng
 * điều hướng bằng bàn phím — khác hẳn việc chỉ bỏ hover chuột.
 */
.nav-search-toggle:focus-visible{ background:var(--bg-alt); }

/* --- Nền mờ -------------------------------------------------------- */

/*
 * KHÔNG khai `display` ở rule trần — luật `hidden` ở đầu file. UA có sẵn
 * `[hidden]{display:none}` và một `display:block` ở đây làm nền mờ phủ kín
 * trang mọi lúc.
 */
.nav-search-backdrop{
  position:fixed;
  inset:0;
  z-index:calc(var(--z-fab) + 2);
  background:rgba(0, 27, 183, .35);
}

.nav-search-backdrop:not([hidden]){ display:block; }

/* --- Vỏ popup ------------------------------------------------------ */

/*
 * `visibility:hidden`, không phải `opacity:0` một mình. Một vùng
 * aria-hidden="true" mà vẫn Tab vào được là lỗi a11y thật; opacity và
 * transform không lấy phần tử ra khỏi thứ tự tab, chỉ visibility và display
 * mới làm được. Cùng lý lẽ đã ghi cho ngăn kéo ở mục 7.
 *
 * CSS bám vào `aria-hidden` của CHÍNH vỏ chứ không bám vào nút: nút và vỏ là
 * anh em qua một nền mờ nên bộ nối `+` không tới được, và quan trọng hơn —
 * nếu JS quên đảo `aria-hidden` thì popup KHÔNG mở, tức lỗi lộ ra ngay.
 */
/*
 * CANH GIỮA MÀN HÌNH, không treo dưới nút kính lúp.
 *
 * Trước 03/09/2026: `position:absolute; top:100%; right:0` — popup dính vào
 * nút, nên nó luôn nằm sát mép trên bên phải. Khách đang đọc giữa trang mà mở
 * tìm kiếm thì mắt phải nhảy lên góc trên. Nay là hộp thoại canh giữa cả hai
 * chiều, đúng chỗ mắt đang nhìn.
 *
 * `position:fixed` BÁM VIEWPORT được ở đây vì `.site-header` chỉ có
 * position:sticky + z-index. Sticky KHÔNG tạo containing block cho phần tử
 * fixed — chỉ transform/filter/perspective/will-change/contain mới tạo. Nếu
 * ngày nào header có thêm một trong số đó thì popup sẽ bám header thay vì
 * viewport và rơi trở lại đúng lỗi này.
 *
 * Ngữ cảnh xếp chồng thì KHÔNG đổi: popup vẫn nằm trong ngữ cảnh của header,
 * nên hai số z-index vẫn theo đúng thang đã ghi ở mục 6.
 */
.nav-search-pop{
  position:fixed;
  /*
   * top:36% (SUA LAN 2, 03/09/2026: khach xem lai anh chup, van thay popup
   * o thap, yeu cau "cho len top 1 chut" tiep). Lan dau da doi 50% -> 42%;
   * 42% van con thap so voi anh khach gui lan nay (input nam duoi phan giua
   * man hinh, gan sat khoi filter/danh sach san pham). Doi tiep xuong 36%.
   *
   * VAN GIU BEN DUOI header (~64-90px o mobile): voi chieu cao popup ~80px
   * va cong thuc can giua quanh diem top (xem transform ngay duoi), mep tren
   * thuc te o 36% cua 812px la ~252px — con cach header hon 160px, khong
   * dung cham.
   */
  top:36%;
  left:50%;
  z-index:calc(var(--z-fab) + 3);
  width:min(420px, 92vw);
  padding:var(--sp-4);
  border-radius:var(--r-card);
  background:var(--white);
  box-shadow:var(--sh-3);
  visibility:hidden;
  opacity:0;
  /* Canh giữa + trượt lên 6px lúc đóng; hai phép dịch phải đi CÙNG một
     thuộc tính transform, không tách được. */
  transform:translate(-50%, calc(-50% - 6px)); /* -50% la CAN GIUA QUANH diem top:42%, khong phai giua man hinh */
  /*
   * `visibility 0s linear var(--dur-state)` — ĐỘ TRỄ, KHÔNG PHẢI THỜI LƯỢNG.
   * Và nó là bản vá cho một lỗi ĐÃ ĐO ĐƯỢC, không phải một mẹo chép về.
   *
   * Bản đầu viết `visibility var(--dur-state) var(--ease)`, tức cho visibility
   * chạy qua transition ở CẢ HAI chiều. Đo trên trình duyệt thật: ngay sau
   * khi JS đặt aria-hidden="false", getComputedStyle(pop).visibility vẫn trả
   * về `hidden` — hàm timing ở thời điểm 0 cho ra 0, và với thuộc tính rời
   * rạc thì 0 ánh xạ về giá trị đầu. Hệ quả: lời gọi focus() đồng bộ ngay
   * sau đó bị trình duyệt TỪ CHỐI vì phần tử đang visibility:hidden, và
   * activeElement rơi về <body>. Người dùng bàn phím mở popup rồi mất dấu
   * tiêu điểm, và bẫy Tab không có gì để giam.
   *
   * Cách viết này tách hai chiều ra:
   *   - MỞ (rule [aria-hidden="false"] bên dưới): độ trễ 0 → visibility đổi
   *     tức thì → focus() ăn ngay.
   *   - ĐÓNG (rule này): độ trễ đúng bằng thời lượng → phần tử ở lại nhìn
   *     thấy được cho tới khi opacity mờ hết, rồi mới thật sự biến mất.
   *
   * KHÔNG bỏ hẳn visibility khỏi transition: bỏ thì popup tắt phụt lúc đóng,
   * và opacity mờ dần trở thành đồ trang trí không ai thấy.
   */
  transition:opacity var(--dur-state) var(--ease),
             transform var(--dur-state) var(--ease),
             visibility 0s linear var(--dur-state);
}

.nav-search-pop[aria-hidden="false"]{
  visibility:visible;
  opacity:1;
  transform:translate(-50%, -50%);
  transition:opacity var(--dur-state) var(--ease),
             transform var(--dur-state) var(--ease),
             visibility 0s linear 0s;
}

.nav-search-row{
  display:flex;
  align-items:stretch;
  gap:var(--sp-2);
}

.nav-search-label{ display:block; flex:1 1 auto; }

.nav-search-input{
  width:100%;
  padding:9px var(--sp-3);
  border-style:solid;
  border-width:1px;
  border-color:var(--border);
  border-radius:var(--r-input);
  background:var(--white);
  font-family:var(--font-body);
  font-size:14px;
  line-height:1.2;
  color:var(--text);
}

.nav-search-go{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:38px;
  flex:0 0 auto;
  padding:0;
  border:0;
  border-radius:var(--r-input);
  background:var(--cobalt-ink);
  color:var(--white);
  cursor:pointer;
}

.nav-search-go:hover{ background:var(--navy); }

.nav-search-close{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:38px;
  flex:0 0 auto;
  padding:0;
  border:0;
  border-radius:var(--r-input);
  background:none;
  color:var(--text);
  cursor:pointer;
}

.nav-search-close:hover{ background:var(--bg-alt); color:var(--navy); }

/* --- Vùng kết quả -------------------------------------------------- */

.nav-search-results{ max-height:min(60vh, 420px); overflow-y:auto; }

/*
 * Không có kết quả thì KHÔNG chừa khoảng trắng. `:empty` là trạng thái duy
 * nhất đúng ở đây: lúc chưa gõ gì, và ở trình duyệt không có JS — nơi vùng
 * này vĩnh viễn rỗng.
 */
.nav-search-results:empty{ margin:0; }
.nav-search-results:not(:empty){ margin-top:var(--sp-4); }

.nav-search-group + .nav-search-group{ margin-top:var(--sp-4); }

.nav-search-group-title{
  margin:0 0 var(--sp-2);
  font-family:var(--font-head);
  font-weight:800;
  font-size:12px;
  letter-spacing:.06em;
  text-transform:uppercase;
  /* #6B7280 trên --white = 4.83:1 — đạt AA cho chữ nhỏ. */
  color:var(--text-muted);
}

.nav-search-list{ margin:0; padding:0; list-style:none; }
.nav-search-list > li{ margin:0; }

.nav-search-item{
  display:flex;
  align-items:center;
  gap:var(--sp-3);
  padding:var(--sp-2) var(--sp-2);
  border-radius:var(--r-sm);
  font-size:14.5px;
  /* Xem ghi chú về stylesheet nội tuyến của core ở đầu mục. */
  color:var(--text);
}

.nav-search-item:hover{ background:var(--bg-alt); color:var(--navy); }

.nav-search-thumb{
  flex:0 0 auto;
  width:40px;
  height:40px;
  border-radius:var(--r-sm);
  background:var(--img-bg);
  object-fit:cover;
}

.nav-search-title{ min-width:0; }

.nav-search-empty{
  margin:0;
  font-size:14px;
  color:var(--text-muted);
}

/* --- Không có JS thì popup KHÔNG được biến mất --------------------- */

/*
 * `html.vp-js` do một dòng script nội tuyến ở <head> đặt (inc/enqueue.php).
 * Nó KHÔNG phải class trạng thái song song với aria-expanded: nó không bao
 * giờ đổi giá trị và không nói gì về đóng/mở.
 *
 * Không có nó — tức trình duyệt không chạy JS — thì popup nằm NGAY TRONG HÀNG
 * như một ô tìm kiếm thường trực, và nút kính lúp (thứ chỉ có tác dụng khi có
 * JS) biến đi. Người dùng gõ và bấm Enter, ra đúng trang kết quả của WordPress.
 *
 * Nếu bỏ khối này thì lời hứa "không JS vẫn tìm được" chỉ còn đúng trên giấy:
 * <form> vẫn nằm trong HTML nhưng ô nhập bị visibility:hidden và không có gì
 * mở nó ra.
 *
 * Độ ưu tiên: `html:not(.vp-js) .nav-search-pop` là (0,2,1), thắng cả
 * `.nav-search-pop` (0,1,0) lẫn `.nav-search-pop[aria-hidden="false"]` (0,2,0)
 * — không phụ thuộc thứ tự nguồn.
 */
html:not(.vp-js) .nav-search-toggle{ display:none; }
html:not(.vp-js) .nav-search-close{ display:none; }

html:not(.vp-js) .nav-search-pop{
  position:static;
  width:auto;
  padding:0;
  border-radius:0;
  background:none;
  box-shadow:none;
  visibility:visible;
  opacity:1;
  transform:none;
}

html:not(.vp-js) .nav-search-input{ width:150px; max-width:26vw; border-width:0; }

/* ============================================================
   2. Menu chính
   ============================================================ */

.nav-links{
  display:flex;
  align-items:center;
  gap:var(--sp-5);
  margin:0;
  padding:0;
  list-style:none;
}

/*
 * Mục mega dùng `position:static` để panel toàn chiều rộng bám vào .nav;
 * mục dropdown dùng `position:relative` để panel hẹp bám vào chính nó. Hai
 * chế độ định vị khác nhau, nên KHÔNG đặt position chung lên `.nav-links > li`.
 */
.nav-links > li{ margin:0; }
.vp-nav-item--mega{ position:static; }
.vp-nav-item--dropdown{ position:relative; }

/*
 * text-transform:uppercase + letter-spacing — feedback khách đợt 1, STT 1:
 * "chữ viết hoa", đã chốt VIẾT HOA TOÀN BỘ menu. Làm ở CSS (lớp trình bày),
 * KHÔNG sửa nhãn menu trong CSDL — nội dung gốc "Trang chủ" vẫn giữ nguyên,
 * chỉ hiển thị hoa. letter-spacing nhẹ vì chữ hoa liền nhau (mặc định 0) khó
 * đọc hơn chữ thường ở cùng cỡ.
 */
.nav-links > li > a,
.vp-nav-trigger{
  display:inline-flex;
  align-items:center;
  gap:6px;
  padding:10px 0;
  border:0;
  background:none;
  font-family:var(--font-body);
  font-weight:600;
  font-size:15px;
  line-height:1.2;
  color:var(--text);
  text-decoration:none;
  text-transform:uppercase;
  letter-spacing:.02em;
  cursor:pointer;
}

.nav-links > li > a:hover,
.vp-nav-trigger:hover,
.vp-nav-trigger[aria-expanded="true"]{ color:var(--navy); }

.nav-links > li > a[aria-current="page"]{ color:var(--navy); }

.caret{
  display:inline-flex;
  transition:transform var(--dur-micro) var(--ease);
}

.vp-nav-trigger[aria-expanded="true"] .caret{ transform:rotate(180deg); }

/* ============================================================
   3. Panel mega và dropdown
   ============================================================
   Trạng thái nằm trên NÚT (`aria-expanded`), còn thứ đổi hình là PANEL bên
   cạnh — nên bộ nối anh em `+` là bắt buộc. Hợp đồng Task 3 cấm đặt `hidden`
   lên .vp-panel, và ở đây cũng cố ý KHÔNG dùng `display:none`: display giết
   transition, và tệ hơn, nó làm rule mở phía dưới nói "visible" trong khi
   mắt không thấy gì.
   ============================================================ */

.vp-panel{
  position:absolute;
  top:100%;
  z-index:var(--z-mega);
  margin:0;
  padding:0;
  list-style:none;
  background:var(--white);
  box-shadow:var(--sh-3);
  opacity:0;
  visibility:hidden;
  transform:translateY(-8px);
  transition:opacity var(--dur-state) var(--ease),
             transform var(--dur-state) var(--ease),
             visibility var(--dur-state) var(--ease);
}

.vp-nav-trigger[aria-expanded="true"] + .vp-panel{
  opacity:1;
  visibility:visible;
  transform:translateY(0);
}

.vp-mega-panel{
  left:var(--wrap-x);
  right:var(--wrap-x);
  border-radius:0 0 var(--r-lg) var(--r-lg);
  border-top:1px solid var(--border);
}

.vp-dd-panel{
  left:0;
  z-index:var(--z-dd);
  min-width:240px;
  padding:var(--sp-2) 0;
  border-radius:var(--r-sm);
}

.vp-dd-panel > li{ margin:0; }

.vp-dd-panel a{
  display:block;
  padding:10px var(--sp-4);
  font-size:14.5px;
  color:var(--text);
  text-decoration:none;
}

.vp-dd-panel a:hover{ background:var(--bg-alt); color:var(--navy); }

/* ============================================================
   4. Ruột panel mega — tablist dọc + lưới danh mục
   ============================================================ */

.mega-inner{
  display:grid;
  grid-template-columns:240px 1fr;
  gap:var(--sp-5);
  padding:var(--sp-5);
}

.mega-sidebar{
  display:flex;
  flex-direction:column;
  gap:2px;
  border-right:1px solid var(--border);
  padding-right:var(--sp-4);
}

/*
 * CỘT TRÁI — TRẠNG THÁI CHỌN PHẢI ĐỌC ĐƯỢC, KHÔNG PHẢI "ĐẬM HƠN MỘT CHÚT".
 *
 * Bản trước dùng `background:var(--bg-alt)` cho CẢ `:hover` lẫn
 * `[aria-selected="true"]`. Hai trạng thái khác nhau về ý nghĩa mà giống hệt
 * nhau về mắt: rê chuột qua ba tab là ba tab cùng sáng lên như nhau, và người
 * dùng không còn cách nào biết lưới đang hiện thuộc tab nào. Mockup
 * (~/vp-mockups/trang-chu.html, `.mega-cat.active`) dùng NỀN ĐẶC --navy với
 * chữ trắng. Số đo: --white trên --navy = 11.41:1; hover --navy trên
 * --cobalt-light = 10.00:1; lúc nghỉ --text trên --white = 17.40:1.
 *
 * `--cobalt-light` chứ không phải `--bg-alt` dù hôm nay hai token bằng nhau
 * (`--cobalt-light: var(--bg-alt)`): mockup gọi màu này là "cobalt nhạt" ở
 * đúng ngữ cảnh nhấn tương tác, còn --bg-alt là nền khối chung. Ngày ai đó
 * tách hai token, chỗ này phải đi theo cái đúng nghĩa.
 */
.mega-cat{
  display:flex;
  align-items:center;
  width:100%;
  border:0;
  background:none;
  border-radius:var(--r-btn);
  padding:11px var(--sp-3);
  font-family:var(--font-body);
  font-weight:600;
  font-size:14.5px;
  text-align:left;
  color:var(--text);
  cursor:pointer;
  transition:background var(--dur-micro) var(--ease),
             color var(--dur-micro) var(--ease);
}

.mega-cat:hover{ background:var(--cobalt-light); color:var(--navy); }

/*
 * THỨ TỰ HAI DÒNG NÀY LÀ MỘT QUYẾT ĐỊNH, KHÔNG PHẢI NGẪU NHIÊN.
 *
 * `.mega-cat:hover` và `.mega-cat[aria-selected="true"]` đều là (0,2,0) — hoà
 * độ ưu tiên, nên THỨ TỰ NGUỒN quyết. Đảo hai dòng thì rê chuột lên tab đang
 * chọn sẽ làm nó NHẠT ĐI, và người dùng đọc ra "vừa bỏ chọn". Chốt bằng khẳng
 * định ở tests/test-css-cascade.php mục 13.1, hồ sơ "đang chọn VÀ đang hover".
 */
.mega-cat[aria-selected="true"]{ background:var(--navy); color:var(--white); }

/*
 * RULE `.mega-cat-row` / `.mega-cat-goto` ĐÃ XOÁ — SỬA LẦN 3 (feedback
 * khách, ngay sau lần 2). Icon mũi tên riêng cho "đi thẳng tới trang" bị bỏ,
 * đổi sang RÊ CHUỘT (hover/focus) trên chính `.mega-cat` để xem trước — click
 * giờ điều hướng thẳng, không cần một phần tử thứ hai nữa. Markup sinh ra
 * hai class này đã gỡ khỏi mega-menu.php — giữ rule lại là để CSS chết trong
 * repo, đúng loại lỗi mà `.footer-more`/`.footer-legal`/`.mega-subitem--all`
 * đã dọn trước đó.
 */

/*
 * `:not([hidden])` chứ không phải `.mega-subgrid` trần.
 *
 * Hợp đồng Task 3: tabpanel không được chọn mang thuộc tính `hidden`, và
 * trạng thái nhìn thấy phải khớp với `aria-selected`. Một dòng
 * `.mega-subgrid{display:grid}` sẽ đè `[hidden]{display:none}` của UA và
 * làm MỌI tabpanel hiện cùng lúc, trong khi aria-selected vẫn nói chỉ có
 * một cái đang chọn. CSS không được phép gỡ `hidden`.
 */
.mega-subgrid:not([hidden]){
  display:grid;
  grid-template-columns:repeat(3, 1fr);
  gap:var(--sp-3);
}

/*
 * DANH MỤC CON LÀ MỘT THẺ CÓ VIỀN, KHÔNG PHẢI MỘT DÒNG DANH SÁCH.
 *
 * Viền là thứ tạo ra lưới trong mockup; bỏ viền đi thì sáu mục trôi thành một
 * khối chữ và ô icon bên trái mất chỗ dựa. Hover đổi MÀU VIỀN sang --cobalt
 * (3.02:1 trên trắng, đạt WCAG 1.4.11 cho đường bao thành phần) chứ không chỉ
 * đổi nền: đổi nền một mình thì trên màn hình kém tương phản gần như không
 * thấy gì.
 */
.mega-subitem{
  display:flex;
  align-items:center;
  gap:var(--sp-3);
  padding:12px var(--sp-3);
  border:1px solid var(--border);
  border-radius:var(--r-sm);
  font-size:13.5px;
  font-weight:600;
  color:var(--text);
  text-decoration:none;
  transition:border-color var(--dur-micro) var(--ease),
             background var(--dur-micro) var(--ease),
             color var(--dur-micro) var(--ease);
}

.mega-subitem:hover{ border-color:var(--cobalt); background:var(--cobalt-light); color:var(--navy); }

/*
 * Ô ICON. Mockup vẽ một hộp bo góc 34×34 nền --cobalt-light; bản trước chỉ có
 * một chấm tròn rỗng, và đó là chỗ khách chụp lại đầu tiên.
 *
 * `flex-shrink:0` là bắt buộc chứ không phải trang trí: tên danh mục dài
 * ("Túi nilon tự hủy sinh học") sẽ bóp ô icon thành một vệt dọc nếu thiếu nó.
 */
.sub-ico{
  display:flex;
  align-items:center;
  justify-content:center;
  flex-shrink:0;
  /* 34 -> 40px (feedback khách 03/09/2026: icon nhìn hơi nhỏ). */
  width:40px;
  height:40px;
  border-radius:var(--r-btn);
  background:var(--cobalt-light);
  color:var(--navy);
}

/*
 * RULE `.mega-subitem--all` / `.mega-subitem--all .sub-ico` ĐÃ XOÁ —
 * feedback khách: "bỏ phần này [mục 'Xem tất cả'], chỉ hiện thị danh mục
 * con". Mục đó từng là đường vào danh mục CHA (vì cột trái chỉ là NÚT, xem
 * docblock đầu template-parts/header/mega-menu.php), nay cột trái tự nó đã
 * là liên kết nên không còn cần đường vòng đó nữa. Markup sinh ra hai class
 * này đã gỡ khỏi mega-menu.php — giữ rule lại là để CSS chết trong repo,
 * đúng loại lỗi mà `.footer-more`/`.footer-legal` đã dọn trước đó.
 */

/*
 * KHỐI SẢN PHẨM TIÊU BIỂU. Đường kẻ trên là thứ tách nó khỏi lưới danh mục;
 * thiếu nó thì hai khối dính vào nhau và hàng ảnh đọc ra như hàng danh mục
 * thứ tư.
 */
.mega-bestseller{
  margin-top:var(--sp-5);
  padding-top:var(--sp-4);
  border-top:1px solid var(--border);
}

.mega-bestseller-head{
  display:flex;
  align-items:center;
  justify-content:space-between;
  margin-bottom:var(--sp-3);
}

.mega-bestseller-head h4{ font-family:var(--font-head); font-size:14.5px; color:var(--navy); }

/*
 * LIÊN KẾT "Xem tất cả →" PHẢI CÓ RULE MÀU CẤP CLASS.
 *
 * Không phải để đẹp. WordPress core in một stylesheet NỘI TUYẾN
 * `a:where(:not(.wp-element-button)){color:var(--wp--preset--color--navy)}` ở
 * (0,0,1); global.css chặn nó bằng `body a:where(...)` ở (0,0,2) với
 * `color:inherit`. Nghĩa là một link không có rule riêng ở đây sẽ KẾ THỪA màu
 * của khối cha — hôm nay là --text, tức không còn dấu hiệu nào cho biết nó là
 * link. Bộ giải cascade của bộ test KHÔNG đọc được stylesheet nội tuyến đó,
 * nên chỗ này đã được kiểm thêm trên trình duyệt thật.
 *
 * --cobalt-ink chứ không phải --cobalt của mockup: --cobalt trên nền trắng chỉ
 * đạt 3.02:1, trượt AA cho chữ 12.5px. --cobalt-ink đạt 5.37:1.
 */
.mega-bestseller-head a{
  font-size:12.5px;
  font-weight:700;
  color:var(--cobalt-ink);
  text-decoration:none;
}

.mega-products{ display:grid; grid-template-columns:repeat(5, 1fr); gap:var(--gap-card); }

/*
 * THẺ SẢN PHẨM: BO GÓC THUỘC VỀ HỘP CẮT, KHÔNG THUỘC THẺ NGOÀI.
 *
 * Cùng bài học với ô bento (commit 10736a3): `.mega-product` KHÔNG mang
 * border-radius. Hộp bo góc và hộp cắt phải là CÙNG MỘT hộp, và hộp cắt ở đây
 * là `.mp-img` — nơi có `overflow:hidden`. Đặt bo góc lên thẻ ngoài rồi cắt ở
 * trong là công thức cho ảnh góc VUÔNG nằm trong một thẻ BO TRÒN.
 */
.mega-product{ display:flex; flex-direction:column; text-decoration:none; color:var(--text); }

/*
 * `aspect-ratio` giữ chiều cao ô ổn định TRƯỚC KHI ảnh tải xong và kể cả khi
 * sản phẩm chưa có ảnh nào. Thiếu nó thì hàng năm ô cao 0px lúc chưa có ảnh —
 * đúng cái mà khách gọi là "danh sách trống".
 */
/*
 * SỬA 05/09/2026 (khách: "chỉnh lại nhóm khoanh đỏ cho đẹp hơn"). ĐO TRƯỚC KHI
 * SỬA: năm khung đều nhau 142x127 nhưng ảnh để `object-fit:cover` — ảnh sản
 * phẩm là bao bì chụp trên nền trắng, tỉ lệ và khoảng đệm mỗi tấm một khác, nên
 * cover phóng theo cạnh ngắn rồi CẮT: gói đứng mất phần trên-dưới, gói ngang
 * mất hai bên, và sản phẩm hiện to nhỏ không đều dù khung bằng nhau — đúng cảm
 * giác lệch mà khách khoanh đỏ.
 *
 * Khung VUÔNG + ảnh `contain` + đệm 10px: mỗi sản phẩm hiện trọn vẹn trong cùng
 * một hộp, năm ô đọc thành một hàng đều. Nền trắng + viền --border thay nền xám
 * đặc: panel mega vốn nền trắng, năm hộp xám làm dải này nặng hơn cả phần danh
 * mục ở trên; viền mảnh đủ tách hộp mà vẫn nhẹ.
 */
.mp-img{
  display:block;
  position:relative;
  width:100%;
  aspect-ratio:1 / 1;
  margin-bottom:var(--sp-2);
  border:1px solid var(--border);
  border-radius:var(--r-sm);
  overflow:hidden;
  background:#fff;
  transition:border-color .2s ease;
}

/*
 * `height:100%` PHẢI khai tường minh. global.css có
 * `:where(img, svg, video, canvas, iframe){ height:auto }` ở (0,0,0); ảnh 3:2
 * trong một ô 1:0.9 sẽ tự cao theo tỉ lệ của nó và để lại một dải nền --img-bg
 * dưới đáy, hoặc thò ra ngoài. `object-fit:cover` là thứ cắt phần thừa.
 */
.mp-img img{ display:block; width:100%; height:100%; max-width:100%; padding:10px; object-fit:contain; }

.mega-product:hover .mp-img{ border-color:var(--cobalt); }

/*
 * DẠNG DỰ PHÒNG. Cùng khuôn `.bt-media-fallback` của lưới bento: hình vẽ
 * aria-hidden phủ kín ô, không phải khoảng trắng. Màu --cobalt-ink chứ không
 * phải --cobalt: trên nền --img-bg thì --cobalt chỉ đạt 2.87:1, trượt ngưỡng
 * 3:1 của WCAG 1.4.11; --cobalt-ink đạt 4.96:1.
 */
.mp-img-fallback{
  position:absolute;
  inset:0;
  display:flex;
  align-items:center;
  justify-content:center;
  color:var(--cobalt-ink);
}

/*
 * <h5> tên sản phẩm. global.css khai `h1, h2, h3, h4, h5{ color:var(--navy);
 * font-weight:800; font-family:var(--font-head) }` THẲNG lên thẻ ở (0,0,1), và
 * khai báo trực tiếp thắng kế thừa bất kể độ ưu tiên. Không có rule cấp class
 * thì tên sản phẩm ra navy đậm cỡ tiêu đề, nằm ngay trên dòng "Liên hệ" cũng
 * navy — hai dòng cùng màu cùng độ đậm, không còn thứ bậc nào. Đúng cơ chế đã
 * làm tiêu đề cột chân trang gần như vô hình.
 *
 * `min-height` giữ đáy các thẻ thẳng hàng khi tên dài ngắn khác nhau; mockup
 * đặt 34px, đủ cho hai dòng ở cỡ 12.5px.
 */
.mega-product h5{
  font-family:var(--font-body);
  font-size:12.5px;
  font-weight:600;
  line-height:1.4;
  min-height:34px;
  margin-bottom:var(--sp-1);
  color:var(--text);
  /* Tên dài tràn 3 dòng thì năm ô lệch chân nhau — kẹp 2 dòng, phần thừa cắt bằng "…". */
  display:-webkit-box;
  -webkit-line-clamp:2;
  -webkit-box-orient:vertical;
  overflow:hidden;
}

.mega-product:hover h5{ color:var(--cobalt-ink); }

.mp-contact{ display:block; font-size:12px; font-weight:700; color:var(--navy); }
.vp-mega-empty{ padding:var(--sp-5); color:var(--text-muted); }

/* ============================================================
   5. Nút hamburger
   ============================================================
   Ẩn ở desktop, hiện từ mốc 900px trở xuống. Trước Task 5 không có dòng nào
   ẩn nó, nên nút hiện ngay cạnh menu đầy đủ trên màn rộng.
   ============================================================ */

.vp-drawer-toggle{
  display:none;
  align-items:center;
  justify-content:center;
  width:44px;
  height:44px;
  margin-left:auto;
  padding:0;
  border:0;
  border-radius:var(--r-sm);
  background:none;
  color:var(--navy);
  cursor:pointer;
}

.vp-burger{
  display:flex;
  flex-direction:column;
  justify-content:center;
  gap:5px;
  width:22px;
  height:16px;
}

.vp-burger > span{
  display:block;
  height:2px;
  width:100%;
  border-radius:2px;
  background:currentColor;
  transition:transform var(--dur-micro) var(--ease),
             opacity var(--dur-micro) var(--ease);
}

/* Ba gạch thành chữ X. Trạng thái vẫn đọc từ aria-expanded, không có class. */
.vp-drawer-toggle[aria-expanded="true"] .vp-burger > span:nth-child(1){ transform:translateY(7px) rotate(45deg); }
.vp-drawer-toggle[aria-expanded="true"] .vp-burger > span:nth-child(2){ opacity:0; }
.vp-drawer-toggle[aria-expanded="true"] .vp-burger > span:nth-child(3){ transform:translateY(-7px) rotate(-45deg); }

/* ============================================================
   6. Nền mờ
   ============================================================
   KHÔNG khai `display` ở rule trần. Xem luật về `hidden` ở đầu file.

   THỨ TỰ VẼ Ở ĐÂY KHÔNG DO SỐ z-index QUYẾT ĐỊNH — DO NGỮ CẢNH XẾP CHỒNG.

   Đây là chỗ bản sửa đầu tiên đã sai, và sai theo đúng kiểu mà cả bộ test
   này sinh ra để chống: đổi số rồi tưởng là xong.

   TRIỆU CHỨNG: ở 375px, thanh nút liên hệ nổi vẽ ĐÈ lên ngăn kéo đang mở.
   Đo trên trình duyệt thật: ngăn kéo và thanh FAB chồng nhau 60px đáy, và
   elementFromPoint() trong vùng đó trả về phần tử của .vp-fab.

   CHẨN ĐOÁN SAI LẦN ĐẦU: "hai bên hoà z-index, nâng ngăn kéo lên là xong."
   Nâng ngăn kéo lên 103 và nền mờ lên 102 — ĐO LẠI: KHÔNG ĐỔI GÌ. 630/630
   điểm trong hình chữ nhật thanh FAB vẫn trả về phần tử FAB.

   NGUYÊN NHÂN THẬT, đo bằng cách duyệt chuỗi tổ tiên:

     .vp-drawer và .vp-drawer-backdrop nằm TRONG header.site-header, mà
     .site-header là position:sticky + z-index:var(--z-header) → nó TẠO MỘT
     NGỮ CẢNH XẾP CHỒNG. Mọi z-index bên trong chỉ xếp thứ tự với nhau; so
     với thế giới bên ngoài thì cả cụm mang đúng MỘT giá trị: 50.

     .vp-fab là con trực tiếp của <body> → ngữ cảnh GỐC.

     Phép so thật sự quyết định là .site-header (50) với .vp-fab (100).
     100 > 50, nên FAB ở trên — bất kể ngăn kéo mang số nào. Cho ngăn kéo
     z-index 9999 cũng vô hiệu.

   BẢN SỬA: hạ .vp-fab xuống DƯỚI .site-header. Xem mục 12.

   THANG SAU KHI SỬA — hai tầng, và phải đọc theo hai tầng:

     Ngữ cảnh GỐC (thứ tự này mới quyết định việc vẽ):
       calc(var(--z-header) - 1) = 49    .vp-fab
       var(--z-header)           = 50    .site-header  ← chứa ngăn kéo + nền mờ
       calc(var(--z-fab) + 1)    = 101   .skip-link    (global.css mục 10)

     BÊN TRONG .site-header (chỉ có ý nghĩa tương đối với nhau):
       calc(var(--z-fab) + 2)    .vp-drawer-backdrop
       calc(var(--z-fab) + 3)    .vp-drawer

   Hai bậc trong header vẫn dẫn xuất từ --z-fab và vẫn KHÔNG hoà nhau: nền
   mờ phải nằm dưới ngăn kéo, và hoà thì thứ tự DOM phân giải.
   ============================================================ */

.vp-drawer-backdrop{
  position:fixed;
  inset:0;
  z-index:calc(var(--z-fab) + 2);
  background:rgba(0, 27, 183, .35);
}

.vp-drawer-backdrop:not([hidden]){ display:block; }

/* ============================================================
   7. Ngăn kéo
   ============================================================
   HỢP ĐỒNG TASK 4: `aria-expanded` trên nút là nguồn sự thật, và
   `aria-hidden` trên vỏ đi kèm nó. CSS ở đây bám vào `aria-hidden` của
   CHÍNH vỏ chứ không bám vào nút, vì hai lý do: vỏ và nút là anh em qua một
   nền mờ nên bộ nối `+` không tới được, và quan trọng hơn — nếu JS quên đảo
   `aria-hidden` thì ngăn kéo sẽ KHÔNG mở, tức lỗi lộ ra ngay thay vì để lại
   một vùng nhìn thấy được mà trình đọc màn hình bị cấm đọc.

   `visibility:hidden`, không phải `opacity:0`. Một vùng aria-hidden mà vẫn
   Tab vào được là lỗi a11y thật; opacity và transform không lấy phần tử ra
   khỏi thứ tự tab, chỉ visibility và display mới làm được.
   ============================================================ */

.vp-drawer{
  position:fixed;
  top:0;
  right:0;
  bottom:0;
  z-index:calc(var(--z-fab) + 3);
  display:flex;
  flex-direction:column;
  width:min(360px, 88vw);
  max-width:100%;
  background:var(--white);
  box-shadow:var(--sh-4);
  overflow-y:auto;
  overscroll-behavior:contain;
  visibility:hidden;
  transform:translateX(100%);
  transition:transform var(--dur-state) var(--ease),
             visibility var(--dur-state) var(--ease);
}

.vp-drawer[aria-hidden="false"]{
  visibility:visible;
  transform:translateX(0);
}

.vp-drawer-head{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5);
  border-bottom:1px solid var(--border);
}

.vp-drawer-title{
  font-family:var(--font-head);
  font-weight:800;
  font-size:16px;
  color:var(--navy);
}

.vp-drawer-close{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:40px;
  height:40px;
  padding:0;
  border:0;
  border-radius:var(--r-sm);
  background:none;
  color:var(--text);
  cursor:pointer;
}

.vp-drawer-close:hover{ background:var(--bg-alt); color:var(--navy); }

.vp-drawer-nav{ padding:var(--sp-3) 0; }

.vp-drawer-list{ margin:0; padding:0; list-style:none; }
/*
 * SỬA — khách: "thiết kế UI UX cho dễ nhìn xem". Trước đây SÁU mục cấp 1
 * (Trang chủ/Giới thiệu/Sản phẩm/Dịch vụ/Dự án/Liên hệ) không có gì tách
 * bạch chúng — không viền, không khoảng cách rõ, ba mục có submenu co giãn
 * chiều cao khác ba mục còn lại càng làm danh sách khó quét mắt. Thêm một
 * đường kẻ mảnh GIỮA các mục cấp 1 (không phải quanh mỗi mục — viền quanh
 * từng mục lặp lại viền của submenu bên trong, rối mắt hơn là rõ ràng hơn).
 * `:last-child` bỏ viền dòng cuối, tránh viền kép với `.vp-drawer-nav` sát
 * dưới nó.
 */
.vp-drawer-list > li{ margin:0; border-bottom:1px solid var(--border); }
.vp-drawer-list > li:last-child{ border-bottom:0; }

/*
 * SỬA — khách: "đồng bộ lại font". `.vp-nav-trigger` ("GIỚI THIỆU"/"SẢN
 * PHẨM"/"DỊCH VỤ") kế thừa font-weight:600 + text-transform:uppercase +
 * letter-spacing:.02em từ rule DÙNG CHUNG với thanh nav desktop (mục 1:
 * `.nav-links > li > a, .vp-nav-trigger{...}`). Ba mục còn lại ("Trang chủ"/
 * "Dự án"/"Liên hệ") là <a> THƯỜNG, không khớp selector đó, nên rơi về chữ
 * thường/không hoa — SÁU mục cùng cấp trông như hai kiểu khác nhau, đúng
 * điều khách chụp lại. Khai tường minh CẢ BA thuộc tính đó ở đây cho `> li >
 * a` (với `.vp-nav-trigger` là lặp lại vô hại, giá trị giống hệt rule đã có)
 * để sáu mục cấp 1 luôn cùng một kiểu chữ, không phụ thuộc chúng là <a> hay
 * <button>. font-size đồng bộ về 15px (từ 15.5px) — cùng lý do, khớp đúng
 * `.vp-nav-trigger`.
 *
 * SỬA LẦN 2 — khách: "check lại spacing giữa các item không đều". Mục 1
 * còn có `line-height:1.2` trong CÙNG rule dùng chung đó, và lần sửa trước
 * bỏ sót đúng thuộc tính này. `.vp-nav-trigger` khớp `.nav-links > li > a,
 * .vp-nav-trigger{...}` nên ăn line-height:1.2 (15px × 1.2 = 18px) — còn
 * <a> thường trong ngăn kéo (`.vp-drawer-list > li > a`) KHÔNG khớp selector
 * đó (sai tổ tiên: `.nav-links` là thanh desktop, không phải
 * `.vp-drawer-list`), nên rơi về line-height mặc định của body: 1.6
 * (15px × 1.6 = 24px). Cộng cùng padding dọc var(--sp-3)=12px hai đầu: <a>
 * cao 12+24+12=48px, .vp-nav-trigger cao 12+18+12=42px — SÁU dòng xen kẽ
 * hai chiều cao khác nhau 6px, đúng thứ khách gọi là "không đều". Khai
 * line-height:1.2 tường minh ở đây (khớp .vp-nav-trigger, dáng chữ hoa +
 * letter-spacing hợp với dòng sít hơn là dòng giãn 1.6 của văn bản thường)
 * để cả sáu mục cùng 42px, không phụ thuộc <a> hay <button>.
 */
.vp-drawer-list > li > a,
.vp-drawer-list > li > .vp-nav-trigger{
  display:flex;
  align-items:center;
  justify-content:space-between;
  width:100%;
  padding:var(--sp-3) var(--sp-5);
  font-weight:600;
  font-size:15px;
  line-height:1.2;
  text-transform:uppercase;
  letter-spacing:.02em;
  color:var(--text);
}
.vp-drawer-list > li > a:hover,
.vp-drawer-list > li > a:focus-visible{ color:var(--navy); background:var(--bg-alt); }

/*
 * Trang đang xem — ngăn kéo TRƯỚC ĐÂY không có dấu hiệu nào cho biết đang ở
 * trang nào, dù Vinpack_Nav_Walker đã in sẵn `aria-current="page"` (walker
 * dùng CHUNG cho cả header lẫn ngăn kéo, xem docblock đầu mobile-drawer.php)
 * — dữ liệu có sẵn, chỉ thiếu CSS đọc nó. Cùng cách xử lý đã dùng cho
 * `.nav-links > li > a[aria-current="page"]` ở thanh nav desktop (mục 1).
 */
.vp-drawer-list > li > a[aria-current="page"]{
  color:var(--navy);
  font-weight:800;
  background:var(--bg-alt);
}

/*
 * SỬA LỖI THẬT — chữ "Giới thiệu"/"Sản phẩm"/"Dịch vụ" TRẮNG TRÊN NỀN
 * TRẮNG trong ngăn kéo, khách chụp lại: mở ngăn kéo lúc trang CHƯA cuộn thì
 * ba dòng đó biến mất hoàn toàn, chỉ còn một khoảng trống bằng đúng chiều
 * cao ba nút — "Trang chủ" rồi một khoảng trắng lớn rồi mới tới "Dự án".
 *
 * NGUYÊN NHÂN: mục 1 (đầu file) có
 * `.site-header:not(.scrolled) .vp-nav-trigger{ color:var(--white) }` ở độ
 * ưu tiên (0,3,0) — viết cho `.vp-nav-trigger` của THANH NAV DESKTOP (đứng
 * trên nền navy lúc trang chưa cuộn). `.vp-drawer` lại là CON DOM của
 * `.site-header` (get_template_part() gọi ngay TRONG cặp thẻ
 * `<header>...</header>`, xem site-header.php) nên rule đó vô tình ăn luôn
 * vào ba nút `.vp-nav-trigger` trong ngăn kéo — nền ngăn kéo LUÔN trắng,
 * không phụ thuộc trang đã cuộn hay chưa, nên chữ trắng biến mất, layout
 * vẫn giữ nguyên chiều cao (đúng cỡ khoảng trống khách thấy). Nút hamburger
 * `.vp-drawer-toggle` đổi trắng lúc chưa cuộn là ĐÚNG (nó đứng trên nền navy
 * của thanh header, không phải trong ngăn kéo) — không phải cùng một lỗi.
 *
 * SỬA: khoá `.vp-nav-trigger` trong ngăn kéo LUÔN --text, không phụ thuộc
 * `.scrolled`. Bốn lớp `.vp-drawer .vp-drawer-nav .vp-drawer-list
 * .vp-nav-trigger` = (0,4,0) — CHẮC CHẮN thắng (0,3,0) của rule lỗi bằng
 * ĐỘ ƯU TIÊN, không dựa vào thứ tự nguồn (rule này đã đứng sau, nhưng viết
 * rõ bằng số lớp để không phụ thuộc "đừng ai chèn rule cùng thứ tự sau nó").
 * `.caret svg` dùng `stroke="currentColor"` nên tự đổi màu theo, không cần
 * khoá riêng.
 */
.vp-drawer .vp-drawer-nav .vp-drawer-list .vp-nav-trigger{ color:var(--text); }
.vp-drawer .vp-drawer-nav .vp-drawer-list .vp-nav-trigger:hover,
.vp-drawer .vp-drawer-nav .vp-drawer-list .vp-nav-trigger[aria-expanded="true"]{ color:var(--navy); }

/* ============================================================
   8. Bản gọn của mega bên trong ngăn kéo
   ============================================================ */

.mega-compact{ margin:0; padding:0 0 var(--sp-3); list-style:none; }

/*
 * SỬA — khách: "thêm dropdown cấp 2". Mỗi <li> cấp 1 giờ CÓ THỂ chứa ba con:
 * link tên danh mục, nút thu/mở, danh sách con. flex-wrap để nút nằm cùng
 * hàng với tên (bấm tên vẫn điều hướng, bấm nút chỉ thu/mở — hai vùng bấm
 * tách biệt), còn danh sách con luôn xuống hàng riêng: flex-basis:100% trên
 * .mega-compact-kids khiến nó không đủ chỗ ở hàng đầu, buộc phải wrap. Mục
 * KHÔNG có con thì .mega-compact-top chỉ có một con (link) — flex một-con
 * nhìn y hệt block, không đổi gì so với trước.
 */
.mega-compact-top{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  padding:0;
}

.mega-compact-link{
  display:block;
  flex:1 1 auto;
  min-width:0;
  padding:10px var(--sp-5);
  font-weight:600;
  font-size:15px;
  color:var(--navy);
  text-decoration:none;
}

/*
 * Nút thu/mở cấp 2 — ĐỘC LẬP với .mega-compact-link, không lồng vào nhau
 * (lồng <button> vào trong <a> là HTML không hợp lệ). Tách hai vùng bấm rõ
 * ràng còn tránh đúng cái bẫy khách từng báo cho .mega-cat ở bản đầy đủ
 * ("bấm vào tên là điều hướng, không bấm được vào con") — nhưng ở đây KHÔNG
 * dùng hover như bản desktop đã chọn, vì di động không có con trỏ để hover.
 * Hai vùng bấm riêng là lối ra hợp lý cho ngữ cảnh chạm. `.caret` và phép
 * xoay 180° khi mở DÙNG LẠI đúng rule toàn cục ở mục 1, không định nghĩa
 * riêng ở đây.
 */
.mega-compact-toggle{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  flex:0 0 auto;
  width:44px;
  min-height:44px;
  border:0;
  background:none;
  color:var(--text);
  cursor:pointer;
}

.mega-compact-toggle:hover,
.mega-compact-toggle[aria-expanded="true"]{ color:var(--navy); }

/*
 * Accordion cấp 2 — LẶP LẠI đúng cơ chế của .vp-panel trong ngăn kéo (mục
 * 10): max-height cho hoạt ảnh cao thấp, CỘNG visibility:hidden lúc đóng để
 * link con không lọt vào lượt Tab khi mắt không thấy gì (max-height:0 một
 * mình KHÔNG loại phần tử khỏi thứ tự Tab — đây là bẫy đã biết, xem mục 10).
 * Không cần đặt trong @media(max-width:900px) như .vp-panel, vì .mega-compact
 * CHỈ BAO GIỜ render trong ngăn kéo di động (cờ $GLOBALS['vp_mega_compact'])
 * — không có hình thái desktop nào dùng chung selector này.
 *
 * padding:0 (KHÔNG phải `0 0 var(--sp-2)` như bản đầu) — khách: "check luôn
 * mấy cái cấp 2 luôn", sau khi vừa báo đúng lỗi này ở .vp-dd-panel (mục 3,
 * "Giới thiệu"/"Dịch vụ"). Y HỆT lỗi đó, tự lặp lại ở đây: padding-bottom
 * đặt ngay trên chính phần tử bị max-height:0 — max-height không ép padding
 * CỦA CHÍNH NÓ về 0 (chỉ cắt nội dung tràn qua overflow), nên dù max-height
 * tính ra 0px, mỗi ".mega-compact-kids" đóng vẫn cao thật 8px (đo bằng
 * getBoundingClientRect() trên trang live: rectHeight:8 ở cả bốn danh mục
 * có con — Túi nilon/Bao bì đóng gói/Túi vải/Bao bì giấy). Bỏ hẳn padding ở
 * đây, không dời đi đâu khác: từng link con vẫn có đệm riêng từ
 * `.mega-compact-sub{ padding:8px ... }`, mất khung đệm quanh NHÓM không
 * mất đệm từng MỤC — cùng cách xử lý đã dùng cho .vp-dd-panel.
 */
.mega-compact-kids{
  flex-basis:100%;
  margin:0;
  padding:0;
  list-style:none;
  max-height:0;
  overflow:hidden;
  visibility:hidden;
  transition:max-height var(--dur-state) var(--ease),
             visibility var(--dur-state) var(--ease);
}

.mega-compact-toggle[aria-expanded="true"] + .mega-compact-kids{
  max-height:1000px;
  visibility:visible;
}

.mega-compact-sub{
  display:block;
  padding:8px var(--sp-5) 8px calc(var(--sp-5) + var(--sp-4));
  font-size:14px;
  color:var(--text);
  text-decoration:none;
}

.mega-compact-sub:hover{ background:var(--bg-alt); color:var(--navy); }

/* ============================================================
   9. Chân ngăn kéo
   ============================================================
   CỐ Ý không đụng background, color, border-radius của .vp-drawer-cta: nút
   đó mang cả .btn lẫn .btn-primary, và đặt lại ba thứ ấy ở đây là cách
   nhanh nhất để một nút navy hoá xám mà không ai thấy. Chỉ đặt bố cục.
   ============================================================ */

.vp-drawer-foot{
  margin-top:auto;
  display:flex;
  flex-direction:column;
  gap:var(--sp-3);
  padding:var(--sp-5);
  border-top:1px solid var(--border);
}

.vp-drawer-cta{ width:100%; }

.vp-drawer-tel{
  display:inline-flex;
  align-items:center;
  gap:var(--sp-2);
  font-weight:700;
  font-size:15px;
  color:var(--navy);
  text-decoration:none;
}

.vp-drawer-socials{
  display:flex;
  flex-wrap:wrap;
  gap:var(--sp-2);
  margin:0;
  padding:0;
  list-style:none;
}

.vp-drawer-social{
  display:inline-flex;
  align-items:center;
  padding:6px var(--sp-3);
  border:1px solid var(--border);
  border-radius:var(--r-pill);
  font-size:13px;
  color:var(--text);
  text-decoration:none;
}

.vp-drawer-social:hover{ border-color:var(--cobalt); color:var(--navy); }

/* ============================================================
   10. Mốc 900px — chỗ hai thứ đổi chiều
   ============================================================ */

@media (max-width:900px){
  .nav{ min-height:64px; }

  /* Hai dòng này là món nợ mà Task 5 phải trả; xem chú thích ở mục 5. */
  .nav-links{ display:none; }
  .vp-drawer-toggle{ display:inline-flex; }
  /*
   * BỎ margin-left:auto CỦA CHÍNH .vp-drawer-toggle (mục base, dòng ~980)
   * — CHỈ trong khổ hẹp này, nơi cả .nav-actions VÀ .vp-drawer-toggle đều
   * hiển thị cùng lúc kể từ khi khách yêu cầu "trả nợ" kính lúp ở mobile
   * (mục ngay dưới). Trước đó .nav-actions luôn ẩn ở khổ này nên margin-
   * left:auto một mình trên .vp-drawer-toggle vô hại — giờ có HAI phần
   * tử liền kề cùng margin-left:auto, và flexbox CHIA ĐỀU khoảng trống
   * cho từng auto-margin thay vì gộp làm một. Đo thật trước khi sửa (khổ
   * 375px): kính lúp và hamburger cách nhau 84px, mỗi bên margin-left
   * tính ra đúng 72.33px bằng nhau — dấu hiệu kinh điển của hai auto-
   * margin cùng trục cạnh nhau, KHÔNG phải cache hay lỗi hiển thị.
   *
   * Gỡ auto ở đây thì CHỈ CÒN `.nav-actions{margin-left:auto}` đẩy TRỌN
   * cụm (kính lúp + hamburger) sát mép phải, và `.nav{gap:var(--sp-3)}`
   * (mục ngay dưới) trở thành khoảng cách CỐ ĐỊNH duy nhất giữa hai icon
   * — đúng ý đồ "cho sát vào" mà comment gap đó đã nói tới nhưng chưa
   * thành hiện thực vì bị auto-margin thứ hai này ăn mất.
   */
  .vp-drawer-toggle{ margin-left:0; }

  /*
   * .nav-actions VẪN HIỆN ở khổ hẹp, nhưng chỉ còn nút kính lúp.
   *
   * Trước 03/09/2026 cả cụm bị ẩn, và chú thích cũ ghi thẳng: "Ô tìm kiếm thì
   * mất thật ở khổ này; đó là món nợ đã biết". Khách yêu cầu trả nợ đó.
   *
   * Nút CTA vẫn ẩn ở đây — không phải vì thiếu chỗ mà vì NGĂN KÉO ĐÃ IN LẠI
   * nó từ cùng một vinpack_cta(). Hiện cả hai là hai nút giống hệt cách nhau
   * 40px.
   *
   * Hai hộp thoại (popup tìm kiếm và ngăn kéo) nay CÙNG mở được ở một khổ màn
   * hình — điều mà chú thích ở mục 3 đã cảnh báo là sẽ đụng z-index vì chúng
   * hoà nhau ở bậc +3. Cách chữa KHÔNG phải tách bậc rồi đoán, mà là làm
   * chúng LOẠI TRỪ NHAU: mở cái này thì cái kia đóng (bindModal trong
   * assets/js/layout.js). Không bao giờ mở cùng lúc thì bậc bằng nhau không
   * còn là vấn đề, và không cần đo elementFromPoint.
   */
  /* margin-left:auto da co o rule goc (muc 1b), khong lap lai. */
  /*
   * gap CỦA .nav (mục 1a, 96px desktop) áp dụng ĐỀU cho mọi cặp con liền kề,
   * kể cả cặp .nav-actions <-> .vp-drawer-toggle — 96px giữa hai icon nhỏ
   * nhìn như chúng chẳng liên quan gì nhau (khách 03/09/2026: "cho sát vào").
   * Không đụng gap gốc (khách đã đo/chốt 96px cho khoảng logo->menu ở
   * desktop) — chỉ NHÍCH XUỐNG ở đúng khổ hẹp này, nơi .vp-nav đã ẩn nên
   * gap chỉ còn tác dụng đúng một cặp: kính lúp <-> hamburger.
   */
  .nav{ gap:var(--sp-3); }

  .nav-actions{ display:flex; }

  .nav-actions .vp-header-cta{ display:none; }

  /*
   * Bên trong ngăn kéo, panel dropdown và mega chuyển thành accordion xếp
   * chồng: bỏ position:absolute, thu chiều cao thay vì trượt. `visibility`
   * vẫn do hai rule ở mục 3 quyết định, nên trạng thái đóng vẫn không Tab
   * vào được.
   *
   * padding:0 — khách: "có 2 cái spacing to quá nè" (khoanh đỏ đúng dưới
   * "GIỚI THIỆU" và dưới "DỊCH VỤ"). NGUYÊN NHÂN đo được thật bằng
   * getBoundingClientRect(): `.vp-dd-panel{ padding:var(--sp-2) 0 }` (mục 3)
   * đặt padding NGAY TRÊN chính phần tử bị `max-height:0` — max-height KHÔNG
   * ép padding của CHÍNH nó về 0 (chỉ cắt phần NỘI DUNG tràn qua overflow),
   * nên dù max-height tính ra 0px, chiều cao hiển thị thật của
   * `.vp-dd-panel` vẫn đúng bằng tổng padding: 8px + 8px = 16px — đo trực
   * tiếp trên trang live ra rectHeight:16, khớp y hệt độ cao dải trắng thừa
   * trong ảnh khách chụp. `.vp-mega-panel` ("Sản phẩm") KHÔNG dính lỗi này
   * vì padding của nó nằm ở con bên trong (.mega-compact), không nằm trên
   * chính phần tử .vp-panel bị ép chiều cao — con bị overflow:hidden của
   * cha cắt sạch, còn padding CỦA CHA (0) thì có gì đâu mà cắt.
   *
   * SỬA: `padding:0` ở đây, cùng cấp với max-height, thắng
   * `.vp-dd-panel{padding:var(--sp-2) 0}` bằng độ ưu tiên
   * (`.vp-drawer .vp-panel` = (0,2,0) > `.vp-dd-panel` = (0,1,0)) — không
   * đổi gì cho .vp-mega-panel (vốn đã padding:0 sẵn), chỉ triệt padding thừa
   * của .vp-dd-panel khi ở trong ngăn kéo. Danh sách bên trong vẫn có
   * khoảng đệm riêng từ `.vp-dd-panel a{ padding:10px var(--sp-4) }` của
   * từng link — mất khung padding quanh NHÓM, không mất padding từng MỤC.
   */
  .vp-drawer .vp-panel{
    position:static;
    z-index:auto;
    padding:0;
    box-shadow:none;
    border-radius:0;
    border-top:0;
    transform:none;
    max-height:0;
    overflow:hidden;
    background:var(--bg-alt);
    transition:max-height var(--dur-state) var(--ease),
               opacity var(--dur-state) var(--ease),
               visibility var(--dur-state) var(--ease);
  }

  .vp-drawer .vp-nav-trigger[aria-expanded="true"] + .vp-panel{
    max-height:1400px;
    transform:none;
  }

  .vp-drawer .mega-inner{ display:block; padding:0; }
}

@media (max-width:1180px){
  .mega-products{ grid-template-columns:repeat(2, 1fr); }
}

/* ============================================================
   11. Chân trang — lưới, nền tối, món nợ .logo-white
   ============================================================
   Nền là var(--navy-dark) và CHỈ --navy-dark. Tương phản đã đo (Task 2, đo
   lại từ token trong tests/test-footer.php mục 2):

     --footer-text #9AA8CC trên --navy-dark #001291 = 5.92:1  (AA đạt)
     --white       #FFFFFF trên --navy-dark #001291 = 14.04:1

   Đổi nền sang --navy #001BB7 thì --footer-text tụt còn 4.81:1 — vẫn đạt AA
   nhưng sát mép hơn nhiều; và --cobalt tụt còn 3.67:1, tức TRƯỢT. Đừng đổi
   nền mà không đo lại cả hai vế.

   HAI CHỖ PHẢI ĐÈ TƯỜNG MINH, nếu không chữ biến mất trên nền tối:

     - h1..h5 của global.css khai `color:var(--navy)` ở (0,0,1) THẲNG lên thẻ,
       nên .footer-col-title là navy trên navy-dark — gần như vô hình. Màu kế
       thừa từ .site-footer KHÔNG cứu được, vì khai báo trực tiếp luôn thắng
       kế thừa bất kể độ ưu tiên. Cần (0,2,0) để lật lại.
     - `:where(a){color:inherit}` ở (0,0,0) nên link tự kế thừa --footer-text;
       chỗ này không cần đè, chỉ cần đè lúc hover.

   SỐ CỘT MENU LÀ 0, 1, 2 HOẶC 3 — nhân viên gán bao nhiêu vị trí thì bấy
   nhiêu. Production hôm nay chưa gán vị trí nào nên footer thật đang có 0
   cột. Vì thế lưới dùng repeat(auto-fit, minmax(...)) chứ KHÔNG ghi cứng bốn
   cột: một `repeat(4, 1fr)` sẽ để lại ba rãnh trống khi chưa gán menu nào, và
   không có gì báo lỗi.
   ============================================================ */

.site-footer{
  background:var(--navy-dark);
  color:var(--footer-text);
  /* Mockup 64px/28px — thang token khong co hai tri so nay (--sp-7 la 48,
     --sp-5 la 24), ghi thang px dung bang thiet ke. */
  padding-top:64px;
  padding-bottom:28px;
}

/*
 * `.footer-grid` — tên của mockup, và cũng là tên đúng: từ Phase D Task 4 nó
 * chỉ còn chứa CỘT (thương hiệu + 0..3 cột menu), không còn chứa liên hệ, mạng
 * xã hội và dòng pháp lý như `.footer-inner` cũ.
 *
 * Mockup ghi `grid-template-columns:1.3fr 1fr 1fr 1fr`. KHÔNG chép được: bốn
 * track cố định để lại ba rãnh trống trên production hôm nay (0 vị trí menu đã
 * gán, tức đúng MỘT cột). `auto-fit` + `minmax` cho đúng số cột có thật, và ô
 * đầu rộng hơn nhờ `.footer-brand` đặt riêng bên dưới.
 *
 * tests/test-css-cascade.php mục 6.3 giải lại rule này cho 0/1/2/3 cột menu và
 * đòi CẢ BỐN ca ra cùng một giá trị.
 */
.footer-grid{
  display:grid;
  /*
   * Mockup chia `1.3fr 1fr 1fr 1fr` — cot thuong hieu RONG HON ba cot menu,
   * trong khi ban dung dau ra BON COT BANG NHAU (do that: 261px x4).
   *
   * KHONG ghi thang `1.3fr 1fr 1fr 1fr` o day: tests/test-css-cascade.php khoa
   * lui rang track list phai la auto-fit/auto-fill, va ly le cua no dung —
   * ghi cung 4 cot thi khi khach chua cau hinh menu (0 cot menu) chan trang se
   * de lai ba ranh TRONG ma khong co gi bao loi.
   *
   * Dat be rong cho cot thuong hieu bang `min-width` cua CHINH no (xem
   * `.footer-brand` ben duoi) thi duoc ca hai: track do rong ra dung ti le
   * mockup (do lai: 300 / 232 = 1.29, mockup 1.3), con auto-fit van nguyen nen
   * it cot hon cung khong sinh ranh trong.
   */
  grid-template-columns:repeat(auto-fit, minmax(220px, 1fr));
  gap:40px;
  margin-bottom:40px;
  align-items:start;
}

/*
 * Cột thương hiệu rộng hơn các cột menu — tinh thần `1.3fr` của mockup, nhưng
 * diễn đạt bằng một track linh hoạt để nó vẫn đúng khi chỉ có MỘT cột.
 */
.footer-brand{
  grid-column:span 1;
  min-width:0;
}

/*
 * Ti le mockup `1.3fr 1fr 1fr 1fr` — CHI ap khi that su co du bon cot.
 *
 * Da thu dat `min-width` cho .footer-brand de keo rieng track do rong ra:
 * KHONG an, vi `minmax(220px,1fr)` chot day track o 220px va `1fr` cao bang
 * phan con lai — do that van ra 249px x4. Track chi doi khi doi chinh track
 * list.
 *
 * `:has(> :nth-child(4))` la cho de ghi ti le co dinh ma KHONG mat cai
 * tests/test-css-cascade.php dang bao ve: rule nay chi khop khi chan trang
 * thuc su co bon con. Khach chua cau hinh menu (0-2 cot menu) thi rule khong
 * khop, `auto-fit` o tren van chay, khong sinh ranh trong.
 *
 * Trinh duyet chua ho tro `:has()` (Safari < 15.4) bo qua rule nay va roi ve
 * bon cot bang nhau — van dung bo cuc, chi khong dung ti le. Suy giam nhe, chap
 * nhan duoc.
 */
.footer-grid:has(> :nth-child(4)){ grid-template-columns:1.3fr 1fr 1fr 1fr; }

/*
 * Man hep tra ve auto-fit — bon cot chat cung nhau khong doc noi.
 *
 * Viet bang `max-width` chu khong phai `min-width` cho rule tren: bo giai cua
 * tests/test-css-cascade.php chi hieu `max-width`, gap `min-width` la no bao
 * "khong hieu @media" va do ca file test. Cung ket qua, khac cach dien dat.
 *
 * Lap lai nguyen selector `:has(> :nth-child(4))` chu khong viet gon
 * `.footer-grid`: rule tren o (0,2,0) vi `:has()` cong do dac hieu cua doi so,
 * mot `.footer-grid` tran (0,1,0) se THUA va rule nay khong bao gio an.
 */
@media(max-width:1023px){
  .footer-grid:has(> :nth-child(4)){ grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); }
}

/*
 * Liên hệ và mạng xã hội NAY NẰM TRONG cột thương hiệu (hợp đồng DOM mới, xem
 * docblock của template-parts/footer/site-footer.php).
 *
 * Hai rule cũ viết cho lúc chúng là ANH EM đã được gỡ, không phải đè:
 *   - `.footer-social-row` đặt `margin-top:var(--sp-6)` và một `border-top`
 *     ngang hết bề rộng chân trang. Nằm trong một cột thì đường kẻ đó chỉ dài
 *     bằng cột, trông như một gạch chân lạc lõng.
 *   - Không có rule nào của .footer-contact phải sửa: nó chưa từng có rule
 *     riêng ngoài .footer-contact-list / -item / -label, và cả ba đều là móc
 *     bên trong nên vị trí của khối cha không đụng tới.
 */
/*
 * SỬA LẦN 3 — khách (24/08/2026, "xoá footer cũ làm lại giống y như vầy"):
 * .footer-socials-title GỠ NỐT khỏi danh sách này (mockup không có tiêu đề
 * "Kết nối với Vinpack tại:", markup đã bỏ) — cùng lý do SỬA LẦN 2 đã gỡ
 * .footer-legal-info/.footer-certs: không còn markup thì không giữ selector.
 * Còn đúng hai khối trong cột thương hiệu cần cùng nhịp khoảng cách dọc.
 */
.footer-contact{
  margin-top:var(--sp-5);
}

/*
 * Món nợ Task 5 trả cho Task 2.
 *
 * .logo-white bị mục 1 đặt display:none ở (0,1,0) và không rule nào trong repo
 * override được — markup in <img class="logo-img logo-white"> vào DOM mà nó
 * vẫn vô hình. Chân trang là ngữ cảnh nền tối DUY NHẤT của theme, đúng chỗ
 * biến thể trắng sinh ra để phục vụ. (0,3,0) thắng bằng ĐỘ ƯU TIÊN, không nhờ
 * thứ tự nguồn — nên nó vẫn đúng nếu ai đó dời rule. Phải là (0,3,0) chứ không
 * phải (0,2,0): mục 1 đã cần (0,2,0) để thắng `.logo img`, và hai rule hoà độ
 * ưu tiên thì chỉ còn thứ tự nguồn phân giải.
 *
 * NGOÀI chân trang .logo-white vẫn ẩn; đó là chủ ý, header là nền trắng.
 */
/*
 * RULE `.site-footer .footer-logo .logo-white` ĐÃ XOÁ (STT 12).
 * Nó sinh ra để trả "món nợ Task 5" — cho biến thể logo trắng hiện lên trong
 * chân trang, ngữ cảnh nền tối duy nhất của theme. Nay chân trang KHÔNG in ảnh
 * logo nữa (khách yêu cầu), nên món nợ đó không còn tồn tại: không markup nào
 * đưa `.logo-white` vào trong `.footer-logo`.
 *
 * `.logo-white` ngoài chân trang vẫn do mục 1 đặt display:none, và header vẫn
 * dùng biến thể `.for-dark` của nó — phần đó KHÔNG đổi.
 */

.footer-logo{
  display:inline-flex;
  align-items:center;
  gap:var(--sp-3);
  margin-bottom:var(--sp-4);
  text-decoration:none;
}

.footer-brand-name{
  font-family:var(--font-head);
  font-weight:800;
  font-size:19px;
  color:var(--white);
}

.footer-intro{
  margin:0;
  font-size:14.5px;
  line-height:1.7;
}

/*
 * (0,2,0) — phải mạnh hơn `h1, h2, h3, h4, h5{color:var(--navy)}` (0,0,1) của
 * global.css. Xem chú thích đầu mục.
 */
.site-footer .footer-col-title{
  margin:0 0 var(--sp-3);
  font-family:var(--font-head);
  font-weight:800;
  font-size:15px;
  color:var(--white);
}

.footer-list,
.footer-contact-list{
  margin:0;
  padding:0;
  list-style:none;
  display:flex;
  flex-direction:column;
  gap:var(--sp-2);
}

/*
 * SỬA LẦN 3 — mockup để khoảng cách giữa các dòng liên hệ THƯA HƠN các dòng
 * menu (12px so với 8px): mỗi dòng liên hệ nay là icon + chữ, dòng địa chỉ
 * còn cao hai dòng, nên nhịp 8px của cột menu làm chúng dính nhau. Tách khỏi
 * rule dùng chung ngay trên thay vì sửa `--sp-2` — token đó dùng khắp theme.
 */
.footer-contact-list{ gap:var(--sp-3); }

.footer-list a,
.footer-contact-value{
  font-size:14.5px;
  line-height:1.6;
  text-decoration:none;
}

/*
 * RULE `.footer-list li.more a` ĐÃ XOÁ — SỬA LẦN 3 (khách 24/08/2026): mục
 * "Xem tất cả →" đổi thành danh mục thật "Bao bì đóng gói", không còn mục nào
 * mang class "more" nên rule này hết đối tượng. Nguồn gắn class (tham số
 * $classes của vp_demo_item()) cũng gỡ cùng lượt — xem
 * tests/fixtures/demo-data.php.
 *
 * Giữ rule lại là để CSS chết trong repo, đúng loại lỗi mà `.footer-more`/
 * `.footer-legal`/`.footer-contact-label` đã dọn ở các đợt trước.
 */

/*
 * SỬA LẦN 3 — ICON + CHỮ, đúng mockup.
 *
 * Bản trước là NHÃN CHỮ + giá trị cùng dòng ("HOTLINE  0901 80 86 87"). Ghi
 * lại lý do bản đó tồn tại, vì nó vẫn đúng và đừng ai quay lại bản CŨ HƠN
 * NỮA: bản cũ hơn xếp DỌC (nhãn trên, giá trị dưới) làm bảy kênh thành mười
 * bốn dòng, kéo cột thương hiệu cao 656px trong khi ba cột menu chỉ 154-191px.
 * Dù nhãn chữ hay icon thì bất biến phải giữ là MỘT DÒNG MỘT KÊNH.
 *
 * Nay nhãn chữ đổi thành icon (mockup) và số kênh còn ba — xem docblock trong
 * site-footer.php cho phần a11y (vì sao bỏ nhãn chữ không làm mất nghĩa).
 *
 * `align-items:flex-start` chứ không `baseline`: dòng địa chỉ cao HAI dòng
 * chữ, canh theo baseline sẽ dìm icon xuống ngang dòng đầu một cách lệch lạc.
 * `margin-top:2px` trên icon là để tâm icon khớp dòng chữ đầu (mockup dùng
 * đúng số này).
 *
 * RULE `.footer-contact-label` ĐÃ XOÁ — không còn markup nào sinh ra nó.
 * Giữ lại là để CSS chết trong repo, đúng loại lỗi mà `.footer-more`/
 * `.footer-legal`/`.footer-legal-list` đã dọn ở các đợt trước.
 */
.footer-contact-item{
  display:flex;
  align-items:flex-start;
  gap:10px;
}

.footer-contact-item svg{
  flex-shrink:0;
  width:15px;
  height:15px;
  margin-top:2px;
  color:var(--on-navy-accent);
}

/*
 * SỬA LẦN 4 — khách (24/08/2026, ảnh khoanh đỏ hàng icon MXH): "phần này a di
 * chuyển xuống giúp e tí cho khoảng cách nó giãn bằng khoảng cách hotline với
 * email".
 *
 * ĐÂY LÀ SỬA MỘT LỖI THẬT DO SỬA LẦN 3 GÂY RA, không phải tinh chỉnh thẩm mỹ.
 * Đo trình duyệt: khoảng cách email → hàng MXH đang là ĐÚNG 0px, trong khi
 * hai dòng liên hệ cách nhau 12px — icon dính sát dòng email.
 *
 * NGUYÊN NHÂN là một cú đè cascade âm thầm, đáng ghi lại: rule
 * `.footer-contact, .footer-socials{ margin-top:var(--sp-5) }` ở mục trên khai
 * margin cho hàng MXH, nhưng CHÍNH rule này (cùng độ ưu tiên (0,1,0), đứng SAU
 * trong file) có `margin:0` — shorthand đè sạch margin-top của rule kia. Nên
 * `margin-top` cho `.footer-socials` CHƯA BAO GIỜ có tác dụng; trước SỬA LẦN 3
 * không ai thấy vì tiêu đề "Kết nối với Vinpack tại:" nằm chen giữa và tự
 * gánh khoảng cách. Gỡ tiêu đề đi thì lỗi có sẵn mới lộ ra.
 *
 * Sửa TẠI ĐÂY chứ không thêm một rule mạnh hơn ở chỗ khác: nơi duy nhất khai
 * margin cho `.footer-socials` phải là chính nó, nếu không lần sau lại có
 * người sửa nhầm rule kia rồi tưởng không ăn.
 *
 * --sp-3 (12px) = ĐÚNG gap của `.footer-contact-list`, tức đúng khoảng cách
 * hotline ↔ email khách lấy làm mốc. Dùng cùng token chứ không gõ 12px, để
 * hai chỗ đổi cùng nhau nếu nhịp chân trang đổi.
 */
.footer-socials{
  display:flex;
  flex-wrap:wrap;
  gap:var(--sp-2);
  /*
   * Viết TÁCH `margin:0` + `margin-top:` chứ không gộp `margin:var(--sp-3) 0 0`
   * — hai lý do, lý do thứ hai mới là lý do thật:
   *   1. Nói rõ ý: reset margin mặc định của <ul>, RỒI mới khai khoảng cách trên.
   *   2. Bộ giải cascade của tests/test-css-cascade.php KHÔNG bung shorthand.
   *      Viết gộp thì nó trả "không rule nào" cho margin-top và phép đo ở mục
   *      6.3b mù hẳn — trình duyệt vẫn đúng 12px nhưng test không nhìn thấy để
   *      canh. Một giá trị không đo được là một giá trị không ai giữ hộ.
   */
  margin:0;
  margin-top:var(--sp-3);
  padding:0;
  list-style:none;
}

.footer-social{
  display:inline-flex;
  align-items:center;
  min-height:36px;
  padding:6px var(--sp-3);
  border:1px solid rgba(255, 255, 255, .22);
  border-radius:var(--r-pill);
  font-size:13.5px;
  text-decoration:none;
}

/*
 * Bien the CO ICON — dung mockup: o vuong 34x34, bo 8px, nen trang mo 8%.
 * Chi ap khi thuc su co icon (`.footer-social--icon` do PHP gan), kenh la van
 * giu nhan chu o tren, khong bao gio ra o vuong trong.
 */
.footer-social--icon{
  width:34px;
  height:34px;
  min-height:0;
  padding:0;
  justify-content:center;
  border:0;
  border-radius:8px;
  background:rgba(255, 255, 255, .08);
  color:var(--white);
  transition:background var(--dur-micro) var(--ease);
}
.footer-social--icon:hover,
.footer-social--icon:focus-visible{ background:var(--cobalt); color:var(--white); }
.footer-social--icon svg{ width:16px; height:16px; }

/*
 * "Xem tất cả →" ở cuối cột sản phẩm.
 *
 * MOCKUP DÙNG var(--cobalt) — KHÔNG CHÉP. Đo lại từ token: --cobalt trên
 * --navy-dark là 4.52:1, tức đạt AA cho chữ thường nhưng SÁT MÉP; một lần
 * chỉnh token màu thương hiệu là nó trượt và không ai biết. --white cho
 * 14.04:1, và chữ đậm 600 đã đủ tách nó khỏi các mục menu bên trên (chúng kế
 * thừa --footer-text, 5.92:1). Nghĩa là sự khác biệt do CẢ màu lẫn độ đậm,
 * không chỉ do màu — đúng nguyên tắc "đừng dùng riêng màu để mang thông tin".
 *
 * Hai class: `.more` giữ đúng tên mockup, `.footer-more` là móc mà CSS thật sự
 * nhắm. Một `.more` trần trong một stylesheet toàn site là một cái tên quá
 * chung để đem ra làm móc.
 */
/*
 * RULE `.footer-more` ĐÃ XOÁ — không còn markup nào sinh ra nó (STT 12).
 * Khách yêu cầu bỏ hẳn liên kết "Xem tất cả →" khỏi cột sản phẩm và thay bằng
 * một mục danh mục thật ("Bao bì đóng gói"). Giữ lại rule là để CSS chết trong
 * repo, và tệ hơn là để các khẳng định cascade canh nó vẫn xanh trong khi
 * không còn gì để canh. Docblock cân màu/tương phản của nó cũng đi theo.
 */

/*
 * RULE `.footer-legal-list` / `.footer-legal-item` / `.footer-legal-link` /
 * `.footer-legal-text` / `.footer-certs-list` (+ ảnh con) ĐÃ XOÁ — SỬA LẦN 2,
 * khách (22/08/2026, "làm lại cái footer giống y ảnh chụp"). Bốn class đầu
 * phục vụ khối `.footer-legal-info` ("Thông tin liên hệ" phần hai), hai class
 * sau phục vụ khối `.footer-certs` ("Chứng nhận") — cả hai khối đã gỡ khỏi
 * site-footer.php (xem docblock đầu file đó), không markup nào còn sinh ra
 * sáu class này nữa. Giữ rule lại là để CSS chết trong repo, đúng loại lỗi mà
 * `.footer-more` đã dọn trước đó.
 *
 * `.footer-legal` / `.footer-legal-line` / `.footer-credit` PHỤC HỒI ngay
 * dưới, đúng nguyên CSS trước đợt "footer như bản gốc 100%" (SỬA LẦN 1) gỡ
 * chúng — đối chiếu git history nếu cần: commit gỡ là "Footer: đồng bộ 100%
 * với chân trang thật vinpack.vn". CEO/MST KHÔNG MẤT — nay lại là hai
 * `.footer-legal-item` (span, KHÁC nghĩa với `.footer-legal-item` cũ vừa
 * xoá ở trên dù trùng tên — namespace chung một lần, không đè nhau vì bản cũ
 * đã gỡ hẳn khỏi DOM) bên trong `.footer-legal-line`.
 */

.footer-legal{
  margin-top:var(--sp-5);
  padding-top:var(--sp-4);
  border-top:1px solid rgba(255, 255, 255, .14);
  display:flex;
  flex-wrap:wrap;
  justify-content:space-between;
  align-items:baseline;
  gap:var(--sp-2) var(--sp-4);
}

.footer-legal-line,
.footer-credit{
  margin:0;
  font-size:13.5px;
  line-height:1.7;
}

/*
 * Ba mảnh pháp lý là ba <span> riêng (mockup), không phải một chuỗi đã nối
 * bằng " — ". Khoảng cách là việc của CSS, nên dấu ngăn không lọt vào chuỗi
 * dịch và không bị trình đọc màn hình xướng lên như một từ.
 */
.footer-legal-line{
  display:flex;
  flex-wrap:wrap;
  gap:2px var(--sp-4);
}

/* Hover đổi sang --white (14.04:1). (0,1,1) — không cần mạnh hơn. */
.site-footer a:hover{ color:var(--white); }

/* ============================================================
   12. Nút liên hệ nổi
   ============================================================
   KHÔNG CÓ JS. Các nút hiện thẳng, không có cụm bung ra, nên không có trạng
   thái nào để giữ và không có `aria-expanded` nào để làm nguồn sự thật. Thêm
   một module JS chỉ để bung một danh sách vốn đã vừa màn hình là thêm một
   nguồn lỗi mà không đổi lấy được gì.

   z-index DẪN XUẤT TỪ --z-header, KHÔNG PHẢI --z-fab. Đọc kỹ, đây là chỗ
   phản trực giác nhất của file.

   Ngăn kéo và nền mờ của nó nằm TRONG .site-header, và .site-header tạo một
   ngữ cảnh xếp chồng ở var(--z-header). Nên câu hỏi "FAB hay ngăn kéo ở
   trên" KHÔNG được trả lời bằng z-index của ngăn kéo — nó được trả lời bằng
   .vp-fab so với .site-header. Cách duy nhất để ngăn kéo lên trên mà không
   phải đụng vào token của header là hạ FAB xuống dưới header.

   49 vẫn cao hơn mọi nội dung thường (z-index auto), nên FAB vẫn nổi trên
   trang. Nó nằm dưới header dính, dưới panel mega (--z-mega, trong ngữ cảnh
   header) và dưới ngăn kéo — cả ba đều đúng mong muốn. Header và FAB không
   chồng nhau về hình học, nên việc FAB xuống dưới header không mất gì.

   Bảng hai tầng đầy đủ ở đầu mục 6.

   KHÔNG THÊM RULE ẨN FAB KHI NGĂN KÉO MỞ. Nền mờ phủ kín khung nhìn và nay
   nằm trên FAB, nên khi ngăn kéo mở thì FAB đã bị làm mờ và không bấm tới
   được — cùng một cơ chế áp cho MỌI thứ phía sau ngăn kéo, không phải một cơ
   chế thứ hai chỉ dành cho FAB. Thứ tự Tab thuộc về bẫy focus dựa trên
   keydown của Task 4; cho CSS ẩn thêm FAB chỉ CHE một cái bẫy hỏng chứ không
   sửa nó.

   VÙNG BẤM ≥ 44×44px (WCAG 2.5.8). <svg> chỉ 22px, 11px padding mỗi bên là
   đúng 44; min-width/min-height là chốt chặn nếu ai đó đổi cỡ icon.

   TƯƠNG PHẢN (mới, đo cả hai vế):
     --white #FFFFFF trên --navy      #001BB7 = 11.41:1
     --white #FFFFFF trên --navy-dark #001291 = 14.04:1  (trạng thái hover)
   Icon là stroke="currentColor" nên nó theo đúng hai tỉ số này.

   KHÔNG DÙNG `order`. Thứ tự nút là thứ tự nhân viên đặt trong repeater; lệch
   thứ tự Tab so với thứ tự thị giác là lỗi a11y thật. `flex-direction:row` và
   `column` giữ nguyên thứ tự tài liệu, `order` thì không.
   ============================================================ */

.vp-fab{
  position:fixed;
  right:var(--sp-4);
  bottom:var(--sp-4);
  z-index:calc(var(--z-header) - 1);
}

.vp-fab-list{
  display:flex;
  flex-direction:column;
  gap:var(--sp-2);
  margin:0;
  padding:0;
  list-style:none;
}

.vp-fab-item{ margin:0; }

/*
 * Ảnh của ô "Icon tải lên" (07/09/2026) đứng đúng chỗ <svg> 22×22 mặc định:
 * ép cỡ và `object-fit:contain` để ảnh vuông hay chữ nhật đều nằm gọn, không
 * kéo nút phình ra khỏi vòng tròn 44px.
 */
.vp-fab-ico-img{
  display:block;
  width:22px;
  height:22px;
  object-fit:contain;
}

.vp-fab-btn{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  min-width:44px;
  min-height:44px;
  padding:11px;
  border-radius:var(--r-full);
  background:var(--navy);
  color:var(--white);
  box-shadow:var(--sh-3);
  text-decoration:none;
  transition:background var(--dur-micro) var(--ease);
}

.vp-fab-btn:hover{ background:var(--navy-dark); }

/*
 * MÀU RIÊNG TỪNG KÊNH (26/08/2026, khách: "giữ đúng màu như web hiện tại
 * https://vinpack.vn/") — trước đây CẢ NĂM nút cùng nền --navy (chữ/icon
 * trắng đồng bộ). Bốn kênh dưới đè nền + màu icon bằng token --fab-* mới
 * (assets/css/global.css mục 2 — xem chú thích ở đó về màu gốc/ngoại lệ
 * tương phản). Kênh `email` KHÔNG có trong widget Chaty của web cũ (không
 * có màu để đối chiếu) — GIỮ NGUYÊN nền --navy mặc định của rule .vp-fab-btn
 * ở trên, không tự bịa màu.
 *
 * Độ ưu tiên: BẮT BUỘC viết compound `.vp-fab-btn.vp-fab-btn--call` (hai
 * class trên CÙNG một selector, không phải liệt kê hai selector rời) —
 * `.vp-fab-btn--call` một mình chỉ (0,1,0), HOÀ với `.vp-fab-btn` (0,1,0),
 * lúc đó thắng-thua rơi vào thứ tự dòng trong file (giòn, dễ vỡ khi có ai
 * chèn thêm rule ở giữa sau này). Compound hai class là (0,2,0), thắng chắc
 * chắn không phụ thuộc thứ tự — không cần !important.
 *
 * zalo/map đổi SANG NỀN TRẮNG (đúng web cũ — hai kênh đó không dùng nền
 * xanh) nên phải tự đổi `color` (icon dùng stroke="currentColor") sang màu
 * riêng, nếu không icon trắng-trên-trắng sẽ biến mất.
 */
.vp-fab-btn.vp-fab-btn--call{ background:var(--fab-call); }
.vp-fab-btn.vp-fab-btn--whatsapp{ background:var(--fab-whatsapp); }
.vp-fab-btn.vp-fab-btn--zalo{ background:var(--white); color:var(--fab-zalo); }
.vp-fab-btn.vp-fab-btn--map{ background:var(--white); color:var(--fab-map); }

/*
 * Hover riêng bốn kênh — PHẢI ghi lại `background` NGAY TRONG rule :hover
 * này (không thể dựa vào rule nghỉ ở trên): `.vp-fab-btn:hover` (0,2,0, hai
 * class rời — .vp-fab-btn + :hover) và `.vp-fab-btn.vp-fab-btn--call` lúc
 * nghỉ (0,2,0, compound hai class) HOÀ độ ưu tiên; lúc hover cả hai rule đều
 * khớp nên vẫn rơi vào thứ tự dòng — giòn y hệt vấn đề trên. Compound BA
 * thành phần `.vp-fab-btn.vp-fab-btn--call:hover` (0,3,0) mới thắng chắc
 * chắn. `filter:brightness(.92)` thay vì đặt bốn màu tối tay — tự hoạt động
 * đúng với BẤT KỲ nền nào trong bốn nền trên (2 xanh lá + 2 trắng).
 */
.vp-fab-btn.vp-fab-btn--call:hover{ background:var(--fab-call); filter:brightness(.92); }
.vp-fab-btn.vp-fab-btn--whatsapp:hover{ background:var(--fab-whatsapp); filter:brightness(.92); }
.vp-fab-btn.vp-fab-btn--zalo:hover{ background:var(--white); filter:brightness(.92); }
.vp-fab-btn.vp-fab-btn--map:hover{ background:var(--white); filter:brightness(.92); }

.vp-fab-ico{ display:inline-flex; }

/* ============================================================
   KHỐI ≤480px ĐÃ GỠ — KHÁCH YÊU CẦU CỘT DỌC (STT 13)
   ============================================================
   Ở đây TRƯỚC ĐÂY có một khối `@media (max-width:480px)` ép cụm nút nằm THÀNH
   HÀNG dưới đáy màn hình, kèm `body{ padding-bottom:72px }` để bù chiều cao
   dải đó. Lý lẽ ghi lại nguyên văn, vì nó KHÔNG sai và người sửa sau cần biết
   mình đang đánh đổi cái gì:

     "Cột dọc năm nút cao 44×5 + 8×4 = 252px và nằm sát mép phải. Trên 375px nó
      phủ lên đúng dải bên phải của .footer-legal và của nút gửi form."

   Khách xem bản dựng trên máy thật và chốt ngược lại: "4 nút liên hệ đang nằm
   ngang → xếp dọc". Đây là quyết định của khách trên một đánh đổi đã được nêu,
   không phải chúng ta quên mất rủi ro.

   HAI ĐIỀU ĐÃ ĐỔI SO VỚI LÚC LÝ LẼ CŨ ĐƯỢC VIẾT:
     - Nay là BỐN nút, không phải năm: 44×4 + 8×3 = 200px, thấp hơn 252px.
     - Cụm neo `bottom:var(--sp-4)` chứ không dính đáy, nên nó nổi trên nội
       dung theo đúng khuôn nút liên hệ nổi mà chính site cũ của khách dùng.

   Rủi ro CHE NỘI DUNG vẫn còn thật và đã đo lại sau khi đổi — kết quả ghi
   trong commit. Nếu sau này ai đó thấy nút che mất nút gửi form ở một trang
   mới, cách chữa ĐÚNG là chừa lề phải cho chính trang đó, KHÔNG phải trả cụm
   nút về nằm ngang: đó là thứ khách đã bác.

   Không còn khối media nào cho .vp-fab — cụm nút xếp dọc ở MỌI bề rộng, và
   `body` không còn padding-bottom bù ở bất kỳ khổ nào.
   ============================================================ */

/* ============================================================
   13. Dải chứng nhận
   ============================================================
   NỀN SÁNG, KHÔNG PHẢI NỀN TỐI. Dải nằm ngay trên .site-footer (--navy-dark).
   Nền sáng ở đây tự nó là ranh giới thị giác với chân trang, và thêm
   border-top để tách khỏi nội dung trắng phía trên — hợp đồng đòi có ranh
   giới, không để hai khối dính thành một.

   TƯƠNG PHẢN (mới, đo cả hai vế):
     --navy #001BB7 trên --bg-alt #EAF0FF = 10.00:1   (.vp-certs-title)
     --navy #001BB7 trên --white  #FFFFFF = 11.41:1   (.vp-cert-name, nền thẻ)
     --text #1A1A1A trên --white  #FFFFFF = 17.40:1   (.vp-cert-note)

   GHI CHÚ DÙNG --text, KHÔNG DÙNG --text-muted: #6B7280 trên #EAF0FF chỉ
   4.24:1 và trên trắng cũng chỉ 4.83:1 ở cỡ chữ nhỏ — đúng con số mà mục 3
   của test-css-cascade.php đã bắt một lần ở ô bảng lúc hover.

   .vp-certs-title KHÔNG bị giấu. Nó là <h2> thật và là tên của landmark qua
   aria-labelledby; muốn giấu thì phải dùng .screen-reader-text (đã có sẵn
   trong global.css), KHÔNG dùng display:none — thứ đó xoá luôn khỏi cây a11y.

   SỐ MỤC LÀ 1..n. auto-fit chứ không ghi cứng số cột.

   KHÔNG min-height THEO CHIỀU CAO ẢNH. .vp-cert--text (chứng nhận không có
   logo) và .vp-cert--logo dùng CHUNG một hộp; ép chiều cao theo ảnh sẽ làm
   mục chữ trông như một ô rỗng. Ảnh nằm TRONG hộp và tự co, không ngược lại.

   .vp-cert-img chỉ có max-width/max-height + height:auto + width:auto. Ghi
   cứng width hay height sẽ bóp méo logo của bên thứ ba — đó là vấn đề nhãn
   hiệu, không phải thẩm mỹ.
   ============================================================ */

.vp-certs{
  background:var(--bg-alt);
  border-top:1px solid var(--border);
  padding-top:var(--sp-6);
  padding-bottom:var(--sp-6);
}

.vp-certs-title{
  margin:0 0 var(--sp-5);
  font-size:20px;
  color:var(--navy);
}

.vp-certs-list{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(160px, 1fr));
  gap:var(--sp-3);
  margin:0;
  padding:0;
  list-style:none;
}

.vp-cert{
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:var(--sp-2);
  margin:0;
  padding:var(--sp-4);
  background:var(--white);
  border-radius:var(--r-card);
  text-align:center;
}

.vp-cert-logo{
  display:flex;
  align-items:center;
  justify-content:center;
}

.vp-cert-img{
  max-width:100%;
  max-height:48px;
  width:auto;
  height:auto;
}

.vp-cert-body{
  display:flex;
  flex-direction:column;
  gap:2px;
}

.vp-cert-name{
  font-family:var(--font-head);
  font-weight:700;
  font-size:14.5px;
  line-height:1.4;
  color:var(--navy);
}

.vp-cert-note{
  font-size:13px;
  line-height:1.6;
  color:var(--text);
}
