/* ============================================================
   HỮU TÍN — home.css
   Homepage-only styles (9 sections). Loaded after common.css.
   Section families: banner · m-markets · m-about · m-products
   · m-news · m-contact · m-ribbon
   ============================================================ */

/* =========================================================
   2 · BANNER — hero full-bleed Swiper (KHÔNG split)
   ========================================================= */
/* overflow hidden: chứa phần dịch chuyển parallax của .swiper (effects.js) */
/* Header (sticky, chiếm chỗ trong flow) + banner = ĐÚNG 1 màn hình người dùng.
   Chiều cao header thực tế = --header-h (64px, của .header__inner) + 1px
   border-bottom → phải trừ CẢ hai, nếu không trang dôi ra 1px và xuất hiện
   thanh cuộn dọc ngay khi chưa cuộn. Hằng số 80px cũ là của header nền tối đã bỏ.
   svh = viewport "nhỏ" trên mobile → thanh địa chỉ không cắt mất đáy banner;
   dòng vh phía trên là fallback cho trình duyệt chưa hỗ trợ svh. */
.banner{position:relative;height:calc(100vh - var(--header-h) - 1px);height:calc(100svh - var(--header-h) - 1px);min-height:480px;background:var(--ink);overflow:hidden;}
.banner .swiper,.banner .swiper-wrapper,.banner .swiper-slide{height:100%;}
.banner__slide{position:relative;height:100%;overflow:hidden;}
.banner__slide::before{
  content:"";position:absolute;inset:0;z-index:2;
  /* NHẸ HƠN NHIỀU nhưng chữ vẫn đọc rõ hơn trước — nhờ ĐỔI HÌNH DẠNG lớp phủ
     chứ không chỉ hạ số.
     Cũ: gradient 90deg nặng bên trái (di sản từ thời hero canh TRÁI) + một lớp
     scrim PHẲNG .28 trùm cả ảnh để vá phần mép phải chữ bị hụt. Lớp phẳng đó
     chính là thứ làm ảnh xỉn: nó dìm cả vùng sáng bên phải để bảo vệ chữ
     không nằm ở đó.
     Mới: tiêu đề nay canh GIỮA nên dùng vệt ellipse canh giữa, khu trú đúng
     khối chữ. Nhờ khu trú, phần còn lại của ảnh sáng lên rõ rệt.

     ĐỘ ĐẬM TÂM VỆT = .54 — ĐÃ LÀ SÀN, đừng hạ.
     Ngày 2026-07-27 tiêu đề banner BỎ text-shadow theo yêu cầu thiết kế (chữ
     phải sạch, không quầng mờ). Trước đó shadow gánh một phần tương phản nên
     tâm vệt chỉ cần .34; nay không còn gì bám theo nét chữ, TOÀN BỘ tương phản
     dồn về lớp phủ này → phải nâng .34 → .54 (đúng con số đã đo sẵn từ trước,
     xem đoạn "nếu KHÔNG có text-shadow" trong lịch sử file).
     Muốn hạ lại xuống .34 thì BẮT BUỘC trả text-shadow về .banner h1 — hai thứ
     đó là MỘT CẶP, không bỏ cả hai được.

     NGƯỠNG DO ẢNH SÁNG NHẤT QUYẾT ĐỊNH, KHÔNG PHẢI TRUNG BÌNH.
     banner-bg-05.jpg cũ là mấy cuộn dây trên nền TRẮNG; một mình nó ép lớp
     phủ phải ở .50. Ngày 2026-07-27 đã đổi sang ảnh ruy băng nền tối (bản sao
     của m-footer-ribbon-01.jpg) → slide 05 từ ảnh TỆ NHẤT thành AN TOÀN NHẤT.
     Cái chặn hiện nay là SLIDE 02, sát sau là 04 rồi 03. Muốn nhạt hơn nữa thì
     phải thay ảnh 02, chỉnh số ở đây là vô ích.

     SLIDE 01 ĐÃ ĐỔI ẢNH 2026-07-27 — lý do là ĐỘ PHÂN GIẢI, không phải tương phản.
     Bản cũ chỉ 644x448 mà phải trải full-bleed → phóng 4.06x trên màn retina,
     vỡ thấy rõ; tệ hơn nữa, đây đúng là ảnh preload/LCP nên là thứ khách nhìn
     đầu tiên. Thay bằng ảnh chồng cuộn ruy băng sa-tanh 2240x1260 → còn 1.29x.
     Nguồn: Pexels #5486793 (giấy phép Pexels: dùng thương mại được, không cần
     ghi nguồn, không có người nhận diện được trong ảnh). Ảnh mới SÁNG hơn bản cũ
     nhưng vẫn được 4.27:1 — dư sức, nên KHÔNG phải đụng tới lớp phủ.
     LƯU Ý: m-products-ruy-bang-01.jpg và product/huutin_sp_ruy-bang.jpg vẫn là
     ảnh 644x448 CŨ (3 file trước đây trùng md5 nhau). Cố ý giữ nguyên: ở khu sản
     phẩm chúng chỉ hiển thị <=386px nên không hề vỡ. Đừng "đồng bộ" chúng theo.

     ĐO LẠI 2026-07-27, ở tâm .54 và ĐÃ BỎ text-shadow. Mỗi ảnh lấy 5% pixel
     SÁNG NHẤT trong dải chữ (tình huống xấu nhất, KHÔNG phải trung bình):
                     khung 1280   khung 1440
        slide 01 ....  4.98:1      4.27:1     (ảnh mới, xem ghi chú cuối khối)
        slide 02 ....  3.44:1      3.26:1   ← CHẶN
        slide 03 ....  3.74:1      3.67:1
        slide 04 ....  3.63:1      3.41:1
        slide 05 .... 16.33:1     17.21:1

     PHẢI ĐO Ở KHUNG RỘNG, ĐỪNG ĐO Ở KHUNG HẸP. Khung càng rộng kết quả càng
     tệ: dải chữ có bề ngang CỐ ĐỊNH (833px, vì font-size đã kịch trần 62px)
     trong khi ellipse giãn theo bề ngang khung → hai đầu dòng chữ trôi dần ra
     vùng nhạt của vệt. Đo ở 1280 rồi yên tâm là TỰ LỪA MÌNH.
     Biên của slide 02 chỉ còn .26 trên chuẩn → hạ .54 là rớt ngay.
     (Tham chiếu: hồi CÒN shadow, tâm .34 cho slide 02 được 3.15:1.)

     Cách đo đúng: KHI CÒN text-shadow thì phải diff 2 lần render (có h1 / ẩn h1)
     rồi lấy vành pixel SÁT VIỀN NÉT, vì shadow chỉ tối quanh chữ — đo nền chung
     sẽ lệch tới 2 bậc. NAY ĐÃ BỎ shadow nên nền sát nét CHÍNH LÀ nền chung: chỉ
     cần siết vùng lấy mẫu vào đúng dải chữ (bề ngang lấy từ Range API của h1,
     bề dọc = cap-height + dấu tiếng Việt ≈ 0.95em) rồi lấy phân vị 95.
     Đo cả hộp h1 là SAI: hộp rộng hơn chữ nhiều, khoảng trắng hai bên kéo tụt
     kết quả xuống ~2.6:1 và làm tưởng nhầm là rớt chuẩn.
     WCAG AA chữ lớn = 3:1. */
  background:
    /* Cả ramp nhân ~1.59 để tâm đạt .54 mà GIỮ NGUYÊN hình dạng vệt: vẫn khu
       trú quanh khối chữ, mép ảnh vẫn về 0 → rìa ảnh sáng như cũ. */
    radial-gradient(ellipse 72% 44% at 50% 50%,
      rgba(18,19,20,.54) 0%,rgba(18,19,20,.43) 32%,
      rgba(18,19,20,.25) 60%,rgba(18,19,20,.08) 82%,rgba(18,19,20,0) 100%),
    /* chân ảnh: giữ cho pagination bullets còn đọc được */
    linear-gradient(0deg,rgba(18,19,20,.34),rgba(18,19,20,0) 45%),
    /* scrim phẳng chỉ còn .06 (ban đầu .28) — gần như chỉ để ảnh khỏi "cháy" */
    linear-gradient(0deg,rgba(18,19,20,.06),rgba(18,19,20,.06));
}
.banner__img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transform:scale(1.08);transition:transform 7s ease-out;}
.swiper-slide-active .banner__img{transform:scale(1);}
.banner__inner{
  position:absolute;inset:0;z-index:3;display:flex;align-items:center;justify-content:center;text-align:center;
}
/* max-width bỏ trần 660px: tiêu đề 1 dòng cần trọn bề ngang .container. */
.banner__content{max-width:none;margin-inline:auto;color:var(--white);}
/* Hero entrance — nhẹ, chạy khi tải trang, stagger theo từng dòng.
   Reduced-motion: kill-switch toàn cục (common.css) rút thời lượng còn ~0 → hiện ngay. */
.banner__content>*{animation:heroIn .55s var(--ease) both;}
@keyframes heroIn{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}

/* Các class reveal dùng chung (.reveal-word-wrap / .reveal-word /
   .reveal-cword / .reveal-char / .reveal-mask / .reveal-line) đã CHUYỂN sang
   common.css (2026-08-03): effects.js + hero-title.js chạy trên cả các trang
   con (ve-huu-tin, san-pham) — để ở đây thì trang con thiếu mask. */
/* JS present: GSAP owns these entrances — silence the CSS keyframe and hide
   the element until per-word states are set (prevents a full-text flash). */
html.js .banner h1.js-hero-title{animation:none;opacity:0;}
html.js [data-word-reveal]{opacity:0;}
/* ĐÃ GỠ .banner__eyebrow / .banner__sub / .banner__cta: banner chỉ còn ĐÚNG
   một dòng chữ (spec §3.3), ba phần tử kia đã không còn trong markup từ trước.
   Khớp banner trong "Web HuuTin.pdf": tiêu đề là MỘT khối chữ duy nhất —
   một font, một nét, một màu trắng. Từ 2026-08-04 font đó là SVN-Gotham 500
   (trước là FS Magistral 400) — xem ghi chú trong .banner h1 bên dưới. */
.banner h1{
  /* NGOẠI LỆ CÓ CHỦ Ý của thang --fs-* (common.css): tiêu đề này phải nằm gọn
     ĐÚNG MỘT DÒNG ở mọi khổ, nên cỡ chữ bám thẳng vw với hệ số 6 chứ không
     theo nấc nào; hero-title.js còn đo bề ngang dải chữ để canh mặt nạ reveal.
     Đưa nó về --fs-h1 là chữ rớt dòng và mặt nạ lệch. */
  /* 56px (trước 62px) — giảm cùng đợt với thang --fs-* ngày 2026-08-05. Tuy là
     ngoại lệ của thang, nó vẫn là TIÊU ĐỀ và là tiêu đề lớn nhất site: bỏ quên
     nó thì mọi tiêu đề khác lùi 5-8px còn hero đứng nguyên, tức chênh lệch giữa
     hero và phần còn lại DOÃNG RA thay vì thu lại như khách yêu cầu.
     Hệ số vw hạ 6 → 5.5 cho khớp: trần và hệ số phải giảm cùng nhau, nếu không
     thì ở các khổ giữa (dưới ~1030px) chữ vẫn to y như cũ vì chưa chạm trần.
     Giảm cỡ luôn AN TOÀN với ràng buộc white-space:nowrap — chỉ tăng mới có
     nguy cơ chạm mép .container. */
  /* 2026-09-07: từng nâng 56 → 68px cho Playfair, rồi trả về 56 / 5.5vw khi chốt
     Beautique Display (cap-height cao hơn Gotham 11%, không cần bù). Tiêu đề nay
     viết THƯỜNG nên chuỗi ngắn hơn bản in hoa cũ — nowrap càng dư chỗ. */
  /* +6px cùng đợt với --fs-h1 (2026-09-07 tối): trần 56 → 62, hệ số 5.5 → 6.1vw.
     Chuỗi viết thường ngắn hơn bản in hoa cũ nên nowrap vẫn dư chỗ (đo 1280:
     703px/1184px ở 68px thời Playfair, Beautique rộng hơn ~10%). */
  color:var(--white);font-size:clamp(22px,6.1vw,62px);
  /* ====== line-height LÀ SỐ ĐO, KHÔNG PHẢI THẨM MỸ — ĐÃ ĐO LẠI 2026-08-04 ====
     Hạ quá sàn thì half-leading thành ÂM (hộp dòng nhỏ hơn hộp mực của font),
     mà .reveal-word-wrap (mặt nạ GSAP) lại overflow:hidden → ngọn dấu bị XÉN
     THẬT, không phải chỉ sát mép.
     Đo bằng canvas measureText trên chính file font, cỡ 62px, với bộ ký tự xấu
     nhất (HỮU TÍN TỪ TÂM ĐẾN TẦM + ẾỀỄỆẤẦẪẬỐỒỖỘỚỜỠỢỨỪỮỰ) — tổng phần có mực:
        FS Magistral 400 ... 1.181em   (font CŨ của banner)
        SVN-Gotham   400 ... 1.238em
        SVN-Gotham   500 ... 1.279em   ← nét đang dùng
     --lh-tight = 1.38 nên còn dư .10em. Con số 1.279 này trùng khít "sàn tuyệt
     đối" đã ghi ở thang --lh-* trong common.css — hai phép đo độc lập ra cùng
     một chỗ, nên tin được.
     Tiêu đề chỉ 1 dòng (nowrap) nên line-height rộng KHÔNG tốn khoảng trắng
     thị giác — nó chỉ nới hộp mặt nạ ra. Đổi font/nét thì ĐO LẠI. */
  line-height:var(--lh-tight);
  /* +.01em: in hoa cần thở ra một chút, và spec §1.3 chặn trần đúng ở .01em
     (giãn hơn nữa thì dấu tách rời khỏi thân chữ, nhìn rời rạc). Không dùng
     -.02em của --ls-heading: âm tracking trên chữ hoa có dấu là quá chật. */
  letter-spacing:.01em;margin-bottom:22px;
  /* FS MAGISTRAL — khách chốt lại 2026-08-05 (vòng 6): "font chữ ở banner đổi
     thành font fs". Đây là CALL-SITE DUY NHẤT của --font-accent trên toàn site;
     mọi tiêu đề khác vẫn SVN-Gotham. Đừng rải nó ra chỗ khác — thang
     line-height trong common.css đo trên metric Gotham, còn dòng này có số đo
     riêng ngay phía trên.
     NÉT vẫn đọc --fw-heading (500) như mọi tiêu đề khác, và file font kéo về
     cũng đúng nét 500 — nên không có faux-weight. */
  /* 2026-09-07: --font-accent nay là alias của --font-heading = Beautique Display,
     nét --fw-h1 700 (banner là tiêu đề trang). ĐÃ ĐO LẠI cho font này: sàn
     Beautique 700 = 1.250em, --lh-tight 1.38 còn dư .13em — mặt nạ reveal không
     xén dấu. Bề rộng tương đương Gotham; nowrap giữ như cũ. */
  font-family:var(--font-accent);font-weight:var(--fw-h1);
  /* 2026-09-07: BỎ in hoa. Khách muốn tiêu đề viết thường theo nhịp web ref;
     chuỗi trong i18n.js (hero.title) cũng đã chuyển "Hữu Tín từ tâm đến tầm".
     Không còn text-transform nào trên h1–h4 toàn site. */
  /* ===== KHÔNG CÓ text-shadow — CỐ Ý =====
     Yêu cầu thiết kế 2026-07-27: tiêu đề phải là nét chữ sạch, không quầng mờ.
     Trước đây có 3 tầng shadow gánh tương phản, nhờ đó lớp phủ ảnh chỉ cần .34.
     Đã gỡ → toàn bộ phần tương phản đó chuyển sang lớp phủ ở .banner__slide::before
     (cả desktop lẫn @media mobile đều đã nâng .34 → .54).
     ĐỪNG thêm shadow lại mà không hạ lớp phủ về .34, và ngược lại — thêm cả hai
     sẽ làm ảnh banner tối gấp đôi mức cần thiết.
*/
  /* Tiêu đề chạy 1 dòng: khoá wrap, và font-size theo vw để luôn vừa bề ngang.
     6vw (trước 6.1) bù lại phần bề ngang tăng do cỡ chữ 56→62 và nét 400→500,
     để ở mọi bề ngang màn hình chữ vẫn không chạm mép .container. */
  white-space:nowrap;
}
/* Trên banner, .h-hl phải TÀNG HÌNH: kế thừa nguyên vẹn h1 để không lộ ra chỗ
   nối hai nửa câu. hero-title.js giữ nguyên class .h-hl khi cắt chữ nên
   selector này vẫn trúng.
   2026-08-05 đã gỡ `font-weight:inherit` khỏi đây: nó từng là bản vá chống lại
   rule trộn nét trong common.css, mà rule đó nay cũng trả về --fw-heading —
   toàn site một nét. Giữ lại vá thừa thì lần sau đọc ra như còn ngoại lệ.
   `white-space:nowrap` thì PHẢI giữ: tiêu đề banner chạy đúng một dòng và
   hero-title.js đo bề ngang từng từ. */
.banner h1 .h-hl{
  font-family:inherit;color:inherit;letter-spacing:inherit;
  white-space:nowrap;
}

/* ĐÃ GỠ: .banner__foot / .banner__pagination / .banner__count cùng keyframe
   bannerBar. Khách chốt BỎ dot pagination trong banner (spec §3.3), markup
   tương ứng đã xoá khỏi index.html nên toàn bộ khối này thành mã chết.
   Thanh chạy bannerBar còn ghim cứng 5s — tức nếu để lại, nó cũng đã lệch với
   nhịp autoplay 4s mới. Cần dot trở lại thì dựng mới theo nhịp đọc từ JS, đừng
   khôi phục bản cũ. */

/* =========================================================
   3 · MARKETS — 3 thẻ (May mặc / Giày dép / Bao bì)
   ========================================================= */
/* padding-top = đúng gap 4px của lưới 3 thẻ: khe hở giữa banner và cụm thẻ
   khớp với khe hở giữa các thẻ, cả 3 mảng đọc như một khối liền. */
.m-markets{background:var(--paper);padding-top:4px;padding-bottom:0;}
/* 3 cột đều, full-bleed sát nhau như reference. Section này CỐ TÌNH không có
   khối heading: mở thẳng vào lưới 3 thẻ, đúng bản dựng PDF. */
/* gap 3px = đúng khe hở đo được trên ref manubens.es (ô 425px cách nhau 3px). */
.m-markets__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:3px;}
.market-card{
  position:relative;display:flex;flex-direction:column;justify-content:flex-end;
  min-height:clamp(420px,31vw,480px);border-radius:var(--radius-lg);overflow:hidden;
  /* Nền sand thay cho đen: đây là màu LỘ RA lúc ảnh chưa tải và ở mép ô —
     chính là thứ tạo tông ấm đặc trưng của ref. */
  background:var(--sand);isolation:isolate;
}
.market-card__img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transition:transform .8s var(--ease);z-index:-2;}
.market-card::after{
  content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(0deg,rgba(18,19,20,.88) 0%,rgba(18,19,20,.35) 55%,rgba(18,19,20,.1) 100%);
  transition:background .4s var(--ease);
}
.market-card:hover .market-card__img{transform:scale(1.06);}
.market-card__body{position:relative;padding:30px 30px 12px;color:#fff;}
.market-card__num{font-family:var(--font-display);font-size:var(--fs-meta);color:var(--gold-light);letter-spacing:.1em;margin-bottom:auto;}
/* ---- ĐƯỜNG KẺ NGĂN TIÊU ĐỀ / MÔ TẢ (2026-08-19) ----
   Kẻ đặt trên .market-card p, KHÔNG phải ::after của h2: gạch của h2 chỉ dài
   bằng CHỮ tiêu đề, mà tiêu đề ba thẻ dài ngắn khác nhau ("Giày dép" vs
   "May mặc & Thời trang") → ba đường kẻ ba độ dài, đọc như lỗi. Neo vào p thì
   cả ba cùng một thước.
   ĐỘ DÀI: 40% cột chữ (khách chỉnh dần 2026-08-19: trọn cột 34ch → 66% → 50%
   → 40%). Đây là % của CỘT CHỮ, không phải của thẻ — nên ba thẻ luôn bằng nhau
   và ở khổ nào kẻ cũng giữ đúng tỉ lệ đó với đoạn mô tả bên dưới.
   Dùng ::before định vị tuyệt đối chứ KHÔNG dùng border-top nữa — border luôn
   dài bằng trọn hộp, không cắt ngắn được. Vì kẻ đã rời khỏi luồng, p phải
   position:relative.
   ĐỘ DÀY: 3px (khách chỉnh dần 2026-08-19: 1px → 2px → 3px).
   Trắng .28 alpha: còn thấy được trên mảng ảnh sáng nhất (ô bao bì) mà không
   cắt đôi bố cục như kẻ đặc.
   padding-top 17px = 3px kẻ + 14px thở, cho khe DƯỚI kẻ đúng bằng khe TRÊN
   (margin-bottom 14px của h2). Đổi độ dày thì phải chỉnh lại padding-top theo
   công thức đó, nếu không kẻ lệch về phía đoạn mô tả.
   Ràng buộc "sàn 12px" của margin-bottom h2 trước đây sinh ra từ gạch chân
   hover ở -8px; gạch đó nay đã gỡ (xem mục MICRO-INTERACTIONS) nên hết hiệu lực. */
/* 20px, KHÔNG phải 32px: đây là tiêu đề THẺ, phải nhỏ hơn tiêu đề section (32).
   Nét 500 (2026-07-27, trước là 400): chữ trắng trên ảnh tuy đã tách nền bằng
   tương phản, nhưng nét 400 đọc quá mảnh so với khối ảnh phía sau. 500 là mức
   cao nhất còn giữ đúng thứ bậc — bằng nét tiêu đề section, không vượt. */
/* +4px so với nấc --fs-h3 (khách 2026-09-07 tối: "May mặc… cho to hơn khoảng
   4px"). Ngoại lệ có chủ ý của thang: tiêu đề thẻ nằm trên ảnh tối cần nhỉnh hơn
   tiêu đề thẻ trên nền sáng; ghi bằng calc để vẫn co giãn theo token. */
.market-card h2{color:#fff;font-size:calc(var(--fs-h3) + 4px);margin-bottom:14px;}
.market-card p{font-size:var(--fs-body);color:#D0CEC9;line-height:var(--lh-body);max-width:34ch;margin-bottom:18px;
  position:relative;padding-top:17px;}
.market-card p::before{
  /* 40% → 30% (khách 2026-09-07: "line chỗ này ngắn lại chút"). */
  content:"";position:absolute;top:0;left:0;width:30%;height:3px;
  /* VÀNG (khách 2026-09-07: "line đổi thành màu vàng"), trước là trắng 28%.
     Nằm trên nền ảnh tối nên dùng --gold đặc; hover sáng lên --gold-light — đúng
     vai "vàng nhạt CHỈ trên nền tối" đã ghi ở token. */
  /* Nghỉ = vàng 55% (khách: "line khi chưa hover thì nhạt lại"), hover = vàng đủ. */
  background:color-mix(in srgb,var(--gold) 55%,transparent);
}

/* =========================================================
   4 · M-ABOUT — mốc logo 2002→2025 + giới thiệu + dấu "20 năm"
   Bố cục bám theo mockup "Web HuuTin.pdf" (artboard ~1786px):
   dải logo canh giữa · 2 cột chia bởi đường kẻ dọc · con dấu góc phải.
   ========================================================= */
.m-about{background:linear-gradient(to bottom,var(--paper) 0%,var(--white) 100%);position:relative;overflow:hidden;}
/* Hình phác nhà xưởng dưới nền. inset đẩy xuống 160px và bù -160px ở đáy để
   giữ NGUYÊN chiều cao — nếu chỉ đặt top:160px thì khung thấp đi và `cover`
   sẽ phóng ảnh to lên chứ không phải dịch chuyển. .m-about có overflow:hidden
   nên phần thò ra dưới bị cắt gọn. */
.m-about::before{
  content:"";position:absolute;inset:160px 0 -160px 0;z-index:0;pointer-events:none;
  background:url(../images/bg-sktech.png?v=b51ca840) center/cover no-repeat;
  opacity:.07;
}
.m-about .container{position:relative;z-index:1;max-width:var(--maxw-wide);padding-inline:var(--pad-wide);}

/* ---- Dải logo qua các thời kỳ ---------------------------------------- */
/* 4 file PNG được cắt cùng một khung dọc (170px) nên chỉ cần set chung
   `height` là 4 logo tự canh đúng đỉnh/đáy như bản thiết kế. */
.m-about__eras{
  display:flex;flex-wrap:wrap;justify-content:center;align-items:flex-start;
  gap:clamp(30px,11.8vw,206px);
  margin-bottom:clamp(44px,5.6vw,72px);
}
.era{display:flex;flex-direction:column;align-items:center;}
.era__mark{height:clamp(74px,6.9vw,123px);width:auto;}
.era__year{
  margin-top:clamp(10px,1.1vw,18px);
  /* Bold (khách 2026-09-07: "bold năm") — AlmarenaVN Display Bold, file thật. */
  font-family:var(--font-body);font-size:var(--fs-lead);font-weight:var(--fw-bold);
  letter-spacing:.02em;color:var(--gray-500);
}

/* ---- Hai cột: nhãn + tên công ty | đoạn giới thiệu -------------------- */
/* Cột trái hơi rộng hơn tỉ lệ mockup (.35) — container ở đây là 1440 chứ không
   phải 1561 như artboard, nên "CÔNG TY TNHH SX - TM" cần thêm chỗ để không rớt
   dòng khi SVN-Gotham chưa tải xong và trình duyệt dùng font dự phòng. */
/* align-items:center — HAI CỤM CANH GIỮA THEO NHAU (khách chốt 2026-08-04).
   Trước là start + một padding-top 46-100px chép tay ở cột trái để "dìm" nó
   xuống cho ngang tầm cột phải: con số đó chỉ đúng ở đúng một độ dài đoạn văn,
   sửa chữ là lệch ngay. Nay để lưới tự canh, và GỠ cả hai padding-top bù trừ
   ở .m-about__lead lẫn .m-about__body — chúng chống lại việc canh giữa. */
.m-about__cols{display:grid;grid-template-columns:.36fr .64fr;align-items:center;}
.m-about__lead{text-align:right;padding-right:clamp(18px,2.2vw,38px);}
.m-about__kicker{font-size:var(--fs-lead);color:var(--gray-500);margin-bottom:clamp(14px,1.4vw,24px);}
/* ===== THANG TIÊU ĐỀ — ĐỌC CẢ CỤM TRƯỚC KHI SỬA MỘT DÒNG =====
   Từ 2026-08-04 các con số KHÔNG còn nằm ở đây nữa: mọi cỡ đọc từ thang
   --fs-* trong common.css, mọi nét đọc từ --fw-*. Bảng dưới chỉ ghi lại giá
   trị THỰC TẾ ở khổ 1440px để kiểm tra thứ bậc — muốn đổi thì đổi ở token.

     hero h1 .......... 62px / 400  (--fs riêng, FS Magistral — xem .banner h1)
     SECTION h2 ....... 38px / 500  (--fs-h2)  ← lớn nhất sau hero
     thẻ trên ảnh h3 .. 21px / 500  (--fs-h3, market-card)
     band h2 .......... 20px / 400  (pband__title — cỡ riêng, nét nhẹ có chủ ý)
     thẻ tin h3 ....... 21px / 500  (--fs-h3)
     thẻ sp h4 ........ 17px / 500  (--fs-h4)
     body ............. 17px / 400  (--fs-body)

   QUY TẮC: tiêu đề section PHẢI lớn hơn mọi tiêu đề thẻ. Trước đây thẻ
   market-card để 32px còn section 24px → thẻ to hơn mục chứa nó, thứ bậc đảo
   ngược. Đổi một token thì phải kiểm lại cả cụm này.
   Nét 500 (không phải 400) để tiêu đề tách khỏi đoạn văn cùng nằm nền sáng —
   nay là MẶC ĐỊNH của h1-h4 nên không selector nào phải khai lại. */
.m-about__lead h2{font-size:var(--fs-h2);line-height:var(--lh-heading);text-wrap:balance;}

.m-about__body{
  /* VÀNG (khách 2026-09-07: "line những chỗ này đổi thành màu vàng"), trước là đen 20%. */
  border-left:3px solid var(--gold);
  /* KHÔNG padding-top: xem chú thích align-items:center ở .m-about__cols. */
  padding-left:clamp(20px,2.1vw,37px);
}
.m-about__body p{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--gray-700);text-align:justify;}
.m-about__body p b{font-weight:var(--fw-bold);color:var(--ink);}
.m-about__more{
  display:inline-flex;align-items:center;
  /* BỐ CỤC RỘNG (min-width + space-between + gap) KHÔNG khai ở đây — nó nằm
     chung với .btn--wide trong common.css. Đừng chép lại xuống đây.
     Cao/đệm/kiểu chữ lấy từ thang nút chung; trước là 50px, 16px, nét 400,
     KHÔNG in hoa — lệch chuẩn ở cả bốn thông số cùng lúc. */
  margin-top:clamp(24px,3vw,49px);
  height:var(--btn-h);
  /* padding-bottom = --cap-nudge bù lệch quang học của SVN-Gotham, giống hệt
     .btn. Nút này không mang class .btn nên không tự nhận — xem chú thích đầy
     đủ tại token trong common.css. */
  padding:var(--cap-nudge-top) var(--btn-pad-x) var(--cap-nudge);
  /* Bo nhẹ như mọi nút chữ khác (khách chốt 2026-08-05, trước là pill 999px).
     Nút này không mang class .btn nên không tự nhận bo góc từ đó — phải khai
     riêng, và phải đổi CÙNG LÚC với .btn kẻo hai nút lệch nhau. */
  border-radius:var(--radius);
  /* NỀN XANH (khách chốt 2026-08-05). Trước là xám #D8D9DB lấy từ mockup, và
     màu xanh chỉ xuất hiện khi hover — tức trạng thái nghỉ của CTA chính trên
     trang chủ không có tín hiệu hành động nào. Nay dùng đúng token accent của
     site; hover đẩy sâu thêm một bậc sang --gold-deep. */
  /* Chữ ĐEN trên mảng vàng (2026-08-19) — trắng trên #D3AF37 chỉ 2.11:1. */
  /* 2026-09-07: gradient vàng (token --gold-grad). Hover không đổi background
     nữa — gradient không transition được — mà mờ dần lớp ::after xám đen. */
  background:var(--gold-grad);color:var(--btn-ink);
  position:relative;isolation:isolate;overflow:hidden;
  font-family:var(--font-display);font-weight:var(--btn-fw);
  font-size:var(--btn-fs);letter-spacing:var(--btn-ls);text-transform:uppercase;
  line-height:1;
  transition:color .25s var(--ease);
}
.m-about__more::after{content:"";position:absolute;inset:0;z-index:-1;background:var(--btn-grad-dark);opacity:0;transition:opacity .25s var(--ease);pointer-events:none;}
/* Hover đậm thêm một bậc trong CÙNG hệ accent (--gold → --gold-deep), giống
   cách .btn hover. Nền nghỉ nay đã là xanh nên hover không còn là "đổi màu"
   mà là "đậm lên" — vẫn đủ thấy (5.04:1 → 6.95:1) mà không nhảy sang màu khác.
   (Mũi tên trong nút và cú trượt 4px khi hover đã gỡ 2026-08-04 cùng đợt bỏ
   icon khỏi mọi CTA; đổi nền là tín hiệu hover duy nhất còn lại.) */
.m-about__more:hover{color:var(--white);}
.m-about__more:hover::after{opacity:1;}

/* ---- Con dấu "20 NĂM" góc dưới bên phải ------------------------------- */
.m-about__seal{
  position:relative;display:flex;flex-direction:column;align-items:flex-end;
  margin-top:clamp(26px,3.5vw,58px);padding-right:clamp(14px,1.7vw,30px);
}
/* Thanh dọc cạnh "20+ NĂM" — XÁM. Vòng 6 (2026-08-05, cùng ngày) khách đảo lại
   quyết định vài giờ trước đó là cho nó màu xanh: trong section này chỉ CTA
   "ĐỌC THÊM" giữ accent, thanh dọc lùi về vai đường kẻ.
   ĐỪNG đổi lại sang var(--gold) theo comment cũ — CTA mang accent + thanh cũng
   mang accent là trạng thái đã bị khách bác bỏ (hồi đó accent là navy, nay là
   vàng metallic; quyết định "thanh dọc lùi về vai đường kẻ" không đổi theo màu).
   alpha chứ không hex đặc: nền section có ảnh nét mờ phía sau, để thanh ăn
   theo nền thì nó chìm đúng mức của một đường kẻ. */
.m-about__seal::after{
  content:"";position:absolute;right:0;top:0;bottom:0;
  /* Vàng cùng đợt với vạch dọc .m-about__body (2026-09-07), trước là đen 18%. */
  width:clamp(6px,.62vw,11px);background:var(--gold);
}
.seal__num{
  font-family:var(--font-display);font-weight:var(--fw-bold);text-transform:uppercase;
  font-size:var(--fs-display);line-height:1;letter-spacing:-.02em;
  /* Đục 100%, không còn là watermark mờ như mockup — theo yêu cầu của khách. */
  color:var(--ink);
}
/* Cả cụm canh phải, nên khi đếm 0→20 chữ số nhảy từ 1 lên 2 ký tự sẽ đẩy
   "NĂM" giật sang trái. Khoá bề ngang đúng 2 chữ số + tabular-nums (mọi
   chữ số cùng bề ngang) để khối đứng yên suốt lúc chạy. */
.seal__num .value{
  display:inline-block;min-width:2ch;text-align:right;
  font-variant-numeric:lining-nums tabular-nums;   /* lining: SVN-Georgia mặc định số kiểu cũ (2026-09-22) */
}
/* Dấu "+" dạng số mũ (khách chốt 2026-08-05).
   DÙNG position/top CHỨ KHÔNG vertical-align:super — `super` được tính vào hộp
   dòng, nên nó sẽ nong line-height của cả dòng "20+ NĂM" ra và đẩy .seal__cap
   xuống; ở cỡ --fs-display (78px) sai lệch đó là hàng chục px. position:relative
   nhấc glyph lên mà không đụng tới dòng.
   Nâng .52em (tính theo cỡ CỦA CHÍNH NÓ, tức ~20px ở khổ 1440) để đỉnh dấu "+"
   xấp xỉ đỉnh chữ số — không kéo cao hơn, vì "+" cân theo trục toán học chứ
   không theo cap-height, đẩy quá là nó lơ lửng khỏi khối số. */
.seal__plus{
  font-size:.5em;
  position:relative;top:-.52em;
  vertical-align:baseline;
  /* Dính sát chữ số: letter-spacing âm của .seal__num tính theo cỡ chữ LỚN nên
     ở đây thành khe hở quá rộng so với cỡ chữ nhỏ. Trả về 0. */
  letter-spacing:0;
  /* VÀNG (khách 2026-09-07) — dấu "+" là điểm nhấn duy nhất mang accent trong
     con dấu; "20 NĂM" vẫn --ink. */
  color:var(--gold);
}
/* Không kéo âm sang phải: cả "20 NĂM" lẫn dòng này cùng dừng ở mép trong của
   thanh dọc, cách ra đúng padding-right của .m-about__seal. Trước đây margin
   âm đẩy chữ chui vào thanh nên "NGÀNH" bị dính vạch. */
.seal__cap{
  /* 2026-09-07 khách: "gần lên trên chút, to và bold đậm hơn chút (khoảng semi)".
     Khoảng cách 8–15 → 4–8px; cỡ --fs-micro 12 → --fs-meta 13px; nét: bộ
     AlmarenaVN không có 500/600, nấc đậm hơn Light duy nhất là Display Bold 700 —
     dùng 700 (khai 600 cũng ra file này). Giãn chữ giảm .11 → .09em vì nét đậm
     hơn đã tự chiếm chỗ. */
  margin-top:clamp(4px,.5vw,8px);
  /* Khách xem 700 thấy "bold quá" → về 400 (file Light) và làm dày nét bằng
     -webkit-text-stroke .3px cùng màu: cách duy nhất ra "khoảng semi" khi bộ
     AlmarenaVN không có 500/600. 0.3px ở 13px là ngưỡng chưa lem; đừng tăng. */
  font-size:var(--fs-meta);font-weight:var(--fw-body);-webkit-text-stroke:.3px currentColor;
  letter-spacing:.11em;text-transform:uppercase;
  color:var(--ink);
}

/* =========================================================
   5 · M-PRODUCTS — dải ảnh + thẻ dấu dệt + nửa vòng danh mục
   Dựng theo bản PDF của khách (đo lại ở khổ 1440px):
     · dải 2 ảnh tràn viền, thẻ nổi đè lên đáy dải
     · tên danh mục đang chọn cỡ lớn, canh giữa
     · vòng tròn có TÂM nằm đúng mép dưới section → nửa dưới bị
       .m-products{overflow:hidden} cắt, chỉ còn nửa trên
   Mọi kích thước hình học suy ra từ --pw-r (bán kính vành ngoài,
   230px ở khổ 1440) nên tỉ lệ giữ nguyên ở mọi bề rộng màn hình.
   ========================================================= */
/* Padding-block rút gọn cho 3 section: giới thiệu · tin tức · liên hệ
   (~100–120px), ghi đè .section-pad (clamp 72–140px).
   m-products KHÔNG nằm trong nhóm này: dải ảnh phải chạm mép trên và
   mép dưới section phải trùng tâm đường tròn, nên padding-block = 0. */
.m-about.section-pad{padding-block:clamp(64px,8vw,112px);}
/* Tin tức + Liên hệ thở hẹp hơn ~18% so với Giới thiệu: hai section này là
   LƯỚI THẺ và LƯỚI FORM — bản thân nội dung đã có khoảng trắng nội tại (gap
   giữa thẻ, gap giữa các field), nên giữ nguyên 112px làm cả cụm trôi ra xa
   tiêu đề. Giới thiệu thì ngược lại: hai cột chữ đặc, cần đệm rộng để không
   bị dải logo phía trên ép. Tỉ lệ 6.6/8 giữ nguyên ở MỌI bề rộng, nhịp dọc
   vì thế vẫn co giãn đồng bộ với các section còn lại. */
.m-news.section-pad,
.m-contact.section-pad{padding-block:clamp(52px,6.6vw,92px);}
/* Đáy Liên hệ cắt tiếp còn ~nửa: .m-contact và .m-ribbon ĐỀU nền trắng tinh,
   không có ranh giới màu nào giữa hai section, nên đệm đáy 84px của Liên hệ
   CỘNG DỒN với đệm đỉnh 72px của Ribbon thành một mảng trắng liền 156px —
   mắt đọc ra là trang bị đứt đoạn chứ không phải hai khối tách nhau.
   Đo ở 1280: 156px → 115px. Muốn hẹp nữa thì sửa .m-ribbon{padding-block}
   (72px) chứ đừng hạ tiếp con số này — dưới ~40px thì nút gửi form bắt đầu
   dính vào ảnh ribbon.
   KHÔNG áp cho .m-news: section đó nền xám --gray-100, mép dưới là ranh giới
   màu thật, đệm ở đó có việc để làm. */
.m-contact.section-pad{padding-bottom:clamp(28px,3.4vw,48px);}
/* Hình chìm sau section sản phẩm.
   ẢNH RIÊNG, KHÔNG DÙNG CHUNG với section giới thiệu: hai section liền nhau mà
   cùng một ảnh thì lúc cuộn qua nó lặp lại y hệt, đọc ra là lỗi lát nền chứ
   không phải hoạ tiết.
   bg-sketch-store-01.jpg — nội thất CỬA HÀNG THỜI TRANG dựng bằng nét (khách
   chốt 2026-08-19, thay ảnh nhà máy gửi trước đó ít phút). Hợp section này hơn
   hẳn: đây là chỗ nói SẢN PHẨM ĐI TỚI ĐÂU, còn ảnh nhà máy thì trùng vai với
   .m-about (chỗ nói về năng lực sản xuất).

   ĐỘ MỜ .04, KHÔNG phải .07 như .m-about (khách: "giảm opacity xuống thêm
   nữa"). Không chỉ vì bị chê đậm — hai ảnh có MẬT ĐỘ NÉT khác hẳn: bg-sktech.png
   là phác thảo thưa, còn ảnh này kín nét (giá kệ, móc treo, đèn trần), nên ở
   cùng .07 nó cho ra mảng xám đậm hơn thấy rõ. Cùng một con số opacity KHÔNG
   có nghĩa là cùng một sức nặng thị giác.
   z-index:0 + các lớp con nâng lên z-index:1: .m-products vốn đã có
   overflow:hidden (để cắt nửa dưới vòng xoay) nên phần ảnh thò ra tự bị cắt,
   không cần thêm gì.
   ⚠️ KHÔNG đặt ảnh này lên chính .m-products bằng `background`: nền của section
   là var(--white) đặc, và dải ảnh .pband nằm sát mép trên — ảnh nền sẽ lộ ra ở
   khe giữa hai khối ảnh. Lớp ::before riêng cho phép chỉnh inset độc lập. */
.m-products::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:url(../images/bg-sketch-store-01.jpg?v=332b3ed3) center/cover no-repeat;
  opacity:.04;
}
/* Mọi khối nội dung phải nằm TRÊN hình chìm. .pband và .pwheel-wrap đều là con
   trực tiếp của section. */
.m-products > *{position:relative;z-index:1;}
.m-products{
  --pw-r:clamp(158px,15.97vw,230px);
  /* Số slot hiện MỖI BÊN mục đang chọn. CSS giữ quyền quyết định theo bề
     ngang (xem media query cuối file); product-wheel.js chỉ đọc con số này
     rồi làm mờ hẳn các slot ngoài tầm. */
  --pw-slots:2;
  /* Nhịp nháy mờ của cụm MÔ TẢ quanh mốc thay chữ (0.42·DURATION).
     Từ vòng 6 token này CHỈ còn phục vụ .pwheel__desc: dải ảnh không mờ chéo
     nữa mà TRƯỢT, và cú trượt đọc nhịp từ --pw-slide (product-wheel.js ghi
     xuống từ hằng DURATION). Đừng gán --pw-swap cho ảnh trở lại — hai chuyển
     động nay có bản chất khác nhau, ép chung một con số là sai chứ không phải
     đồng bộ. */
  /* .40s — đi kèm DURATION 1700 (đã lùi từ 2000 sau feedback "chậm quá").
     Ghi chú gốc:
     Với expo-out, mốc thay chữ SWAP_AT .36 rơi vào lúc vòng đã đi ~92% quãng
     đường. Đường cong mới đi đều hơn nên cùng mốc đó vòng mới đi được ~19% —
     chữ đổi khi vòng còn đang bay, đọc ra như hai chuyển động rời nhau. Nên
     SWAP_AT dời về .46 (product-wheel.js), và để khoảng TÀNG HÌNH
     (SWAP_AT·DURATION − --pw-swap = 782 − 400 = 382ms) không phình theo thì
     token này phải nới cùng. Ba số ràng nhau, đổi một là kiểm lại hai số kia. */
  --pw-swap:.40s;
  background:var(--bg);position:relative;overflow:hidden;padding-block:0;
}

/* ---- Dải 2 ảnh tràn viền + thẻ dấu dệt --------------------------- */
.pband{position:relative;}
/* ===== BĂNG CHUYỀN NGANG (khách chốt 2026-08-05, vòng 6) ==================
   "đổi hiệu ứng ở hình ảnh thành kiểu kéo ảnh ngang qua… trượt cả dải như một
   băng chuyền". Ảnh phải đi XUYÊN QUA khe 3px giữa hai ô.

   Điểm khác cốt lõi so với hai bản trượt bị loại ở vòng 4 ("ảnh chạy xuyên
   khung, vỡ bố cục"): TẦNG CẮT nay là .pband__view, một khung nhìn DUY NHẤT
   bao cả dải. Trước đây mỗi ô tự overflow:hidden nên ảnh trượt sẽ thò ra ngoài
   ô rồi biến mất giữa dải — đúng cái bị chê. Nay không gì ra khỏi được dải, và
   khe 3px nằm BÊN TRONG từng khung nên nó trượt theo ảnh, đúng nghĩa băng
   chuyền.
   Khe 3px vẫn là CÙNG một con số với .m-markets__grid và marquee (đo từ ref
   manubens.es) — đổi thì đổi cả ba cho khỏi lệch nhịp. */
/* Cao thêm 10% so với bản 150/20.35vw/293 (khách chốt 2026-09-07). Ba số nhân
   đều 1.1 để nhịp co giãn theo khổ màn giữ nguyên. */
.pband__view{
  position:relative;overflow:hidden;
  height:clamp(165px,22.4vw,322px);background:var(--paper);
}
/* Xếp CHỒNG tuyệt đối rồi đẩy bằng translateX(k*100%), KHÔNG dùng một track
   flex dài: với vòng lặp 5 mục, mỗi bước có đúng một khung phải nhảy vòng từ
   -200% sang +200%. Track flex sẽ buộc phải chép thẻ hoặc xoay DOM
   (appendChild) như news.js; xếp chồng thì chỉ cần ghi lại --k.
   --k = khoảng cách slot CÓ DẤU tới mục đang chọn, do product-wheel.js ghi
   bằng chính hàm shortest() mà các nhãn đang dùng — nhờ vậy ảnh và nhãn không
   bao giờ trượt ngược hướng nhau.
   Giá trị --k ban đầu nằm INLINE trong HTML: JS chết thì dải vẫn hiện đúng mục
   mặc định thay vì chồng cả 5 khung lên nhau. */
.pband__frame{
  position:absolute;inset:0;
  /* Khai HÀNG là bắt buộc, không phải trang trí: khung có chiều cao xác định
     (inset:0) nhưng hàng ngầm định của grid là `auto` nên nó co theo NỘI DUNG.
     Ảnh khai height:100% mà hàng lại auto → không giải được, trình duyệt rơi
     về kích thước tự nhiên của ảnh (đo được 719px trong khung cao 293px) và
     ảnh tràn ra ngoài.
     minmax(0,1fr) chứ KHÔNG phải 1fr trần: `1fr` nghĩa là `minmax(auto,1fr)`,
     mà sàn `auto` của một ô lưới lấy theo kích thước nội tại của nội dung —
     ảnh gốc ở đây là 1344×896. Tức chính cái bẫy vừa làm hỏng chiều cao vẫn
     còn cửa quay lại qua đường sàn. minmax(0,…) đóng hẳn cửa đó ở CẢ HAI
     chiều. */
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:minmax(0,1fr);gap:3px;
  /* * -100% (khách 2026-09-22: "đổi chiều HÌNH, vòng giữ nguyên"): dải ảnh và
     dải thẻ trượt NGƯỢC chiều với vòng quay; --k vẫn do product-wheel.js ghi. */
  transform:translateX(calc(var(--k,0) * -100%));
  /* --pw-slide do product-wheel.js ghi từ hằng DURATION (nguồn sự thật của cả
     vòng quay) nên ảnh và vòng luôn cùng nhịp; 1700ms chỉ là số dự phòng khi
     JS chưa chạy.
     --ease-glide chứ KHÔNG --ease (đổi 2026-08-19): expo-out đưa dải ảnh đi
     hết nửa quãng trong ~10% thời gian đầu — đó là cú "giật" mà khách chỉ ra ở
     băng tin, và dải ảnh này mắc đúng bệnh ấy. product-wheel.js chép cùng
     đường cong bằng JS cho vòng chữ, hai bên phải luôn khớp. */
  transition:transform var(--pw-slide,1700ms) var(--ease-glide);
}
/* Khung phải NHẢY VÒNG (từ -200% sang +200% hoặc ngược lại) thì cấm chuyển
   cảnh — để nguyên nó sẽ lướt ngang qua khung nhìn một lần nữa, ngược chiều
   với cả dải. JS gắn/gỡ class này quanh một lần ép reflow. */
.pband__frame.is-jumping{transition:none;}
/* Khung có data-cat không khớp danh mục nào (xem paintBand). Ẩn hẳn thay vì để
   nó nằm ở --k:0 che mất khung đang hiện — thiếu ảnh dễ thấy hơn sai ảnh. */
.pband__frame.is-orphan{display:none;}
.pband__img{width:100%;height:100%;object-fit:cover;display:block;}

@media (prefers-reduced-motion:reduce){
  .pband__frame{transition:none;}
}

/* Thẻ đè lên đáy dải ảnh rồi thò xuống nền trắng bên dưới.
   Canh giữa bằng left/right/margin-auto chứ KHÔNG bằng translateX(-50%):
   thẻ mang data-aos, mà [data-aos] trong common.css tự đặt transform để
   chạy hiệu ứng hiện lên — đặt transform ở đây sẽ bị đè và thẻ lệch hẳn
   sang phải nửa chiều rộng. */
/* KHUNG CẮT + DẢI THẺ — thẻ trắng nay là MỘT DẢI đi song song với dải ảnh
   (khách 2026-09-07: "phải gom 2 cái đó thành 1 cụm rồi cho nó đi cùng nhau
   chứ không phải đi riêng như hiện tại").

   Cách làm: mỗi danh mục có MỘT thẻ riêng, xếp chồng trong cùng một ô lưới của
   .pband__cardslot, và mỗi thẻ đọc CHÍNH BIẾN --k mà product-wheel.js ghi cho
   khung ảnh cùng danh mục — cùng công thức translateX, cùng thời lượng, cùng
   đường cong. Hai dải vì thế không thể lệch pha: chúng dùng chung một nguồn.

   Bản trước là MỘT thẻ tự trượt ra rồi vào lại bằng --pw-card-dist/-dur; dù đã
   chỉnh cho cùng tốc độ, nó vẫn là hai chuyển động riêng nên khách nhìn ra ngay.

   display:grid + grid-area:1/1 cho mọi thẻ: chồng khít nhau mà chiều cao khung
   vẫn bằng thẻ CAO NHẤT, không cần đo bằng JS.
   overflow:hidden cắt phần thẻ trượt ra ngoài; padding-bottom 70px chừa chỗ cho
   bóng đổ (~54px) khỏi bị cắt cụt, và `bottom` lùi thêm đúng 70px để thẻ không
   xê vị trí. Hai số này phải đổi cùng nhau. */
.pband__cardslot{
  position:absolute;left:0;right:0;
  bottom:calc(clamp(28px,4.44vw,64px) * -1 - 70px);
  padding-bottom:70px;z-index:2;overflow:hidden;
  display:grid;
}
.pband__cardframe{
  grid-area:1/1;display:flex;justify-content:center;
  /* * -100% (khách 2026-09-22: "đổi chiều HÌNH, vòng giữ nguyên"): dải ảnh và
     dải thẻ trượt NGƯỢC chiều với vòng quay; --k vẫn do product-wheel.js ghi. */
  transform:translateX(calc(var(--k,0) * -100%));
  transition:transform var(--pw-slide,1700ms) var(--ease-glide);
}
/* Cùng lý do với .pband__frame.is-jumping: khung nhảy từ rìa này sang rìa kia
   thì cấm chuyển cảnh, nếu không nó lướt ngang qua khung nhìn ngược chiều. */
.pband__cardframe.is-jumping{transition:none;}
@media (prefers-reduced-motion:reduce){
  .pband__cardframe{transition:none;}
}
.pband__card{
  position:relative;margin-inline:auto;
  /* #EFF1F3 cứng → var(--gray-100): cùng họ nền xám nhạt, đổi cùng token (2026-09-07). */
  /* 76vw → 64vw ở điện thoại (khách 2026-09-08: "cụm trắng ở giữa quá rộng").
     Trần 343px vẫn giữ nguyên nên desktop và tablet không đổi gì; chỉ khổ hẹp
     mới rơi vào nhánh vw. Ở 390px: 297 → 250px, thẻ tách hẳn khỏi hai mép ảnh
     hai bên thay vì gần chạm. */
  width:min(343px,64vw);text-align:center;background:var(--gray-100);
  padding:clamp(13px,1.25vw,18px) clamp(16px,1.7vw,24px) clamp(13px,1.25vw,18px);
  box-shadow:0 26px 46px -18px rgba(45,45,45,.42);
}
/* 5 dấu chồng KHÍT lên nhau trong một ô vuông cố định — nếu để chúng nối tiếp
   trong luồng thì mỗi lần đổi mục chiều cao thẻ lại nhảy. aspect-ratio thay cho
   height cố định vì cả bộ đều là SVG vuông 64×64. */
.pband__marks{
  position:relative;width:clamp(44px,4.5vw,65px);aspect-ratio:1;
  margin:0 auto clamp(8px,.9vw,13px);
}
.pband__mark{position:absolute;inset:0;width:100%;height:100%;opacity:0;}
.pband__mark.is-on{opacity:1;}
.pband__title{
  /* Nét 400 KHAI TƯỜNG MINH. Từ 2026-08-04 (chiều) nét mặc định của h1-h4 đã
     quay lại 400 theo spec §1.3, nên khai báo này TRÙNG với mặc định — vẫn giữ
     vì nó ghi lại CHỦ Ý: dải nhãn trên ảnh phải nhẹ hơn tiêu đề section, nên
     nếu sau này mặc định lại đổi thì chỗ này không được đổi theo.
     Cụm nhấn <span class="h-hl"> bên trong vẫn lên 700 theo rule chung.
     Cỡ chữ cũng là NGOẠI LỆ có chủ ý: canh theo dải ảnh có kích thước cố định
     nên không đọc --fs-*; xem chú thích thang cỡ chữ trong common.css. */
  margin:0;
  font-size:clamp(13px,1.45vw,20px);line-height:var(--lh-heading);
  letter-spacing:var(--ls-heading);color:#525457;
}
/* Cỡ chữ NGOẠI LỆ như .pband__title ngay trên: cả cặp canh theo dải ảnh có
   kích thước cố định, phải co giãn cùng một nhịp với nhau chứ không theo thang
   nội dung. */
.pband__sub{
  margin:clamp(6px,.75vw,11px) 0 0;
  /* Không phải tiêu đề, nhưng vướng đúng ngưỡng font đó: 1.22 cắt mất dấu nặng
     của "bởi giá trị". Xem phần dẫn giải ở --lh-heading (common.css). */
  font-size:clamp(11.5px,1.18vw,17px);line-height:var(--lh-heading);color:#5B5D60;
}

/* ---- Nửa vòng chọn danh mục -------------------------------------- */
/* Section cao thêm ~180px so với bản bám PDF nguyên bản, để trên vành có
   chỗ thật cho MÔ TẢ + NÚT của mục đang chọn. Bản dựng PDF không có hai
   thứ đó nên section chỉ là "vòng chữ" — đẹp mà không nói được sản phẩm
   là gì và bấm vào đâu. Tên mục đang chọn (slot 0) cũng được nâng cao
   tương ứng, xem SLOTS trong product-wheel.js. */
.pwheel-wrap{padding-top:clamp(96px,13.4vw,193px);}

/* Hộp chỉ cao bằng BÁN KÍNH: đáy hộp = tâm đường tròn = mép dưới
   section. Ba vòng tròn con đều neo bottom:0 rồi đẩy xuống 50% chiều
   cao chính nó, nên tâm cả ba trùng nhau và nửa dưới bị cắt gọn. */
/* touch-action:pan-y đặt ở ĐÂY chứ không chỉ trên .pwheel__disc: từ 2026-09-08
   vùng kéo là cả cụm vòng (xem dragEl trong product-wheel.js). Thiếu nó thì
   trình duyệt tự cuộn trang theo cú vuốt ngang và pointermove không bao giờ
   tới nơi; pan-y giữ lại đúng cú vuốt DỌC cho việc cuộn trang.
   cursor:grab để trên desktop cũng nhìn ra là kéo được. */
.pwheel{position:relative;height:var(--pw-r);touch-action:pan-y;cursor:grab;}
.pwheel.is-grabbing{cursor:grabbing;}
.pwheel__ring,
.pwheel__band,
.pwheel__disc{
  position:absolute;left:50%;bottom:0;
  /* --pw-in do effects.js tween lúc section vào tầm nhìn. Phải nằm TRONG
     transform ở đây chứ không để GSAP tự đặt transform, nếu không
     translate(-50%,50%) — thứ định vị cả ba vòng — sẽ bị ghi đè. */
  transform:translate(-50%,50%) scale(var(--pw-in,1));
  aspect-ratio:1;border-radius:50%;
}
.pwheel__ring{width:calc(var(--pw-r) * 2);border:1px solid #C1C1C1;}
/* Vòng giữa GIỮ XÁM: bản 2026-09-07 từng đổi sang gradient vàng do đọc nhầm
   yêu cầu — khách muốn đổi cái CHẤM dưới tên sản phẩm đang chọn, xem
   .pwheel__dotmark::before ở dưới. */
.pwheel__band{width:calc(var(--pw-r) * 1.665);border:calc(var(--pw-r) * .063) solid #C1C1C1;}
.pwheel__disc{
  width:calc(var(--pw-r) * 1.296);
  background:linear-gradient(180deg,#C1C1C1 0%,#484848 100%);
  overflow:hidden;cursor:grab;
  /* pan-y: kéo NGANG là cử chỉ của vòng quay, còn vuốt dọc vẫn phải cuộn
     trang được — thiếu dòng này là mobile kẹt cứng ở section. */
  touch-action:pan-y;
}
.pwheel__disc.is-grabbing{cursor:grabbing;}

/* ---- DẢI CHẤM TRÊN VÀNH (khách chốt 2026-08-05, vòng 6; sửa vòng 9) -------
   "cho chấm các điểm nhỏ vào, và cho nó xoay mỗi khi đổi sản phẩm";
   vòng 9: "thiếu 2 chấm" — phải thấy ĐỦ 5 chấm trên cung hở.

   Mỗi chấm là HÌNH CHIẾU CỦA NHÃN TƯƠNG ỨNG LÊN VÀNH: renderAt() trong
   product-wheel.js ghi left/top cho từng chấm theo đúng góc nhãn đã nội suy,
   từng khung hình. 5 chấm vì thế nằm ở 0/±34/±68 — thẳng hàng với 5 nhãn —
   và vẫn CHUYỂN ĐỘNG mỗi lần đổi mục (đi cùng nhãn, kể cả cú lặn qua đáy).

   Bản vòng 6–8 rải chấm ĐỀU 72° quanh TRỌN vòng rồi quay cả lớp này bằng
   rotate(var(--pw-rot)) + transition: chia đều là điều kiện để phép quay
   nguyên lớp khớp lại vị trí, nhưng hệ quả là 2/5 chấm vĩnh viễn nằm ở nửa
   dưới bị .m-products cắt — đúng cái khách chê. Rẻ quạt 34° của nhãn không
   bao giờ khớp dưới phép quay nguyên lớp, nên bỏ hẳn --pw-rot lẫn transition
   ở đây (chấm chạy bằng rAF của renderAt, transition chỉ làm nó lết sau nhãn).

   VẪN KHÔNG XOAY ĐĨA XÁM: đĩa là linear-gradient(180deg) sáng-trên tối-dưới,
   tức một khối có hướng chiếu sáng — quay nó là trục sáng nghiêng hẳn sang
   bên, đọc ra như lỗi render chứ không như vòng đang quay. */
.pwheel__dots{
  position:absolute;left:50%;bottom:0;width:0;height:0;z-index:1;
  /* Hộp 0×0 nên translate(-50%,50%) ra đúng 0px — nó ở đây để chuỗi transform
     khớp từng bước với ba vòng tròn anh em, đừng đọc nhầm là phép canh tâm
     (canh tâm là do left:50% + bottom:0 trên một hộp không kích thước).
     --pw-in GHÉP VÀO chuỗi này chứ không thay thế nó: GSAP (effects.js) chỉ
     được ghi BIẾN, ghi thẳng `transform` là xoá mất phép canh kia. */
  transform:translate(-50%,50%) scale(var(--pw-in,1));
}
.pwheel__dot{
  --d:9px;
  position:absolute;
  /* Vị trí do JS ghi bằng left/top (px), KHÔNG bằng transform: transform ở đây
     phải để trống cho hiệu ứng phóng to của chấm đang chọn. Lề âm nửa đường
     kính kéo TÂM chấm về đúng điểm neo. */
  width:var(--d);height:var(--d);margin:calc(var(--d) / -2);
  border:0;padding:0;border-radius:50%;background:#C1C1C1;cursor:pointer;
  transition:background .3s var(--ease),transform .3s var(--ease);
}
.pwheel__dot:hover{background:#8E8E8E;}
/* Chấm đang chọn KHÔNG tự đổi màu — .pwheel__dotmark bên dưới mới là dấu đậm.
   Giữ class vì JS vẫn cần nó để khoá con trỏ. */
.pwheel__dot.is-active{cursor:default;}
@media (prefers-reduced-motion:reduce){
  .pwheel__dots{transition:none;}
}

/* ---- DẤU ĐẬM ĐỨNG YÊN Ở ĐỈNH (khách chốt 2026-08-05, vòng 8) ---------------
   "chấm xanh luôn ở giữa chứ". (Chấm này XANH cho tới 2026-08-19 thì khách đổi
   sang xám đen đậm — xem màu ở ::before bên dưới. Mọi chữ "xanh" trong đoạn
   dẫn giải này nói về LỊCH SỬ, không phải màu hiện tại.)

   Bản vòng 6 tô màu THẲNG vào chấm đang chọn. Màu đổi tức thì còn vành thì
   quay mất trọn DURATION, nên suốt cú xoay dấu này nằm lệch hẳn một nấc (72°) rồi
   mới trườn về đỉnh — chụp màn hình giữa chừng là thấy ngay.
   Nay tách làm hai việc: vành chấm xám vẫn quay đủ DURATION, còn dấu đậm là một
   phần tử RIÊNG neo cứng ở đỉnh vòng. Lúc vành đứng yên nó phủ đúng lên chấm
   đang chọn (14px đè 9px nên chấm xám khuất hẳn); lúc vành quay nó ở nguyên
   chỗ cũ — tức "luôn ở giữa" theo đúng nghĩa đen.

   Dựng thành một VÒNG TRÒN đủ cỡ 2R rồi gắn dấu ở đỉnh nó, chứ không đặt thẳng
   một chấm cách tâm R: có vậy scale(var(--pw-in)) lúc section hiện lên mới kéo
   dấu vào/ra cùng nhịp với ba vòng tròn anh em — một chấm neo cứng theo px sẽ
   đứng im giữa lúc cả vòng đang nở ra. */
.pwheel__dotmark{
  position:absolute;left:50%;bottom:0;z-index:1;pointer-events:none;
  width:calc(var(--pw-r) * 2);aspect-ratio:1;border-radius:50%;
  transform:translate(-50%,50%) scale(var(--pw-in,1));
}
.pwheel__dotmark::before{
  content:"";position:absolute;left:50%;top:0;
  /* 14px = 9px (--d của .pwheel__dot) × 1.6, đúng cỡ phóng to của bản vòng 6. */
  width:14px;height:14px;margin:-7px 0 0 -7px;
  /* VÀNG ACTION (khách chốt 2026-09-07: "đổi màu vàng ở vòng tròn dưới tên sản
     phẩm được chọn"). Đây là dấu DUY NHẤT chỉ ra mục đang chọn trên vành, nên
     nó mang màu hành động của site; các chấm còn lại giữ xám .pwheel__dot.
     Trước là --heading (xám đen), đổi cùng đợt với .pwheel__item.is-active hồi
     2026-08-19 — nay hai chỗ đó KHÁC màu nhau là có chủ ý. */
  /* MÀU ĐẶC --gold, KHÔNG phải --gold-grad. Ở 14px thì một dải chuyển sắc không
     đọc ra được gì, nhưng nó lại là ẢNH NỀN (gradient) trên một phần tử tí hon
     nằm trong một hộp 2R gần như rỗng — đúng kiểu trình duyệt hay bỏ vẽ trong
     lúc trang đang cuộn rồi vẽ lại khi cuộn xong (khách báo 2026-09-08: "trong
     lúc scroll vòng tròn vàng biến mất"). Màu đặc thì không có ảnh nền để bỏ.
     ⚠️ CHƯA tái hiện được bằng trình duyệt tự động (Lenis chặn cuộn lập trình),
     nên đây là cách chữa theo nguyên nhân khả dĩ nhất chứ không phải đã chứng
     minh. Nếu còn tái diễn thì hỏi khách đang xem trên trình duyệt nào. */
  /* ĐEN (khách 2026-09-22: "bỏ màu vàng ở giữa đổi thành màu đen") — về lại
     --heading như trước 2026-09-07; các ghi chú về màu vàng ở trên là lịch sử. */
  border-radius:50%;background:var(--heading);
}
/* Vệt sáng quét ngang mỗi lần đổi mục — gợi cảm giác cuộn chỉ đang quay
   mà vẫn giữ nguyên sắc xám phẳng đã duyệt. */
.pwheel__sheen{
  position:absolute;inset:-10% -30%;opacity:0;pointer-events:none;
  background:linear-gradient(100deg,transparent 34%,rgba(255,255,255,.85) 50%,transparent 66%);
}

/* Neo kích thước 0 đặt ĐÚNG tâm vòng. Nhãn là con của neo này;
   product-wheel.js đặt vị trí từng cái bằng transform theo toạ độ cực,
   nên toàn bộ toán vị trí chỉ có một gốc quy chiếu. */
.pwheel__items{position:absolute;left:50%;bottom:0;width:0;height:0;z-index:2;}

/* Nhãn danh mục. Bản dựng cho chữ NHẠT DẦN xuống chân chữ (đo được
   #606060 ở đỉnh → gần trắng ở đáy) chứ không phải màu đặc, nên dùng
   mask gradient; --pw-a1/--pw-a2 là độ đậm đầu/cuối và ĐƯỢC JS nội suy
   theo slot: càng xa mục đang chọn càng nhạt.
   KHÔNG đặt transform/font-size ở đây — JS sở hữu cả hai. Chính vì thế cỡ chữ
   dưới đây là NGOẠI LỆ CÓ CHỦ Ý của thang --fs-* (common.css): nó chỉ là mốc
   gốc để product-wheel.js nhân hệ số theo slot (.70/.56 cho các mục phụ), chứ
   không phải một nấc trong thang tiêu đề. */
.pwheel__item{
  position:absolute;left:0;top:0;
  transform-origin:50% 50%;
  background:none;border:0;padding:0;cursor:pointer;text-align:inherit;
  /* line-height 1.08 → 1.3 (khách 2026-09-07: "1 số text bị cắt mất dấu thanh").
     1.08em KHÔNG đủ cho chữ hoa tiếng Việt có dấu chồng: sàn đo được của site
     là 1.28 (xem ghi chú thang --lh-* trong common.css), dưới sàn thì phần dấu
     nằm NGOÀI hộp dòng và bị cắt ở bất kỳ tổ tiên nào có overflow — ở đây là
     .m-products{overflow:hidden}. Nhãn chỉ một dòng nên nới line-height không
     tốn chỗ, chỉ nới hộp ra. */
  font-family:var(--font-display);line-height:1.3;color:#525457;
  /* THANG RIÊNG trở lại (khách chốt 2026-08-05: "tăng nhẹ size tiêu đề chính"
     của section sản phẩm). 27px ở khổ 1440, so với 24px của vòng trước.
     Vì sao lại tách khỏi --fs-h3 dù vòng 3 vừa gộp vào: cùng đợt này --fs-h3
     tụt 24→20px cho toàn site. Nếu để nó đọc chung thì tên danh mục — chữ lớn
     nhất và là trọng tâm thị giác của cả section — sẽ GIẢM 4px trong khi khách
     yêu cầu TĂNG. Hai yêu cầu ngược chiều nhau nên buộc phải tách thang; đây là
     một ngoại lệ có chủ ý, ghi cạnh .pband__title và .banner h1.
     Đặt trên .pwheel__item (mốc gốc) chứ KHÔNG trên .is-active: hệ số scale của
     SLOTS (.70/.56) nhân từ mốc này, và nếu cỡ chữ đổi lúc class .is-active
     nhảy qua nhãn khác thì mỗi bước xoay sẽ kèm một lần reflow chữ.
     JS đọc offsetHeight của nhãn để tính lại bán kính (SLOTS['0'].r) nên vòng
     quay tự canh lại theo cỡ mới, không cần sửa gì trong product-wheel.js. */
  /* 27 → 24px ở khổ 1440 (khách 2026-09-07: "giảm khoảng 2-4px"). Hệ số vw hạ
     cùng nhịp để các khổ giữa cũng giảm, không chỉ khổ chạm trần. */
  font-size:clamp(19px,1.78vw,24px);
  /* Nhãn phụ để nét NHẸ; chỉ mục đang chọn mới đậm. 400 = svn-gotham-book,
     có file thật nên không bị trình duyệt tổng hợp faux-light. Cùng với cỡ
     chữ đã giảm trong SLOTS (.70/.56 thay cho .917/.778), các mục phụ lùi
     hẳn về sau và không còn tranh chấp với tên đang chọn. */
  font-weight:var(--fw-body);
  /* nowrap có chủ đích: nhãn đang được transform mỗi khung hình, để nó
     ngắt dòng giữa chừng là vừa giật vừa reflow. Bề ngang đã đủ ở mọi
     breakpoint còn hiện nhãn — hẹp hơn thì --pw-slots ẩn hẳn chúng. */
  white-space:nowrap;
  /* Mờ đều cả chữ, KHÔNG dùng mask gradient nữa. Bản trước làm chữ nhạt
     dần xuống chân (bám bản dựng PDF) nhưng trên nền trắng nó đọc ra
     thành một lớp phủ trắng cắt ngang chân chữ — rối hơn là tinh tế.
     JS ghi --pw-o (độ mờ theo slot) chứ không ghi thẳng opacity, để rule
     :hover bên dưới còn đè được: khai báo inline luôn thắng stylesheet. */
  opacity:var(--pw-o,1);
  /* KHÔNG transition opacity: JS ghi lại --pw-o mỗi khung hình khi vòng
     quay, có transition là trình duyệt cứ phải đuổi theo giá trị mới —
     độ mờ trễ lại so với vị trí. Hover đổi tức thì, sắc nét hơn. */
}
/* Từng ký tự của nhãn, do product-wheel.js dựng (paintNames) và uốn lên cung
   tròn (bendChars).
   inline-block là BẮT BUỘC chứ không phải để canh: transform không áp được lên
   phần tử inline không-thay-thế, để mặc định thì mọi phép uốn im lặng biến mất.
   transform-origin giữa ký tự để phép xoay của nó không kéo chữ trôi ngang. */
.pwheel__ch{display:inline-block;transform-origin:50% 50%;}

/* Mục đang chọn không bắt chuột: bấm vào nó không có nghĩa gì — nút bên
   dưới mới là thứ để bấm.
   MÀU XÁM ĐEN ĐẬM (khách chốt 2026-08-19), thay cho xanh accent của vòng 8.
   --heading = #33363A, 12.14:1 trên trắng. Cặp đôi với .pwheel__dotmark: hai
   thứ này luôn phải CÙNG MÀU vì chấm chính là điểm neo của tên trên vành —
   đổi một bên là chúng đọc ra như hai phần rời nhau.
   Thứ bậc trong vòng nay do ĐẬM/NHẠT lo (mục đang chọn #33363A, bốn nhãn phụ
   xám mờ .42), không còn nhờ màu accent. */
/* Tên mục ĐANG CHỌN: đậm + IN HOA, gạch chân vàng khi rê chuột (khách
   2026-09-07) — cùng ngôn ngữ với CTA chữ, chỉ khác là gạch ở đây neo theo
   chính nhãn (nhãn là <button> định vị tuyệt đối trên vòng). */
/* cursor:pointer (khách 2026-09-07): từ nay bấm vào tên đang chọn là MỞ TRANG
   danh mục (xem product-wheel.js), nên con trỏ phải báo là bấm được. */
/* letter-spacing .04em: chữ IN HOA luôn cần nới chữ, không nới thì các thân
   chữ dính sát nhau và mắt đọc ra dòng chữ "gập ghềnh" (khách báo nhiều vòng:
   "các chữ cái không nằm trên cùng 1 line thẳng hàng").
   ⚠️ Đã đo raster ở CẢ Chromium lẫn WebKit, 1x và 2x: baseline các chữ TRÙNG
   nhau, lệch tối đa 0.5px CSS và đúng ở G/U/O — đó là overshoot quang học mà
   font nào cũng có (AlmarenaVN Display Bold: descent 0.456px ở cỡ 24px). Đây
   KHÔNG phải lỗi baseline; nới chữ là để sửa cảm giác đọc, không phải sửa toạ
   độ. Đừng đi tìm lỗi trong transform của product-wheel.js. */
.pwheel__item.is-active{
  cursor:pointer;font-weight:var(--fw-bold);text-transform:uppercase;
  letter-spacing:.04em;color:var(--heading);
}
.pwheel__item.is-active::after{
  content:"";position:absolute;left:0;right:0;bottom:-6px;height:2px;
  background:var(--gold);transform:scaleX(0);transform-origin:left;
  transition:transform .3s var(--ease);
}
/* GẠCH CHÂN CHỈ HIỆN KHI CHUỘT THẬT SỰ DI CHUYỂN — đây là chốt chặn cuối sau
   ba vòng chữa hụt (khách báo lại ba lần: "khi scroll thì tiêu đề bị giật 1 cái
   hiện line gạch chân rồi biến mất").
   Vì sao hai cách trước chưa đủ:
     · cờ .is-turning chỉ bắt lúc VÒNG quay, không bắt lúc TRANG cuộn;
     · pointer-events:none lúc cuộn vẫn hụt khung hình ĐẦU TIÊN, vì class chỉ
       kịp gắn sau sự kiện cuộn đầu.
   Cách này chặn từ gốc: `html.pointer-moved` do main.js gắn khi có mousemove
   THẬT và GỠ NGAY khi bắt đầu cuộn. Cuộn không sinh mousemove, nên trong suốt
   cú cuộn selector dưới đây không thể khớp — kể cả ở khung hình đầu. */
html.pointer-moved .pwheel__item.is-active:hover::after{transform:scaleX(1);}
.m-products.is-turning .pwheel__item.is-active:hover::after{transform:scaleX(0);transition:none;}
html.is-scrolling .pwheel__item{pointer-events:none;}
.pwheel__item:not(.is-active):hover,
.pwheel__item:focus-visible{opacity:.8;}

/* (Dải chấm chỉ báo trên vành và bộ đếm 01/05 đều đã bỏ theo feedback —
   điều khiển chỉ còn cặp mũi tên + bấm nhãn + kéo tay, như ref ltdstudio.) */

/* Mô tả + nút hành động của mục đang chọn. Đứng yên trên nền TRẮNG phía
   trên vành, KHÔNG quay theo vòng.
   Đặt trên nền trắng chứ không trên đĩa xám là vì đo được: đỉnh đĩa chỉ
   ~#B1B1B1, chữ trắng lên đó được 1.9:1 — rớt xa ngưỡng đọc. Cặp mũi tên
   nằm được là vì chúng ở thấp hơn, chỗ đĩa đã tối, và là icon chứ không
   phải chữ cần đọc.
   GOM LÀM MỘT KHỐI, neo một lần theo đáy, khoảng cách bên trong tính bằng
   px: nếu neo riêng từng phần theo bội số --pw-r thì ở khổ nhỏ hai neo co
   lại gần nhau trong khi nút vẫn cao 48px cố định → chữ chồng lên nút. */
/* ⚠️ .pwheel__lead / __desc / __ctaslot: markup ĐÃ GỠ khỏi index.html
   2026-09-07 (khách bỏ mô tả + nút "Xem chi tiết" trên vòng xoay). Giữ nguyên
   ba rule để dán lại markup là chạy — xem ghi chú tại chỗ gỡ trong index.html. */
.pwheel__lead{
  /* bottom ở đây chỉ là DỰ PHÒNG khi JS không chạy — product-wheel.js ghi
     đè bằng LEAD_GAP để chỉ có một nguồn duy nhất cho khoảng cách này. */
  position:absolute;left:0;right:0;bottom:calc(var(--pw-r) + 44px);
  z-index:3;text-align:center;
}
/* Nhịp đổi chữ = ĐÚNG BẢN LIVE (chốt vòng 5): mô tả chỉ NHÁY MỜ 0.2s quanh
   mốc thay chữ rồi hiện lại ngay — khối mô tả + nút coi như luôn có mặt.
   Bản "trượt 2 pha" trước đó làm CẢ CỤM biến mất ~1s mỗi lần xoay: nhìn vào
   đúng lúc đó section như thiếu hẳn một khối, mọi thứ dồn khít lại (chính là
   cái khách chê "sai layout / không có spacing"). */
.pwheel__desc{
  margin:0 auto 20px;max-width:min(46ch,86vw);
  font-size:var(--fs-sm);line-height:var(--lh-body);color:var(--gray-700);
  /* --pw-swap + --ease-glide: cùng token, cùng đường cong với thẻ badge trên
     dải ảnh (.pband__cardin) — hai khối phải tắt/hiện như một. */
  transition:opacity var(--pw-swap) var(--ease-glide);
}
.pwheel-wrap.is-swapping .pwheel__desc{opacity:0;}
/* display:flex chứ KHÔNG phải line-height:0 (sửa 2026-08-04).
   Mục đích ban đầu của line-height:0 là khử khe trắng dưới phần tử inline —
   nhưng nó KẾ THỪA xuống .pwheel__cta bên trong, làm hộp dòng của nhãn co về
   0. Hệ quả: gạch chân hover của .link-arrow (định vị bottom:-3px so với hộp
   cao 0) rơi vào GIỮA chữ, hiện ra thành một vạch gạch ngang chữ — đúng "lỗi
   cta" khách chụp. Hộp flex khử khe trắng đó mà không đụng tới line-height,
   nên nhãn giữ đúng chiều cao và gạch chân về lại dưới chân chữ.
   Phụ thêm: product-wheel.js đo leadEl.offsetHeight để tính bán kính vòng
   quay — trước đây ô này khai cao 0 nên số đo bị hụt, nay đúng thật. */
.pwheel__ctaslot{display:flex;justify-content:center;}
/* Nút dùng lại .link-arrow của common.css — nhãn hành động chung của cả site,
   nên quen mắt và không phải bịa thêm kiểu nút mới cho riêng chỗ này.
   (Rule .pwheel__cta .icon nạp arrow-right.svg đã gỡ 2026-08-04 cùng đợt bỏ
   icon khỏi mọi CTA; product-wheel.js cũng thôi dựng thẻ <i> đó.)
   XÁM KHI NGHỈ, XANH KHI HOVER (khách chốt 2026-08-05 vòng 8 — trước đó vòng 7
   để xanh cả hai trạng thái). Vẫn khai riêng chỗ này chứ KHÔNG sửa .link-arrow
   dùng chung: nhãn đó còn xuất hiện ở đầu section tin tức và trong các trang
   con trên nền khác, đổi chung là kéo theo cả loạt chỗ khách không yêu cầu.
   Accent nay dồn hết cho TÊN danh mục (.pwheel__item.is-active) — hai thứ cùng
   xanh thì section có hai trọng tâm, mà tên mới là thứ phải đọc trước.
   --gray-500 chứ không nhạt hơn: 5.27:1 trên nền trắng, đủ AA cho chữ 13px
   IN HOA giãn .14em của .link-arrow. Gạch chân trượt vốn đã là --gold nên lúc
   hover cả cụm về đúng một màu. */
.pwheel__cta{color:var(--gray-500);}
.pwheel__cta:hover{color:var(--gold-deep);}

/* Cặp mũi tên nằm trên đĩa xám, viền trắng mảnh như bản dựng. */
.pwheel__nav{
  position:absolute;left:50%;bottom:calc(var(--pw-r) * .235);
  transform:translate(-50%,50%);z-index:4;
  display:flex;gap:clamp(8px,1.04vw,15px);
}
.pwheel__btn{
  /* Trần = --btn-icon để ở desktop bằng đúng nút điều hướng tin tức (48px);
     vẫn co lại theo vw vì nút nằm TRONG vòng tròn, cỡ cố định sẽ tràn khung
     trên màn hẹp. Sàn nâng 32→36 cho đủ vùng chạm 36px. */
  /* 3.75vw = 48px đúng tại 1280px, tức chạm trần --btn-icon ở desktop và bằng
     hệt nút điều hướng tin tức. Hệ số cũ 2.92vw chỉ ra 37px nên trần 48 không
     bao giờ với tới — trần mà không đạt được thì coi như không có.
     Vẫn co theo vw vì nút nằm TRONG vòng tròn; sàn 36px = vùng chạm tối thiểu. */
  /* SÀN 44px, KHÔNG PHẢI 36: đây là nút bấm chính của vòng xoay và 36px là
     DƯỚI ngưỡng vùng chạm 44×44 mà spec §6.1 đặt ra — trên điện thoại, đúng nơi
     nút bị co lại nhất, cũng là nơi nó khó bấm nhất. Trần vẫn --btn-icon (44px)
     nên thực chất nút nay cố định 44px; giữ clamp để nếu sau này nâng trần thì
     nút vẫn co giãn theo vw như thiết kế ban đầu.
     BO NHẸ 6px (khách chốt 2026-08-05 vòng 6, trước là tròn hẳn) — cùng ngôn
     ngữ với mũi tên tin tức, và nay cũng cùng với mọi nút chữ trên site. */
  width:clamp(44px,3.75vw,var(--btn-icon));height:clamp(44px,3.75vw,var(--btn-icon));flex:none;
  border-radius:var(--radius);
  display:flex;align-items:center;justify-content:center;
  border:1px solid rgba(255,255,255,.6);background:none;color:var(--white);cursor:pointer;
  transition:background .25s var(--ease),border-color .25s var(--ease),transform .2s var(--ease);
}
.pwheel__btn:hover{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.92);}
.pwheel__btn:active{transform:scale(.94);}
.pwheel__btn .icon{width:var(--icon-nav);height:var(--icon-nav);}

/* (Bộ đếm 01/05 đã bỏ theo feedback 2026-08-03 — nav chỉ còn cặp mũi tên.) */

/* static grid fallback (reduced motion) — hidden by default */
.m-products__fallback{display:none;grid-template-columns:repeat(3,1fr);gap:20px;}
.pfall{border-radius:var(--radius-lg);overflow:hidden;background:var(--bg);}
.pfall__img{aspect-ratio:4/3;width:100%;object-fit:cover;}
.pfall__body{padding:20px 22px;}
.pfall h3{font-size:var(--fs-h4);margin-bottom:8px;}
.pfall p{font-size:var(--fs-sm);color:var(--gray-700);line-height:var(--lh-body);}
/* Lưới này CHỈ hiện khi bật reduced-motion — người dùng ở chế độ đó không
   bao giờ thấy vòng quay, nên CTA phải có mặt ở đây, nếu không họ gặp
   đúng cái ngõ cụt mà cả thay đổi này sinh ra để dẹp. */
.pfall__cta{
  /* display do rule chung .link-arrow,.pfall__cta (common.css) lo — inline-flex
     để xếp chữ + icon. Chỉ giữ phần riêng của chỗ này. */
  margin-top:14px;font-family:var(--font-display);
  font-size:var(--fs-micro);letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold-deep);text-decoration:none;
  /* Gạch vàng + hover: dùng CHUNG ::after với .link-arrow, khai ở common.css
     ("GẠCH VÀNG CHUNG cho CTA chữ", 2026-09-07). Không khai border ở đây nữa. */
}
.pfall__cta:hover{color:var(--gold-deep);}   /* giữ trong họ accent, không nhảy về đen */

/* =========================================================
   6 · M-NEWS — centred card conveyor (ltdstudio-style)
   ========================================================= */
/* Dải xám bắt đầu ĐÚNG mép dưới section vòng xoay: .m-products có
   padding-bottom 0 + overflow:hidden nên đĩa quay bị cắt ngang ngay tại đường
   ranh — không chèn khoảng trắng nào vào giữa.
   --gray-100 (xám TRUNG TÍNH) chứ KHÔNG phải --cream: đứng cạnh nền trắng của
   .m-products, sắc kem ám vàng đọc ra như trắng bị ố chứ không ra một dải màu.
   Trắng (vòng xoay) — xám (đây) — trắng trở lại (.m-contact): dải xám có biên
   hai đầu nên đọc ra là một khối cố ý, không phải trang đổi tông nửa chừng. */
.m-news{background:var(--gray-100);}
/* Cùng nhịp rộng với header và phần giới thiệu — xem --maxw-wide (common.css).
   Đặt chung một dòng với .m-contact vì hai section này là cặp cuối trang, lệch
   bề ngang giữa chúng lộ ra ngay. */
.m-news .container,
.m-contact .container{max-width:var(--maxw-wide);padding-inline:var(--pad-wide);}
.m-news__head{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:20px;margin-bottom:clamp(28px,4vw,48px);}
.m-news__head h2{font-size:var(--fs-h2);margin-top:14px;}
/* Không nền riêng: nền trắng khiến mỗi thẻ hiện ra như một cột trắng nổi trên
   dải xám của section, băng tin vỡ thành từng mảnh thay vì đọc liền một dải. */
.news-card{display:flex;flex-direction:column;height:100%;background:transparent;}
/* __media là KHUNG ĐỨNG YÊN — thứ trượt là tấm ẢNH bên trong .inner
   (overflow:hidden), nên chuyển động bị cắt gọn TRONG khung, không bao giờ
   đè sang thẻ bên cạnh. (Bản trước animate cả .news-card__media như ref —
   nhưng vì thẻ của ta có khe hở giữa các card nên cú lệch -50% lộ nguyên
   khối ảnh đè lên thẻ trước, đọc ra "double" — feedback vòng 3.) */
.news-card__media{position:relative;margin-bottom:16px;}
.news-card__media .inner{display:block;position:relative;aspect-ratio:13/8;border-radius:var(--radius-lg);overflow:hidden;background:var(--greige);}
.news-card__media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform .7s var(--ease);}
.news-card:hover .news-card__media img{transform:scale(1.05);}
/* Rèm màu của cú reveal (vòng 8 — lấy lại từ ref/live): đỗ ở mép trái dưới dạng
   lát zero-width nên vô hình, chỉ hiện khi animation news-wipe bên dưới kéo
   clip-path quét qua. CHỈ rèm màu quay lại — cú ảnh trượt -50% kèm theo ở bản
   live vẫn bị loại (vòng 4: "giật lùi sang trái rồi mới hiện"). */
.news-card__media::before{
  content:"";position:absolute;inset:0;z-index:3;
  /* --gold (vàng metallic) chứ không --gold-deep: đây là MẢNG ĐẶC phủ kín ảnh
     trong ~0,6s, không phải chữ hay nét — vai đúng của sắc vàng sáng. Đồng thau
     đậm ở cỡ mảng này đọc ra như một vệt bùn. */
  background:var(--gold-grad);
  clip-path:polygon(0 0,0 0,0 100%,0 100%);
}
/* CHIP CHUYÊN MỤC — KHUÔN CHUẨN của site (khách chốt 2026-09-07: "vị trí như ở
   home mới đúng"): nền trắng mờ, LÙI 14px khỏi góc ảnh, bo --radius-sm.
   .ntag và .nfeat__tag bên news-list.css đã được kéo về đúng khuôn này; đổi một
   chỗ thì đổi cả ba. (Bản trước đó thử ngược lại — đưa home về kiểu vàng sát
   góc của trang Tin tức — và khách bác.) */
.news-card__tag{
  position:absolute;top:14px;left:14px;z-index:4;
  padding:6px 12px;
  /* NỀN VÀNG (khách 2026-09-07, đổi từ trắng mờ): VỊ TRÍ giữ nguyên kiểu trang
     chủ — lùi 14px, bo --radius-sm. Chữ ĐEN: trắng trên đầu tối của gradient chỉ
     1.96:1. */
  background:var(--gold-grad);color:var(--btn-ink);
  font-family:var(--font-display);font-size:var(--fs-micro);
  font-weight:var(--fw-medium);letter-spacing:.1em;text-transform:uppercase;
  line-height:1;
  border-radius:var(--radius-sm);
}
/* line-height:1 để icon lịch và dãy số THẬT SỰ cùng tâm — align-items:center
   căn giữa hộp dòng, mà hộp dòng còn thừa leading trên/dưới. Cùng một sửa với
   .ndate bên news-list.css; hai chỗ phải đổi cùng nhau. */
.news-card__date{font-size:var(--fs-meta);line-height:1;color:var(--gray-500);letter-spacing:.04em;margin-bottom:10px;display:flex;align-items:center;gap:8px;}
.news-card__date .icon{width:15px;height:15px;color:var(--gold);}
/* 17px/500: nằm trong luồng đọc cạnh body 16px, nên cần CẢ cỡ lẫn nét mới nổi.
   15px/400 trước đây gần như lẫn vào đoạn văn. */
.news-card h3{
  /* TIÊU ĐỀ THẺ TIN = FONT CHỮ CHẠY, NÉT BOLD (khách chốt 2026-09-07: "đồng bộ
     lại tiêu đề tin tức: font body bold"). Trước đó ba khối lệch nhau: thẻ nổi
     bật đi AlmarenaVN 500, thẻ trong dải và lưới đi Beautique 500, thẻ trang chủ
     đi Beautique 500. Ba call-site (.news-card h3 · .ncard h3 · .nfeat__title)
     phải đổi CÙNG NHAU — đây là một trong ba.
     2026-09-22: RIÊNG thẻ TRANG CHỦ — thẻ NHỎ về nét THƯỜNG, thẻ TO (.is-active)
     vẫn BOLD (khách: "card to vẫn bold, chỉ card nhỏ không bold"); .ncard h3 và
     .nfeat__title bên trang Tin tức vẫn bold. */
  font-family:var(--font-body);font-weight:var(--fw-body);
  font-size:var(--fs-h3);line-height:var(--lh-heading);color:var(--heading);margin-bottom:12px;transition:color .25s var(--ease);
}
/* --dark chứ KHÔNG --gold-deep: từ 2026-08-19 --gold-deep chính là #33363A
   (= --heading), tức đúng màu nghỉ của tiêu đề này — hover sẽ KHÔNG đổi gì.
   Vàng thì không làm chữ trên nền sáng được (2.11:1), nên tín hiệu hover đi
   theo hướng còn lại: đậm thêm một bậc về gần đen. */
.news-card:hover h3{color:var(--dark);}
.m-news__swiper .swiper-slide.is-active .news-card h3{font-weight:var(--fw-bold);}   /* thẻ to giữ bold (2026-09-22) */
.news-card p{font-size:var(--fs-sm);color:var(--gray-700);line-height:var(--lh-body);margin-bottom:16px;}
/* arrows sit under the whole track (i.e. under the tall featured card), flush
   with the container's left edge — same anchoring as the section title above */
/* Khoảng cách xuống mũi tên SIẾT LẠI (khách chốt 2026-08-05: "giảm khoảng cách
   từ tin tức đến mũi tên"), 16-28px → 10-16px. Cụm nút phải đọc ra là thuộc về
   băng tin ngay trên nó, không phải một khối rời.
   ĐỪNG siết bằng cách đụng vào .m-news__swiper{padding-bottom:4px} — news.js
   cộng đúng con số đó vào minHeight khi khoá chiều cao track, sửa một mình nó
   là mũi tên nhấp nhô trở lại mỗi lần đổi thẻ. */
.m-news__nav{display:flex;gap:10px;position:relative;z-index:3;
  margin-top:clamp(10px,1.15vw,16px);}
/* Nghỉ = chỉ viền, hover = tô đen đặc. Hai nút giống hệt nhau lúc nghỉ: cặp này
   đọc ra như MỘT bộ điều khiển, phần tô đen dành riêng cho phản hồi khi rê chuột
   chứ không dùng để nhấn mạnh sẵn một hướng.
   Cùng khuôn với .pwheel__btn (viền → tô khi hover), chỉ khác là bộ này nằm trên
   nền sáng nên dùng mực đen thay cho trắng. */
.m-news__nav button{
  /* BO NHẸ 6px (khách chốt 2026-08-05 vòng 6). Mũi tên carousel từng là ngoại
     lệ được vượt trần bo góc; nay không còn ngoại lệ nào cho khối chữ nhật —
     chỉ cụm nút liên hệ nổi (.group-contact) còn tròn, và đó là vì logo FB/Zalo
     bên trong vốn tròn. 44×44 = đúng vùng chạm tối thiểu trên mobile. */
  width:var(--btn-icon);height:var(--btn-icon);border-radius:var(--radius);box-sizing:border-box;
  display:flex;align-items:center;justify-content:center;
  /* Viền XÁM NHẠT (2026-09-07, khách: "border của button giảm bớt"): trước là
     --gold-deep xám đen, cùng màu với icon nên khung nặng ngang mũi tên. Nay
     --border-strong (#C9C9CE) — icon vẫn xám đen, khung chỉ còn là gợi ý vùng chạm. */
  border:1px solid var(--border-strong);background:transparent;color:var(--gold-deep);
  transition:background .25s var(--ease),color .25s var(--ease),border-color .25s var(--ease);
}
/* Hover = nền VÀNG + icon ĐEN: nút mũi tên là một trong số ít "action nhỏ"
   được phép mang accent (khách chốt 2026-08-19). Icon trắng trên vàng 2.11:1
   là rớt chuẩn — xem khối MÀU HÀNH ĐỘNG trong common.css. */
.m-news__nav button:hover{background:var(--gold);border-color:var(--gold);color:var(--btn-ink);}
.m-news__nav .icon{width:var(--icon-nav);height:var(--icon-nav);}

/* ---------------------------------------------------------------------------
   BĂNG TIN "CONVEYOR" — KHÔI PHỤC ĐÚNG BẢN LIVE (feedback vòng 7, đối chiếu
   screenshot huutin.monademo.com): markup mượn class Swiper nhưng KHÔNG init
   Swiper — news.js tự chạy. Thẻ featured (.is-active) RỘNG 1.75× và đứng THỨ
   HAI trong hàng: nhỏ · to · nhỏ · nhỏ, lấp track vừa khít 100% nên không có
   thẻ nào bị cắt ở hai mép (khác hẳn bản centeredSlides+loop bị chê vòng 7).
   Hai điểm ĐÃ CHỐT các vòng trước vẫn giữ, không quay lại theo live:
     · hiệu ứng xuất hiện trên thẻ active (ảnh trượt -50% + rèm quét) — XOÁ
       (vòng 4: "giật lùi sang trái rồi mới hiện" đọc ra như lỗi);
     · vì không còn rèn che, bề ngang thẻ KHÔNG snap như live nữa mà
       TRANSITION cùng nhịp --news-dur với cú trượt của track (vòng 1: bản
       snap của live chính là cái "khựng" bị chê đầu tiên).
   --news-dur là nguồn sự thật duy nhất về tốc độ: news.js đọc ngược nó từ
   .m-news nên JS/CSS không bao giờ lệch nhau.
--------------------------------------------------------------------------- */
/* Bề ngang tính theo đơn vị "thẻ nhỏ" để hàng thẻ luôn cộng đúng 100% track:
   --news-span = 3 nhỏ + 1 to 1.75× = 4.75. Muốn hiện nhiều/ít thẻ hơn chỉ cần
   đổi --news-span. */
.m-news{
  --news-ratio:1.75;                          /* bề ngang thẻ to ÷ thẻ nhỏ */
  --news-span:4.75;                           /* 3 nhỏ + 1 to */
  --news-lead:1;                              /* số thẻ nhỏ ĐỨNG TRÁI thẻ to;
                                                 news.js đọc để biết đỗ track ở đâu */
  --news-w:calc(100% / var(--news-span));
  --news-w-act:calc(100% * var(--news-ratio) / var(--news-span));
  --news-gutter:12px;                         /* nửa khe hở nhìn thấy */
  /* 1900ms (đường đi 2000 → 1500 → 2200 → 1900: khách chê 2200 "chậm quá",
     lùi lại một nấc; phần chữa "giật" nằm ở ĐƯỜNG CONG nên rút ngắn thời
     lượng không làm nó quay lại).
     Ghi chú gốc của mốc 2200: Ngày 2026-08-19 khách chê băng tin
     "không phải trượt qua mà là giật lên" — CHẬM LẠI CHỈ LÀ NỬA CÁCH CHỮA,
     nửa còn lại là đường cong (xem .is-swapping bên dưới).
     Vì sao cú lật này có thành phần chuyển động DỌC để mà "giật lên": thẻ nổi
     bật rộng 1.75× thẻ thường, ảnh lại khoá aspect-ratio 13/8 → thẻ to có ảnh
     CAO HƠN ~120px ở khổ 1440. Mỗi lượt lật, ảnh thẻ cũ co lại kéo chữ bên
     dưới TRỒI LÊN, ảnh thẻ mới nở ra đẩy chữ của nó xuống. Đó là thiết kế
     (bám ref), không phải lỗi — nhưng với đường cong expo-out thì toàn bộ
     quãng dọc ấy dồn vào ~150ms đầu, tức đúng một cú giật.
     Đây là NGUỒN SỰ THẬT DUY NHẤT về tốc độ lật: news.js đọc ngược giá trị này
     bằng getComputedStyle, và cả hai transition morph (bề ngang thẻ, cỡ chữ
     tiêu đề) cũng bám vào nó — đổi ở đây là đổi cả bốn chỗ.
     AUTO trong news.js (khoảng NGHỈ giữa hai lần lật) là số RIÊNG, phải nới
     tay theo: giữ nguyên 3500 ở nhịp 2200 thì thẻ gần như vừa đỗ đã đi tiếp. */
  --news-dur:1900ms;                          /* thời gian track trượt một bước */
  /* Nhịp rèm quét trỏ về token chung — cùng 1.2s/bezier(.77,0,.18,1) mà ref
     dùng, và là nguồn gốc của ngôn ngữ chuyển động cả site. */
  --news-reveal:var(--reveal-dur);
  --news-reveal-ease:var(--reveal-ease);
}
@media (max-width:1199.98px){ .m-news{--news-span:3.75;} }   /* 2 nhỏ + to */
@media (max-width:899.98px){  .m-news{--news-span:2.75;} }   /* 1 nhỏ + to */

.m-news__swiper{overflow:hidden;padding-bottom:4px;}
/* Canh đỉnh: mọi thẻ chung một đường mép trên, thẻ to nở XUỐNG dưới. */
.m-news__swiper .swiper-wrapper{
  display:flex;align-items:flex-start;
  transform:translateX(0);
}
/* Khe hở là padding TRONG hộp slide chứ không phải flex gap: nhờ vậy một bước
   băng chuyền đúng bằng một bề ngang slide, không dư số phải cộng thêm. */
.m-news__swiper .swiper-slide{
  flex:0 0 auto;box-sizing:border-box;
  width:var(--news-w);height:auto;
  padding-inline:var(--news-gutter);
}
/* Bề ngang chỉ transition TRONG lúc trượt (news.js gắn .is-swapping đúng một
   bước --news-dur). Để transition thường trực thì lúc init và lúc resize cửa
   sổ, thẻ cũng morph theo — init phải hiện ngay thế đỗ như live, còn resize
   phải bám kịp viewport.
   --ease-glide (đổi 2026-08-19, trước là --ease): bề ngang thẻ và cú trượt của
   track PHẢI dùng CHUNG một đường cong, nếu không hai chuyển động chạy lệch
   nhau và mép thẻ đọc ra như đang co giật. Đây cũng là nửa còn lại của cách
   chữa "giật lên" — xem dẫn giải ở --news-dur. */
.m-news__swiper.is-swapping .swiper-slide{
  transition:width var(--news-dur) var(--ease-glide);
}
/* Clamp 2 dòng VÀ chừa sẵn đúng 2 dòng (min-height): tiêu đề 1 dòng với 2 dòng
   mà khác chiều cao thẻ thì mỗi lượt trượt, thẻ to đổi bài viết là cả track đổi
   chiều cao → cụm mũi tên bên dưới nhấp nhô (feedback vòng 8). */
.m-news__swiper .news-card h3{
  margin-bottom:0;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;
  min-height:calc(2em * var(--lh-heading));
  /* HAI NẤC, và cả hai đều hạ thêm một bậc so với thang thẻ chung (khách
     2026-08-04 vòng 2: "đang to quá").
     Lý do thẻ tin cần nhỏ hơn thẻ ở nơi khác: cột thẻ NHỎ trong băng chuyền
     chỉ rộng ~264px ở khổ 1440 (100%/4.75 trừ khe). Ở --fs-h3 25px thì tiêu đề
     chỉ còn ~11 ký tự mỗi dòng, mà lại bị kẹp cứng 2 dòng — chữ vỡ vụn.
     Nay: thẻ nhỏ --fs-body (16,5px), thẻ nổi bật --fs-h4 (20px). Vẫn chênh
     1,21× nên nhìn ra ngay thẻ nào là thẻ chính; nét 500 kế thừa từ h1-h4 giữ
     cho nó đọc ra là TIÊU ĐỀ chứ không phải đoạn văn dù bằng cỡ --fs-body. */
  font-size:var(--fs-body);
}
@media (min-width:640px){
  /* +6px (khách 2026-09-07): thẻ nổi bật cần tách rõ hơn khỏi 3 thẻ nhỏ
     (--fs-body). Nay 24 so với 16,5 — chênh 1,45× thay vì 1,21×. */
  .m-news__swiper .swiper-slide.is-active .news-card h3{font-size:calc(var(--fs-h4) + 6px);}
}
/* Cỡ chữ đổi CÙNG NHỊP với bề ngang thẻ, và cũng chỉ trong lúc trượt — cùng lý
   do đã ghi ở .is-swapping .swiper-slide: để thường trực thì lúc init và lúc
   resize, chữ cũng morph 2s theo. */
.m-news__swiper.is-swapping .news-card h3{
  transition:font-size var(--news-dur) var(--ease-glide);
}
/* Dưới 640px thẻ đã gần full màn nên không còn khái niệm thẻ to. */
@media (min-width:640px){
  .m-news__swiper .swiper-slide.is-active{
    width:var(--news-w-act);
    z-index:2;
  }
}

/* --- rèm màu quét trên thẻ vừa thành nổi bật (hiệu ứng màu của ref) ---------
   Rèm navy lớn dần từ mép trái, phủ kín khung ở giữa nhịp, rồi rút ra qua mép
   phải — ảnh bên dưới ĐỨNG YÊN, chỉ có tấm màu chạy qua. Animation bám
   .is-active nên tự phát lại mỗi lần vai thẻ to đổi chủ; lúc xoay DOM class đi
   theo phần tử, không phát thừa. */
@keyframes news-wipe{
  0%  {clip-path:polygon(0 0,0 0,0 100%,0 100%);}
  50% {clip-path:polygon(0 0,100% 0,100% 100%,0 100%);}
  100%{clip-path:polygon(100% 0,100% 0,100% 100%,100% 100%);}
}
/* VỆT VÀNG chạy trên CHÍNH THẺ VỪA THÀNH TO (khách đổi ý 2026-09-22: "hiệu ứng
   vàng từ card nhỏ bên phải mới thành card to"). Thẻ to đỗ ở vị trí 2
   (--news-lead:1); lúc chuyển nó trượt từ vị trí 3 (nhỏ, bên phải) về 2 và nở
   ra — vệt vàng bám .is-active nên quét đúng trên cú "3 qua 2" đó.
   (Bản 2026-09-07 đặt vệt lên thẻ đứng trước bằng :has(+ .is-active) để đọc ra
   "2 qua 1" — đã bỏ theo yêu cầu mới; không còn phụ thuộc :has().) */
.m-news__swiper .swiper-slide.is-active .news-card__media::before{
  animation:news-wipe var(--news-reveal) var(--news-reveal-ease);
}
@media (prefers-reduced-motion:reduce){
  .m-news__swiper.is-swapping .swiper-slide{transition:none;}
  .m-news__swiper .swiper-slide.is-active .news-card__media::before{animation:none;}
}

/* Mobile: mỗi lần một thẻ, thẻ kế ló mép. --news-lead về 0 để thẻ to dẫn đầu
   hàng thay vì trốn khỏi mép phải. */
@media (max-width:639.98px){
  .m-news{--news-lead:0;}
  .m-news__swiper .swiper-slide{width:88%;}
}

/* =========================================================
   7 · M-CONTACT — form → info@huutin.co
   ========================================================= */
/* (TOÀN BỘ section này đã chuyển sang common.css ngày 2026-08-05 — nền, lưới
   .m-contact__grid và cỡ chữ .m-contact__intro — nối tiếp đợt chuyển
   .m-contact__info + .contact-form/.field trước đó.
   Lý do: trang san-pham-chi-tiet.html nay kết bằng ĐÚNG khối liên hệ này, mà
   nó không nạp home.css. Để lưới ở lại đây thì trang kia có đủ markup nhưng
   sập thành một cột không kiểu dáng — hỏng ÂM THẦM, không báo lỗi gì.
   Ở lại home.css chỉ còn hai override .section-pad riêng của trang chủ và
   quy tắc .m-contact .container ở phần nhịp rộng.) */


/* =========================================================
   8 · M-RIBBON — marquee dải ruy băng (nền trắng)

   Nền SÁNG chứ không phải tối: dải ảnh này nằm ngay trên footer đen tuyền, để
   nền tối thì hai khối dính vào nhau thành một mảng đen. Trắng trùng luôn nền
   .m-contact ở trên nên phía trên liền mạch, chỉ còn MỘT đường cắt duy nhất là
   mép footer.
   ========================================================= */
/* padding-bottom = 3px, ĐÚNG BẰNG khe giữa hai hàng ảnh (.m-ribbon__row): mép
   footer đen đọc ra như một ô nữa của cùng lưới, chứ không phải một dải trắng
   thừa. Đổi khe của lưới thì đổi luôn số này. */
.m-ribbon{background:var(--bg);padding-block:clamp(40px,6vw,72px) 3px;overflow:hidden;}
.m-ribbon__caption{text-align:center;color:var(--secondary);font-family:var(--font-display);font-size:var(--fs-micro);letter-spacing:.24em;text-transform:uppercase;margin-bottom:30px;}
/* Không mask mờ hai mép (feedback 2026-08-03): ảnh chạy tràn thẳng tới cạnh
   — bớt một lớp mask compositing trên track đang animate vô hạn. */
.marquee{display:flex;overflow:hidden;}
/* padding-right PHẢI luôn bằng gap: keyframe chạy -50% = đúng một bộ ảnh, nên
   khe giữa ảnh cuối của track này và ảnh đầu của track sau chính là padding
   này. Lệch một pixel là thấy khựng mỗi vòng lặp.
   3px = cùng khe với .m-markets__grid và .pband__frame. */
/* will-change: track này animate transform VĨNH VIỄN nên cho compositor biết
   ngay từ đầu — WebKit dựng layer sớm và giữ backing ổn định thay vì
   promote/demote giữa chừng (một nguồn nữa của ô trắng trên Safari). Đúng
   trường hợp được phép dùng will-change: phần tử luôn động, không phải "phòng
   khi". */
.marquee__track{display:flex;flex-shrink:0;gap:3px;padding-right:3px;animation:marquee 38s linear infinite;will-change:transform;}
/* KHÔNG dừng khi hover: từng ảnh CHƯA gắn link nên dừng lại không phục vụ
   thao tác nào (feedback 2026-08-03). Khi nào mỗi ảnh có link riêng thì thêm
   lại:  .marquee:hover .marquee__track{animation-play-state:paused;}  */
.marquee--rev .marquee__track{animation-direction:reverse;animation-duration:45s;}
.ribbon-img{
  height:clamp(120px,16vw,180px);width:clamp(180px,24vw,280px);
  /* GIỮ VUÔNG có chủ ý (ngoại lệ của --radius-*, xem chú thích token trong
     common.css): dải này chạy tràn viền và các ảnh chỉ cách nhau 3px — bo góc
     sẽ sinh ra một hàng hình thoi trắng tại mỗi mối nối, và hở góc ở hai mép
     màn hình. */
  border-radius:0;flex:0 0 auto;object-fit:cover;display:block;
  /* Ô giữ chỗ lúc ảnh chưa về — đậm hơn nền kem một nấc để thấy được khung */
  background:var(--gray-200);
}
@keyframes marquee{from{transform:translateX(0);}to{transform:translateX(-50%);}}
/* Khe DỌC giữa 2 hàng phải bằng khe NGANG giữa các ảnh, nếu không hai hàng đọc
   ra như hai dải rời chứ không phải một mảng ô ảnh. */
.m-ribbon__row{margin-bottom:3px;}
.m-ribbon__row:last-child{margin-bottom:0;}

/* =========================================================
   MICRO-INTERACTIONS — home sections
   Small hover/focus polish layered on the existing motion system.
   Uses --ease; honours the global reduced-motion kill switch.
   ========================================================= */

/* --- Market cards: body lifts + đường kẻ ngăn sáng lên khi hover ---
   Gạch chân hover cũ (h2::after, scaleX 0→1) đã GỠ, hai lý do:
   1) Nó chưa từng chạy — selector viết .market-card:hover h3::after trong khi
      markup là <h2> (đổi thẻ từ hồi nào không ai sửa selector).
   2) Dù có sửa h3→h2 thì nó cũng vẽ thêm 1 vạch trắng 2px ngay TRÊN đường kẻ
      ngăn mới (kẻ nằm cách đáy tiêu đề 14px, gạch chân ở -8px) → hai vạch
      song song cách nhau 6px, đọc như lỗi.
   Chuyển động giữ nguyên tinh thần: chính đường kẻ ngăn sáng lên (vàng 55% →
   vàng đủ, từ 2026-09-07 kẻ là vàng) khi rê chuột, đi cùng cú nhấc 4px. */
.market-card__body{transition:transform .5s var(--ease);}
.market-card:hover .market-card__body{transform:translateY(-4px);}
.market-card p::before{transition:background-color .45s var(--ease);}
.market-card:hover p::before{background:var(--gold);}


/* =========================================================
   RESPONSIVE — home
   ========================================================= */
@media (max-width:1024px){
  /* ⚠️ Lưới thị trường KHÔNG rớt xuống 2 cột ở mốc này nữa: iPad dọc (768px)
     rơi đúng vào đây, và khách chốt 2026-09-08 ba thẻ phải nằm trên MỘT hàng ở
     khổ đó. Mốc 2 cột đã dời xuống 767px, xem media query bên dưới. */
  /* Xếp chồng: bỏ canh phải + đường kẻ dọc, chữ trở về canh trái cho dễ đọc. */
  .m-about__cols{grid-template-columns:1fr;gap:clamp(24px,4vw,36px);}
  .m-about__lead{text-align:left;padding-right:0;padding-top:0;}
  .m-about__body{border-left:0;padding-left:0;padding-top:0;}
  .m-about__body p{text-align:left;}
  /* (.m-contact__grid gộp một cột đã chuyển sang common.css cùng đợt với quy
     tắc gốc của nó — xem ghi chú ở mục 7 phía trên.) */
}
@media (max-width:860px){
  /* Dưới 860px khoảng trống hai bên vòng không còn đủ cho 4 nhãn: chỉ giữ
     cặp trong (cách mục đang chọn 1 bước). Danh mục vẫn duyệt hết được
     bằng mũi tên / kéo / bàn phím. */
  .m-products{--pw-slots:1;}
}
@media (max-width:720px){
  /* MOBILE HIỆN ĐỦ 4 NHÃN (khách 2026-09-28: "text nhỏ lại xíu cũng được mà
     hiển thị đủ 4 tên"). Với hình học desktop (±46°/±76°, tỉ lệ .63) ở 375px
     thì cặp ngoài bị mép section cắt và "Dây webbing" dính "Dây ruy băng", nên
     mobile có bộ số riêng — product-wheel.js đọc các biến này trong measure():
       --pw-side-s .48  chữ 4 nhãn bên nhỏ lại (desktop .63)
       --pw-name-s .9   tên đang chọn nhỏ lại 10% để chừa chỗ cặp trong
       --pw-a1 41 / --pw-a2 69  kéo cặp ngoài lên khỏi mép cắt, giãn đều 28°
     Đo ở 375px: cả 5 nhãn nằm trong khung, đáy nhãn thấp nhất cách mép cắt 28px. */
  .m-products{
    --pw-slots:2;--pw-r:clamp(120px,30vw,168px);
    --pw-side-s:.48;--pw-name-s:.9;--pw-a1:41;--pw-a2:69;
  }
}
/* ⚠️ LƯỚI THỊ TRƯỜNG CHỈ CÓ HAI TRẠNG THÁI: 3 thẻ trên MỘT hàng, hoặc xếp DỌC
   hết. TUYỆT ĐỐI KHÔNG có nhánh 2 cột (khách chốt 2026-09-08: "không để layout
   2 trên 1 dưới").
   Lý do hiển nhiên khi nhìn: đúng ba thẻ, mà lưới 2 cột thì hàng dưới chỉ có
   một thẻ và bỏ trống nửa hàng — đọc ra như thiếu mất một mục.
   Bản 2026-09-08 sáng từng thêm nhánh 2 cột cho dải 641-767px, đã gỡ. */
@media (max-width:640px){
  .m-markets__grid{grid-template-columns:1fr;}
  .market-card{min-height:400px;}
  /* Dải logo: 4 mốc xếp 2×2 thay vì 1 hàng bị tràn. */
  .m-about__eras{display:grid;grid-template-columns:1fr 1fr;gap:26px 18px;}
  .banner__content{max-width:none;}
  /* Điện thoại: BỎ nowrap, cho tiêu đề xuống dòng.
     "HỮU TÍN TỪ TÂM ĐẾN TẦM" dài 22 ký tự — ép 1 dòng ở 375px thì chữ tràn
     khỏi .container 21px (đo thật), mà thu nhỏ cho vừa thì còn ~22px, bé hơn
     cả chữ body. Xuống 2 dòng vừa hết tràn vừa cho phép chữ TO HƠN.
     line-height 1.4 thừa hưởng từ rule gốc nên dấu chồng tầng vẫn không bị
     xén, đồng thời làm luôn khoảng cách giữa 2 dòng. */
  .banner h1{white-space:normal;font-size:clamp(22px,7vw,34px);}
  /* Vệt tối phải NỞ RỘNG trên điện thoại. Bản desktop (ellipse 72% 44%) được
     tính cho tiêu đề 1 dòng nằm gọn giữa màn; ở đây tiêu đề xuống 2 dòng và
     trải gần hết bề ngang, nên hai đầu dòng rơi vào phần rìa mờ của ellipse.
     Đo lại theo đúng tỉ lệ khung điện thoại: dùng nguyên ellipse 72% 44% của
     desktop thì tương phản tụt còn 2.88:1 ở 320px (RỚT chuẩn AA 3:1).
     Ellipse rộng ra 105% 40%, tâm vệt cùng mức với desktop (.54 — đã nâng từ
     .34 khi bỏ text-shadow của .banner h1, xem comment ở đó).
     Scrim phẳng để .08 (desktop .06): khung dọc của điện thoại cắt ảnh sát
     hơn nên hay lọt vùng sáng gắt vào giữa khung. */
  .banner__slide::before{
    background:
      radial-gradient(ellipse 105% 40% at 50% 50%,
        rgba(18,19,20,.54) 0%,rgba(18,19,20,.43) 32%,
        rgba(18,19,20,.25) 60%,rgba(18,19,20,.08) 82%,rgba(18,19,20,0) 100%),
      linear-gradient(0deg,rgba(18,19,20,.34),rgba(18,19,20,0) 45%),
      linear-gradient(0deg,rgba(18,19,20,.08),rgba(18,19,20,.08));
  }
}

/* =========================================================
   REDUCED MOTION — swap wheel for static grid
   ========================================================= */
@media (prefers-reduced-motion:reduce){
  .pwheel-wrap{display:none;}
  .m-products__fallback{display:grid;padding-block:clamp(48px,6vw,84px);}
  .banner__img,.swiper-slide-active .banner__img{transform:none;}
  .marquee__track{animation:none;flex-wrap:wrap;}
}
